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

2019-08-14 01:30发布

前提:对应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  */    上面的红 {MOD}部分可知:如果你想使用溢出中断,必须配置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 ",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! ");               }       }                                   }}                  这里注意:如果溢出了,就按照手册的要求清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 ",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! ");  //发生溢出错误                             }                     }                                          }} (3)下面是模拟接受溢出现象的错误的写法void USART1_IRQHandler(void) {            my_printf("USART1->SR =%#x ",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! ");                     }            }                                        }}
0条回答

一周热门 更多>