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を見ながら追加してみた。
うまく動いたら、上記サイト主に相談してみるか?
# 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
}// -----------------------------------------------------------------------------
// 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に転送してみるか。


