|
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 */
|