嗨,又见面了。九月的秋风吹过,微微凉意。
闲话少叙,回归正题。今天我们继续玩串口,主题包括:
实现printf打印到串口。
vspd和串口助手的使用。
识别上位机下发的固定多字节命令。
一、 实现printf打印到串口
在c语言程序设计课程,同学们肯定用过printf在控制台打印过“hello world!”。
printf(hello world!); 这次,我们用printf在串口打印“hello world”。我们会付出2k程序空间的代价。但,我乐意!对,我乐意。
还记得吗, printf函数在stdio.h头文件中定义。那我们先添加头文件到主程序代码。
#include stdio.h 为了实现printf重定位到串口,即把数据送到串口,我们需要重写putchar函数。putchar定义如下:
char putchar(char c){ //初始重新定向到串口 uart_senduchar(c); //返回字符到调用者printf return c;}
试一试吧。hello world代码如下:
#include uart.hvoid main(){ float temperature = 21.0/13; unsigned int count = 123; uart_init(); printf(hello world!); printf(temperature: %.3f count: %d., temperature, count); printf(printf demo. 2022-9-1 guilin,china.); while(1);}
在main函数,我们首先初始化串口(uart_init),然后打印hello world, 接着是温度(temperature,浮点数,但只打印三位小数)和计数器值(count,整数)。
虚拟终端显示结果如下。
关于printf函数的使用,参考:c stdio.h printf programming | library | reference - code-reference.com
二、 vspd和串口助手的使用
vspd是虚拟串口软件,用于在同一台pc调试串口程序。串口通信双方的程序都运行在同一台pc上。
vspd官网提供14天试用版本,无功能限制。
下载并安装vspd软件,然后添加一个虚拟串口对:com1-com2。安装过程略,配置过程如下:
修改仿真电路图,删除虚拟终端,添加compim。修改后的仿真电路图如下。注意,连接单片机和compim时,rxd连接rxd,txd连接txd,相当于把51单片机的串口绑定到compim。
3. 配置compim,使用虚拟串口对中的一个端口,这里选择了com1。
4. 修改主函数代码,重新编译。代码如下:
void main(){ float temperature = 21.0/13; unsigned int count = 123; uart_init(); while(1) { printf(hello world!); printf(temperature: %.3f count: %d., temperature, count); printf(printf demo. 2022-9-1 guilin,china.); delayms(1000); count++; }} 5. 在仿真电路中双击单片机,选择重新编译好的程序。
6. 运行仿真。
7. 运行stc-isp烧录软件,切换到串口助手,设置串口,选择com2(虚拟串口对的另一个),波特率9600,如下图所示。
8. 单击串口助手的打开串口按钮。在接收缓冲区可以收到51单片机串口发送的数据。
演示视频:
三、识别上位机下发的固定多字节命令。
来而不往非礼也。下面我们实现单片机接收并执行上位机串口下发的命令。
假设命令是2个字节的,第一个字节表示命令类型,第二个字节表示命令参数,如下表所示。
msb
lsb
命令类型(1字节)
命令参数(1字节)
命令常用于控制单片机外设或设置单片机功能。在本例中,我们计划通过串口助手下发命令控制蜂鸣器和清零count。
具体命令定义如下:
01 00h:关闭蜂鸣器
01 01h:启动蜂鸣器
02 00h:清零count值
f0 0fh:不玩了,关闭串口
如何实现两个字节命令的接收和解释执行呢?
思路:我们知道上位机只会发两个字节的命令数据到单片机,因此可以对串口接收字节进行计数。
当连续收到两个字节时,表示收到命令。然后判断第一个字节获得命令类型,再执行相应动作即可。
unsigned char uart_rx_buffer[2]; //全局变量,串口接收缓存//串口中断函数void isr_uart() interrupt 4{ static unsigned char rx_byte_count = 0; if(ri) //收到数据 { uart_rx_buffer[rx_byte_count] = sbuf; rx_byte_count++; ri = 0; if(rx_byte_count == 2) //接收到2个字节数据 { rx_byte_count = 0; //下面是命令解析及执行~魔幻的if else if(uart_rx_buffer[0] == 0x01 && uart_rx_buffer[1] == 0x00) { beeper_en = 1; //beeper off printf(执行命令:关闭蜂鸣器!); } else if (uart_rx_buffer[0] == 0x01 && uart_rx_buffer[1] == 0x01) { beeper_en = 0; //beeper on printf(执行命令:开启蜂鸣器!); } else if (uart_rx_buffer[0] == 0x02 && uart_rx_buffer[1] == 0x00) { count = 0; printf(执行命令:清零count!); } else if (uart_rx_buffer[0] == 0xf0 && uart_rx_buffer[1] == 0x0f) { printf(将关闭串口,再见!); tr1 = 0; } } }}
编译并仿真,单片机能够正确接收并执行命令,如下。
哈哈,执行完f0 0fh命令,要想再次使用串口,只能重启仿真了。
结束语
今天的内容有点多,结束语就短点吧。
附上本次串口源码,如下。如果你觉得有所帮助,请点赞,请打赏。
如果需要仿真电路和串口工程源码,请在后台留言。
uart.h头文件
//uart.h#include reg51.h#include stdio.h#ifndef _uart_h#define _uart_h#define uchar unsigned char#define uint unsigned intvoid uart_init(); //串口初始化函数void uart_sendbyte(uchar c); //发送单字节函数void uart_sendchar(char c); //发送char数据函数void uart_senduchar(uchar c); //发送unsigned char数据函数void uart_senduint(uint num); //发送unsigned int 数据函数void uart_sendint(int num); //发送int数据函数void uart_sendfloat(float num); //发送float数据函数void uart_sendlong(long num); //发送long数据函数void uart_senddouble(double num); //发送double数据void uart_sendstring(uchar* pstr); //发送字符串函数char putchar(char c); //重写printf的重定向putchar函数#endif
uart.c源码
#include uart.h#include reg51.h//串口初始化函数void uart_init(){ //串口初始化:工作方式1(10-bit), 9600bps @11.0592mhz scon = 0x50; //tx and rx ea = 1; es = 1; tmod = tmod|0x20; //定时器t1 8位自动重载 tl1 = 0xfd; //初值@9600bps th1 = 0xfd; tr1 = 1; //启动定时器t1 ri = 0; ti = 0; //清零 }//发送char数据函数 void uart_sendchar(char c){ uchar *p; p = &c; uart_senduchar(*p);}//发送unsigned char数据函数 void uart_senduchar(uchar c){ es = 0; //关串口中断 sbuf = c; while(ti==0); //等待发送完成 ti = 0; //清零发送标志 es = 1; //恢复串口中断 } //发送unsigned int 数据函数//先传输msb字节void uart_senduint(uint num){ uchar *p; p = # uart_senduchar(*p); p++; uart_senduchar(*p);} //发送int数据函数void uart_sendint(int num){ uchar *p; p = # //指向msb字节 uart_senduchar(*p); p++; //指向下一个字节 uart_senduchar(*p);}//发送float数据函数, msb byte first void uart_sendfloat(float num){ uchar *p; uchar i; p = # for(i=0; i<4;i++) { uart_senduchar(*(p++)); } } //发送字符串函数, 字符串以''结尾void uart_sendstring(uchar* pstr){ while(*pstr != '') { uart_senduchar(*pstr); pstr++; //指向下一个字符 } }/**************************重写stdio.h中的putchar函数,实现调用printf函数打印字符串到串口必须包含stdio.h头文件**************************/char putchar(char c){ //初始重新定向到串口 uart_senduchar(c); //返回字符到调用者printf return c;}
主程序源码
//uart_firstdemo.c#include uart.h//#includereg51.hsbit beeper_en = p2^0;sbit key_s1 = p1^0;char msg[] = welcome back.;unsigned char uart_rx_buffer[2]; unsigned int count = 0;//函数定义void delayms(unsigned int nms);void keyscan();void delayms(unsigned int nms){ unsigned int i,j; for(i=0;i
自研94GHz超距毫米波雷达芯片,交通毫米波雷达实现国产化、高性能突破
物联网时代,得供应链者得天下!
差动变压器的误差因素分析
大疆在上海金山区成立慧飞SDK中心 目前全球有近10万无人机开发者
LG V30发布日期终确定,骁龙835加OLED全面曲屏这次,LG能够靠它逆袭吗?
实现printf打印到串口
时尚轻商务智能运动手表,荣耀手表GS 3正式发布,完美诠释腕出非凡
基于Linux和netlink socket技术的电子收款机
微软等科技巨头布局定制芯片制造领域
nreal推出nreal light MR智能眼镜
重塑设备维护管理的主要趋势
世界首个大脑芯片:可模仿人类思考
Xsens超高性价比惯性传感器问市,厘米级高精度
输送带划伤如何进行修复
一款0~150V直流稳压电源电路剖析
五百内的蓝牙耳机哪款好?适合学会党的平价蓝牙耳机推荐
电脑立体眼镜的同步效果
疫情的发生 加速了无人配送的落地发展
电机转子是什么材质
资深车友教你如何选购摩托车