USART (轮询 中断 DMA)
in 机械/电子/内核
快速目录
USART三种收发方式简介
接受发送数据就像你去收发快递,发送还好说因为你知道自己什么时候发快递,这样就能很好的安排什么时候发快递,你完全可以控制发快递的时间不会影响你的工作。但是接受数据是个麻烦的事情,因为是别人发送的数据如果你不去收它就没了,而且这完全是不可控的,也就是说无法预料,为了不干扰你当前的工作我们一般用DMA接受。下面我举个栗子。
轮询接受:你在工作的安排中不停的询问有没有你的快递,在程序中就是一段语句看寄存器上有没有信号,缺点使你工作效率低下,而且没询问的时候消息就没有收到。
中断接受:中断像个宿管大爷,他不会帮你保存,一来快递就喊你下来拿,如果你不来就没有了。这样的话你可能在写作业,突然下来拿快递效率也很低。
DMA接受:这就像菜鸟驿站,菜鸟驿站帮你保存,包裹接受的过程都不用你做,你只要快速的在一个包裹接受结束时吧包裹拿走。
下面是喜闻乐见的“shut up and shoe your code”环节 MX配置就是USRAT打开DMA要打开USRAT中断不然空闲中断没法触发
发送(重定义printf中fputc)
#ifdef __GNUC__
#define PUTCHAR_PROTOTYPE int __io_putchar(int ch) //判断编译器
#else
#define PUTCHAR_PROTOTYPE int fputc(int ch, FILE *f) //printf输出到串口,需要将fputc里面的输出指向串口(重定向)
#endif
PUTCHAR_PROTOTYPE
{
HAL_UART_Transmit(&huart1, (uint8_t *)&ch , 1 , 0xFFFF); //轮询输出
return ch;
}
接受
- 中断处理函数
void UsartReceive_IDLE(UART_HandleTypeDef *huart)
{
uint32_t temp; //空闲余量
if((__HAL_UART_GET_FLAG(huart,UART_FLAG_IDLE) != RESET)) //判断空闲中断有没有触发
{
__HAL_UART_CLEAR_IDLEFLAG(&huart1); //清理空闲中断有没有触发
HAL_UART_DMAStop(&huart1); //暂停DMA,因为操作时不能改DMA
temp = huart1.hdmarx->Instance->CNDTR; //获取DMA余量
UsartType.RX_Size = RX_LEN - temp; //DMA开启空间 - DMA余量 = 收取的实际长度值
UsartType.RX_flag=1; //接受结构体设置flag,告知有新串口输入
HAL_UART_Receive_DMA(&huart1,UsartType.RX_pData,RX_LEN);//继续开启DMA为了下一段输入
}
}
- 开启中断
位置:main.c /* USER CODE BEGIN 2 */
__HAL_UART_CLEAR_IDLEFLAG(&huart1); //清理空闲中断flag
__HAL_UART_ENABLE_IT(&huart1,UART_IT_IDLE); //使能空闲中断
HAL_UART_Receive_DMA(&huart1, UsartType.RX_pData, 1024); //开启接受,这样才能有空闲中断触发,一定要加上
- 中断处理函数加入中断Handle
位置: stm32f1xx_it.c void USART1_IRQHandler(void) /* USER CODE BEGIN USART1_IRQn 0 */
这里是stm103 stm405就在是4xx_it 在这个中断.c里面找到USART1_IRQHandler(void)函数添加 UsartReceive_IDLE(&huart1);执行自己写>的中断处理函数。
void USART1_IRQHandler(void)
{
/* USER CODE BEGIN USART1_IRQn 0 */
UsartReceive_IDLE(&huart1); //执行自己写的中断处理函数。
/* USER CODE END USART1_IRQn 0 */
HAL_UART_IRQHandler(&huart1);
/* USER CODE BEGIN USART1_IRQn 1 */
/* USER CODE END USART1_IRQn 1 */
}