开始学习Arduino,用一篇文章作为学习笔记😃
前言
本人当前学习过程基于Arduino社区教程贴,后续可能有其他渠道。部分图文引用自Arduino社区教程贴,侵删。
Arduino Uno 概览
玩单片机先点灯
可编程LED
知识点:数字信号
Arduino上每一个带有数字编号的引脚,都是数字引脚,包括写有“A”编号的模拟输入引脚。使用这些引脚(个人尝试,0号和1号引脚并不能用来点灯,TX和RX应为串行信号输入输出,不能直接点灯),可以完成输入输出数字信号的功能。
数字信号是以0、1表示的电平不连续变化的信号,也就是以二进制的形式表示的信号。 在Arduino中数字信号通过高低电平来表示,高电平则为数字信号1,低电平则为数字信号0 。
知识点:输入输出
在使用输入或输出功能前,需要先通过pinMode() 函数配置引脚的模式为输入模式或输出模式。
1 | pinMode(pin,mode); |
pin为指定配置的引脚编号,mode为指定的配置模式:INPUT输入,OUTPUT输出,INPUT_PULLUP输入上拉。
配置成输出模式后,还需要使用digitalWrite() 让其输出高电平或者是低电平。其调用形式为:
1 | digitalWrite(pin,value) |
参数pin为指定输出的引脚编号;参数value为你要指定输出的电平,使用HIGH指定输出高电平,或是使用LOW指定输出低电平。
Arduino中输出的低电平为0V,输出的高电平为当前Arduino的工作电压。例如Arduino UNO的工作电压为5V,其高电平输出也是5V;Arduino Due工作电压为3.3V,所以高电平输出也就是3.3V。
数字引脚除了用于输出信号外,还可以用digitalRead() 函数读取外部输入的数字信号,其调用形式为:
1 | int value = digitalRead(pin); |
参数pin为指定读取状态的引脚编号;返回值value为获取到的信号状态,1为高电平,0为低电平。
Arduino UNO会将大于3V的输入电压视为高电平识别,小于1.5V的电压视为低电平识别。所以,即使输入电压不太准确,Arduino UNO也可以正常识别。需要注意的是,超过5V的输入电压可能会损坏Arduino UNO。(不同型号的Arduino有所不同)
在Arduino核心库中,OUTPUT被定义等于1,INPUT被定义等于0,HIGH被定义等于1,LOW被定义等于0。因此这里也可以用数字替代这些定义。如:
1 | pinMode(13,1); |
但不推荐这样写代码,因为这样会降低程序的可读性。
按键点灯
原理图:
其中两个电阻的作用不同。
知识点:限流电阻
一般LED的最大能承受的电流为25mA,如若直接将LED连接到电路中,当其点亮时,如果电流过大,很容易烧毁。如图2-24所示,我们在LED一端串联了一个电阻R2,这样做可以控制流过LED的电流,防止损坏LED。这个电阻我们称之为限流电阻。
知识点:下拉电阻/上拉电阻
在Arduino的2号引脚到GND之前,连接了一个阻值10K的电阻。如果没有该电阻,当未按下按键时,2号引脚会一直处于悬空状态,此时使用digitalRead() 读取2号引脚状态,会得到一个不稳定的值(可能是高,也可能是低)。添加这个R1电阻到地就是为了稳定引脚的电平,当引脚悬空时,就会识别为低电平。而这种将某节点通过电阻接地的做法,叫做下拉,这个电阻叫做下拉电阻。
个人理解,在引脚悬空时,引脚在高低电平间来回切换,没有稳定状态,使用一个电阻将其接地,拉低引脚上的电平信号即为下拉电阻。反之,若使用电阻将其接到电源线上将电平信号拉高则为上拉电阻。
进阶按键点灯(使用内部上拉)
原理图:
注意,此处按钮不接5V线,因为内部上拉就是接5V电源线上拉,所以不需要外部接5V线,与上面不同。
呼吸灯
知识点:模拟信号
生活中,接触到的大多数信号都是模拟信号,如声音、温度的变化等。模拟信号是用连续变化的物理量表示的信息,信号随时间作连续变化。在Arduino UNO上,可以接受0~5V的模拟信号。
模拟输入引脚是带有ADC(Analog-to-Digital Converter,模数转换器)功能的引脚。它可以将外部输入的模拟信号转换为芯片运算时可以识别的数字信号,从而实现读入模拟值的功能。
Arduino 模拟输入功能有10位精度,即可以将0~5V的电压信号转换为0~1023的整数形式表示。
模拟输入功能需要使用analogRead() 函数。
1 | int value = analogRead(pin) |
参数pin是指定要读取模拟值的引脚,被指定的引脚必须是模拟输入引脚。如analogRead(A0)即是读取A0引脚上的模拟值。
与模拟输入功能对应的是模拟输出功能,我们使用analogWrite() 函数实现这个功能。但该函数并不是输出真正意义上的模拟值,而是以一种特殊的方式来达到输出近似模拟值的效果,这种方式叫做脉冲宽度调制(PWM,Pulse Width Modulation)。在Arduino UNO中,提供PWM功能的引脚为3、5、6、9、10、11。
当使用analogWrite() 函数时,指定引脚会通过高低电平的不断转换输出一个周期固定的方波,通过改变高低电平在每个周期中所占的比例(占空比),而得到近似输出不同的电压的效果。需要注意的是,这里仅仅是得到了近似模拟值输出的效果,如果要输出真正的模拟值,还需要加上外围滤波电路。
1 | analogWrite(pin,value) |
参数pin是指定要输出PWM波的引脚,参数value指定是PWM的脉冲宽度,范围为0~255。
在analogWrite() 和analogRead() 函数内部,已经完成了引脚的初始化,因此不用在Setup() 函数中进行初始化操作。
编写类库
实际上就是编写C语言类模板,.h头文件声明类模板,.cpp文件函数实现。需要额外添加一个keywords.txt文件用于让Ardunio IDE识别并高亮代码。如:“SR04 KEYWORD1"和"GetDistance KEYWORD2"。KEYWORD1为数据类型高亮,KEYWORD2为函数高亮。
将数据存储在Flash中
关键字PROGMEM
是一个变量修饰符,使用该修饰符可实现将变量存储在Flash空间内,而不占用RAM空间。
使用方法如下:
1 | const 数据类型 变量名[] PROGMEM = {data0, data1, data3…}; |
需要注意的是,通常使用PROGMEM的变量,需要是全局变量。如果在局部变量中使用,需要添加static关键字,将变量定义为静态变量,方法如下:
1 | const static 数据类型 变量名[] PROGMEM = {data0, data1, data3…}; |
对于一些需要输出的长文本字符串,可以使用Arduino提供的F()
宏函数,以达到和使用PROGMEM
一样的效果。如:
将
1 | Serial.print("Early to bed early to rise makes a man healthy wealthy and wise"); |
可修改为:
1 | Serial.print(F("Early to bed early to rise makes a man healthy wealthy and wise")); |
即可将变量存储到Flash中,以节省RAM空间。
Arduino程序兼容性
不同型号的Arduino开发板可能使用了不同的核心控制器,如Arduino UNO使用的核心控制器为8位AVR架构的Atmega328P,而Arduino Due使用的是32位ARM Cortex m4架构的 SAMD21G18A。两者主要区别如下:
工作电压不一样。AVR核心的arduino通常工作电压为5V,ARM核心的arduino通常工作电压为3.3V。因此在接入外部设备时,可能需要转换通信电平,以兼容不同的工作电压的外设。
在软件层面,不同架构的芯片对应的编译环境,可能相同数据类型的数据长度不一样。如在Arduino UNO、MEGA中,int及unsigned int占用2字节(16位);而在Arduino Due、Zero中,int及unsigned int占用4字节(32位)。
所以在编写和移植时程序时,如果直接复用代码,可能会造成一些错误。
这种情况下,可以使用宏判断来判断当前编译的目标开发板上的核心MCU型号,进而选择编译对应的语句,方式如下:
1 | #ifdef(__arm__) |
可以通过以下宏判断芯片类型:
芯片类型 | 对应的宏 |
---|---|
AVR | AVR |
ARM | arm |
可以通过以下宏判断开发板型号:
开发板型号 | 对应的宏 |
---|---|
Arduino UNO | AVR_ATmega328P |
Arduino MEGA | AVR_ATmega2560 |
Arduino Leonardo | AVR_ATmega32U4 |
Arduino Zero | SAMD21G18A |
Arduino Due | SAM3X8E |
兼容数据长度不一样的情况,可以使用intx_t指定长度方式定义变量,如int8_t即是长度为8位的整形。使用方式和int一样:
1 | int8_t a = 123; |
常用类型如下:
类型 | 取值范围 | 长度(bit) |
---|---|---|
int8_t | -128 ~ 127 ( -27 ~ 27 - 1) |
8 |
uint8_t | 0 ~ 255 ( 0 ~ 28 - 1) |
8 |
int16_t | -32,768 ~ 32,767 ( -215 ~ 215 - 1) |
16 |
uint16_t | 0 ~ 65,535 ( 0 ~ 216 - 1) |
16 |
int32_t | -2,147,483,648 ~ 2,147,483,647 ( -231 ~ 231 - 1) |
32 |
uint32_t | 0 ~ 4,294,967,295 ( 0 ~ 232 - 1) |
32 |
Arduino上传机制
在Arduino核心MCU的Flash存储上,通常分有两个区域——应用程序区(Application)和引导程序区(Boot)。
应用程序区存放着用户应用程序,即开发者通过IDE编写的代码编译生成的程序,其功能由开发者定义和实现;
引导程序区,通常存放有一段启动加载程序(bootloader),该程序可以将PC发送来的应用程序,存储到应用程序区中。
Arduino每次启动后,都会先运行bootloader程序,如果PC没有向Arduino传输新的应用程序,Arduino则会很快开始运行现有的用户应用程序。
因此要更新Arduino上的程序,就要先将Arduino复位,让其运行bootloader,以备接收新上传的应用程序。
在Arduino UNO/Mega上串口芯片DTR引脚和AVR RST引脚间串联了一个100nf的电容,Arduino IDE在上传程序前,会通过发送串口DTR脉冲让Arduino开发板复位,进而让Arduino运行bootloader。然后Arduino IDE再按照规定的协议传输应用程序,即可在bootloader的协助下,将应用程序写入到应用程序区中。