玩转单片机之智能车小露——通过UART为单片机增加TTY终端
单片机只能使用按键与程序进行交互吗?有时候限于单片机有限的引脚,没有多余的IO口设计按键以实现不同的功能,这时候怎么办?
最简单的是使用UART与PC进行通信,这样PC上的键盘就可以变成控制单片机最灵活的工具。本文实现稍复杂但是炫酷的UART通信交互功能,类似于电脑Shell程序,通过字符串传递命令和参数,比如可以这样(默认实现测试命令):
串口发送:echo hello,world!
串口接收:hello,world!
串口初始化及命令字符串接收
直接使用单片机ISP串口,波特率设置稍高一些避免通信时间过长。
SystickCallbackUART1函数负责检测通信数据流是否结束,并打上结束标记。
void TTYInit(void){
GPIO_InitTypeDef gpio;
COMx_InitDefine com1;
gpio.Pin = GPIO_Pin_0|GPIO_Pin_1;
gpio.Mode = GPIO_PullUp;
GPIO_Inilize(GPIO_P3,&gpio);
com1.UART_Mode = UART_8bit_BRTx;
com1.UART_BRT_Use = BRT_Timer1;
com1.UART_BaudRate = 115200ul;
com1.UART_RxEnable = ENABLE;
com1.BaudRateDouble = DISABLE;
UART_Configuration(UART1,&com1);
NVIC_UART1_Init(ENABLE,Priority_1);
UART1_SW(UART1_SW_P30_P31);
}
//UART1 systick callback(10ms)
void SystickCallbackUART1(void){
if(--COM1.RX_TimeOut < 1){
RX1_Buffer[COM1.RX_Cnt] = '\0';
COM1.RX_Cnt = 0; //UART1 timeout reset RX_Cnt
TTYsig = TRUE;
}
}
串口命令行参数分割与解析
串口命令行默认格式为:cmd arg0 arg1 arg2 arg3 arg4各参数以空格区分,支持1个命令加最多5个参数,TTYArgPhase函数负责分割这些字符串命令和参数。
unsigned char *pTTYCmd;
unsigned char *pTTYArg[5];
//tty 命令行解析与参数分割
unsigned char TTYArgPhase(void){
unsigned char i = 0;
char *str = RX1_Buffer;
if(*str == ' ' || *str == '\0')
return FALSE;
pTTYCmd = str;
while(*str != '\0' || i > 4){
str++;
if(*str == ' '){
*str = '\0';
pTTYArg[i++] = ++str;
}
}
return TRUE;
}
命令解析并转换为函数调用
在定时器(500ms)中判断是否有新的命令,并解析命令转调真正的功能函数,根据具体函数传递正确的参数类型。
unsigned char TTYsig,TTYInfork;
void TTYFork(void){
TTYsig = FALSE;
if(TTYArgPhase() == FALSE)
return;
if(strcmp(pTTYCmd,"echos") == 0 && pTTYArg[0] != NULL)
TTYEchoString(pTTYArg[0]);
if(strcmp(pTTYCmd,"echon") == 0 && pTTYArg[0] != NULL)
TTYEchoNumber(atoi(pTTYArg[0]));
if(strcmp(pTTYCmd,"echot") == 0)
TTYEchoTime();
// TODO: 在此处添加用户代码
if(strcmp(pTTYCmd,"exit") == 0){
TTYechot = FALSE;
}
*pTTYCmd = NULL; //目前由输入者确保参数正确性
*pTTYArg[0] = NULL;
*pTTYArg[1] = NULL;
*pTTYArg[2] = NULL;
*pTTYArg[3] = NULL;
*pTTYArg[4] = NULL;
}
//Systick callback 500s
void SystickCallbackTTY(void){
if(TTYsig == TRUE)
TTYFork();
}
可命令行执行函数的设计
命令行可执行函数可以设计为两类,一类是单次执行的,执行完成后即可退出,如echos和echon;
另一类需要持续运行,直到命令行收到退出命令exit后退出,如echot,真正的动作仍然需要在特定的定时函数里执行。
unsigned char TTYechot;
//tty 字符回显函数
void TTYEchoString(char *str){
printf(str);
}
//tty 数字回显函数
void TTYEchoNumber(int num){
printf("%hd",num);
}
//tty 循环显示开机运行时间
void TTYEchoTime(void){
TTYechot = TRUE; //接收"exit"后复位
}
//Systick callback 1s
void SystickCallbackTTYEchot(void){
if(TTYechot == TRUE)
printf("Running time(s):%ld",systick);
}
实战例程:七彩呼吸灯控制
以 玩转单片机之智能车小露——七彩LED呼吸灯 为例,编写命令行控制函数还不是手到擒来?
//颜色字符串转换为整形
unsigned char ctoi(char *str){
if(strcmp(str,"red") == 0)
return BLED_COLOR_RED;
if(strcmp(str,"orange") == 0)
return BLED_COLOR_ORANGE;
if(strcmp(str,"yellow") == 0)
return BLED_COLOR_YELLOW;
if(strcmp(str,"green") == 0)
return BLED_COLOR_GREEN;
if(strcmp(str,"cyan") == 0)
return BLED_COLOR_CYAN;
if(strcmp(str,"blue") == 0)
return BLED_COLOR_BLUE;
if(strcmp(str,"purple") == 0)
return BLED_COLOR_PURPLE;
if(strcmp(str,"white") == 0)
return BLED_COLOR_WHITE;
return BLED_COLOR_OFF;
}
//模式字符串转换为整形
unsigned char mtoi(char *str){
if(strcmp(str,"single") == 0)
return BLED_MODE_SINGLE;
if(strcmp(str,"color") == 0)
return BLED_MODE_COLOR;
return BLED_MODE_OFF;
}
void TTYFork(void){
。。。
if(strcmp(pTTYCmd,"bledon") == 0 && pTTYArg[0] != NULL)
BLEDTurnOn(ctoi(pTTYArg[0]));
if(strcmp(pTTYCmd,"bleddisplaycolor") == 0 && pTTYArg[0] != NULL && pTTYArg[1] != NULL && pTTYArg[2] != NULL)
BLEDDisplayColor((unsigned char)atoi(pTTYArg[0]),(unsigned char)atoi(pTTYArg[1]),(unsigned char)atoi(pTTYArg[2]));
if(strcmp(pTTYCmd,"bledbreath") == 0 && pTTYArg[0] != NULL && pTTYArg[1] != NULL)
BLEDBreath(mtoi(pTTYArg[0]),ctoi(pTTYArg[1]));
if(strcmp(pTTYCmd,"bledoff") == 0)
BLEDTurnOff();
。。。
}
- 本文采用的单片机为STC32G系列,感兴趣的朋友可以参考测试,或去Gitee上下载例程。
- 下载地址:https://gitee.com/loganxiang/lgxsmartcar ,参考tty.h/tty.c。

浙公网安备 33010602011771号