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に転送してみるか。

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です

CAPTCHA