stm32FのUARTでシリアルデータを受信するのにDMAのcircularモードを使うとDMAだけでデータをリングバッファに読み込むことが出来ます。
リングバッファからの読み出し関数は、DMAのポインタ(huart_cobs->hdmarx->Instance->CNDTR)をリングバッファへの書き込みポインタとして、読み出しポインタで書き込みポインタを追いかけるようにするだけです。
この手法は割り込みン数を使わずにデータをリングバッファに取り込めるのでとても簡単で効率が良いのですがSTM32CubeMXのHALライブラリを使っているとエラーが起きた時に勝手に送受信を止められてしまうという困った現象が起きます。
解決法を探していると同じことをやっている人がいて、「受信エラーはCRCチェックで対処するからHALライブラリのエラーハンドリングは邪魔なのでこうやってやめさせたよ。」という回答をみつけました (下のトピック)。
Topic: ‘Best’ way to load UART data to ring buffer with STM32/HAL
このトピックの投稿者が「この方法がとてもクールだと思わないか?」と言ってるのに対して、別の人が「自分もそう思う、だけどエラーハンドリングには注意した方がいいよ。」とアドバイスを書いています。
受信部のソースコード : スマートにまとめられているので参考に転載しておきます。
/* * The STM32 makes receiving chars into a large circular buffer simple * and requires no CPU time. The UART receiver DMA must be setup as CIRCULAR. */ #define CIRC_BUF_SZ 64 /* must be power of two */ static uint8_t rx_dma_circ_buf[CIRC_BUF_SZ]; static UART_HandleTypeDef *huart_cobs; static uint32_t rd_ptr; #define DMA_WRITE_PTR ( (CIRC_BUF_SZ - huart_cobs->hdmarx->Instance->CNDTR) & (CIRC_BUF_SZ - 1) ) void msgrx_init(UART_HandleTypeDef *huart) { huart_cobs = huart; HAL_UART_Receive_DMA(huart_cobs, rx_dma_circ_buf, CIRC_BUF_SZ); rd_ptr = 0; } static bool msgrx_circ_buf_is_empty(void) { if(rd_ptr == DMA_WRITE_PTR) { return true; } return false; } static uint8_t msgrx_circ_buf_get(void) { uint8_t c = 0; if(rd_ptr != DMA_WRITE_PTR) { c = rx_dma_circ_buf[rd_ptr++]; rd_ptr &= (CIRC_BUF_SZ - 1); } return c; }
エラーへの対処 : HALの余計なお世話を無効にする方法、これが私の探していた答えです。
/* These uart interrupts halt any ongoing transfer if an error occurs, disable them */ /* Disable the UART Parity Error Interrupt */ __HAL_UART_DISABLE_IT(&huart1, UART_IT_PE); /* Disable the UART Error Interrupt: (Frame error, noise error, overrun error) */ __HAL_UART_DISABLE_IT(&huart1, UART_IT_ERR);