Arduino程序设计(十二)串口通信实验(上位机发送指令,下位机执行)

串口通信实验(上位机发送指令,下位机执行)

  • 前言
  • 一、字符串数据类型操作
    • 1、字符串简介
    • 2、常见的字符串操作函数
  • 二、串口通信介绍
    • 1、基础知识
    • 2、工作原理
  • 三、串口通信实验
    • 1、上位机控制LED灯
    • 2、上位机实现按键检测


前言

  • 本文主要介绍String类函数、Serial类函数及两个串口通信实验,分别是:
  • 1、字符串数据类型操作;
  • 2、串口通信介绍;
  • 3、上位机控制LED灯;
  • 4、上位机实现按键检测。

一、字符串数据类型操作

1、字符串简介

  • (1)字符串用于存储文本,可用于在LCD或Arduino IDE串行监视器窗口中显示文本。 同时字符串对于存储用户输入也很有用。 例如,用户在连接到Arduino的键盘上键入的字符。

  • (2)Arduino编程中有两种类型的字符串:

  • ① 字符数组,与C编程中使用的字符串相同;

  • ② Arduino String,它允许我们在程序中使用字符串对象。

  • (3)构造 String 类的实例,以下都是字符串的有效声明:

String stringOne = "Hello String";                    // 用双引号括起来的常量字符串(即 char 数组)
String stringOne = String('a');                       // 单个常量字符,用单引号括起来
String stringTwo = String("This is a string");        // converting a constant string into a String object
String stringOne = String(stringTwo + " with more");  //concatenating two strings
String stringOne = String(13);                        // 一个常量整数
String stringOne = String(analogRead(0), DEC);        // using an int and a base
String stringOne = String(45, HEX);                   // using an int and a base (hexadecimal)
String stringOne = String(255, BIN);                  // using an int and a base (binary)
String stringOne = String(millis(), DEC);             // using a long and a base
String stringOne = String(5.698, 3);                  // 浮点数或双精度数,使用指定的小数位
  • (4)String 类的声明:
  • 从数字构造字符串会生成一个包含该数字的 ASCII 表示形式的字符串,默认是十进制。
字符串声明 功能说明
String thisString = String(13) 十进制字符串"13"
String thisString = String(13, HEX) 字符串"d",它是 13 的十六进制表示
String thisString = String(13, BIN) 字符串"1101",它是 13 的二进制表示
String(val) val:要格式化为字符串的变量。允许的数据类型:string、char、byte、int、long、unsigned int、unsigned long、float、double。
String(val, base) val同上,base:(可选)格式化整数值的基数
String(val, decimalPlaces) val同上,decimalPlaces:仅当 val 为 float 或 double 时.所需的小数位

2、常见的字符串操作函数

String类 功能描述
String() String 类的实例,注意:在“双引号”中指定的常量字符串被视为char数组,不是String类的实例
charAt(n) 返回字符串中第n个字符
compareTo(S2) 和给的S2字符串比较,如果两个字符串相同,返回值等于0;否则,返回值不等于0
equals() 比较两个字符串是否相等。 比较区分大小写,这意味着字符串“hello”不等于字符串“HELLO”。
length() 返回字符串中的字符数
substring(from, to) 获取String的子字符串。从指定的起始索引 " from " 到结束索引 " to " (不包括)截取字符串的子串。
substring(from) 返回一个从给定索引" from " 到结尾的新的字符串
toInt() 返回字符串中数字为整数值
toCharArray(buf,len) 把字符串转换成数组char[]。buf:指定char[]的位置,char[] 的大小一定要>=字符串的大小,len:复制的长度
strlen() 用于获取字符串的长度。 字符串的长度仅适用于可打印字符,不包括空终止符。
sizeof() 用于获取包含字符串的数组的长度。 长度包括空终止符,因此长度比字符串的长度多一个。
strcat() 将传递给它的第二个字符串放到传递给它的第一个字符串的末尾
concat(S2) 返回字符串和字符串S2合并后的新字符串
equals(S2) 如果字符串和S2完全相符,就返回TRUE

二、串口通信介绍

1、基础知识

  • (1)串口通信协议:

  • 串行通信协议应严格遵守,串行通信协议是一套规则,必须应用这些规则才能使设备正确地解释它们相互交换的数据。但是,Arduino 会自动处理这个问题,这样程序员/用户的工作就可以简化为简单的写(发送的数据)和读(接收的数据)。Arduino 串口通信的优缺点如下:

  • ① 优点:传输线少,长距离传送时成本低

  • ② 缺点:数据的传送控制比并行通信复杂

  • (2)串行通信的分类:

  • 同步通信 —— 同步的设备使用相同的时钟,它们的时序彼此同步。

  • 异步通信 —— 异步的设备具有各自的时钟,并由前一状态的输出触发。

  • 如果给所有连接的设备提供相同的时钟,则它们是同步的。如果没有时钟线,它是异步的。

  • (3)串行通信的传输方向:

  • ① 单工:是指数据传输仅能沿一个方向,不能实现反向传输。

  • ② 半双工:是指数据传输可以沿两个方向,但需要分时进行。

  • ③ 全双工:是指数据可以同时进行双向传输。

  • (4)串口通信定义:串口通信(Serial CommunicaTIons)是指串口按位(bit)发送和接收字节。串口用于ASCII码字符的传输,通信使用3根线完成,分别是地线、发送线、接收线。由于串口通信是异步的,端口能够在一根线上发送数据同时在另一根线上接收数据,其他线用于握手,但不是必须的。串口通信最重要的参数是波特率、数据位、停止位和奇偶校验。 对于两个进行通信的端口,这些参数必须要匹配。

  • (5)波特率:通信双方需要使用一致的的波特率才能正常通信。 Arduino串口通信通常会使用以下波特率:300、600、1200、2400、4800、9600、14400、19200、28800、38400、57600、115200等,最常用的是9600。波特率越大,说明串口通信的速率越快。

  • (6)Arduino UNO通信:通过Arduino上的USB接口与计算机连接而进行Arduino与计算机之间的串口通信。除此之外,还可以使用串口引脚连接其他的串口设备进行通信。需要注意的是,通常一个串口只能连接一个设备进行通信。

2、工作原理

  • (1)在 Arduino 与其他器件通信的过程中,数据传输实际上都是以数字信号(即电平高低变化)的形式进行的,串口通信也是如此。当使用 Serial. print() 函数输出数据时,Arduino 的发送端会输出一连串的数字信号,称这些数字信号为数据帧。
  • (2)UART模式的数据格式如下:
    Arduino程序设计(十二)串口通信实验(上位机发送指令,下位机执行)_第1张图片
数据格式 功能描述
起始位 起始位总为低电平,是一组数据帧开始传输的信号。
数据位 数据位是一个数据包,其中承载了实际发送的数据的数据段。 当Arduino通过串口发送一个数据包时,实际的数据可能不是8位的,比如,标准的ASCII码是0-127(7位)。而扩展的ASCII码则是0-255(8位)。如果数据使用简单的文本(标准ASCII码),那么每个数据包将使用7位数据。Arduino 默认使用8位数据位,即每次可以传输1B数据。
校验位 校验位是串口通信中一种简单的检错方式。可以设置为偶校验或者奇校验。当然,没有校验位也可以。Arduino 默认无校验位。
停止位 每段数据帧的最后都有停止位表示该段数据帧传输结束。停止位总为高电平,可以设置停止位为1位或2位。Arduino默认是1位停止位。当串口通信速率较高或外部干扰较大时,可能会出现数据丢失的情况。为了保证数据传输的稳定性,最简单的方式就是降低通信波特率或增加停止位和校验位。在Arduino中,可以通过 Serial.begin(speed,config) 语句配置串口通信的数据位、停止位和校验位参数。
  • (3)硬件串口通信:
  • 硬件串口的操作类为HardwareSerial,定义于 HardwareSerial.h 源文件中,并对用户公开声明了Serial对象,用户在Arduino程序中直接调用Serial, 就可实现串口通讯。常用的成员函数如下:
Serial类 功能描述 语法 参数 返回值
begin() 初始化串口,通常置于setup()函数中,该函数可配置串口的各项参数。 Serial.begin(speed);Serial.begin(speed, config); speed:波特率,一般取值9600,115200等。config:设置数据位、校验位和停止位。例如Serial. begin(9600 , SERIAL _8E2) 语句设置串口波特率为9600,数据位为8,偶校验,停止位为2。
end() 结束串口通信,该操作可以释放该串口所在的数字引脚,此时串口Rx和Tx可以作为数字IO引脚使用。 Serial.end();
available() 判断串口缓冲区的状态,获取串口接收到的数据个数,即获取串口接收缓冲区中的字节数。接收缓冲区最多可保存64B的数据。 Serial.available(); 可读取的字节数
print() 串口输出数据,写入字符数据到串口。将数据输出到串口。数据会以ASCII码形式输出。如果想以字节形式输出数据,则需要使用 write() 函数。 Serial.print(val);Serial.print(val, format); val:需要输出的数据,任意数据类型。format:输出的数据格式。BIN(二进制)、OCT(八进制)、DEC(十进制)、HEX(十六进制)。对于浮点数,此参数指定要使用的小数位数(默认输出2位)。 返回输出的字节数
println() 将数据输出到串口,并回车换行。数据会以ASCII码形式输出。 Serial.println(val);Serial.println(val, format); val:需要输出的数据,任意数据类型。format:输出的数据格式。和 Serial.print(val) 和相同。 返回输出的字节数
readBytes() 从接收缓冲区读取指定长度的字符,并将其存人一个数组中。若等待数据时间超过设定的超时时间,则退出该函数。 Serial.readBytes(buffer, length); buffer:用于存储数据的数组(char[]或者byte[])。length:需要读取的字符长度。 读到的字节数;如果没有找到有效的数据,则返回0。
peek() 返回1字节的数据,但不会从接收缓冲区删除该数据。与read()函数不同,read()函数读取数据后,会从接收缓冲区删除该数据。 Serial. peek(); 进入接收缓冲区的第1字节的数据;如果没有可读数据,则返回-1。
write() 输出数据到串口。以字节形式输出到串口。 Serial. write(val);Serial. write(str);Serial. write(buf, len); val:发送的数据。str:String型的数据。.buf:数组型的数据。len:缓冲区的长度。 输出的字节数
read() 读取串口数据,一次读一个字符,读完后删除已读数据。 Serial.read(); 返回串口缓存区中第一个可读字节,当没有可读数据时返回-1。
readString() 每次读取一个字符串,程序不知道什么时候能够读取完整的字符串,会默认系统等待1s的时间 serial.readString() String
readStringUntil() 每次读取一个字符串,知道遇到截至字符,比如:Serial.readStringUntil(‘,’):程序遇到逗号就不读取串口中的内容了,系统也不会等待,所以你只需要把你要传达的命令放在逗号之前就可以了,相当于给系统一个明确的提示,花费的时间与read()不相上下,但是又能成功读取到字符串。 serial.readStringUntil(inByte) inByte (int) 指定用于标记数据结束的字符 String
  • 本文未涉及软件模拟串口通信,所以软串口操作类SoftwareSerial暂不介绍。

三、串口通信实验

1、上位机控制LED灯

  • (1)本实验采用Arduino UNO R3开发板D13引脚对应的LED及Arduino软件自带的串口调试助手,实现预设功能。
  • (2)实现功能:
  • ① 串口发送 " led_on() " 点亮LED,单片机返回一次" ON ";
  • ② 串口发送 " led_off() " 熄灭LED,单片机返回一次" OFF ";
  • ③ 串口发送 " led_flash(x) " 使LED闪烁x次,单片机返回一次" OK "。

代码实现:

//串口通信实验
//串口助手发送"led_on()"点亮LED,发送"led_off()"熄灭LED,发送led_flash(x)使LED闪烁x次,单片机收到指令后并执行后返回"OK"
//x范围任意

int led = 13;

String myString = ""; //接收串口发送过来的值

String short_String = ""; //存储myString截取后的字符串

String xstr = ""; //存储led_flash(x)的字符串x

String Control_LED[] = {"led_on()", "led_off()", "led_flash(x)"}; //定义字符串数组

int x = 0;//存储led_flash(x)的整数x

void setup()
{
  pinMode(led, OUTPUT);
  Serial.begin(9600);
}

void loop()
{
  if (Serial.available() > 0)//如果串口有数据
  {
    myString = Serial.readStringUntil('\n');//读取字符串
    short_String = myString.substring(0, 8);//截取输入字符串myString的前8位字符
    Control_LED[0] = Control_LED[0].substring(0, 8);//截取字符串Control_LED[0]的前8位字符
    Control_LED[1] = Control_LED[1].substring(0, 8);//截取字符串Control_LED[1]的前8位字符
    Control_LED[2] = Control_LED[2].substring(0, 8);//截取字符串Control_LED[2]的前8位字符

    if (myString.length() > 10)
    {
      xstr = myString.substring(10, myString.length() - 1) ; //提取灯的闪烁次数xstr字符串
      x = xstr.toInt();//将字符串xstr转成数字x
    }

    if (short_String.compareTo(Control_LED[0]) == 0)//比较short_String和截取后led_on()是否相同
    {
      Serial.println("ON");
      digitalWrite(led, HIGH);
    }
    else if (short_String.compareTo(Control_LED[1]) == 0)//比较short_String和截取后led_off()是否相同
    {
      Serial.println("OFF");
      digitalWrite(led, LOW);
    }
    else if (short_String.compareTo(Control_LED[2]) == 0 ) //比较short_String和截取后led_flash(x)是否相同
    {
      Serial.println("OK");

      while (x > 0)
      {
        x--;//x为灯的闪烁次数
        digitalWrite(led, HIGH);
        delay(500);
        digitalWrite(led, LOW);
        delay(500);
      }
    }
  }
}
  • (3)实现现象(部分展示):
    Arduino程序设计(十二)串口通信实验(上位机发送指令,下位机执行)_第2张图片
  • 注意:红色箭头标注的LED灯,对应D13引脚。

2、上位机实现按键检测

  • (1)本实验采用Arduino UNO R3开发板及自主搭建电路的方式,实现预设功能。

  • (2)上位机实现按键检测的电路图,如下图所示:

Arduino程序设计(十二)串口通信实验(上位机发送指令,下位机执行)_第3张图片

  • (3)实现功能:串口发送 " read_key() " 读取按键状态,如果按键的IO口为高电平,返回一次 " key=1 " ;如果为低电平,返回一次 " key=0 " 。

代码实现:

//串口通信实验
//串口助手发送"read_key()"读取按键状态,如果按键的IO口为高电平返回"key=1",如果为低电平返回"key=0"
//按下只返回一次"key=0",松开只返回一次"key=1"

int KEY = 8;
bool flag = 0;
String myString = ""; //接收串口发送过来的值
String KEY_String = "read_key()";//定义字符串

void setup() {
  pinMode(KEY, INPUT_PULLUP);
  Serial.begin(9600);
}
void loop()
{
  if (Serial.available() > 0)//如果串口有数据
  {
    myString = Serial.readStringUntil('\n');//读取字符串
  }

  if (myString.compareTo(KEY_String) == 0)//比较myString和"read_key()"是否相同
  {
    if (digitalRead(KEY) == LOW)        //有按键按下
    {
      delay(10);                        //延时去抖动
      if (digitalRead(KEY) == LOW)      //有按键按下
      {
        if (flag == 1)
        {
          flag = 0;
          Serial.println("key = 0");//有按键按下,IO口为低电平,打印"key = 0"
        }
      }
    }
    else
    {
      if (flag == 0)
      {
        flag = 1;
        Serial.println("key = 1");//没有按键按下,IO口为高电平,打印"key = 1"
      }
    }
  }
}

  • (4)实现现象(部分展示):
    Arduino程序设计(十二)串口通信实验(上位机发送指令,下位机执行)_第4张图片
  • 现象说明:串口发送 " read_key() " ,单片机返回 " key=1 ",按下按键,单片机返回 " key=0 ",松开按键,单片机返回 " key=1 "。

参考资料1: Arduino String()用法及代码示例
参考资料2: Arduino - 字符串( Strings)
参考资料3: Arduino字符串操作函数
参考资料4: Arduino基础篇(五)-- 如何快速上手串口通信(Serial)

你可能感兴趣的:(Arduino,arm开发,单片机)