設計が行き詰まって逃避してきた朝のスタバ。客の7割がPCで作業中。キモい。わたしはスマホでブログ書いてるのでキモくない。
STM32で任意の特性M系列を出力したい。つまり任意の1bit出力ですね.割り込みじゃ周期がフラつくので、DMAでやる必要がある。
以前こんな投稿をしたとき、1bit出力をするためにI2Sを利用した。
しかしI2Sには面倒事があって、clock周波数がaudio系なので48kHzまたは44.1kHzの倍数になっちまうとの苦い記憶。
いまやりたいのは、外部clockでドライブされる1bit出力である。しかも任意のclock周波数で。
未体験ゾーンの「GPIOへDMAで出力」が出来れば良い。
昨日の夕方にGPIO-DMAに着手したんだが動かない。
ーーーー
まずGPIOの叩き方はどうするか?
普段はHAL_GPIOWrite()を使ってしまう。
しかしDMAの場合はそうも行かない。
DMAの送り先アドレスにGPIOレジスタを指定してやれば良い。↓これな
GPIOD−>BSSR
BSSRは32bitレジスタで、GPIOD[15:0]を出力制御出来る。
BSSR[31:16]がbit set
BSSR[15:0]がbit reset
みたいな感じ。set reset逆かもだが。
なので例えばDMAで10bit出力するのに32bit配列が10個必要というメモリ非効率はあるっちゃある。
ーーーー
DMAを動かすには、initはMXの自動生成codeにお任せするとして、こんなので起動すればイイと思われる。
HAL_DMA_Start(&dma,元,行き先,個数)
行き先は上述の &(GPIOD−>BSSR) とする。
だが動かない。
ーーーー
CPUはSTM32H743だ。
このCPUにおけるGPIOの所在地、というか内部busのぶら下がり地点は、AHB4という辺境なんだ。
CPUは王様なので辺境でもアクセス出来る。
しかしDMAは自分の所属ドメイン内のRAMやperipheralしかアクセス出来ないとの制限がある。
APB1やAPB2にぶら下がったDMAはbus bridgeが繋いでくれてるみたいなのだけど、AHB4はあまり繋ぎが密でないみたいよ。それゆえ辺境呼ばわりしてる。
busについては知識が乏しい。
AHB4傘下のGPIOにアクセス出来るDMAは在るのか?
ある。BDMAだ。
BDMAがアクセス出来るRAMはどれだ?
RAM_D3 0x38000000からの64KBのやつ
指針を得たのでやってみた。だが動かない。
家に戻って考え直してみるなり。
ーーーー
指針というやつはこれ. →リンク
1)The GPIO banks are on AHB4, use BDMA to copy data from SRAM4 to GPIOx>BSRR or ODR.
2)SRAM4 is 64KB situated at 0x38000000.
3)Should be able to use LPTIM2 or LPTIM3 OUT as a clocking/triggering source.
BDMAのトリガにLPTIMを使えって言ってる.LPTIMって簡素なので使い勝手はあまりよくなくなくない?
あと、BDMAをEXTI0でトリガしたいんだがもしかしたらEXTI0がBDMAまで届いてないのかもしれない.
ーーーー
夕方まで粘りましたが、BDMAが治りません.いじる場所はそんなに多くないので手が出ない.
撤退して、SAIを試してみます.
→SAIはmasterでexternal clkが使えませんでした
I2Sはpinが埋まっていて使えません.
詰みっ!
ーーーー
夜、動いたかもしれない.
以下、やったことをダラーッと書きます.みなさんの役に立ちますように.ボク人柱.
【やることポエム】
CPUはSTM32H743.
DMAをcircularで動かし、GPIOに3kHzの一定レートで1bit streamを出力する.
GPIO-D12が出力先.
3kHzはEXTI0へ入力されるパルスとする.
【DMAで制御可能なGPIO出力port】
GPIOD->BSRRに書き込むとGPIOD[15:0]に出力できる.
[31:16]に1を立てたbitがresetされる.
[15:0]に1を立てたbitがsetされる.
したがって下記をcyclicに回すと、D12に111100の繰り返しが出力される.1bitの送信に4BYTEを浪費することになるがやむを得ない.
uint32_t LBP[6];
LBP[0]=(1<<12); // GPIOD12=1
LBP[1]=(1<<12); // GPIOD12=1
LBP[2]=(1<<12); // GPIOD12=1
LBP[3]=(1<<12); // GPIOD12=1
LBP[4]=(1<<28); // GPIOD12=0
LBP[5]=(1<<28); // GPIOD12=0
【制約】
GPIOはAHB4傘下に在る.通常のDMAはアクセス不能.
GPIOにアクセスできるのはBDMAである.(D3 domain)
また、BDMAがアクセスできるRAMは、SRAM4 64kB 0x380000000.
【D3 domain RAMの動かし方】
まず、リンカファイルを改造する.→STM32H743VITX_FLASH.ld
末尾のところにRAM D3を追加.
.DATA_RAM_D3 (NOLOAD) :
{
} >RAM_D3
.ARM.attributes 0 : { *(.ARM.attributes) }
}
送信データバッファを宣言.
RAM D3に置くようにattributeを追記しておく.
__attribute__ ((section (".DATA_RAM_D3"))) uint32_t LBP[6];
注意:LBP[]宣言と同時に初期化したものはCPUの起動時に上書きされちゃうみたいよ.なので送信データはmain()の然るべき場所でやりましょう.
【serial out DMA起動】
// GPIOD12 serial output
LBP[0]=(1<<12); // GPIOD12=1
LBP[1]=(1<<12); // GPIOD12=1
LBP[2]=(1<<12); // GPIOD12=1
LBP[3]=(1<<12); // GPIOD12=1
LBP[4]=(1<<28); // GPIOD12=0
LBP[5]=(1<<28); // GPIOD12=0
HAL_DMAEx_EnableMuxRequestGenerator(&hdma_bdma_generator0);
HAL_DMA_Start( &hdma_bdma_generator0, (uint32_t)LBP, (uint32_t)&(GPIOD->BSRR), 6);
補足しておくと、、、こんなブロック図になっている.
EXTI0→TRIGMUX→BDMA→GPIO
DMA起動の呪文は2つからなる.
1)TRIGGER MATRIXを起動 HAL_DMAEx_EnableMuxRequestGenerator()
2)BDMAを起動 HAL_DMA_Start()
【STM32cubeMXの設定】
EXTI0をGPIO入力にしておく操作は割愛する.EXTI0に3kHzを入れるのをお忘れなく.
BDMAの設定がキモ.といってもこれ↓で全てだと思う.
あでゅ〜