ラベル DSP の投稿を表示しています。 すべての投稿を表示
ラベル DSP の投稿を表示しています。 すべての投稿を表示

2024年2月10日土曜日

BlackFinを使ってみるなり(33) 開発環境CCESがlicence expiredで....

ハードコアリフォームが終わりそうなので、またDSP開発のセカイに舞い戻ろうとするわたし.

BlackFinに電源投入するのは2ヶ月ぶりだと思う.開発環境ccesを起動する.

だが、ccesがlicence expiredになってしまう.
おかしいな、評価ボードのライセンスは1年間じゃなかったっけ?
調べたら180日でした.それなら切れる頃だろう.

しかしなー、ccesが動かないんじゃ困るって.さよならBlackFinなのか?

評価ボードを2台買ったので、2台目に付属の評価ライセンスを打ち込んでも無駄.なぜなら同じナンバーだから.

これは、、、詰んだ.

ccesのLicence managerを開くと、licence fileの置き場がわかる.
 C:\ProgramData\Analog Devices\CrossCore Embedded Studio
ここにlicence.datがある.

このlicence.datを消してみた.(正確には移動した)

そしてccesを再起動.

licence managerにそれまで表示されなかった選択肢が表示され、ゴニョゴニョといじったら、再び使えるようになりました.

ただし不穏なものも見え隠れしています.
 「Status: Evaluation, Validated, Expiring in 90 days」
90日間に短くなってるんです.
最初にインストした評価ライセンスは180日でしたから、半分になってる.
180→90→45→22→11日とかどんどん短くなっていったら怖いな.

結果は3ヶ月後、5月10日に判明するのであった.
ちなみに、.dat fileによると最初の評価ライセンスは8月5日~2月1日だったみたいよ.

かしこ

2023年12月19日火曜日

BlackFinを使ってみるなり(32) arduinoでW25Q32 FLASHをうごかした件

arduinoでW25Q32を動かしました.SPIね。

arduino IDEのsketchはこちらです.消したり書いたり読んだりしてserialに結果を出力しているsampleです.

arduino nanoを使いまして、結線はこうなってます.

sketchを見てもらうと、WPとHOLDを制御するのを忘れてます.pull upされてるので放置してても動いてる無防備な状態.

W25Q32を動かしてみて、へーそうなんだと思ったこと.

1)W25Q32にはいろいろなwrite protect機能がありますけど、工場出荷時ではそれらは全部OFFになってる.なのでsketchでは「WPを解除するのに苦労」とかしてません.無防備な状態です.

2)writeやeraseする前に、write enable commandを送信する必要があります.それは理解できるんですけど、、、write動作が終わった後で、WEが自動的に解除されます.なので、さっきWEしたじゃんと思っていると書けずにハマります.

3)chip eraseには10秒ぐらいかかるので、busy flugをpollする必要があるでしょう.

4)write commandは、1Byte~256Byteまで任意長の一括writeができます.「page program」と呼ばれています.

5)page programは、address 0番地から256Bytesだと切りが良くて円満ですが、pageをまたぐ半端なaddressだと厄介みたいよ.datasheetあんまり読んでないのだけど、address[7:0]が中途半端な番地からpage操作するのはやめといたほうがいい感じ.

そんなところかな.
地雷回避は各位でよろしくでぇす.

31へ    33へ

かしこ

2023年12月4日月曜日

BlackFinを使ってみるなり(31) arduinoでW25Q32を読み書きしたい

BF592のプリント基板を作る下準備を始めました.

blackfinのprogrammingにはいくつかの方法がありますが、自作PCBではSPI slave bootというのを使おうと思います.

↓blackfinの王道的programmingブロックはこれでしょう.boot時のBF592はSPI masterになってW25Q32というSPI serial FLASHからbinaryを読みます.

↓SPI slave bootではこんな絵図です.arduinoでなくても良いのですが、ここではarudinoを使います.arduinoがSPI masterになり、W25Q32に在るbinaryをBF592に転送してくれます.BF592はSPI slaveです.
↓実際にはW25Q32を焼くためにPCと接続せないかんですから、arduinoのUSB-COMでPCと繋ぎます.

わたしはserial FLASHを制御した経験が無いのでまずはarduino-W25Q32のSPI通信を動かしてみます.
↓そのための基板を発注したところ.JLCPCBへ.

リフォームで忙しいのですがボチボチ火入れします.


かしこ

2023年11月28日火曜日

BlackFinを使ってみるなり(30) ライブラリのFFT、これクセあるわぁ

blackfinライブラリのFFTを動かせました.かなりクセあるなぁ.

今回の情報ネタ元はここです.ライブラリのサポート情報は有料なのかな? netの情報が少ないです.

じゃぁクセの強いところを掘り下げてゆきましょう.

1.FFTを動かすのに必要なwork memory

fract32 fft_input[FFT_LENGTH];
fract32 fft_window[FFT_LENGTH]; // window function
complex_fract32 fft_twiddle[FFT_LENGTH/2]; // sin(2pi/N*k)
complex_fract32 fft_output[FFT_LENGTH];
fract32 fft_magnitude[FFT_LENGTH];

FFT_LENGTHは256とか512とかです.FFTですから2のべき乗.

fft_window[]はハミング窓とかの格納です.

fft_twiddle[]は、フーリエ変換でsin(2πf/N*k)みたいなの使うじゃないですか、それをあらかじめ計算しておいて格納しておきます.

fft_output[]は複素数で出てきますので、√(re*re+im*im)しなくちゃグラフになりません.

fft_magnitude[]は√(re*re+im*im)の結果を格納します.


2.FFTを動かす手順

1)あらかじめハミング窓を計算しておく.
gen_hamming_fr32(fft_window, 1, FFT_LENGTH);

2)input配列にハミング窓を掛け算する.
__builtin_mult_fr1x32x32()は32bitx32bit=32bitの掛け算をしてくれる関数です.ご提供してくださる関数を使いましょう.
for(int i=0; i<FFT_LENGTH; i++) {
 fft_input[i] = __builtin_mult_fr1x32x32(fft_input[i], fft_window[i]);
 }

3)sin(2πf/N*k)を計算しておく.
twidfftrad2_fr32(fft_twiddle, FFT_LENGTH);

4)FFT関数が出力する数値.256長なら8となる.512長なら9となる.
int block_exponent; // log2(FFT_LENGTH) = log2(256) = 8

5)32bit FFT本体.引数の1の意味はよくわからん.
rfft_fr32(fft_input, fft_output, fft_twiddle, 1, FFT_LENGTH, &block_exponent, 1);

6)√(re*re+im*im)を計算する.
fft_magnitude_fr32(fft_output, fft_magnitude, FFT_LENGTH, block_exponent, 1);

7)dBに直して出力する.X範囲はfnyqまでなので/2してますが各位お好みで.
for(int i=0; i<FFT_LENGTH/2; i++) {
 print(20*log10(fft_magnitude[i]));
}

以上です.

わかってしまえば楽なんですけど、関数定義されているfilter.hを読んでも関数の使い方はなにも書かれてないんです.深い闇だ.

29へ    31へ

かしこ

BlackFinを使ってみるなり(29) 間引きフィルタ

前回はFIR filterを実装してみました.

今回は、blackfinライブラリのfilter.hにある間引き機能(decimation)を動かしてみます.

まず間引かない普通のFIRのおさらいをします.

用意するもの.構造体、FIFO.
_fir_fr32_state firparam;
fract32 fir_input[64], fir_output[64];
fract32 fir_delay[2 * FILTER_TAP_NUM];
64の意味は、FIRに64 sampleのデータを一括入力するという意味です.出力も64 sampleが出てきます.

FIR構造体を設定します.
fir_init(firparam, filter_taps, fir_delay, FILTER_TAP_NUM, 1);
引数1 構造体
引数2 タップ配列
引数3 work area タップ数の2倍
引数4 タップ数
引数5 間引かないので1

FIRを呼び出します.
fir_fr32 (
 fir_input,
 fir_output,
 64,
 &firparam  );

ーーーー
次は、1/32に間引くやりかたです.

つまり、ADCはsampling freq. 32kHz ですが、これを1kHz sampleに変換するという意味です.

用意するもの.構造体、FIFO.
_fir_fr32_state firparam;
fract32 fir_input[64], fir_output[2];
fract32 fir_delay[2 * FILTER_TAP_NUM];
outputサイズが2になってます.理由は、1/32に間引くので64個が2個に減っちゃうからです.

FIR構造体を設定します.
fir_init(firparam, filter_taps, fir_delay, FILTER_TAP_NUM, 32);
引数5 1/32に間引くので32

FIRのdecimationバージョンを呼び出します.
fir_decima_fr32 (
 fir_input,
 fir_output,
 64,
 &firparam  );

データを間引いたメリットはこうです.
間引く前:
ADCのcallbackは32kHzの64サンプル分=2mSec周期です.2mSecで64個のデータを処理しなくちゃいけません.

1/32に間引くと:
2mSecで2個のデータを処理するだけですのでとても軽くなりました.いろんなフィルタ処理をやりやすくなるでしょう.


次はFFTやってみる.

28へ    30へ

かしこ

2023年11月27日月曜日

BlackFinを使ってみるなり(28) FIR filterライブラリ

ADが提供する演算ライブラリがいろいろあります.マジで便利そう.使えればですが.

さしあたってやりたいのが、32ksps→1kspsぐらいに間引きたい.単純に1/32に間引くとエイリアシングが出てしまうので、500HzのLPFを通しておいてから1/32に間引くというのが作法です.

LPFはFIR filterで構成します.FIR filterはhardwareでやるにせよ、softwareでやるにせよ、大きな計算量が必要です.結局のところ、間引き(decimation)というのはFIR filterが演算量の大部分を占めるってことになります.

ADのccesのインストフォルダを探すとfilter.hというのがあってその中にFIRやIIRのプロトが書かれています.

typedef struct{
  fract32 *h; // coefficients
  fract32 *d; // start of delay line
  fract32 *p; // read/write pointer
  int k; // number of coefficients
  int l; // interpolation/decimation index
} _fir_fr32_state;

void fir_fr32 (
   const fract32 _input[],
   fract32 _output[],
   int _length,
   fir_state_fr32 *_filter_state);

あーそんな感じねと察します.でもsample programが無いとちょっとかったるい.delay lineの目的が不明です.検索したけど見当たりませんでした.

自力でやるのかくそう・・・・

ーーーー
それでやってみたのですが、意外と簡単にうごかせたかな.

1.FIRのタップ係数を決める

ライブラリとは別なのでこれを最初に説明するってのもどうかと思いますが、こちらのサイトが便利です.好みのfilter特性を入力すると、タップ係数を出力してくれます.
自力で逆フーリエ変換してもタップ係数を算出できますけど、めんどくさいので自力計算はやめときました.

やたらと厳しいfilter特性を指定するとタップ係数が長くなります.タップ係数が長くなると計算時間がかかってDSPが処理できなくなります.BF706で500タップを実装したらhung-upしちゃいました.200タップなら動きました.

同サイトはタップ係数定義を吐き出すので、source codeにコピペしましょう.
キモは、
・32bit FIRならば、、、
・32bit intで係数出力させる(ライブラリ内ではfract32と型表記)
・係数のgain調整はしなかった

#define FILTER_TAP_NUM 85

static int filter_taps[FILTER_TAP_NUM] = {
  -11666725,
  -4238744,
  -4883710,
  -5507981,
  -6093541,
  -6612955,  以下略

2.FIR filterにタップ係数などを設定する

blackfinのライブラリを使います.定義はfilter.hに書かれてる.

_fir_fr32_state firparam;   ←FIRのパラメータ構造体

構造体にFIRのパラメータを設定する.
fract32 delay[2*FILTER_TAP_NUM];
fir_init( firparam, filter_taps, delay, FILTER_TAP_NUM, 1 );

引数の意味はなんとなくわかるけど、、、
最凶に判らないのがdelayというもの.タップ数の2倍長のwork areaを与えてあげると正しく動く.→こちらが参考
末尾引数1は、間引き数、補間数なのでここでは1でいいかんじ.0じゃない.

3.FIR filterをうごかす

わたしは初めてDSPを使っているのですが、どーゆー風に実装するのかなぁ?とモヤモヤしていた事があります.

というのは、、、verilogでhardware FIRを設計する場合は毎サンプルに1つデータが入って来ます.それをガシャンと演算します.FIR pipelineから1つのデータが出力されます.これは判りやすい.設計経験もあります.

しかしDSPでは、ADCから入ってくるデータはFIFOに格納され、数~数10サンプルのブロックでデータが到来します.これをFIRにガガガッと演算させ、ブロックで出力される.ガガガッの最後には仕掛データが在庫のようにFIR内部に溜まっている.こんなの実装するのどうするのかな、かったるい、そんな風に思っていました.未体験ゾーンだし.

だがしかし、blackfinライブラリを使ったらすんなり動いたので中身がどうなっているのかは判らずじまいでスルーします.

それでFIR処理のdata flowはこんなです.
・hardwareは、BF706のI2Sに32bit ADCと32bit DACがついてる
・起動時にI2Sに「ADC FIFOが満タンになったらcallbackしてね」と設定しておく
・参考までにADC FIFO長は64サンプルにしてる(32bit 2ch 64sample=512BYTE)
・callbackが来たら以下の処理を行う
 1)FIFOからdataを取り出し、fract32の64個配列に格納
 2)同配列をFIRライブラリへ突っ込む
 3)FIR出力が出てくる (fract32 64個配列)
 4)出力配列をDACのFIFOにコピー
 5)DAC FIFOをI2S DMAにおまかせする

fract32 input[64], output[64];
for(int i=0; i<64; i++){
 input[i] = *pBufRx;  ←ADC FIFO  (1)
 pBufRx += 2;
 }
 fir_fr32 ( input, output, 64, &firparam );(2)(3)
 for(int i=0; i<64; i++){
  *pBufTx = output[i];  ←DAC FIFO (4)
  pBufTx += 2;
 }

以上でさらっとおしまい.

#ライブラリにはdecimationもあるのでそれも試します.

すいませんが、project folder詰め合わせのupは諸般の事情でやめときます.ボケた写真でも載せておこう.右下がBF706です.

27へ    29へ

かしこ

2023年11月24日金曜日

BlackFinを使ってみるなり(27) BF592 slave bootのSPY1_RDYとHWAIT

すごく重箱の隅です。

BF592のプリント基板の回路図を描いてるとこです。

ーーーー
SPI_RDYって知ってます?

SPIにそんな信号あったかな? MISO,MOSI,SS,CLKの他にRDYですとぉ? フロー制御のための信号です.

今朝の電車でBF592 BF706のhardware referenceを読んでいて、SPI SLAVE BOOTの項で、SPI_RDYを使うと書かれている。

BF592を使いたいのにBF706 datasheetを読んでるのが狂っているのですが、peripheralは同じだろ、という理由です.

それで、BF592 datasheetをRDYで検索したのですがhitしません。そんなpin無いじゃんと戸惑い。

一方でBF706 datasheetだとSPI_RDYがhitします。
「GPIO MULTIPLEXING」の項です。外部pinの切り替え機能ですね。よくある話で納得です。

もしかしてBF592ではGPIO MULTIPLEXINGが使えないのかと思ったけど、そんなまさかね。BF592 datasheetにはGPIO MULTIPLEXINGの文字ならあります。
なのですが、同一覧表が存在しないんです。なんじゃその手抜きは?
そのせいでSPI_RDYがdatasheetに出て来ないと思われます。

ということは、家に帰ってccesを立ち上げて、BF592のpin assign画面を見ればSPI_RDYが見つかるはずです。

結果は後ほど。

ーーーー
というわけで、ccesでBF592のprojectを作ってpin multiplexingの画面がこれですが、これじゃわかんないよ.SSELのどれかがSPI1_RDYなのだろうけど、わからないー
詰みました.

ーーーー
改めて、BF706とBF592のhardware referenceを読み比べてみたら、、、SPI SLAVE BOOTのときには、
BF706ではSPYx_RDY pinをつかう.
BF592ではHWAIT pinを使う.
というように仕様が違いました.peripheralは同じだろと思ってたが甘かった.
BF706
BF592

というわけでオチがついたところで、お次がよろしいようで・・・・

26へ    28へ

かしこ

2023年11月16日木曜日

BlackFinを使ってみるなり(26) BF592 datasheet

通勤電車でBF592のDstasheetを読んでの感想など。

よく使ってたSTM32F2xxシリーズと比較しての話だが、BF592って小規模なんだなぁという印象が強い。

64pinQFN。内部clk400MHz。


timerが3つしかない。わたしゃtimerをポコポコ使う人なのでたった3つかぁって思ふ。例えば電源indicatorだけでtimer1ヶ占有みたいな事をしてしまう。まぁstate machine回せばtimerの少なさを回避できるので別にいいんだけど。ちなみにSTM32F2xxはtimerが8個だったかな。
timerにはPWMがつきものだけど、遊びで動かした事があるぐらいで実用で使った経験が一度もない。わたしはpower系を全然やらず専ら信号処理だからPWMには縁がないわ。

I2SでAD/DAを接続するSPORTは2ヶ。AD/DAを8個つなげるのでよろしいでしょう。

USRTは1個。
これは禍根を残すかもしれない。debug用に1つは使うのと、slave bootでUART使うらしいんだよなぁ。足りなくなるのかな。
slave bootはSPI1でした。.ldr fileをSPI1から流し込んでやるらしい。.ldrはccesで生成出来るのだろう。
SPI1から流し込んでいる最中にHWAITがassertedなら流し込みを一時停止せよと書かれている。

USBは無い。要らない。

SRAMはinstruction 32k、data 32kとなっており、小さいねぇ。STM32F2xxでは96KBをFIFOとして使いたい放題みたいな使い方しとった。

instruction ROM 64kってFLASHなのか? ISPでこれを使えばいいのにどうして外部serial FLASHをみんな使ってるのかな?
後の方にはROMについてこんな事が読み取れる。
・マスクROMっぽい
・メーカーカスタムでuser codeを入れれる(FLASHではないみたい)
・通常はとあるlibraryが在中
・ROMからstartするmodeが用意されている

middlewareでRTOSもあるけど有償みたいよ。

耳寄り情報。user自身が設計した回路でJTAG debuggerを動かすguide documentがある。→こちら まだ読んでない。

回路は金子システムのこちらが参考になります.

この手のICはpin数が多くて半田付けが超大変なケースが多いけれど、BF592はたったの64pinなのでらくちんでいい.その反面、電源とJTAGとCLKなどを除くとuserに解放されているのは30pinぐらいしかない.外部IFはやりくりに苦労するかもしれない.

25へ    27へ

かしこ

2023年11月9日木曜日

BlackFinを使ってみるなり(25) BF592来たよ

DSP基板を自作しようかと思います.

AD製の評価ボードはBF706搭載ですが、廉価なBF592を中華通販で入手しました.記憶がウロではありますが、USBの有無が両者の違いだったかなと.
QFNパッケージは足ピン曲がりを気にしないでよいのでハンドリングは安心です.半田付けは結構大変です.

さて、回路図を描こうか.

24へ    26へ

かしこ

2023年11月4日土曜日

BlackFinを使ってみるなり(24) 自作BlackFin PCBのbootどうする?

DSPのBlackFinを使っています.前回のupは2023年9月19日でしたからお久しぶりです.個別の設計に入っているためブログに書けないのでステルスになっています.

これはBF706の評価ボードです.1.5万円ぐらいと評価ボードにしては安価な方と言えるでしょう.2枚目を買いました.これにAD/DAを取り付けたりしています.

いつまでも評価ボードを使ってるわけにもいかないので、いつかは自作基板で動かさなくちゃいけません.BF592という下位ICは中華通販の最安ですと700円ぐらいで入手できるのでBF592でPCBを自作しようと思っています.
しかしPCBを作ったとしても、ISPとbootをどうすんだよという問題があります.

bootについて言えば、、、BF706評価ボードにはserial FLASHが乗っていて、BF706が起動するとserial FLASHからトロトロ読んで自分をprogrammingします.

ところが、serial FLASHをISPするやり方が判らない.BF706評価ボードには専用のICE chipが載っていて、ICE1000というのがそれです.これを使ってFB706と協働して、FB706のSPIに接続されたserial FLASHをISPしているらしい.複雑なことしててblack boxみたいよ.

BlackFinのprogrammingについての草の根情報ではこのようなものがありますが、これとてJTAG HWが品切れだとか書かれています.こちらのページはFT232HLを利用するやり方が書かれています.現実的だけどwin10で動くのかな?

困りましたね.PCB自作しても焼けねぇとか泣きじゃん.

BlackFinのboot方法はこの3つです.
 1)serial FLASH
 2)SPI master CPU
 3)UART
BF706評価ボードに実装されているのは1です.

3だと電源OFFで揮発しちゃうのでいまいちです.

2は、arduinoを脇に載せてarduinoにbootの手助けをしてもらえばいい.2を試してみようかなぁ.
2をやるとして、BlackFin開発環境のccesが2のbinaryを生成してくれなくちゃ始まりません.ccesのpropertiesを確認すると「SPI slave」という選択肢があります.これが2のbinaryを生成する設定でしょう.
というわけで、BF592自作基板を作って2番のISPを試してみようと思います.成果があったらupします.


かしこ

2023年9月19日火曜日

BlackFinを使ってみるなり(23) ADC/DACをI2S接続詳細

BlackFin BF706評価ボードに、TIのADC PCM1808を取り付けて、loopbackでDACでモニタする機能を実装しました.
 PCM1808 96kHz 24bit 秋月で売ってるもの

DACはPCM510xです.

BF706へのADC/DACの接続の考え方はこちらで書きました.
骨子は、、、
・ADCがmasterになります.ADCがBCK,LRCK,DATAを出力
・DACはslaveです.BCK,LRCK,DATAを入力される
・BF706のI2Sは、SPORT-AがDAC用、SPORT-BがADC用
・SPORT-AはBCKを入力される、LRCK,DATAを出力する→DACへ
・SPORT-BはBCK,LRCK,DATAをADCから入力される
CrossCoreのIOアサイン画面の様子です.SPORT1を使いました.

PCM1808の回路はこんなです.

ーーーー
BF706のfirmwareについて以下で説明します.key pointは、
・SPORTをI2S modeに設定する
・I2SをDMAで動かす
・Buffer関連のeventは割り込みのcallback関数で受け取る
・Tx Buffer(DAC)とRx Buffer(ADC)がある
・Bufferを上手く操作してADC→DACへloopbackする
・DAC Lchに内部発生信号を出力し、それをADC Rchで変換し、DAC Rchにそのまま出力する、というloopback動作をさせています

project folder詰め合わせはこちらです.(zip)
たぶん何かしらbugってます.免責でよろしく.

ではprogramの説明でぇす.なにから説明しましょうかねぇ?

SPORTをI2S modeに設定する
ライブラリの使いこなしです.

void h_wavegen_I2S_start(void)  ←この関数の中身です

int div = (100000000 / paramL.bck) + 0.5 - 1;
これはBCK周波数をSPORTに発生させるための分周比を計算します.SPORTのclockは100MHzです.サンプリング周波数96kHzとすると、BCKはその32bit 2chで64倍ですから6.144MHzです.分周比=100/6.144≒16と計算されます.なんつってこのdiv使わないんですがね.

まずはSPORT1-B Rx ADC用の設定です.
adi_sport_Open(1, ADI_HALF_SPORT_B, ADI_SPORT_DIR_RX,  ADI_SPORT_I2S_MODE,  DeviceMemory_B, ADI_SPORT_DMA_MEMORY_SIZE, &hDeviceB);
まずはopenです.引数がいろいろあります.
1: SPORT1の意味
ADI_HALF_SPORT_B: SPORT1-Bの意味
ADI_SPORT_DIR_RX: 受信の意味.すなわちADC.
ADI_SPORT_I2S_MODE: I2Sの意味
DeviceMemory_B: callback関係のwork memoryだけどとくに使ってない
ADI_SPORT_DMA_MEMORY_SIZE: 同work memoryのsizeで、ヘッダファイルの定義を踏襲する.こいつをNULLとかにすると静かに暴走するので要注意です.
hDeviceB: openしたハンドラのポインタ

adi_sport_ConfigData(hDeviceB, ADI_SPORT_DTYPE_ZERO_FILL, 31u, false, false, false );
31という数字は、ADCから32bitのサンプリングデータを受け取る意味.PCM1808は24bit ADCですが、I2Sは32bitでinterfaceしますので要注意.

adi_sport_ConfigClock(hDeviceB, div,false,false,false);
I2Sのclockを外部入力BCKにします.
1つめのfalseが外部入力BCKを使う意味です.
外部入力BCKを使うので、内部分周比のdivは意味ないです.0にするのもアレなのでなんとなくdivを引数に与えています.

adi_sport_ConfigFrameSync(hDeviceB, 31u, true,  false, false, false, false, false);
LRCKの設定をします.ここにも31という数字が登場します.
最初のfalseがLRCK入力の意味です.

adi_sport_EnableDMAMode (hDeviceB, true); DMA好きです

adi_sport_RegisterCallback(hDeviceB, SportCallbackB, NULL);
callback関数の登録.

adi_sport_SubmitBuffer(hDeviceB, nBufferRx0, SIZE_OF_RX_BUFFER);
受信準備.受信バッファとサイズを与えています.受信満タンになると割り込みがかかります.

state_sportB = H_SPORTB_ADC_TO_BUF0; 受信ステータス
adi_sport_Enable(hDeviceB, true); 受信開始

以上がSPORT1-B RxにADCを接続する設定です.

次にDACの設定をする前に、300uSec delayを入れました.
理由は、Tx Buffer処理とRx Buffer処理が時間的に近接してしまい、同時にcallback関数が呼ばれてややこしくなったら嫌だなと思ったからです.
h_delay_us(300);
なんで300uSecなのか?
Bufferサイズを512BYTEにしてあります.512/8=64個.96kHzサンプリングだと64個は666uSecに相当するので、666uSec毎にBuffer割り込みがかかる計算です.その中間で300uSecです.
delayにはTimer割り込みを使いました.その説明は後述します.

つぎはSPORT1-A Tx DAC用の設定です.
adi_sport_Open(1,ADI_HALF_SPORT_A, ADI_SPORT_DIR_TX,  ADI_SPORT_I2S_MODE,  DeviceMemory_A, ADI_SPORT_DMA_MEMORY_SIZE, &hDeviceA);
openはADCの設定と似たよなもの.

adi_sport_ConfigData(hDeviceA, ADI_SPORT_DTYPE_ZERO_FILL, 31u, false, false, false );
dataはADCの設定と同じかな.

adi_sport_ConfigClock(hDeviceA, div, false, false, false);
BCKはADCの設定と同じかな.

adi_sport_ConfigFrameSync(hDeviceA, 31u, true, true, false, false, false, false);
DACのLRCKはI2Sが出力します.

adi_sport_EnableDMAMode (hDeviceA, true); DMAオン、ADCと同じ

adi_sport_RegisterCallback(hDeviceA, SportCallbackA, NULL);
callback関数登録.ADCと似たよなもん.

adi_sport_SubmitBuffer(hDeviceA, nBufferTx0, SIZE_OF_TX_BUFFER);
送信準備.送信バッファとサイズを与えています.送信済で割り込みがかかります.

txbuf01=0; 送信ステータス
adi_sport_Enable(hDeviceA, true); 送信開始


delay timer
one shot timerでdelayを作りました.
これもcallback関数でdelay終了を検知しています.

設定方法.
adi_tmr_Open(2, Timer2CBMEM, ADI_TMR_MEMORY, Timer2Handler, 0, &phCTimer2);
Timer2をopenします.Timer2Handlerがcallback関数名.

adi_tmr_SetMode(phCTimer2, ADI_TMR_MODE_SINGLE_PWMOUT);
PWMじゃないけど、PWMのSINGLE SHOT modeにします.

adi_tmr_SetIRQMode(phCTimer2, ADI_TMR_IRQMODE_DELAY);
delay countが済んだら割り込みがかかるようにします.

次にcallback関数です.
void Timer2Handler(......){
timer2ongoing=false;
return;
}
やってることはtimer2ongoingをいじってるだけ.

次にdelay関数本体です.
void h_delay_us(int us){
 ①adi_tmr_SetWidth(phCTimer2, 1);
 ②adi_tmr_SetDelay(phCTimer2, 100*us); // clk=100MHz
 timer2ongoing=true;
 ③adi_tmr_Enable(phCTimer2, true);
 ④while(timer2ongoing);
 ⑤adi_tmr_Enable(phCTimer2, false);
}
①②Timer2にdelay時間設定をする.②だけじゃなくて①も必要.
③Timer2スタート
④callback関数がtimer2ongoingをfalseにするまでwait
⑤Timer2停止


バッファ処理
バッファはRx2本、Tx2本の計4本あり、各バッファは512BYTESです.
96kHzサンプルですので、666uSec毎にバッファが消費されます.
バッファの動作イメージ図.

Rx callback関数
はしょってキモのところだけ抜き出しました.
ADC受信バッファが満タンになったらここへ飛んできます.
case文で、満タンになったのはBUF0かBUF1かで処理を分けていますが、いずれにせよやってることは2つです.
・ステータスの切り替え(PROCESSING_BUF0とか)
・次の受信をkick-offしている
void SportCallbackB(........){
 switch(state_sportB) {
  case H_SPORTB_ADC_TO_BUF0:
   state_sportB = H_SPORTB_PROCESSING_BUF0;
   adi_sport_SubmitBuffer(hDeviceB, nBufferRx1, SIZE_OF_RX_BUFFER);
   break;
  case H_SPORTB_ADC_TO_BUF1:
   state_sportB = H_SPORTB_PROCESSING_BUF1;
   adi_sport_SubmitBuffer(hDeviceB, nBufferRx0, SIZE_OF_RX_BUFFER);
   break;
  }
}

Tx callback関数
DAC送信バッファが空になったらここへ飛んできます.
次のDAC dataを用意するのが仕事です.実測で30uSecかかっています.
void SportCallbackA(......){
 fract32 *pBufTx, *pBufRx; バッファのポインタ

前回のターンで用意済のバッファを送信起動します.
 adi_sport_SubmitBuffer(
  hDeviceA,
  txbuf01==0 ? nBufferTx1 : nBufferTx0,
  SIZE_OF_TX_BUFFER
  );

ここからバッファコピー.
 // Lch
 pBufTx = txbuf01==0 ? nBufferTx0: nBufferTx1;  コピー先バッファ
 pBufRx = state_sportB==H_SPORTB_PROCESSING_BUF0 ? nBufferRx0 : nBufferRx1;  コピー元バッファ

DAC信号選択がADC loopbackだった場合に限り、バッファコピーを行う.
 if(paramL.waveform==H_WAVEGEN_ADC){
  for(int i=0; i<SIZE_OF_TX_BUFFER; i=i+8){
   *pBufTx = *pBufRx;
   pBufTx += 2;
   pBufRx += 2;
  }
 }
バッファには、偶数word(32bit)がLch、奇数word(32bit)がRchの順で並んでいるので、for文は8づつ増やし、コピーポインタは2づつ増やします.

DAC信号選択が内部関数のsin波等である場合は、バッファコピーをせず、関数出力をTxバッファに積むのみ.
 else {
  for(int i=0; i<SIZE_OF_TX_BUFFER; i=i+8){
   *pBufTx = h_wavegen(H_WAVEGEN_LCH);
   pBufTx += 2;
  }
 }

 // Rch   Lchと似てるので省略

 // state update
 txbuf01 = txbuf01==0 ? 1 : 0;  Txバッファの切り替え
 state_sportB = state_sportB==H_SPORTB_PROCESSING_BUF0 ? H_SPORTB_ADC_TO_BUF1 : H_SPORTB_ADC_TO_BUF0; Rxバッファの切り替え

ここでは2つのバッファを交互にWR切り替えして使っていますが、BlackFinには「ring buffer」という自動的にグルグル回り続ける便利なバッファがあるみたいです.それの使い方をまだ知らないのですが、いずれそっちに移行するべきだと思います.
「programming reference manual」の中の「Automatic Circular Addressing」で検索すると出てきました.CPUのindex registerに開始と終了アドレスを設定するとその間を自動的に循環してくれるそうです.ライブラリで提供されてるのかどうかは知りませんが、フツー提供されてると思うんです.

ーーーー
DAC/ADC loopbackのキモは上記のとおりです.

この他に、UART,Timerなども制御しています.SPIは挫折しました.UARTはたしか115200bpsだったかな.
UART経由で信号を変えられます.
 a l 60  Lch amplitude 60%の意味
 a r 89  Rch amplitude 89%の意味
 w l s  Lch sin波の意味
 w l u  Lch 上昇三角波の意味
 w r l  Rch Loopbackの意味


やっと信号が通ったので、信号処理を始められます.

22へ     24へ

かしこ

2023年9月13日水曜日

BlackFinを使ってみるなり(22) ADCをI2S接続

blackfinマニアの皆様いかがお過ごしでしょうか? たぶん2weeksぶりのDSP連載です。

まずはこの間になんでサボっていたのかからお話ししましょう。

「中華ADCが動かなかったから」がサボりの原因です。

TIのADCを使おうとしています。
 本命  PCM1804 192kHz 24bit 中華
 暫定1 PCM1803 96kHz 24bit 中華(手持ち)
 暫定2 PCM1808 96kHz 24bit 秋月

性能的にはPCM1804がmustなのですが、中華通販は届くまで時間がかかるので手持ちのPCM1803のプリント基板を作り火入れしました。しかしこれが動かない。

1つめ:3.3VがIC内部shortしてるのか知らないけど電流がいくらでも流れて発熱。うんともすんとも。

2つめ:沈黙

3つめ:電流が250mAも゙流れてしまうのは異常だがdigital回路は動いてるようでBCK,LRCKは出てくる。しかしAD変換されない。

すごい、すごすぎる。
TIの工場が捨てた不良品を拾って刻印して販売したんじゃね?
PCM1803は断念しました。
最近のハズレ品を引く確率も゙SUGOI DEKAI。

秋月で買えるPCM1808基板を作成。これは一発完動。 ←いまここ

そうこうしてるうちにPCM1804が意外に早く届いたのでプリント基板発注済。

というわけでPCM1808ですけど、ADCの接続設定及びDSPのdata flowを書ける段階になりました。BF706のI2Sにbufferに積んでもらってcalkback関数までお届けされてるのが昨夜の状況。
そのままDACへ出力してloopbackしてhardware的な動作確認終了とします。

いつも通りproject folder詰め合わせをupするけど、あいにく数日間はリフォームとかで忙しいので数日後にまた覗いてみてください。

#23にADC詳細を書きましたのでそっちへ廻ってくださいな

#SPIは相変わらず動いてません

21へ    23へ

あでゅ〜

2023年8月29日火曜日

BlackFinを使ってみるなり(21) SPIが動かない~

BlackFin BF706のSPIが動かなくて困っています.(丸二日悩んでいる)

CrossCoreのexampleを元にしてSPI2を動かし、SPI FLASHのW25Q32を読んでみようかと思っています.回路図ではこんなです.BF706はmasterです.
なのですが、動きません.
症状: SPI completeみたいな応答が返ってこない.割り込みも、polingも、DMAもだめ.

library codeをsemaphoreとかmutexみたいな言葉が出てくる界隈まで降りると、その先で無応答でhungupしてる.
SPI HWのレジスタを表示させたが、そんなにおかしな点は見当たらず.
power saveでうんすん状態とかでもない・・・

外面的な事象でおかしなのはこの写真です.
 上: SPI CLOCK
 下: SPI CS
なぜか、SPI HWが、clockを連続8byte以下しか出さないんです.
転送要求を12byteにしてもなぜか8byteで切られてしまう.
これだからcompleteにならないみたい.

かな~り詰んでいます.

たぶん明日にはADCのプリント基板が到着するので、SPIはここで撤退するかな.

20へ      22へ

かしこ

2023年8月22日火曜日

BlackFinを使ってみるなり(20) BlackFinにADCをつなぐには?

BlackFin DSPにADCを繋ごうとしているひらりんです.

BF706のSPORTのI2S modeでAD/DAを接続するのですが、信号の入出力関係がよくわかんないので調査しました.

SPORTをI2Sモードで使います.
 SPORT1のA Tx DAC
 SPORT1のB Rx ADC
FB706評価ボードの、SPORTの接続場所を確認します.(回路図はこちら)
DACの線とADCの線として使える場所はこうなっていると思われます.
 SPT1_xCLK  →BCK
 SPT1_xFS  →LRCK
 SPT1_xD0  →DATA

↓現在はDACのみ繋いでいまして、ADCは未接続です.配線はこうなっています.
・源発振は外部XTAL(25MHz)で内部PLLで100MHzにupしてI2Sのclkになっている
・I2S内部で適宜分周する.ぴったり96kHzにはならない.
・I2Sが出力側
・DACが入力側

これにADCを追加するにはどうするか?
↓これが1つの解と思われます.
・BF706のXTALが源発振
・ADCはslaveである(BCK,LRCKを入力される側)
・ADCにとって、BCK/XTALが非同期で構わないとの前提 ※
※ADCのXTALはdigital演算用の512fsとかのclockです.BCKと非同期でもOKなのかどうかはdatasheetから読み取れてません.旭化成のAK4495などのDACでは非同期でもOKなんです.

↓2つ目の解はこうです.
・ADCがmasterであり、ADCのXTALが全てを支配する
・すっきりして気持ちイイ
・配線が少し増えるが、こっちの方が確実

I2SのBCKを入力に切り替えるには?
赤字のところが切り替えです.
true: BCKは出力
false: BCKは入力
adi_sport_ConfigClock(hDev, 0u,false,false,false)

I2SのLRCKを入力に切り替えるには?
赤字のところが切り替えです.
true: LRCKは出力
false: LRCKは入力
adi_sport_ConfigFrameSync(hDev, 31u, true, false, false, false, false, false);

この調査結果で円満にADCを追加できると思うのですが、現物を動かしてみなくちゃ油断は禁物なのよね.

PCM1803のプリント基板はJLCPCBが製造中です.

19へ    21へ

かしこ

2023年8月21日月曜日

BlackFinを使ってみるなり(19) ADC MCP3561 つかえない~

本日はリフォームはお休みです.3時間も昼寝しました.

BlackFin DSPいじりを再開します.ADCを接続するのが次のお仕事です.

MCP3561というICを使おうと思います.24bit 150kspsというSPECなので実験には使えるのと、Audio用途じゃないので余計なHPFとか入って無さそうだから.マルツで¥1000ぐらいと、まぁお手頃価格.↓先日のこの写真がそれ.
実装しようかとdatasheetを読んでいたら、MCP3561は使えないと判明しました.153kspsだと16bitになってしまうんです.プリント基板も作ったのに丸損でぇす.こうゆうしくじりはよくある.

なーんも考えずにTIのPCM1804(24bit 192ksps)を使えば良かったかも.
PCM1804なら持ってたよなと探したら、、、手持ちはPCM1803でした.こちらは24bit 96kHzなんですが、とりあえず火入れには使えるでしょう.プリント基板を作ります.
AliexpressにPCM1804を注文しましたが本物なのかは知りません.

18へ      20へ

かしこ

2023年8月17日木曜日

BlackFinを使ってみるなり(18) お休み前、PCからUART経由で制御

明日からリフォームのひらりんです.

リフォームで多忙になる前にBlackFin BF706で動作確認を終わらせておきたかったことはほぼ出来ました.

今日はUART Rxを動かして、PCからUSB COMでDSPを制御しました.

今まで、UARTの設定がよくわからず例外が発生していたのですが、以下の設定で動きました.UART0のTxRx両方使いです.全二重と呼びますか?
例外の原因は、赤字のメモリサイズでした.TxまたはRxの場合のメモリサイズは小さくていいが、両方向は大きなメモリサイズが必要です.BIDIRとは両方向のことです.
adi_uart_Open(0, ADI_UART_DIR_BIDIRECTION, driverMemory_uart, ADI_UART_BIDIR_DMA_MEMORY_SIZE, &hDevice);
adi_uart_SetMode(hDevice, ADI_UART_MODE_UART);
adi_uart_SetBaudRate(hDevice, 115200);
adi_uart_SetParity(hDevice, ADI_UART_NO_PARITY);
adi_uart_SetNumStopBits(hDevice, ADI_UART_ONE_STOPBIT);
adi_uart_SetWordLen(hDevice, ADI_UART_WORDLEN_8BITS);
adi_uart_EnableDMAMode(hDevice, true);
adi_uart_RegisterCallback(hDevice, UARTCallback, (void*)0);

adi_uart_SubmitTxBuffer(hDevice, txbuf_uart[0], 1);
adi_uart_EnableTx(hDevice, true);

adi_uart_SubmitRxBuffer(hDevice, rxbuf_uart, 1);
adi_uart_EnableRx(hDevice, true);

Tx側は以前の投稿を読んでもらうとして、今回はRx側です.

Rxはどうも使い心地が悪いかなぁ.現状のRxのやりかたをポエム風に表現するとこうです.
 ・DMAである
 ・1BYTE Rxの要求を出す
 ・1BYTE Rxが完遂されたら割り込みcallbackが生じる
 ・callbackの内部で、その1文字を自前のバッファに積む
 ・CRを受信したらコマンド解析などを行う
この「1文字毎にcallback処理する」のってなんかのろくさくて好かないんだけど.
もちろん、50文字Rxという要求を出すのも可能なのですが、それだとコマンド列の末尾で50文字に満たず無反応になってしまうので受諾できません.なので1文字毎にしてます.
または、pollingで「Rxしたぁ?」「何文字?」みたいな関数も用意されてるみたいなのですが、なんのためのDMAなのかなと思ってしまいますが、1文字毎にDMAで受信してるパープリンの言う事じゃねぇや、と思わんでもなく.なんか居心地悪いです.

callback関数の1文字処理のところ.
①ADI_UART_EVENT_RX_BUFFER_PROCESSEDというevent flagが受信完了の意味.
②すぐに受信文字をsaveしといてから、
③次の1文字をRxしろと要求する
④UARTへecho back
⑤CR受信で行末検出
⑥1行受信のflagを立てる
void UARTCallback(){
 switch (event) {
  case ADI_UART_EVENT_RX_BUFFER_PROCESSED: ①
   char x = rxbuf_uart[0]; ②
   adi_uart_SubmitRxBuffer(hDevice_uart, rxbuf_uart, 1); ③
   h_uart_tx_char(x); ④
   switch (x) {
    case 0x0d: ⑤
     comN = N;
     if(N==0 && i==0) break; // no character
     N=0; i=0;
     bComplete_uart_CR = true; // end of a line ⑥
   break;

⑥のフラグをmain()のloopでチェックしていまして、そこでコマンド解析し、DACに発生させる周波数、電圧、波形などを変更します.結果として、BF706をPCから制御していることになります.
制御コマンドはこんな感じです.
 f r 600   Rchを600Hzにする
 a l 67    Lchの電圧をフルスケールの67%にする

32bitのsin(x)を67%に小さくする演算って固定小数点だとどうやるんだろう???
浮動小数点で表現するとこんなに簡単なわけですが、、、
 out = sin(x) * 67 / 100;
固定小数点だと現状はこんなにしています.かったるくない?
 long long t64 = mult64_32x32( 0x7fffffff, 67 );
 int t32 = t64 / 100;
 o = sin_fr32( x );
 o = mult_fr1x32x32( o, t32 );
 out = polarity==true ? o : (-1)*o; ←前回書いた極性制御
赤字の乗算関数はライブラリが提供するものです.

ーーーー
CrossCoreのproject詰め合わせはこちらです.

BF706のサンプルとしてどんな事を見れるかというと、、、
・BF706評価ボードがtarget
・Timer0でLEDチカチカ(GPIOも)
・Timer1でUARTの周期的送信(10Hzだったかな)
・UART0 Tx 内部状態をPCへ送信
・UART0 Rx PCからBF706を制御する
・SPORTのI2SでDACへ音声データ送信
・sin_fr32(x)で固定小数点でsin波発生→DAC出力
・speed tuneはなにもやってません、だらだらcodeです

免責:被害に遭っても知りません.

明日からDSPのupdateは遅くなりますのでまたね~

17へ      19へ

かしこ

2023年8月16日水曜日

BlackFinを使ってみるなり(17) 固定小数点のsin_fr32()でsin波を出す

明後日からリフォーム復活です.こんばんわ.ひらりんです.

FB706の固定小数点演算でsin波を出してみよう.こんな感じです.ノコギリ波のイジイジしたリンギングはDACのfilterのせいです.

math_bf.hにsin_fr32(x)という関数が定義されています.これを使ってsin波を出します.

sin_fr32(x)は32bit固定小数点入出力の関数です.
この関数の定義域と値域はどうなってるのかをまず調べました.
↓こうなりました.すなわち、
 定義域  x = ーπ/2 ~ +π/2
 値域   sin_fr32(x) = -1 ~ +1
まず値域については、、、32bit符号付整数の表現範囲のmaxが 0x7FFFFFFF で、これが+0.999999に相当します.グラフが2E+09まで振れているのは、0x7FFFFFFF = 2147483647 ≒ 2E+9だからです.表現範囲のマイナス方向minは0x80000000で、これが-0.999999に相当します.
つぎに定義域は、x=0.99999を+π/2に相当させているようです.マイナス方向も同様.

sin_fr32は周期関数でなくて、ブツ切りsin関数ですから、使い方には注意が必要です.
X=0から少しずつXを増やしてsin波を発生させるのは通常どおりですが、X=0x7FFFFFFFに達した次はX=0x80000000に飛んでしまうので、そこから先は(-1)*sin_fr32(z)に極性を変える必要があります.
こういう極性反転をπラジアン毎に繰り返す必要があります.

ーーーー
このような初期化関数で、サンプリング周波数、周波数、レベル、DC、波形を設定します.レベルとDCは未実装です.いずれは位相も調整できるようにしなくちゃいけない.
 h_wavegen_init(
  H_WAVEGEN_RCH,
  192000,   // sample freq.   ex.96000 Hz
  10000,    // signal freq.
  0,        // level
  0,        // dc
  H_WAVEGEN_SIN
 );

ーーーー
I2Sのバッファ空っぽ割り込みで、バッファに波形データを仕込みます.
 L = h_wavegen(H_WAVEGEN_LCH); ←この中でsin_fr32()を呼ぶ
 nBufferTx0[i+0] = L;  ←リトルエンディアン
 nBufferTx0[i+1] = L>>8;
 nBufferTx0[i+2] = L>>16;
 nBufferTx0[i+3] = L>>24;

ーーーー
sin_fr32(x)のxの更新stepの計算式.
 step_sin = 0x100000000 * 2 / ( fsmpl / freq ) ;

たとえば96kHzサンプルで1000Hzを発生させるには、
 step_sin = 0x100000000 * 2 / ( 96000/ 1000 ) = 8947848
となります.

ーーーー
sin_fr32(x)の極性反転について.
xが正→負に変わった瞬間で極性反転させています.
 xold = x;
 x += step;
 if( xold>=0 && x<0 ) {
  p->polarity = p->polarity==true ? false : true;
 }
boolの論理反転って~pか!pで出来ないのかね? > Cさん

ーーーー
project詰め合わせはこちらです.

16へ    18へ

かしこ

2023年8月15日火曜日

BlackFinを使ってみるなり(16) 閑話 アナログ基板

お盆休みなので自分で前髪切りました.こんにちは、ひらりんです.

本日はDSPの進展は無いんです.
アナログフロントエンド基板が届いたので組み立てました.

↓プリント基板取付のための支柱を3DPで印刷します.3DPはエレキ開発にも役立ちます.
↓5枚の基板の継ぎはぎがアナログフロントエンド基板です.仕様は秘密.下の基板はおなじみBF706評価ボード.ADCをDSPで制御したいのだけど、marutsuへの入荷待ちです.品切れとか言われたらやだな.

今回の5枚の基板は急いだので深く考えずに発注したため、部品のピン配ミス、コネクタが逆順などの凡ミスがあり、早くも2枚の基板が致命傷です.アートワーク修正してJLCPCBに発注しました.$7は¥1000ぐらいか.

15へ    17へ

かしこ

2023年8月14日月曜日

BlackFinを使ってみるなり(15) TIMERの例外の解決

アニメキャラの腰つきを重視するひらりんです.

BF706 DSPを動かしています.

AD社提供のライブラリを使います.しかし、TIMERとUARTで例外が出るんです.

例えばこれはUARTで生じる例外.→こちらに少し書いた
A non-recoverable error or exception has occurred.
  Description:   A data CPLB miss has occurred without a corresponding CPLB entry.
  General Type:  RunTimeError

これと同類の例外がTIMERでも起きて困ってたんです.がっ解決しました.

症状: TIMER0は動くが、1や2や3をopenすると例外が生じる

以下原因を書きますが、ポエム的表現ではこんな感じでした.
AD提供のTIMERライブラリには、TMRとCTMRの2つあり、CTMRが新しいようだ.
TMRなら正常.しかしCTMRは例外を生じる.
CTMRは、ライブラリソース自体がTIMER1~7を動かすように作られていなかった.

UARTの例外も似たよな事情かもしれません.

ーーーー
↓CTMRライブラリ関数を使ったTIMER1の設定はこんな関数でやってました.でもこれだと例外です.
#include <services/tmr/adi_ctmr.h>
adi_ctmr_Open (1, Timer1Handler, 0, &phCTimer1);
adi_ctmr_SetPeriod(phCTimer1, 100000);
adi_ctmr_SetScale(phCTimer1, 100);
adi_ctmr_Enable(phCTimer1, true);
adi_ctmr_EnableAutoReload(phCTimer1, true);
adi_ctmr_Activate(phCTimer1, true);

↓TMRライブラリ関数だとこうなります.これなら動きます.
#include <services/tmr/adi_tmr.h>
adi_tmr_Open(1, Timer1CBMEM, ADI_TMR_MEMORY, Timer1Handler, 0, &phCTimer1);
adi_tmr_SetMode(phCTimer1,ADI_TMR_MODE_CONTINUOUS_PWMOUT);
adi_tmr_SetPeriod(phCTimer1,100000*100);
adi_tmr_SetIRQMode(phCTimer1, ADI_TMR_IRQMODE_PERIOD);
adi_tmr_Enable(phCTimer1, true);

↓CTMRライブラリ関数の問題箇所はここです.
TIMER0~7まで8個の配列が必要ですがTIMER0しか書かれていませんから、TIMER1をopenしたらメモリ壊しますわなぁ.そして例外が生じます.1~7を追加するのはやめときます.
static ADI_CTMR_DATA gCTimerData[] = {
 { // TIMER0のみ
  (volatile ADI_CTMR_REGS  *)TCNTL,
  NULL,
  NULL,
  false
 },
}

↓TMRにも同様の配列がありまして、こちらは0~7まで記述されており、正しく動きます.
static ADI_TMR_DEV  gaTimers[] = {
 {    /* Timer 0 */
  (ADI_TMR_REGS_PTR) pREG_TIMER0_TMR0_CFG,
  (uint32_t)INTR_TIMER0_TMR0,
  0u,
  NULL
 },
 {    /* Timer 1 */
  (ADI_TMR_REGS_PTR) pREG_TIMER0_TMR1_CFG,
  (uint32_t)INTR_TIMER0_TMR1,
  1u,
  NULL
 },
以下Timer7まで記述されている....

ーーーー
訂正.
BFにはGP Timer 0-7とCore Timer 0 の2種類あるとわかりました.(コメ欄の情報より)

TMRはGP Timerのライブラリ関数です.CTMRはCore Timerのライブラリ関数です.

わたしは最初からTMR関数を使うべきだったのでした.

資料を調べました.

1)ADSP-BF7xxシリーズdatasheet
General-Purpose Timer の記述あり.
Core Timer 「coreが独自に持つtimerでsystem tickに使用」と記述あり.

2)ADSP-BF70x Blackfin+ Processor Hardware Reference
General-Purpose Timer の記述あり.
Core Timerの記述無し.

3)ADSP-BF7xx Blackfin+ Processor Programming Reference
General-Purpose Timer の記述無し.
Core Timer の記述あり.

4)system.svcからのexample
「GP timer configured in PWM out mode」exampleあり
「Core Timer configured to auto reload」exampleあり ←わたしが最初に掴んでしまったもの

14へ     16へ

かしこ

BlackFinを使ってみるなり(14) UART Tx実装

ライブラリで悩むのがプログラミングに要する時間の大半、ひらりんです.

UARTのbaudrate問題が解決したので、UARTのTxのみですが、project詰め合わせをupしておきます.こちらです.Txのみなのは、内部状態を出力させたいためです.

あまりdebugしてないのでなにか不具合はあるでしょう.ダメなら素直に死のう.

ーーーー
USB COMのhardware
UART0_TXだけしか使ってません.なので信号の取り出しはコネクタP9の3番ということになります.この先にUSB COM ICであるFT232などを取り付けることになります.
FT232基板(自作)

main()のUART初期化界隈
power界隈を初期化しないとbaudrate設定が正しく動きませんので.
adi_pwr_Init(0,25000000); // clkin 25MHz

UART設定いろいろ.UARTを使い慣れた人ならやってる事はわかるでしょう.送信終了でcallbackしてもらいたいので登録もしています.DMAもオンしてます.
adi_uart_Open(0, ADI_UART_DIR_TRANSMIT, driverMemory_uart, ADI_UART_UNIDIR_DMA_MEMORY_SIZE, &hDevice_uart);
adi_uart_SetMode(hDevice_uart, ADI_UART_MODE_UART);
adi_uart_SetBaudRate(hDevice_uart, 115200);
adi_uart_SetParity(hDevice_uart, ADI_UART_NO_PARITY);
adi_uart_SetNumStopBits(hDevice_uart, ADI_UART_ONE_STOPBIT);
adi_uart_SetWordLen(hDevice_uart, ADI_UART_WORDLEN_8BITS);
adi_uart_EnableDMAMode(hDevice_uart, true);
adi_uart_RegisterCallback(hDevice_uart, UARTCallback, (void*)0);

enableするために、先だってsubmitしなくちゃいけないのがBFライブラリの特徴と言えるかもしれない.
adi_uart_SubmitTxBuffer(hDevice_uart, txbuf_uart[0], 1);
adi_uart_EnableTx(hDevice_uart, true);

起動メッセージをCOMへ発信します.h_uart_tx_str()はひら自作です.
h_uart_tx_str("\n\n====== BF706 test program ======", true, false);


ひら自作関数
 h_uartcom.cがそれです.

大雑把な構成を先に説明すると、、、
char txbuf_uart[2][100]
2本のバッファは、片方はUART Txに使用中、もう片方は送信したいデータを積むところ.それを交互に切り替えます.

なので、送信したいデータを積むのは基本的に随時行えます.(あまりにもドカドカ積むとoverflowしますけど)

積むのは随時.ならば、UART Txはいつkickされるのか?
TIMER0で0.1秒毎にkickされます.バッファが空ならスルーします.
本当はTIMER1を使いたかったのですが、不調で動きませんのでTIMER0を使います.

TIMER callback()
0.1秒割り込み毎にh_uart_tx()を呼びます."run"は動作確認用.
Timer1Handler(){
 switch( Event ){
 case ADI_CTIMER_EXPIRED:
  h_uart_tx();
  h_uart_tx_str("run",false,true);
  break;
 }
 return;
}

h_uart_tx()
①UART送信中なら何もしない
②積みバッファの長さ
③積み荷が在ればUART Txをkickする
h_uart_tx(){
  if(!bComplete_uart) return; ①
  int len = strlen(txbuf_uart[tx01_uart]); ②
  if(len!=0) adi_uart_SubmitTxBuffer(hDevice_uart, txbuf_uart[tx01_uart], len); ③
}

UARTCallback()
UART Txが終わったら処理します.
①終了フラグ
②バッファ切り替え
③バッファクリア
UARTCallback(){
 switch (event) {
  case ADI_UART_EVENT_TX_BUFFER_PROCESSED:
   bComplete_uart = true; ①
   if(tx01_uart==0) tx01_uart=1; ②
   else tx01_uart=0;
   txbuf_uart[tx01_uart][0]=0; ③
   break;
 }
}

バッファへ積む関数
例としてstringをバッファへ積む関数です.
末尾にspaceを追加するか、CRを追加するかはお好みです.
void h_uart_tx_str(char* x, bool CR, bool spc){
  h_uart_tx_bufadd(x);
  if(spc) h_uart_tx_char(0x20);
  if(CR)  h_uart_tx_char(0x0d);
}


こんなところですかね.

13へ     15へ

かしこ