问题:
该问题由某客户提出,发生在 STM8S105C6T6 STM8S105C6T6 器件上。据其工程师讲述:当他所撰写的程序不使用奇偶校验的时候,程序工作是正常的;但是当他把奇偶校验改成偶检验 EVEN 时,程序无法正常工作;现象为:一、不管上位机发送的数据是不带校验位的,还是带奇偶校验位的,STM8S 都可以正常地接收到数据;二、当奇偶校验位使能后,接收到的数据再返回上位机,显示的数据不一定是正确的。
调研:
检查客户的 UART 程序,UART 初始化程序如下:
- static void UART_Config(void)
- {
- /* UART2 configured as follow:
- - BaudRate = 9600 baud
- - Word Length = 8 Bits
- - One Stop Bit
- - No parity
- - Receive and transmit enabled
- */
- UART2_DeInit();
- UART2_Init((uint32_t)9600, UART2_WORDLENGTH_8D, UART2_STOPBITS_1, UART2_PARITY_EVEN,
- UART2_SYNCMODE_CLOCK_DISABLE, UART2_MODE_TXRX_ENABLE);
- /* Enable UART2 */
- UART2_Cmd(ENABLE);
- }
复制代码
主程序可简化为:
- void main(void)
- {
- unsigned char val = 0x00;
- /* CLK configuration -----------------------------------------*/
- CLK_Config();
- /* UART configuration -----------------------------------------*/
- UART_Config();
- while(1)
- {
- /* Wait the byte is entirely received by UART2 */
- while (UART2_GetFlagStatus(UART2_FLAG_RXNE) == RESET)
- {
- }
- /* Get data */
- val = UART2_ReceiveData8();
- /* Wait until end of transmit */
- while (UART2_GetFlagStatus(UART2_FLAG_TXE) == RESET)
- {
- }
- /* Send data */
- /* Write one byte in the UART1 Transmit Data Register */
- UART2_SendData8(val);
- }
- }
复制代码
观察客户的程序,把 UART2_PARITY_EVEN 改回 UART2_PARITY_NO,确实就是正常的 8 个数据位不带奇偶校验的程序。那么为什么修改成带奇偶校验后,就会有客户所描述的问题呢?
继续使用 UART2_PARITY_EVEN,我们用串口分析工具、示波器或逻辑分析仪来分析一下数据。
• 当上位机发送 0x01 时(数据中有 1 个“1”),从串口分析工具可以看到上位机收到回来的数据变为 0x81;观察示波器波形,发现 STM8S 发出来的数据位在 Bit7 确实变为 1;
• 当上位机发送 0x03 时(数据中有 2 个“1”),从串口分析工具可以看到上位机收到回来的数据为 0x03;观察示波器波形,发现数据倒是正确的,可是校验位“0”并不存在。
通过这两个实验,我们可以看到,好像是数据的位数不一致,STM8S 发出来的数据是 7 位的数据,也就是只有 Bit0~Bit6,Bit7 变成了奇偶校验位!
我们来看一下 STM8S 的参考手册是如何描述奇偶校验控制的,我们看一下帧格式的表格:
我们发现,在 STM8S 中 M 位所定义是帧长度,而不是数据位的长度!也就是说 M 位所定位的长度为“数据位+奇偶校验位”个数的总和。当数据位为 8 位时,不使用奇偶校验的时候,M 的长度为 8 位;而要使用奇偶校验的时候,M 的长度应该为 9 位!
到此,我们应该知道如何去修改程序了,我们将主程序和 UART 的初始化程序分别修改为:
- void main(void)
- {
- unsigned char val = 0x00;
- /* CLK configuration -----------------------------------------*/
- CLK_Config();
- /* UART configuration -----------------------------------------*/
- UART_Config();
- while(1)
- {
- /* Wait the byte is entirely received by UART2 */
- while (UART2_GetFlagStatus(UART2_FLAG_RXNE) == RESET)
- {
- }
- /* Get data */
- val = UART2_ReceiveData9();
- /* Wait until end of transmit */
- while (UART2_GetFlagStatus(UART2_FLAG_TXE) == RESET)
- {
- }
- /* Send data */
- /* Write one byte in the UART1 Transmit Data Reg
- CLK_Config();
- /* UART configuration -----------------------------------------*/
- UART_Config();
- while(1)
- {
- /* Wait the byte is entirely received by UART2 */
- while (UART2_GetFlagStatus(UART2_FLAG_RXNE) == RESET)
- {
- }
- /* Get data */
- val = UART2_ReceiveData9();
- if(UART2_GetFlagStatus(UART2_FLAG_PE) == RESET)
- {
- /* Wait until end of transmit */
- while (UART2_GetFlagStatus(UART2_FLAG_TXE) == RESET)
- {
- }
- /* Send data */
- /* Write one byte in the UART1 Transmit Data Register */
- UART2_SendData9(val);
- }
- }
- }
复制代码
编译,再测试看看。我们将上位机端的串口分析软件的奇偶校验位仍然修改为 ODD,上位机发送“0103 07 0F”,结果发现上位机还是可以收到 STM8S 发来的“01 03 07 0F”!这是为什么呢?难道 PE是不起作用的,奇偶校验仍然是假的?当然,我们还是得来查一下为什么 PE 会检测不到呢。
我们再来看一下 PE 标志位是在什么情况下被清除的,我们在参考手册可以看到:要清除 PE 标志位,软件要按以下操作顺序进行执行:先读取 UART_SR,再读取 UART_DR。于是我们再看一下上面的程序,可以看到,程序中先执行了 UART2_GetFlagStatus(UART2_FLAG_RXNE),这是一个读取UART_SR 的过程,然后再执行 UART2_ReceiveData9(),这是一个读取 UART_DR 的过程,那么,至此,PE 标志即使之前已经置位,那么经过这两个操作后,早已被清除,这个时候再执行
if(UART2_GetFlagStatus(UART2_FLAG_PE) == RESET)显然没什么意义。
所以,我们还得再继续修改一下程序:
- void main(void)
- {
- unsigned char val = 0x00;
- /* CLK configuration -----------------------------------------*/
- CLK_Config();
- /* UART configuration -----------------------------------------*/
- UART_Config();
- while(1)
- {
- /* Wait the byte is entirely received by UART2 */
- while (UART2_GetFlagStatus(UART2_FLAG_RXNE) == RESET)
- {
- }
- if(UART2_GetFlagStatus(UART2_FLAG_PE) == RESET)
- {
- /* Get data */
- val = UART2_ReceiveData9();
- /* Wait until end of transmit */
- while (UART2_GetFlagStatus(UART2_FLAG_TXE) == RESET)
- {
- }
- /* Send data */
- /* Write one byte in the UART1 Transmit Data Register */
- UART2_SendData9(val);
- }
- else
- {
- /* Get data */
- val = UART2_ReceiveData9(); // For clear PE
- }
- }
- }
复制代码
再编译,再测试,得到了正确的结果:
• 将上位机端的串口分析软件的奇偶校验位设置为 ODD,上位机发送“01 03 07 0F”,STM8S发现奇偶校验位不对,没有回传任何数据给上位机;
• 将上位机端的串口分析软件的奇偶校验位仍然设置为 EVEN,上位机发送“01 03 07 0F”,上位机正确地收到 STM8S 发回来的“01 03 07 0F”。
结论:
由于客户对 STM8S 的参考手册没有详细研究,误以为只要在程序中将 UART2_PARITY_NO 改为
UART2_PARITY_EVEN,就可以实现 UART 的奇偶校验功能。
处理:
修改程序,将帧长度相应的都改成 9 位,加入对 PE 标志位的判断,并且经过正确的操作顺序来进行判
断处理。
建议:
当需要使用 UART 的奇偶校验功能时,需要注意以下几个方面:
1. M 所代表为帧长度,而不是数据位的长度。当使用奇偶校验的时候,帧长度=数据位长度+奇偶校验位长度;
2. 不管数据奇偶校验位是否正确,UART 都会将数据接收回来。要对奇偶校验进行判断的话,必须对 PE 标志位进行检测,PE 标志位由硬件置位,当 PE=1 时,奇偶校验出错,做相应处理;
3. PE 标志位的清除方式是:先读 UART_SR,再读 UART_DR。所以在程序中一定要注意:在判断RXNE 之后,必须在读取 UART_DR 之前就得先读取 PE,否则 PE 将在读取之前被清除;
4. 必须在 RXNE 标志位被置位的情况下才可对 PE 位进行清除。
|