打印
[应用相关]

串口中断结合定时器实现稳定可靠的串口接收

[复制链接]
444|6
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
晓伍|  楼主 | 2019-7-5 08:30 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
  串口的接收方式可以有很多种,之前也提到过使用延时判断串口接收指正的值是否改变来进行接收完成的判断。也介绍过使用DMA的空闲中断触发以判断串口接收完成。以上两种方式,在一定情况下可以使用效果也还可以。延时判断的方式,对于不跑操作系统的工程里影响不大,但是到了一个跑实时系统的工程里,这种方式就显得不稳定了。而DMA方式接收,效率很高而且不占用处理器时间,但是这样接收就是太快了,在数据频繁发送的情况下,经常会把两条应该是分开的数据合为了一条。

    结合定时器的使用,以10ms为数据包间的最大间隔,如果超过10ms没有接收到新的数据,那么这一次数据就可以认为已经发送完成了。如果在10ms内接收到了新的数据,则把定时器的值重装并开始计时。


使用特权

评论回复
沙发
晓伍|  楼主 | 2019-7-5 08:30 | 只看该作者
具体代码如下。

    //定义接收缓冲区

unsigned char RX3_Temp[RX3_Recv_Len] = {0};     /* 临时缓冲 */
unsigned char RX3_Point = 0;          /* 缓冲区指针 */

使用特权

评论回复
板凳
晓伍|  楼主 | 2019-7-5 08:31 | 只看该作者
//初始化串口3

void USART3_Init_JAVE( u32 bound )
{               
                GPIO_InitTypeDef  GPIO_InitStructure;
                USART_InitTypeDef USART_InitStructure;
                NVIC_InitTypeDef  NVIC_InitStructure;
               
                USART_DeInit(USART3);               
                //使能串口3的时钟 和对应GPIO时钟
                RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART3, ENABLE);
                RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);               
                //配置TX引脚GPIO
                GPIO_InitStructure.GPIO_Pin=GPIO_Pin_10;
                GPIO_InitStructure.GPIO_Speed=GPIO_Speed_2MHz;
                GPIO_InitStructure.GPIO_Mode=GPIO_Mode_AF_PP;
                GPIO_Init(GPIOB,&GPIO_InitStructure);       
                //配置RX引脚GPIO
                GPIO_InitStructure.GPIO_Pin=GPIO_Pin_11;
                GPIO_InitStructure.GPIO_Mode=GPIO_Mode_IN_FLOATING;
                GPIO_Init(GPIOB,&GPIO_InitStructure);               
                //配置串口3
                USART_InitStructure.USART_BaudRate = bound;
                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(USART3, &USART_InitStructure);               
                //使能串口3
                USART_Cmd(USART3,ENABLE);
                //使能串口接收中断
                USART_ITConfig(USART3, USART_IT_RXNE, ENABLE);               
                //配置串口3接收中断
                NVIC_InitStructure.NVIC_IRQChannel = USART3_IRQn;
                NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
                NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
                NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
                NVIC_Init(&NVIC_InitStructure);               
                USART_ClearFlag(USART3, USART_FLAG_RXNE);               
                USART_GetFlagStatus(USART3,USART_FLAG_TC);     /* 先读SR,再写DR */                               
                TIM7_Int_Init(99,7199);                //10ms中断
                RX3_Point=0;                //清零
                TIM_Cmd(TIM7,DISABLE);                        //关闭定时器7
}


使用特权

评论回复
地板
晓伍|  楼主 | 2019-7-5 08:31 | 只看该作者
//初始化定时器7 及中断
//定时器7初始化和中断处理
void TIM7_Int_Init(u16 arr,u16 psc)
{
        TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;
        NVIC_InitTypeDef NVIC_InitStructure;
        RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM7, ENABLE); //时钟使能       
        //定时器TIM7初始化
        TIM_TimeBaseStructure.TIM_Period = arr; //设置在下一个更新事件装入活动的自动重装载寄存器周期的值       
        TIM_TimeBaseStructure.TIM_Prescaler =psc; //设置用来作为TIMx时钟频率除数的预分频值
        TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1; //设置时钟分割:TDTS = Tck_tim
        TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;  //TIM向上计数模式
        TIM_TimeBaseInit(TIM7, &TIM_TimeBaseStructure); //根据指定的参数初始化TIMx的时间基数单位
        TIM_ITConfig(TIM7,TIM_IT_Update,ENABLE ); //使能指定的TIM3中断,允许更新中断
        //中断优先级NVIC设置
        NVIC_InitStructure.NVIC_IRQChannel = TIM7_IRQn;  //TIM7中断
        NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 3;  //先占优先级0级
        NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;  //从优先级3级
        NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ通道被使能
        NVIC_Init(&NVIC_InitStructure);  //初始化NVIC寄存器
        TIM_Cmd(TIM7, ENABLE);  //使能TIMx                                         
}


使用特权

评论回复
5
晓伍|  楼主 | 2019-7-5 08:31 | 只看该作者
void TIM7_IRQHandler(void)   //TIM7中断
{
        if (TIM_GetITStatus(TIM7, TIM_IT_Update) != RESET)  //检查TIM7更新中断
        {

                RX3_Point|=0x80;//最高位置1,标记接收完成

                TIM_ClearITPendingBit(TIM7, TIM_IT_Update  );  //清除TIMx更新中断标志
                TIM_Cmd(TIM7, DISABLE);  //关闭TIM7
        }
}


使用特权

评论回复
6
晓伍|  楼主 | 2019-7-5 08:32 | 只看该作者
  //串口3中断
void USART3_IRQHandler( void )
{
        char temp = 0;
        if(USART_GetITStatus(USART3, USART_IT_RXNE) == SET)
        {           
                temp = USART_ReceiveData( USART3 );     /* 读取USART3数据,自动清零标志位 RXNE */
                if((RX3_Point&0x80) == 0)
                {
                        if(RX3_Point < RX3_Recv_Len)//还可以接收数据
                        {                                       
                                TIM_SetCounter(TIM7,0);        //计数器清空   
                                if(RX3_Point==0)                                 //使能定时器7的中断
                                {
                                        TIM_Cmd(TIM7,ENABLE);//使能定时器7
                                }
                                RX3_Buff[RX3_Point++]=temp;        //记录接收到的值         
                        }
                        else
                        {
                                RX3_Point|=0x80; //长度超出限制了
                        }
                }
        }
}


使用特权

评论回复
7
晓伍|  楼主 | 2019-7-5 08:32 | 只看该作者
在应用程序中,只需要一直扫描判断RX3_Point的最高位是否为1就可以判断是否接收完成了。
if(RX3_Point & 0x80)

{

     printf("接收完成\r\n");

    RX3_Point =0;

}

使用时需要注意的是,扫描到一次结果后,对其进行处理,最后必须要对RX3_Point 进行清零操作,否则将不再接收新的数据。

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

60

主题

3893

帖子

1

粉丝