nRF52832には、EasyDMAという機構というかユニットがある。
DMAは、Direct Memory Accessで、CPUを介さずにメモリをアクセスするユニットだと思えばよいだろう。
だいたい、DMAを使うときにはこういうデータをセットする。
- 転送元のアドレス
- 転送元アドレスの進め方
- 転送先のアドレス
- 転送先アドレスの進め方
- バスの扱い方
最近使っていないからちょっとあやしいが、だいたいこんなもんだろう。
メモリからメモリへの転送であれば、アドレスはアクセスごとに進めるなり戻るなりしたいだろうけど、片方がレジスタであれば同じアドレスをアクセスし続けたいはずだ。
また、DMAユニットがメモリのバスを使い続けると、そのバスを使いたい人は空くまで待たないといけない。
バースト転送だとそんな感じだけど、アクセスごとにバスを空ける方式もあったと思う。
転送が終わったら割込みが上がるか、転送完了フラグが立つか、というパターンであろう。
nRF52832のProduct Specでは、2ページ程度で説明されている。
レジスタ仕様などなく、こんな接続ですよ、というくらいだ。
EasyDMAは、メモリ-メモリ間の転送はできず、RAMといくつかの周辺機器間に限定しているようだ。
その分、使い方が簡単になっているのが「Easy」の意味か。
そして、周辺機器の相手は「メモリ」ではなく「RAM」なのだ。
たとえば、UARTドライバの送信nrf_drv_uart_tx()では、EasyDMAを使うルートにこういうコメントがある。
// EasyDMA requires that transfer buffers are placed in DataRAM,
// signal error if the are not.
渡されたアドレスがデータRAMかどうかをチェックして、違う場合はエラーにしているのだ。
チェックするマクロは、こうなっている。
#define IS_EASY_DMA_RAM_ADDRESS(addr) (((uint32_t)addr & 0xFFFF0000) == 0x20000000)
送信だからconstなデータを送ることもあるだろうけど、EasyDMAを使いたいのであれば一度RAMに移して、そのアドレスから送信してやらないといかんということだ。
EasyDMAがある場合のUART構成は、こうなっている。
Product Specでは「UARTE」となっている。
nRF51と同じ構成のUARTもある。
外から見ると、STARTRX, STOPRXや、PSELRXD, PSELTXDは同じなのだけど、それが内部でどうつながっているかが違うようだ。
app_uart_put()を使うと送信サイズが1なので、あまりEasyDMAを使ってもありがたみがなさそうな気がする。
だが、転送データは最終的に構造体に入れられてnrf_drv_uartに渡されるので、アドレスは心配しなくてよさそうだ。
ドライバのソースは、EasyDMAなのかどうかを、CODE_FOR_UARTE()とCODE_FOR_UART()で分けている。
分けているのだが、パターンとして
- UARTのみ
- UARTEのみ
- UARTEとUART両方
を許容しているようだ。
EasyDMAだけだと、RAMに置けないような大きいデータをやりとりできなくなってしまうので、両方使えるようにもしてあるのだろうか。
nRF51でUARTの送信バイト間がやたらと空いてしまうことが有り、おそらくUART送信とSoftDeviceの処理が重なり、SoftDeviceが優先されたためだろうと思っている。
バイト間タイムアウトがあるデバイスを使っていたので問題になったけど、DMAもなく上手に回避できなかったのだ。
はじめまして。
返信削除私は現在、nr52840 DKボードでUARTドライバの動作検証を行っています。
接続したいデバイスは2つあり、そのため、
UART0とUARTE1を使おうと考えております。
UART0に関してははnrf_drv_uart_tx(ノンブロッキング)でデータを送信できることが確認できていますが、
UARTE1については、nrfx_uarte_tx()をノンブロッキングで使用すると、ハードフォルトになり、
ブロッキングで使用するとポートから正しくデータが出力されません。
※送信データはオンメモリにあります。
———————————————————
送信だからconstなデータを送ることもあるだろうけど、EasyDMAを使いたいのであれば一度RAMに移して、そのアドレスから送信してやらないといかんということだ。
——————————————————
原因は、上記を行なっていないためなのでしょうか?
また、RAMへの移し方を教えて頂けないでしょうか?
よろしくお願い致します。
>原因は、上記を行なっていないためなのでしょうか?
削除こちらはわかりません。
当時の記憶ももうなく、実際に動かしたかどうかも覚えていません。。
この記事が既に3年以上も前なので、今は実装が変わっているかもしれませんが、RAMかどうかチェックしている箇所の有無を調べたり、ブレークポイントで止めてみるなどがよいかと思います。
>また、RAMへの移し方を教えて頂けないでしょうか?
これは簡単で、コピーしたいサイズのRAMを配列などでサイズ分用意して、memcpy()するだけです。