stm32FのDMA Circular mode を使ったUART受信

stm32FのUARTでシリアルデータを受信するのにDMAのcircularモードを使うとDMAだけでデータをリングバッファに読み込むことが出来ます。
リングバッファからの読み出し関数は、DMAのポインタ(huart_cobs->hdmarx->Instance->CNDTR)をリングバッファへの書き込みポインタとして、読み出しポインタで書き込みポインタを追いかけるようにするだけです。

この手法は割り込みン数を使わずにデータをリングバッファに取り込めるのでとても簡単で効率が良いのですがSTM32CubeMXのHALライブラリを使っているとエラーが起きた時に勝手に送受信を止められてしまうという困った現象が起きます。

解決法を探していると同じことをやっている人がいて、「受信エラーはCRCチェックで対処するからHALライブラリのエラーハンドリングは邪魔なのでこうやってやめさせたよ。」という回答をみつけました :-P (下のトピック)。

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);

STM32CubeMXでTimerをエンコーダ入力モードに設定する

STM32CubeMXでタイマーをエンコーダ入力モードに設定する方法がわかりにくかったので書いておきます。

ターゲットCPU stm32F407

stm32Fシリーズはピンに2相エンコーダの信号を接続してアップダウンカウンタとして使う機能があります。
STM32CubeMXを使ってタイマをエンコーダ入力モードに設定しようとしたのですが、どのように設定してもエンコーダ入力にする方法がわからず、結局グーグル先生に聞いてロシア語のサイトでやっと設定方法が見つかりました。

端子をタイマーモードで使うにはまずPinout設定のCPU端子が表示されている画面で端子機能を選択します。
TIM3のCH1とCH2をエンコーダインターフェースにしたいので、端子をクリックしてPA6でTIM3_CH1を選択します。

configtimer2

同様にTIM3_CH2機能がある端子でTIM3_CH2を選択して、次に左の機能がツリー構造で表示されている部分でTIM3の機能を選択します。

普通はSlave Mode, Trigger Source, Clock Sourceと手探りで設定していけばなんとか答えにたどり着けるのですが今回はどうやってもエンコーダモードらしき設定が出てきません。
しばらく試行錯誤した後についにあきらめてグーグル先生に聞いた正解が次の通りでした。

configtimer1

上から全部Disableに設定すると最後にCombined Channelsで Encoder Modeが選べるようになります。

そうしておいてConfigure画面のタイマー設定で入力にどのCHを使うかを設定すればOKということでした。

configtimer3