Raspberry Pi Pico のDMA、ADCとPIOを連携させる試み
はじめに
C12880MAの蓄積時間を正確に決めるためにPicoのPIOを使って、クロックを生成しているのであるが、ADCに関してはPythonのループ中でクロックを生成しつつADCでアナログ値を変換している。
これをオシロスコープで観察していると、結構な間が開くことがある。特に得られているデータに問題があるようには見えないが、気持ちが悪いので改善したい。
解決の方法
PIOでCLK信号を立ち上げて、下げる。CM12880MAがVideo信号を出力する。DMAがADCからデータを転送、転送されたことをPIOが知るために転送先はPIOのTX FIFOにする。PIOがPullできるようになったことで、PIOが次のCLKを出力、ついで、PullしたデータをRX FIFOにPush、Pushされると別のチャンネルのDMAがPython側に用意したメモリにRX FIFOの内容を転送する。PIOでは288個のデータ用のカウンタで一連の動作を制御する。できるか?CLKを出力してから、Pullかな?MicroPython側はこの一連の動作の開始を指示するだけになる。
課題
DMAをPythonから使ったことがない。とりあえず、Picoのレジスタを直接操作する方法については次のページを参考にした。uctypesが用意されている。
https://iosoft.blog/pico-adc-dma
ただ、このサイトのrp_devices.pyにはPIOの定義や一部省略されているところがあったので、Datasheetを見ながら追加してみた。
うまく動いたら、上記サイト主に相談してみるか?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
# DMA: RP2040 datasheet 2.5.7 DMA_CTRL_TRIG_FIELDS = { "AHB_ERROR": 31<<BF_POS | 1<<BF_LEN | BFUINT32, "READ_ERROR": 30<<BF_POS | 1<<BF_LEN | BFUINT32, "WRITE_ERROR": 29<<BF_POS | 1<<BF_LEN | BFUINT32, "BUSY": 24<<BF_POS | 1<<BF_LEN | BFUINT32, "SNIFF_EN": 23<<BF_POS | 1<<BF_LEN | BFUINT32, "BSWAP": 22<<BF_POS | 1<<BF_LEN | BFUINT32, "IRQ_QUIET": 21<<BF_POS | 1<<BF_LEN | BFUINT32, "TREQ_SEL": 15<<BF_POS | 6<<BF_LEN | BFUINT32, "CHAIN_TO": 11<<BF_POS | 4<<BF_LEN | BFUINT32, "RING_SEL": 10<<BF_POS | 1<<BF_LEN | BFUINT32, "RING_SIZE": 6<<BF_POS | 4<<BF_LEN | BFUINT32, "INCR_WRITE": 5<<BF_POS | 1<<BF_LEN | BFUINT32, "INCR_READ": 4<<BF_POS | 1<<BF_LEN | BFUINT32, "DATA_SIZE": 2<<BF_POS | 2<<BF_LEN | BFUINT32, "HIGH_PRIORITY":1<<BF_POS | 1<<BF_LEN | BFUINT32, "EN": 0<<BF_POS | 1<<BF_LEN | BFUINT32 } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
// ----------------------------------------------------------------------------- // Field : DMA_CH0_CTRL_TRIG_AHB_ERROR // Description : Logical OR of the READ_ERROR and WRITE_ERROR flags. The channel // halts when it encounters any bus error, and always raises its // channel IRQ flag. #define DMA_CH0_CTRL_TRIG_AHB_ERROR_RESET _u(0x0) #define DMA_CH0_CTRL_TRIG_AHB_ERROR_BITS _u(0x80000000) #define DMA_CH0_CTRL_TRIG_AHB_ERROR_MSB _u(31) #define DMA_CH0_CTRL_TRIG_AHB_ERROR_LSB _u(31) #define DMA_CH0_CTRL_TRIG_AHB_ERROR_ACCESS "RO" // ----------------------------------------------------------------------------- // Field : DMA_CH0_CTRL_TRIG_READ_ERROR // Description : If 1, the channel received a read bus error. Write one to // clear. // READ_ADDR shows the approximate address where the bus error was // encountered (will not be earlier, or more than 3 transfers // later) #define DMA_CH0_CTRL_TRIG_READ_ERROR_RESET _u(0x0) #define DMA_CH0_CTRL_TRIG_READ_ERROR_BITS _u(0x40000000) #define DMA_CH0_CTRL_TRIG_READ_ERROR_MSB _u(30) #define DMA_CH0_CTRL_TRIG_READ_ERROR_LSB _u(30) #define DMA_CH0_CTRL_TRIG_READ_ERROR_ACCESS "WC" // ----------------------------------------------------------------------------- |
左が、Pythonのuctypes向けの記述、ちょっと分けがわからない。奇妙だが、 とりあえず指定方法はわかった。右がpico-sdkにあるヘッダーファイルでAHB_ERRORとREAD_ERRORの該当部分、わかりやすい。
結果
ADCからメモリ上にデータを転送することは可能。ただし、複数データの場合、ADC FIFOに8個ほどデータが残る問題がある。これを読み出さずにクリアする良い方法ないのかな?今回は一つのデータで十分なので、後の課題としておく。
さて、次はDMAで値をPIOのTX FIFOに転送してみるか。