简介
UART传输是我们实际应用开发中最常用的一种串行通信方式。关于通信方式有许多种类:串行,并行,单工,半双工,全双工,异步,同步等等。波特率是衡量通信速度的单位。比如2400bps = (1个停止位+1个结束位+8位数据位)* 240,也就是一秒钟传输240个字节。
在单片机中,当每次来数据的时候,都会产生中断,这个中断函数调用的地方是在启动文件(.s)中调用的,函数名也是固定的,所以我们只需要重构这个相应的中断函数就可以了。
但是实际应用中我们会有个问题,就是我们一般都是等传输结束后再去处理数据(对于一般有数据格式的还好,自行判断),但是什么时候结束尼,这是个问题。
实际有两个方法:
- 利用DMA的空闲中断
- 使用定时器做超时中断(比如说10ms没数据就等于传输结束)
下面主要介绍下利用定时器做超时中断
配置流程
本例程以stm32F0为例:
配置时钟源
/*这里的USART2是挂在APB1时钟线上,RCC模块提供了许多关于时钟相关的配置*/
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOA, ENABLE); //使能GPIOA的时钟
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM14, ENABLE); //使能TIM14的时钟
RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2, ENABLE);//使能USART的时钟
初始化
GPIO初始化
GPIO_InitTypeDef GPIO_InitStructure;
/*USART相应管脚初始化 USART2_TX ->PA2*/
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2; //选中串口默认输出管脚
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF; //定义GPIO功能,这里是复用功能,可以使用
/*GPIO_PinAFConfig函数注册管脚的复用功能*/
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; /*定义管脚的模式为推挽输出,实际用不上推挽方式,可以保证驱动能力*/
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;/*上拉*/
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;//GPIO的频率
GPIO_Init(GPIOA, &GPIO_InitStructure); //GPIO初始化
/*如果需要配置RX就配置一下RX相应的管脚,管脚模式要改为输入或者浮空*/
USART初始化
USART_InitTypeDef USART_InitStructure; //定义串口初始化结构体
/*串口通讯参数设置*/
USART_InitStructure.USART_BaudRate = 9600; //波特率
USART_InitStructure.USART_WordLength = USART_WordLength_8b; //数据位8位
USART_InitStructure.USART_StopBits = USART_StopBits_1; //停止位1位
USART_InitStructure.USART_Parity = USART_Parity_No; //校验位 无
USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
/*硬件流控制,需要相应的CTX和RTX配和*/
USART_InitStructure.USART_Mode = USART_Mode_Tx; //配置USART的模式,TX还是RX
USART_Init(USART_DEBUG, &USART_InitStructure); /*初始化USART*/
USART_ITConfig(USART, USART_IT_RXNE, ENABLE); /*使能接收完完成中断*/
定时器初始化
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
TIM_TimeBaseStructure.TIM_Period = (ms-1);/*装载值(其实这里比较好理解,我们配置好时钟后,
就可以知道1s产生多少次计数,这里就是计数值)--(该装载值/1s产生的总计数)s,计算出来的就是多少
秒产生中断*/
TIM_TimeBaseStructure.TIM_Prescaler =(div-1);/*预分频值,+1为分频系数
TIM_CLK = APB1_CLK / (div+1)
比如说主频是48M,div我们设置为4799,
那TIM的频率就是1M,即1秒产生(48,000,000 / 4800 = 10,000)个计数*/
TIM_TimeBaseStructure.TIM_ClockDivision = 0; //还不清楚具体干嘛,一般都填0
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; /*TIM向上计数模式,累加的,
比如说装载值为1999,即计数到1999的时候产生中断,前提是打开了中断*/
TIM_TimeBaseInit(tim, &TIM_TimeBaseStructure); /*根据TIM_TimeBaseInitStruct中指定的
参数初始化TIMx的时间基数单位*/
TIM_ITConfig(tim,TIM_IT_Update,ENABLE); //使能TIM中断源
中断初始化
/*中断管理模块是在NVIC模块下,可以配置*/
NVIC_InitTypeDef NVIC_InitStructure;
NVIC_InitStructure.NVIC_IRQChannel = channle;//中断通道,中断源里面有相应的宏,比如:TIM14_IRQn
NVIC_InitStructure.NVIC_IRQChannelPriority = priority; //中断优先级
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;//IRQ通道被使能
NVIC_Init(&NVIC_InitStructure); //根据NVIC_InitStruct中指定的参数初始化外设NVIC寄存器
使能
TIM_Cmd(tim, ENABLE); //使能TIMx外设
USART_Cmd(USART_DEBUG, ENABLE); //使能USART
收发
void USART1_IRQHandler(void)
{
if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET)
{
if(rcv_flag == 0){ /*是否刚开始接收*/
timer_cmd(AT_TIMER,1); /*开启定时器*/
timer_set_count(AT_TIMER,0); /*清空计数*/
}
rcv_flag = 1; /*已经在接收过程中了*/
if(rcv_offset >= MAX_RECV_NUM){ /*大于最大接收数,防止数组越界*/
USART_ReceiveData(USART1); /*用做清中断*/
return;
}
rcv_buf[rcv_offset] = (uint8_t)USART_ReceiveData(USART1); /*接收数据并保存到数组中*/
rcv_offset++; /*数据偏移1个字节*/
timer_set_count(AT_TIMER,0); /*重置定时器*/
}
}
/*该定时器用于串口接收超时判断*/
void TIM14_IRQHandler(void)
{
if (TIM_GetITStatus(TIM14, TIM_IT_Update) != RESET) //定时器中断
{
rcv_end = 1;
timer_cmd(AT_TIMER,0); //关闭定时器
TIM_ClearITPendingBit(TIM14, TIM_FLAG_Update); //清除中断待处理位
}
}