2015年11月30日月曜日

PSoC 1を使ってみる PSoC Designer & MiniProg

PSoC 1はM8Cという8bitのCPUがコアなのでマイコンとしてはちょっとしょぼい。

が、8Pinのパッケージがあって他のPSoCにはないスイッチトキャパシタも搭載されているので使ってみることにした。

開発にはPSoC4やPSoC5LPとは違って「PSoC Designer」を使う。

Windows 8.1ではPSoC DesignerでBuildできない


以前少しだけ触ったときはPCのOSがXPだったが、8.1にしてから全然触っていないかった。

PSoC Designer自体は動作するが、Buildしようとすると
Starting MAKE...
creating project.mk
name translation failed on D:/Program/Cypress/PSoC\ Designer/5.4/Common/CypressSemiBuildMgr - 3
name translation failed on D:\Program\Cypress\PSoC - 2
name translation failed on Designer\5.4\Common\CypressSemiBuildMgr\tools\include\CY8C29000 - 3
lib/led.asm
!E LED.inc(13): file 'm8c.inc' not found in current directory or in include path(s)
というエラーがでてBuildできない。

メッセージを見るとMakeのパス名の処理で引っかかっているようなので、インストール場所をドライブのルート直下にしたりしたがダメ。

XPでは動いていたので、Virtual BoxでWindows XPの仮想マシンを作ってやってここにインストールしたらBuildできた(^q^/

PSoCへの書き込みは「PSoC Programmer」を使うのでこちらだけ8.1でOKかなと思ったが、これもダメ。

なのでVirtual Boxの仮想マシンに、MiniProgのUSBを割り当ててなんとか書き込みもできた(^q^/

こういうことがあるから、なかなかWindows10へ移行する踏ん切りがつかないんだな(--;

サンプル・プロジェクト


参考書の「はじめてのPSoCマイコン」も持っているが、オフィシャルページにあった「PSoC
® Designer™ Quick Start Guide」をやってみた。

POTの値をADCで読み取ってLチカの速度を変化させるというもの。

PSoC1 CY8C29466-24PXI Pinout

このプロジェクトでは
P0_0: LED (ADCの閾値表示)
P0_2: LED (POTの値で点滅の周期が変化)
P0_1: POT
というピン割り当てになっている。

配線図


3連POT基板は、うち一つだけ使っている。

PSoCのロードマップ


http://www.cypress.com/resource-types/product-roadmaps/cypress-psoc-and-mcu-portfolio-roadmap

このロードマップを見ると次のPSoCはARM Cortex-M7ベースになるみたいだ。コアのアップグレードだけじゃなく、フレキシブルな部分の拡充にも期待(^q^/


ベースマシンの構想(メモ)

リズムマシンがそろそろ一段落しそうなので次に作りたいベースマシンの構想。

TB303をリスペクト(^q^;

妄想図

図の右半分はシーケンサーとUIの部分だ。

それぞれI2Cで接続する。LCDは別だが基板同士をI2Cでつなぐのはリズムマシンでやったので、がんばればできそう。

左半分はアナログシンセでいうVCO、VCF、VCAだ。

これも制御信号としてI2Cでつなぐ。なので各基板にI2C信号を解釈するマイコンが必要。

黒・オレンジのラインはアナログの波形信号だ。

メモ:


  • 打ち込み用のタクト・スイッチがリズムマシンよりかなり増えそうなので(音階だけで13個いる)LEDの表示はなくして別基板のLCDで表示。
  • 28PinのATMega328PでもPinの数がたりなさそうなので、PSoC 4 Prototyping Kitを制御用に想定。
  • Display基板はグラフィックLCDを考えているが、パラレルのやつをMaster基板に載せた方がいいかな?
  • OSC、Filter、Ampの実現方法はまだ全然考えてないの最初はここから実験する予定。
  • それぞれの制御に必要なロータリーエンコーダを置いてみたが、うまくすればモード切り替えとかでMaster基板のロータリーエンコーダで代用できるかも?
  • ある程度実験がうまくいったら、作る前にそれぞれの基板と全体の状態遷移を考えた方がいいなこれ・・・


2015年11月27日金曜日

ATtiny13でDDS波形出力 ちょっと修正

PWM出力のあたりを少し改良した。

 /*  
  * ATtinyVCO.c  
  *  
  * Created: 2015/11/25 14:07:46  
  * Author: gizmo  
  *  
  * PB1: PWM out  
  * PB2: check pin  
  *  
  * 2015.11.27 PWM出力を6bit精度に変更  
  *  
  */   
   
 #define F_CPU    9600000ul  
   
 #include <avr/io.h>  
 #include <avr/pgmspace.h>  
 #include <util/delay.h>  
 #include <stdint.h>  
   
 #define SAMPLE_CLOCK    (8000.0f)  
 #define POW_2_16        (65536ul)  
   
 #define FREQUENCY_MAX    (2000.0f)  
   
 // Saw dawn wave up table  
 const PROGMEM uint8_t sawUpTable[] = {  
     0,    1,    2,    3,    4,    5,    6,    7,    8,    9,    10,    11,    12,    13,    14,    15,  
     16,    17,    18,    19,    20,    21,    22,    23,    24,    25,    26,    27,    28,    29,    30,    31,  
     32,    33,    34,    35,    36,    37,    38,    39,    40,    41,    42,    43,    44,    45,    46,    47,  
     48,    49,    50,    51,    52,    53,    54,    55,    56,    57,    58,    59,    60,    61,    62,    63  
 };  
   
 volatile uint16_t phaseAccumlator;  
 volatile uint16_t tuningWord;  
 volatile uint8_t amp;  
   
 //=============================================================================  
 // 波形生成  
 //  
 // ----------------------------------------------------------------------------  
 // setDDSParameter()  
 // parameter: frequency: 生成する周波数  
 //  
 void setDDSParameter(float frequency)  
 {  
     tuningWord = (int16_t)(frequency * POW_2_16 / SAMPLE_CLOCK);  
 }  
   
 // ----------------------------------------------------------------------------  
 // generateWave()  
 // return: 出力値(0..255)  
 //  
 uint8_t generateSawWave()  
 {  
     uint8_t index;  
       
     phaseAccumlator += tuningWord;  
       
     // 右へシフト: 16bit -> 6bit(64個)  
     index = phaseAccumlator >> 10;  
     return pgm_read_byte(&sawUpTable[index]);  
 }  
   
 //=============================================================================  
 // 波形生成  
 //  
 // ----------------------------------------------------------------------------  
 // setPWMDuty()  
 // parameter: value: 設定するDuty(0 .. OCR0A)  
 //  
 void setPWMDuty(uint8_t value)  
 {  
     // PWMのデューティー比を設定  
     OCR0B = value;  
 }  
   
 //=============================================================================  
 // ADC  
 //  
 // ----------------------------------------------------------------------------  
 // getCV()  
 // return: CV値(0..4095)  
 //  
 uint16_t getCV() {  
     // ADC_CVの値を取得  
      return 901;    // 440Hz  
     //return 4095;    // 2000Hz  
 }  
   
 // ----------------------------------------------------------------------------  
 // getGate()  
 // return: Gate値(0..255)  
 //  
 uint8_t getGate() {  
     // ADC_GATEの値を取得  
     return 255;  
 }  
   
 //=============================================================================  
 // ラッチ割込み  
 //  
 //ISR()  
 //    generateSawWave()を呼び出し  
 //    amp値を乗算  
 //    PWM DACに出力値を設定  
   
 //=============================================================================  
 // メイン・ルーチン  
 //  
 int main()  
 {  
     uint16_t cv;  
     uint8_t v;  
       
     //-------------------------------------------------------------------------  
     // PORT設定  
     //-------------------------------------------------------------------------  
     // DDRB = 0;  
     DDRB |= (1 << DDB1);    // PB1(OC0B): PWM out  
     // Debug用  
     //  
     DDRB |= (1 << DDB2);    // PB2: output  
       
     //-------------------------------------------------------------------------  
     // PWM設定  
     //-------------------------------------------------------------------------  
     // TCCR0A = 0;  
     // TCCR0B = 0;  
     //-------------------------------------------------------------------------  
     // 波形生成モード: WGM0: 1:1:1  
     // 高速PWM(モード7)  
     TCCR0A |= (1 << WGM01) | (1 << WGM00);  
     TCCR0B |= (1 << WGM02);  
     //-------------------------------------------------------------------------  
     // コンペア・アウトプットB: COM0B: 1:0  
     // コンペア・マッチでOC0Bクリア、TOPでOC0Bセット  
     TCCR0A |= (1 << COM0B1) | (0 << COM0B0);  
     //-------------------------------------------------------------------------  
     // クロック設定: CS0: 0:0:1  
     // 分周なし  
     TCCR0B |= (0 << CS02) | (0 << CS01) | (1 << CS00);  
       
     // TCCR0A = 0b00100011;  
     // TCCR0B = 0B00001001;  
       
     //-------------------------------------------------------------------------  
     // 分解能 6bit(0 .. 63)   
     OCR0A = 64;  
       
     for (;;) {  
         // Debug用: PB2  
         PORTB |= (1 << PORTB2);  
           
         cv = getCV();  
         setDDSParameter(cv * FREQUENCY_MAX / 4096.0f);  
         amp = getGate();  
         v = generateSawWave();  
         setPWMDuty(v);  
           
         // Debug用: PB2  
         PORTB &= ~(1 << PORTB2);  
           
         _delay_us(125);    // 8,000Hz          
     }  
 }  
 // EOF  

AVRのTimer0はWGMをFast PWMのモード7にすると周期を可変にできるようだ。OCR0Aで周期を設定、OCR0BでDuty比を設定する。

ちゃんと確認したわけではないがモード7の場合PWM出力はOC0A(PB0)は使えなくて、OC0B(PB1)に限定されるみたいだ。

矩形波だけのファンクションジェネレーターを作るとき「ATMega328PのTimer1でPWMを使ってみる」でFast PWMがうまく使えなかった原因はこれかな?

16bitのTimer1より8bitのTimer0の方が設定するレジスタが少なくて多少わかりやすいかも。

波形テーブルの要素数と分解能


OCR0Aを64に設定した。こうすると周期が64になってOCR0Bを0..63に設定してDuty比を設定できる。64段階なのでbit数にすると6bit。6bitのなんちゃってDACだ。

周期が64なので、9.6MHz駆動させた場合
9.6MHz / 64 = 150kHz
と、波形をのせる基本のパルス波の周波数を上げられた。

オシロで波形をみると


※オシロ画面のキャプチャーだと横幅が狭くて見づらいのでUSB経由でPCのオシロに付属していたユーティリティ・ソフトで表示させた。(このソフトあんまり使い勝手がよくないのでほとんど使っていない(^q^;)

ch1(赤)がOC0Bの出力波形、ch2(黄色)がプログラムで波形計算の前後でH/Lさせたもの

ch1は143.799kHzとなっていて粗密がノコギリ波的な感じになっている。

ch2はHiの区間が波形を計算するのに使った時間でだいたい100uSぐらいだ。本当は外部割り込み(ラッチ)でサンプリング周期を与えようと思っているが、今回は単純にメインループで順次処理している。

出力波形をオーディオインタフェース経由でPCのWaveSpectraで見てみた。



無事ノコギリ波が出力されている(^q^/

パルス波が150kHzで可聴帯域外なのでLPFをいれなくてもパルス波は除去されているようだ(オーディオインタフェースがLPF)

6bitなのでガタガタだが。

メモ:


  • スレーブをいっぱい並べると駆動クロックは同期させておかないとやっぱりマズそう。スレーブごとに内部クロックで動かすと波形がずれてジッターみたいになりそう。→それはそれで面白いかもしれない(^q^;
  • tuningWordを求めるときに浮動小数点演算をしているのでメモリを90%以上使ってしまっている。これを整数演算にしないと後はもう何もできない。
  • 振幅も可変にできるようにしたいが、和音とかSuperSawなら別にスレーブごとに音量を変えなくてもいいかのか?う~ん。

PSoC1


Tiny85とLPC810を代替案に考えていたが、PSoC1に8pinのものがあったのを思い出した。
CY8C24123A-24PXI 240円@秋月
I2CありSPIあり9bitDACあり14bitADCあり

いっぱいならべるとまあまあ高いが5個なら1,200円か。

2015年11月26日木曜日

リズムマシン ハードウェアでけた

はんだ付け終わった(^q^/

表面


LCDは結局普通のHD44780互換のパラレルのものを使った。

PSoC 5LPとLCDを外した状態


LCDの下に拡張用のSPI端子を出している。

はんだ面


2層目の配線にはポリウレタン線を使った。

シーケンサー基盤とつないで動作


USBケーブルは電源供給のためだけに使っている。


もう少しファームウェアをいじってからもう少し詳しい記事書きます。

2015年11月25日水曜日

ATtiny13でDDS波形出力の実験

前回の「ATtinyをいっぱい並べるオシレーターの妄想」からもう少し考えてみた。

マスターとスレーブのやり取りはシリアル通信かな~と思ってたが、アナログシンセの制御方法のCVとGATEをそのまま踏襲すればそれぞれ1線の計2線で済むのでは?

と、妄想が広がった。

IC間の通信をアナログ値でやるという(^q^;

ATtiny13の周辺機能にはシリアル通信のハードウェアはないが(SPIでプログラムできるのだから本当はあるんだろうけど、よくわからない)立派なADCが搭載されているのでこれを使うという妄想。

ATtiny13をVCOとして使う妄想図


アナログっぽいところは赤、デジタルっぽいところは青で色分けした。

CV、Gateというのは周波数と音量を設定する電圧値だ。ADCでデジタル値に変換してからマイコンで処理して、周辺機能のPWMで疑似アナログ値の波形を出力する。

これをアナログ回路のLPFでパルス波形(高周波数)を取り去ってアナログ波形を出力するというもの。

CV、Gateの電圧はマスターのAVR(とかPSoCとかLPCとか・・・)が計算して出力する。

入力側のClockはシステム全体の同期をとるためのクロック。

Latchはこの信号を受け取った時にスレーブ・オシレーター達がいっせーので出力電圧(PWM出力なのでPWMのDuty比)を切り替える。

Selectは今設定されているCV、Gateの値が自分宛のものか判断する(SPIのSSのぱくり)

メモ:


  • Selectはスレーブごとに信号線を割り当てるので工夫すればここにシリアル・データを乗せられるかも?
  • Latchで同期をとれればClockでシステム全体の同期をとる必要はないのかな?(よくわかりません)


ATtiny13AでDDS波形出力


とにかく周波数可変で波形を出力できないと妄想も深まらないので、今まで使ってきたDDSでテストプログラムを書いてみた。

 /*  
  * ATtinyVCO.c  
  *  
  * Created: 2015/11/25 14:07:46  
  * Author: gizmo  
  */   
 #define F_CPU     9600000ul  
 #include <avr/io.h>  
 #include <avr/pgmspace.h>  
 #include <util/delay.h>  
 #include <stdint.h>  
 #define SAMPLE_CLOCK     (8000.0f)  
 #define POW_2_16          (65536ul)  
 #define FREQUENCY_MAX     (2000.0f)  
 // Saw up wave table  
 const PROGMEM uint8_t sawUpTable[] = {  
      0,     1,     2,     3,     4,     5,     6,     7,     8,     9,     10,     11,     12,     13,     14,     15,  
      16,     17,     18,     19,     20,     21,     22,     23,     24,     25,     26,     27,     28,     29,     30,     31,  
      32,     33,     34,     35,     36,     37,     38,     39,     40,     41,     42,     43,     44,     45,     46,     47,  
      48,     49,     50,     51,     52,     53,     54,     55,     56,     57,     58,     59,     60,     61,     62,     63,  
      64,     65,     66,     67,     68,     69,     70,     71,     72,     73,     74,     75,     76,     77,     78,     79,  
      80,     81,     82,     83,     84,     85,     86,     87,     88,     89,     90,     91,     92,     93,     94,     95,  
      96,     97,     98,     99,     100,     101,     102,     103,     104,     105,     106,     107,     108,     109,     110,     111,  
      112,     113,     114,     115,     116,     117,     118,     119,     120,     121,     122,     123,     124,     125,     126,     127  
 };  
 volatile uint16_t phaseAccumlator;  
 volatile uint16_t tuningWord;  
 volatile uint8_t amp;  
 //=============================================================================  
 // 波形生成  
 //  
 // ----------------------------------------------------------------------------  
 // setDDSParameter()  
 // parameter: frequency: 生成する周波数  
 //  
 void setDDSParameter(float frequency)  
 {  
      tuningWord = (int16_t)(frequency * POW_2_16 / SAMPLE_CLOCK);  
 }  
 // ----------------------------------------------------------------------------  
 // generateWave()  
 // return: 出力値(0..255)  
 //  
 uint8_t generateSawWave()  
 {  
      uint8_t index;  
      phaseAccumlator += tuningWord;  
      // 右へシフト: 16bit -> 7bit(128個)  
      index = phaseAccumlator >> 8;  
      return pgm_read_byte(&sawUpTable[index]);  
 }  
 //=============================================================================  
 // 波形生成  
 //  
 // ----------------------------------------------------------------------------  
 // setPWMDuty()  
 // parameter: value: 設定するDuty比(0..255)  
 //  
 void setPWMDuty(uint8_t value)  
 {  
      // PWMのデューティー比を設定  
      OCR0A = value;  
 }  
 //=============================================================================  
 // ADC  
 //  
 // ----------------------------------------------------------------------------  
 // getCV()  
 // return: CV値(0..4095)  
 //  
 uint16_t getCV() {  
      // ADC_CVの値を取得  
      return 2048;  
 }  
 // ----------------------------------------------------------------------------  
 // getGate()  
 // return: Gate値(0..255)  
 //  
 uint8_t getGate() {  
      // ADC_GATEの値を取得  
      return 255;  
 }  
 //=============================================================================  
 // ラッチ割込み  
 //  
 //ISR()  
 //     generateSawWave()を呼び出し  
 //     amp値を乗算  
 //     PWM DACに出力値を設定  
 //=============================================================================  
 // メイン・ルーチン  
 //  
 int main()  
 {  
      uint16_t cv;  
      uint8_t v;  
      //-------------------------------------------------------------------------  
      // PORT設定  
      //-------------------------------------------------------------------------  
      DDRB = 0b00000001;     // PB0(OC0A): PWM out  
      // Debug用  
      //  
      DDRB |= 0b00000010;     // PB1  
      //-------------------------------------------------------------------------  
      // PWM設定  
      //-------------------------------------------------------------------------  
      // TCCR0A = 0;  
      // TCCR0B = 0;  
      //-------------------------------------------------------------------------  
      // 波形生成モード: WGM0: 0:1:1  
      // 高速PWM(モード3)  
      TCCR0A |= (1 << WGM01) | (1 << WGM00);  
      TCCR0B |= (0 << WGM02);  
      //-------------------------------------------------------------------------  
      // コンペア・アウトプットA: COM0A: 1:0  
      // コンペア・マッチでOC0Aクリア、TOPでOC0Aセット  
      TCCR0A |= (1 << COM0A1) | (0 << COM0A0);  
      //-------------------------------------------------------------------------  
      // クロック設定: CS0: 0:0:1  
      // 分周なし  
      TCCR0B |= (0 << CS02) | (0 << CS01) | (1 << CS00);  
      // TCCR0A = 0b10000011;  
      // TCCR0B = 0B00000010;  
      // 初期設定 1kHz Duty:50%  
      //  
      //setDDSParameter(1000.f);  
      //setPWMDuty(127);  
      for (;;) {  
           // Debug用: PB1をH  
           PORTB |= 0b00000010;  
           cv = getCV();  
           setDDSParameter(cv * FREQUENCY_MAX / 4096.0f);  
           amp = getGate();  
           v = generateSawWave();  
           setPWMDuty(v);  
           // Debug用: PB1をL  
           PORTB &= 11111101;  
           _delay_us(125);     // 8,000Hz  
      }  
 }  
 // EOF  


ADCの値取得とかLatchによる波形生成の同期は割愛した。

あとで修正/拡張/いじりやすいように冗長ぎみに書いておいた。


配線図

ADC入力は実装していないのでPWM出力とタイミング計測用の「Check」のみ。

ATtiny13のピンアサイン


PWM出力は使用によりP5(OC0A)かP6(OC0B)のどちらか。ATtiny13は8Pinなので周辺機能をいろいろ使おうと思うとPin割り当てをちゃんと考えないといけない。

今のところ
P1:ADC0 (Gate/analog)
P2: CLKI (Master Clock)
P3: ADC2 (CV/analog)
P4: GND
P5: PWM (Output)
P6: Select
P7: Latch
P8: VCC
という使い方にしようと思っている。

いろいろ計測


PWMの素の出力


Duty比50%の素のPWM波形で、周波数36.44kHzになっている。

9.6MHz(Fuse bit: l:7A h:FF)駆動させていてTimer0が8bitなので256カウントでオーバーフローするので

9.6MHz / 256 = 37.5kHz

PWMだと意外と低いもんだなあ(^q^;

ATtiny13を20MHz駆動させても78.125kHzだ。

この辺をストップバンドにしてLPFを設計すればいいのかな。よくわからないので実験しながら。

一応、DDSでノコギリ波らしきPWM波形の出力はできた。


浮動小数点演算


プログラムではサンプリング周波数8kHzでノコギリ波を出力させているが、途中で浮動小数点演算をしていてかなりメモリを食うみたいだ。浮動小数点演算をしているところをコメントアウトすると消費メモリー量がかなり減る。

浮動小数点演算ありのAtmel Studioのoutput

Task "RunOutputFileVerifyTask"
Program Memory Usage : 1014 bytes   99.0 % Full
Data Memory Usage : 5 bytes   7.8 % Full
Done executing task "RunOutputFileVerifyTask".

浮動小数点演算をコメントアウトしたAtmel Studioのoutput

Task "RunOutputFileVerifyTask"
Program Memory Usage : 290 bytes   28.3 % Full
Data Memory Usage : 5 bytes   7.8 % Full
Done executing task "RunOutputFileVerifyTask".

浮動小数点演算のルーチンは引き算すると700byteぐらいなのかな?

これを32bit整数演算に置き換えてメモリを節約できるかどうか。

メモ:


去年tozさんが書かれていた「tinyぴゅんぴゅん」の記事を読むとADCの待ち時間もなんか危なそうだ。

Gate(音量)の掛け算は整数演算でうまく処理できればいいが、単純な波形出力でもかなり制約があるのでムリなら後段のMixerでGateをうけて音量制御できるようにするとか・・・

素子数が増えると作る気なくしそう(TqT;

う~ん(^q^;;;;

2015年11月23日月曜日

PSoC 5LPでI2SのExampleをやってみた。

Code ExampleのInterICSoundというプロジェクト。

実際にI2Sのデバイスを動かすのではなく、PSoCのI2Sコンポーネントのsdo(データ出力)からsdi(データ入力)にループバックさせるというものだった。

CPUからI2SへDMAを経由してデータ転送するあたりに重点が置かれている。実際PSoCのコンポーネントのなかでもDMAは使い方がちょっとややこしい感じだ。

ブレッドボード図


PSoC 5LPのボードは、「基板付き体験編 ARM PSoCで作るMyスペシャル・マイコン」の付属基板を使った。

Exampleでデフォルトで指定されているPINを変更せずにそのまま配線した。

PSoC CreatorのPins


PSoC CreatorのTopDesign


動作させるとLCDに送信データと受信データが表示される(ループバックされているので同じ値)

タクトスイッチを押すとデータの1byte目がインクリメントされる。

ループバックの配線(ブレッドボード図の青いケーブル)を抜くと、RXがTXと同期しないで「RX: FF FF FF FF」と表示されるので、細かいことはわからないがまあ動作してるんだと思う。

LEDがつながっているClipはRXがClipしたらうんぬんと書いてあるが、まだちゃんとDATASHEETを読んでないのでよくわからない。タクトスイッチを押しっぱなしにしていると点灯するようだ。

<追記:2016.02.28>

ClipはI2SコンポーネントがRXモードで受信したときにConfigureダイアログで設定した値内に収まっていない場合を判定するようです。レベルメーターで赤色にするのに使うとかなのかなあ?実際に試していないのでメモ

</追記>

I2Sの信号


測定中



I2SコンポーネントのSCLKとWSは配線されていないので、ブレッドボードに引き出してオシロで確認してみた。

SDO/SDI



ch1:SDO/SDI ch2:SCLK

SCLKの周波数が約3MHzになっている。I2Sの設定画面にはI2CやSPIみたいに、Data Rateを指定するところがない。

<追記>
TopDesignをよく見たらI2SのコンポーネントにClockを入力するところがあった(^q^;;;
6MHzのクロックが入力されて3MHzのSCLKが出ているようだ。
</追記>

I2SのConfigure-General


I2SのConifgure-Advanced


う~んわからない(^q^;

WS



WSはConfiureで64となっているが、オシロの計測値を見ると
10.680us / 160.0ns = 66.75
なので数が合わない。2bitぐらい何か挿入されているんだろうか?

今月号の「Interface (インターフェース)」にI2Sの記事が載っていたので買ってきた。

近所の本屋にはトラ技は置いてないのにインターフェイスは毎号おいている。インターフェイスの方が売れてるのかな?

ともかくもう少し調べて見る予定。

PSoC 5LPでSPI DAC(MCP4922)を動かす。

PSoC 4とPSoC 5LPでは使えるSPIのコンポーネントが違うので簡単にテスト。

ボードは「基板付き体験編 ARM PSoCで作るMyスペシャル・マイコン」の付属基板を使った。

ブレッドボード図



PSoC Creator Top Design


PSoC Creator ピンアサイン


<main.c>

 #include <project.h>  
 #define TX_DATA_SIZE  (2u)  
 int main()  
 {  
   uint8 txBuffer[TX_DATA_SIZE];  
   CyGlobalIntEnable; /* Enable global interrupts. */  
   SPIM_Start();  
   for(;;)  
   {  
     uint16 i;  
     for (i = 0; i < 4096; i++) {  
       // Highバイト(0x30=OUTA/BUFなし/1x/シャットダウンなし)   
       txBuffer[0] = ((i >> 8) & 0x0f) | 0x30;  
       txBuffer[1] = i & 0x00ff;   
       Pin_LDAC_Write(1u);  
       SPIM_PutArray(txBuffer, TX_DATA_SIZE);  
       while (0u == (SPIM_ReadTxStatus() & SPIM_STS_SPI_DONE)) {  
         // wait  
       }  
       Pin_LDAC_Write(0u);  
       CyDelay(1u);  
     }  
   }  
 }  

1usのサンプリング間隔でノコギリ波を出力させている。

出力波形


24MHz駆動、 SPI Data Rate 4MHz


まずは基本的なスペックで動作させてみた。


ch1:MOSI ch2:SCLK


ch1:CS ch2:SCLK


ch1:LDAC ch2:SCLK

LDACはプログラムで出力しているので、SPI通信の処理時間はコミコミで6.960us。
while (0u == (SPIM_ReadTxStatus() & SPIM_STS_SPI_DONE)) {
    //wait
}
と、SPI通信が完了するまでWaitしているが、この処理を取り除くと


ch1:LDAC ch2:SCLK

SPI通信が終わる前にLDACがLに落ちている。←これではダメなので待ち処理は必要だ。


60MHz駆動、 SPI Data Rate 20MHz


MCP4922は仕様では20MHzまで動作するのでSPI Clockを上げてみた。

PSoC 5LPのMaster Clockは以下のとおり設定。



ch1:MOSI ch2:SCLK

SCLKの周波数は、表示では「(2)15.03MHz」となっている。波形数えてみても5区間で波形8個なので
100ns * 5 / 8 = 62.5ns
1 / 62.5ns = 16MHz
SPI Masterコンポーネントでは20MHzに設定しているので謎。


ch1:CS ch2:SCLK


ch1:LDAC ch2:SCLK

SPI通信にかかる時間を2.412us(だいたい414.6kHz)まで減らせた。

2015年11月21日土曜日

mbedでLチカ Nucleo-F401REでfrdm_gpioを動かす

去年Nucleo-F401REを買って使い方がよくわからないのでほっておいたが、cortex M4なので少し調べてみることにした。

mbedの使い方は

https://developer.mbed.org/compiler/

にアクセスして「インポート」でサンプルプログラムをインポートして「コンパイル」して落ちてきた「*.bin」ファイルを、mbedターゲットをつないだ時に現れるフォルダにDrag&Dropすればいい。

これで、「mbed_blinky」や「Nucleo_blink_led」のLチカはできる。→オン・ボードのLEDがチカチカする。

が、その後がよくわからないのでほっておいた。

「Nucleo_pwm」を開いてみると「PWM_OUT」という定義っぽいものが出てきて、さらっとprintf()も出てくる。printf()はUART通信で文字列を表示するのかな?と思うが、「PWM_OUT」というのがどこで定義されているのか?

と思って探してみたら

https://developer.mbed.org/platforms/ST-Nucleo-F401RE/

の「Information」の囲みのところで
PWM_OUT=PB_3
となっている。これかな?

main.cppを見ると10ms間隔で1msのパルスを発生させる様な感じだ。
今日はオシロをしまってしまって引っ張りだすのがめんどくさいのであきらめた。

frdm_gpio


少しソースをいじったら動作した。


このexampleはオン・ボードのLチカとGPIOのPINチカ(?)のプログラムのようだ。



デフォルトでは
led: LED_RED
gpo: D0
と定義されている。

「Nucleo-F401RE」では「LED_RED」はないと怒られるので「LED1」に変更。gpoも「D0」だと動作しないので「D12」に変更。

ArduinoのPIN番号で指定するのかな?


LED1がD13に割り当てられてるのもArduino互換な感じだ。

メモ:


最近cortex M7というのが出たみたいだ。STM32 F7 Discovery気になるがとうてい使い切れそうにないので眺めるだけにしておきます(^q^;

ATtinyをいっぱい並べるオシレーターの妄想

ATtiny13のデータシートを眺めながらもう少し妄想。


スレーブのATtinyが何をすればいいのかというと、アナログシンセの制御方法にあやかって、CV(周波数)とGate(音量)を受け取って波形を出力する。

図でいうと「(3)データ」でCVとGate(とその他もろもろ)をマスターからスレーブに送る。ラッチの間隔がサンプリング周波数になる(と思う)

スレーブは与えられたCVとGateから出力波形を計算して「(2)ラッチ」が送られた時にPWMで電圧を出力する。

いっぱい並べるので(5~7個ぐらい?)ピン数が多いと実装がしんどそうなので8Pinぐらいがよさそう。

矩形波はPWMで大丈夫そうだが、ノコギリ波もやりたいのでDDSの波形生成も実装したい。内蔵のFlash ROMが足りない場合は外付けでEEPROMをつけて、それぞれのスレーブで共有して要求にしたがってSRAMに読み込んで使う。

電源に2PIN、I2Cに2PIN、クロックに1PINとして、のこりは3PINなので8PINのマイコンを使おうと思うとこれでぎりぎりだ。

ATtiny13のスペック


ATtiny13のFlash ROMは1kBなのでプログラムで消費する量を考えると128サンプルぐらいにしないと波形テーブルは乗せられなそうだ。

I2CのEEPROMを使おうと思っても、USI(シリアル通信用の基本のハードウェア)もない。

調べてみるとATtiny13にはタイマー(PWM)が1つしかない。PWM出力でTimer1個、波形生成でTimer1個、合計2個使いたいたいのでちょっと困った。

→Timerはやりくりすれば1個で済ますこともできそうだが、自信なし(^q^;

マイコンの選定


8Pinであまり高くないやつ

ATtiny85

秋月で160円、マルツで215円
PWM用のTimerと別にもう一つTimerがついている。USIあり。表面実装タイプもあり(実装面積を節約できそう)。

LPC810

秋月で80円、マルツで75円
まだ使ったこと無いけどARM CortexM0でこの値段は安すぎる気がしないでもない。一度使ってみようかな。

2015年11月20日金曜日

リズムマシン デジタルフィルタ処理

リズムマシンのメイン基板のPSoC 5LPにデジタルフィルタの処理を追加してみた。

今のところ8kHzのサンプリング・レートでやっている。

サンプリング間隔のうちどれぐらい時間がかかっているかオシロで見てみた。

PSoC Creaatorのプロジェクト
https://github.com/ryood/RhythmMachine/tree/master_DFB_test/Master_PSoC5LP_Test/PSoC/Sequencer_Master/Sequencer_Master_PSoC5LP_ProtoKit_HD44780.cydsn

波形生成の処理時間


ch1:ISR_Check ch2:Timer_Sampling_TC

ch2はサンプリング周期でパルスを発生させて補足したもの。ch1は波形生成処理の前後でH/Lさせている。

サンプリング周期は0.126ms(126us)で波形生成は60.400us。8kHzだと約半分で処理が完了しているようだ。

プログラムはほとんど最適化していないので最終的にはサンプリング周波数をもう少しあげられるかもしれない。

デジタル・フィルターの処理時間



ch1:ISR_Check ch2:Ext_Check

ch2はデジタル・フィルタの処理の前後でH/Lさせたもの。2usで済んでいるようだ。

メモ:


  • 8kHzのサンプリング周波数だとカットオフ周波数は4kHzに制限される。ナイキスト周波数だからしかたない。ノイズにHPFをかけてみたがかなり変な音になってしまった(^q^;
  • PSoC 5LPのFilterコンポーネントは実行時にパラメータをいじれないようだ。カットオフ周波数ぐらいは変化させたかったので残念。
  • 自力でDFBをいじればできるかもしれないが、DSPプログラミングはやったことがないので保留。BlackFinをいじってからまた考えよう。
  • フィルターをCPUでプログラミングすればパラメータをいじれそうだが、確実に小数点演算なので実行時間がどれ位かかるか・・・
    →固定小数点演算でできれば現実的な範囲かな?


2015年11月18日水曜日

リズムマシン メイン基板 部品並べ(メモ)

拡張用SPIは外付けDACを想定。SPI用に電源ラインがあと2Pいるのか・・・

SPI経由でグラフィックLCDの実験をするためにはSPIのスレーブセレクト用にプラス2Pぐらい入れた方がいいかな?


メイン基板とシーケンサー基板


PSoC 5LP Prototyping KitでChar LCD(HD44780)を使う まとめ

PSoC 5LP Prototyping Kitのポートによって動いたり動かなかったりするので一通りチェックしてみた。

ExampleのCharLCD_CustomFontでチェック。

配線図(P2[6:0])


接続表(P2[6:0]])


結果


P2とP12ではうまく動くが他は動かせなかった。

使ったLCDは共立で買ったHD44780互換のやつです。

動作


文字化け