Arduino:UNO板的接口和应用
目录
前置知识点:
一、数字接口
基本数字IO接口封装函数:
案例:按钮控制流水灯
高级数字IO接口封装函数:
二、模拟接口
模拟IO接口的封装函数:
三、串行通信接口
基础串行通信接口封装函数:
案例:串行控制全彩RGB灯颜色
IC2总线接口及应用
案例:简易聊天器
SPI通信接口及应用
SPI类库函数:
四、外部中断接口及应用
基础外部中断函数:
五、EEPROM概述
基础库函数:
前置知识点:
在TTL门电路中,把高于3.5V的电压规定为逻辑高电平,把小于0.3V的电压规定为逻辑低电平。
上拉电阻(电阻接+5V):可以将输入引脚的不确定状态,固定在高电平的确定状态。
下拉电阻(电阻接GND):可以将输入引脚的不确定状态,固定在高电平的确定状态。
模拟电路:时间上和幅度上都连续变化的信号。
数字电路:时间上和幅度上都断续变化的信号。
一、数字接口
Arduino Uno的数字引脚编号为0~13,数字引脚可设置为INPUT(默认状态)、OUTPUT、INPUT_PULLUP三种模式。INPUT_PULLUP模式利用了Arduino微控制器自带的内部上拉电阻。
基本数字IO接口封装函数:
函数 | 解释 |
---|---|
digitalRead(pin) | 读取引脚电平 |
digitalWrite(pin,value) | 设置引脚电平 |
pinMode(pin,model) | 设置引脚模式 |
value:有两种取值其中HIGH为高电平,LOW为低电平。
model:有三种取值分别为INPUT、OUTPUT、INPUT_PULLUP
案例:按钮控制流水灯
代码:
int key_pin = 7; //按键引脚
int led_first_pin = 2; //第一个LED引脚
int num = 3; //LED的个数
void setup() {pinMode(key_pin, INPUT);for (int i = led_first_pin; i < led_first_pin + num; i++) {pinMode(i, OUTPUT);}
}
void loop() {int val;for (int i = led_first_pin; i < led_first_pin + num; i++) {val = digitalRead(key_pin); //如果松开按键,立刻停止流水灯的进程if (val == 0) {break;}digitalWrite(i, HIGH);delay(500);}for (int i = led_first_pin; i < led_first_pin + num; i++) {val = digitalRead(key_pin); //如果松开按键,立刻停止流水灯的进程if (val == 0) {break;}digitalWrite(i, LOW);delay(500);}if (val == 0) { //当松开按键时,立刻熄灭所有LEDfor (int i = led_first_pin; i < led_first_pin + num; i++) {digitalWrite(i, LOW);}}
}
电路图:
连接图:
高级数字IO接口封装函数:
函数 | 解释 |
---|---|
pulseIn(pin,value,[timeout]) | 读取一个引脚脉冲的持续时间。 |
pulseInLong(pin,value,[timeout]) | |
shiftIn(dataPin,clockPin,bitOrder) | 串行读入数据一次一位,读入时钟引脚为高电平,读入后时钟引脚为低电平。bitOrder值有两种:MSBFIRST(高位先入),LSBFIRST(低位先入) |
shiftOut(dataPin,clockPin,bitOrder,value) | 串行输出数据一次一位,读入时钟引脚为高电平,读入后时钟引脚为低电平。bitOrder值有两种:MSBFIRST(高位先入),LSBFIRST(低位先入) |
tone(pin,frequency,duration) | 指定引脚输出占空比50%的方波,可以产生固定频率的PWM信号来驱动扬声器发声。 |
noTone(pin) | 停止发出声音的引脚。 |
二、模拟接口
Arduino Uno的模拟引脚编号为A0~A5,可以读取模拟信号,信号范围是0~5V,Arduino板支持A/D转化,可以将0~5V的电压转化成0~1023。数字引脚名称前标有~的引脚为支持PWM的输出引脚,这些引脚可以通过数字信号控制模拟信号。PWM方式指定电压是0~255,0是低,255是高,按占空比计算。占空比指的是在方形波的一个周期内,高电平信号持续时间占周期比。
模拟引脚可设置为INPUT(默认状态)、OUTPUT、INPUT_PULLUP三种模式。INPUT_PULLUP模式利用了Arduino微控制器自带的内部上拉电阻。
模拟IO接口的封装函数:
函数 | 解释 |
---|---|
analogReference(tyoe) | 配置模拟引脚的参考电压 |
analogRead(pin) | 读取并转化指定模拟引脚的电压 |
analogWrite(pin,value) | 通过PWM方式在指定引脚输出模拟量,调用该方法之前不需要调用pinMode()函数设置该引脚为输出。 |
三、串行通信接口
波特率指的是bit/s。串口是用于传输数据的接口,代码对象为Serial,Arduino Uno通过数字引脚0(RX)和1(TX)与计算机的USB接口进行通信。
串行通信接口及其应用:
函数 | 解释 |
---|---|
if(Serial) | 测试指定串口是否准备好。 |
Serial.available() | 读取从串口接受到的字节数(存在串口缓冲区中的字节数)。 |
Serial.availableForWrite() | 不阻塞写操作,读取写到串行缓冲区的字节数。 |
Serial.begin(speed,[config]) | 设置串口波特率。 |
Serial.end() | 禁止窗口通信。 |
Serial.find(target) | 在串口缓冲区查找字符串。 |
Serial.findUntil(target,terminal) | 在串口缓冲区查找起始串,读取到结束串为止。 |
Serial.flush() | 等待发送的串行数据传送完毕。 |
Serial.parseFloat() | 从串口缓存中读取第一个有效的浮点数。 |
Serial.parseInt() | 从串口缓存中读取第一个有效的整数。 |
Serial.peek() | 读取串口缓冲区中的第一个字节或字符,不对缓冲区内容修改,连续调用返回相同字符串。 |
Serial.print(val,[format]) | 发送字符串数据到串口,串口显示打印。 |
Serial.println(val,[format]) | 发送字符串数据到串口,串口显示打印,多了\n。 |
Serial.read() | 一次读取一个字符,读完后删除已读数据。 |
Serial.readBytes(buffer,length) | 读取输入的串口字符到缓冲区。 |
Serial.readBytesUntil(character,buffer,length) | 从串口缓冲区读数据到一个数组,读到结束字符为止,结束字符不返回。 |
Serial.readString() | 从串口缓冲区读取全部数据到字符串型变量中。 |
Serial.readStringUntil() | 从串口缓冲区读取数据到字符串型变量中,读到结束字符停止。 |
Serial.write() | 发送二进制数据到串口。 |
Serial.setTimeout() | 设置串口操作的超时时间。 |
Serial.serialEvent() | 串口数据准备好时,触发的事件函数。 |
具体解析
Serial.print(val,[format])/Serial.println(val,[format])
发送字符串到串口,数据会被自动换行成字符串,串口显示打印数据 ,在使用该函数之前需要指定波特率。
value(val):可以是字符串、整数、浮点数(默认保留2位小数)。
format:指明数字进制,BIN二进制,OCT八进制,DEC十进制,HEX十六进制。指明小数数位,0无小数,1保留一位小数。
Serial.write(val)/Serial.write(buf,len)
发送原始二进制到串口,数据为原始数据,串口显示打印数据 ,在使用该函数之前需要指定波特率。
buf:多个字节组成的数组。
len:数组长度。
value(val):可以是字符串、整数、浮点数(默认保留2位小数)。
案例:串行控制全彩RGB灯颜色
效果视频:
电路图:
连接图:
代码:
int i; //保存PWM需要输入的值
String Digital = ""; //保存数字
String LED=" "; //用于判断指定LED颜色对应的引脚
boolean stringComplete = false; //用于判断数据读取是否完成void setup()
{Serial.begin(9600);
}
void loop()
{while(Serial.available()){//读取新的字符char inChar = Serial.read();//如果是英文则存到变量LED中,如果是中文则存到inString中if(isDigit(inChar)){Digital+=inChar;}else if(inChar == '!'){stringComplete = true;Serial.println("Complete!");i= Digital.toInt();}else {LED=inChar;}}if (stringComplete){if(LED == "A"){analogWrite(9,i);}else if (LED == "B"){analogWrite(10,i);}else if (LED == "C"){analogWrite(11,i);}//清空数据,为下次读取做准备stringComplete =false;Digital= "";LED=""; }
}//使用串口事件
//读取并分离字母和数字
IC2总线接口及应用
I2C总线只有两根双向信号线,一根是数据线SDA(Uno板上是A4),另一根是时钟线SCL(Uno板上是A5),这两根总线均要通过上拉电阻器与正电源相连。当总线空闲时,两根信号线都是高电平。
注意:使用I2C通信需要引入Wire库。
主设备函数 | 解释 |
---|---|
Wire.begin([address]) | 初始化Wire库,将I2C设备作为主设备(未指定从地址)或从设备接入总线。 |
Wire.beginTransmission(address) | 启动一个已知地址的I2C从设备通信。 |
Wire.write(value,[length]) | 写数据到发送队列。length在发送数组时使用。 |
Wire.endTransmission() | 结束I2C从设备的数据发送,将发送队列的字符串发给从设备。 |
Wire.available() | 对主设备,在调用requestForm()后接收到缓冲区的字节数。 |
Wire.requestForm(address,quantity,stop) | 设置主设备的接收缓冲区大小,主设备要求从设备发送的字节数。 |
Wire.read() | 从接收缓冲区读取一个字节。 |
Wire.setClock(clockFrequency) | 修改I2C的通信时钟频率。最低100Hz。 |
Wire.onReceive(handler) | 当从设备接收来自主设备的数据时,注册一个处理函数。 handler 函数举例:void myhandler(int num)。其中num代表从主设备接收到的字节数。 |
Wire.onRequest(handler) | 当从设备接收来自主设备的发送数据请求时,注册一个处理函数。handler 函数举例:void myhandler() |
案例:简易聊天器
效果视频:
电路图
连接图
代码:
//主设备
#include<Wire.h>
String str="";//信息存储void setup() {Wire.begin();Serial.begin(9600);
}void loop() {String value="";while(Serial.available()){char x=Serial.read();value+=x;}if(value==""){receive();}else{send(value);}}
void receive(){String value="";int flag=0;Wire.requestFrom(8,200);while(Wire.available()){flag=1;char c=Wire.read();if(c=='!'){while(Wire.available()){c=Wire.read();}break;}value+=c;}if(value!=""){Serial.println(value);//Serial.print("Receive Success!");}delay(500);
}
void send(String value){Wire.beginTransmission(8);const char* p =value.c_str();Wire.write((char *)p);Wire.endTransmission();//Serial.print("Send Success!");delay(500);
}
//从设备
#include<Wire.h>void setup() {Wire.begin(8);Serial.begin(9600);Wire.onRequest(send);Wire.onReceive(receive);
}void loop() {delay(1000);
}
void receive(int howMany){int flag=0;while(Wire.available()){flag=1;char c=Wire.read();if(c=='!'){while(Wire.available()){c=Wire.read();}}else{Serial.print(c);}}if(flag){Serial.println("");}
}
void send(){String str="";//信息存储while(Serial.available()){char x=Serial.read();//if(x=='!'){//break;//}else{str+=x;//}}str+='!';const char* p =str.c_str();Wire.write((char*)p);delay(500);
}
SPI通信接口及应用
SPI是在物理上通过与微处理控制单元(MCU)连接的同步串行端口SSP模块来实现数据通信的,它允许MCU以全双工的同步串行方式与各种外围设备进行高速数据通信。
SPI主要应用在可擦除只读存储器(EEPROM)、闪存(Flash)、实时时钟(RTC)、模数转换器(ADC)、数字信号处理器(DSP)以及数模转换器(DAC)中。
SPI占芯片的四个引脚,分别为SCLK时钟信号线(用于同步数据传输)、MOSI主机发送从机接收数据线(用于主机向从机发送数据)、MISO主机接收从机发送数据线(用于从机向主机发送数据)、SS片选线(用于确定要进行通信的主机,低电平有效)。SPI还有最重要的两项设置分别为时钟极性和时钟相位,主从设备的时钟极性和时钟相位应该保持一致。
时钟极性(CPOL或UCCKPL):用于设置传输空闲时的电平。
时钟相位(CPHA或UCCKPH):用于设置读取数据和发送数据的时钟沿。
SPI针对这两项设置有四种模型,如下表所示。
SPI的4种模式 | ||||
---|---|---|---|---|
模式 | CPOL | CPHA | 数据发送 | 数据接收 |
SPI_MODE0 | 0 | 0 | 下降沿 | 上升沿 |
SPI_MODE1 | 0 | 1 | 上升沿 | 下降沿 |
SPI_MODE2 | 1 | 0 | 上升沿 | 下降沿 |
SPI_MODE3 | 1 | 1 | 下降沿 | 上升沿 |
作为主设备的Uno板SS引脚要设置成OUTPUT,要不然自动进入从模式。Uno板的各个SPI引脚分配如下:
MOSI | MISO | SCK(SCLK) | SS | Level |
---|---|---|---|---|
11 | 12 | 13 | 10 | 5V |
SPI类库函数:
函数 | 解释 |
---|---|
SPI.begin() | 初始化,设置SCK、MOSI和SS为输出,将SCK和MOSI拉为低电平,SS拉为高电平等。 |
SPI.end() | 完全关闭SPI接口,释放所占用的资源。 |
SPI.beginTransaction(mySettings) | 用被定义的SPISettings对象初始化 SPI总线 |
SPI.endTransaction() | 结束一个SPI事务 |
SPI.transfer()和SPI.transfer16() | 传输、接收和发送数据 |
SPI.usingInterrupt(InterruptNumber) | 注册中断号 |
具体解析
SPI.transfer()和SPI.transfer16()
SPI.transfer():用于传输单个字节或多个字节。
语法:byte receivedData = SPI.transfer(byte data);
语法:byte receivedData = SPI.transfer(byte[ ] buffer ,int size);
SPI.transfer16():用于传输16位的数据(两个字节)。
语法:uint16_t receivedData = SPI.transfer16(uint16_t data);
注意:一个字节占8比特。
uint16_t receivedData = SPI.transfer16(0x1234); // 发送 16 位数据 0x1234,并接收返回的 16 位数据
SPI.beginTransaction(mySettings)
SPISettings对象语法格式:
SPISettings mySetting(speedMaximum,dataOrder,dataMode);
参数说明:
speedMaximum:通信的最大速度,参考SPI芯片的频率。
dataOrder:MSBFIRST(高位先送)或LSBFIRST(低位先送)。
dataMode:SPI_MODE0、SPI_MODE1、SPI_MODE2、SPI_MODE3。
SPISettings mySPISettings(4000000, MSBFIRST, SPI_MODE0);
void setup() { SPI.begin(); // 启动SPI // 使用SPISettings SPI.beginTransaction(mySPISettings); // 发送数据 digitalWrite(SS_PIN, LOW); // 选择从设备 SPI.transfer(data); // 发送数据 digitalWrite(SS_PIN, HIGH); // 释放从设备 SPI.endTransaction(); // 结束SPI事务
}
四、外部中断接口及应用
中断服务程序ISRs所用的函数是CPU响应特定中断时强制执行的函数,它不能有参数和任何返回值,Adruino的外部中断引脚为2、3。
基础外部中断函数:
函数 | 解释 |
---|---|
attachInterrupt(digitalPinToInterrupt(pin),ISR,mode) | 设置一个外部中断 |
detachInterrupt(interrupt) | 关闭某个已启用的中断号 |
interrupts() | 开中断 |
noInterrupts() | 停止已经设置好的中断,使程序不受中断影响。 |
具体解释:
attachInterrupt(digitalPinToInterrupt(pin),ISR,mode)
digitalPinToInerrupt(pin):将实际的数字引脚转换为指定的中断号。
ISR:中断服务函数名。
mode:定义中断触发方式,定义如下。
LOW:当引脚为低电平时,触发中断。
CHANGE:当引脚变化时,触发中断。
RISING:当引脚产生低到高的跳变时,触发中断。
FALLING:当引脚产生高到低的跳变时,触发中断。
HIGH:当引脚为高电平时,触发中断。
五、EEPROM概述
Arduino板基于AVR的微控制器包含EEPROM,以字节为单位进行数据读写,掉电不会丢失数据,用来记录和保存参数。
基础库函数:
函数 | 解释 |
---|---|
EEPROM.read(address) | 从EEPROM的对应地址读取一个字节。 |
EEPROM.write(address,value) | 在指定的地址写入一个字节的数据。 |
EEPROM.update(address,value) | 在指定的地址更新一个字节的数据。 |
EEPROM.put(address,data) | 在指定的地址更新任意类型的数据或对象。 |
EEPROM.get(address,data) | 从EEPROM中读取任意类型的数据或对象。 |