11

单片机很好玩4,实现交互,使用电脑控制单片机的执行动作(1)

 3 years ago
source link: https://blog.popkx.com/%E5%8D%95%E7%89%87%E6%9C%BA%E5%BE%88%E5%A5%BD%E7%8E%A94-%E5%AE%9E%E7%8E%B0%E4%BA%A4%E4%BA%92-%E4%BD%BF%E7%94%A8%E7%94%B5%E8%84%91%E6%8E%A7%E5%88%B6%E5%8D%95%E7%89%87%E6%9C%BA%E7%9A%84/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

第2节介绍了如何用单片机控制 LED 小灯闪烁起来,在此基础上,又在上一节讨论了如何制作“呼吸灯”。

9f1b3cd6b34df55456cff070258e6282.png

缺乏交互的单片机

不过,这两节制作的小灯时,我们把使用 C语言编写的控制程序烧写到单片机后,就无法再控制 LED 小灯了,也就是说,“只能看不能动”,交互性比较差。接下来两节,将介绍一种交互方法,目的是烧写 C语言控制程序到单片机后,仍然能够从外界控制 LED 小灯。

既然想实现交互,单片机就得能捕捉外界的变化,最简单的方法就是通过按键。不过这里不打算使用按键,而是通过“输入命令”的方式控制 LED 小灯。

将单片机内部的信息,printf 传递给电脑

一般的软件开发中,如果想查看某个变量的值,或者想输出一句提示信息,直接使用 printf 将信息输出到屏幕即可。遇到分支流程需要外界选择时,我们也只需按一下键盘就可以。但是对于 51 单片机来说,怎么与之交互呢?我的这款 51 单片机可既没有配屏幕,也没有配键盘:

aaa0764821312e24b2cb76c9c8fd47c5.png

其实 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: %d\n", 98);
    while(1);
}

编译程序并烧写到单片机,打开电脑中的串口调试软件,发现字符串都正确,但是C语言程序明明想传递的是 98,电脑端的串口工具显示的数字却是 25088!

ff8fa570f49d8f436bba2da0ca084ca3.png

这个问题估计是因为我使用的单片机是 8 位的原因。解决问题的方法,其实 keil 帮助文件里也提了:
4b3dc51831e9b17aec359c8e1a7b35e4.png

使用格式符 “%bd”代替“%d”即可,或者将要输出的数字强转为 int 类型也可以:
...
    printf("hello world, num: %bd\n", 98);
    printf("hello world, num: %d\n", (int)98);
...

再编译烧写,发现输出正常了。

254a474fb0a0e3e3ba7dde6096238806.png

将信息传递给单片机

以后可以使用 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++;
    }
}
7bf68900f82d4bef02905a0c39d46cf1.png

当电脑通过串口发送数据给单片机时,串口数据会被逐字节放入 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;
}
f105f877736ffdb6354220ae83def3f2.png

这个函数会阻塞等待 is_cmd_ready 标志位,收到电脑端发送来的命令后,将命令通过 oCmd 传出,返回接收到的命令长度。现在可以如下写测试程序了:
void main()
{
    char mycmd[32] = {0};

    init_uart(9600);

    printf("enter a num...\n");
    get_uart_cmd(mycmd);
    printf("cmd: %d\n", (int)(mycmd[0]));

    printf("enter a string...\n");
    get_uart_cmd(mycmd);
    printf("cmd: %s\n", mycmd);

    while(1);
}
930429fb9514eeb54691ab904f0f16f4.png

编译C语言程序并烧写,会在电脑端的串口调试工具中发现如下提示信息:
37bf052688b281e6522ad3cea6d2b0e4.png

因为单片机先需要一个数字,且命令需要换行符结尾,所以勾选了下面的选项,点击发送,发现终端输出:
f911e9e7eaf8834df5a2413f820b93b9.png
104正好等于十六进制的 68,接着再输入一段字符串:
dbe5692b6abe46f9b86de71e7383bac2.png

一切与预期一致。

将通信模块封装成库

这一步是简单的,只需要删去 my_uart.c 文件里的 main 函数,然后再新建一个头文件,如下图:

87dd2d2640dd2e096e6fd37e6a790a69.png

以后在其他功能开发中,如果需要与电脑端交互,直接把这两个文件加入工程就可以了。接下来,将使用本节介绍的交互模块,目的是能够通过电脑控制单片机,决定单片机的动作。例如:
* 在电脑端输入 led twinkle 命令,LED 小灯会闪烁。
* 在电脑端输入 led breath 命令,LED 小灯会变成“呼吸灯”,并且在变暗阶段向电脑端输出“呼气”,在变亮阶段向电脑端输出“吸气”。

限于篇幅,下一节再介绍了。


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK