USART2とPCで通信するプログラムを作成します。 送受信共にDMAを使用します。 受信に関しては、リングバッファを実装します。 PCから受信したデータをPCへ返送します。 |
| 0
1.1. USART2の設定 USART2 の設定に関しては USARTの使い方を参照してください。 |
1.2. DMAの設定 UART2のDMA Settingsを選択しUSART2_TXとUSART2_RXを追加します。DMA ChannelはDMA1 Channel 4 と DMA1 Channel 5を設定してください。 DMA1 Channel 2/3も選択可能ですが、categoryの関係で使えないようです。 USART2_RXのIncrement Addressは、ここではチェックを外してください。 USART2_RXのIncrement Addressはチェックしてください。 |
2.1. ヘッダファイル |
今回は main.cには記述せず、別ファイルを追加して記述します。 ./Core/Inc にusart2.hを追加し、以下の様に記述します。 リングバッファのサイズはここでは8バイトにしておきます。 // // USART2 DMA Ring Buffer 対応 // #define RINGBUFFSIZE (8) typedef struct { uint8_t ubOneData; uint8_t ubBuffer[RINGBUFFSIZE]; uint32_t uwInIndex; uint32_t uwOutIndex; } RINGBUFF, *PRINGBUFF; extern RINGBUFF g_stRing; void USART_Receive_DMA_Start(UART_HandleTypeDef *huart, PRINGBUFF pRing); uint32_t GetReceiveBytes(UART_HandleTypeDef *huart); uint16_t GetData(UART_HandleTypeDef *huart); void WaitTransmitEnd(UART_HandleTypeDef *huart); void TransmitStrings(UART_HandleTypeDef *huart, char *pData); void TransmitOneData(UART_HandleTypeDef *huart, uint8_t ubData); |
2.2. 受信DMA開始関数 |
次に ./Core/Src に usart2.cを追加します。 最初に受信DMA開始関数を記述します。 // // 受信DMA開始 // void USART_Receive_DMA_Start(UART_HandleTypeDef *huart, PRINGBUFF pRing) { (*pRing).uwInIndex = 0; (*pRing).uwOutIndex = 0; HAL_UART_Receive_DMA(huart, &(*pRing).ubOneData, sizeof((*pRing).ubOneData)); } RINGBUF構造体のメンバuwOneDataは、USART2で受信したデータをDMA転送で受け取るためのバッファです。 |
2.3. 受信DMA完了割込み処理 |
次に受信完了後の割込み処理を記述します。// // USART RX DMA Interrupt Callback // void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart) { PRINGBUFF pRing = (PRINGBUFF)(*huart).pRxBuffPtr; if ((*pRing).uwInIndex - (*pRing).uwOutIndex < RINGBUFFSIZE) { (*pRing).ubBuffer[(*pRing).uwInIndex++ & (RINGBUFFSIZE - 1)] = (*pRing).ubOneData; } HAL_UART_Receive_DMA(huart, &(*pRing).ubOneData, sizeof((*pRing).ubOneData)); } RINGBUFFのメンバ uwOneDataに受信データが存在するので、リングバッファにストアします。 バッファフルの場合、今回は捨てます。 (*huart).pRxBuffPtrは、割込みやDMAで使用する受信バッファへのポインタ変数で、HAL_UART_Receive_DMAで設定されます。 HAL_UART_Receive_DMA呼び出しで uwOneDataのポインタを渡しているので、キャスティングすれば RINGBUFF構造体を示すことになります。 本来はUART_HandleTypeDefにRINGBUFF構造体を追加したいのですが、ここに変更を加えるのは抵抗があります。 オブジェクト化としたいための苦肉の策です。 賛否両論あるかと思いますが。。。 |
2.4. その他関数 |
GetReceiveBytes関数はリングバッファ内の有効データ数を返します。 GetData関数はリングバッファ内の有効データ1バイトを返します。 有効データがない場合は0xFFFFを返します。 // // リングバッファ内受信データ数を返す // uint32_t GetReceiveBytes(UART_HandleTypeDef *huart) { PRINGBUFF pRing = (PRINGBUFF)(*huart).pRxBuffPtr; return (*pRing).uwInIndex - (*pRing).uwOutIndex; } // // リングバッファ内受信データを返す // uint16_t GetData(UART_HandleTypeDef *huart) { PRINGBUFF pRing = (PRINGBUFF)(*huart).pRxBuffPtr; uint16_t uhData = 0xFFFF; if ((*pRing).uwInIndex - (*pRing).uwOutIndex != 0) { uhData = (uint16_t)(*pRing).ubBuffer[(*pRing).uwOutIndex++ & (RINGBUFFSIZE - 1)]; } return uhData; } |
2.5. 送信DMA関数 |
TransmitStrings関数は文字列送信、TransmitOneData関数は1バイト送信となります。 両関数ともHAL_UART_Transmit_DMAでDMA送信後、WaitTransmitEnd関数で送信完了まで待つようにしています。 // // 送信DMA完了待ち // void WaitTransmitEnd(UART_HandleTypeDef *huart) { while ((*huart).gState != HAL_UART_STATE_READY); } // // 文字列DMA送信 // void TransmitStrings(UART_HandleTypeDef *huart, char *pData) { HAL_UART_Transmit_DMA(huart, (uint8_t *)pData, strlen(pData)); WaitTransmitEnd(huart); } // // バイナリデータDMA送信 // void TransmitOneData(UART_HandleTypeDef *huart, uint8_t ubData) { HAL_UART_Transmit_DMA(huart, (uint8_t *)&ubData, sizeof(ubData)); WaitTransmitEnd(huart); } |
2.6. main関数への処理追加 まずmain.hにusart2.hを追加します。/* Private includes ----------------------------------------------------------*/ /* USER CODE BEGIN Includes */ #include "usart2.h" /* USER CODE END Includes */ /* USER CODE BEGIN 2 */ TransmitStrings(&huart2, "DMA start\r\n"); USART_Receive_DMA_Start(&huart2, &g_stRing); /* USER CODE END 2 */ /* USER CODE BEGIN WHILE */ while (1) { /* USER CODE END WHILE */ /* USER CODE BEGIN 3 */ if ((uhData = GetData(&huart2)) != 0xFFFF) { TransmitOneData(&huart2, (uint8_t)uhData); } } /* USER CODE END 3 */ |