我们可以不使用单片机本身带有的串口, 而自己用程序去模拟一个串口并达到和本身的串口具有同样的功能,
首先, 我们需要用到 CH340 串口模块, 大家可以上某宝自行购买.
正面:
反面:
然后我们需要了解一下这串口模块上的引脚:
5V: 与 VCC 短路为 5V TLL 输出(电源和信号输出都是 5V)
VCC: 可以与 3.3V 和 5V 用跳帽连接
3.3V: 与 VCC 短路为 3.3V TLL 输出(电源和信号输出都是 3.3V)
TXD: 发送数据端口(与单片机上的接收引脚用杜邦线连接)
RXD: 接收数据端口(与单片机上的发送引脚用杜邦线连接)
GND: 地线
因为我是有另外一条串口提供了 51 单片机的电源, 所以就没连接 5V 和 VCC, 只与单片机连发送, 接收和地端口.
现在, 让我们一起了解一下串口通信协议是怎样的
我们可以将该协议分成三部分
一, 起始信号
二, 数据位
三, 结束信号
首先, 串口主要有发送和接受两个主要功能,
发送
比如说我们需要发送一个 0X48 的十六进制数, 它的二进制为 01001011
则过程为
一, 起始信号
默认电平为高 (1), 先将发送端的引脚电平拉低(0) 持续 104us, 来作为一个起始信号
二, 八个数据位
数据的发送, 是从最低位开始到最高位一位一位发送的, 每一个数据位都需要持续 104us,
三, 结束信号
需要将发送端的引脚电平拉高 (1) 持续 104us 以上.
接收
其实接收也很好理解的, 怎样发送就怎样接收, 只是看接收的技巧而已
比如说刚刚发送的 0X48 从主机发送到了单片机上, 我们需要接收
则过程为
一, 起始信号
我们需要读取到它的起始信号, 就是当它把你的接收端引脚持续拉低 104us 之后, 就可以当作是开始接收了.
二, 八个数据位
因为是八个数据位了, 所以所接收的数据也会按照发送一样一个一个发送, 我们只要去读取这时候的引脚电平就好.
三, 结束信号
最后也是一样, 再需要确认一次结束信号, 读取电平是否为高电平, 如果全部确认成功后, 就可以认为是接收一个有效的数据了.
下面为个人图解
理论已经说完, 接下来就是怎样用代码实现了
那我们一步一步开始吧
第一, 创建工程
第二, 创建延时函数
代码:
- void Delay_us(int x) // 微秒
- {
- while( x-- != 0 )
- {
- _nop_();
- _nop_();
- }
- }
- void Delay_ms(int x) // 毫秒
- {
- int i,j;
- while( x-- !=0 )
- {
- for(j=0;j<10;j++)
- for(i=0;i<85;i++);
- }
- }
这里的 Delay.c 里面有两个延时函数, 一个为微秒级, 一个为毫秒级.
我们还需要再创建一个 Delay.h 头文件来向外声明这两个函数.
头文件的话, 我们还需要把它的路径添加到工程里, 步骤如图
我不把这两个函数写进 main.c 的原因是如果以后写的代码过长了, 全部都堆在主函数里的话不方便管理, 而这样分开的话可以降低他们的耦合关系, 下面也同理了.
第三, 串口函数
这里的函数创建和上面的步骤是一样的了
发送函数:
代码:
- void vFn_Uart_Send(unsigned char uContent)
- {
- unsigned int i;
- unsigned char uSendContent =0xff; // 定义一个发送数据的变量
- uSendContent = uContent; // 将要发送的值赋给该变量
- // 起始信号
- P05 = 0; //P05 作为发送端的引脚
- Delay_us(61); // 延迟 104us
- // 数据发送
- for(i=0;i<8;i++)
- {
- P05 = uSendContent & 0x01; // 将所要发送的八位数据的最低位和 0x01 进行于运算, 若最低位为 1, 相于后的结果为 1, 则 P05 电平为高, 否则为 0
- uSendContent = uSendContent>> 1; // 将第二位数据位移到最低位, 重复进行七次移动
- Delay_us(61);
- }
- // 结束信号
- P05 = 1; // 将该引脚电平拉高
- Delay_us(61); // 延时 104us
- }
这里的 104us 延迟函数, 我已经用示波器测试过时间的了, 是可以直接使用的.
因为烧写时候不同的频率也会造成延时函数改变, 所以大家有条件的话, 也可以自己具体去测量一下的
接收函数:
代码:
- void vFn_Uart_Receive()
- {
- unsigned int i;
- unsigned char uContent = 0x00; // 定义一个用于接收数据变量
- // 起始信号
- if(P21 == 0) // 判断是否接收到起始信号
- {
- Delay_us(29); // 延时 52 微秒
- if(P21 == 0) // 再判断一次接收端电平是否被拉低
- {
- // 数据接收
- for(i=0;i<8;i++)
- {
- uContent = uContent>> 1; // 将值右移一位
- Delay_us(61); // 延时 104 微秒
- if(P21 == 1) // 判断是否为高电平
- {
- uContent |= 0x80; // 若为 1 则与 0x80 进行或运算(加上 0x80 也是一样的道理)
- }
- }
- }
- Delay_us(61); // 延时 104 微秒
- // 结束信号
- if(P21 == 1) // 判断电平是否被拉高
- {
- vFn_Uart_Send(uContent); // 将 uContent 的值用发送函数发送出来
- Delay_ms(1000);
- }
- }
- }
这里讲解一下接收函数, 我们知道起始信号拉低后, 有 104us 的延时, 延时完后就是八个数据位了, 如果我们每一次都等完 104us 再去读取引脚的电平的话可能会不太稳定, 所以我们可以在每个电平中间去读取, 这个时候是最稳定的. 下面为图解
第四, 主函数编写
因为都把函数放到了其他文件中, 这样主函数会看的比较简洁, 我们也直接调用相关的函数就行了, 主函数使用也没有太多的要求.
这里的话是需要每写完一个函数就去测试的, 不过我是已经是全部测试通过的了, 然后大家就还是写完一个测试一个, 因为一次性写完所有函数然后马上通过的机率不大, 还是要修改很多东西
然后我用的是下载软件是这个, 烧写的时候我们需要将 IRC 的频率改成 12MHz 进行烧写
串口助手的话, 我用的是 SSCOM, 另外因为我这个单片机的主芯片是 IAP15W4K48S4, 里面的 GPIO 的波特率也设置为了 9600, 所以我们也需要将其设置为 9600 的波特率才能看到数据, 不然就是一堆乱码了, 再然后就是点击 HEX 显示和 HEX 发送了.
我们来看是已经发送出来了.
再就是测试接收了
这里是把接收直接放到 while(1)里面跑, 大家也可以使用定时器的, 用中断去跑的话就更好了, 这里只是一个小小的示范.
发送了 0x52, 也接收了 0x52, 成功了.
这样一来, 我们也就完成模拟串口的收发了.
最后再教一下如何用模拟串口发送文字吧, 非常简单的一个小函数,
代码
- void vFn_Send_Word(unsigned char *s)
- {
- while(*s)
- {
- vFn_Uart_Send(*s++);
- }
- }
然后再在主函数调用就可以实现了.
我的关于 51 单片机模拟串口到这就结束了, 谢谢大家.
来源: https://www.cnblogs.com/zhenghaoyu/p/10059848.html