arduino教程-05. 串口


串口操作函数与示例代码大全

5.1 课程说明

了解通过usb串口与电脑的通信;了解字符与字符串数据类型;利用项目了解通过电脑上的串口通信的方式,
项目功能:控制Arduino的第13脚的led。将依据用户输入“1”或“0”,点亮或关闭位于第13脚的LED。

5.2 器材:

名称数量规格
Arduino uno控制板1R3

5.3 重点器材介绍

5.4 电路:

5.5 预备知识:

5.5.1 认识字符与字符串数据类型

电脑把文字信息分成字符(character)和字符串(string)

控制字符ASCII编码程序写法说明
NULL0\0没有数据
CR(Carriage Return)13\r回车
LF(Line Feed或New Line)10\n换行
Tab09\t定位键
Backspace08\b退位键
BEL07\a铃声
  1. 字符
    每个字符都用一个唯一的数字码来代表,,
    char data = ‘A’;
    char data = 65; //以数字格式存储“字符” ASCII定义了128个字符,其中95个可显示的字符,包括空格键(十进制32)、英文字母和符号。IBM在此基础上,延伸定义了额外的128字符,
    ASCII定义的其它33个字符,则是不能显示字符,例如:回车、ESC、tab等
    回车(CR):是一个让输出回到该行文字开头的控制字符
    换行(LF):一个让输出设备切换到下一行的控制字符
  2. 字符串
    一连串字符的集合,Arduino程序采用数组来存放字符串,数据值前后一定双引号括起来。每个字符串都有一个Null字符结尾,因此实际长度比看到的多一
    例如:
    char s = ‘a’;
    char str[] = “Arduino”; // 结尾会自动加一个Null,用双引号
    char str1[]={‘A’,‘r’, ‘d’,‘u’,‘i’,‘n’,‘o’,’\0’}; //str与str1都是字符串,值也相同。
5.5.2. switch…case控制结构

语句格式
switch(运算式){
case 值1:
语句组;
break;
case 值2:
语句组2;
break;
……
default:

}
说明:

  • 通过比对switch()里的变量和case后面的值,来决定切换执行那一段程序。
  • break代表“终止”
  • switch……case并不能完全取代if……else语句,因为前者只能判断条件是否完全相等。
    Tips: 利用switch……case语句改写上面的程序。
void loop() {
    if (Serial.available() ) {
        val = Serial.read();
        switch (val) {
            case '0': 
                digitalWrite(LED, LOW);
                break;
            case '1':
                digitalWrite( LED, HIGH);
                break;
        }
    }
}

5.6 从Arduino传输串行信息给电脑

我们可以通过串口联机来观察程序内部的运行情况
Arduino内建处理串口联机的Serial扩展库
设置数据传输频率
Serial.begin(9600)
从串口输出数据
Serial.print("hello ");
Serial.print(“World.”);

Serial.print("Hello\t");
Serial.print("world.");

Serial.print("\n");

Serial.println("Hello\tWorld.");
Serial.println("bye");

5.7 动手做一做

5.7.1 观察串口数据

• 实验说明: 通过串口来观察某个变量的数值。
• 实验程序:

代码1
void setup(){
    Serial.begin(9600);
    Serial.print("hello ");
    Serial.print("World.");
        
    Serial.print("Hello\t");
    Serial.print("world.");
        
    Serial.print("\n");
        
    Serial.println("Hello\tWorld.");
    Serial.println("bye");
}

void loop(){
    
}
代码2
const byte ledPin = 13;
void setup(){
    Serial.begin(9600);
    Serial.println("hello,");
    Serial.print("\tLED pin is: ");
    Serial.print(ledPin);
    Serial.print("\nBye");
}

void loop(){
    
}
代码3
char str[] = "hello ";
const byte ledPin = 13;
void setup(){
    Serial.begin(9600);
    Serial.println(str);
    Serial.print("\tLED pin is: ");
    Serial.print(ledPin);
    Serial.print("\nBye");
}

void loop(){
    
}
5.7.2 从Arduino接收串口数据

Arduino 在微控制器的内存中划分出缓存区(buffer,可暂存64字节的串行数据),用于来自串口的输入数据

  • 查看缓存区 Serial.available(); //返回已经缓存的字节数,若缓存区没有数据,将返回0。 Serial.read(); //返回排在缓存区里的第一个字节,**若缓存区里面没有数据,它将返回-1
  • 查看缓存区大小
    Arduino缓存区的大小,设置在Arduino安装文件“hardware\arduino\cores\arduino\HardwareSerial.cpp里,其定义依据可用的内存而定,划分出16或者64字节的空间
#if (RAMEND < 1000)   //若可用的内存(RAM)少于此值
    #define SERIAL_BUFFER_SIZE 16    //串行缓存区大小设置成16字节
#else 
    #define SERIAL_BUFFER_SIZE 64
#endif

5.8 从串口控制LED

代码:

const byte LED = 13;
char val;

void setup(){
    pinMode(LED,OUTPUT);  // 将LED引脚设置为输出
    Serial.begin(9600);   //启动串口,并设置传输速率为9600bps
    Serial.print("Welcome to Arduino!"); //提示:联机成功。
}

void loop(){
    if (Serial.available()) { //available()函数返回大于等于0的数
        val =  Serial.read();
        if (val == '1') {  // 接收到的数据类型为字符。
            digitalWrite(LED, HIGH);
            Serial.print("LED ON");
        } else if (val == '0') {
            digitalWrite(LED, LOW);
            Serial.print("LED OFF");
        }
    }
}

修改上面的代码,可以提醒输入的数值不是0或1,同时显示打开和关闭LED的次数
Ps:在电脑键盘按下数字‘1’,是字符‘1’,实际数据值是49,因此:val == 'a’与val == 49两种写法相同。

串口监控窗口底下的弹出式菜单说明

决定是否在输出字符串后面加上“换行”字符。
• 没有行结束符:不在数据后面加上“回车”或“换行
• NL(new line):在数据后面加上“换行”
• CR(carriage return):加上“回车”
• NL与CR:加上“回车”与“换行”
例如,若选择NL,那么当送出‘1’时,实际发送的数据是“1\n”,也就是ASCII编码49和10两个字符。

5.9 补充阅读材料:

5.9.1. Arduino 字符串

字符串用于存储文本。它们可用在LCD或Arduino IDE串口监视器窗口中显示文本。字符串也可用于存储用户输入。例如,用户在连接到Arduino的键盘上键入的字符。
在Arduino编程中有两种类型的字符串:
字符数组,与C编程中使用的字符串相同。

  1. Arduino字符串,它允许我们在草图中使用字符串对象。 在本章中,我们将学习Arduino草图中的字符串,对象和字符串的使用。在本章末尾,你将学习在草图中使用哪种类型的字符串。
    字符串字符数组
    我们要学习的第一种类型的字符串是 char 类型的一系列字符。在前面的章节中,我们学习了一个数组是什么:存储器中存储的相同类型的变量的连续序列。一个字符串是一个char变量类型的数组。

字符串是一个特殊的数组,在字符串的末尾有一个额外的元素,其值总是为0(零)。这被称为“空终止字符串”。

  1. 字符串字符数组示例
    此示例将显示如何创建字符串并将其打印到串口监视器窗口。
void setup() {
   char my_str[6]; // an array big enough for a 5 character string
   Serial.begin(9600);
   my_str[0] = 'H'; // the string consists of 5 characters
   my_str[1] = 'e';
   my_str[2] = 'l';
   my_str[3] = 'l';
   my_str[4] = 'o';
   my_str[5] = 0; // 6th array element is a null terminator
   Serial.println(my_str);
}

void loop() { 

}

以下示例显示了字符串由什么组成。一个具有可打印字符的字符数组和0作为数组的最后一个元素,表示这是字符串结束的位置。通过使用 Serial.println()并传递字符串的名称,可以将字符串打印到Arduino IDE串口监视器窗口。
同样的例子可以用更方便的方式编写,如下所示:
 示例

void setup() {
   char my_str[] = "Hello";
   Serial.begin(9600);
   Serial.println(my_str);
}

void loop() {

}

在这个草图中,编译器计算字符串数组的大小,并自动使用空值0终止字符串。一个长度为六个元素长,由五个字符后跟一个零组成的数组,其创建方式与上一个草图完全相同。
3. 操作字符串数组
我们可以在草图中更改字符串数组,如下图所示。
例子

void setup() {
   char like[] = "I like coffee and cake"; // create a string
   Serial.begin(9600);
   // (1) print the string
   Serial.println(like);
   // (2) delete part of the string
   like[13] = 0;
   Serial.println(like);
   // (3) substitute a word into the string
   like[13] = ' '; // replace the null terminator with a space
   like[18] = 't'; // insert the new word
   like[19] = 'e';
   like[20] = 'a';
   like[21] = 0; // terminate the string
   Serial.println(like);
}

void loop() {

}

结果
I like coffee and cake
I like coffee
I like coffee and tea
以上草图按以下方式工作。
(1)创建和打印字符串 在上面给出的草图中,创建了一个新的字符串,然后打印出来显示在串口监视器窗口中。
(2)缩短字符串 通过用空终止0替换字符串中的第14个字符来缩短字符串。这是从0开始计算的字符串数组中的13号元素。
打印字符串时,所有字符都打印到新的空终止0。其他字符不消失;它们仍然存在于内存中,并且字符串数组仍然是相同的大小。唯一的区别是任何使用字符串的函数只能看到第一个空终止符前的字符串。
(3)更改字符串中的单词 最后,草图用“tea”代替“cake”一词。它首先必须用空格替换空终止符,如[13],以便将字符串恢复为原来的格式。
新字符用单词“tea”覆盖单词“cake”的“cak”。这是通过覆盖单个字符来完成的。“cake”的“e”被替换为新的空终止字符。结果是字符串实际上终止于两个空字符,即字符串末尾的原始字符,以及替换“cake”中的“e”的新字符。这在打印新字符串时没有区别,因为打印字符串的函数在遇到第一个空终止字符时将停止打印字符串字符。
4. 操作字符串数组的函数
上一个草图通过访问字符串中的单个字符,以手动方式操作字符串。为了更方便操作字符串数组,你可以编写自己的函数来执行,也可以使用 C语言库中的一些字符串函数。
下面显示了操作字符串数组的列表函数。
下一个草图使用了一些C字符串函数。
例子

void setup() {
   char str[] = "This is my string"; // create a string
   char out_str[40]; // output from string functions placed here
   int num; // general purpose integer
   Serial.begin(9600);

   // (1) print the string
   Serial.println(str);

   // (2) get the length of the string (excludes null terminator)
   num = strlen(str);
   Serial.print("String length is: ");
   Serial.println(num);

   // (3) get the length of the array (includes null terminator)
   num = sizeof(str); // sizeof() is not a C string function
   Serial.print("Size of the array: ");
   Serial.println(num);

   // (4) copy a string
   strcpy(out_str, str);
   Serial.println(out_str);

   // (5) add a string to the end of a string (append)
   strcat(out_str, " sketch.");
   Serial.println(out_str);
   num = strlen(out_str);
   Serial.print("String length is: ");
   Serial.println(num);
   num = sizeof(out_str);
   Serial.print("Size of the array out_str[]: ");
   Serial.println(num);
}

void loop() {

}

结果 This is my string
String length is: 17
Size of the array: 18
This is my string
This is my string sketch.
String length is: 25
Size of the array out_str[]: 40
以上草图按以下方式工作。
 (1)打印字符串 最新创建的字符串将打印到串口监视器窗口,如之前的草图所完成的。
 (2)获取字符串的长度 strlen()函数用于获取字符串的长度。字符串的长度仅对于可打印字符,不包括空终止符。
 该字符串包含17个字符,因此我们在串口监视器窗口中看到17个字符。
 (3)获取数组的长度 运算符sizeof()用于获取包含字符串的数组的长度。长度包括空终止符,因此长度比字符串的长度多1。
 sizeof()看起来像一个函数,但技术上是一个运算符。它不是C字符串库的一部分,但在草图中用于显示数组大小和字符串大小(或字符串长度)之间的差异。
 (4)复制字符串 strcpy()函数用于将str[]字符串复制到out_num[]数组。strcpy()函数将传递给它的第二个字符串复制到第一个字符串中。现在,字符串的副本存在于out_num[]数组中,但只占用了数组的18个元素,因此在数组中仍然有22个空闲的char元素。这些空闲元素在内存中的字符串的后面可以找到。
将字符串复制到数组中,以便我们在数组中有一些额外的空间用于草图的下一部分,即在字符串的末尾添加一个字符串。
 (5)将字符串附加到字符串(连接) 草图将一个字符串加到另一个字符串,这称为串联。这是使用strcat()函数完成的。strcat()函数将传递给它的第二个字符串放到传递给它的第一个字符串的末尾。
串联后,打印字符串的长度以显示新的字符串长度。然后打印数组的长度,以显示在40个元素长的数组中有一个25个字符长度的字符串。
请记住,25个字符长度的字符串实际上占用了数组的26个字符,因为还有空终止0。
5. 数组边界
使用字符串和数组时,在字符串或数组的边界内工作是非常重要的。在示例草图中,创建了一个长度为40个字符的数组,以分配可用于操作字符串的内存。
如果数组太小,而我们尝试复制比数组大的字符串,那么字符串将复制到超出数组的末尾。超出数组末尾的内存可能包含草图中使用的其他重要数据,然而它们将被字符串覆盖。如果超出字符串末尾的内存超出范围,则可能会导致草图崩溃或导致意外行为。

5.10 补充: Arduino 通信

已经定义了数百个通信协议来实现这种数据交换。每个协议可以分为两类:并行或串行。

5.10.1 并行通信

通过输入/输出端口在Arduino和外设之间进行并行连接是短距离(最多几米)的理想解决方案。然而,在其他情况下,当需要在两个设备之间建立较长距离的通信时,不可能使用并行连接。并行接口同时传输多个位。它们通常需要数据总线 - 通过八条,十六条或更多的线路进行传输。数据以1和0的巨大波形传输。

并行通信的优点和缺点 并行通信肯定有其优势。它比串行更快,更直接,相对容易实施。然而,它需要许多的输入/输出(I / O)端口和线路。如果你曾经把一个项目从一个基本的Arduino Uno移动到一个Mega,你就知道微处理器上的I/O线是很宝贵的,而且很少。因此,我们更喜欢串行通信,牺牲针脚空间的潜在速度。

5.10.2 串行通信模块

今天,大多数Arduino板都是用几种不同的串行通信系统作为标准设备。
使用哪个系统取决于以下因素:

  • 微控制器有多少个器件与数据交换?
  • 数据交换的速度有多快?
  • 这些设备之间的距离是多少?
  • 是否需要同时发送和接收数据?
  • 有关串行通信的最重要的事情之一是协议,应该严格遵守。它是一套规则,必须应用这些规则才能使设备正确地解释它们相互交换的数据。幸运的是,Arduino会自动处理这个问题,这样程序员/用户的工作就可以简化为简单的写(发送的数据)和读(接收的数据)。
串行通信类型

串行通信可以进一步分类为:

  • 同步 - 同步的设备使用相同的时钟,它们的时序彼此同步。
  • 异步 - 异步的设备具有各自的时钟,并由前一状态的输出触发。

很容易找出设备是否同步。如果给所有连接的设备提供相同的时钟,则它们是同步的。如果没有时钟线,它是异步的。
例如,UART(通用异步收发器)模块是异步的。
异步串行协议有一些内置的规则。这些规则只是有助于确保可靠且无误的数据传输的机制。这些避免外部时钟信号的机制是:

  • Synchronization bits 同步位
  • Data bits 数据位
  • Parity bits 奇偶校验位
  • Baud rate 波特率
  1. 同步位
    同步位是与每个数据包传输的两个或三个特殊位。它们是起始位和停止位。正如它们的名称,这些位分别标记数据包的开始和结束。
    起始位始终只有一个,但停止位的数量可以配置为一个或两个(尽管通常保持为1)。
    起始位始终由从1到0的空闲数据线指示,而停止位将通过将线保持在1处而转换回空闲状态。

  2. 数据位
    每个分组中的数据量可以设置为5到9位的任意大小。当然,标准数据大小是基本8位字节,但其他大小有它们的用途。7位数据包的效率可能比8位高,特别是如果你只是传输7位ASCII字符。

  3. 奇偶校验位
    用户可以选择是否应该有奇偶校验位,如果是,则奇偶校验应该是奇数还是偶数。如果数据位中的1的数目是偶数,则奇偶校验位为0。奇数的奇偶校验正好相反。

  4. 波特率
    术语波特率用于表示每秒传输的位数[bps]。注意,它指的是位,而不是字节。协议通常要求每个字节与几个控制位一起传输。这意味着串行数据流中的一个字节可以包括11位。例如,如果波特率为300bps,则每秒可以传输最大37字节和最小27字节。

  5. Arduino UART
    以下代码将使Arduino在启动时发送hello world。

void setup() {
   Serial.begin(9600); //set up serial library baud rate to 9600
   Serial.println("hello world"); //print hello world
}

void loop() {

}

将Arduino草图上传到Arduino后,打开Arduino IDE右上角的串口监视器搜索Search。
在串口监视器的顶部框中键入任意内容,然后按发送键或键盘上的enter键。这将发送一系列字节到Arduino。
以下代码返回它接收到的任何东西作为输入。
以下代码将使Arduino根据提供的输入传送输出。

void setup() {
   Serial.begin(9600); //set up serial library baud rate to 9600
}

void loop() {
   if(Serial.available()) //if number of bytes (characters) available for reading from { 
      serial port
      Serial.print("I received:"); //print I received
      Serial.write(Serial.read()); //send what you read
   }
}

请注意,Serial.print 和 Serial.println 将发回实际的ASCII代码,而 Serial.write 将返回实际的文本。请参阅ASCII代码了解更多信息。

5.10.2 print()语法:

• Serial.print(val)
• Serial.print(val, format)
参数:
• val: 要发送的数据(任何数据类型)
• format: 指定数字的基数(用于整型数)或者小数的位数(用于浮点数)。允许的值为:BIN (binary二进制), OCT (octal八进制), DEC (decimal十进制), HEX (hexadecimal十六进制)。对于浮点数,该参数指定小数点的位数。
返回值:
size_t (long): print()返回发送的字节数(可丢弃该返回值)。
说明

  1. 以人类可读的ASCII码形式向串口发送数据,该函数有多种格式。整数的每一数位将以ASCII码形式发送。浮点数同样以ASCII码形式发送,默认保留小数点后两位。字节型数据将以单个字符形式发送。字符和字符串会以其相应的形式发送。
  2. 你可以用F()把待发送的字符串包装到flash存储器。例如: Serial.print(F(“Hello World”))
  3. 要发送单个字节数据,请使用Serial.write()。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

蔚蓝慕

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值