当前位置: 首页 > >

STM32 串口DMA收发不定长数据(cubeMx)

发布时间:

1、配置SYS、RCC以及时钟树




2、配置串口

串口选择异步通信,波特率115200,数据位8,停止位1,无校验;添加TX、RX DMA,DMA配置为Normal模式,最后使能串口全局中断入口函数。






3、生成工程

保存选择.c与.h分开,然后确定工程名称、保存路径及工程类型,此处为MDK5的工程。




4、源码添加
a.printf支持

#define USART1_Printf
int fputc(int ch,FILE *f)
{
#if defined USART1_Printf
HAL_UART_Transmit(&huart1,(uint8_t *)&ch,1,0xFFFF);
#elif defined USART3_Printf
HAL_UART_Transmit(&huart3,(uint8_t *)&ch,1,0xFFFF);
#else
return ch;
#endif

return ch;
}

b.串口 DMA收发缓存区配置

#define UART_DMA_BUFF_LEN_MAX 255

uint8_t Uart1RxBuff[UART_DMA_BUFF_LEN_MAX];
uint8_t Uart1TxBuff[UART_DMA_BUFF_LEN_MAX];
uint16_t Uart1RxBuffLen = 0;

uint8_t Uart3RxBuff[UART_DMA_BUFF_LEN_MAX];
uint8_t Uart3TxBuff[UART_DMA_BUFF_LEN_MAX];
uint16_t Uart3RxBuffLen = 0;

void USRT_DMA_IDLE_Start()
{
__HAL_UART_ENABLE_IT(&huart1,UART_IT_IDLE); //使能IDLE中断
HAL_UART_Receive_DMA(&huart1,Uart1RxBuff,UART_DMA_BUFF_LEN_MAX); //开启DMA接收

__HAL_UART_ENABLE_IT(&huart3,UART_IT_IDLE);
HAL_UART_Receive_DMA(&huart3,Uart3RxBuff,UART_DMA_BUFF_LEN_MAX);
}

c.串口接收回调

在stm32f1xx_it.c文件中,于串口中断函数调用串口回调函数,该函数自己实现。


//引用外部串口回调函数
extern void UART_Rx_Callback(UART_HandleTypeDef *huart);


/**
* @brief This function handles USART1 global interrupt.
*/
void USART1_IRQHandler(void)
{
/* USER CODE BEGIN USART1_IRQn 0 */

/* USER CODE END USART1_IRQn 0 */
HAL_UART_IRQHandler(&huart1);
/* USER CODE BEGIN USART1_IRQn 1 */
UART_Rx_Callback(&huart1); //调用串口接收回调
/* USER CODE END USART1_IRQn 1 */
}

/**
* @brief This function handles USART3 global interrupt.
*/
void USART3_IRQHandler(void)
{
/* USER CODE BEGIN USART3_IRQn 0 */

/* USER CODE END USART3_IRQn 0 */
HAL_UART_IRQHandler(&huart3);
/* USER CODE BEGIN USART3_IRQn 1 */
UART_Rx_Callback(&huart3); //调用串口接收回调
/* USER CODE END USART3_IRQn 1 */
}

d.回调函数IDLE中断

IDLE中断用于帧判断。


void UART_Rx_Callback(UART_HandleTypeDef *huart)
{
//判断IDLE中断
if(!__HAL_UART_GET_FLAG(huart,UART_FLAG_IDLE))
{
return;
}
//清除IDLE中断--直接读取SR、DR寄存器即可清除IDLE中断
__HAL_UART_CLEAR_IDLEFLAG(huart);
// temp = huart->Instance->SR;
// temp = huart->Instance->DR;

//判断中断源
if(huart == &huart1)
{
UART1_Rx_IDLE_Callback();
}
else if(huart == &huart3)
{
UART3_Rx_IDLE_Callback();
}
}

e.处理串口数据

串口接收完数据通过DMA回发。


void UART1_Rx_IDLE_Callback()
{
//暂停DMA
HAL_UART_DMAStop(&huart1);

//获取DMA缓存区剩余长度
// Uart3BuffLen_DMA = __HAL_DMA_GET_COUNTER(&hdma_usart1_rx);

//实际长度 = 总长度-剩余长度
Uart1RxBuffLen = UART_DMA_BUFF_LEN_MAX-__HAL_DMA_GET_COUNTER(&hdma_usart1_rx);

//回发测试
UART_Transmit_DMA(&huart1,Uart1RxBuff,Uart1RxBuffLen);

//开始DMA接收
HAL_UART_Receive_DMA(&huart1,Uart1RxBuff,UART_DMA_BUFF_LEN_MAX);
}
void UART3_Rx_IDLE_Callback()
{
//暂停DMA
HAL_UART_DMAStop(&huart3);

//获取DMA缓存区剩余长度
// Uart3BuffLen_DMA = __HAL_DMA_GET_COUNTER(&hdma_usart1_rx);

//实际长度 = 总长度-剩余长度
Uart3RxBuffLen = UART_DMA_BUFF_LEN_MAX-__HAL_DMA_GET_COUNTER(&hdma_usart3_rx);

UART_Transmit_DMA(&huart3,Uart3RxBuff,Uart3RxBuffLen);
//开始DMA接收
HAL_UART_Receive_DMA(&huart3,Uart3RxBuff,UART_DMA_BUFF_LEN_MAX);
}


HAL_StatusTypeDef UART_Transmit_DMA(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size)
{
uint8_t *TxBuff = NULL;

if(huart == &huart1)
{
TxBuff = Uart1TxBuff;;
}
else if(huart == &huart3)
{
TxBuff = Uart3TxBuff;
}

memmove(TxBuff,pData,Size);

//开始发送
return HAL_UART_Transmit_DMA(huart,TxBuff,Size);
}

f.注意事项

发送数据长度低于接收缓存区长度;


接收数据时,若数据超出缓存区长度则产生数据丢失、错误等现象;本次DMA配置为Normal,超出缓存区长度的数据直接丢失不接收数据长度为缓存区长度;若配置为Circlur模式,则超出部分数据会继续接收,覆盖缓存区旧数据,数据长度为实际长度对缓存区长度求余;若缓存区满,则调用回调函数“HAL_UART_RxCpltCallback”;


发送数据时若上一次发送缓存区未发送完成,则新数据会覆盖旧数据导致数据发送失败、错误;数据发送完成,则调用回调函数“HAL_UART_TxCpltCallback”;


g.解决方法

接收时,增大接收缓存区,或者分包发送,保证自定义串口协议中数据包最大长度低于缓存区长度;


或者DMA接收设为Circlur模式,合理利用以下函数,及时将缓存区数据读出,避免数据被覆盖;


void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart); //缓存区满时调用
void HAL_UART_RxHalfCpltCallback(UART_HandleTypeDef *huart); //缓存区存储一半时调用

?


发送时,注意发送完成回调函数:“HAL_UART_TxCpltCallback”,完成一次发送之后再通过标志位、信号量等通知下一次数据发送。


?



友情链接: