私はUART in DMAプッシュボタンが押されるたびに単純な文字列を送信するモードを実装しようとしています。
そこで、CubeMXを使用してコードを生成し、UART2 TX DMA通常(循環ではない)モードで構成し、no FIFOおよびバーストなし)を構成しました。
デバッグモードでコードを実行するたびに、最初に文字列を送信しようとすると、問題なく動作して文字列を送信しますが、DMA IRQハンドラー内でTxHalfCpltCallbackを呼び出し、 TxCpltCallbackおよびUART gStateはBUSYモードのままなので、これ以上文字列を送信することはできません。
私の質問は、なぜTxCpltCallbackではなくTxHalfCpltCallbackを呼び出すのですか?そして、どのようにそれを処理する必要があります(HALリファレンスは、バッファーの後半を送信するのを待っていると言うので!何?)
また、データの次の半分を送信すると、UARTのgStateが解放されますか?
プロジェクトでUARTを設定する例を教えてください。
DMAを使用している場合、2つの割り込みがあります。1つはバッファの半分が送信され、もう1つは残りの半分が送信される(全体)場合です。
すべてが正常であれば、両方とも起動する必要があります。この理由は、大量のデータを送信するときに、DMAによってバッファの後半が送信されている間に、TxHalfCpltCallback
のバッファの前半に新しいデータのロードを開始できるためです。また、前半が送信されている間に、新しいデータをTxCpltCallback
の後半のバッファにロードできます。
利点は、次のデータチャンクをバッファにコピーする前に送信全体が完了するのを待つ必要はありませんが、送信の進行中に既にロードを開始できることです。
次に例を示します。
この例では、DMAを使用して2000バイトが転送されます。Transmit Half CompleteおよびTransmit Complete最高のパフォーマンスを実現する割り込み。
送信バッファーの前半には、CPUによってTransmit Half Complete割り込みコールバックで新しいデータがロードされますが、バッファーの後半はバックグラウンドでDMAによって送信されます。
次に、Transmit Completeで、送信バッファの後半がCPUによって新しいデータによってロードされ、前半(以前に更新された)が実行されますバックグラウンドでDMAによって送信されます。
#include "stm32f4xx.h"
uint8_t dma_buffer[2000];
volatile uint8_t toggle = 0;
UART_HandleTypeDef huart2;
DMA_HandleTypeDef hdma_usart2_tx;
void uart_gpio_init()
{
GPIO_InitTypeDef GPIO_InitStruct;
__GPIOA_CLK_ENABLE();
/**USART2 GPIO Configuration
PA2 ------> USART2_TX
PA3 ------> USART2_RX
*/
GPIO_InitStruct.Pin = GPIO_PIN_2|GPIO_PIN_3;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Pull = GPIO_PULLUP;
GPIO_InitStruct.Speed = GPIO_SPEED_HIGH;
GPIO_InitStruct.Alternate = GPIO_AF7_USART2;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
}
void uart_dma_init()
{
/* DMA controller clock enable */
__DMA1_CLK_ENABLE();
/* Peripheral DMA init*/
hdma_usart2_tx.Instance = DMA1_Stream6;
hdma_usart2_tx.Init.Channel = DMA_CHANNEL_4;
hdma_usart2_tx.Init.Direction = DMA_MEMORY_TO_PERIPH;
hdma_usart2_tx.Init.PeriphInc = DMA_PINC_DISABLE;
hdma_usart2_tx.Init.MemInc = DMA_MINC_ENABLE;
hdma_usart2_tx.Init.PeriphDataAlignment = DMA_MDATAALIGN_BYTE;
hdma_usart2_tx.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE;
hdma_usart2_tx.Init.Mode = DMA_NORMAL;
hdma_usart2_tx.Init.Priority = DMA_PRIORITY_LOW;
hdma_usart2_tx.Init.FIFOMode = DMA_FIFOMODE_DISABLE;
HAL_DMA_Init(&hdma_usart2_tx);
__HAL_LINKDMA(&huart2,hdmatx,hdma_usart2_tx);
/* DMA interrupt init */
HAL_NVIC_SetPriority(DMA1_Stream6_IRQn, 0, 0);
HAL_NVIC_EnableIRQ(DMA1_Stream6_IRQn);
}
void uart_init()
{
__USART2_CLK_ENABLE();
huart2.Instance = USART2;
huart2.Init.BaudRate = 115200;
huart2.Init.WordLength = UART_WORDLENGTH_8B;
huart2.Init.StopBits = UART_STOPBITS_1;
huart2.Init.Parity = UART_PARITY_NONE;
huart2.Init.Mode = UART_MODE_TX_RX;
huart2.Init.HwFlowCtl = UART_HWCONTROL_NONE;
huart2.Init.OverSampling = UART_OVERSAMPLING_16;
HAL_UART_Init(&huart2);
/* Peripheral interrupt init*/
HAL_NVIC_SetPriority(USART2_IRQn, 0, 0);
HAL_NVIC_EnableIRQ(USART2_IRQn);
}
/* This function handles DMA1 stream6 global interrupt. */
void DMA1_Stream6_IRQHandler(void)
{
HAL_DMA_IRQHandler(&hdma_usart2_tx);
}
void USART2_IRQHandler(void)
{
HAL_UART_IRQHandler(&huart2);
}
void HAL_UART_TxCpltCallback(UART_HandleTypeDef *huart)
{
uint16_t i;
toggle = !toggle;
for(i = 1000; i < 1998; i++)
{
if(toggle)
dma_buffer[i] = '&';
else
dma_buffer[i] = 'z';
}
dma_buffer[1998] = '\r';
dma_buffer[1999] = '\n';
}
void HAL_UART_TxHalfCpltCallback(UART_HandleTypeDef *huart)
{
uint16_t i;
for(i = 0; i < 1000; i++)
{
if(toggle)
dma_buffer[i] = 'y';
else
dma_buffer[i] = '|';
}
}
int main(void)
{
/* Reset of all peripherals, Initializes the Flash interface and the Systick. */
HAL_Init();
uart_gpio_init();
uart_dma_init();
uart_init();
uint16_t i;
for(i = 0; i < 1998; i++)
{
dma_buffer[i] = 'x';
}
dma_buffer[1998] = '\r';
dma_buffer[1999] = '\n';
while(1)
{
HAL_UART_Transmit_DMA(&huart2, dma_buffer, 2000);
}
}
この例は、STM32F4 Discoveryボード(STM32F407VG)用に作成されています。適切なDMAインスタンス、UART-DMAチャネル、GPIO、および代替機能の設定は、使用中のSTM32マイクロコントローラーに従って変更する必要があります。
問題は https://community.st.com/s/question/0D50X00009XkgtY/dma-uart-with-hal-remain-busy-bug に似ています
HAL_UART_IRQHandler()を有効にする必要があります
つまり、「main.c」(またはハードウェアを初期化する場所)内に次を追加します。
HAL_NVIC_SetPriority(USART2_IRQn, 0, 0);
HAL_NVIC_EnableIRQ(USART2_IRQn);
「stm32f4xx_it.c」内:
void USART2_IRQHandler(void)
{
HAL_UART_IRQHandler(&huart2);
}
ジャガーノートHALモンスターの代わりにベアレジスタアプローチを使用すると、DMA transfer(およびもちろん受信))をコーディングする方がはるかに簡単です。
例STM32F446(レジスタのリセット値を想定)
DMA1_Stream6 -> NDTR = nTransfers;
DMA1_Stream6 -> PAR = (uint32_t)&(USART2 -> DR);
DMA1_Stream6 -> M0AR = (uint32_t)&dataBuff;
DMA1_Stream6 -> CR = DMA_SxCR_CHSEL_2 | DMA_SxCR_MINC | DMA_SxCR_DIR_0 | DMA_SxCR_TCIE; // you can enable half transfer enable as well
USART2 -> BRR = FCLK / LOWSPEED;
USART2 -> CR3 |= USART_CR3_DMAT;
USART2 -> CR1 = (USART_CR1_TE | USART_CR1_RE | USART_CR1_UE);
DMA1_Stream6 -> CR |= DMA_SxCR_EN;
とても簡単ですよね?
void DMA1_Stream6_IRQHandler(void) { // now it does nothing only clears the flag
if(DMA1 -> HISR & (DMA_HISR_TCIF6)) {
DMA1 -> HIFCR |= DMA_HISR_TCIF6;
while(!(USART2 -> SR & USART_SR_TC));
}
}
私にとって、DMAを使用すると送信エラーが発生していました。 TXE割り込みを有効にすることで問題が解決しました。
void sas_write(char* buf, uint16_t size)
{
HAL_UART_Transmit_DMA(&uart_handle, buf, size) ;
__HAL_UART_ENABLE_IT(&uart_handle, UART_IT_TXE) ;
}
関数を使用する場合
HAL_StatusTypeDef HAL_UART_Transmit_DMA(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size)
CubeMX
ライブラリで、すべてのDMA割り込みを有効にします。DMA_SxCR
レジスタのHTIEビットをクリアすることにより、ハーフ転送割り込みを無効にできます。