基础外设的使用
基础外设使用
GPIO的读写
引脚配置 pinMode
在使用数字输入输出时需要配置引脚
在Arduino SDK中是pinMode函数
函数原型
void pinMode(uint8_t pin , uint8_t mode)
- pin :引脚号
- 模式:
// GPIO 功能定义
#define INPUT 0x01 // 输入模式
// 将 OUTPUT 从 0x02 修改为与 Arduino 的 pinMode(pin, OUTPUT) 行为一致
// 即使引脚设置为 OUTPUT 模式,也可以读取引脚状态
#define OUTPUT 0x03 // 输出模式
#define PULLUP 0x04 // 上拉电阻模式
#define INPUT_PULLUP 0x05 // 输入模式并启用上拉电阻
#define PULLDOWN 0x08 // 下拉电阻模式
#define INPUT_PULLDOWN 0x09 // 输入模式并启用下拉电阻
#define OPEN_DRAIN 0x10 // 开漏模式
#define OUTPUT_OPEN_DRAIN 0x13 // 开漏输出模式 可以输出高电平
#define ANALOG 0xC0 // ADC模式
数字输出 digitalWrite
函数原型
void digitalWrite(uint8_t pin, uint8_t val)
- pin :引脚号
- val :输出值 0或1
一般使用下面的宏定义代替
#define LOW 0x0
#define HIGH 0x1
数字读取 digitalRead
int digitalRead(uint8_t pin)
- pin :引脚号
- return: 0或1
模拟读取ADC analogRead
读取原始值默认为12位 0-4095映射到0-3.3v uint16_t analogRead(uint8_t pin);
[推荐使用]读取电压 单位毫伏 uint32_t analogReadMilliVolts(uint8_t pin); 返回值是毫伏,不用二次转换
注意 esp32在使用wifi时ADC2不可用 具体影响详情见手册
PWM输出
ESP32有两种PWM输出外设 - LED PWM 控制器 - 电机控制脉宽调制器 (MCPWM)
一般我们使用LED PWM 控制器
乐鑫官方文档链接
Arduino API使用步骤
uint32_t ledcSetup(uint8_t chan, uint32_t freq, uint8_t bit_num)
初始化pwm通道
- chan :通道号 一共有8个通道 0-7
- freq :频率 范围跟分辨率相关
- bit_num : 分辨率 单位bit 0-16 (位)无特殊要求推荐12位
void ledcAttachPin(pin, channel)
绑定pwm通道到引脚
- pin 引脚号
- channel 通道号 见上文
void ledcWrite(uint8_t chan, uint32_t duty)
输出PWM波
- chan :通道号 见上文
- duty :输出值 例如在设置为12位分辨率时 0-4095映射到0-100%占空比 其他分辨率以此类推
串口的使用
ESP32有三个硬件串口可以映射到任意引脚,正常情况下不指定引脚是因为串口有默认引脚
arduino默认帮我们定义好了串口对象不用自己创建
但是也可手动创建例如
HardwareSerial yourSerial(/*UART NUM=*/1);
注意HardwareSerial有继承关系,部分API来自基类
HardwareSerial <- Stream <- Print



使用前配置
普通的串口是HardwareSerial的类对象
Arduino sdk默认宏定义了esp32的硬件串口
注意
- Serial0一定是硬件串口
- Serial有可能USB模拟的串口即HWCDC类对象,
- HWCDC接口和HardwareSerial类似但是不能当作HardwareSerial类型传参


初始化:begin()
void HardwareSerial::begin(unsigned long baud, uint32_t config, int8_t rxPin, int8_t txPin, bool invert, unsigned long timeout_ms, uint8_t rxfifo_full_thrhd)
注意begin函数原型比较复杂实际使用一般只关注前4个参数
void HardwareSerial::begin(unsigned long baud, uint32_t config, int8_t rxPin, int8_t txPin)
- baud :波特率
- config :配置数据位/校验位/停止位
这里有许多宏定义,无特殊要求使用 ‘SERIAL_8N1’ 8数据位不校验1停止位
- rxPin :接受数据引脚号
- txPin :发送数据引脚号
使用
发送函数
这里分为两类
- 偏字符串的和调试输出的函数接口
- print :不带换行的打印
- println :带换行的打印
- printf :和C语言用法一样的printf 可以有格式化占位符
- 偏字节流数据输出的函数接口
size_t write(uint8_t);//输出单个字节
size_t write(const uint8_t *buffer, size_t size);//输出多个字节
接收函数
注意Arduino中的串口读取是从串口缓冲区中读取
串口接收发来的数据时会自动存入串口缓冲区,这个过程是Arduino SDK完成的,不需要干预。
int available(void):读取缓冲区可用数据数量
int read(void):从缓冲区读取并删除一个字节
size_t read(uint8_t *buffer, size_t size):从缓冲区读取并删除多个字节
int peek(void):从缓冲区读取一个字节(不删除)
I2C/SPI
这两种通信协议都是基于时钟信号,并且多使用读写寄存器的方式封装协议
I2C
ArduinoSDK中I2C通信由TwoWire类实现
和串口有类似的继承关系
实际使用时I2C器件一般都有库,建议直接搜索库使用 如果需要自行编写驱动也建议使用ESP-IDF的API编写
SPI
ArduinoSDK中SPI通信由SPIClass类实现
实际使用时SPI器件一般都有库,建议直接搜索库使用
ArduinoSDK的SPI收发是阻塞的,在高频收发场景是比较致命的缺点
如果需要自行编写驱动也建议使用ESP-IDF的API编写
CAN通信
ESP32的CAN叫做双线汽车接口 (TWAI)
这两是一个标准
官方api文档
初始化
-
twai_driver_install(twai_general_config_t, twai_timing_config_t, twai_filter_config_t*) :配置引脚/波特率/滤波器
-
twai_start():启动 TWAI 驱动程序
下面是一个典型的初始化示例
//总线速率,1Mbps
static const twai_timing_config_t t_config = TWAI_TIMING_CONFIG_1MBITS();
//滤波器设置,接受所有地址的数据
static const twai_filter_config_t f_config = TWAI_FILTER_CONFIG_ACCEPT_ALL();
//总线配置
static const twai_general_config_t g_config = TWAI_GENERAL_CONFIG_DEFAULT(gpio_num_t(TX_PIN), gpio_num_t(RX_PIN), TWAI_MODE_NO_ACK);
//传入驱动配置信息
twai_driver_install(&g_config, &t_config, &f_config);
//CAN驱动启动
twai_start();
数据包格式 twai_message_t
ESP32的can消息包格式 原型
typedef struct {
union {
struct {
//The order of these bits must match deprecated message flags for compatibility reasons
uint32_t extd: 1; /**< Extended Frame Format (29bit ID) */
uint32_t rtr: 1; /**< Message is a Remote Frame */
uint32_t ss: 1; /**< Transmit as a Single Shot Transmission. Unused for received. */
uint32_t self: 1; /**< Transmit as a Self Reception Request. Unused for received. */
uint32_t dlc_non_comp: 1; /**< Message's Data length code is larger than 8. This will break compliance with ISO 11898-1 */
uint32_t reserved: 27; /**< Reserved bits */
};
//Todo: Deprecate flags
uint32_t flags; /**< Deprecated: Alternate way to set bits using message flags */
};
uint32_t identifier; /**< 11 or 29 bit identifier */
uint8_t data_length_code; /**< Data length code */
uint8_t data[TWAI_FRAME_MAX_DLC]; /**< Data bytes (not relevant in RTR frame) */
} twai_message_t;
主要关心以下成员
extd:是否是扩展帧rtr:是否是远程帧self:是否发给自己,为1时自身也会收到该数据包identifier:CAN标识符/CAN地址data_length_code:数据长度标识data[TWAI_FRAME_MAX_DLC]:存放数据的数组
发送数据
函数原型
esp_err_t twai_transmit(const twai_message_t *message, TickType_t ticks_to_wait);
- message :要发送的数据包指针
- ticks_to_wait: 最大等待时间,这里是基于FreeRTOS的不会阻塞CPU,但是会阻塞当前线程 正常情况下使用:portMAX_DELAY
- return :成功返回 ESP_OK
接收数据
函数原型
esp_err_t twai_receive(twai_message_t *message, TickType_t ticks_to_wait);
该函数会从CAN消息缓冲区读取CAN消息,如果没有CAN消息会等待ticks_to_wait时间,等待时不阻塞CPU,会阻塞当前线程
message:接收数据的数据包指针ticks_to_wait: 最大等待时间,这里是基于FreeRTOS的不会阻塞CPU,但是会阻塞当前线程 正常情况下使用:portMAX_DELAY- return :成功返回 ESP_OK
ESP-NOW通信
简单理解espnow就是基于WIFI底层的用MAC地址确认收发方的通信协议
主要使用步骤 初始化->添加配对->接收和发送数据
ESPNOW是基于MAC在通信时需要对通信的MAC地址添加配对
接收函数是基于回调的
初始化
在调用espnow初始化之前需要初始化wifi
WiFi.mode(WIFI_STA);:wifi有很多模式一般使用WIFI_STA
之后再初始化espnow
esp_now_init()
添加配对
不添加配对无法接收和发送数据包 广播地址也是!
函数原型
esp_err_t esp_now_add_peer(const esp_now_peer_info_t *peer);
esp_now_peer_info_t定义如下
typedef struct esp_now_peer_info {
uint8_t peer_addr[ESP_NOW_ETH_ALEN]; /**< ESPNOW peer MAC address that is also the MAC address of station or softap */
uint8_t lmk[ESP_NOW_KEY_LEN]; /**< ESPNOW peer local master key that is used to encrypt data */
uint8_t channel; /**< Wi-Fi channel that peer uses to send/receive ESPNOW data. If the value is 0,
use the current channel which station or softap is on. Otherwise, it must be
set as the channel that station or softap is on. */
wifi_interface_t ifidx; /**< Wi-Fi interface that peer uses to send/receive ESPNOW data */
bool encrypt; /**< ESPNOW data that this peer sends/receives is encrypted or not */
void *priv; /**< ESPNOW peer private data */
} esp_now_peer_info_t;
其中我们只关注下面的几个参数
- peer_addr :配对MAC地址数组
- channel : WIFI信道,必须处于相同信道的设备才能通信
- ifidx :通信模式->无特殊需求使用WIFI_IF_STA
注意
esp_now_peer_info_t只能是全局变量,在添加多个配对时只需要改变peer_addr再调用一次esp_now_add_peer即可
即使是new出来的esp_now_peer_info_t对象也不能添加配对,原因未知
发送数据
函数原型
esp_err_t esp_now_send(const uint8_t *peer_addr, const uint8_t *data, size_t len);
- peer_addr :发送的MAC地址
- data :数据指针
- len : 数据长度(字节)
注意没有添加配对的MAC地址是不能成功发送的
接收数据
接收数据是通过回调函数实现的,当收到数据时会中断调用回调函数
下面是注册回调函数的函数定义
esp_err_t esp_now_register_recv_cb(esp_now_recv_cb_t cb);
esp_now_recv_cb_t定义如下:
typedef void (*esp_now_recv_cb_t)(const uint8_t *mac_addr, const uint8_t *data, int data_len);
即
回调函数是如下的类型
//接收数据时的回调函数,收到数据时自动运行
void OnDataRecv(const uint8_t *mac, const uint8_t *data, int len) {
//处理数据.....
}