找回密码
 立即注册
查看: 51|回复: 0

【15系列】SYK-0806-A2S1 工业自动化控制之【14-串口收发字符串】

[复制链接]
  • 打卡等级:偶尔看看I
  • 打卡总天数:15
  • 最近打卡:2025-04-21 13:00:02

19

主题

9

回帖

215

积分

中级会员

积分
215
发表于 2025-4-17 15:15:32 | 显示全部楼层 |阅读模式
大家好,我是『芯知识学堂』的SingleYork,前一篇文章给大家介绍了“【STC15系列】SYK-0806-A2S1 工业自动化控制之【13-串口收发单个字符】”,

这一篇中,笔者继续给大家介绍“串口收发字符串”。

相信很多小伙伴对单个字符的收发是很熟悉了,那么如何实现字符串的收发呢?

首先,我们先来看一下字符串在单片机中是如何存储的。

比如,我们要存储一个字符串“Helloworld”到数组Arry[]中,那么,它在Arry[]这个数组中实际上是按如下方式来存储的:

Arry[0] = ‘H’;
Arry[1] = ‘e’;
Arry[2] = ‘l’;
Arry[3] = ‘l’;
Arry[4] = ‘o’;
Arry[5] = ‘w’;
Arry[6] = ‘o’;
Arry[7] = ‘r’;
Arry[8] = ‘l’;
Arry[9] = ‘d’;
Arry[10] = ‘\0’;

其中,’\0’就是字符串的结束符,也就是说,我们想要存储“Helloworld”到数组Arry[],实际上需要占用11个字节。

那么我们在定义数组Arry[]并赋值的时候,就应该是:Arry[11] = {“Helloworld”},

或者是不指定数组长度,直接这样写也是可以的:Arry[] = {“Helloworld”}。

明白了这一点,那就好办了!接下来我们分析一下单片机接收字符串需要注意些什么。

如果是接收指定长度的字符串,相信大家都知道该怎么处理,定义一个一维数组用来存放将要接收到的字符串,

从数组的0号元素开始存放,直到接收完指定个长度后接收完成。

这样比较简单,笔者就不多说了,笔者要跟大家介绍的是接收不定长度的字符串。那么这个不定长度的字符串要怎么处理呢?

我们一般的做法是,在字符串末尾加一个特定的结束符,一般是用“回车换行符”来作为结束符,

当然我们也可以用“空格”或其他特定符号来作为结束符,

本例中, 笔者是通过“回车换行符”来作为字符串的结束符。

废话不多说,我们直接来看代码,本例代码还是在上一讲基础上做修改,其他部分代码都不需要修改,只需要修改app.c里面的内容即可。

首先,因为我们后面有用到,strstr(char *str1, const char *str2)和memset(void *s, int ch, size_t n)这两个函数,

所以我们需要在app.c文件中添加string.h头文件:

  1. #include        "app.h"
  2. #include "string.h"
复制代码

接下来,定义变量Uart1_RX_Finish 和Uart1_TX_EN,分别作为uart1数据接收完成标志和uart1发送数据使能标志:

  1. bit Uart1_RX_Finish = 0;//Uart1数据接收完成标志,1 为接收完成
  2. bit Uart1_TX_EN     = 0;//Uart1发送数据使能标志
复制代码

然后,就是app_init()函数了,主要是对GPIO和UART的初始化:

  1. void app_init(void)
  2. {
  3.         UART_config();                 //UART初始化
  4.     GPIO_Config();                 //GPIO初始化
  5.    
  6.     EA = 1;                                       //开启总中断
  7. }
复制代码

字符串接处理及接收完成的判断是在“UART1中断函数”中,笔者这里是以收到“回车换行符”来作为字符串结束判断:

  1. /********************* UART1中断函数************************/
  2. void UART1_int (void) interrupt UART1_VECTOR
  3. {
  4.         if(RI)
  5.         {
  6.                 RI = 0;
  7.         if(COM1.RX_Cnt >= COM_RX1_Lenth)        COM1.RX_Cnt = 0;
  8.         RX1_Buffer[COM1.RX_Cnt++] = SBUF;
  9.         
  10.         if((RX1_Buffer[COM1.RX_Cnt-2]=='\r')&&(RX1_Buffer[COM1.RX_Cnt-1]=='\n'))
  11.         {
  12.             //以“回车换行符”为结束标志
  13.             Uart1_RX_Finish = 1;        //将接收完成标志位置1
  14.             COM1.TX_write   = 0;
  15.         }
  16.         }
  17.         if(TI)
  18.         {
  19.                 TI = 0;
  20.         COM1.B_TX_busy = 0;
  21.         }
  22. }
复制代码

当字符串接收完成后,将标志位Uart1_RX_Finish 置“1”,同时进入app_run()函数中处理接收到的数据:

  1. void app_run(void)
  2. {
  3.     if(Uart1_RX_Finish)
  4.     {
  5.         if(!Uart1_TX_EN)
  6.         {
  7.             //根据收到的指令执行相应的动作
  8.             if(strstr((char *)RX1_Buffer,"Y00_ON\r\n")!=NULL)       //若两字符串相等
  9.             {
  10.                 Y00 = OutputT_ON;
  11.             }
  12.             else if(strstr((char *)RX1_Buffer,"Y00_OFF\r\n")!=NULL)
  13.             {
  14.                 Y00 = OutputT_OFF;
  15.             }
  16.             Uart1_TX_EN = 1;//uart1发送数据使能置“1”,准备开始发送数据
  17.         }
  18.             
  19.         if(COM1.TX_write<COM1.RX_Cnt)
  20.         {
  21.             if(COM1.B_TX_busy == 0)
  22.             {
  23.                 COM1.B_TX_busy = 1;
  24.                 SBUF = RX1_Buffer[COM1.TX_write++];        //发送接收到的字符
  25.             }
  26.         }
  27.         else
  28.         {
  29.             COM1.RX_Cnt     = 0;
  30.             Uart1_RX_Finish = 0;
  31.             Uart1_TX_EN     = 0;//数据发送完成,uart1发送数据使能清“0”
  32.             
  33.             memset(RX1_Buffer,NULL,sizeof(RX1_Buffer));//清空RX1_Buffer数组
  34.         }
  35.     }
  36. }
复制代码

这部分代码主要分为两个功能,一部分就是对接收到的字符串执行相关的动作,比如:

在收到字符串"Y00_ON\r\n"时,Y00输出ON;
在收到字符串"Y00_OFF\r\n"时,Y00输出OFF;

  1. if(strstr((char *)RX1_Buffer,"Y00_ON\r\n")!=NULL)       //若两字符串相等
  2. {
  3.     Y00 = OutputT_ON;
  4. }
  5. else if(strstr((char *)RX1_Buffer,"Y00_OFF\r\n")!=NULL)
  6. {
  7.     Y00 = OutputT_OFF;
  8. }
复制代码

这里用到了一个strstr(char *str1, const char *str2)函数,用来查找字符串,具体功能如下:

  1. 函数原型:
  2. extern char *strstr(char *str1, const char *str2);
  3. 语法:
  4. * strstr(str1,str2)
  5. str1: 被查找目标 string expression to search.
  6. str2: 要查找对象 The string expression to find.
  7. 返回值:若str2是str1的子串,则返回str2在str1的首次出现的地址;如果str2不是str1的子串,则返回NULL。
复制代码

第二部分的功能,就是将收到的字符串返回到串口助手,在发送完成之后,清零相关变量和数组:

  1. if(COM1.TX_write<COM1.RX_Cnt)
  2. {
  3.     if(COM1.B_TX_busy == 0)
  4.     {
  5.         COM1.B_TX_busy = 1;
  6.         SBUF = RX1_Buffer[COM1.TX_write++];        //发送接收到的字符
  7.     }
  8. }
  9. else
  10. {
  11.     COM1.RX_Cnt     = 0;
  12.     Uart1_RX_Finish = 0;
  13.     Uart1_TX_EN     = 0;//数据发送完成,uart1发送数据使能清“0”
  14.    
  15.     memset(RX1_Buffer,NULL,sizeof(RX1_Buffer));//清空RX1_Buffer数组
  16. }
复制代码

这部分有用到一个memset(void *s, int ch, size_t n)来快速清零数组RX1_Buffer,关于该函数的具体功能如下:

  1. void *memset(void *s, int ch, size_t n);
  2. 函数解释:将s中当前位置后面的n个字节 (typedef unsigned int size_t )用 ch 替换并返回 s 。
  3. memset:作用是在一段内存块中填充某个给定的值,它是对较大的结构体或数组进行清零操作的一种最快方法 [1]  。
  4. memset()函数原型是extern void *memset(void *buffer, int c, int count)
  5. 其中,buffer:为指针或是数组,c:是赋给buffer的值,count:是buffer的长度.
复制代码

01.png

好了,关于使用本节内容笔者就介绍到这里了,有疑问的小伙伴们可以给笔者留言或者直接参与评论,

下一节笔者将继续给大家介绍“串口的基本使用”,详见“SYK-0806-A2S1 工业自动化控制之【15-串口收发十六进制数】”,感谢大家的支持!

本章附件:

【STC15系列】SYK-0806-A2S1- 14-串口收发字符串.rar (70.16 KB, 下载次数: 2)




本帖被以下淘专辑推荐:

回复

使用道具 举报 送花

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

QQ|手机版|小黑屋|深圳国芯人工智能有限公司 ( 粤ICP备2022108929号-2 )

GMT+8, 2025-5-1 21:35 , Processed in 0.125126 second(s), 51 queries .

Powered by Discuz! X3.5

© 2001-2025 Discuz! Team.

快速回复 返回顶部 返回列表