玩转单片机之智能车小露——通过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();
}

可命令行执行函数的设计

命令行可执行函数可以设计为两类,一类是单次执行的,执行完成后即可退出,如echosechon
另一类需要持续运行,直到命令行收到退出命令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();
	。。。
}
posted @ 2025-10-25 18:08  芥子的世界  阅读(7)  评论(0)    收藏  举报