OpenEdv-开源电子网

 找回密码
 立即注册
正点原子全套STM32/Linux/FPGA开发资料,上千讲STM32视频教程免费下载...
查看: 2796|回复: 0

STM32F103系列--串口之接收部分的总结

[复制链接]

15

主题

50

帖子

1

精华

中级会员

Rank: 3Rank: 3

积分
322
金钱
322
注册时间
2014-10-29
在线时间
37 小时
发表于 2017-9-14 01:12:50 | 显示全部楼层 |阅读模式
前提:对应USART的接收部分,前面的初始化代码就不贴了,和原子的一样,注意一点,串口中断的配置只开启了USART_IT_RXNE


(1)      当使用发送字节函数时需要等待数据发送完成,或是数据已经由DR转移到移位寄存器了,再发送下一个数据;(注意:这里没有使用发送中断的方法)
关于这点的总结:
1 如果你不加while(USART_GetFlagStatus(USART1,USART_FLAG_TXE)!=SET)语句的话,会造成什么现象。你的数据也能发出去但是会丢失很多数据,原因是你可能将数据放入DR的速度远远快于串口向外发送的速度,也就是说你放入第一个数据到DR,硬件自动将这个数据转到移位寄存器,然后发送,它还没发完呢,你可能已经向DR写了N个数据了,那么当串口发完你第一个数据时,才来DR读数据到移位寄存器,你想中间可能丢了N-1个数据了。
2 检测什么时候向DR寄存器写入数据到底使用USART_GetFlagStatus(USART1,USART_FLAG_TXE)还是USART_GetFlagStatus(USART1,USART_FLAG_TC)呢,看手册:
    1. 通过在USART_CR1寄存器上置位UE位来激活USART
    2. 编程USART_CR1的M位来定义字长。
    3. 在USART_CR2中编程停止位的位数。
    4. 如果采用多缓冲器通信,配置USART_CR3中的DMA使能位(DMAT)。按多缓冲器通信中的描述配置DMA寄存器。
    5. 利用USART_BRR寄存器选择要求的波特率。
    6. 设置USART_CR1中的TE位,发送一个空闲帧作为第一次数据发送。
    7. 把要发送的数据写进USART_DR寄存器(此动作清除TXE位)。在只有一个缓冲器的情况下,对每个待发送的数据重复步骤7。
    8. 在USART_DR寄存器中写入最后一个数据字后,要等待TC=1,它表示最后一个数据帧的传输结束。当需要关闭USART或需要进入停机模式之前,需要确认传输结束,避免破坏      最后一次传输。
很清楚了吧,使用USART_GetFlagStatus(USART1,USART_FLAG_TXE)就可以,这样效率更高一些,中间少等了串口发的N个bit的时间。实测功能完全正常,但是如果你发完最后一个数据要停掉USART的功能情况下就必须等待USART_GetFlagStatus(USART1, USART_FLAG_TC)了,因为不这样最后一个数据可能会丢失。
(2)      当使能了串口接收中断RXENIE时,溢出中断是不会自己开启的,但是溢出标志位是可以置位的;
(3)      调试硬件USART时,通过调试窗口,当进入接收中断时看不到RXNE置位,
  原因是:当我们调出窗口时,对于MDK来说就是已经读取了DR寄存器,相当于将RXNE位给清零了,所以我们看不到RXNE置位,
(4)      串口的接收部分的实验总结:
   a)  网上有人认为stm32的串口部分存在一些bug。例如:很多网友认为说只要我们开启了接收中断USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);那么溢出中断USART_IT_ORE就也会默认开启的,但是通过USART_GetITStatus(USART1,USART_IT_ORE)函数又得不到溢出中断的置位标记,而只能通过USART_GetFlagStatus(USART1,USART_FLAG_ORE)函数来判断是否发生了溢出,所以说这是个bug;
首先:我认为这样理解是错误的。
原因:
1    根据库文件stm32f10x_usart.c      V3.5.0中的串口中断配置函数
void USART_ITConfig(USART_TypeDef* USARTx, uint16_tUSART_IT, FunctionalState NewState)
/**
  * @brief Enables or disables the specified USART interrupts.
  * @param USARTx: Select the USART or the UART peripheral.
  *  This parameter can be one of the following values:
  *  USART1, USART2, USART3, UART4 or UART5.
  * @param USART_IT: specifies the USART interrupt sources to be enabled ordisabled.
  *  This parameter can be one of the following values:
  *    @arg USART_IT_CTS:  CTS changeinterrupt (not available for UART4 and UART5)
  *    @arg USART_IT_LBD:  LIN Breakdetection interrupt
  *    @arg USART_IT_TXE:  Transmit DataRegister empty interrupt
  *    @arg USART_IT_TC:   Transmissioncomplete interrupt
  *    @arg USART_IT_RXNE: Receive Data register not empty interrupt
  *    @arg USART_IT_IDLE: Idle line detection interrupt
  *    @arg USART_IT_PE:   Parity Errorinterrupt
  *     @arg USART_IT_ERR:  Error interrupt(Frame error, noise error,overrun error)
  * @param NewState: new state of the specified USARTx interrupts.
  *  This parameter can be: ENABLE or DISABLE.
  * @retval None
  */
    上面的红色部分可知:如果你想使用溢出中断,必须配置USART_IT_ERR中断才可以使用USART_GetITStatus(USART1,USART_IT_ORE)函数来检测溢出中断;但是你在前面的初始化配置中只是打开了USART_ITConfig(USART1, USART_IT_RXNE, ENABLE)接收中断,试问你怎样通过USART_GetITStatus(USART1,USART_IT_ORE)来检测到溢出中断。(结论:如果只配置USART_IT_RXNE中断使能,USART_IT_ORE溢出中断仍然是不会触发的)
     另外,在接收过程中如果发生了溢出我们是可以通过USART_GetFlagStatus(USART1,USART_FLAG_ORE)函数来检测看是否发生了溢出错误。根据是数据手册的:当RXNE仍然是’1’的时候,当前被接收在移位寄存器中的数据,需要传送至RDR寄存器时,硬件将该位置位。如果USART_CR1中的RXNEIE为’1’的话,则产生中断。可知该位可以是由硬件置位的;所以你能使用USART_GetFlagStatus(USART1,USART_FLAG_ORE)函数来检测。注意对“如果USART_CR1中的RXNEIE为’1’的话,则产生中断。”的理解,你开启了USART_IT_RXNE中断,那么RXNEIE肯定是为 1 的,也就是说在前面一句的基础上,发生溢出会产生中断,但是你都没使能溢出中断,它怎么触发中断,所以你用USART_GetITStatus(USART1, USART_IT_ORE)检测到溢出中断才怪了。
       下面是我自己的调试代码:
(1)      正常的中断处理函数
void USART1_IRQHandler(void)
{
   my_printf("USART1->SR= %#x\n",USART1->SR);  这句是用来解决JLINK调试中看不到RXNE置位用的
   if(USART_GetITStatus(USART1,USART_IT_RXNE) != RESET)  如果是接收中断
   {        
        //注意使用的是USART_GetFlagStatus()函数,进入接收中断后,加个检测看是否发生了溢出错误
if(USART_GetFlagStatus(USART1,USART_FLAG_ORE)!=SET)         
{  
     //接收中断的读DR就是清中断,我这里是读完直接发出去,这样你在终端上可以直接看到自己的输入,交互性更好一些
                 my_putc(USART_ReceiveData(USART1) );     
                 flag= 1;
        }
        else
        {   //如果溢出了,就清一下,
               USART_GetITStatus(USART1,USART_IT_RXNE);
               USART_ReceiveData(USART1);
               if(flag> 0)   //这里是个打印次数的控制变量,防止不停地发
               {
                      flag--;
                      my_puts("Receiveoverflow!\n");
               }
       }                                
   }
}
                  这里注意:如果溢出了,就按照手册的要求清ORE位,由软件序列将其清零(先读USART_SR,然后读USART_CR)
         USART_GetITStatus(USART1, USART_IT_RXNE);    USART_ReceiveData(USART1);这两句
                  另外注意:结合ORE置位的要求是 RXNE仍然是’1’的时候”,怎样理解呢,你既然要溢出,肯定是你上一次接受到的数据还没有从DR读走,又来了一个数据,所以肯定会先进入接收中断(覆盖上一个数据),接着你又检测ORE,这时ORE肯定置位了,就执行else分支,提示溢出。其次,注意书册上清ORE的方法,USART_GetITStatus(USART1,USART_IT_RXNE);    USART_ReceiveData(USART1);这两句很有意思,这两句不仅清了ORE的置位,还把RXNE的置位也给清了,因为读DR可以请RXNE位。
(2)      下面是模拟产生接收溢出的代码:
voidUSART1_IRQHandler(void)
{
my_printf("USART1->SR= %#x\n",USART1->SR);  这句是用来解决JLINK调试中看不到RXNE置位用的
     if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET)  如果是接收中断
              {        
                       //注意使用的是USART_GetFlagStatus()函数,进入接收中断后,加个检测看是否发生了溢出错误
if(USART_GetFlagStatus(USART1,USART_FLAG_ORE)!=SET)         
{  
                              //注意这里我什么也没做,就是没有读DR,那么此时程序会不停地进入接收中断进行处理         
                              //假如我们接收到一个数据,此时就开始不停地触发接收中断,因为中断出发了,你没有处理啊。
                              //但是此时还没有发生溢出错误,因为此时就收到一个数据,如果又来了一个数据,就要进入下面的else
                              //因为这里的if不成立了,此时已经发生了溢出错误。
                                               flag = 1;
                      }
                      else
                       {     //如果溢出了,就清一下,如果你不清,这时的中断还是接受中断USART_IT_RXNE,切记此时溢出中断你都没有打开
                             USART_GetITStatus(USART1,USART_IT_RXNE);//这里只要求读SR寄存器,所以参数随便整,无所谓;
                             USART_ReceiveData(USART1);
                             if(flag > 0)   //这里是个打印次数的控制变量,防止不停地发
                             {
                                    flag--;
                                     my_puts("Receiveoverflow!\n");  //发生溢出错误
                             }
                     }                                    
      }
}
(3)下面是模拟接受溢出现象的错误的写法
void USART1_IRQHandler(void)
{
            my_printf("USART1->SR =%#x\n",USART1->SR);  这句是用来解决JLINK调试中看不到RXNE置位用的
            if(USART_GetITStatus(USART1,USART_IT_RXNE) != RESET)  如果是接收中断
            {        
                     //注意使用的是USART_GetITStatus()函数,
            if(USART_GetITStatus(USART1, USART_IT_ORE) != SET)                 
            {      //注意这里我什么也没做,就是没有读DR,那么此时程序会不停地进入接收中断进行处理
                          //分析一下,这种情况下不管你接收到几个数据都不会进入else了,因为if判断永远成立。因为你溢出中断压根就没有开启
                          flag = 1;
                   }
            else
            {        //如果溢出了,就清一下,
                     USART_GetITStatus(USART1,USART_IT_RXNE);
                     USART_ReceiveData(USART1);
                     if(flag> 0)   //这里是个打印次数的控制变量,防止不停地发
                     {
                               flag--;
                               my_puts("Receiveoverflow!\n");
                     }
            }                                 
      }
}

正点原子逻辑分析仪DL16劲爆上市
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则



关闭

原子哥极力推荐上一条 /2 下一条

正点原子公众号

QQ|手机版|OpenEdv-开源电子网 ( 粤ICP备12000418号-1 )

GMT+8, 2025-6-18 20:24

Powered by OpenEdv-开源电子网

© 2001-2030 OpenEdv-开源电子网

快速回复 返回顶部 返回列表