Loading... ## 第一章 绪论 ### 什么是嵌入式系统 - 从技术角度定义: > 以`应用`为中心、以`计算机`技术为基础、软件硬件`可裁剪`、适应应用系统对`功能、可靠性、成本、体积、功耗`严格要求的`专用计算机系统(个人计算机)`。 - 从系统的角度定义: > 嵌入式系统是设计完成复杂功能的`硬件和软件`,并是其紧密耦合在一起的计算机系统。 ### 嵌入式系统的组成 - 软件子系统: > 嵌入式操作系统和各种应用 - 硬件子系统: > 以嵌入式处理器为中心,连接各种外围设备 ### 什么是Arduino - 什么是Arduino: > Arduino是一款灵活、方便上手的`开源`电子原型平台,包括`硬件(各种型号的arduino板)`和`软件集成开发环境(arduino IDE)`。 - Arduino的优势 1. 开源:硬件开源,软件开源 2. 跨平台:IDE可以运行在Windows,Linux, Mac OS 3. 简单清晰的开发 4. 社区与第三方支持:众多的实例,第三方硬件,外设和类库 - Arduino UNO的规格 1. 处理器 ATmega328(AMTEL公司的`8位`RISC单片机) 2. 工作电压 5V 3. 数字IO脚 14 (其中6路作为PWM输出3 5 6 9 10 11脚) 4. 模拟输入脚 6(A0~A5) 5. Flash Memory 32 KB (ATmega328,其中0.5 KB 用于 bootloader) 6. SRAM 2 KB (ATmega328) 7. EEPROM 1 KB (ATmega328) 8. 工作时钟 16 MHz - 特殊端口 1. UART通讯 0:RX,1:TX 2. 外部中断输入: 2, 3 3. SPI通讯:10, 11 , 12, 13 4. TWI:A4,A5 5. AREF口 ### 实例程序 - 灯光闪烁程序 ```cpp void setup()//初始化程序 { pinMode(13, OUTPUT);//设置13号口为输出 } void loop()//程序循环体,程序会一直循环这个函数 { digitalWrite(13, HIGH);//写入13号口为高电平,灯亮 delay(1000);//延时1s,灯光亮一秒 digitalWrite(13, LOW);//写入13号口为低电平,灯灭 delay(1000);//延时1s,灯光灭一秒 } ``` ## 数字IO编程 ### 数字信号 数字信号是0、1表示的不连续的信号,也就是以二进制形式表示的信号。`在Arduino中数字信号用高低电平来表示,高电平(HIGH)为数字信号1, 低电平(LOW)为数字信号0`。 现实例子:门、开/关 ### Arduino UNO的数字IO口 - D0~D13:数字输入/输出引脚 - 输出:`低电平0V`,`输出高电平为工作电压5V` - 输入:`范围-0.5~1.5V输入电压识别为低电平`,`3~5.5V的输入电压作为高电平` ### IO编程API + 设置引脚模式: ```cpp pinMode(pin, mode); //pin: 引脚号 //mode: 工作模式 ``` + 工作模式 > INPUT: 输入模式 OUTPUT: 输出模式 INPUT_PULLUP: 输入上拉模式 INPUT_PULLDOWN: 输入下拉模式 + 输出 ```cpp digitalWrite(pin,value); //pin:引脚号 //value: HIGH or LOW ``` + 读取 ```cpp int value=digitalRead(pin); //pin:引脚号 ``` + 上拉电阻和下拉电阻 上拉(Pull Up )或下拉(Pull Down)电阻(两者统称为“拉电阻”)最基本的作用是:将状态不确定的信号线(悬空)通过一个电阻将其`箝位`至高电平(上拉)或低电平(下拉),同时也起到`限流`的作用。 > 开关高电平无效用下拉电阻, 开关低电平默认无效用上拉电阻。 ### 交通灯的控制 - 实验连接图  - 实验原理图  - 实验代码 ```cpp /* * 10号管脚控制红灯, * 9号管脚控制黄灯, * 8号管脚控制绿灯, */ int red = 10; int yellow = 9; int green = 8; void setup() { pinMode(red, OUTPUT); pinMode(yellow, OUTPUT); pinMode(green, OUTPUT); } //设置灯亮时间,单位ms int red_time = 60000; int yellow_time = 5000; int green_time = 45000; void loop()//循环主体 { run(); } void run()//控制灯光顺序以及时间 { changeLight(green, red, yellow);//绿灯亮,其他两个不亮 delay(green_time);//延迟绿灯时间 changeLight(yellow, red, green);//黄灯亮,其他两个不亮 delay(yellow_time);//延迟黄灯时间 changeLight(red, green, yellow);//红灯亮,其他两个不亮 delay(red_time);//延迟红灯时间 } //控制三个灯的亮暗 //on:需要亮的灯 //off1,off2:另外两个不需要亮的灯 void changeLight(int on, int off1, int off2) { digitalWrite(on, HIGH); digitalWrite(off1, LOW); digitalWrite(off2, LOW); } ``` ## 模拟IO编程 ### 模拟信号 生活中接触到的大多数信号都是模拟信号,如声音和温度的变化,模拟信号用连续变化的物理量来表示信息的,信号随时间做连续变化,`在Arduino中,常用0~5V的电压来表示模拟信号`。 ### Arduino UNO的模拟IO口 + A0~A5:模拟输入/输出引脚,返回0~1023 + 模拟输入 ```cpp int value = analogRead(pin); //pin: 输入引脚 //value: 读入的值,范围在[0,1023] ``` + 模拟输出 ```cpp analogWrite(pin, value); //pin: 输出引脚 //value: 输出的值,范围在[0,255] ``` ## 高级IO编程 ### 调频函数tone 主要用于连接蜂鸣器或扬声器发声的场合,其实质是输出一个`频率可调`的方波。 + 简单的使用方法 ```cpp tone(pin, frequency); tone(pin, frequency, duration); //pin:需要输出方波的引脚 //frequency:频率 //duration:频率持续的时长,如果没有该参数,将持续发声,直到改变频率或遇到noTone()函数 noTone(pin); //需要停止输出方波的引脚 ``` + 注意事项 1. tone函数会干扰3、11号管脚的PWM输出,如果要PWM输出,用其他管脚 2. 同一时间tone函数只能作用于一个管脚,如果有多个管脚需要使用tone函数,必须noTone停止,然后tone开启新管脚的方波输出。 ### 脉冲宽度测量pulseIn + 简单的使用方法 ```cpp unsigned long distance; distance = pulseIn(pin, value); distance = pulseIn(pin, value, timeout); //pin:引脚 //value:需要读取的脉冲的类型,为HIGH或LOW //timeout:超时时间,如果超过设定时间仍未检测到脉冲,返回0,不设置timeout,timeout默认为1秒。 //distance:脉冲宽度,单位为微秒,如果在指定时间没有检测到脉冲,将返回0 ``` ### 设置ADC参考电压 > analogRead(pin) 的函数返回值 = (被测电压/参考电压)X 1023 默认参考电压就是Arduino的工作电压5V。 当要测量的电压较小时或者对测量精度要求较高时,可以通过AREF引脚引入外部参考电压(注意:一定要小于5V的工作电压,否则可能会损毁板子) analogReference(type) type:DEFAULT,默认当前Arduino的工作电压为参考电压。 EXTERNAL:使用从AREF引脚输入的外部参考电压。 ## 中断编程 ### 什么是中断 CPU执行时原本是按程序指令一条一条向下顺序执行的。 但如果此时发生了某一事件B请求CPU迅速去处理(中断发生),CPU暂时中断当前的工作,转去处理事件B(中断响应和中断服务). 待CPU将事件B处理完毕后, 再回到原来被中断的地方继续执行程序(中断返回),这一过程称为中断。 + 内部中断 内部中断主要为`定时中断`,定时中断是指主程序在运行一段程序过后自动进行的中断服务程序。 + 外部中断 一般由外设发出中断请求,如:键盘中断、打印机中断、外部中断需`外部中断源`发出`中断请求`才能发中断。 ### UNO外部中断 + 中断引脚 引脚2:中断0 引脚3:中断1 + 中断模式: | 模式名称 | 说明 | | :----- | :---| |LOW | 低电平触发(UNO不支持HIGH)| |CHANGE| 电平变化触发,即高电平变低电平、低电平变高电平 | |RISING| 上升沿触发,即低电平变高电平 | |FALLING| 下降沿触发,即高电平变低电平 | ### 中断API + 简单使用 ```cpp void f()//中断函数 { ......... ......... } attachInterrupt(interrupt, function, mode);//初始化中断 detachInterrupt(interrupt);//禁止外部中断 noInterrupts();//禁止中断 interrupts();//重新启用中断 //interrupt:中断编号(不是引脚号) //function:中断函数,填写中断函数的名字 //mode:中断模式 ``` ### 定时中断 + 简单使用 ```cpp MsTimer2::set(time, function); //time:间隔时间 //function:中断服务程序,填写中断函数的名字 MsTimer2::start();//开启定时中断 MsTimer2::stop();//关闭定时中断 ``` + 注意 需要添加库函数:`MsTimer2.h` ## 串口通讯编程 ### 并行和串行通讯 + 并行通讯 数据在多条总线上同时传输,传输速度较快,数据传输比较简单,但由于总线比较多且总线与总线之间在传输过程中会产生干扰,不适合用于长距离传输。 + 串行通讯 数据在一条总线上按位传输,传输速度较慢,数据传输控制较复杂,但只需要一根总线就能完成数据传输且不会产生干扰,适合用来长距离传输。 ### 同步串行和异步串行通讯 + 同步串行通讯 发送方对接收方的时钟进行控制,使双方时钟完全同步 每个字符之间不留空隙,既保持位同步关系也保持字符同步关系 以特定的位组合`01111110`作为帧的开始和结束 实现的硬件设备较为复杂 + 异步串行通讯 不要求双方时钟一致 每个字符要附加2-3位(起始位、校验位、停止位) 各帧之间有任意间隔,位之间间隔一定 用于单片机和单片机、单片机和计算机之间的通信 ### 单工、半双工、全双工 + 单工 单工数据传输只支持数据在一个方向上传输;在同一时间只有一方能接受或发送信息,不能实现双向通信,举例:电视,广播。 + 半双工 半双工数据传输允许数据在两个方向上传输,但是,在某一时刻,只允许数据在一个方向上传输,它实际上是一种切换方向的单工通信;在同一时间只可以有一方接受或发送信息,可以实现双向通信。举例:对讲机。 + 全双工 全双工数据通信允许数据同时在两个方向上传输,因此,全双工通信是两个单工通信方式的结合,它要求发送设备和接收设备都有独立的接收和发送能力;在同一时间可以同时接受和发送信息,实现双向通信,举例:电话通信。 ### 串口参数 串口参数:波特率(bps),数据位,停止位,校验位,通讯双方必须一致 - 起始位:起始位总为低电平,是一组数据帧开始传输的信号。 - 数据位:承载了实际发送的数据段,可能是8位也可能小于8。 - 奇偶效验位:奇校验(数据位奇数个1该位置1) 或者偶校验。 - 停止位:每段数据帧的最后都有停止位表示该段数据帧传输结束。停止位总为高电平。可以设置停止位为1位或2位。 如果外部干扰太大,导致数据丢失,常采用降波特率,增加停止位和校验位。 ### 硬串口和软串口 - 硬串口:硬件实现的串口,UNO只有一个硬串口,管脚:RX:0,TX:1 - 类库:HardwareSerial类库,默认包含,不用#include > UNO只有一个硬串口对象:Serial UNO 硬串口主要用于与计算机通讯(USB转串口)。 - 软串口:将其他数字引脚通过程序来模拟成串口通讯 - 类库:SoftwareSerial,必须#include <SoftwareSerial.h> > 局限性:程序模拟,不如硬串口稳定 ### 硬串口常用API - available() 获取串口数据 使用方法: ```cpp Serial.available(); //返回值:可读取的字节数 ``` - begin() 初始化串口 使用方法: ```cpp Serial.begin(speed); Serial.begin(speed, config); //speed:波特率 //config:数据位、校验位、停止位配置 ``` - end() 结束串口通讯 使用方法: ```cpp Serial.end(); ``` - find() 从串口缓冲区读取数据 使用方法: ```cpp Serial.find(target); //target:需要搜索的字符或字符 //返回值:bool值类型,true表示找到,false表示没找到 ``` - print(),println(),write() 使用方法: ```cpp Serial.print(); Serial.println(); Serial.write(); ``` 三者的差异: print()不带换行,println()带换行 print:将数据转换成字符,再将字符对应的ASCII吗发送出去,串口收到ASCII码,则会显示对应的字符。 write:发送数据本身,但串口监视器接收数据后,会将数据当做ASCII码而显示对应的字符 - read(),peek() 使用方法: ```cpp col = Serial.read(); col = Serial.peek(); //返回值:返回读取的字符 ``` 两者的区别: read:从缓冲区读取数据后,会将该数据从接收缓冲区中移除。 peek:读取数据后,不会移除接收缓冲区中的数据。 ### 软串口SoftwareSerial类库使用 软串口由软件模拟产生,使用不如硬串口稳定,波特率越高越不稳定 - SoftwareSerial() 构造函数 使用方法: ```cpp SoftwareSerial mySerial = SoftwareSerial(rxPin, txPin); SoftwareSerial mySerial(rxPin, txPin); //mySerial:用户自定义软串口对象 //rxPin:软串口接收引脚 //txPin:软串口发送引脚 ``` - listen() 开启软串口监听功能 使用方法: ```cpp mySerial.listen(); ``` - isListening() 检测软串口是否处于监听状态 使用方法: ```cpp mySerial.isListening(); //返回值:bool值类型,true表示正在监听,false表示没有监听 ``` - overflow() 检测缓冲区是否溢出 使用方法: ```cpp mySerial.overflow(); //返回值:bool值类型,true表示溢出,false表示溢出 //软串口缓冲区最多保存64B数据 ``` ### 两台Arduino的通讯 实现代码: ```cpp #include <SoftwareSerial>//导入SoftwareSerial类库 //rx:10 接收端口 //tx:11 发送端口 SoftwareSerial softSerial(10, 11); void setup() { //初始化串口通信 Serial.begin(9600); //初始化软串口通信 softSerial.begin(9600); //监听软串口 softSerial.listen(); } //两个字符串表示分别用户存储A,B两端传来的数据 String device_A_String = ""; String device_B_String = ""; void loop() { //读取计算机传入的数据,并通过softSerial发送给另一台设备 if(Serial.available() > 0) { if(Serial.peek() != '\n')//将输入的信息读到device_B_String { device_B_String += (char)Serial.read(); } else//如果读入完毕 { Serial.read();//清空缓冲区 Serial.print("you said: "); Serial.println(device_B_String);//在串口中打印传送的信息 softSerial.println(device_B_String);//将信息通过软串口传送到设备A device_B_String = "";//清空读入的信息 } } //读取从另一台设备传入的数据,并在串口监视器中显示 if(softSerial.available() > 0) { if(softSerial.peek() != '\n')//将另一台设备传送的信息读到device_A_String { device_A_String += (char)softSerial.read(); } else//如果读入完毕 { softSerial.read();//清空软串口缓冲区 Serial.print("device A said: "); Serial.println(device_A_String);//在串口中打印另一台设备传入的信息 device_A_String = "";//清空软串口读入的信息 } } } ``` ## IIC和SPI串行总线 ### IIC总线(IIC,Inter-Integrated Circuit) 飞利浦设计的一种`串行总线`,其特点是: 1. 采用2根线(SDA:数据线,SCL:时钟线)来完成数据的传输和外围器件的扩展 2. 对各器件的寻址是`软寻址方式`(`8位地址`),因此器件上没有`片选线` 3. 器件有主机(Master)和从机(Slave)之分,主机提供时钟信号,启动和终止数据传输;从机会被主机寻址,并且响应主机的请求;从机之间无法通讯,任何数据传输都必须通过主机进行 #### wire类库的主要方法 - begin() 初始化IIC链接 使用方法: ```cpp Wire.begin(); Wire.begin(address); //address:一个7位从机地址,范围0-127 //当填写address时为从机模式,不填写为主机模式 ``` - requestFrom() 主机向从机发送数据请求信号 使用方法: ```cpp Wire.requestFrom(address, quantity); Wire.requestFrom(address, quantity, stop); //address:设备地址 //quantity:请求的字节数 //stop:bool型,true将发送停止信息,释放IIC总线,false将发送一个重新开始信息,保持IIC总线的连接 ``` - beginTransmission() 设定传输数据到指定地址的从机设备 使用方法: ```cpp Wire.beginTransmission(address); //address:设备地址 ``` - endTransmission() 结束数据传输 使用方法: ```cpp Wire.endTransmission(); Wire.endTransmission(stop); //address:设备地址 //stop:bool型,true将发送停止信息,释放IIC总线,false将发送一个重新开始信息,保持IIC总线的连接 ``` - write() 当为主机状态:主机将要发送的数据加入发送队列 当为从机状态:从机发送数据至发起请求的主机 使用: ```cpp Wire.write(value); Wire.write(string); Wire.write(data, length); //value:以单字节发送 //string:以字符串发送 //data:以字节形式发送数组 //length:传输的字节数 //返回值:byte,返回输入的字节数 ``` - available() 返回接收到的字节数 使用方法: ```cpp Wire.available(); //返回值:返回接收到的字节数 ``` - read() 读取1B的数据 使用方法: ```cpp Wire.read(); //返回值:返回接收到的字节数据 ``` - onReceive() 在从机端注册一个事件,当从机收到主机发送的数据时触发 使用方法: ```cpp Wire.onReceive(handle); //handle为触发的函数 ``` - onRequest() 注册一个事件,当从机接收到主机的数据请求时即被触发 使用方法: ```cpp Wire.onRequest(handle); //handle为触发的函数 ``` ### SPI总线(Serial Peripheral Interface,串行外设接口) SPI(Serial Peripheral Interface,串行外设接口),Arduino可以通过SPI接口连接多个外设,如:SD卡,图形液晶,网络芯片等。 在SPI总线中,也有主机和从机之分,主机负责输出时钟信号`SCK`,主机和从机之间有两条数据线(`MISO`和`MOSI`),而每个从机跟主机之间有一个`CS`(片选)信号线,决定主机选择哪个从机通讯,CS低电平选中。 Arduino作为主机,其他设备为从机,所以Arduino只提供主机类库。 - UNO的SPI引脚位置: > MOSI:11 MISO:12 SCK: 13 SS: 10 或者ICSP的6根引脚 #### SPI类库成员函数 - begin() 初始化SPI通讯,SCK、MOSI、SS引脚设置为输出模式,SCK、MOSI拉低,SS拉高 使用方法: ```cpp SPI.begin(); ``` - end() 关闭SPI总线通讯 使用方法: ```cpp SPI.end(); ``` - setBitOrder() 设置传输顺序 使用方法: ```cpp SPI.setBitOrder(order); //order:LSBFIRST:低位在前、MSBFIRST:高位在前 ``` - setClockDivider() 设置通信时钟 使用方法: ```cpp SPI.setClockDivider(divider); //divider: /* SPI_CLOCK_DIV2 * SPI_CLOCK_DIV4 默认 * SPI_CLOCK_DIV8 * SPI_CLOCK_DIV16 * SPI_CLOCK_DIV32 * SPI_CLOCK_DIV64 * SPI_CLOCK_DIV128 */ ``` - setDataMode() 设置数据模式 使用方法: ```cpp SPI.setDataMode(mode); //mode: /* SPI_MODE0 * SPI_MODE1 * SPI_MODE2 * SPI_MODE3 */ ``` - transfer() 传送1B数据 使用方法: ```cpp SPI.transfer(val); //val:要发送的字节数据(1B) //返回值:读到的字节数据(1B) ``` - SPISettings() 使用新的SPI库 使用方法: ```cpp SPISettings mySettings(speed, dataOrder, datamode); //speed:通讯速度 //dataOrder:传输顺序,LSBFIRST低位在前,MSBFIRST,高位在前 //dataMode:数据模式 SPI_MODE0~SPI_MODE4 //用begin默认的SPI配置是:14000000, MSBFIRST, SPI_MODE0 ``` - beginTransaction() 使用SPI配置定义的总线 使用方法: ```cpp SPI.beginTransaction(SPISettings(speed,dataOrder,dataMode)); //SPISetting配置如上 ``` - endTransaction() 停止使用SPI总线 使用方法: ```cpp SPI.endTransaction(); ``` - usingInterrupt() 在中断使用SPI 使用方法: ```cpp SPI.usingInterrupt(InterruptNumber); //InterruptNumber:中断号 ``` ## 存储 ### 用EEPROM存储数据 EEPROM(Electrically Erasable Programmable Read-Only Memory)电可擦可编程只读存储器,UNO EEPROM大小为1KB 特点: 1. 断电后数据不丢失 2. 写慢读快,不适应于频繁写的场景 3. 有擦写寿命(100000次) 4. 不适合做大规模存储 ### EEPROM类库的成员函数 - 写:EEPROM.write(address, value) > address: 0~1023, value:写入的数据,byte型 - 读:EEPROM.read(address) > 返回指定地址存储的数据,byte型 - 更新:EEPROM.update(addrss,value) > 如果value不同于已保存在同一地址的值的时候才会写入,使用该函数替换write函数可以节省使用周期 - 多个字节读:EEPROM.get(address, data) > data读取的数据,可以使一个基本类型(如float)或者是一个自定义的结构体。 - 多个字节写:EEPROM.put(address, data) > data写入的数据,可以是一个基本类型(如float)或者是一个自定义的结构体。 EEPROM[address]:直接对EERROM单元进行读取和写入操作。 ### SD卡类库&成员函数 - begin:初始化SD卡库和SD卡 - open(filename,mode)打开SD卡上的一个文件,MODE(可选):FILE_READ, FILE_WRITE。返回文件对象 - remove(filename):删除SD卡上的一个文件 - exists(filename):检查文件或文件夹是否存在于SD卡上 - mkdir(filename):创建文件夹 - rmdir(filename):删除文件夹 ### File类的成员函数 写入数据到文件:file.write(data) / file.write(buf,len) file.print(data)/print(data,BASE) 输出数据到文件 读取1B的数据: file.read() 关闭文件:file.close() 文件大小:size() ## 显示 ### 1602 LCD 1. 字符型液晶 2. 16X2个字符(2行16列)=>1602 3. 并行接口(8位双向数据线) ### 1602引脚 |序号|引脚|说明| |:----|:----|:----| |1|VSS|地线| |2|VDD|电源| |3|V0|对比度调整,电压越大对比度越小| |4|RS|数据/指令选择。高电平数据,低电平指令| |5|R/W|读写操作。高电平读,低电平写| |6|E|使能信号。高电平变为低电平时执行命令| |7-14|D0-D7|8位双向数据线| |15|A|LDC背光电源正极| |16|K|LDC背光电源正极| ## 红外遥控 ### 无线通信 - 无线通信方式:`ZigBee、WiFi、蓝牙、4G、5G` - 串口透传`无线模块:如:ESP8266 Arduino<-- 串口-->无线模块` - SPI接口`模块:如Arduino WiFi扩展板, 传输速率更快` - `红外`(Infra-Red)是一种`最常用、成本最低`的无线通信方式 ### IRremote类库的成员函数 - IRReceive 红外接收类 > 构造函数IRrecv(recvpin) enableIRIn():初始化红外解码 decode(&result), result.value就是编码 resume:接收下一个编码 - IRSend 红外发射类 > 构造函数IRSend() sendNEC(data, nbits) sendSony(data, nbits) sendRaw(buf, len, hz) ## Ethernet和WiFi ### TCP/IP网络基础知识 既然是互联互通,大家必须遵循一定的协议,协议通过国际组织制定和发布称为标准,最出名的就是`OSI`和`TCP/IP`网络体系标准 - 什么是计算机网络 多个计算机通过物理介质和网络设备互联互通组成的网络。 - 常见网络设备 以太`交换机`、以太`路由器`、无线路由器 ### WiFi基础知识 WiFi的全称是Wireless Fidelity,又叫802.11b标准,是IEEE定义的一个无线网络通信的工业标准。该技术使用的2.4GHz附件的频段,最高带宽11Mbps,通信距离达几十到百米。 - WiFi模式 802.11a 5GHz,54~72Mbps 10~100M 802.11g 是802.11b的提速,达54Mbps 802.11n 72.2M~150Mbps - WiFi所在的网络层次 对应TCP/IP网络层次的MAC层和物理层 用于组建无线局域网(LAN) Last modification:January 13, 2025 © Allow specification reprint Support Appreciate the author AliPayWeChat Like 如果觉得我的文章对你有用,请随意赞赏
6 comments
老子终于背完了!!!(╯‵□′)╯︵┴─┴
不是只是随便看看嘛,你怎么都背下来了
有错误
原来是这样
测试评论中回复的回复效果
改好了٩(ˊᗜˋ*)و