【15系列】SYK-0806-A2S1 工业自动化控制之【14-串口收发字符串】
大家好,我是『芯知识学堂』的SingleYork,前一篇文章给大家介绍了“【STC15系列】SYK-0806-A2S1 工业自动化控制之【13-串口收发单个字符】”,这一篇中,笔者继续给大家介绍“串口收发字符串”。
相信很多小伙伴对单个字符的收发是很熟悉了,那么如何实现字符串的收发呢?
首先,我们先来看一下字符串在单片机中是如何存储的。
比如,我们要存储一个字符串“Helloworld”到数组Arry[]中,那么,它在Arry[]这个数组中实际上是按如下方式来存储的:
Arry = ‘H’;
Arry = ‘e’;
Arry = ‘l’;
Arry = ‘l’;
Arry = ‘o’;
Arry = ‘w’;
Arry = ‘o’;
Arry = ‘r’;
Arry = ‘l’;
Arry = ‘d’;
Arry = ‘\0’;
其中,’\0’就是字符串的结束符,也就是说,我们想要存储“Helloworld”到数组Arry[],实际上需要占用11个字节。
那么我们在定义数组Arry[]并赋值的时候,就应该是:Arry = {“Helloworld”},
或者是不指定数组长度,直接这样写也是可以的:Arry[] = {“Helloworld”}。
明白了这一点,那就好办了!接下来我们分析一下单片机接收字符串需要注意些什么。
如果是接收指定长度的字符串,相信大家都知道该怎么处理,定义一个一维数组用来存放将要接收到的字符串,
从数组的0号元素开始存放,直到接收完指定个长度后接收完成。
这样比较简单,笔者就不多说了,笔者要跟大家介绍的是接收不定长度的字符串。那么这个不定长度的字符串要怎么处理呢?
我们一般的做法是,在字符串末尾加一个特定的结束符,一般是用“回车换行符”来作为结束符,
当然我们也可以用“空格”或其他特定符号来作为结束符,
本例中, 笔者是通过“回车换行符”来作为字符串的结束符。
废话不多说,我们直接来看代码,本例代码还是在上一讲基础上做修改,其他部分代码都不需要修改,只需要修改app.c里面的内容即可。
首先,因为我们后面有用到,strstr(char *str1, const char *str2)和memset(void *s, int ch, size_t n)这两个函数,
所以我们需要在app.c文件中添加string.h头文件:
#include "app.h"
#include "string.h"
接下来,定义变量Uart1_RX_Finish 和Uart1_TX_EN,分别作为uart1数据接收完成标志和uart1发送数据使能标志:
bit Uart1_RX_Finish = 0;//Uart1数据接收完成标志,1 为接收完成
bit Uart1_TX_EN = 0;//Uart1发送数据使能标志
然后,就是app_init()函数了,主要是对GPIO和UART的初始化:
void app_init(void)
{
UART_config(); //UART初始化
GPIO_Config(); //GPIO初始化
EA = 1; //开启总中断
}
字符串接处理及接收完成的判断是在“UART1中断函数”中,笔者这里是以收到“回车换行符”来作为字符串结束判断:
/********************* UART1中断函数************************/
void UART1_int (void) interrupt UART1_VECTOR
{
if(RI)
{
RI = 0;
if(COM1.RX_Cnt >= COM_RX1_Lenth) COM1.RX_Cnt = 0;
RX1_Buffer = SBUF;
if((RX1_Buffer=='\r')&&(RX1_Buffer=='\n'))
{
//以“回车换行符”为结束标志
Uart1_RX_Finish = 1; //将接收完成标志位置1
COM1.TX_write = 0;
}
}
if(TI)
{
TI = 0;
COM1.B_TX_busy = 0;
}
}
当字符串接收完成后,将标志位Uart1_RX_Finish 置“1”,同时进入app_run()函数中处理接收到的数据:
void app_run(void)
{
if(Uart1_RX_Finish)
{
if(!Uart1_TX_EN)
{
//根据收到的指令执行相应的动作
if(strstr((char *)RX1_Buffer,"Y00_ON\r\n")!=NULL) //若两字符串相等
{
Y00 = OutputT_ON;
}
else if(strstr((char *)RX1_Buffer,"Y00_OFF\r\n")!=NULL)
{
Y00 = OutputT_OFF;
}
Uart1_TX_EN = 1;//uart1发送数据使能置“1”,准备开始发送数据
}
if(COM1.TX_write<COM1.RX_Cnt)
{
if(COM1.B_TX_busy == 0)
{
COM1.B_TX_busy = 1;
SBUF = RX1_Buffer; //发送接收到的字符
}
}
else
{
COM1.RX_Cnt = 0;
Uart1_RX_Finish = 0;
Uart1_TX_EN = 0;//数据发送完成,uart1发送数据使能清“0”
memset(RX1_Buffer,NULL,sizeof(RX1_Buffer));//清空RX1_Buffer数组
}
}
}
这部分代码主要分为两个功能,一部分就是对接收到的字符串执行相关的动作,比如:
在收到字符串"Y00_ON\r\n"时,Y00输出ON;
在收到字符串"Y00_OFF\r\n"时,Y00输出OFF;
if(strstr((char *)RX1_Buffer,"Y00_ON\r\n")!=NULL) //若两字符串相等
{
Y00 = OutputT_ON;
}
else if(strstr((char *)RX1_Buffer,"Y00_OFF\r\n")!=NULL)
{
Y00 = OutputT_OFF;
}
这里用到了一个strstr(char *str1, const char *str2)函数,用来查找字符串,具体功能如下:
函数原型:
extern char *strstr(char *str1, const char *str2);
语法:
* strstr(str1,str2)
str1: 被查找目标 string expression to search.
str2: 要查找对象 The string expression to find.
返回值:若str2是str1的子串,则返回str2在str1的首次出现的地址;如果str2不是str1的子串,则返回NULL。
第二部分的功能,就是将收到的字符串返回到串口助手,在发送完成之后,清零相关变量和数组:
if(COM1.TX_write<COM1.RX_Cnt)
{
if(COM1.B_TX_busy == 0)
{
COM1.B_TX_busy = 1;
SBUF = RX1_Buffer; //发送接收到的字符
}
}
else
{
COM1.RX_Cnt = 0;
Uart1_RX_Finish = 0;
Uart1_TX_EN = 0;//数据发送完成,uart1发送数据使能清“0”
memset(RX1_Buffer,NULL,sizeof(RX1_Buffer));//清空RX1_Buffer数组
}
这部分有用到一个memset(void *s, int ch, size_t n)来快速清零数组RX1_Buffer,关于该函数的具体功能如下:
void *memset(void *s, int ch, size_t n);
函数解释:将s中当前位置后面的n个字节 (typedef unsigned int size_t )用 ch 替换并返回 s 。
memset:作用是在一段内存块中填充某个给定的值,它是对较大的结构体或数组进行清零操作的一种最快方法 。
memset()函数原型是extern void *memset(void *buffer, int c, int count)
其中,buffer:为指针或是数组,c:是赋给buffer的值,count:是buffer的长度.
好了,关于使用本节内容笔者就介绍到这里了,有疑问的小伙伴们可以给笔者留言或者直接参与评论,
下一节笔者将继续给大家介绍“串口的基本使用”,详见“SYK-0806-A2S1 工业自动化控制之【15-串口收发十六进制数】”,感谢大家的支持!
本章附件:
页:
[1]