OpenEdv-开源电子网

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

串口加printf重定向一些问题,解决一半,还剩一半

[复制链接]

3

主题

15

帖子

0

精华

初级会员

Rank: 2

积分
76
金钱
76
注册时间
2016-9-19
在线时间
8 小时
发表于 2017-11-11 17:34:56 | 显示全部楼层 |阅读模式
1金钱
今天用串口收发数据的时候发现第一字节的数据无法收到。
实际为第一字节收到,但是无法打印在调试助手上。
问题变成,为什么无法打印第一个字节。
这个问题以前碰到过,硬件复位的时候SR的TC为置位导致无法发送第一个字节,清除即可,但是我碰到的不是这个情况。下面背景描述:
首先板B向板A串口3发 0x42 0x0d 0x0a;
板A串口3收到数据后,通过串口1发送给PC串口助手显示,这里串口助手仅能显示0d 0a。下附程序部分:
[mw_shl_code=c,true]        while(1)
        {
                if(USART_RX_STA&0x8000)
                {                                          
                        len=USART_RX_STA&0x3fff;//得到此次接收到的数据长度
                        //USART_ClearFlag(USART1,USART_FLAG_TC);   //方案1
                        for(t=0;t<len;t++)
                        {
                               
                                USART_SendData(USART1, USART_RX_BUF[t]);         //向串口1发送数据
                                while(USART_GetFlagStatus(USART1,USART_FLAG_TC)!=SET);//等待发送结束
                        }
                        //delay_ms(1);    //方案2
                        printf("\r\n");//插入换行
                       
                        USART_RX_STA=0;
                }else
                {
                        times++;
                        if(times%30==0)LED0=!LED0;//闪烁LED,提示系统正在运行.
                        delay_ms(10);   
                }
        }[/mw_shl_code]
以下是printf的重定向输出函数:
[mw_shl_code=c,true]//重定义fputc函数
int fputc(int ch, FILE *f)
{        
        while((USART1->SR&USART_FLAG_TC)==0);//循环发送,直到发送完毕
        USART1->DR = (u8) ch;
      
//while((USART1->SR&USART_FLAG_TC)==0);//循环发送,直到发送完毕    //方案3
        return ch;
}[/mw_shl_code]

在这个情况下,接收器仅能收到上述代码中的:
printf("\r\n");//插入换行
串口3接收到的回车和换行没有算在数据当中,被中断函数过滤了。

三种方案解决了这个问题,
1、//USART_ClearFlag(USART1,USART_FLAG_TC);
在发送数据前清空TC位,但是每次发送都要清空
2、//delay_ms(1);   
在发送数据后延时一小会,我觉得不可取
3、//while((USART1->SR&USART_FLAG_TC)==0);//循环发送,直到发送完毕
在重定向函数fputc中将TC标志位等待放到发送数据后面

三种方案之后都可以接收到数据,但是还是不理解为什么原来的情况无法接收到数据,按理说
USART_SendData(USART1, USART_RX_BUF[t]); //向串口1发送数据
while(USART_GetFlagStatus(USART1,USART_FLAG_TC)!=SET);//等待发送结束
在这里已经等待了标志位发送完成,不会造成数据寄存器被覆盖的情况,求解惑?

最佳答案

查看完整内容[请看2#楼]

标志位错了,把你代码里的两处 while(USART_GetFlagStatus(USART1,USART_FLAG_TC)!=SET) 全改为 while(USART_GetFlagStatus(USART1,USART_FLAG_TXE)!=SET)
正点原子逻辑分析仪DL16劲爆上市
回复

使用道具 举报

3

主题

33

帖子

0

精华

初级会员

Rank: 2

积分
177
金钱
177
注册时间
2017-9-27
在线时间
33 小时
发表于 2017-11-11 17:34:57 | 显示全部楼层
本帖最后由 Bigflish 于 2017-11-12 11:15 编辑

标志位错了,把你代码里的两处
while(USART_GetFlagStatus(USART1,USART_FLAG_TC)!=SET)
全改为
while(USART_GetFlagStatus(USART1,USART_FLAG_TXE)!=SET)
回复

使用道具 举报

530

主题

11万

帖子

34

精华

管理员

Rank: 12Rank: 12Rank: 12

积分
165524
金钱
165524
注册时间
2010-12-1
在线时间
2116 小时
发表于 2017-11-12 01:28:48 | 显示全部楼层
USART_SendData(USART1, USART_RX_BUF[t]); //向串口1发送数据
while(USART_GetFlagStatus(USART1,USART_FLAG_TC)!=SET);//等待发送结束

这两个,掉个头试试看
我是开源电子网www.openedv.com站长,有关站务问题请与我联系。
正点原子STM32开发板购买店铺http://openedv.taobao.com
正点原子官方微信公众平台,点击这里关注“正点原子”
回复

使用道具 举报

11

主题

94

帖子

0

精华

中级会员

Rank: 3Rank: 3

积分
236
金钱
236
注册时间
2015-6-7
在线时间
39 小时
发表于 2017-11-12 07:14:39 | 显示全部楼层
这个问题碰到过,但忘记咋解决的了。DMA接收,缺一个字节
回复

使用道具 举报

3

主题

15

帖子

0

精华

初级会员

Rank: 2

积分
76
金钱
76
注册时间
2016-9-19
在线时间
8 小时
 楼主| 发表于 2017-11-12 13:57:00 | 显示全部楼层
正点原子 发表于 2017-11-12 01:28
USART_SendData(USART1, USART_RX_BUF[t]); //向串口1发送数据
while(USART_GetFlagStatus(USART1,USART_F ...

原子哥的方法也是可行的,我的代码就是开发板例程里的,这里试了一下,主要是检验标志位和发送数据的顺序要和printf重定向函数里的一致,这样两种标志位都可以
回复

使用道具 举报

3

主题

15

帖子

0

精华

初级会员

Rank: 2

积分
76
金钱
76
注册时间
2016-9-19
在线时间
8 小时
 楼主| 发表于 2017-11-12 14:01:58 | 显示全部楼层
Bigflish 发表于 2017-11-11 17:34
标志位错了,把你代码里的两处
while(USART_GetFlagStatus(USART1,USART_FLAG_TC)!=SET)
全改为

换成这个确实好使,还有一点就是判断标志位的和发送数据的顺序要一致,如果使用TC标志位且判断标志位在发送数据后,需要在初始化里清楚标志位,否则复位后第一个字符发不出来。
回复

使用道具 举报

3

主题

15

帖子

0

精华

初级会员

Rank: 2

积分
76
金钱
76
注册时间
2016-9-19
在线时间
8 小时
 楼主| 发表于 2017-11-12 14:31:33 | 显示全部楼层
Bigflish 发表于 2017-11-11 17:34
标志位错了,把你代码里的两处
while(USART_GetFlagStatus(USART1,USART_FLAG_TC)!=SET)
全改为

不过我的问题还是没有解决,想知道为什么原来的不行,谁能分析一下吗?
回复

使用道具 举报

3

主题

15

帖子

0

精华

初级会员

Rank: 2

积分
76
金钱
76
注册时间
2016-9-19
在线时间
8 小时
 楼主| 发表于 2017-11-12 14:31:51 | 显示全部楼层
正点原子 发表于 2017-11-12 01:28
USART_SendData(USART1, USART_RX_BUF[t]); //向串口1发送数据
while(USART_GetFlagStatus(USART1,USART_F ...

不过我的问题还是没有解决,想知道为什么原来的不行,谁能分析一下吗?
回复

使用道具 举报

3

主题

33

帖子

0

精华

初级会员

Rank: 2

积分
177
金钱
177
注册时间
2017-9-27
在线时间
33 小时
发表于 2017-11-13 11:09:06 | 显示全部楼层
本帖最后由 Bigflish 于 2017-11-13 14:33 编辑

不管是库函数开发还是寄存去的开发,寄存器都是要了解的。
以下是《中文手册》的定义:
》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》
位 7  TXE:发送数据寄存器为空 (Transmit data register empty)
当 TDR 寄存器的内容已传输到移位寄存器时,该位由硬件置 1。如果 USART_CR1 寄存器      
中 TXEIE 位 = 1,则会生成中断。通过对 USART_DR 寄存器执行写入操作将该位清零。
0:数据未传输到移位寄存器
1:数据传输到移位寄存器
注意:单缓冲区发送期间使用该位。
位 6  TC:发送完成 (Transmission complete)
如果已完成对包含数据的帧的发送并且 TXE 置 1,则该位由硬件置 1。如果 USART_CR1 寄存      
器中 TCIE = 1,则会生成中断。该位由软件序列清零(读取 USART_SR 寄存器,然后写入      
USART_DR 寄存器)。TC 位也可以通过向该位写入‘0’来清零。建议仅在多缓冲区通信  
时使用此清零序列。
0:传送未完成
1:传送已完成
《《《《《《《《《《《《《《《《《《《《《《《《《《《《《
   结合图片不难明白:
            在发送数据的时候,只要数据从TDR寄存器发送到了移位寄存器上,TXE必定由硬件置1(TDR的值为空,可以在往里写值,准备下一字节的传输,这就是我们理解的“传输完成”),
     接下来数据会自动的往TX线上发送出去,而不占内存,但是会消耗一定的时间,
          在数据从移位寄存器移动到TX线的期间,往TDX写入数值,会使得TXE置0,
          但是要是在这期间没有值往TDX里写,数据完全发送到TX线的同时(此时TXE=1),TC便会由硬件置1.   






发送时的TC&amp;TXE行为.png
回复

使用道具 举报

3

主题

15

帖子

0

精华

初级会员

Rank: 2

积分
76
金钱
76
注册时间
2016-9-19
在线时间
8 小时
 楼主| 发表于 2017-11-18 15:27:56 | 显示全部楼层
Bigflish 发表于 2017-11-13 11:09
不管是库函数开发还是寄存去的开发,寄存器都是要了解的。
以下是《中文手册》的定义:
》》》》》》》》 ...

首先十分感谢你耐心的回答,这个确实是理解的。

后来想了一下,TC空闲时刻就是高所以当执行以下两句时,
            USART_SendData(USART1, USART_RX_BUF[t]);         //向串口1发送数据
            while(USART_GetFlagStatus(USART1,USART_FLAG_TC)!=SET);//等待发送结束
第一次运行的时候虽然向USART_DR 寄存器写入,但是因为没有读取 USART_SR 寄存器,所以TC并不会清零,导致这个时候的while直接跳过,然后开始写入第二个byte,所以用这个标志位如果和printf组合的话,经常会有第一个字节消失的问题。
回复

使用道具 举报

1

主题

16

帖子

0

精华

初级会员

Rank: 2

积分
83
金钱
83
注册时间
2017-11-12
在线时间
18 小时
发表于 2017-11-18 16:48:16 | 显示全部楼层
多查一下寄存器手册,带fifo列队的可以一次性填完fifo,为啥要一个个字节操作?
回复

使用道具 举报

3

主题

15

帖子

0

精华

初级会员

Rank: 2

积分
76
金钱
76
注册时间
2016-9-19
在线时间
8 小时
 楼主| 发表于 2017-11-18 18:57:07 | 显示全部楼层
本帖最后由 WengYuH 于 2017-11-18 19:02 编辑
今天星期一 发表于 2017-11-18 16:48
多查一下寄存器手册,带fifo列队的可以一次性填完fifo,为啥要一个个字节操作?

这样吗。。没有了解过,能说具体一些吗?
回复

使用道具 举报

55

主题

151

帖子

0

精华

中级会员

Rank: 3Rank: 3

积分
304
金钱
304
注册时间
2018-3-12
在线时间
53 小时
发表于 2018-10-9 14:58:28 | 显示全部楼层
为什么我直接包含头文件#inlcude "stdio.h",报错,显示error:invalid peprocessing directive
回复

使用道具 举报

35

主题

560

帖子

2

精华

资深版主

Rank: 8Rank: 8

积分
17769
金钱
17769
注册时间
2018-3-3
在线时间
523 小时
发表于 2018-10-9 17:26:46 | 显示全部楼层
蓝野先生 发表于 2018-10-9 14:58
为什么我直接包含头文件#inlcude "stdio.h",报错,显示error:invalid peprocessing directive

#include
/*
*
*
*
*
*
*/
回复

使用道具 举报

55

主题

151

帖子

0

精华

中级会员

Rank: 3Rank: 3

积分
304
金钱
304
注册时间
2018-3-12
在线时间
53 小时
发表于 2018-10-9 17:28:42 | 显示全部楼层

好吧!我想打自己一巴掌
回复

使用道具 举报

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

本版积分规则



关闭

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

正点原子公众号

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

GMT+8, 2025-6-8 10:28

Powered by OpenEdv-开源电子网

© 2001-2030 OpenEdv-开源电子网

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