STM32库函数void USART_SendData的缺陷和解决方法

问题及现象(STM32F103系列http://www.y-ec.com/cpcp/class/?32.html

使用USART_SendData()函数非连续发送单个字符是没有问题的;当连续发送字符时(两个字符间没有延时),就会发现发送缓冲区有溢出现象。若发送的数据量很小时,此时串口发送的只是最后一个字符,当发送数据量大时,就会导致发送的数据莫名其妙的丢失。

如:

      for(TxCounter = 0;TxCounter < RxCounter; TxCounter++)

      USART_SendData(USART1, RxBuffer[TxCounter]);

原因

此API函数不完善,函数体内部没有一个判断一个字符是否发送完毕的语句,而是把数据直接放入发送缓冲区,当连续发送数据时,由于发送移位寄存器的速度限制(与通信波特率有关),导致发送缓冲区的数据溢出,老的数据还未及时发送出去,新的数据又把发送缓冲区的老数据覆盖了。

解决方法(目前总结的两种方案)

方案1. 加入延时函数(下下策),不需要修改USART_SendData()函数

      for(TxCounter = 0;TxCounter < RxCounter; TxCounter++)

{

              USART_SendData(USART1, RxBuffer[TxCounter]);

              DelayMS(2); //加入一个小的延时

}

方案2. 修改USART_SendData()函数,在其内部加入发送缓冲区的USART_FLAG_TXE状态检测语句,确保一个字符完全发送出去,才进行下一个字符的发送。

实现方法:每发送一个字符都检测状态寄存器,确保数据已经发送完毕。具体操作步骤如下所示。

修改前的函数定义体

void USART_SendData(USART_TypeDef* USARTx, u16 Data)

{

  assert_param(IS_USART_ALL_PERIPH(USARTx));

  assert_param(IS_USART_DATA(Data));

   

  USARTx->DR = (Data & (u16)0x01FF);

}

修改后的函数定义体

void USART_SendData(USART_TypeDef* USARTx, u16 Data)

{

  assert_param(IS_USART_ALL_PERIPH(USARTx));

  assert_param(IS_USART_DATA(Data)); 

  USARTx->DR = (Data & (u16)0x01FF);

  while(USART_GetFlagStatus(USARTx, USART_FLAG_TXE) == RESET){} //等待发送缓冲区空才能发送下一个字符

}

方案3. 不修改原来的库函数,在每一个字符发送后检测状态位。

USART_SendData(USART1, RxBuffer[TxCounter]);

while(USART_GetFlagStatus(USARTx, USART_FLAG_TXE) == RESET){} //等待发送缓冲区空才能发送下一个字符 

ST这么做的原因是:使用发送中断功能。

STM32(Cortex-M3)中的优先级概念

STM32(Cortex-M3)中有两个优先级的概念——抢占式优先级和响应优先级,有人把响应优先级称作'亚优先级'或'副优先级',每个中断源都需要被指定这两种优先级。

具有高抢占式优先级的中断可以在具有低抢占式优先级的中断处理过程中被响应,即中断嵌套,或者说高抢占式优先级的中断可以嵌套低抢占式优先级的中断。 

当两个中断源的抢占式优先级相同时,这两个中断将没有嵌套关系,当一个中断到来后,如果正在处理另一个中断,这个后到来的中断就要等到前一个中断处理完之后才能被处理。如果这两个中断同时到达,则中断控制器根据他们的响应优先级高低来决定先处理哪一个;如果他们的抢占式优先级和响应优先级都相等,则根据他们在中断表中的排位顺序决定先处理哪一个。

既然每个中断源都需要被指定这两种优先级,就需要有相应的寄存器位记录每个中断的优先级;在Cortex-M3中定义了8个比特位用于设置中断源的优先级,这8个比特位可以有8种分配方式,如下:

所有8位用于指定响应优先级

最高1位用于指定抢占式优先级,最低7位用于指定响应优先级

最高2位用于指定抢占式优先级,最低6位用于指定响应优先级

最高3位用于指定抢占式优先级,最低5位用于指定响应优先级

最高4位用于指定抢占式优先级,最低4位用于指定响应优先级

最高5位用于指定抢占式优先级,最低3位用于指定响应优先级

最高6位用于指定抢占式优先级,最低2位用于指定响应优先级

最高7位用于指定抢占式优先级,最低1位用于指定响应优先级

这就是优先级分组的概念。--------------------------------------------------------------------------------
Cortex-M3允许具有较少中断源时使用较少的寄存器位指定中断源的优先级,因此STM32把指定中断优先级的寄存器位减少到4位,这4个寄存器位的分组方式如下:

第0组:所有4位用于指定响应优先级

第1组:最高1位用于指定抢占式优先级,最低3位用于指定响应优先级

第2组:最高2位用于指定抢占式优先级,最低2位用于指定响应优先级

第3组:最高3位用于指定抢占式优先级,最低1位用于指定响应优先级

第4组:所有4位用于指定抢占式优先级

可以通过调用STM32的固件库中的函数NVIC_PriorityGroupConfig()选择使用哪种优先级分组方式,这个函数的参数有下列5种:

NVIC_PriorityGroup_0 => 选择第0组

NVIC_PriorityGroup_1 => 选择第1组

NVIC_PriorityGroup_2 => 选择第2组

NVIC_PriorityGroup_3 => 选择第3组

NVIC_PriorityGroup_4 => 选择第4组

接下来就是指定中断源的优先级,下面以一个简单的例子说明如何指定中断源的抢占式优先级和响应优先级:

// 选择使用优先级分组第1组

NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1);

// 使能EXTI0中断

NVIC_InitStructure.NVIC_IRQChannel = EXTI0_IRQChannel;

NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1; // 指定抢占式优先级别1

NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0; // 指定响应优先级别0

NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;

NVIC_Init(&NVIC_InitStructure);

// 使能EXTI9_5中断

NVIC_InitStructure.NVIC_IRQChannel = EXTI9_5_IRQChannel;

NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0; // 指定抢占式优先级别0

NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1; // 指定响应优先级别1

NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;

NVIC_Init(&NVIC_InitStructure);

--------------------------------------------------------------------------------
要注意的几点是:(STM32F101系列:http://www.y-ec.com/cpcp/class/?31.html

1)如果指定的抢占式优先级别或响应优先级别超出了选定的优先级分组所限定的范围,将可能得到意想不到的结果;

2)抢占式优先级别相同的中断源之间没有嵌套关系;

3)如果某个中断源被指定为某个抢占式优先级别,又没有其它中断源处于同一个抢占式优先级别,则可以为这个中断源指定任意有效的响应优先级别。

通信方式

通信方式多种多样,可以从不同的角度划分,按照数据流的组织方式分为并行通信和串行通信;按照传输信号频率范围分为基带传输和宽带传输;按照信息同时传输的方向分为单工通信、半双工和全双工通信三种;按照通信两端通信的同步方式分为异步通信和同步通信

4.2.1  同步和异步通信

所谓同步是指接收端要按照发送端所发送的每个数据的起止时间和重复频率来接收数据,既收发双方在时间上必须一致.数据传输的同步方式有异步传输与同步传输两种。

同步和异步通信区分重点:是否要解决时钟合拍问题

1、异步通信

异步传输是以字符为单位的数据传输,每个字符都要附加1位起始位和l位停止位以标记一个字符的开始和结束。此外,还要附加1位寄偶校验位,可以选择奇校验或偶校验方式对该字符实施简单的差错控制。一个字符占用5~8位,具体取决于数据所采用的字符集。例如,电报码字符为5位、ASCll码字符为7位、汉字码则为8位。起始位和停止位结合起来,便可实现字符的同步。

发送端与接收端除了采用相同的数据格式(字符的位数、停止位的位数、有无核验位及校验方式等)外,还必须采用相同的传输速率。典型的速率有: 1200、2400、4800、9600和19200 b/s等。

异步传输又称为起止式异步通信方式。其优点是简单、可靠,常用于面向字符的、低速的异步通信场合。例如,主计算机与终端之间的交互式通信通常采用这种方式。

2、同步通信

同步传输是以数据块为单位的数据传输。每个数据块的头部和尾部都要附加一个特殊的字符或比特序列,标记一个数据块的开始和结束,一般还要附加一个校验序列(如16位或32位CRC校验码),以便对数据块进行差错控制。根据同步通信规程,同步传输又分为面向字符的同步传输和面向位流的同步传输。 

4.2.2  基带传输与频带传输 

基带传输:基带传输是指在线路上直接传输基带信号或略加整形后进行的传输。基带是原始信号所占用的基本频带,当终端把数字,信息转换为适合于传送的电信号时,这个电信号所固有的频带即为基带使用单路数字信号,信号可双向传输。数字信号的基带传输就是以原来的“0”和“1”的形式直接用数字信号在信道上传输。这是一种最简单的传输方式,一般在微机通信中采用。

频带传输:当进行远距离通信时,往往将数字数据转换成模拟信号后传输,在接收端再进行信号的恢复,当调制成频率信号的频率范围在音频范围(200Hz—3.4 kHz)内时,这种传输方式称为频带传输。其频率范围比音频范围宽时,则称之为宽带传输。 

4.2.3  单工与双工通信

一个通信系统至少应含有三部分:发送设备、传输介质、接收设备。其中发送设备用于产生数据,并通过传输介质将数据传送给接收设备,以完成两点之间的数据传送。按照数据传输方向及其时间关系可分为:

l         单工(传呼机、有线电视)

l         半双工(对讲机)

l         双工通信(电话) 

(一)  单工通信

在单工通信中,发送设备和接收设备之间只有一个传输通道,数据单方向的从发送端到接收端,传输通道的方向不能改变。计算机和输出设备之间的通信大多采用单工通信方式。例如,计算机与打印机、计算机与显示器等。该方式的特点是设备简单、造价低,但传输效率也低。

图4.2.3-1  单工通信

(二)  全双工通信

在全双工通信中,两个设备之间有两个传输通道,并且可同时双向传送数据。相当于两个相反方向的单工通信的组合。该方式传输效率高,控制简单,但造价高,同时要求传输通道有足够的带宽给予充分的支持。

(三)半双工通信

在半双工通信中,两个设备之间有两个传输通道,可以轮流双方向地传送,但不能同时进行。即在某一时刻只能沿着一个方向传输数据。该方式要求每一端都具有发送设备、接收设备及改变数据传输方向的控制器。此方式适用于对话式终端之间或者用在传输通道没有足够的带宽支持双向通道的场合。由于该方式在通信中要不断的改变传输通道的方向,因此控制复杂,传输效率极低。(STM32F105系列:http://www.y-ec.com/cpcp/class/?33.html)

原文地址:https://www.cnblogs.com/scdyxcc/p/2937932.html