前一讲中,笔者跟大家介绍了如何让单片机实现串口收发单个字符,这一讲笔者要跟大家分享一下,如何让单片机实现串口收发字符串。相信,很多小伙伴对单个字符的收发是很熟悉了,但是对字符串的收发却有点头疼,确实,对字符串的操作,笔者也是花了不少时间才搞明白,那么笔者跟大家一起分享一下学习的成果吧。
首先,我们要明白,比如单片机字符串在存储的时候,其实是有一个特定的结束符'\0'的,也就是说,假设我们要定义一个一维数组Arry存放一个字符串"HelloIcKey!",他实际上在数组里面是按如下方式存放:
Arry = 'H';
Arry = 'e';
Arry = 'l';
Arry = 'l';
Arry = 'o';
Arry = 'I';
Arry = 'c';
Arry = 'K';
Arry = 'e';
Arry = 'y';
Arry = '!';
Arry = '\0';
也就是说,我们想要存储字符串的长度为10,实际上他要占用11个BYTE,这个时候如果我们要定义用来存放字符串的一维数组的时候,这个数组的长度就要特别注意了!应该是Arry = {"HelloIcKey!"};,或者跟笔者一样,直接不指定长度:Arry = {"HelloIcKey!"};
明白了这一点,那就好办了!接下来我们分析一下单片机接收字符串需要注意些什么。如果是接收指定长度的字符串,相信大家都知道该怎么处理,定义一个一维数组用来存放将要接收到的字符串,从数组的0号元素开始存放,直到接收完指定个长度后接收完成。这样比较简单,笔者就不多说了,笔者要跟大家介绍的是接收不定长度的字符串。那么这个不定长度的字符串要怎么处理呢?
我们一般的做法是,在字符串末尾加一个特定的结束符,一般是用“回车符”来作为结束符,当然我们也可以用“空格”来作为结束符,本例中, 笔者通过“回车符”和“空格”两种结束符来做对比进行讲解。为什么要用这两种而不是只介绍一种就好了呢,后面笔者会跟大家解释这个问题。
那么基本的理论知识分析完毕了,我们接下来看下程序部分吧,在串口中断函数中我们写入如下代码:
/********************* UART1中断函数************************/
void UART1_int (void) interrupt UART1_VECTOR
{
if(RI)
{
RI = 0;
ReciveData = SBUF; //将串口收到的数据存放到ReciveData中
if((ReciveData == '\n')||(ReciveData == ' '))//接收到结束符,接收完成
{
Uart1RXFinish = 1; //将接收完成标志位置1
}
else
RX_CNT ++; //接收计数加“1”
}
if(TI)
{
TI = 0;
COM1.TX_Busy = 0;
}
}
逻辑很简单,就是接收到“回车符”或者“空格”的时候,接收完成,然后我们自定义一个发送函数,将接收到的数据发送出去:
/********************* 串口1发送命令************************/
void Uart1_Send(void)
{
if(Uart1RXFinish) //收到数据,自动将接收到的数据返回
{
if(COM1.TX_Busy == 0)
{
COM1.TX_Busy = 1; //标志发送忙
SBUF = ReciveData; //将接收到的字符串发送出去,单字节发送
if(COM1.TX_read>RX_CNT)
{
RX_CNT = 0;
Uart1RXFinish = 0;
COM1.TX_read = 0;
}
}
}
}
如果仅仅只是要实现串口收发字符串,那么这样就搞定了!但是,实际应用中我们并不只要完成收发就算完事了!我们可能会需要用字符串作为命令,发送给单片机,然后单片机接收到不同命令后执行相关的动作。比如,我们发送字符串“LED_ON”让单片机板上的LED亮,发送“LED_OFF”让单片机板上的LED灭。那么这个功能要怎么实现呢?
其实,只要我们能正确接收到字符串了,要实现上述功能也就不难了!这里我们需要用到字符串比较函数,笔者这里自己重定义了一个字符串比较函数:
//对比字符串str1和str2
//*str1:字符串1指针
//*str2:字符串2指针
//返回值:0,相等;1,不相等;
u8 Str_Cmp(u8 *str1,u8 *str2)
{
while(1)
{
if(*str1!=*str2)return 1; //不相等
if(*str1=='\0')break; //对比完成了.
str1++;
str2++;
}
return 0;//两个字符串相等
}
笔者这里通过将接收到的字符串跟设定的字符串比较,如果不相等,则让OUT00亮,OUT01灭;如果相等,则让OUT00灭,OUT01亮。
/*将接收到的字符串跟设定的字符串做对比,执行相应的动作*/
if(Str_Cmp(Arry,RxArry)==1) //若两个字符串不相等
{
OUT00 = ON;
OUT01 = OFF;
}
else //若两个字符串相等
{
OUT00 = OFF;
OUT01 = ON;
}
本例中以字符串“HelloIcKey!”为例,我们应该都知道,发送字符串的时候,都是以ASCII的形式发送。那么我们先来看下“空格”跟“回车符”的ASCII码:
从上面两张图我们可以看到,“空格”的十六进制ASCII码是0x20占用一个字节,“回车符”的十六进制ASCII码是0x0D,0x0A,占用两个字节,说到这里,大家应该可以理解笔者为什么要用这两种方式来做对比了。
首先,如果是加入“空格”作为结束符的话,我们利用串口监控软件可以看到发送的数据如下,末尾多了一个0x20,即为“空格”ASCII码的十六进制:
如果是加入“回车”作为结束符的话,我们利用串口监控软件可以看到发送的数据如下,末尾多了一个0x0D,0x0A,即为“回车”ASCII码的十六进制:
所以,在这两种方式下,我们用于存放接收数据的一维数组里面存放的可能就是这样的数据:
1、用“空格”作为结束符时,ReciveData = {"HelloIcKey! + 0x20"}(这里只是形象说明一下,实际这种语法是不对的);
2、用“回车”作为结束符时,ReciveData = {"HelloIcKey! + 0x0D + 0x0A"}(这里只是形象说明一下,实际这种语法是不对的);
这样就会导致接收到的数据长度也不一样,我们实际设定好的要用来做对比的字符串是Arry = {"HelloIcKey!"},前面说了,字符串存储的时候是会有一个结束符'\0'的,那么就相当于Arry = {"HelloIcKey! + '\0'};由于这两种方式下接收到的数据长度不一样,那么就会影响到我们发送时的数据,如果我们要在发送字符串的后面加上结束符'\n'的话,就得分两种情况了:
if(ReciveData == '\n')//若是收到回车符0x0D 0x0A
RxArry = '\0'; //给新的字符串加入结束符
if(ReciveData == ' ') //若是收到空格
RxArry = '\0'; //给新的字符串加入结束符
不多说,我们将程序下载到板子中,来看下实验现象吧:
添加“空格”结束符
添加“回车”结束符
从上面两个结果中,我们可以看到,字符串的收发是OK的,那么接下来我们再看下字符串对比的结果:
正确的字符串
OUT00亮,OUT01灭
错误的字符串
OUT00灭,OUT01亮
从上面的结果我们可以看到,字符串对比也是OK的,好了,有关单片机收发字符串的知识就简单分享到这里了,由于笔者能力有限,欢迎大家提问一起交流,完整代码详见附件!
源代码: