今天我们来学习下串口通讯,串口通讯分好几种,今天就讲TTL电平的,串口。
今天就讲讲单片机与电脑通讯用的串口,也就是串口最简单的应用。
STM8的串口是全双工串口,异步通讯。
全双工就是指能同时双向传输,因为有TX和RX两条线路。
异步指没有同步时钟线。
先看看串口的波形吧,了解一下一条信号线是如何通讯的。
这是用逻辑分析仪抓取的0x55的波形
是不是很神奇,这不就是个方波信号么?
怎么就是0x55了?
先不急,再来看看字符型的55
what?
为啥这也是55?
波形完全不一样,还长了好多!
先不急,再看看下图
咦,这图不是和上图一样么?
怎么这里变成35、35了,不是55了??
真有意思
下面先来看看串口波形的资料
先看8位数据长度的,
一个低电平的起始位,8位数据位,一位高电平停止位
串口波形,一帧的数据构成就是这样
由1位的低电平起始位置,8位、9位或者5位、4位的数据位再加上奇偶校验位(可不用)再加上1位或者1.5或者2位的停止位组成。
貌似说的有点啰嗦了,结构就是:起始位+数据位+校验位+停止位
这就是一帧串口数据的结构,其中数据位可长可短,校验位可有可无,停止位也有长短分别
那么,在没有同步时钟的异步串口中,我们如何能够知道这个波形代表的是1还是11还是111呢?
这个就关系到一个重要的概念:波特率
波特率就是每秒能发送多少个码元,码元简单来说在数字信号里就是代表0和1的最小单位。
以最常见的9600的串口波特率来说,一秒能传输9600个二进制位,那么每个位所占的时间长度就能得出1s/9600=104us
所以一个位的长度就是104us。知道位长度了就可以开始分割数据位了。
串口数据发送里是低位先传送,TTL是正逻辑电平。那现在就来对第一张图的波形进行解析。
上面的串口配置是9600,8数据位,无校验,1停止位。
上面读下来就是
0101010101
砍头去尾剩下10101010,因为是低位先传,来个倒序
01010101,读出来就是0x55
字符串是用ASCII码进行传送,因此5对应的ASCII码的16进制位是35.
之所以第二第三张图显示不同是因为分析仪设置不同,图二是ASCII码解析,图三是HEX解析
好了,下面是单片机的串口结构
看不清,看重要的
这里看到了串口的主要结构
灰色方框写的是UART_DR,就是串口数据寄存器
但是里面还包含了很多寄存器
有接收移位寄存器,发送移位寄存器,发送数据寄存器,接收数据寄存器
但是实际操作时读写的都是UART_DR这个地址
这是咋回事呢?
这其实是写UART_DR其实是要写到TDR,而读则是读到了RDR。
今天的实验很简单,就是用串口把接收到的数据再传回去
先看看配置流程
首先配置UART_CR1的M,选择数据长度
然后是UART_CR3,配置停止位
接下来是配置波特率,这个要小心,
波特率是通过主时钟分频来的,
UART_BRR要拆分配置
UART_BRR1还不能配置成00。
接下来是配置UART_CR2使能发送接收。
然后再下来就是写如要发送的数据到UART_DR.
最后是等待发送完成。
我这里就是简单的回传接收到的数据
貌似很简单,但实际操作还是遇到了很多麻烦
最开始我是接收到一个字节马上就回传,并且是在接收中断里处理。
结果是单字节完全正常,长字符串就乱码。
后面想想应该是有部分字节丢失导致的,因为串口的数据是连续传输,如果不及时读出则数据会被覆盖
后面就使用一个数组做接收缓存,还是出问题。
主要还是对字符串的结束判断出了问题。
之前一直以为字符串的结尾会有个‘\0’,结果是没有的。
后面想想也是,字符串为啥会有‘\0’?
这只是在储存进内存里的一个结束标识位,遇到这个标识默认是字符串已经结束。
在传输时并不会传输这个标志。
这咋办呢?得自己加一个啊,不如就加回车吧也就是\r\n判断\n结尾
万一要是没有回车结尾呢?
那就做个超时吧
结果就是下面的程序
- #include<iostm8s105k4.h>
- void init()
- {
- CLK_SWCR=0x02; //时钟配置
- CLK_SWR=0xb4; //外部晶体
- CLK_CKDIVR=0x00; //不分频
-
- UART2_CR1=0x00; //1,8,n,1
- UART2_CR3=0x00;
-
- UART2_BRR1=0x34; //波特率 9600 8M/833
- UART2_BRR2=0x01;
-
- UART2_CR2=0x2c; //串口发送接收打开,开接收中断
-
- }
- #pragma vector=0x17 //串口接收中断
- __interrupt void UART()
- {
- unsigned char buf[50]=""; //接收缓存
- unsigned char *p=buf; //定义指针
- unsigned int count=0; //超时计数
-
- while(1) //接收循环
- {
- while(!(UART2_SR&0x20)) //等待接收完成
- {
- count++; //接收超时计数
- if(count>65000) break; //接收超时,跳出等待
- }
- if(count>65000) break; //接收超时,跳出接收
- count=0; //计数复位
-
- *p=UART2_DR; //取值
- if((*p=='\n')||((p-buf)>=50)) break; //字符串结束或超缓存跳出
- p++; //下一位
- }
-
- p=buf; //取缓存地址
- while((*p!='\0')&&(*p!='\n')&&((p-buf)<50)) //当不为结束符且未超缓存时继续循环发送
- {
- UART2_DR=*p; //送数据
- while(!(UART2_SR&0x40)); //等待发送完成
- p++; //下一位
- }
- }
- int main( void )
- {
- init();
- asm("rim"); //允许中断
- while(1);
- }
这回总算是正常了
最后再总结下串口乱码的原因,一个因为没有同步时钟,波特率偏差引起乱码
还有就是接收问题,有丢失字节,可能是接收不及时或者没接收完
串口输出一般不会出问题。
STM8首次学习实验~流水灯
http://bbs.mydigit.cn/read.php?tid=2452931STM8学习之——中断
http://bbs.mydigit.cn/read.php?tid=2453643STM8学习之——TIMER4定时器
http://bbs.mydigit.cn/read.php?tid=2456190STM8学习之——timer1输入捕获
http://bbs.mydigit.cn/read.php?tid=2457109STM8学习之——timer1外部时钟源模式2
http://bbs.mydigit.cn/read.php?tid=2459809STM8学习之——timer1周期测量
http://bbs.mydigit.cn/read.php?tid=2461507STM8学习之——周期测量的疑问
http://bbs.mydigit.cn/read.php?tid=2462114STM8学习之——timer1频率占空比测量
http://bbs.mydigit.cn/read.php?tid=2464249STM8学习之——timer1 PWM输出
http://bbs.mydigit.cn/read.php?tid=2467013STM8学习之——AWU自动唤醒
http://bbs.mydigit.cn/read.php?tid=2477122STM8学习之——BEEP资源
http://bbs.mydigit.cn/read.php?tid=2477224STM8学习之——IWDG独立看门狗
http://bbs.mydigit.cn/read.php?tid=2477395STM8学习之——WWDG窗口看门狗
http://bbs.mydigit.cn/read.php?tid=2477802STM8学习之——UART串口
http://bbs.mydigit.cn/read.php?tid=2479924