微控制器缺乏交互
然而,当我们制作这两段小灯时,当我们将C语言编写的控制程序编写到单片机中后,我们就无法再控制小LED灯了。 换句话说,“只能看不能动”。 交互性比较差。 在接下来的两节中,将介绍一种交互方法。 目的是将C语言控制程序编入单片机中,仍然能够从外界控制小LED灯。
既然要实现交互,单片机就必须能够捕捉外界的变化。 最简单的方法是按下按钮。 不过,这里我们不打算使用按钮,而是通过“输入命令”来控制LED灯。使用printf将单片机内部的信息传输到计算机
在一般的软件开发中,如果想要检查某个变量的值或者输出提示信息,可以直接使用printf将信息输出到屏幕上。 当一个分支进程需要外部选择时,我们只需要按键盘即可。 但是对于51单片机,如何向它输入命令呢? 我的 51 微控制器既没有屏幕也没有键盘:
事实上,printf只是将信息输出到终端。 终端不一定是屏幕,还可以是其他字符设备,例如微控制器中常见的串行端口外设。 因此,没有屏幕的51单片机也可以使用printf函数,只需要将其输出端口重定向到串口即可。
keil4的重定向工作已经完成。 剩下的工作我们需要做的只是配置单片机的串口寄存器。 这项工作非常简单。 C语言代码可以写成如下,请参见:
void init_uart(unsigned int baud) { SCON = 0x5a; TMOD = 0x20; TH1 = TL1 = -(FOSC/12/32/baud); TR1 = 1; ES = 1; EA = 1; }
其中,FOSC是单片机的晶振频率。 我使用的微控制器的频率是11.0592MHz。 现在包含“stdio.h”头文件以使用 printf 函数:
#include "reg51.h" #include "stdio.h" void main() { init_uart(9600); printf("hello world, num: %dn", 98); while(1); }
编译程序烧录到单片机中,打开电脑中的串口调试软件,发现字符串是正确的,但是C语言程序明明要传输98,但是串口工具上显示的数字电脑是25088!
这个问题可能是因为我使用的微控制器是8位的。 解决问题的方法其实在keil帮助文件中提到过:
可以使用格式字符“%bd”代替“%d”,也可以强制将数字输出为int类型:
... printf("hello world, num: %bdn", 98); printf("hello world, num: %dn", (int)98); ...
再次编译编程,发现输出正常。
将信息传递给微控制器
以后你可以使用printf函数将单片机内部的信息传输到计算机中,但既然是“交互式”的,那么你也应该能够将计算机中的信息传输到单片机中。 那么,如何将信息从计算机传输到单片机呢? 其实也可以使用串口。 请看init_uart()函数的代码。 应该可以发现C语言程序已经开启了串口中断,所以可以如下编写中断处理程序,请看:
#define MAX_CMD_LEN 32 static bit is_cmd_ready = 0; static char cmd[MAX_CMD_LEN] = {0}; static unsigned char cmd_len = 0; void interrupt_uart() interrupt 4 { static unsigned char i = 0; if(RI){ RI = 0; cmd[i%MAX_CMD_LEN] = SBUF; if(cmd[i%MAX_CMD_LEN]=='n'){ cmd_len = i; i = 0; is_cmd_ready = 1; }else i++; } }
当计算机通过串口向单片机发送数据时,串口数据会逐字节放入SBUF中,C语言程序立即进入interrupt_uart()函数。 此时可以使用全局变量cmd来接收串口数据。 我们和计算机约定,发送的每个命令都以换行符“n”结尾,因此当接收到“n”时,可以将 is_cmd_ready 标志设置为 1。
我们常说的“通信协议”实际上是一系列协议。 从这个角度来看,这里所说的“发送的每个命令都以换行符‘n’结尾”实际上属于一种“通信协议”。
也就是说,计算机通过串口发送的命令会自动放入cmd中,因此我们可以定义接收命令的C语言函数如下:
unsigned char get_uart_cmd(char* oCmd) { unsigned char i = 0; while(!is_cmd_ready); for(i=0;i<cmd_len;i++) oCmd[i] = cmd[i]; is_cmd_ready = 0; return cmd_len; }
该函数将阻塞并等待 is_cmd_ready 标志。 接收到电脑发送的命令后,会通过oCmd发送命令,并返回接收到的命令长度。 现在您可以编写测试程序如下:
void main() { char mycmd[32] = {0}; init_uart(9600); printf("enter a num...n"); get_uart_cmd(mycmd); printf("cmd: %dn", (int)(mycmd[0])); printf("enter a string...n"); get_uart_cmd(mycmd); printf("cmd: %sn", mycmd); while(1); }
编译C语言程序并烧录,在电脑上的串口调试工具中会发现如下提示信息:
因为单片机首先需要一个数字,并且命令需要以换行符结尾,所以我勾选了以下选项,点击发送,发现终端输出:
104 正好等于十六进制的 68,然后输入一个字符串:
一切都按预期进行。
将通信模块封装到库中
这一步很简单。 只需要删除my_uart.c文件中的main函数,然后新建一个头文件,如下所示:
以后开发其他功能时,如果需要与电脑交互,只需将这两个文件添加到工程中即可。 接下来,本节介绍的交互模块将用于通过计算机控制单片机,并确定单片机的动作。 例如:
由于篇幅限制,我们将在下一节介绍。
欢迎在评论区一起讨论、提问。 文章均为手写、原创。 他们每天深入浅出地介绍C语言、Linux等嵌入式开发。 如果你喜欢我的文章,就关注吧,你可以看到最新的更新和往期文章。