2016年2月28日日曜日

オーディオ用DACを使ったファンクションジェネレータ UIの検討

状態遷移図

LCD表示

ガラケーのUI設計みたいだな(^q^;;

オーディオ用DACを使ったファンクションジェネレータ ハンダ付け完了


動作チェック


ブレッドボード上で動作させたのと同程度の波形は出ているようだ。ロータリーエンコーダーはまだつないでいなので要チェック。

ケースへの仮置き

LCDは直接基板に取り付けると表示が見難いのでケースの蓋に貼り付ける方向で考えた。

LCDをでかくする?


どうせケースの蓋に貼り付けるなら秋月のAQM1602Aにしたほうが見栄えはいいかもしれない。

<追記:2016.03.01>

この向きだと表示が上下逆になってしまう。物理的に逆にすると表示域がキーパッドと干渉するのであきらめた。

</追記>

2016年2月27日土曜日

オーディオ用DACを使ったファンクションジェネレータ ハンダ付け

テストはまだですが、ハンダ付け完了

部品面

3.3V LDOには手製の放熱板をとりつけた。


アルミ板を金鋸でカットして共立で売ってた熱伝導シートで貼り付けた。


ハンダ面

ジャンパは0.6mmのポリウレタン線を使用。


テスト表を作っていたら回路図を間違えていたのに気づいたので、続きは次回。

2016年2月24日水曜日

オーディオ用DACを使ったファンクションジェネレータ 基板設計 続き

回路図

基板図

結合テスト用のブレッドボードの配線図

PSoC Creatorのプロジェクト
https://github.com/ryood/I2S_FG/tree/master/PSoC/I2S_FG/PrototypingKit_FG_Test.cydsn

やったこと


電源電圧にシビアそうなので電源電圧を監視するために分圧抵抗を追加。ファームウェアでDelSigADC(デルタシグマADC)コンポーネントで測定。内蔵の基準電圧を利用。

I2C LCDのコントラストを調整できるようにファームウェアを変更

3.3VのLDOが熱を持つようなので放熱板をつけられるように位置を修正(アルミ板を加工して小さいのを作って接着する予定)

基板上でI2C/I2Sを配線しやすいようにPSoCのピンの設定を変更

部品並べ



基板をカットしてケースと合わせてみる

電池ボックスと基板はなんとか収まりそう。ケースの上面にキーパッドを貼るとLCD見難くなるかも。LCDは蓋にはりつけてフラットケーブルでつなごうかな?



2016年2月22日月曜日

オーディオ用DACを使ったファンクションジェネレータ 基板設計

部品並べ

電池を単4×3にすればケース(タカチPB-2)に収まるかもしれない。ぎりぎりっぽいのでもう少し考えてダメなら電池を外出しにする。

回路図

基板図

単4×3の電池ケースの幅は40mmでタカチのPB-2は110mmなので66mm+40mm=106mmで基板と電池ケースは干渉するかしないか。

メモ:


R1とC10はピンソケットを使って差し替えできるようにする。

2016年2月21日日曜日

オーディオ用DACを使ったファンクションジェネレータ 結合テスト

配線図


I2C LCDの電源のOn/OffはMOS-FETで行うつもりだったが、秋月のAQM0802は1mAしか消費しないのでPSoCのGPIOで直接駆動することにした。

LCDの電源実測値
電圧: 3.28V
電流: 0.15mA
ちょっと怖い感じもするが、実測では0.15mAしか流れていなかった。PSoCのPinの設定は「Strong Drive」で正負両方FETでドライブされている。


PSoCの駆動周波数


Github:
https://github.com/ryood/I2S_FG/tree/master/PSoC/I2S_FG/PrototypingKit_FG_Test.cydsn

PSoC CreatorのTopDesign

PCM5102aのサンプリングレートを382kHzにするために48MHz駆動させていたが、演算が追いつかないようだ。

20MHzの水晶で80MHzに逓倍した場合と40MHzに逓倍した場合を比較してみた。サンプリングレートはどちらも
40MHz(I2Sコンポーネントに入力したクロック) / 32bit / 2ch / 2 = 312,500Hz
出力に2000pF/470Ωの1次RC-LPFを入れている。

80MHz駆動

ch1:LPF通過後 ch2:LPF通過前

40MHz駆動

40MHzの方はときどき出力レベルの更新が間に合っていないようで波形が崩れている。


出力波形



ch1:LPF通過後 ch2:LPF通過前

10kHz程度でも1次LPFだけでは波形のガタガタがわかる→正弦波の歪になる。外付けにするにしてもう少しスパっと切れるLPFは必要かな?

メモ:


DMAのバッファの更新をL/RでPing Pongで書き換えすれば48MHzでも間に合う?

L/R出力して、LのDMA転送終了でバッファを書き換えてRを使えば間に合う?

CPUは内蔵クロックで80MHz駆動してI2Sのクロックだけ外付けのクリスタルで生成できないか?(非同期)

<追記:2016.02.24>

非同期作戦はI2SコンポーネントがUDBで作られているのでMaster Clockと同期していないとダメと怒られてBuildできなかった。

PSoC Creatorをだまして96MHz駆動させるのは成功。(10MHz水晶を使う設定で80MHz駆動させて実は12MHzの水晶を使って96MHz駆動させる)

長時間動かして壊れたらいやなので完成してから実験する予定。

</追記>

2016年2月19日金曜日

オーディオ用DACを使ったファンクションジェネレータ ファームウェアのコーディング+α

配線図



やったこと


  • MOS FETによるLCDの電源のスイッチング
  • 3.3V LDOで電源供給
  • キーパッドから電卓のように周波数値を入力する

Github:
https://github.com/ryood/I2S_FG/tree/master/PSoC/I2S_FG/UI_Test.cydsn

MOS FETのスイッチング


MOS FETのスイッチングはハイサイドだと電源電圧が上げられないのでローサイドで行った。

Hi Side





Low Side



メモ:


2N7000をLTSpiceで使う方法はトラ技2016年1月号付録の「LTSpice使いこなし事典」にのっていた。モデルが.SUBCKTで記述されているのでcmp\standard.mosに直接記述はできない。

2016年2月15日月曜日

Arduinoで4×4のキーパッドの読み取り 続き

Arduinoで4×4のキーパッドの読み取り」の続きで「押して離して」を判定するようにしてみた。

配線は前回と同じ。
押しっぱなしにしても連続入力されない。

//------------------------------------------------------
// KeyPad_Scan
//
// D2..D4 KeyPad Column Input (Pull Down)
// D5..D9 KeyPad Row Output
//
//------------------------------------------------------

static char keypadChar [] = {
  '1', '4', '7', '*',
  '2', '5', '8', '0',
  '3', '6', '9', '#',
  'A', 'B', 'C', 'D',  
};

int cnt;

void setup() {
  pinMode(2, INPUT);
  pinMode(3, INPUT);
  pinMode(4, INPUT);
  pinMode(5, INPUT);
  pinMode(6, OUTPUT);
  pinMode(7, OUTPUT);
  pinMode(8, OUTPUT);
  pinMode(9, OUTPUT);
  Serial.begin(9600);
  Serial.println("Key Scan Start..");
}

//------------------------------------------------------
// keyScan1(): 押されているキー
// return: キースキャンの結果 0..15
//         押されていない場合は -1
//------------------------------------------------------
int keyScan1() {
  int i, j;
  for (i = 0; i < 4; i++) {
    digitalWrite(i + 6, HIGH);
    for (j = 0; j < 4; j++) {
      if (digitalRead(j + 2) == HIGH) {
        digitalWrite(i + 6, LOW);
        return i * 4 + j;
      }
    }
    digitalWrite(i + 6, LOW);
  }
  return -1;
}

//------------------------------------------------------
// keyScan(): 押して離されたキー
// return: キースキャンの結果 0..15
//         変化がない場合は -1
//------------------------------------------------------
int keyScan() {
  static int keyBuff;
  int kv, kvv;
  
  //チャタリング防止
  kv = keyScan1();
  delay(1);
  kvv = keyScan1();
  
  if (kv == kvv) {
    if (kv != keyBuff) {
      keyBuff = kv;
      return kv;
    }
  }
  return -1;
}

void loop() {
  int v;
  v = keyScan();
  if (v != -1) {
    Serial.print(keypadChar[v]);
    // 20字で改行
    cnt++;
    if (cnt > 20) {
      cnt = 0;
      Serial.println("");
    }
  }
}

オーディオ用DACを使ったファンクションジェネレータ UI系のテスト

配線図


やったこと


  • PSoC 5LPを外付け水晶振動子(12MHz)で駆動
  • I2C LCD(AQM0802@秋月)
  • キーパッド
  • ロータリーエンコーダ

Github
https://github.com/ryood/I2S_FG/tree/master/PSoC/I2S_FG/UI_Test.cydsn

外付け水晶振動子


P15[0]とP15[1]につなげる。P15[2]とP15[3]はkHzクロック用。「PSoC 5LP Prototyping Kitを外付けクリスタルで動作させる

I2Sを32bit/382kHzにするとビットレートは24.448MHz。PSoCのI2Sコンポーネントの仕様により、2倍の48.896MHzのクロックを入れないといけないが、PSoC 5LPのクロックの最大値は80MHzなので48.896MHzで駆動する必要ありそう。

今回は12MHzの水晶でPLLで逓倍して48MHzで駆動。

48MHzでは駆動周波数が低くて演算が追い付かない場合、80MHz駆動にするためには20MHzの水晶で4倍で駆動、2倍の40MHzをI2Sコンポーネントに入力するとI2Sのサンプリングレートは312.5kHz。


I2C LCD(AQM0802)


I2CのPullUpはPSoCの内部プルアップを使用。内部PullUpした場合、抵抗値はおそらく5.6kΩ
http://www.cypress.com/knowledge-base-article/tolerance-internal-gpio-pull-updown-resistance-psoc-135

RST線をHにするのを忘れていて最初動かなかった。AQM1602で動作確認して気づいた。


キーパッド


Row×4、Col×4なのでGPIOのでポート1個でちょうど8個。Port3では動作しなかった。P3[2]があやしい。PSoC 5LP Prototyping Kit、3枚で試したがいずれもP3[2]がおかしいようだ。

Port2に変更したら動作した。

パラレルLCD(HD44780)でテストした時の使えるポートと関係あるかも?
PSoC 5LP Prototyping KitでChar LCD(HD44780)を使う まとめ

キーパッドの入力系のPullDownもPSoCのPinの設定でPullDownしている。


ロータリーエンコーダ


秋月の「ロータリーエンコーダ(24クリックタイプ)」はPanasonicのEVERやaitendoのRE1111とはAB相の遷移の仕様が違ったのでコードを変更。1デテントで2個進む感じ。

2016年2月14日日曜日

PSoC 5LP Prototyping Kit: UARTの使い方 (KitProgを分割した場合)

PSoC 5LP Prototyping Kitのプログラマー部分のKitProgを分割すると、そのままではUSBを使ったUART通信ができなくなる。はまってしまったのメモ。

配線図


KitProgのP12[7](UART TX)、P12[6](UART RX)をPrototyping Kit本体のP12[6]、P12[7]につなぐ。TX同士、RX同士ではなくTXとRX。

KitProgをUSBのVBUSで5V駆動、PrototypingKit本体を別電源で3.3V駆動しても動作する。(ほんとはちょっとまずいかも?)

2016年2月12日金曜日

NUCLEO-F401RE(Cortex-M4)とPSoC 5LP(Cortex-M3)の浮動小数点演算比較

小ネタ実験です。

NUCLEO-F401REに載っているMPUはSTM32F401でCortex-M4ベースでFPUが搭載されている。しかも1,500円@秋月とPSoC 5LP Prototyping Kit(Cortex-M3ベース)と同じ値段だ。

このあいだPSoC 5LPで浮動小数点演算をしてサイン波のテーブルを作成してみたが、FPU付きのNUCLEO-F401REでも同じことをして比較してみた。

NUCLEOはmbedのオンラインコンパイラ使った。

 #include "mbed.h"  
   
 //------------------------------------  
 // Hyperterminal configuration  
 // 9600 bauds, 8-bit data, no parity  
 //------------------------------------  
 #include <math.h>  
   
 #define M_PI      (3.14159265358979323846) /* pi double*/  
 #define M_PI_f     (3.1415926f) /* pi float */  
 #define TABLE_LENGTH  (8192)  
   
 Serial pc(SERIAL_TX, SERIAL_RX);  
 Timer timer0;  
 uint8_t waveTable_0[TABLE_LENGTH];  
   
 void genSineTable_double(uint8_t *table, int length)   
 {   
   int i;   
   int16_t v;   
   int8_t* p8;   
   for (i = 0; i < length / 2; i++) {   
      v = (int16_t)(sin(2.0 * M_PI * i * 2 / length) * 32767);   
      p8 = (int8_t *)&v;   
      table[i*2] = *(p8+1);   
      table[i*2+1] = *p8;   
   }   
 }  
   
 void genSineTable_float(uint8_t *table, int length)   
 {   
   int i;   
   int16_t v;   
   int8_t* p8;   
   for (i = 0; i < length / 2; i++) {   
      v = (int16_t)(sinf(2.0f * M_PI_f * i * 2 / length) * 32767);   
      p8 = (int8_t *)&v;   
      table[i*2] = *(p8+1);   
      table[i*2+1] = *p8;   
   }   
 }  
     
 int main() {  
   timer0.start();  
     
   // 単精度浮動小数点演算 (with FPU)  
   pc.printf("start float: TABLE_SIZE:%d\r\n", TABLE_LENGTH);  
   timer0.reset();  
   genSineTable_float(waveTable_0, TABLE_LENGTH);  
   pc.printf("%d\r\n", timer0.read_us());  
     
   // 倍精度浮動小数点演算  
   pc.printf("start double: TABLE_SIZE:%d\r\n", TABLE_LENGTH);  
   timer0.reset();  
   genSineTable_double(waveTable_0, TABLE_LENGTH);  
   pc.printf("%d\r\n", timer0.read_us());  
 }  
    


FPU付きだが単精度小数点数しか使えないらしいのでdouble型とfloat型の二通りでサイン波テーブルを生成。


TABLE_LENGTH Nucleo float (us) Nucleo double (us) float vs double PSoC 5LP(us)
512 372 18125 48.72311828 15280
1024 743 36255 48.79542396 29640
2048 1486 72718 48.93539704 58400
4096 2972 145631 49.00100942 116000
8192 5943 291349 49.02389366 230000


比較すると、floatだとNUCLEO-F401REが圧倒的に速い。

PSoC 5LPのコードは今回のものと少し違うが、double型で計算している。double型(FPUなし)だとM3のPSoC 5LPの方が若干速い結果になった。

動作クロックはPSoC5LPが72MHz、NUCLEO-F401REは84MHz。

double型(FPUなし)での比較はM3もM4も大差ないというぐらいの認識で(^q^;


メモ:


NucleoというかSTM32はmbed以外にもSTM32ネイティブ(?)のコンパイラがあってオフィシャルではIARとかの有償(企業向)のものが推奨されている。

無償ではCooCox CoIDEというのがある(GCC based IDEと書かれているのはこれかな?)

STM32 Cubeというライブラリもあるみたいで、よくわかりません。

エンディアンの変換 __REV()関数

リトルエンディアンからビッグエンディアンに変換するためにバイト単位で入れ替えていたが、__REV()という(マクロ)関数が用意されていた。Cortex-M用のGCCの関数のようだ。

1バイトずつ入れ替え
v = (sineTable[index] >> 0);
p8 = (uint8 *)(&v);
waveBuffer_0[i]   = *(p8 + 3);
waveBuffer_0[i+1] = *(p8 + 2);
waveBuffer_0[i+2] = *(p8 + 1);
waveBuffer_0[i+3] = *p8;
__REV()を使用
v = (sineTable[index] >> 0);
*((uint32 *)waveBuffer_0) = __REV(v);

__REV()はARMのasmにrevと言う命令があってこれを呼び出していて一発でエンディアンを変換できるようだ。


演算の前後でPinにH/Lを出力して実行時間を比較。


1バイトずつ入れ替え

ch1:実行時間 ch2:I2SのWS

(1)PW:1.520us

__REV()

(1)PW:1.070us

※横軸の縮尺が違います。

PWはパルスの+側の時間で、比較すると__REV()を使ったほうがかなり短縮されている。

また、1バイトずつ入れ替えている方は、実行時間より、WSがHiの時間のほうが長い。つまり2ch出力すると間に合っていなかったようだ。



2016年2月11日木曜日

オーディオ用DACを使ったファンクションジェネレータ ファームウェアを改良

PSoCだけではなくてmbedでもやってみようと思って調べてみたが、I2Sのライブラリがいまいちで、DMAが簡単には使えないようだ。

手持ちのNUCLEO-F401REはCortex-M4なのでもう少し調べてみたいところですが。


というわけで、引き続きPSoC 5LPで。

Github:
https://github.com/ryood/I2S_FG/tree/master/PSoC/I2S_FG


波形の振幅を小さくする。

PCM5102aの電源電圧が3.3Vだとフルスケール出ないようなので、波形テーブルの値を0.9倍にしてみた。

フルスケール

0.9倍

振幅が小さくなって波形の頭打ちがなくなった。

波形テーブルをリッチにする。

間に合わせで、16bit/1024個のテーブルにしていたのを32bit/32768個に拡張した。32bit×32768/8bit=131,072Byteのテーブルになる。PSoC 5LPはFlashメモリが256kBあるのでまだ余裕がある。32bit版も振幅は0.9倍している。

10kHz 16bit/1024

10kHz 32bit/32768

20kHz 16bit/1024

20kHz 32bit/32768

50kHz 16bit/1024

50kHz 32bit/32768

100kHz 16bit/1024

100kHz 32bit/32768

192kHz 16bit/1024

192kHz 32bit/32768

理屈で言うと32bit/32768の方がサンプリングポイントがなめらかになるはずだがオシロで波形を見た限りではよくわからない。

100kHzで32bitの方が悪化して見えるのは、ちゃんと波形が出力されていないのが原因のようだ。メモリ転送が間に合っていないのかも。

2ch出力をやめて1ch出力にしてみる。

CPUの処理を短縮するために、32bit/32768のままI2Sコンポーネントの設定を変更してLchのみ出力するようにしてみた。また、PCM5102aからの出力にかけている470Ω/2200pFのLPFの手前での波形も見てみた。

100kHz

ch1:LPF通過後 ch2:LPF通過前

1ch出力にしたら100kHzの波形(ch1)は綺麗になった。LPFを通す前を見ると高い周波数では相当ガタガタなので1次LPFを通すだけでもかなり波形は綺麗になっている方だろう。

プログラムで振幅を絞る。

POTによるレベル調整ではなくプログラムで出力振幅を絞る実験もしてみた。出力値(整数)をシフト演算したもの。

シフト0 (1倍)

ch1:LPF通過後 ch2:LPF通過前

(1)Vk(RMS):1.889V

シフト1 (1/2倍)

(1)Vk(RMS):954.4mV
1.889V / 2 = 944.5mV

シフト2 (1/4倍)

(1)Vk(RMS):483.0mV
1.889V / 4 = 472.25mV

シフト3 (1/8倍)

(1)Vk(RMS):250.8mV
1.889V / 8 = 236.125mV

シフト4 (1/16倍)

(1)Vk(RMS):142.5mV
1.889V / 16 ≒ 118mV

だいたい想定通りに振幅が小さくなっている。振幅が小さくなると誤差が出ているのはノイズの影響かな。

拡大

周期性がありそうなのでクロックノイズかな?300mV~500mV(p-p)ぐらいなので結構でかい。

メモ:

オシロのFFT機能を使って高調波歪を見てみる。

テーブルの要素数は増やせば良くなるかと思ってたが、どうだかわからない。出力周波数とサンプリングレートの比率でジッターになる?

1次LPFを通す前の波形も出力できるようにして、外付けで高次のActive LPF(+レベル調整)を入れられるようにする?