方法1:使用能够支持多串口通信的单片机,不过通过更换其他单片机来代替8051系列单片机,这样就会直接导致成本的增加,优点就是编程简单,而且通信稳定可靠。
方法2:在io资源比较充足的情况下,可以通过io来模拟串口的通信,虽然这样会增加编程的难度,模拟串口的波特率会比真正的串口通信低一个层次,但是唯一优点就是成本上得到控制,而且通过不同的io组合可以实现更加之多的模拟串口,在实际应用中往往会采用模拟串口的方法来实现多串口通信。
普遍使用串口通信的数据流都是1位起始位、8位数据位、1位停止位的格式的,如表1。
表1
要注意的是,起始位作为识别是否有数据到来,停止位标志数据已经发送完毕。起始位固定值为0,停止位固定值为1,那么为什么起始位要是0,停止位要是1呢?这个很好理解,假设停止位固定值为1,为了更加易识别数据的到来,电平的跳变最为简单也最容易识别,那么当有数据来的时候,只要在规定的时间内检测到发送过来的第一位的电平是否0值,就可以确定是否有数据到来;另外停止位为1的作用就是当没有收发数据之后引脚置为高电平起到抗干扰的作用。
在平时使用红外无线收发数据时,一般都采用模拟串口来实现的,但是有个问题要注意,波特率越高,传输距离越近;波特率越低,传输距离越远。对于这些通过模拟串口进行数据传输,波特率适宜为1200b/s来进行数据传输。
例子:在使用单片机的串口接收数据实验当中,使用串口调试助手发送16字节数据,单片机采用模拟串口的方法将接收到的数据返发到pc机。
模拟串口实验代码:
代码分析
在模拟串口实验代码中,宏的使用占用了相当的一部分。
#define rxd p3_0//宏定义:接收数据的引脚
#define txd p3_1//宏定义:发送数据的引脚
#define timer_enable(){tl0=th0;tr0=1;ftimeouts=0;}//使能t/c
#define timer_disable() {tr0=0;ftimeouts=0;}//禁止t/c
#define timer_wait(){while(!ftimeouts);ftimeouts=0;}//等待t/c超时
模拟串口接收引脚为p3.0,发送引脚为p3.1。为了达到精确的定时,减少模拟串口时收发数据的累积误差,有必要通过对t/c进行频繁的使能和禁止等操作。例如宏timer_enable为使能t/c,宏timer_disable禁止t/c,宏timer_wait等待t/c超时。
模拟串口的工作波特率为9600b/s,在串口收发的数据流当中,每一位的时间为1/9600≈104us,
若单片机工作在12mhz频率下,使用t/c0工作在方式2,那么为了达到104us的定时时间,th0、tl0的初值为256-104=152,在实际的模拟串口中,往往出现收发数据不正确的现象。原因就在于th0、tl0的初值,或许很多人会疑惑,按道理来说,计算t/c0的初值是没有错的。对,是没有错,但是在sendbyte和recv的函数当中,执行每一行代码都要消耗一定的时间,这就是所谓的“累积误差”导致收发数据出现问题,因此我们必须通过实际测试得到th0、tl0的初值,最佳值256-99=157。那么在t/c初始化timerinit函数中,th0、tl0的初值不能够按照常规来计算得到,实际初值在正常初值附近,可以通过实际测试得到。
模拟串口主要复杂在模拟串口发送与接收,具体实现函数在sendbyte和recvbyte函数,这两个函数必须要遵循“1位起始位、8位数据位、1位停止位”的数据流。
sendbyte函数用于模拟串口发送数据,以起始位“0”作为移位传输的起始标志,然后将要发送的自己从低字节到高字节移位传输,最后以停止位“1”作为移位传输的结束标志。
recvbyte函数用于模拟串口接收数据,一旦检测到起始位“0”,就立刻将接收到的每一位移位存储,最后以判断停止位“1”结束当前数据的接收。
main函数完成t/c的初始化,在while(1)死循环以检测起始位“0”为目的,当接收到的数据达到宏receive_max_bytes的个数时,将接收到的数据返发到外设。
方法2:在io资源比较充足的情况下,可以通过io来模拟串口的通信,虽然这样会增加编程的难度,模拟串口的波特率会比真正的串口通信低一个层次,但是唯一优点就是成本上得到控制,而且通过不同的io组合可以实现更加之多的模拟串口,在实际应用中往往会采用模拟串口的方法来实现多串口通信。
普遍使用串口通信的数据流都是1位起始位、8位数据位、1位停止位的格式的,如表1。
表1
起始位 | 8位数据位 | 停止位 | |||||||
0 | bit0 | bit1 | bit2 | bit3 | bit4 | bit5 | bit6 | bit7 | 1 |
要注意的是,起始位作为识别是否有数据到来,停止位标志数据已经发送完毕。起始位固定值为0,停止位固定值为1,那么为什么起始位要是0,停止位要是1呢?这个很好理解,假设停止位固定值为1,为了更加易识别数据的到来,电平的跳变最为简单也最容易识别,那么当有数据来的时候,只要在规定的时间内检测到发送过来的第一位的电平是否0值,就可以确定是否有数据到来;另外停止位为1的作用就是当没有收发数据之后引脚置为高电平起到抗干扰的作用。
在平时使用红外无线收发数据时,一般都采用模拟串口来实现的,但是有个问题要注意,波特率越高,传输距离越近;波特率越低,传输距离越远。对于这些通过模拟串口进行数据传输,波特率适宜为1200b/s来进行数据传输。
模拟串口实验代码:
1#include"stc.h"
2
3#definerxd p3_0//宏定义:接收数据的引脚
4#definetxd p3_1//宏定义:发送数据的引脚
5#definereceive_max_bytes 16//宏定义:最大接收字节数
6
7#definetimer_enable() {tl0=th0;tr0=1;ftimeouts=0;}//使能t/c
8#definetimer_disable() {tr0=0;ftimeouts=0;}//禁止t/c
9#definetimer_wait() {while(!ftimeouts);ftimeouts=0;}//等待t/c超时
10
11
12unsignedcharftimeouts=0;//t/c超时溢出标志位
13unsignedcharrecvbuf[16];//接收数据缓冲区
14unsignedcharrecvcount=0;//接收数据计数器
15
16
17
23voidsendbyte(unsignedcharb)
24{
25unsignedchari=8;
26
27txd=0;
28
29timer_enable();
30timer_wait();
31
32
33while(i--)
34{
35if(b&1)txd=1;
36elsetxd=0;
37
38timer_wait();
39
40b>>=1;
41
42}
43
44
45txd=1;
46
47timer_wait();
48timer_disable();
49}
50
56unsignedcharrecvbyte(void)
57{
58unsignedchari;
59unsignedcharb=0;
60
61timer_enable();
62timer_wait();
63
64for(i=0;i<8;i )
65{
66if(rxd)b|=(1<67
68timer_wait();
69}
70
71timer_wait();//等待结束位
72timer_disable();
73
74returnb;
75
76}
77
83voidprintfstr(char*pstr)
84{
85while(pstr&&*pstr)
86{
87sendbyte(*pstr );
88}
89}
90
96voidtimerinit(void)
97{
98tmod=0x02;
99tr0=0;
100tf0=0;
101th0=(256-99);
102tl0=th0;
103et0=1;
104ea=1;
105}
106
112unsignedcharstartbitcome(void)
113{
114return(rxd==0);
115}
116
122voidmain(void)
123{
124unsignedchari;
125
126timerinit();
127
128printfstr("hello 8051rn");
129
130while(1)
131{
132if(startbitcome())
133{
134recvbuf[recvcount ]=recvbyte();
135
136if(recvcount>=receive_max_bytes)
137{
138recvcount=0;
139
140for(i=0;i 141{
142sendbyte(recvbuf[i]);
143}
144}
145}
146
147}
148}
149
155voidtimer0irq(void) interrupt1using0
156{
157ftimeouts=1;
158}
159
2
3
4
5
6
7
8
9
10
11
12unsignedcharftimeouts=0;//t/c超时溢出标志位
13
14
15
16
17
23
24{
25unsignedchari=8;
26
27txd=0;
28
29timer_enable();
30timer_wait();
31
32
33while(i--)
34{
35if(b&1)txd=1;
36elsetxd=0;
37
38timer_wait();
39
40b>>=1;
41
42}
43
44
45txd=1;
46
47timer_wait();
48timer_disable();
49}
50
56unsignedcharrecvbyte(void)
57{
58unsignedchari;
59unsignedcharb=0;
60
61timer_enable();
62timer_wait();
63
64for(i=0;i<8;i )
65{
66if(rxd)b|=(1<67
68timer_wait();
69}
70
71timer_wait();//等待结束位
72
73
74returnb;
75
76}
77
83
84{
85while(pstr&&*pstr)
86{
87sendbyte(*pstr );
88}
89}
90
96
97{
98tmod=0x02;
99tr0=0;
100tf0=0;
101th0=(256-99);
102tl0=th0;
103et0=1;
104ea=1;
105}
106
112unsignedcharstartbitcome(void)
113{
114return(rxd==0);
115}
116
122
123{
124unsignedchari;
125
126timerinit();
127
128printfstr("hello 8051rn");
129
130while(1)
131{
132if(startbitcome())
133{
134recvbuf[recvcount ]=recvbyte();
135
136if(recvcount>=receive_max_bytes)
137{
138recvcount=0;
139
140for(i=0;i
142sendbyte(recvbuf[i]);
143}
144}
145}
146
147}
148}
149
155
156{
157ftimeouts=1;
158}
159
在模拟串口实验代码中,宏的使用占用了相当的一部分。
#define rxd p3_0
#define txd p3_1
#define timer_enable()
#define timer_disable() {tr0=0;ftimeouts=0;}//禁止t/c
#define timer_wait()
模拟串口接收引脚为p3.0,发送引脚为p3.1。为了达到精确的定时,减少模拟串口时收发数据的累积误差,有必要通过对t/c进行频繁的使能和禁止等操作。例如宏timer_enable为使能t/c,宏timer_disable禁止t/c,宏timer_wait等待t/c超时。
模拟串口的工作波特率为9600b/s,在串口收发的数据流当中,每一位的时间为1/9600≈104us,
若单片机工作在12mhz频率下,使用t/c0工作在方式2,那么为了达到104us的定时时间,th0、tl0的初值为256-104=152,在实际的模拟串口中,往往出现收发数据不正确的现象。原因就在于th0、tl0的初值,或许很多人会疑惑,按道理来说,计算t/c0的初值是没有错的。对,是没有错,但是在sendbyte和recv的函数当中,执行每一行代码都要消耗一定的时间,这就是所谓的“累积误差”导致收发数据出现问题,因此我们必须通过实际测试得到th0、tl0的初值,最佳值256-99=157。那么在t/c初始化timerinit函数中,th0、tl0的初值不能够按照常规来计算得到,实际初值在正常初值附近,可以通过实际测试得到。
模拟串口主要复杂在模拟串口发送与接收,具体实现函数在sendbyte和recvbyte函数,这两个函数必须要遵循“1位起始位、8位数据位、1位停止位”的数据流。
sendbyte函数用于模拟串口发送数据,以起始位“0”作为移位传输的起始标志,然后将要发送的自己从低字节到高字节移位传输,最后以停止位“1”作为移位传输的结束标志。
recvbyte函数用于模拟串口接收数据,一旦检测到起始位“0”,就立刻将接收到的每一位移位存储,最后以判断停止位“1”结束当前数据的接收。
main函数完成t/c的初始化,在while(1)死循环以检测起始位“0”为目的,当接收到的数据达到宏receive_max_bytes的个数时,将接收到的数据返发到外设。
单片机模拟串口发送和波特率问题-九游会j9
8051系列单片机外接能够被除尽的晶振即12mhz、24mhz、48mhz这些晶振时,波特率的精确性就得不到保证。
波特率 (11.0592mhz) | 初值 | 波特率 (12mhz) | 初值 | ||
th1、tl1 (smod=0) | th1、tl1 (smod=1) | th1、tl1 (smod=0) | th1、tl1 (smod=1) | ||
1200 | 0xe7 | 0xd0 | 1200 | 0xe5 | 0xcb |
2400 | 0xf3 | 0xe7 | 2400 | 0xf2 | 0xe5 |
4800 | 0xf9 | 0xf3 | 4800 | 0xf9 | 0xf2 |
9600 | 0xfc | 0xf9 | 9600 | 0xfc | 0xf9 |
14400 | 0xfd | 0xfb | 14400 | 0xfd | 0xfb |
19200 | 0xfe | 0xfc | 19200 | 0xfe | 0xfc |
波特率 (11.0592mhz) | 初值 | 波特率 (12mhz) | 初值 | ||
rcal2h | rcal2l | rcal2h | rcal2l | ||
1200 | 0xfe | 0xe0 | 1200 | 0xfe | 0xc8 |
2400 | 0xff | 0x70 | 2400 | 0xff | 0x64 |
4800 | 0xff | 0xd8 | 4800 | 0xff | 0xb2 |
9600 | 0xff | 0xdc | 9600 | 0xff | 0xd9 |
14400 | 0xff | 0xe8 | 14400 | 0xff | 0xe6 |
19200 | 0xff | 0xee | 19200 | 0xff | 0xed |
如果大家想通过设置不同的晶振获取更加多的波特率的值,可以下载以下工具进行计算: