一. 硬件定时器
ESP32 芯片包含两个硬件定时器组。每组有两个通用硬件定时器。它们都是基于 16 位预分频器和 64 位自动重载功能的向上/向下计数器的 64 位通用定时器。
1. 初始化定时器 timerBegin
hw_timer_t * timerBegin(uint8_t num, uint16_t divider, bool countUp){}
参数:
- num : 定时器编号
- divider:分频数
- countUp: 是否是累加模式
返回值:
返回一个计时器结构体指针 hw_timer_t * ,我们预定义一个指针接收他
hw_timer_t* tim1= NULL;
tim1 = timerBegin(0,80,true); //80MHZ, ESP32主频80MHz
2. 取消初始化定时器 timerEnd
void timerEnd(hw_timer_t *timer)
参数:
- *timer : 目标定时器 ( 计时器结构体指针 hw_timer_t * )
2. 配置定时器中断 timerAttachInterrupt
void timerAttachInterrupt(hw_timer_t timer, void (fn)(void), bool edge){}
参数:
- *timer : 目标定时器 ( 计时器结构体指针 hw_timer_t * )
- void (*fn)(void) : 中断函数入口地址
- 中断边沿触发 : 是否跳变沿触发中断 定时器中断触发方式有: 电平触发中断(level type) 边缘触发中断(edge type)
timerAttachInterrupt(tim1,tim1Interrupt,true);
4. 取消定时器中断 timerDetachInterrupt
void timerDetachInterrupt(hw_timer_t *timer)
5. 定时器设置timerAlarmWrite
void timerAlarmWrite(hw_timer_t *timer, uint64_t alarm_value, bool autoreload){}
参数:
- *timer : 目标定时器 ( 计时器结构体指针 hw_timer_t * )
- alarm_value : 计数上限值
- autoreload : 是否重装载.
timerAlarmWrite(tim1, 100000, true);
6. 使能定时器 timerAlarmEnable
void timerAlarmEnable(hw_timer_t *timer){}
参数:
- *timer : 目标定时器 ( 计时器结构体指针 hw_timer_t * )
timerAlarmEnable(tim1);
7. 失能定时器 timerAlarmDisable
void timerAlarmDisable(hw_timer_t *timer)
8. 判断定时器是否启动 timerAlarmEnabled
bool timerAlarmEnabled(hw_timer_t *timer)
Serial.println(timerAlarmEnabled(tim1));
例子:
#include <Arduino.h>
hw_timer_t *tim1 = NULL;
int tim1_IRQ_count = 0;
void tim1Interrupt()
{
Serial.println("haha");
tim1_IRQ_count++;
Serial.println(timerAlarmEnabled(tim1));
}
void setup()
{
Serial.begin(115200);
tim1 = timerBegin(0, 80, true);
timerAttachInterrupt(tim1, tim1Interrupt, true);
timerAlarmWrite(tim1, 100000, true);
timerAlarmEnable(tim1);
}
void loop()
{
if (tim1_IRQ_count > 10)
{
Serial.println("count trigger");
tim1_IRQ_count = 0;
}
}
二. IIC (主机)
ESP32有两个I2C控制器(也称为端口),负责处理两条I2C总线上的通信。每个I2C控制器都可以作为主机或从机运行。引脚21 默认的SDA, 引脚22是默认的SCL
IIC需要引入自带库 Wire.h Wire继承steam类 steam类有的他都有
#include "Wire.h"
1. 初始化IIC (以主机身份) begin
Wire.begin();
2. 以主机身份像从机请求数据 requestFrom
void requestFrom(uint16_t address, uint8_t size, bool sendStop)
请求完成后 主机可以用 Wire.available()
和Wire.read()
等函数等待并获取从机的回答
参数:
- address : 从机地址
- size: 请求字节数
- sendStop : 是否发送停止 , 如果为true, 释放IIC总线. 如果为false, 发送一个重新开始的信息, 并继续保持IIC总线的连接.
Wire.requestFrom(adress,10,true);
3. 主机开始传输 beginTransmission()
void beginTransmission(int address)
随后, 主机可以使用Wire.write();
写数据并使用Wire.endTransmission();
结束传输
参数:
- address : 从机地址
Wire.beginTransmission(120);
4. 结束数据传输 endTransmission()
结束传输, 并释放IIC
Wire.endTransmission();
5. 结束数据传输但不释放IIC占用 endTransmission(false)
返回值: uint8_t 类型
值 | 含义 |
---|---|
0 | 成功 |
1 | 数据过长,超出发送缓冲区 |
2 | 在地址发送时接收到NACK信号 |
3 | 在数据发送时接收到NACK信号 |
4 | 其他错误 |
Wire.endTransmission(false);
6. 写 write()
当作为主机时: 主机将要发送的数据加入发送队列;
当作为从机时: 从机发送的数据给主机;
参数:
- Wire.write(value); //单字节发送
- Wire.write(string); //以一系列字节发送
- Wire.write(data,length); //以字节形式发送,指定长度
返回值: byte类型
输入的字节数
7. 接收数据寄存器有值 available()
返回接收到的字节数
返回值: byte类型
- 可读字节数
8. 读取1byte数据 read()
当作为主机时: 主机使用requestFrom()后 要使用此函数获取数据;
当作为从机时: 从机读取主机给的数据;
返回值: 读到的字节数据 byte
9.读取多个字节的数据 readBytes()
size_t readBytes(char *buffer, size_t length)
参数:
- buffer: 接收缓冲区, 一个char型指针
- length: 数据长度
返回值: 数据长度
10. 读取直到遇到某字符
size_t readBytesUntil(char terminator, char *buffer, size_t length)
参数:
- terminator : 终结字符 char类型
- buffer: 接收缓冲区, 一个char型指针
- length: 数据长度
返回值: 数据长度
11. 当前IIC忙线中?
Wire.busy();
返回布尔值
12. 读取字符串 readString() readStringUntil()
继承自steam类, 个人感觉iic不会用到的
13. 其他
parseFloat
parseInt
find
findUntil
setTimeout
这些都是steam继承来的 大家灵活应用
三. IIC(从机)
从机有些函数和主机是一样的, 请看上一章节,本章节只有不一样的部分
1. 初始化IIC (以从机身份) begin(adress)
Wire.begin(adress); //adress取值0~127
Wire.begin(120);
2. 当从机被请求时触发函数onRequest()
void onRequest(void (*)())
参数:
- 回调函数
3. 当从机收到数据时触发函数
void onReceive(void (*)(int))
参数:
- 回调函数 (接收一个int类型的参数,代表接收的字节数)
四. SPI
ESP32有四个SPI外设,分别为SPI0、SPI1、HSPI和VSPI。
- SPI0是专用于Flash的缓存,ESP32将连接的SPI Flash设备映射到内存中。
- SPI1和SPI0 使用相同的硬件线,SPI1用于写入flash芯片。
- HSPI和VSPI可以任意使用。
- SPI1、HSPI和VSPI共有三条片选线,因此作为SPI主机允许ESP32 至多驱动三个SPI设备。
1. HSPI和VSPI的接口及引脚
SPI名 | MOSI | MISO | SCLK | SS |
---|---|---|---|---|
VSPI | 23 | 19 | 18 | 5 |
HSPI | 13 | 12 | 14 | 15 |
SPI通讯流程如下:
2. SPI初始化 SPI.begin()
SPI.begin();
SPI接口默认VSPI. 接口频率1 000 000, 数据默认采用MSBFIRST(低有效位优先), 时钟模式:SPI_MODE0(SCLK闲置为0, SCLK上升沿采样)
3. 设置数据在SPI上的传输方式 SPI.setBitOrder(bitOrder);
参数:
- bitOrder : 传输方式, 可选: LSBFIRST 低有效位先传 ; HSBFIRST 高有效位先传
SPI.setBitOrder(LSBFIRST);
3. 设置SPI频率 SPI.setFrequency(freq)
参数:
- freq 频率
SPI.setFrequency(1000000);
4. 设置SPI的时钟模式 SPI.setDataMode(dataMode);
参数:
- dataMode: 时钟模式, 可以取以下值
模式 | 说明 |
---|---|
SPI_MODE0 | SCLK闲置为低电平,上升沿采样(默认) |
SPI_MODE1 | SCLK闲置为低电平,下降沿采样 |
SPI_MODE2 | SCLK闲置为高电平,上升沿采样 |
SPI_MODE3 | SCLK闲置为高电平,下降沿采样 |
SPI.setDataMode(SPI_MODE0);
5. 按照setting的设置启动SPI通信 SPI.beginTransaction(setting);
采用该函数,可以代替上面三个函数了.
参数:
- setting 设置. 是SPISettings类型的对象, 有_bitOrder ,_clock ,_dataMode 这三个属性.
setting1._bitOrder = LSBFIRST;
setting1._clock = 1000000;
setting1._dataMode = SPI_MODE0;
SPI.beginTransaction(setting1);
6. 结束SPI通信 SPI.endTransaction();
结束SPI通信
SPI.endTransaction();
7. 接收/发送一个字节的数据 SPI.transfer(data);
参数:
- data: 要发送的数据
返回值: 接收到的数据
uint8_t SPIClass::transfer(uint8_t data)
SPI.transfer(0x01);
SPI.transfer16(0x0102);
SPI.transfer32(0x01020304);
uint8_t byte1;
uint16_t bytes2;
uint32_t bytes3;
byte1 = SPI.transfer();
bytes2 = SPI.transfer16();
bytes3 = SPI.transfer32();