串口数据实时处理:定时器+串口 判断串口数据接收完成
串口三种发送(Tx)方式:
普通、DMA、中断
核心思想
根据波特率来计算接收一个字节所需要的时间,当超过这个时间没有收到数据,则表明这一帧数据已经接受完毕
实现方法
串口中断函数接收第一个字节之后,开启定时器计数。接受下一个字节的时候清空定时器计数。如此,当没有数据接收后,计时器无法清零,当计时器计数超过设定的数值之后,触发定时器溢出中断,此时数据即接收完毕
下面是代码:
1、为了方便,定义宏和发送接收数组,主要内容如下所示:
#define USE_USART1
#ifdef USE_USART1
#define IAP_USART USART1
#define RCC_IAP_UART_CLK_ENABLE() RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE)
#define IAP_BAUDRATE (115200)
#define IAP_USART_IRQn USART1_IRQn
#define IAP_USART_IRQHandler USART1_IRQHandler
#define IAP_USART_IRQ_PREP (1)
#define IAP_USART_IRQ_SUBP (3)
#define IAP_RX_TIM TIM2
#define RCC_IAP_RX_TIM_CLK_ENABLE() RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE)
#define IAP_RX_TIM_IRQn TIM2_IRQn
#define IAP_RX_TIM_IRQHandler TIM2_IRQHandler
#define IAP_RX_TIM_IRQ_PREP (1)
#define IAP_RX_TIM_IRQ_SUBP (3)
/* 对应0.5ms
** 算法(BACK_RX_TIMER_PRES+1)*(BACK_RX_TIMER_PERI+1)/84000 (ms)
*/
#define IAP_RX_TIMER_PRES (83)
#define IAP_RX_TIMER_PERI (499)
#define IAP_RX_TIMER_DIV TIM_CKD_DIV1
/* DMA参数宏定义 */
#define IAP_DMA_CLK_ENABLE() RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA2,ENABLE)
#define IAP_TX_DMA_STREAM DMA2_Stream7
#define IAP_TX_DMA_FLAG_TCIF DMA_FLAG_TCIF7
#define IAP_TX_DMA_FLAG_FEIF DMA_FLAG_FEIF7
#define IAP_TX_DMA_FLAG_DMEIF DMA_FLAG_DMEIF7
#define IAP_TX_DMA_FLAG_TEIF DMA_FLAG_TEIF7
#define IAP_TX_DMA_FLAG_HTIF DMA_FLAG_HTIF7
#define IAP_TX_DMA_IT_TCIF DMA_IT_TCIF7
#define IAP_TX_DMA_IT_TEIF DMA_IT_TEIF7
#define IAP_TX_DMA_CHANNEL DMA_Channel_4
#define IAP_DMA_TX_IRQn DMA2_Stream7_IRQn
#define IAP_DMA_TX_IRQHandler DMA2_Stream7_IRQHandler
#define IAP_UARTDMA_IRQ_PREP (1)
#define IAP_UARTDMA_IRQ_SUBP (3)
#endif
/* 外部定义引用 */
extern struct rt_semaphore IapRx_OPSem;
uint8_t IAP_TX_BUF[IAP_BUFF_LEN] = {0};
__IO uint8_t IAP_RX_BUF[IAP_BUFF_LEN] = {0}; /* 接收缓冲,最大IAP_BUFF_LEN个字节*/
__IO uint16_t IAP_Rxoffset =0;
2、串口GPIO口、配置初始化、接收中断、发送DMA、定时器接收的配置:
GPIO口配置
/**
* @brief UART MSP Initialization
* This function configures the hardware resources used in this example
* @param None
* @retval None
*/
void RT_UART_MspInit(void)
{
GPIO_InitTypeDef GPIO_InitStruct = {0};
/* Peripheral clock enable */
RCC_AHB1PeriphClockCmd(Debug_U1_TX_CLK, ENABLE);
/**USART1 GPIO Configuration
PA9 ------> USART1_TX
PA10 ------> USART1_RX
*/
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF;
GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_NOPULL;
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_100MHz;
GPIO_InitStruct.GPIO_Pin = Debug_U1_TX_Pin | Debug_U1_RX_Pin;
GPIO_Init(Debug_U1_TX_GPIO_Port, &GPIO_InitStruct);
GPIO_PinAFConfig(Debug_U1_TX_GPIO_Port, Debug_U1_TX_PinSource, GPIO_AF_USART1);
GPIO_PinAFConfig(Debug_U1_RX_GPIO_Port, Debug_U1_RX_PinSource, GPIO_AF_USART1);
}
配置初始化、接收中断、发送DMA、定时器接收的配置:
/**
* @brief BSP层 IAP串口配置初始化
* @param None
* @retval None
*/
static void BSP_IapUartCFG_Init(void)
{
USART_InitTypeDef USART_InitStruct = {0};
/* RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE) */
RCC_IAP_UART_CLK_ENABLE();
USART_InitStruct.USART_BaudRate = IAP_BAUDRATE;
USART_InitStruct.USART_WordLength = USART_WordLength_8b;
USART_InitStruct.USART_StopBits = USART_StopBits_1;
USART_InitStruct.USART_Parity = USART_Parity_No;
USART_InitStruct.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
USART_InitStruct.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
USART_Init(IAP_USART, &USART_InitStruct);
USART_Cmd(IAP_USART,ENABLE); /* 该函数会使能串口 */
}
/**
* @brief BSP层 IAP串口中断的初始化
* @param None
* @retval None
*/
static void BSP_IapUartNVIC_Init(void)
{
NVIC_InitTypeDef NVIC_InitStruct = {0};
USART_ITConfig(IAP_USART,USART_IT_RXNE,ENABLE); /* 开启接收中断 */
/* 使能串口中断 */
NVIC_InitStruct.NVIC_IRQChannel = IAP_USART_IRQn;
NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;
NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = IAP_USART_IRQ_PREP;
NVIC_InitStruct.NVIC_IRQChannelSubPriority = IAP_USART_IRQ_SUBP;
NVIC_Init(&NVIC_InitStruct); /* 定义串口中断优先级 */
}
/**
* @brief BSP层 IAP串口DMA的初始化
* @param None
* @retval None
*/
static void BSP_IapUartDMA_Init(void)
{
DMA_InitTypeDef DMA_InitStruct = {0};
NVIC_InitTypeDef NVIC_InitStruct = {0};
/* 使能DMA时钟 */
IAP_DMA_CLK_ENABLE();
DMA_DeInit(IAP_TX_DMA_STREAM);
DMA_InitStruct.DMA_PeripheralBaseAddr = (uint32_t)(&(IAP_USART->DR)); /* 外设基地址 */
DMA_InitStruct.DMA_Memory0BaseAddr = (uint32_t)(IAP_TX_BUF); /* 内存基地址 */
DMA_InitStruct.DMA_PeripheralInc = DMA_PeripheralInc_Disable; /* 外设非增量模式 */
DMA_InitStruct.DMA_MemoryInc = DMA_MemoryInc_Enable; /* 存储器增量模式 */
DMA_InitStruct.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte; /* 外设数据长度:8位 */
DMA_InitStruct.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte; /* 存储器数据长度:8位 */
DMA_InitStruct.DMA_Mode = DMA_Mode_Normal; /* 外设流控模式 */
DMA_InitStruct.DMA_Priority = DMA_Priority_VeryHigh; /* 优先级 */
DMA_InitStruct.DMA_FIFOMode = DMA_FIFOMode_Disable;
DMA_InitStruct.DMA_FIFOThreshold = DMA_FIFOThreshold_Full;
DMA_InitStruct.DMA_MemoryBurst = DMA_MemoryBurst_Single; /* 存储器突发单次传输 */
DMA_InitStruct.DMA_PeripheralBurst = DMA_PeripheralBurst_Single; /* 外设突发单次传输 */
DMA_InitStruct.DMA_Channel = IAP_TX_DMA_CHANNEL; /* 通道选择 */
DMA_InitStruct.DMA_DIR = DMA_DIR_MemoryToPeripheral; /* 存储器到外设 */
DMA_InitStruct.DMA_BufferSize = IAP_BUFF_LEN;
/* 初始化DMA */
DMA_Init(IAP_TX_DMA_STREAM, &DMA_InitStruct);
/* 清除DMA2_Steam7传输完成标志 */
DMA_ClearFlag(IAP_TX_DMA_STREAM,IAP_TX_DMA_FLAG_TCIF);
DMA_ITConfig(IAP_TX_DMA_STREAM, DMA_IT_TC, ENABLE);
/* 配置DMA发送中断 */
NVIC_InitStruct.NVIC_IRQChannel = IAP_DMA_TX_IRQn;
NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;
NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = IAP_UARTDMA_IRQ_PREP;
NVIC_InitStruct.NVIC_IRQChannelSubPriority = IAP_UARTDMA_IRQ_SUBP;
NVIC_Init(&NVIC_InitStruct);
}
/**
* @brief BSP层 IAP串口定时器的初始化
* @param None
* @retval None
*/
static void BSP_IapUartTIM_Init(void)
{
TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStruct = {0};
NVIC_InitTypeDef NVIC_InitStruct = {0};
/* 接收用的定时器 */
RCC_IAP_RX_TIM_CLK_ENABLE();
TIM_DeInit( IAP_RX_TIM );/* 复位RX定时器 */
TIM_TimeBaseInitStruct.TIM_Period = IAP_RX_TIMER_PERI;
TIM_TimeBaseInitStruct.TIM_Prescaler = IAP_RX_TIMER_PRES;
TIM_TimeBaseInitStruct.TIM_ClockDivision = IAP_RX_TIMER_DIV;
TIM_TimeBaseInitStruct.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInit(IAP_RX_TIM, &TIM_TimeBaseInitStruct);
TIM_ITConfig(IAP_RX_TIM, TIM_IT_Update, ENABLE);
/* 配置定时器中断 */
NVIC_InitStruct.NVIC_IRQChannel = IAP_RX_TIM_IRQn;
NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = IAP_RX_TIM_IRQ_PREP;
NVIC_InitStruct.NVIC_IRQChannelSubPriority = IAP_RX_TIM_IRQ_SUBP;
NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStruct);
TIM_ClearFlag(IAP_RX_TIM, TIM_FLAG_Update);
TIM_Cmd(IAP_RX_TIM,DISABLE);
}
/**
* @brief BSP层 IAP串口的初始化
* 包括其接收中断、配置、发送DMA、接收定时器的初始化
* @param None
* @retval None
*/
void BSP_IAP_Uart_Init(void)
{
/* 配置初始化 */
BSP_IapUartCFG_Init();
/* 中断初始化 */
BSP_IapUartNVIC_Init();
/* DMA初始化 */
BSP_IapUartDMA_Init();
/* 定时器初始化 */
BSP_IapUartTIM_Init();
}
3、串口发送DMA、串口接收中断处理函数、串口接收定时器的中断处理函数、串口DMA发送的中断处理函数
/**
* @brief IAP串口发送DMA
* @param None
* @retval None
*/
void BSP_IAP_USART_Transmit_DMA(uint16_t Size)
{
if ((Size > IAP_BUFF_LEN) || (Size == 0))
{ return; }
/* Disable the USART TX DMA request */
USART_DMACmd(IAP_USART, USART_DMAReq_Tx, DISABLE);
/* Disable the DMA TX Streams */
DMA_Cmd(IAP_TX_DMA_STREAM, DISABLE);
/* Clear all falgs of DMA TX Streams */
DMA_ClearFlag(IAP_TX_DMA_STREAM,
(IAP_TX_DMA_FLAG_FEIF | IAP_TX_DMA_FLAG_DMEIF | IAP_TX_DMA_FLAG_TEIF |
IAP_TX_DMA_FLAG_HTIF | IAP_TX_DMA_FLAG_TCIF )
);
/* Set the DMA TX Stream lenth */
IAP_TX_DMA_STREAM->NDTR = Size;
/* Enable the DMA TX Streams */
DMA_Cmd(IAP_TX_DMA_STREAM, ENABLE);
/* Enable the USART TX DMA request */
USART_DMACmd(IAP_USART, USART_DMAReq_Tx, ENABLE);
}
/**
* @brief IAP串口接收中断处理函数
* @param None
* @retval None
*/
void IAP_USART_IRQHandler(void)
{
/* enter interrupt */
rt_interrupt_enter();
if(USART_GetITStatus(IAP_USART,USART_IT_RXNE) == SET)
{
/* 清除中断标志 */
USART_ClearITPendingBit(IAP_USART,USART_IT_RXNE);
/* 新方案 待校验*/
IAP_RX_BUF[IAP_Rxoffset%IAP_BUFF_LEN] = (u8)(USART_ReceiveData(IAP_USART) & 0xFF);
IAP_Rxoffset++;
if (IAP_Rxoffset >= IAP_BUFF_LEN) IAP_Rxoffset = 0;
/* 清零计数器, 并启动定时器 */
TIM_SetCounter(IAP_RX_TIM, 0);
TIM_Cmd(IAP_RX_TIM,ENABLE);
}
if (USART_GetITStatus(IAP_USART, USART_IT_ORE_RX) == SET)
{
USART_ClearITPendingBit(IAP_USART,USART_IT_ORE_RX);
USART_ReceiveData(IAP_USART);
}
/* leave interrupt */
rt_interrupt_leave();
}
/**
* @brief IAP串口接收定时器的中断处理函数
* @param None
* @retval None
*/
void IAP_RX_TIM_IRQHandler(void)
{
/* enter interrupt */
rt_interrupt_enter();
if (TIM_GetITStatus(IAP_RX_TIM, TIM_IT_Update) != RESET)
{
TIM_ClearITPendingBit(IAP_RX_TIM, TIM_IT_Update);
TIM_SetCounter(IAP_RX_TIM, 0);
/* TIM enable counter */
TIM_Cmd(IAP_RX_TIM, DISABLE);
rt_sem_release(&IapRx_OPSem);
}
/* leave interrupt */
rt_interrupt_leave();
}
/**
* @brief IAP串口DMA发送的中断处理函数
* @param None
* @retval None
*/
void IAP_DMA_TX_IRQHandler(void)
{
/* enter interrupt */
rt_interrupt_enter();
if (DMA_GetITStatus(IAP_TX_DMA_STREAM, IAP_TX_DMA_IT_TEIF))
{
DMA_ClearITPendingBit(IAP_TX_DMA_STREAM, IAP_TX_DMA_IT_TEIF);
DMA_Cmd(IAP_TX_DMA_STREAM,DISABLE); /* 传输完成以后关闭串口DMA */
}
if (DMA_GetITStatus(IAP_TX_DMA_STREAM, IAP_TX_DMA_IT_TCIF))
{
DMA_ClearITPendingBit(IAP_TX_DMA_STREAM, IAP_TX_DMA_IT_TCIF);
/* TODO 待完善发送完成需要关闭该关闭的东西 */
DMA_Cmd(IAP_TX_DMA_STREAM,DISABLE); /* 传输完成以后关闭串口DMA */
}
/* leave interrupt */
rt_interrupt_leave();
}
4、实现:
线程:
/**
* @brief IAP升级串口接收处理
* @param void *paramemter
* @retval None
*/
static void Bsp_iaprx_task(void *paramemter)
{
for (;;)
{
rt_sem_take(&IapRx_OPSem, RT_WAITING_FOREVER);
DRV_IAP_Receive_Task();
}
}
/**
* @brief IAP升级串口发送处理
* @param void *paramemter
* @retval None
*/
static void Bsp_iaptx_task(void *paramemter)
{
for (;;)
{
rt_sem_take(&IapTx_OPSem, RT_WAITING_FOREVER);
APP_IAP_Master_Task();
}
}
线程调用接收处理部分 :
/* 背板串口APP应用回调函数 */
extern void IAP_RxPFrame_DataCallback(void);
extern void IAP_TxPFrame_DataCallback(void);
extern uint8_t IAP_TX_BUF[IAP_BUFF_LEN];
extern __IO uint8_t IAP_RX_BUF[IAP_BUFF_LEN];
extern __IO uint8_t IAP_Rxoffset;
struct rt_semaphore IapRx_OPSem;
struct rt_semaphore IapTx_OPSem;
/* 线程调用接收处理部分 */
void DRV_IAP_Receive_Task(void)
{
rt_memcpy((void*)IAP_TX_BUF, (void*)IAP_RX_BUF, IAP_Rxoffset);
rt_sem_release(&IapTx_OPSem);
}
通讯发送主任务:
extern __IO uint8_t IAP_Rxoffset;
/* IAP串口发送主任务 */
void APP_IAP_Master_Task(void)
{
BSP_IAP_USART_Transmit_DMA(IAP_Rxoffset);
}
此代码发一次回一次,接收不清零。