PCM-PWM変換でステレオサウンド再生

  • 投稿日:
  • 更新日:2015/03/09
  • by
  • カテゴリ:

ATtiny2313でのステレオサウンド再生テストです。

FT245RLモジュールと、ATtiny2313を使い、きわめて簡単な回路でやってみました。 データは単に垂れ流しているだけです。 これで少しだけBitBangモードの使い方がわかったような気がします。 回路はこんな感じ。 端子はすべてFT245RLのものです。

20090323-experiment.PNG

ポートが8ビットなので、LSBの1ビットをLRチャネルの選択に使い、実質7ビットですが、サンプリング周波数は結構高いので、それなりに聞こえます。 ただ、結構プチプチとDCっぽいノイズが乗りますね。 LPFのカットオフはデューティ比50%の時に約16kHzなのですが。

AVRを20MHzで使っているので、PWMの周波数は次のようになります。

20,000,000 / 256 = 78,125 (Hz)

そこで、ffmpegを使い、次のようにして8ビットステレオファイルを作ります。

ffmpeg -i ソースファイル -f rawvideo -acodec pcm_u8 -vn -ar 39062 出力ファイル

この出力ファイルを次のようなソースで吐き出せばokです。

#include <windows>
#include <stdio.h>
#include <sys/unistd.h>
#include "ftd2xx.h"

int main(int argc, char **argv) {
  FT_HANDLE ftHandle;
  FT_STATUS ftStatus;
  DWORD written;
  unsigned int count = 0, i;
  unsigned char val[4096];
  FILE *in;

  if (argc < 2) {
    fprintf(stderr, "Usage: %s \n", argv[0]);
    return 1;
  }
  if (!(in = fopen(argv[1], "rb"))) {
    fprintf(stderr, "Error: opening input: %s\n", argv[1]);
    return 1;
  }
  ftStatus = FT_Open(0, &ftHandle);
  if (ftStatus != FT_OK) {
    fprintf(stderr, "Error: opening USB device\n");
    return 1;
  }
  ftStatus = FT_SetBitMode(ftHandle, 0xff, 1);
  if (ftStatus != FT_OK) {
    fprintf(stderr, "Error: initializing USB device\n");
    return 1;
  }
  ftStatus = FT_SetUSBParameters(ftHandle, 4096, 0);
  if (ftStatus != FT_OK) {
    fprintf(stderr, "Error: setting USB buffer size\n");
    return 1;
  }
  ftStatus = FT_SetBaudRate(ftHandle, 78125 / 2 / 2);
  if (ftStatus != FT_OK) {
    fprintf(stderr, "Erro: setting baud rate\n");
    return 1;
  }
  while (1) {
    if (!fread(val, 4096, 1, in)) {
      break;
    }
    for (i = 0; i < 4096; i++) {
      if (i % 2) {
	val[i] |= 1; // right
      } else {
	val[i] &= 0xfe; // left
      }
    }
    FT_Write(ftHandle, val, 4096, &written);
    if (written == 0) {
      break;
    }
  }
  FT_Close(ftHandle);
  fclose(in);
  return 0;
}

AVR側はきわめて簡単で、次のようなソースになります。

#include <avr/io.h>
#include <avr/interrupt.h>
ISR(TIMER0_OVF_vect) {
  unsigned char val = (PINB & 0xfb) | (PIND & 0x04);
  unsigned char channel = val & 0x01;
  val &= 0x0fe;
  if (channel) {
    OCR0A = val; // right
  } else {
    OCR0B = val; // left
  }
}
int main() {
  PORTB = 0xff;
  PORTD = _BV(PD2);
  DDRB = _BV(PB2);
  DDRD = _BV(PD5);
  TCCR0A = 0b10100011; // OSCA enable, OSCB enable, fast PWM
  TCCR0B = 0b00000001; // OSC=fCPU
  TIMSK = _BV(TOIE0);
  sei();
  while (1);
  return 0;
}

FT245RLのボーレートの設定が、サンプリング周波数の半分(78125/4)なのが謎。 これをサンプリング周波数と同じに設定してしまうと、再生が倍速になってしまいます。

こちらもよく読まれています