1. 1. 组成结构
  2. 2. 基础外设
    1. 2.1. 按键Key
      1. 2.1.1. 样例
    2. 2.2. 姿态传感器(QMI8658)
    3. 2.3. IO拓展(PAC9557)
    4. 2.4. 音频输入(ES7210)
      1. 2.4.1. I2S的IO和寄存器配置
      2. 2.4.2. 标准I2S和TDM_I2S模式
    5. 2.5. 音频输出(ES8311)
    6. 2.6. 液晶显示(ST7789+FT6557)
      1. 2.6.1. 实验
    7. 2.7. 摄像头(GC0308)
      1. 2.7.1. 实验
    8. 2.8. LVGL
  3. 3. 配置
    1. 3.1. 基础配置
    2. 3.2. yml文件
  4. 4. 参考

主要学习立创ESP32S3 N16R8的基本配置以及其各部件组成

组成结构

类别 型号 参数
模组 ESP32-S3-WROOM-1-N16R8 搭载 Xtensa® 32 位 LX7 双核处理器,主频高达 240 MHz,内置SRAM 512kB,外置PSRAM 8MB,外置FLASH 16MB,2.4 GHz Wi-Fi (802.11 b/g/n) 40MHz带宽,Bluetooth 5 (LE) 和 Bluetooth Mesh,集成AI向量指令,加速神经网络计算和信号处理
显示屏 ST7789 2.0寸、IPS全视角、分辨率320*240、SPI接口
触摸屏 FT6336 电容触摸、I2C接口
姿态传感器 QMI8658 三轴加速度+三轴陀螺仪、I2C接口
音频DAC ES8311 单通道、I2C接口
音频ADC ES7210 四通道(开发板用三个通道)、I2C接口
音频功放 NS4150B 单声道D类音频放大器
麦克风 ZTS6216 配套双路麦克风、模拟输出
喇叭 DB1811AB50 1811音腔喇叭、1W
USB HUB CH334F USB2.0 HUB
USB转串口 CH340K 波特率最大2Mbps
电源芯片 SY8088AAC 提供双路、每路1A
GH1.25接口 两路外拓传感器接口,可以给外部传感器供电5V和3.3V,可以作为GPIO、CAN、I2C、UART、PWM等接口
TF卡接口 采用1-SD模式与ESP32连接
Type-C接口 用于供电、程序下载、程序调试,以及USB数据通信
按键 一个复位按键、一个用户自定义按键

基础外设

按键Key

样例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <inttypes.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/queue.h"
#include "driver/gpio.h"

static QueueHandle_t gpio_evt_queue = NULL;

static void IRAM_ATTR gpio_isr_handler(void* arg)
{
uint32_t gpio_num = (uint32_t) arg;
xQueueSendFromISR(gpio_evt_queue, &gpio_num, NULL);
}

static void gpio_task_example(void* arg)
{
uint32_t io_num;
for(;;) {
if(xQueueReceive(gpio_evt_queue, &io_num, portMAX_DELAY)) {
printf("GPIO[%"PRIu32"] intr, val: %d\n", io_num, gpio_get_level(io_num));
}
}
}

void app_main(void)
{
gpio_config_t io_conf = {
.intr_type = GPIO_INTR_NEGEDGE, //falling edge interrupt
.mode = GPIO_MODE_INPUT, //set as input mode
.pin_bit_mask = 1<<GPIO_NUM_0, //bit mask of the pins GPIO0
.pull_down_en = 0, //disable pull-down mode
.pull_up_en = 1 //enable pull-up mode
};
//configure GPIO with the given settings
gpio_config(&io_conf);

//create a queue to handle gpio event from isr
gpio_evt_queue = xQueueCreate(10, sizeof(uint32_t));
//start gpio task
xTaskCreate(gpio_task_example, "gpio_task_example", 2048, NULL, 10, NULL);

//install gpio isr service
gpio_install_isr_service(0);
//hook isr handler for specific gpio pin
gpio_isr_handler_add(GPIO_NUM_0, gpio_isr_handler, (void*) GPIO_NUM_0);
}

姿态传感器(QMI8658)

I2C控制,地址QMI8658_SENSOR_ADDR= 0x6A

内部集成 3 轴加速度传感器和 3 轴陀螺仪传感器,支持 SPI 和 I2C 通信

I2C的频率100000

BSP_I2C_NUM为0

说明 PIN
I2C_SCL GPIO_NUM_1
I2C_SDA GPIO_NUM_2

计算倾斜角度(加速度求角度法)

配置qmi8658

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
#define  QMI8658_SENSOR_ADDR       0x6A
enum qmi8658_reg
{
QMI8658_WHO_AM_I,
......
QMI8658_I2CM_STATUS = 44,
......
QMI8658_dQW_L = 73,
......
QMI8658_RESET = 96
}
// 初始化
void qmi8658_init(void)
{
uint8_t id = 0;

qmi8658_register_read(QMI8658_WHO_AM_I, &id ,1);
while (id != 0x05)
{
vTaskDelay(1000 / portTICK_PERIOD_MS);
qmi8658_register_read(QMI8658_WHO_AM_I, &id ,1);
}
ESP_LOGI(TAG, "QMI8658 OK!");

qmi8658_register_write_byte(QMI8658_RESET, 0xb0); // 复位
vTaskDelay(10 / portTICK_PERIOD_MS);
qmi8658_register_write_byte(QMI8658_CTRL1, 0x40); // CTRL1 设置地址自动增加
qmi8658_register_write_byte(QMI8658_CTRL7, 0x03); // CTRL7 允许加速度和陀螺仪
qmi8658_register_write_byte(QMI8658_CTRL2, 0x95); // CTRL2 设置ACC 4g 250Hz
qmi8658_register_write_byte(QMI8658_CTRL3, 0xd5); // CTRL3 设置GRY 512dps 250Hz
}

// 读操作
esp_err_t qmi8658_register_read(uint8_t reg_addr, uint8_t *data, size_t len)
{
return i2c_master_write_read_device(BSP_I2C_NUM, QMI8658_SENSOR_ADDR, &reg_addr, 1, data, len, 1000 / portTICK_PERIOD_MS);
}
// 写操作
esp_err_t qmi8658_register_write_byte(uint8_t reg_addr, uint8_t data)
{
uint8_t write_buf[2] = {reg_addr, data};

return i2c_master_write_to_device(BSP_I2C_NUM, QMI8658_SENSOR_ADDR, write_buf, sizeof(write_buf), 1000 / portTICK_PERIOD_MS);
}

求取姿态数值

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
typedef struct{
int16_t acc_y;
int16_t acc_x;
int16_t acc_z;
int16_t gyr_y;
int16_t gyr_x;
int16_t gyr_z;
float AngleX;
float AngleY;
float AngleZ;
}t_sQMI8658;

// 读取加速度和陀螺仪寄存器值
void qmi8658_Read_AccAndGry(t_sQMI8658 *p)
{
uint8_t status, data_ready=0;
int16_t buf[6];

qmi8658_register_read(QMI8658_STATUS0, &status, 1); // 读状态寄存器
if (status & 0x03) // 判断加速度和陀螺仪数据是否可读
data_ready = 1;
if (data_ready == 1){ // 如果数据可读
data_ready = 0;
qmi8658_register_read(QMI8658_AX_L, (uint8_t *)buf, 12); // 读加速度和陀螺仪值
p->acc_x = buf[0];
p->acc_y = buf[1];
p->acc_z = buf[2];
p->gyr_x = buf[3];
p->gyr_y = buf[4];
p->gyr_z = buf[5];
}
}

// 获取XYZ轴的倾角值
void qmi8658_fetch_angleFromAcc(t_sQMI8658 *p)
{
float temp;

qmi8658_Read_AccAndGry(p); // 读取加速度和陀螺仪的寄存器值
// 根据寄存器值 计算倾角值 并把弧度转换成角度
temp = (float)p->acc_x / sqrt( ((float)p->acc_y * (float)p->acc_y + (float)p->acc_z * (float)p->acc_z) );
p->AngleX = atan(temp)*57.29578f; // 180/π=57.29578
temp = (float)p->acc_y / sqrt( ((float)p->acc_x * (float)p->acc_x + (float)p->acc_z * (float)p->acc_z) );
p->AngleY = atan(temp)*57.29578f; // 180/π=57.29578
temp = sqrt( ((float)p->acc_x * (float)p->acc_x + (float)p->acc_y * (float)p->acc_y) ) / (float)p->acc_z;
p->AngleZ = atan(temp)*57.29578f; // 180/π=57.29578
}

IO拓展(PAC9557)

外设拓展口,由I2C控制,地址PCA9557_SENSOR_ADDR = 0x19

1
//LCD_CS(LCD显示) 对应 PCA9557 的 IO0 引脚,PA_EN(音频es8311) 对应 IO1 引脚,DVP_PWDN(摄像头GC0308) 对应 IO2 引脚

音频输入(ES7210)

AD0接高电平,AD1接低电平,I2C地址为0x41

ES7210 连接 MIC 负责音频输入ES8311 只负责音频输出

ES7210 可以连接 4 个 MIC,开发板上连接了 3 个 MIC,MIC1 和 MIC2 接收人说话的声音MIC3 连接了 ES8311 的输出,用于回声消除。

S3芯片会做回声消除,音响在播放声音的时候,可以说话打断它

这个原理就是 ES8311 输出的信号,不仅给了喇叭,还给了 ES7210 的 MIC3 输入,ESP32 在接收到 MIC1、MIC2 和 MIC3 的声音后,可以分离出 MIC3,从而进行识别。

本部分例程

  1. 初始化I2S总线

  2. 初始化ES7120芯片

  3. 加载SD卡

  4. 录制声音

I2S的IO和寄存器配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#define EXAMPLE_ES7210_I2C_ADDR    (0x41)

/* I2C port and GPIOs */
#define EXAMPLE_I2C_NUM (0)
#define EXAMPLE_I2C_SDA_IO (1)
#define EXAMPLE_I2C_SCL_IO (2)

/* I2S port and GPIOs */
#define EXAMPLE_I2S_NUM (0)
#define EXAMPLE_I2S_MCK_IO (38)
#define EXAMPLE_I2S_BCK_IO (14)
#define EXAMPLE_I2S_WS_IO (13)
#define EXAMPLE_I2S_DI_IO (12)

/* I2S configurations */
#define EXAMPLE_I2S_TDM_FORMAT (ES7210_I2S_FMT_I2S)
#define EXAMPLE_I2S_CHAN_NUM (2)
#define EXAMPLE_I2S_SAMPLE_RATE (48000)
#define EXAMPLE_I2S_MCLK_MULTIPLE (I2S_MCLK_MULTIPLE_256)
#define EXAMPLE_I2S_SAMPLE_BITS (I2S_DATA_BIT_WIDTH_16BIT)
#define EXAMPLE_I2S_TDM_SLOT_MASK (I2S_TDM_SLOT0 | I2S_TDM_SLOT1)

标准I2S和TDM_I2S模式

I2S模式

TDM_I2S模式

ES7210工作在I2S模式时,只能采集2个通道,而工作在TDM_I2S模式时,可以采集4个通道

音频输出(ES8311)

主函数包括:

  1. i2s_driver_init() 函数初始化 i2s 接口
  2. es8311_codec_init() 函数初始化 i2c 接口并初始化 es8311 芯片
  3. pca9557_init() 函数初始化 IO 扩展芯片 pca9557
  4. pa_en() 函数用于控制音频功放的打开和关闭,IO1引脚
  5. i2s_music() 函数是创建的任务函数,用于播放音乐
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
#define PCA9557_INPUT_PORT              0x00
#define PCA9557_OUTPUT_PORT 0x01
#define PCA9557_POLARITY_INVERSION_PORT 0x02
#define PCA9557_CONFIGURATION_PORT 0x03

#define LCD_CS_GPIO BIT(0) // PCA9557_GPIO_NUM_1
#define PA_EN_GPIO BIT(1) // PCA9557_GPIO_NUM_2
#define DVP_PWDN_GPIO BIT(2) // PCA9557_GPIO_NUM_3

#define PCA9557_SENSOR_ADDR 0x19 /*!< Slave address of the MPU9250 sensor */

#define SET_BITS(_m, _s, _v) ((_v) ? (_m)|((_s)) : (_m)&~((_s)))

void pca9557_init(void);
void lcd_cs(uint8_t level);
void pa_en(uint8_t level);
void dvp_pwdn(uint8_t level);
// 初始化PCA9557 IO扩展芯片
void pca9557_init(void)
{
// 写入控制引脚默认值 DVP_PWDN=1 PA_EN = 0 LCD_CS = 1
pca9557_register_write_byte(PCA9557_OUTPUT_PORT, 0x05);
// 把PCA9557芯片的IO1 IO1 IO2设置为输出(0) 其它引脚保持默认的输入(1)
pca9557_register_write_byte(PCA9557_CONFIGURATION_PORT, 0xf8);
}

液晶显示(ST7789+FT6557)

使用SPI驱动,存储到SPIRAM中

液晶屏显示的开关有两个,一个是 esp_lcd_panel_disp_on_off(),一个是 bsp_display_backlight_on()

区别:

esp_lcd_panel_disp_on_off() 用来控制的是液晶屏的驱动芯片 ST7789 中的寄存器,这个寄存器控制液晶屏显示与否。

bsp_display_backlight_on() 用来控制液晶屏 LED 背光,通过调节 PWM 占空比调节亮度,使用的是 LEDC 外设产生的 PWM 信号。

esp_lcd_panel_swap_xy() 函数控制 xy 坐标翻转,第 2 个参数,true 表示翻转,false 表示不翻转。

esp_lcd_panel_mirror() 函数控制 xy 方向是否镜像。第 2 个参数控制 x 方向,第 3 个参数控制 y 方向,true 表示镜像,false 表示不镜像。

实验

IO42 引脚控制液晶屏的背光,低电平亮,如果 IO42 引脚输出 PWM 信号,就可以通过调节占空比,均匀的控制液晶屏的背光亮度

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#define BSP_LCD_BACKLIGHT     (GPIO_NUM_42)
#define LCD_LEDC_CH LEDC_CHANNEL_0

#define BSP_LCD_PIXEL_CLOCK_HZ (80 * 1000 * 1000)
#define BSP_LCD_SPI_NUM (SPI3_HOST)
#define LCD_CMD_BITS (8)
#define LCD_PARAM_BITS (8)
#define BSP_LCD_BITS_PER_PIXEL (16)

#define BSP_LCD_SPI_MOSI (GPIO_NUM_40)
#define BSP_LCD_SPI_CLK (GPIO_NUM_41)
#define BSP_LCD_SPI_CS (GPIO_NUM_NC)
#define BSP_LCD_DC (GPIO_NUM_39)
#define BSP_LCD_RST (GPIO_NUM_NC)

亮度调节函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
esp_err_t bsp_display_brightness_set(int brightness_percent)
{
if (brightness_percent > 100) {
brightness_percent = 100;
} else if (brightness_percent < 0) {
brightness_percent = 0;
}

ESP_LOGI(TAG, "Setting LCD backlight: %d%%", brightness_percent);
// LEDC resolution set to 10bits, thus: 100% = 1023
uint32_t duty_cycle = (1023 * brightness_percent) / 100;
BSP_ERROR_CHECK_RETURN_ERR(ledc_set_duty(LEDC_LOW_SPEED_MODE, LCD_LEDC_CH, duty_cycle));
BSP_ERROR_CHECK_RETURN_ERR(ledc_update_duty(LEDC_LOW_SPEED_MODE, LCD_LEDC_CH));

return ESP_OK;
}

摄像头(GC0308)

TIP:一般 500W 像素以下的摄像头模块,使用 DVP 接口以上的使用 MIPI 接口。MIPI 接口速度要高于 DVP 接口。

GC0308 摄像头最大分辨率 640 * 480,30W 像素,工作在 24MHz 频率下,输出 240 *320 分辨率时,可达 30 帧。

实验

PWDN 引脚控制摄像头进入待机模式和工作模式,高电平进入待机模式低电平进入工作模式

.ledc_channel.ledc_timer中,LEDC 外设用来给某个引脚产生 PWM 信号,这里可以用来产生时钟信号给摄像头的 XCLK 引脚。但是 S3 芯片用不着,因为 S3 芯片的 CAM 外设会产生 XCLK 信号。关于这一点,看 ESP32-S3 的技术参考手册可以了解到。

1
2
3
4
5
6
7
// 让摄像头显示到LCD
void app_camera_lcd(void)
{
xQueueLCDFrame = xQueueCreate(2, sizeof(camera_fb_t *));
xTaskCreatePinnedToCore(task_process_camera, "task_process_camera", 3 * 1024, NULL, 5, NULL, 1);
xTaskCreatePinnedToCore(task_process_lcd, "task_process_lcd", 4 * 1024, NULL, 5, NULL, 0);
}

创建了两个任务,一个任务是摄像头获取画面的任务,一个是液晶屏显示画面的任务。其中,还创建了一个队列信号,摄像头获取到画面,发送队列信号通知 LCD 显示。ESP32S3 是双核处理器,这两个任务,一个定义在 CPU0 上运行,一个定义在 CPU1 上运行,这样可以提高运行速度。xTaskCreatePinnedToCore()函数的最后一个参数,用来定义在哪个 CPU 上运行。

esp_camera_fb_get()函数用来获取一帧摄像头图像,并把获取到的一帧信息返回

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#define CAMERA_PIN_PWDN -1
#define CAMERA_PIN_RESET -1
#define CAMERA_PIN_XCLK 5
#define CAMERA_PIN_SIOD 1
#define CAMERA_PIN_SIOC 2

#define CAMERA_PIN_D7 9
#define CAMERA_PIN_D6 4
#define CAMERA_PIN_D5 6
#define CAMERA_PIN_D4 15
#define CAMERA_PIN_D3 17
#define CAMERA_PIN_D2 8
#define CAMERA_PIN_D1 18
#define CAMERA_PIN_D0 16
#define CAMERA_PIN_VSYNC 3
#define CAMERA_PIN_HREF 46
#define CAMERA_PIN_PCLK 7

#define XCLK_FREQ_HZ 24000000

LVGL

LVGL(Light and Versatile Graphics Library)是一个开源的图形用户界面库,旨在为嵌入式系统提供轻量级、可移植、灵活且易于使用的图形用户界面解决方案

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
// 开发板显示初始化
void bsp_lvgl_start(void)
{
/* 初始化LVGL */
lvgl_port_cfg_t lvgl_cfg = ESP_LVGL_PORT_INIT_CONFIG();
lvgl_port_init(&lvgl_cfg);

/* 初始化液晶屏 并添加LVGL接口,自己写 */
disp = bsp_display_lcd_init();

/* 初始化触摸屏 并添加LVGL接口,自己写 */
disp_indev = bsp_display_indev_init(disp);

/* 打开液晶屏背光 */
bsp_display_backlight_on();
}

void app_main(void)
{
bsp_i2c_init(); // I2C初始化
pca9557_init(); // IO扩展芯片初始化

bsp_lvgl_start(); // 初始化lvgl显示
lv_demo_benchmark(); // 调用lvgl demo
}

配置

基础配置

  • Flash = 16MB

  • 要存储MP3文件,需要设置FAT文件系统,Default block size = 4096

  • 使用SPI,需要设置SPIRAM,应用外存,Octal(8线SPI) Mode PSRAM,80MHz clock speed

  • 调用摄像头,设置CPU频率为240MHz,还有如下

  • 使用LVGL,需要设置反转颜色,Color settings下的Swap the 2 bytes of…打勾

  • 设置其他字体,Font usage可以开启 Enable Monserrat 24

yml文件

1
2
3
4
5
6
7
8
9
10
11
12
13
dependencies:
lvgl/lvgl: ~8.3.0
espressif/esp_lvgl_port: ~1.4.0 # LVGL接口
espressif/esp_lcd_touch_ft5x06: ~1.0.6 # 触摸屏驱动
chmorgan/esp-audio-player: ~1.0.7 # 音频播放
chmorgan/esp-file-iterator: 1.0.0 # 获取文件
espressif/esp_codec_dev: ~1.3.0 # 音频驱动
espressif/esp-sr: ~1.6.0 # 语音识别
espressif/zlib: ^1.3.0
espressif/esp32-camera: '*'
## Required IDF version
idf:
version: ~5.2.5

参考

【下载中心】实战派 | 立创开发板技术文档中心

【ESP32-S3的开发】| 1.初识 ESP32-S3_esp32s3引脚图详细解释-CSDN博客

components · GSM-Weather-project/gsm-weather-esp32s3-esp-idf5.0 - 码云 - 开源中国