目录
一、接收中断+空闲中断
二、接收中断+外加定时器
三、接收中断+自定义结束符
四、串口DMA接收不定长数据
一、接收中断+空闲中断
RXNE中断和IDLE中断的区别?当接收到1个字节,就会产生RXNE中断,当接收到一帧数据,就会产生IDLE中断。比如给单片机一次性发送了8个字节,就会产生8次RXNE中断,1次IDLE中断。
(1)中断使能:
/* 使能接收中断*/
USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);
/* 使能空闲中断*/
USART_ITConfig(USART1, USART_IT_IDLE, ENABLE);
USART_Cmd(USART1, ENABLE);
(2)串口中断服务:
uint8_t ch[20];
char i = 0;
void USART1_IRQHandler(void)
{
if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET)//接收到一个字节
{
//ch = USART1->DR;
ch[i++] = USART_ReceiveData(USART1);
}
else if(USART_GetITStatus(USART1,USART_IT_IDLE) != RESET)//接收到一帧数据
{
USART1->SR;//先读SR
USART1->DR;//再读DR
i = 0;
printf("%s",ch);//只是测试,所以直接在中断里调用printf
}
}
二、接收中断+外加定时器
串口接收数据时启动定时器,每接收一帧数据要复位定时值以保证定时器不会溢出。
定时器的超时值设置为1.5倍接收一个数据的时间
超时的值设置要根据波特率调整,例如9600的波特率下,接收数据速率为9600bit/s = 1200B/s 也就是 0.83ms/B 那么我们的超时值可以设置为0.84*1.5 = 1.26ms 取个整1.3ms。
三、接收中断+自定义结束符
这种也是很高效的,具体的做法是,在一帧数据尾部添加一个小尾巴 ~ ~
这种方式缺点也是很明显的,即只适用于自定义的通讯格式,或者尾部结束符唯一确定的情况。
四、串口DMA接收不定长数据
思考:我们可以使用DMA去接收数据,不过DMA需要定长才能产生接收中断,如何接收不定长的数据呢?
(1)DMA+串口空闲中断
STM32的IDLE的中断在串口无数据接收的情况下,是不会一直产生的,产生的条件是这样的,当清除IDLE标志位后,必须有接收到第一个数据后,才开始触发,一断接收的数据断流,没有接收到数据,即产生IDLE中断。如果中断发送数据帧的速率很快,MCU来不及处理此次接收到的数据,中断又发来数据的话,这里不能开启,否则数据会被覆盖。有两种方式解决:
在重新开启接收DMA通道之前,将Rx_Buf缓冲区里面的数据复制到另外一个数组中,然后再开启DMA,然后马上处理复制出来的数据。
建立双缓冲,重新配置DMA_MemoryBaseAddr的缓冲区地址,那么下次接收到的数据就会保存到新的缓冲区中,不至于被覆盖。
串口配置:
/**
* @brief USART GPIO 配置,工作参数配置
* @param 无
* @retval 无
*/
void USART_Config(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
USART_InitTypeDef USART_InitStructure;
// 打开串口GPIO的时钟
DEBUG_USART_GPIO_APBxClkCmd(DEBUG_USART_GPIO_CLK, ENABLE);
// 打开串口外设的时钟
DEBUG_USART_APBxClkCmd(DEBUG_USART_CLK, ENABLE);
// 将USART Tx的GPIO配置为推挽复用模式
GPIO_InitStructure.GPIO_Pin = DEBUG_USART_TX_GPIO_PIN;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(DEBUG_USART_TX_GPIO_PORT, &GPIO_InitStructure);
// 将USART Rx的GPIO配置为浮空输入模式
GPIO_InitStructure.GPIO_Pin = DEBUG_USART_RX_GPIO_PIN;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_Init(DEBUG_USART_RX_GPIO_PORT, &GPIO_InitStructure);
// 配置串口的工作参数
// 配置波特率
USART_InitStructure.USART_BaudRate = DEBUG_USART_BAUDRATE;
// 配置 针数据字长
USART_InitStructure.USART_WordLength = USART_WordLength_8b;
// 配置停止位
USART_InitStructure.USART_StopBits = USART_StopBits_1;
// 配置校验位
USART_InitStructure.USART_Parity = USART_Parity_No ;
// 配置硬件流控制
USART_InitStructure.USART_HardwareFlowControl =
USART_HardwareFlowControl_None;
// 配置工作模式,收发一起
USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
// 完成串口的初始化配置
USART_Init(DEBUG_USARTx, &USART_InitStructure);
// 串口中断优先级配置
NVIC_Configuration();
#if USE_USART_DMA_RX
// 开启 串口空闲IDEL 中断
USART_ITConfig(DEBUG_USARTx, USART_IT_IDLE, ENABLE);
// 开启串口DMA接收
USART_DMACmd(DEBUG_USARTx, USART_DMAReq_Rx, ENABLE);
/* 使能串口DMA */
USARTx_DMA_Rx_Config();
#else
// 使能串口接收中断
USART_ITConfig(DEBUG_USARTx, USART_IT_RXNE, ENABLE);
#endif
#if USE_USART_DMA_TX
// 开启串口DMA发送
// USART_DMACmd(DEBUG_USARTx, USART_DMAReq_Tx, ENABLE);
USARTx_DMA_Tx_Config();
#endif
// 使能串口
USART_Cmd(DEBUG_USARTx, ENABLE);
}
DMA配置:
#if USE_USART_DMA_RX
static void USARTx_DMA_Rx_Config(void)
{
DMA_InitTypeDef DMA_InitStructure;
// 开启DMA时钟
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);
// 设置DMA源地址:串口数据寄存器地址*/
DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)USART_DR_ADDRESS;
// 内存地址(要传输的变量的指针)
DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)Usart_Rx_Buf;
// 方向:从内存到外设
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;
// 传输大小
DMA_InitStructure.DMA_BufferSize = USART_RX_BUFF_SIZE;
// 外设地址不增
DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
// 内存地址自增
DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
// 外设数据单位
DMA_InitStructure.DMA_PeripheralDataSize =
DMA_PeripheralDataSize_Byte;
// 内存数据单位
DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;
// DMA模式,一次或者循环模式
//DMA_InitStructure.DMA_Mode = DMA_Mode_Normal ;
DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;
// 优先级:中
DMA_InitStructure.DMA_Priority = DMA_Priority_VeryHigh;
// 禁止内存到内存的传输
DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;
// 配置DMA通道
DMA_Init(USART_RX_DMA_CHANNEL, &DMA_InitStructure);
// 清除DMA所有标志
DMA_ClearFlag(DMA1_FLAG_TC5);
DMA_ITConfig(USART_RX_DMA_CHANNEL, DMA_IT_TE, ENABLE);
// 使能DMA
DMA_Cmd (USART_RX_DMA_CHANNEL,ENABLE);
}
#endif
接收完成处理:
/*串口中断服务函数
*/
void DEBUG_USART_IRQHandler(void)
{
#if USE_USART_DMA_RX
/* 使用串口DMA */
if(USART_GetITStatus(DEBUG_USARTx,USART_IT_IDLE)!=RESET)
{
/* 接收数据 */
Receive_DataPack();
// 清除空闲中断标志位
USART_ReceiveData( DEBUG_USARTx );
}
#else
/* 接收中断 */
if(USART_GetITStatus(DEBUG_USARTx,USART_IT_RXNE)!=RESET)
{
Receive_DataPack();
}
#endif
}
(2)DMA接收+空闲中断(多字节接收)
中断配置:
__HAL_UART_ENABLE_IT(&huart1, UART_IT_IDLE); //使能空闲中断
HAL_UART_Receive_DMA(&huart1, RxDMABuf_1, RXBUF_1_SIZE); //启动DMA接收
中断处理:
if(huart->Instance == USART1)
{
//计算DMA接收的长度,也就是接收完一帧的长度
DMA_Usart1_RxSize = RXBUF_1_SIZE - __HAL_DMA_GET_COUNTER(&hdma_usart1_rx);
if(DMA_Usart1_RxSize > 0 )
{
memcpy(RxBuf_1, RxDMABuf_1, DMA_Usart1_RxSize);
RxBufSize_1 = DMA_Usart1_RxSize;
memset(RxDMABuf_1, 0, sizeof(RxDMABuf_1));
}
HAL_UART_Receive_DMA(&huart1, RxDMABuf_1, RXBUF_1_SIZE); //启动下一次接收
}
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)