嵌入式 Arduino 期末复习

1 基础知识

1.1 概述

对嵌入式的定义

  • 国内定义:以应用为中心,以计算机技术为基础,软件硬件可裁剪,且适应系统对功能,可靠性,成本,体积,功耗严格要求的专用计算机系统。
  • IEEE定义:用于控制,监视或者辅助操作机器和设备的装置。

分类

以下按照形态差异分类:

类名 板型号
芯片级 MCU,SoC
板级 单片机,模块
设备级 工控机

其中,单片机又分为:

  • 不含操作系统(我们学习的arduino不包含操作系统)
  • 含操作系统

组成

嵌入式系统四大组成部分

  • 微处理器
    核心部件,微处理器强弱直接影响嵌入式设备的复杂度和范围。
  • 存储器
    对速度和功耗要求高
  • 输入/输出设备(I/O)
    ①人机交互②传感器设备③输入设备
  • 通信和扩展接口
    嵌入式系统与其他系统进行数据交换和扩展。

1.2 常用元件

名称 相关单位 作用 分类
电阻器(Resistor) 欧姆 Ω(R) /误差/额定功率等 欧姆定律:I=U/R 限流,分压电路 可调电阻器,热敏电阻器,光敏电阻器,压敏电阻器
电容器(Capacitor) 法拉 F(C) (电荷量和电压比值) 储能元件,充放电特性和阻止直流电流,允许交流电 滤波电容器,积分电容器,微分电容器
电感器(Inductor) 亨利 H(L)\(X_L(感抗)=2\pi f(交流电频率)L\) 对突变电流有阻碍作用 。滤波,震荡,延迟, 筛选信号,过滤噪声,稳定电流,抑制电磁波干扰
二极管(Diode) 1V 10mA 标记为D ①正向导通,逆向截止 ②整流,稳压,恒流,开关,发光,光电转换等电路中 发光二极管(LED):长脚阴极短脚阳极。一般工作电压在1V,导电电流在10mA,所以需要一个电阻分压限流\(R=(E-U_E)/I_F\)
(半导体)晶体管 标记为Q 把微弱的电信号放大成幅度值较大的电信号(用作无触点开关) ①NPN型:基极B和发射极E之间施加正向电压,发射极和集电极导通②PNP型:基级和发射极施加反向电压,晶体管导通
万用表 V,A,Ω,β(半导体参数),F 测量直流电流,电压,交流电流,电压,电阻,还可以测量电容量,电感量,半导体参数 数字万用表分为四个测量插孔和红黑表笔各一个
杜邦线 null 扩展实验板引脚,增加实验项目,无焊接进行电路实验 杜邦线有两头,有插针插孔两类,可以两头都是,也可一个一半
面包板 null 电路的组装,调试和训练 一般五个一组,分配在同一金属片中
Arduino扩展板 null 简化电路搭建过程,快速搭建项目 多种,不列举
模块 null 无需了解模块原理,只需按照要求连接,即可快速制作硬件 对于一些简单的模块只有三个信号线:①V/VCC电源②G/GND地线③S信号引脚。例如UART蓝牙模块等

2 Arduino软硬件开发基础

2.1 开发板

2.1.1 Arduino UNO简介

如图所示为一个Arduino UNO板
image
拥有:

  • 14个输入输出引脚(0~13),其中(3,5,6,9,10,11)可以作为PWM输出
  • 6个模拟输入(A0~A5)
  • 1个UART硬件串口
  • 1个电源插孔,1个ICSP插座和1个复位按钮
  • 1个USB接口,与计算机连接,可以供电和通信

引脚名称和功能

电源引脚

image

名称 作用
VIN ①外部电流供电时的输入电压引脚 ②电源插座供电时提供5V电压
5V 输出5V电压 使用该引脚和3.3V引脚可能损坏旁路线性稳压器
3V 输出3.3V电压
GND 接地
AREF 模拟输入的参考电压
RESET 扩展一个按钮实现复位功能。引脚为低电平
输入输出

image

名称 引脚位 作用
串口 0(RX),1(TX) 接收(RX)和发送(TX)TTL串行数据。
外部中断 2,3 触发中断的引脚。触发条件分为:①低电平触发 ②上升沿触发 ③下降沿触发 ④发生变化触发
PWM 3,5,6,9,10,11(~线) 6路八位PWM
SPI 10(SS),11(MOSI),12(MISO),13(SCK) 支持SPI通信
LED 13 高电平LED亮,低电平LED灭
TWI A4/SDA,A5/SCL TWI通信

3 Arduino编程

3.1 函数

时间函数

函数 参数 用法 备注
delay(ms) ms为unsigned long 延长ms毫秒 delay更多用于较短延时,但是有以下缺点:①延时中无法读取传感器的值等,阻止了大部分操作
delayMicroseconds(μs) μs为 unsigned int 延长微秒 更长时间用delay
micros() time=micros() 返回以微秒为单位的运行时间(unsigned long) 计时周期为70min,对于UNO(晶体振荡器为16MHz),返回值为4μs的倍数
millis() time=millis() 返回毫秒运行时间(unsigned long) 计时周期为50天

数学函数

函数 参数 用法
abs(x) x为int家族 绝对值
constrain(x,a,b) x归一化数据,a下限,b上限 任意数据类型 对于x,小于a返回a,小于b返回b ,中间返回x
map(x,fromLow,fromHigh,toLow,toHigh) 只能用于整数 (映射数,现在下限,现在上限,映射后下限,映射后上限) 将整数从一个范围映射到另外一个范围,与上着规则基本一致
max(x,y) 任意类型 返回两个数较大者(可用作限制下限)
min(x,y) 同上 返回最小(控制上限)
pow(base,exponent) float 幂的运算
sqrt(X) 任意 求平方根
sq() 任意实数 求平方
cos/sin/tan() 弧度角 角余弦值/正弦值/正切

字符函数

十三个,较简单,看懂英语也能知其用法:

  • isAlpha 是否为字母
  • isAlphaNumeric 是否为字母或数字
  • isAscii 是否为ASCII码
  • isControl 是否为控制符
  • isDigit 是否为数字
  • isGraph 一个非空字符是否可输出/可打印
  • isHexadecimalDigit 是否为16进制数
  • isLowerCase 是否为小写字母
  • isPrintable 任意字符是否可输出打印
  • isPunct 是否为标点符号
  • isSpace 是否为空格符
  • isUpperCase 是否大写
  • isWhitespace 是否为" ","\f","\n","\r","\t","\v"的任意一种

以上都是若条件成立返回真否则为假

随机函数

random(min,max)

  • min 下限
  • max 可选 上限

返回[min,max-1]之间的随机数(long)

每次执行程序结果相同,如需不同需要以下方法

randomSeed(seed)

  • seed 种子值

这两者都是伪随机,可以逆向分析

位和字节函数

函数 参数 作用
bit(n) n为指定位 返回位的权值(bit 0是1,1是2,2是4)
bitClear(x,n) x 数字变量,n 要读取的位 返回指定位的值
bitSet(x,n) x 数字变量 n 指定置1的位 某一位置0
bitRead(x,n) x 变量 n 指定读取位 读取指定位的值
bitWrite(x,n,b) x 变量 ,n 要赋值的位,b要赋的值 给变量指定位赋值
highByte/lowByte(x) x
变量 提取一个字高字节/低字节

3.2 常量表示

进制前缀

  • 十进制:无
  • 二进制 前缀B
  • 八进制 前缀 0
  • 十六进制 前缀0x

3.3 程序结构

一个Arduino程序分为两个函数

  • setup()
    程序开始运行时调用该函数,可用于变量初始化等等,只会运行一次

  • loop()
    执行完setup会执行看、loop,loop语句始终按照顺序循环执行。

3.4 位运算

  • &按位与
0 0 1 1 
0 1 0 1
--------&
0 0 0 1
  • |按位或
0 0 1 1
0 1 0 1
---------|
0 1 1 1
  • ~ 按位取反

  • ^按位异或

0 0 1 1
0 1 0 1
---------^
0 1 1 0
  • << 左移
    操作符左移

  • >>右移

4 第四章

4.1 数字接口

  • 数字引脚
  • 模拟引脚
  • 通信引脚
  • 外部中断引脚
  • 电源引脚
  • 。。。

I/O接口封装函数

基本封装函数有三个

函数 参数 作用
digitalRead(pin) 读取的引脚编号pin 读取引脚状态
digitalWrite(pin,value) pin同上,value:HIGH/LOW(高引脚低引脚) HIGH为5Vor3.3V,LOW为0V 设置引脚的高低电平
pinMode(pin,mode) mode有INPUT,OUTPUT,INPUT_PULLUP 指定引脚输出模式
  • 设置引脚 为输出(OUTPUT)模式,此时引脚为低阻抗状态,可以向其他电路原件提供电流(通常为40mA以内)

  • 设置引脚为输入(INPUT)模式,,此时引脚为高阻抗状态,此时该引脚可用于读取信号。

  • 设置引脚为输入上拉(INPUT_PULLUP)模式.首先Arduino内部自带上拉电阻。当我们需要使用该内部上拉电阻,可以通过pinMode()将引脚设置为输入上拉(INPUT_PULLUP)模式。当你将一个引脚设置为INPUT_PULLUP模式时,如果没有输入信号,这个引脚的状态将会是HIGH(高电平)。只有当这个引脚被接地(即连接到GND)时,它的状态才会变为LOW(低电平)。这种模式通常用于接按钮或开关。例如,你可以将一个按钮的一端接到interruptPin,另一端接到GND。当按钮没有被按下时,interruptPin的状态是HIGH。当按钮被按下时,interruptPin会被接地,因此它的状态变为LOW。使用内部上拉电阻的好处是,你不需要在你的电路中添加额外的上拉电阻。这可以简化你的电路设计,并减少硬件成本。

4.2 模拟接口的应用

在Arduino中有A0~A5,6个模拟接口,可以通过A/D转换器(10个),将0V~5V的电压转换为0~1023的值。
在板子上有~的接口,可以实现D/A转换。

模拟接口三个封装函数

函数 参数 作用
analogRead(pin) 0~5的接口 模拟读,返回一个转换值(0~1023)
analogReference(type) 选项:①DEFAULT:默认5V/3.3V电压 ②INTERNAL:片内参考电压,1.1V/2.56V ③INTERNAL1V1:片内1.1V参考电压 ④INTERNAL2V56 ⑤EXTERNAL:通过AREF引脚获取电压 配置模拟引脚的参考电压
analogWrite(pin,value) value:0~255之间int型。 设置引脚输出模拟量,用于改变灯亮度或者电机转速等。

注意:

  • 针脚悬空时,analogRead()返回值受到环境的多重影响。
  • AREF引脚若电压在0~5V范围外,在调用analogRead()前必须设置EXTERNAL
  • 调用analogWrite()之前,不需要调用pinMode()函数设置该引脚为输出,analogWrite()和read()没有关系

4.3 串行接口的应用

在Arduino上至少有一个串口(UART/USART),通过RX(0)和TX(1)和计算机USB进行串口通信。

串口通信指:将接收的来自CPU的并行数据转换为连续的串行数据流发送出去,或将接收的串行数据转换为并行数据转发给CPU。

串口的库函数

列举几个在实验中比较常见的函数

  • if(Serial) 串口是否准备好

  • available() 读取串口收到的字节数
    Serial.available()

  • begin() 设置串行通信波特率
    begin(speed,config)
    speed 设置波特率
    config 设置数据位数.默认是,8数据位,无奇偶位,1个停止位。
    Serial.begin(19200)

  • end(结束通信) RX,TX可以作为普通引脚使用

  • find()/findUntil() 从串口缓冲区读取已知长度的目标数据/读取到结束串停止

  • print() 将数据发送到串口显示,一个数字按照一个ASCII字符显示。
    print(val,format) format指定数据显示几位
    相似函数还有println,加一个换行符

  • read() 读字符
    还有readBytes/readBytesUntil/readString/热爱的StringUntil

  • setTimeout() 设置串口超时时间,默认1000ms

  • write()写二进制数据到串口

  • serialEvent()串口数据准备好时触发的事件函数

4.4 I2C总线接口(半双工)(考的简单)

内部集成电路(Inter-Integrated Circuit ,I2C),是具有多主机系统所需的包括总线仲裁和高低速器件同步功能的高性能总线。

两根信号线:数据线SDA,和时钟线SCL。

I2C类库函数

  • begin(address)
    初始化wire库,将I2C设备作为主设备或从设备加入I2C总线,只调用一次。默认为主设备

  • beginTransmission(address)
    启动一个已知地址的I2C从设备的通信

  • write()
    写数据到设备

  • endTransmission()
    结束一个begintransmission()。所以一次数据的传输,需要三个函数连续使用。

  • requestFrom()
    设置从设备向主设备发送的字节数。
    Wire.requestFrom(设备地址,请求字节数,stop[请求后发送停止信息,可选])

  • available()
    调用requestFrom()返回接收的字节数,

  • read()
    接收任意方向的一个字节数据

  • setClock()
    修改底层时钟频率,底线是100KHZ

  • onReceive()
    onReceive(hander)
    当从设备接收主设备的数据时,调用hander函数

  • onRequest()
    当从主设备接收设备的数据时,调用一个hander函数

4.5 SPI接口(全双工)

串行外设接口(Serial Peripheral Interface,SPI),物理上时通过微处理单元(MCU)的同步串行端口(Synchronous Serial Port,SSP)模拟实现数据通信,允许MCU以全双工的同步串行模式进行通信

在芯片只占用四个引脚

引脚名 作用
SCLK 串行时钟,用来同步数据传输,由主机输出
MOSI 主机输出从机输入数据线
MISO 主机输入从机输出数据线
SS 片选线,低电平有效,由主机输出
SS可以实现一条总线多个SPI,所以SPI总线可以出现多个从机,但是只能出现一个主机

4.6 外部中断接口(重要)(模块)

这里中断同操作系统的中断。外部内部发生事件,CPU暂停当前工作,去完成当前发生的事件,之后CPU继续刚刚被终止地方,继续当前工作,这样的过程称为中断。

  • 中断源:产生中断请求的源

  • 中断服务程序(ISRs),处理中断的程序
    有一定限制,不能有参数和返回值等,运行时间尽量短,同一时刻只有一个ISRs运行,delay不能用但是delayMicroseconds可以使用。

  • 中断优先级

  • 中断嵌套

在UNO中,用于中断引脚为 ,2,3

外部中断函数 四个

①attachInterrupt()

推荐语法:
attachInterrupt(digitalPinToInterrupt(pin),ISR,mode)

参数说明:

  • pin:引脚号
  • ISR:调用的ISRs函数(中断服务程序函数名)不能有参数和返回值
  • mode:触发中断方式
mode 定义 含义
LOW 引脚低电平
HIGH 引脚高电平 (不适用UNO)
CHANGE 引脚变化
RISING 引脚从低到高跳变
FALLING 引脚从高到底跳变

我们在使用时建议使用digitalPinToInterrupt(pin)函数进行转换,因为不同开发版的映射不同

在UNO中,中断号和引脚关系为:

INT.0=2
INT.1=3

② detachInterrupt()

关闭某个已启用的中断
detachInterrupt(interrupt)
参数:
interrupt,关闭中断号

③ interrupts()

开启中断
无参数

④ noInterrupts()

停止已经设置好的中断,使程序不受中断影响
无参数

tips:多说一嘴,可以使用开关中断实现原子级的函数,例如对时间敏感的函数(参考操作系统)

外部中断例子

例一:实现开关中断

void setup() {}
void loop()
{
	noInterrupts();
	
	///原子/时间敏感函数
	interrupts();
}

例二:实现LED不断闪烁

const byte ledPin=13;
const byte interruptPin=2;
volatile byte state=LOW; //volatile是每次读都会从内存读新值而不是寄存器
void setup()
{
	pinMode(ledPin,OUTPUT);
	pinMode(interruptPin,INPUT_PULLUP);//外置一个按钮
	attachInterrupt(digitalPinToInterrupt(interruptPin),blink,CHANGE);
	
}
void loop()
{
	digitalWrite(ledPin,state);
}
void blink()
{
	state=!state;
}

4.7 软件串口(重要)

作用:硬件串口为0,1。但是为了解决日常大量使用0,1接口,增加的软件串口。若使用软件串口,只有一个可以同时接收数据。

函数

定义一个数字引脚
SoftwareSerial mySerial(RX,TX)

  • begin()

  • avaliable()

  • read()

  • write()

同硬件串口

  • isListening() 是否为检测状态

  • listen() 使串口处于检测状态,同时只有一个串口处于检测状态

  • overflow()测试软件串口是否溢出,并清除溢出标志

  • peek() 返回软件串口RX接收字符,再次调用返回相同字符

  • print和println

4.8 EEPROM

断电不丢失数据
特点:以字节为单位进行数据读写

考点:
多字节进行数据读写,使用①数组 ②共用体

EEPROM类库函数

  • read(address) 读一个字节

  • write(address,val) 写一个字节

  • update(address.val) 更新一个字节

  • put(address,data) 更新任意数据或者对象
    此函数替代write可以减少擦写次数,减少寿命

  • get(address,data) 读取任意类型的数据或者对象

实现多字节读取函数


#include <EEPROM.h>

// 定义一个共用体,用于存储不同类型的数据
union DataUnion {
  int intValue;
  float floatValue;
  char charArray[10]; // 假设我们需要的最大字符串为9个字符加上终止符'\0'
};

// 函数:从EEPROM中读取多个字节到共用体
void readFromEEPROM(int startAddress, DataUnion *data, size_t dataSize) {
  for (size_t i = 0; i < dataSize; i++) {
    // 逐字节读取数据
    ((char*)data)[i] = EEPROM.read(startAddress + i);
  }
}

void setup() {
  Serial.begin(9600);
  DataUnion data;

  // 假设我们要读取的数据是一个整数,存储在地址0开始的位置
  readFromEEPROM(0, &data, sizeof(data.intValue));
  Serial.println(data.intValue);

  // 假设我们要读取的数据是一个浮点数,存储在地址4开始的位置
  readFromEEPROM(4, &data, sizeof(data.floatValue));
  Serial.println(data.floatValue);

  // 假设我们要读取的数据是一个字符数组,存储在地址8开始的位置
  readFromEEPROM(8, &data, sizeof(data.charArray));
  Serial.println(data.charArray);
}

void loop() {
  // 不需要循环操作
}

应用

实例1:从A0读取模拟量的值,存入到EEPROM。


#include <EEPROM.h>
int addr = 0;                  	//地址初始化
void setup() {
  }
void loop() {
  int val = analogRead(0) / 4;   //模拟量除以4,10位换算为8位
  EEPROM.write(addr, val);  	     //按地址写入变量值
  addr = addr + 1;
  if (addr == EEPROM.length()) 
	addr = 0;
  delay(100);
}

实例2:读EEPROM的内容并送串口监视器显示。

#include <EEPROM.h>
int address = 0;		//从地址0开始读
byte value;
void setup() {
  Serial.begin(9600); 	//初始化串口,等待串口连接
  while (!Serial) {;}		//等待串口连接
}
void loop() {
  value = EEPROM.read(address); //读一个字节到value
  Serial.print(address);
  Serial.print("\t");
  Serial.println(value, DEC);
  address = address + 1;
  if (address == EEPROM.length()) 
         address = 0;
  delay(500);  }  			//程序运行结果

第五章 人机界面和接口

5.1 Arduino与按键的接口技术

按键分为:编码按键和非编码两种

编码按键特点:

  • 使用方便
  • 接口简单
  • 响应速度快
  • 需要专用电脑

非编码按键特点:

  • 没有编码速度快
  • 不需要专用硬件

独立按键接口

image

常用方法是,每个按键接一个I/O口。斜接线,一端接地,另一端通过电阻接到上拉电阻器接口上。没有键按下保持高电平,有键按下就是低电平

按键接口控制方式

  • 随机方式:Arduino空闲执行按键扫描
  • 中断方式:按键按下产生中断请求,中断响应后执行按键请求
  • 定时方式:每隔一定时间执行扫描程序,由Arduino定时器完成

按键在机械按下会产生抖动,必须进行消抖:软件消抖和硬件消抖,硬件使用不多。
image

软件消抖的原理是先延迟20ms再进行按键检测。

实例功能:独立按键软件消抖。
int k1 = 5, k2 = 6, k3 = 7, k4 = 8;
int key = 0;		     //键值
int key1 = 0;		    //判断按键是否释放标志
void setup(){
 Serial.begin(9600);
 pinMode(k1, INPUT); 			
 pinMode(k2, INPUT);
 pinMode(k3, INPUT);
 pinMode(k4, INPUT);  }
void loop(){		//查询有无键按下,有键按下在屏幕显示结果
    read_key(); 		//读取按键
if (key != 0 ) {		//显示是哪个键按下
Serial.print("K");
Serial.print(key);0
Serial.println(" is pressed");
key = 0; }
}
void read_key() { 		//读取按键值并消抖函数
  if (!digitalRead(k1) || !digitalRead(k2) || !digitalRead(k3) || !digitalRead(k4))
  {
    delay (20); 			//消抖动延时
  if (!digitalRead(k1) || !digitalRead(k2) || !digitalRead(k3) || !digitalRead(k4))
  {
  if (!digitalRead(k1)) key = 1;	//键值输出
  if (!digitalRead(k2)) key = 2;
  if (!digitalRead(k3)) key = 3;
  if (!digitalRead(k4)) key = 4;  }
else key = 0;
  }
if (key1 != key) 		    	//判断按键是否释放
    key1 = key;
else
key = 0;                                 	//没有键按下,返回0
}

矩阵按键接口

image

当按钮过多,一个按钮占用一个I/O接口显然不合理,所以在接口多采用矩阵列式,节省资源。矩阵按键同样要考虑按键触点闭合和断开时存在的抖动期。常用有扫描法和反转法。

扫描法

设行线(或列线)为输出,列线(或行线)为输入,依次将行线设置为低电平,同时读入列线的状态,如果列线的状态出现非全1状态,这时0状态的行、列交点的键就是所按下的键。扫描法的特点是逐行(或逐列)扫描查询。

int ko[4] = {5, 6, 7, 8};  		//定义行
int ki[4] = {9, 10, 11, 12}; 		//定义列
int key = 0;          		//键值
int key1 = 0;        		//判断按键是否释放
void setup() {
  Serial.begin(9600);
  for (int i = 0; i < 4; i++) {
    pinMode(ko[i], OUTPUT);  	//行输出
    pinMode(ki[i], INPUT_PULLUP); //列输入,带内部上拉电阻
  }
}
void loop(){
  read_key();  			//调用读键值函数
  if (key != 0 )  {
    Serial.print("K");
    Serial.print(key);
    Serial.println(" is pressed");
    key = 0;
  }
}
void read_key() {
  for ( int l = 0; l < 4; l++)  	// 首先把行线全部输出高电平
    digitalWrite(ko[l], HIGH);
  for ( int i = 0; i < 4; i++) {
    digitalWrite(ko[i], LOW);    	//逐行输出低电平
    delay(5);			//等待稳定
    for (int k = 0; k < 4; k++) { 	//逐列读入
      if (!digitalRead(ki[k])) {   	//低电平表示有键按下
        delay(20);  			//延时消抖动
        if (!digitalRead(ki[k])) { 	//再次读入,低电平确认有键按下
          key =  i * 4 + k + 1; 	//计算键值,行×4+列,键值1~16
        }
        else   key = 0;
      }
    }
  }
  if (key1 != key)  		//判断按键是否释放
    key1 = key;
  else
    key = 0;
}

对于read_key的解释:

  • 初始化所有行为高电平:这一步是准备步骤,确保在开始扫描之前,所有行都不会触发任何列。如果某些行被设为低电平,那么在扫描过程中可能会错误地检测到未被按下的键。

  • 逐行输出低电平:接下来,程序逐行将每一行设为低电平,并检查所有列的状态。这样做的目的是为了逐个检查每一行,看看是否有按键被按下。
    当某一行被设为低电平时,如果这一行的任何键被按下,相应的列会被拉到低电平(因为按键连接了行和列,形成了电路闭合)。①此时,通过检测到的低电平列,我们可以确定是哪个键被按下了。②如果这一行没有键被按下,那么所有列都会保持高电平状态,因为内部上拉电阻会将它们拉高

反转法

反转法通过两个步骤获得键值。
① 将行线设置为低电平,从列线对应引脚读取数据,发现有列线变成低电平,说明该列有按键按下。
②反之,将该列线输出全部设置为低电平,从行对应的引脚读取数据,,发现行有低电平,说明行线有键按下。

//反转法矩阵按键例程 
int kl[4] = { 5, 6, 7, 8};                  		//行线引脚定义
int kc[4] = { 9, 10, 11, 12};               	//列线引脚定义
int key = 0;                          		//键值
int key1 = 0;                       		//按键释放检测
int key_l;                          			//行
int key_c;                          			//列
int flag = 0;                         		//有键按下标志
void setup(){
     Serial.begin(9600);
  }
void loop()
{
  read_key();
  if (key != 0 )
  {
    Serial.print("K");
    Serial.print(key);
    Serial.println(" is pressed");
    key = 0;
  }
}
void read_key() {
  for (int i = 0; i < 4; i++)                 	//行输出,列输入
  {
    pinMode(kl[i], OUTPUT);
    digitalWrite(kl[i], LOW);
    pinMode(kc[i], INPUT_PULLUP);         //带内部上拉电阻
  }
  delay(5);
  for (int k = 0; k < 4; k++) {
    if (!digitalRead(kc[k])) {
      delay(20);
      if (!digitalRead(kc[k])) {
        key_c = k;
        flag = 1;				//有键按下标志
      }
      else flag = 0;
    }
  }
if (flag == 1)  {
    for (int n = 0; n < 4; n++)  {      	//列输出,行输入
       pinMode(kc[n], OUTPUT);
      digitalWrite(kc[n], LOW);
      pinMode(kl[n], INPUT_PULLUP);
    }
    delay(5);
    for (int j = 0; j < 4; j++)   {
      if (!digitalRead(kl[j]))  {
        key_l = j;
        key = key_l * 4 + key_c + 1;	//计算键值,行×4+列,键值1~16
        flag = 0;
      }  }  }
  if (key1 != key)                 		//判断按键是否释放
    key1 = key;
  else
    key = 0;
}

区别

代码上看,扫描法是两个循环嵌套,反转法是四个循环。

模拟量按键接口

仅需一个模拟信号接口,通过输入的电压值不同处理不同的结果

两种:
并联式
image

串联式

image

5.2 红外技术

红外遥控机工作原理

红外遥控器是一种无线发射装置,在波长为0.76~1.5um的近红外线来传输控制信号的遥控设备。
image
红外线二极管发射光波->红外线接收器把红外信号转换为电信号->处理器处理->解调指令

红外发光二极管:特殊的二极管,发出不可见光
红外接收头:VDD,GND,VOUT三个头

特点:
不可穿墙,电路调试简单,编码解码容易

编码

组成:

  • 起始码
  • 用户码
  • 数据码
  • 数据码反码(纠错)

软件编码:八位二段用户码,8位数据码和8位数据反码

5.3 数码管

数码管是8个发光二极管组成的元件,七个段用a~g表示,小数点用dp表示

image

分为共阳极(阴极连接成公共端,接GND),和共阴极(阳极链接成公共端,接5V),区别就是前者阳极高电平发光,后者阴极接地发光。

LCD接口技术

LCD是平面显示器一种。耗电量低,体积小,辐射低。
LCD分为四类:

  • 点阵式液晶屏
  • 段码式液晶屏
  • 字符式液晶屏
  • TFT彩屏

LCD1602

引脚说明

名称 作用
VSS 电源地线
VDD 电源正极
VO 液晶显示偏压
RS 数据/命令选择
R/W 读/写选择
E 使能信号
D0~D7 数据位
BLA 背光板正极
BLK 背光板负极

显示字符有以下几种:
①使用自行库CGROM,包含96个标准ASCII字符,96个日文字符和希腊文字符
②自定义的点阵字符,5*7的矩阵,CGROM存储的也是这种字符,每行用01代表是否显示该点。

LCD类库函数

LiquidCrystal.h

  • LiquidCrystal
    构造函数,可以采用4线(d0~d3悬空),或者8线方式
LiquidCrystal(rs,enable,d4,d5,d6,d7)
LiquidCrystal(rs,rw,enable,d,d,d,d)
  • begin(cols,rows)
    初始化,设定显示列数和行数

  • clear()
    清空

  • write(data)/print(data)
    向LCD写一个字符/字符串,一般来说print用途更广

以下是几个有关光标的函数

  • home()
    光标定位在屏幕左上角

  • setCursor(col,row)
    定位在指定行列

  • cursor()
    显示光标

  • noCursor()
    关闭

  • blink()/noBlink
    闪烁的光标

以下是跟液晶视觉有关的

  • display()/noDisplay()
    开启/关闭液晶显示

  • scrollDisplayLeft()/scrollDisplayRight()
    屏幕上内容向左滚动一个字符

  • autoscroll()/noAutoscroll()
    自动滚动

  • leftToRight()/rightToLeft()
    文本从左到右写入屏幕(默认)/从右向左

  • createChar()
    创建用户自定义字符,可创建8个(0~7),需使用write(num),num为自定义编号。

6 常用模块的应用

6.1 超声波测距

image

超声波测距传感器,采用超声波回波测距原理。
image

下图为SR04原理
image

HR-SR04类库函数

构造函数SR04(int echonPin,int tiggerPin)

  • Distance()
    返回测量距离,long类型,单位cm

下例为超声波测量的例子

#include "SR04.h"		//添加库函数
#define TRIG_PIN 6		//定义引脚
#define ECHO_PIN 7		//定义引脚
SR04 sr04 = SR04(ECHO_PIN,TRIG_PIN);	//构造函数
long a;
void setup() {
   Serial.begin(9600);		//定义串口波特率
   Serial.println("Example written by Coloz From Arduin.CN");
   delay(1000);
}
void loop() {
   a=sr04.Distance(); 		//读取障碍物和SR04的距离
   Serial.print(a);      		//送串口监视器显示
   Serial.println("cm");
   delay(1000);
}

蜂鸣器(模块)

image

是一种一体化结构的电子讯响器,直流电压供电,用作发声器件

有源蜂鸣器(内含驱动电路),可以将直流电转化为一定的脉冲信号,引起磁场变化,带动震动膜片发出震动声音。

和无源蜂鸣器(外部驱动)

有源蜂鸣器

蜂鸣器发声时间不同,频率就不同,声音就不同。

int buzzer = 8;			//设置控制蜂鸣器的数字引脚 正极连接
void setup() { 
      pinMode(buzzer,OUTPUT); 	//设置数字引脚为输出模式
    } 
void loop() { 
   unsigned char i, j;    		//定义变量 
    for(i=0; i<80; i++)  {    		//输出一个频率的声音 
       digitalWrite(buzzer,HIGH);  	//发声 
       delay(1);                       	//延时1ms 
       digitalWrite(buzzer,LOW);  	//不发声 
       delay(1);      }                 	//延时1 ms 
    for(j=0; j<100; j++) {         	//输出另一个频率的声音 
       digitalWrite(buzzer,HIGH);  	//发声 
       delay(2);                     		//延时2ms 
       digitalWrite(buzzer,LOW); 	//不发声 
       delay(2); }                  		//延时2ms 
}

6.3 温湿度传感器

image

DHT11是这学期用到的的。是一款已校准数字信号输出的复合传感器。
有关DHT11的实战可以看我另外一篇博客DHT11+ESP-01S实现上传云数据

DHT11类库函数

  • read()
    获取温湿度状态返回码 0无错误,-1有错,-2超时

  • DHT11.humidity
    int型湿度值

  • DHT11.temperature
    int型温度值

6.4 直流电机(概念即可)

image

直流电机把直流电转换为机械能,
定子:直流电机运行时静止不动的部分。定子的主要作用是产生磁场。
转子:运行时转动的部分,其主要作用是产生电磁转矩和感应电动势

如何调速?

  • 改变电压,电压大小决定转速
  • 改变磁通量
  • 串联几个调节电阻(本质还是调节电压)

如何转向?

改变电流方向即可

6.5 步进电机

步进电机把电脉冲转化为角位移的执行器件。

当步进电机驱动器接收到一个脉冲信号时,它就驱动步进电机按照规定方向转动一个固定角度(步距角)

6.6 陀机

是一种位置(角度)伺服的驱动器,适用于需要角度不断变化并能保持的控制系统
image

image

标准的舵机三条引出线是:电源线(红色)、地线(橙色)、控制线(黄色)。舵机的控制信号是PWM信号,可利用占空比的变化,改变舵机的位置。角度变化和脉冲宽度成正比

陀机类库函数

Servo.h

  • attach(pin,min,max)
    pin:信号引脚
    min:脉冲宽度(可选,默认544/0°)
    max:脉冲最大(可选,默认2400/180°)

  • write(angle)
    设定角度值

  • writeMicroseconds(us)
    设定脉冲宽度

  • read()
    读取陀机的角度值,返回int型(0~180)

  • atached()
    检查陀机是否指定引脚

  • detach()
    分离陀机和指定引脚

陀机代码

例子一:将串口监视器输入的0~9数字转换问0~180对应角度,控制陀机位置

int servopin=9;                   	//定义舵机接口数字接口9
int myangle;                        	//定义角度变量
int pulsewidth;			//定义脉宽变量
int val;
void servopulse(int servopin,int myangle) {  //定义一个脉冲函数
  pulsewidth=(myangle*11)+500;      	//将角度转化为500~2480的脉宽值
  digitalWrite(servopin,HIGH) ;        	//将舵机接口电平置高
  delayMicroseconds(pulsewidth);      	//延时脉宽值的微秒数
  digitalWrite(servopin,LOW );        	//将舵机接口电平置低
  delay(20-pulsewidth/1000);  }
void setup()  {
  pinMode(servopin,OUTPUT);       	//设定舵机接口为输出接口
  Serial.begin(9600);                	//连接到串行端口,波特率为9600
  Serial.println("servu=o_seral_simple ready" ) ;
}

//将0到9的数转化为0到180角度
 void loop()
 {
   val=Serial.read();               		//读取串行端口的值
  if(val>'0'&&val<='9')
{
    val=val-'0';                  		//将字符转化为数值变量
    val=val*(180/9);              		//将数字转化为角度
    Serial.print("moving servo to ");
    Serial.print(val,DEC);
    Serial.println();
    for(int i=0;i<=50;i++)         	//给予舵机足够的时间让它转到指定角度
     {
        servopulse(servopin,val);  	//引用脉冲函数
      }
   }
}

实例2:采用Servo类库,通过电位器控制舵机。程序下载后,当改变电位器的值时,舵机转动的角度会随之改变。

image

#include <Servo.h>            //使用伺服电机类库
Servo myservo;                 //定义伺服对象myservo
int potpin = 0;                  //模拟引脚用于连接电位器
int val;                              //电位器的模拟值存放变量
void setup() {
  myservo.attach(9);        //指定9脚连到舵机
}
void loop() {
  val = analogRead(potpin);       //读取电位器的值( 0~1023)
  val = map(val, 0, 1023, 0, 180);  //变换为控制舵机的角度值(0~180)
  myservo.write(val);            	//根据换算后的值设置舵机位置
  delay(15);                   	//等待舵机到位,以防舵机抖动
}

6.7 SD卡读写模块(概念)

特点:价格低廉,储存容量大,使用方便,通用性和安全性强的特点

两种总线方式

SD方式(六总线):CLK,CMD,DAT0~DAT3

SPI方式(四总线):CLK,CS,DI,DO

SD比SPI较快,但是单片机用SPI偏多。

MicroSD卡和SD兼容,但是MicroSD不带写保护

6.8 三色LED灯(重要)(模块)

image

三色LED灯由三块LED封装而成,分为共阴极和共阳极(原理同LCD1602)。通过analogWrite(),控制三色组合来实现各种颜色。
其中引脚最长的是公共端

例子:

void setup() {
  // 初始化引脚6为输出模式
  pinMode(6, OUTPUT);
}

void loop() {
  // 逐渐增加亮度
  for (int i = 0; i <= 255; i++) {
    analogWrite(6, i);
    delay(10); // 稍作延迟以便观察变化
  }
  
  // 逐渐减少亮度
  for (int i = 255; i >= 0; i--) {
    analogWrite(6, i);
    delay(10); // 稍作延迟以便观察变化
  }
}

6.9 继电器(概念)

继电器(relay)是一个电控制器件,可以把小信号(输入信号)转化为高电压大功率控制信号(输出信号)的自动开关。
image

电磁继电器工作原理

电磁继电器由铁芯,线圈,衔铁,触点簧片组成。

image

简单就是线圈带电产生磁场,吸引弹簧片实现常开,断电弹簧片慢慢回弹实现常闭。分为以下几种:
image

7 通信模块以及应用

7.1 蓝牙通信模块

蓝牙是一种高效稳定的数据传输技术。蓝牙标准中定义了多种协议,使蓝牙协议可应用于各种数据传输。蓝牙端口协议(Serial Port Profile,SPP)是用于规范文本数据传输的协议,该协议可使蓝牙接口能被当成串口一样进行数据传输。

最主要的功能就是取代串口线

HC-05

image
image

引脚 功能
STATE 连接输出高电平,未连接输出低电平
RX 接收
TX 发送
GND 负极
+5V 电源正
EN 使能端,接3.3v时,进入AT指令模式

Hc-05的工作模式

  • 命令响应工作模式

  • 自动连接工作模式:有主(Master),从(Slave),回环(loop)3种工作角色

模式 含义
主(Master)模式 在主模式下,HC-05模块可以主动搜索并连接其他蓝牙设备。这种模式通常用于需要主动发起连接的设备,例如手机或电脑。
从(Slave)模式 在从模式下,HC-05模块只能被其他蓝牙设备搜索并连接,不能主动发起连接。这种模式通常用于需要被动接受连接的设备,例如蓝牙耳机或蓝牙鼠标。
回环(Loop)模式 在回环模式下,HC-05模块会将接收到的数据直接发送回去,形成一个数据回环。这种模式主要用于测试和调试,可以用来检查模块是否能正确接收和发送数据。

灯光:
HC-05状态指示灯,快闪表示没有蓝牙连接;慢闪表示进入AT命令模式;双闪表示有蓝牙连接(配对成功)。
(1)按住按键或EN脚拉高时,HC-05上电开机,此时灯是慢闪,HC-05进入AT命令模式,默认波特率是38400,此模式称为原始模式,该模式下一直是AT命令模式状态。
(2)HC-05上电开机,红灯快闪,按住按键或EN拉高一次,HC-05进入AT命令模式,默认波特率是9600,此模式称为正常模式。HC-05模块出厂时默认为从机,出厂名称:HC-05,波特率9600,配对码是1234

7.2 zigbee通信模块

ZigBee 是一种低成本、低功耗的短距离双向无线通讯协议,可用于取代传 统线路及新兴的感测网络设备之中。

image
XBee 模块是一款把 ZigBee 协议内置在模块 Flash 里的 ZigBee 模块。用户不需要考虑模块中程序是如何运行的,只需将自己的数据通过串口发送给模块,模块会自动把数据无线发送出去,并能按照预先配置好的网络结构和网络中的目的地址节点进行收发通讯,接收模块会自动进行数据校验,如数据无误即发送数据给串口。
也就是用户可以立即使用他的功能无需配置。

XBee模块通信模式

  • AT(Application Transparent)模式
    又被称为透传模式,大意为XBee替代了串口通信线实现了无线通信。若有两个设备连接XBee模块,那么XBee就是这两个模块之间的桥梁。原文接收,原文转发

  • API(Application Programming Interface)模式

此模式下XBee发送和接受数据都会封装成特殊的API帧格式。API模式较复杂但是可以实现AT无法完成的任务。在API下只需要改头地址就能实现群发消息,AT模式只能先进入目的地址再转发。

API比AT的优点:
(1)在网络中可配置本地和远程XBee模块。
(2)数据能发送到一个或多个目标模块。
(3)可识别每个接收包的源地址。
(4)可接收每个发送包的成功/失败状态。
(5)能够得到接收包的信号强度。
(6)可进行先进的网络管理和诊断。
(7)实现某些高级功能。例如:远程固件更新,ZDO、ZCL等等。

ZigBee网络设备模型

  • 协调器(有且只有一个)
    它是网络的开始。协调器选择一个信道(channel)和网络标识(PAN ID)来启动ZigBee网络。协调器在启动网络后,就变成了一个路由器。

  • 路由器
    路由器要先加入ZigBee网络,支持其它设备加入ZigBee网络,数据由路由器转发。不可休眠

  • 终端
    终端必须加入一个ZigBee网络才能工作,不支持其它设备加入网络,不能转发数据,只能通过父设备转发。可以休眠

7.3 WiFi模块(概念)

ESP-01S

image

引脚及定义

官方定义:
image

序号 pin 功能
1 GND 地线
2 IO0/GPIO0 工作模式选择:①悬空:Flash Boot,工作模式 ②下拉:UART DownLoad 下载模式
3 IO2/GPIO2 通用IO
4 RX/RXD 数据接收端
5 TX/TXD 谁发送端
6 3V3/VCC 3.3V,模块供电
7 RST 1)外部复位管交,低电平复位 2)可以悬空或者接外部MCU
8 EN/CH_PD 芯片使能,高电平使能,低电平失能

产品实际图:

image

三种工作模式

  • Station(设备)模式
  • AP(Access Point)模式 (相当于普通路由器)
  • AP兼Station模式

额外知识

一.如何写一个自定义类库

写一个.h文件

首先声明一个类

class SR04 {
		public:
		private:
			}

在public和private中写上会编写到的函数

写.cpp的具体代码

实现高亮显示

在同目录下建立一个keywords.txt文件,键入类似以下代码

SR04	KEYWORD1
Get	KEYWORD2
)

他们中间的间隔是一个tab

image

二.实现共用体对EPROOM的多字节访问

posted @ 2024-06-13 00:11  魔法少女小胖  阅读(146)  评论(0编辑  收藏  举报