每一层目录都有一个CMakeLists.txt配置文件存在。每当添加一个目录时都有对CMakeLIsts.txt文件修改
官方已经帮我们写好了很多常用外设和系统功能的驱动,头文件大多在 components/driver/include/ 和 components/esp_system/include/ 等目录下,我们可以直接使用。
例如像:
-
GPIO:
#include "driver/gpio.h" -
UART:
#include "driver/uart.h" -
I2C:
#include "driver/i2c.h"
注意:启动GDB调试时,记得把launch.json里的"environment": [{ "name": "PATH", "value": "${config:idf.customExtraPaths}" }],注释掉
LED(GPIO的使用)
与STM32的GPIO使用方法类似。
多了个gpio_init_struct.pin_bit_mask = 1ull << LED_GPIO_PIN,用于使能
代码实现
led.c
#include "led.h"
/**
* @brief 初始化LED
* @param 无
* @retval 无
*/
void led_init(void)
{
gpio_config_t gpio_init_struct = {0};
gpio_init_struct.intr_type = GPIO_INTR_DISABLE; /* 失能引脚中断 */
gpio_init_struct.mode = GPIO_MODE_INPUT_OUTPUT; /* 输入输出模式 */
gpio_init_struct.pull_up_en = GPIO_PULLUP_ENABLE; /* 使能上拉 */
gpio_init_struct.pull_down_en = GPIO_PULLDOWN_DISABLE; /* 失能下拉 */
gpio_init_struct.pin_bit_mask = 1ull << LED_GPIO_PIN; /* 设置的引脚的位掩码 */
gpio_config(&gpio_init_struct); /* 配置GPIO */
LED(1); /* 关闭LED */
}
led.h
#ifndef __LED_H_
#define __LED_H_
#include "driver/gpio.h"
/* 引脚定义 */
#define LED_GPIO_PIN GPIO_NUM_1 /* LED连接的GPIO端口 */
/* 引脚的输出的电平状态 */
enum GPIO_OUTPUT_STATE
{
PIN_RESET,
PIN_SET
};
/* LED端口定义 */
#define LED(x) \
do \
{ \
x ? gpio_set_level(LED_GPIO_PIN, PIN_SET) : gpio_set_level(LED_GPIO_PIN, PIN_RESET); \
} while (0) /* LED翻转 */
/* LED取反定义 */
#define LED_TOGGLE() \
do \
{ \
gpio_set_level(LED_GPIO_PIN, !gpio_get_level(LED_GPIO_PIN)); \
} while (0) /* LED翻转 */
/* 函数声明*/
void led_init(void); /* 初始化LED */
#endif
外部中断
ESP32与STM32的对比
相同点
-
都是:配置 IO → 配置触发方式(上升沿/下降沿) → 注册 ISR → 在 ISR 中处理。
-
ISR 里都要尽量简短,复杂逻辑放任务里处理。
不同点
-
STM32 要开时钟 + NVIC 配置,ESP32 自动管理,不用手动开时钟。
-
STM32 EXTI 一个中断线可能映射多个 IO,需要软件区分;ESP32 每个 IO 可独立注册 ISR,更灵活。
-
ESP32 ISR 必须加
IRAM_ATTR,保证 ISR 在内部 RAM 中运行;STM32 没这个要求。
代码实现
.c
#include "exit.h"
/**
* @brief 外部中断服务函数
* @param arg:中断引脚号
* @note IRAM_ATTR: 这里的IRAM_ATTR属性用于将中断处理函数存储在内部RAM中,目的在于减少延迟
* @retval 无
*/
static void IRAM_ATTR exit_gpio_isr_handler(void *arg)
{
uint32_t gpio_num = (uint32_t) arg;
if (gpio_num == BOOT_INT_GPIO_PIN)
{
LED_TOGGLE();
}
}
/**
* @brief 外部中断初始化程序
* @param 无
* @retval 无
*/
void exit_init(void)
{
gpio_config_t gpio_init_struct;
/* 配置BOOT引脚和外部中断 */
gpio_init_struct.mode = GPIO_MODE_INPUT; /* 选择为输入模式 */
gpio_init_struct.pull_up_en = GPIO_PULLUP_ENABLE; /* 上拉使能 */
gpio_init_struct.pull_down_en = GPIO_PULLDOWN_DISABLE; /* 下拉失能 */
gpio_init_struct.intr_type = GPIO_INTR_NEGEDGE; /* 下降沿触发 */
gpio_init_struct.pin_bit_mask = 1ull << BOOT_INT_GPIO_PIN; /* 配置BOOT按键引脚 */
gpio_config(&gpio_init_struct); /* 配置使能 */
/* 注册中断服务 */
gpio_install_isr_service(0);
/* 设置GPIO的中断回调函数 */
gpio_isr_handler_add(BOOT_INT_GPIO_PIN, exit_gpio_isr_handler, (void*) BOOT_INT_GPIO_PIN);
}
.h
#ifndef __EXIT_H_
#define __EXIT_H_
#include "esp_err.h"
#include <inttypes.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/queue.h"
#include "driver/gpio.h"
#include "esp_system.h"
#include "esp_log.h"
#include "sdkconfig.h"
#include "led.h"
/* 引脚定义 */
#define BOOT_INT_GPIO_PIN GPIO_NUM_0
/*IO操作*/
#define BOOT gpio_get_level(BOOT_INT_GPIO_PIN)
/* 函数声明 */
void exit_init(void); /* 外部中断初始化程序 */
#endif
串口
实现功能:串口接收到字符‘5’,反转LED状态。
熟悉下那几个常见的数据接收和发送API即可。
代码实现
usart.c
#include "usart.h"
/**
* @brief 初始化串口
* @param baudrate: 波特率, 根据自己需要设置波特率值
* @note 注意: 必须设置正确的时钟源, 否则串口波特率就会设置异常.
* @retval 无
*/
void usart_init(uint32_t baudrate)
{
uart_config_t uart_config; /* 串口配置句柄 */
uart_config.baud_rate = baudrate; /* 波特率 */
uart_config.data_bits = UART_DATA_8_BITS; /* 字长为8位数据格式 */
uart_config.parity = UART_PARITY_DISABLE; /* 无奇偶校验位 */
uart_config.stop_bits = UART_STOP_BITS_1; /* 一个停止位 */
uart_config.flow_ctrl = UART_HW_FLOWCTRL_DISABLE; /* 无硬件控制流 */
uart_config.source_clk = UART_SCLK_APB; /* 配置时钟源 */
uart_config.rx_flow_ctrl_thresh = 122; /* 硬件控制流阈值 */
uart_param_config(USART_UX, &uart_config); /* 配置uart端口 */
/* 配置uart引脚 */
uart_set_pin(USART_UX, USART_TX_GPIO_PIN, USART_RX_GPIO_PIN, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE);
/* 安装串口驱动 */
uart_driver_install(USART_UX, RX_BUF_SIZE * 2, RX_BUF_SIZE * 2, 20, NULL, 0);
}
usart.h
#ifndef _USART_H
#define _USART_H
#include <string.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "driver/uart.h"
#include "driver/uart_select.h"
#include "driver/gpio.h"
/* 引脚和串口定义 */
#define USART_UX UART_NUM_0
#define USART_TX_GPIO_PIN GPIO_NUM_43
#define USART_RX_GPIO_PIN GPIO_NUM_44
/* 串口接收相关定义 */
#define RX_BUF_SIZE 1024 /* 环形缓冲区大小 */
/* 函数声明 */
void usart_init(uint32_t baudrate); /* 初始化串口 */
#endif
main.c
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "nvs_flash.h"
#include "led.h"
#include "usart.h"
/**
* @brief 程序入口
* @param 无
* @retval 无
*/
void app_main(void)
{
esp_err_t ret;
uint8_t len = 0;
unsigned char data[RX_BUF_SIZE] = {0};
ret = nvs_flash_init(); /* 初始化NVS */
if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND)
{
ESP_ERROR_CHECK(nvs_flash_erase());
ret = nvs_flash_init();
}
led_init(); /* 初始化LED */
usart_init(115200); /* 初始化串口, 波特率115200 */
while (1)
{
uart_get_buffered_data_len(USART_UX, (size_t *)&len); /* 获取环形缓冲区数据长度 */
if (len > 0) /* 判断数据长度 */
{
memset(data, 0, RX_BUF_SIZE); /* 对缓冲区清零 */
printf("\r\n您发送的消息为:\r\n");
uart_read_bytes(USART_UX, data, len, 100); /* 读数据 */
if (data[0] == '5')
{
LED_TOGGLE();
}
uart_write_bytes(USART_UX, (const char *)data, strlen((const char *)data)); /* 写数据 */
}
// LED_TOGGLE();
vTaskDelay(50);
}
}
定时器
高精度定时器
使用流程
用 esp_timer_create 创建 → esp_timer_start_once 或 esp_timer_start_periodic 启动 → 回调函数执行任务 → esp_timer_stop/delete 释放。
实现功能:每1S反转一次LED状态
esptim.c
#include "esptim.h"
/**
* @brief 初始化高精度定时器(ESP_TIMER)
* @param tps: 定时器周期,以微妙为单位(μs),以一秒为定时器周期来执行一次定时器中断,
* 那此处tps = 1s = 1000000μs
* @retval 无
*/
void esptim_int_init(uint64_t tps)
{
esp_timer_handle_t esp_tim_handle; /* 定时器回调函数句柄 */
/* 定义一个定时器结构体 */
esp_timer_create_args_t tim_periodic_arg = {
.callback = &esptim_callback, /* 设置回调函数 */
.arg = NULL, /* 不携带参数 */
};
esp_timer_create(&tim_periodic_arg, &esp_tim_handle); /* 创建一个事件 */
esp_timer_start_periodic(esp_tim_handle, tps); /* 每周期内触发一次 */
}
/**
* @brief 定时器回调函数
* @param arg: 不携带参数
* @retval 无
*/
void esptim_callback(void *arg)
{
LED_TOGGLE();
}
esptim.h
#ifndef __ESPTIM_H_
#define __ESPTIM_H_
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "driver/gpio.h"
#include "esp_timer.h"
#include "led.h"
/* 函数声明 */
void esptim_int_init(uint64_t tps); /* 初始化初始化高分辨率定时器 */
void esptim_callback(void *arg); /* 定时器回调函数 */
#endif
main.c
/**
* @brief 程序入口
* @param 无
* @retval 无
*/
void app_main(void)
{
esp_err_t ret;
ret = nvs_flash_init(); /* 初始化NVS */
if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND)
{
ESP_ERROR_CHECK(nvs_flash_erase());
ret = nvs_flash_init();
}
led_init(); /* 初始化LED */
esptim_int_init(1000000); /* 初始化高分辨率定时器,此处设置定时器周期为1秒,
但该函数事宜微妙为单位进行计算,
故而1秒钟换算为1000000微秒 */
}
通用定时器
gtim.c
#include "gptim.h"
QueueHandle_t queue;
/**
* @brief 初始化通用定时器
* @param counts: 计数值
* @param resolution: 定时器周期,resolution = 1s = 1000000μs(此处,定时器以微秒作为计算单位,)
* @retval 无
*/
void gptim_int_init(uint16_t counts, uint32_t resolution)
{
gptimer_config_t g_tim_handle;
gptimer_alarm_config_t alarm_config;
gptimer_event_callbacks_t g_tim_callbacks;
uint64_t count;
/* 配置通用定时器 */
ESP_LOGI("GPTIMER_ALARM", "配置通用定时器"); /* 创建通用定时器句柄 */
gptimer_handle_t g_tim = NULL;
g_tim_handle.clk_src = GPTIMER_CLK_SRC_DEFAULT; /* 选择定时器时钟源 */
g_tim_handle.direction = GPTIMER_COUNT_UP; /* 递增计数模式 */
g_tim_handle.resolution_hz = resolution; /* 计数器分辨率 */
g_tim_callbacks.on_alarm = gptimer_callback; /* 注册用户回调函数 */
alarm_config.alarm_count = 1000000; /* 报警目标计数值 */
ESP_ERROR_CHECK(gptimer_new_timer(&g_tim_handle, &g_tim)); /* 创建新的通用定时器,并返回句柄 */
queue = xQueueCreate(10, sizeof(gptimer_event_t)); /* 创建一个队列,并引入一个事件 */
if (!queue)
{
ESP_LOGE("GPTIMER_ALARM", "创建队列失败"); /* 创建队列失败 */
return;
}
/* 设置和获取计数值 */
ESP_LOGI("GPTIMER_ALARM", "设置计数值");
ESP_ERROR_CHECK(gptimer_set_raw_count(g_tim, counts)); /* 设置计数值 */
ESP_LOGI("GPTIMER_ALARM", "获取计数值");
ESP_ERROR_CHECK(gptimer_get_raw_count(g_tim, &count)); /* 获取计数值 */
ESP_LOGI("GPTIMER_ALARM", "定时器计数值: %llu", count);
/* 注册事件回调函数 */
ESP_ERROR_CHECK(gptimer_register_event_callbacks(g_tim, &g_tim_callbacks, queue)); /* 配置通用定时器回调函数 */
/* 设置报警动作 */
ESP_LOGI("GPTIMER_ALARM", "使能通用定时器");
ESP_ERROR_CHECK(gptimer_enable(g_tim)); /* 使能通用定时器 */
ESP_ERROR_CHECK(gptimer_set_alarm_action(g_tim, &alarm_config)); /* 配置通用定时器报警事件 */
ESP_ERROR_CHECK(gptimer_start(g_tim)); /* 启动通用定时器 */
}
/**
* @brief 定时器回调函数
* @param 无
* @retval 无
*/
bool IRAM_ATTR gptimer_callback(gptimer_handle_t timer, const gptimer_alarm_event_data_t *edata, void *user_data)
{
BaseType_t high_task_awoken = pdFALSE;
queue = (QueueHandle_t)user_data;
/* 从事件数据中检索计数值 */
gptimer_event_t ele = {
.event_count = edata->count_value
};
/* 可选:通过操作系统队列将事件数据发送到其他任务 */
xQueueSendFromISR(queue, &ele, &high_task_awoken);
/* 重新配置报警值 */
gptimer_alarm_config_t alarm_config = {
.alarm_count = edata->alarm_value + 1000000, /* 在接下来的1秒内报警 */
};
gptimer_set_alarm_action(timer, &alarm_config);
/* 返回是否需要在ISR结束时让步 */
return high_task_awoken == pdTRUE;
}
gtim.h
#ifndef __GPTIM_H_
#define __GPTIM_H_
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/queue.h"
#include "driver/gptimer.h"
#include "esp_log.h"
/* 参数引用 */
typedef struct {
uint64_t event_count;
} gptimer_event_t;
extern QueueHandle_t queue;
/* 函数声明 */
void gptim_int_init(uint16_t counts, uint32_t resolution); /* 初始化通用定时器 */
bool IRAM_ATTR gptimer_callback(gptimer_handle_t timer, const gptimer_alarm_event_data_t *edata, void *user_data); /* 定时器回调函数 */
#endif
main.c
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "driver/gpio.h"
#include "nvs_flash.h"
#include "esp_log.h"
#include "led.h"
#include "gptim.h"
/**
* @brief 程序入口
* @param 无
* @retval 无
*/
void app_main(void)
{
uint8_t record;
esp_err_t ret;
gptimer_event_t g_tim_evente;
ret = nvs_flash_init(); /* 初始化NVS */
if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND)
{
ESP_ERROR_CHECK(nvs_flash_erase());
ret = nvs_flash_init();
}
led_init(); /* 初始化LED */
gptim_int_init(100, 1000000); /* 初始化通用定时器 */
while (1)
{
record = 1;
if (xQueueReceive(queue, &g_tim_evente, 2000))
{
ESP_LOGI("GPTIMER_ALARM", "定时器报警, 计数值: %llu", g_tim_evente.event_count); /* 打印通用定时器发生一次计数事件后获取到的值 */
record--;
}
else
{
ESP_LOGW("GPTIMER_ALARM", "错过一次计数事件");
}
}
vQueueDelete(queue);
}
看门狗
代码实现
wdt.c
#include "wdt.h"
esp_timer_handle_t esp_tim_handle; /* 定时器回调函数句柄 */
/**
* @brief 初始化任务看门狗计时器
* @param arr: 自动重装载值
* tps: 定时器周期
*/
void wdt_init(uint16_t arr, uint64_t tps)
{
/* 定义一个定时器结构体 */
esp_timer_create_args_t tim_periodic_arg = {
.callback = &wdt_isr_handler, /* 设置回调函数 */
.arg = NULL, /* 不携带参数 */
};
/* 创建定时器事件 */
esp_timer_create(&tim_periodic_arg, &esp_tim_handle); /* 创建一个事件 */
esp_timer_start_periodic(esp_tim_handle, tps); /* 每周期内触发一次 */
}
/**
* @brief 重新启动当前运行的计时器
* @param timeout: 定时器超时时间,该超时时间以微妙作为基本计算单位,故而设置超时时间为1s,则需要转换为微妙(μs),即timeout = 1s = 1000000μs
* @retval 无
*/
void restart_timer(uint64_t timeout)
{
esp_timer_restart(esp_tim_handle, timeout); /* 重新启动当前运行的计时器,用以模拟喂狗过程 */
}
/**
* @brief 看门狗回调函数
* @param arg: 无参数传入
* @retval 无
*/
void IRAM_ATTR wdt_isr_handler(void *arg)
{
esp_restart(); /* 若没有及时进行喂狗,那么芯片将一直进行复位 */
}
wdt.h
#ifndef __WDT_H_
#define __WDT_H_
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_timer.h"
/* 参数定义 */
#define TWDT_TIMEOUT_MS 3000
#define TASK_RESET_PERIOD_MS 2000
#define MAIN_DELAY_MS 10000
/* 函数声明 */
void wdt_init(uint16_t arr, uint64_t tps); /* 初始化独立看门狗 */
void restart_timer(uint64_t timeout); /* */
void IRAM_ATTR wdt_isr_handler(void *arg);
#endif
main.c
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "driver/gpio.h"
#include "nvs_flash.h"
#include "led.h"
#include "key.h"
#include "wdt.h"
/**
* @brief 程序入口
* @param 无
* @retval 无
*/
void app_main(void)
{
esp_err_t rets;
rets = nvs_flash_init(); /* 初始化NVS */
if (rets == ESP_ERR_NVS_NO_FREE_PAGES || rets == ESP_ERR_NVS_NEW_VERSION_FOUND)
{
ESP_ERROR_CHECK(nvs_flash_erase());
rets = nvs_flash_init();
}
led_init(); /* 初始化LED */
key_init(); /* 初始化按键 */
wdt_init(5000, 1000000); /* 初始化定时器 */
LED(0); /* LED灯常亮 */
while (1)
{
if (key_scan(0) == BOOT_PRES) /* 如果BOOT按下则喂狗 */
{
restart_timer(1000000); /* 喂狗 */
}
vTaskDelay(10); /* LED闪烁 */
}
}
软件PWM
乐鑫官方甚至给PWM封装了对应的结构体。
代码实现
pwm.c
#include "pwm.h"
/**
* @brief 初始化PWM
* @param resolution: PWM占空比分辨率
* freq: PWM信号频率
* @retval 无
*/
void pwm_init(uint8_t resolution, uint16_t freq)
{
ledc_timer_config_t ledc_timer = {0}; /* LEDC定时器句柄 */
ledc_channel_config_t ledc_channel = {0}; /* LEDC通道配置句柄 */
/* 配置LEDC定时器 */
ledc_timer.duty_resolution = resolution; /* PWM占空比分辨率 */
ledc_timer.freq_hz = freq; /* PWM信号频率 */
ledc_timer.speed_mode = LEDC_LOW_SPEED_MODE; /* 定时器模式 */
ledc_timer.timer_num = LEDC_PWM_TIMER; /* 定时器序号 */
ledc_timer.clk_cfg = LEDC_AUTO_CLK; /* LEDC时钟源 */
ledc_timer_config(&ledc_timer); /* 配置定时器 */
/* 配置LEDC通道 */
ledc_channel.gpio_num = LEDC_PWM_CH0_GPIO; /* LED控制器通道对应引脚 */
ledc_channel.speed_mode = LEDC_LOW_SPEED_MODE; /* LEDC高速模式 */
ledc_channel.channel = LEDC_PWM_CH0_CHANNEL; /* LEDC控制器通道号 */
ledc_channel.intr_type = LEDC_INTR_DISABLE; /* LEDC失能中断 */
ledc_channel.timer_sel = LEDC_PWM_TIMER; /* 定时器序号 */
ledc_channel.duty = 0; /* 占空比值 */
ledc_channel_config(&ledc_channel); /* 配置LEDC通道 */
}
/**
* @brief PWM占空比设置
* @param duty:PWM占空比
* @retval 无
*/
void pwm_set_duty(uint16_t duty)
{
ledc_set_duty(LEDC_LOW_SPEED_MODE, LEDC_PWM_CH0_CHANNEL, duty); /* 设置占空比 */
ledc_update_duty(LEDC_LOW_SPEED_MODE, LEDC_PWM_CH0_CHANNEL); /* 更新占空比 */
}
pwm.h
#ifndef __PWM_H_
#define __PWM_H_
#include <stdio.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/semphr.h"
#include "driver/ledc.h"
#include "driver/gpio.h"
/* 引脚以及重要参数定义 */
#define LEDC_PWM_TIMER LEDC_TIMER_1 /* 使用定时器0 */
#define LEDC_PWM_CH0_GPIO GPIO_NUM_1 /* LED控制器通道对应GPIO */
#define LEDC_PWM_CH0_CHANNEL LEDC_CHANNEL_1 /* LED控制器通道号 */
/* 函数声明 */
void pwm_init(uint8_t resolution, uint16_t freq); /* 初始化PWM */
void pwm_set_duty(uint16_t duty); /* PWM占空比设置 */
#endif
main.c示例
#include "nvs_flash.h"
#include "pwm.h"
/**
* @brief 程序入口
* @param 无
* @retval 无
*/
void app_main(void)
{
esp_err_t ret;
uint8_t dir = 1;
uint16_t ledpwmval = 0;
ret = nvs_flash_init(); /* 初始化NVS */
if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND)
{
ESP_ERROR_CHECK(nvs_flash_erase());
ret = nvs_flash_init();
}
pwm_init(10, 1000); /* 初始化PWM */
while(1)
{
vTaskDelay(10);
if (dir == 1)
{
ledpwmval += 5; /* dir==1 ledpwmval递增 */
}
else
{
ledpwmval -= 5; /* dir==0 ledpwmval递减 */
}
if (ledpwmval > 1005)
{
dir = 0; /* ledpwmval到达1005后,方向为递减 */
}
if (ledpwmval < 5)
{
dir = 1; /* ledpwmval递减到5后,方向改为递增 */
}
/* 设置占空比 */
pwm_set_duty(ledpwmval);
}
}
硬件PWM
代码实现
pwm.c
#include "pwm.h"
/**
* @brief 初始化PWM
* @param resolution:PWM占空比分辨率
* freq: PWM信号频率
* @retval 无
*/
void pwm_init(uint8_t resolution, uint16_t freq)
{
ledc_timer_config_t ledc_timer; /* LEDC定时器句柄 */
ledc_channel_config_t ledc_channel; /* LEDC通道配置句柄 */
/* 配置LEDC定时器 */
ledc_timer.duty_resolution = resolution; /* PWM占空比分辨率 */
ledc_timer.freq_hz = freq; /* PWM信号频率 */
ledc_timer.speed_mode = LEDC_PWM_MODE; /* 定时器模式 */
ledc_timer.timer_num = LEDC_PWM_TIMER; /* 定时器序号 */
ledc_timer.clk_cfg = LEDC_AUTO_CLK; /* LEDC时钟源 */
ledc_timer_config(&ledc_timer); /* 配置定时器 */
/* 配置LEDC通道 */
ledc_channel.gpio_num = LEDC_PWM_CH0_GPIO; /* LED控制器通道对应引脚 */
ledc_channel.speed_mode = LEDC_PWM_MODE; /* LEDC高速模式 */
ledc_channel.channel = LEDC_PWM_CH0_CHANNEL; /* LEDC控制器通道号 */
ledc_channel.intr_type = LEDC_INTR_DISABLE; /* LEDC失能中断 */
ledc_channel.timer_sel = LEDC_PWM_TIMER; /* 定时器序号 */
ledc_channel.duty = 0; /* 占空比值 */
ledc_channel_config(&ledc_channel); /* 配置LEDC通道 */
ledc_fade_func_install(0); /* 使能渐变(该函数不可或缺) */
}
/**
* @brief PWM占空比设置
* @param duty:PWM占空比
* @retval 无
*/
void pwm_set_duty(uint16_t duty)
{
ledc_set_fade_with_time(LEDC_PWM_MODE, LEDC_PWM_CH0_CHANNEL, duty, LEDC_PWM_FADE_TIME); /* 设置占空比以及渐变时长 */
ledc_fade_start(LEDC_PWM_MODE, LEDC_PWM_CH0_CHANNEL, LEDC_FADE_NO_WAIT); /* 开始渐变 */
ledc_set_fade_with_time(LEDC_PWM_MODE, LEDC_PWM_CH0_CHANNEL, 0, LEDC_PWM_FADE_TIME); /* 设置占空比以及渐变时长 */
ledc_fade_start(LEDC_PWM_MODE, LEDC_PWM_CH0_CHANNEL, LEDC_FADE_NO_WAIT); /* 开始渐变 */
}
pwm.h
#ifndef __PWM_H_
#define __PWM_H_
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/semphr.h"
#include "driver/ledc.h"
#include "driver/gpio.h"
/* 引脚以及重要参数定义 */
#define LEDC_PWM_TIMER LEDC_TIMER_0 /* 使用定时器0 */
#define LEDC_PWM_MODE LEDC_LOW_SPEED_MODE /* 模式设定必须使用LEDC低速模式 */
#define LEDC_PWM_CH0_GPIO GPIO_NUM_1 /* LED控制器通道对应GPIO */
#define LEDC_PWM_CH0_CHANNEL LEDC_CHANNEL_0 /* LED控制器通道号 */
#define LEDC_PWM_DUTY 8000 /* 渐变的变大最终目标占空比 */
#define LEDC_PWM_FADE_TIME 3000 /* 变化时长 */
/* 函数声明 */
void pwm_init(uint8_t resolution, uint16_t freq); /* 初始化PWM */
void pwm_set_duty(uint16_t duty); /* PWM占空比设置 */
#endif
main.c示例
#include "nvs_flash.h"
#include "pwm.h"
/**
* @brief 程序入口
* @param 无
* @retval 无
*/
void app_main(void)
{
esp_err_t ret;
ret = nvs_flash_init(); /* 初始化NVS */
if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND)
{
ESP_ERROR_CHECK(nvs_flash_erase());
ret = nvs_flash_init();
}
pwm_init(13, 5000); /* 初始化PWM */
while(1)
{
vTaskDelay(10);
pwm_set_duty(LEDC_PWM_DUTY); /* 设置占空比 */
}
}
ADC
完全就是调用官方封装好的库函数。
代码实现
adc1.c
#include "adc1.h"
/**
* @brief 初始化ADC
* @param 无
* @retval 无
*/
void adc_init(void)
{
adc_digi_pattern_config_t adc1_digi_pattern_config; /* ADC1配置句柄 */
adc_digi_configuration_t adc1_init_config; /* ADC1初始化句柄 */
/* 配置ADC1 */
adc1_digi_pattern_config.atten = ADC_ATTEN_DB_11; /* 配置ADC衰减程度 */
adc1_digi_pattern_config.channel = ADC_ADCX_CHY; /* 配置ADC通道 */
adc1_digi_pattern_config.unit = ADC_UNIT_1; /* 配置ADC单元 */
adc1_digi_pattern_config.bit_width = ADC_BITWIDTH_12; /* 配置ADC位宽 */
adc1_init_config.adc_pattern = &adc1_digi_pattern_config; /* 配置将要使用的每个ADC参数 */
adc_digi_controller_configure(&adc1_init_config); /* 配置ADC1 */
}
/**
* @brief 获取ADC转换且进行均值滤波后的结果
* @param ch : 通道号, 0~9
* @param times : 获取次数
* @retval 通道ch的times次转换结果平均值
*/
uint32_t adc_get_result_average(uint32_t ch, uint32_t times)
{
uint32_t temp_val = 0;
uint8_t t;
for (t = 0; t < times; t++) /* 获取times次数据 */
{
temp_val += adc1_get_raw(ch);
vTaskDelay(5);
}
return temp_val / times; /* 返回平均值 */
}
adc1.h
#ifndef __ADC_H_
#define __ADC_H_
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "driver/gpio.h"
#include "driver/adc.h"
#include "esp_log.h"
#include "esp_adc/adc_oneshot.h"
#include "esp_adc/adc_cali.h"
#include "esp_adc/adc_cali_scheme.h"
#define ADC_ADCX_CHY ADC1_CHANNEL_7
/* 函数声明 */
void adc_init(void); /* 初始化ADC */
uint32_t adc_get_result_average(uint32_t ch, uint32_t times); /* 获取ADC转换且进行均值滤波后的结果 */
#endif
RTC
代码实现
rtc.c
#include "esp_rtc.h"
_calendar_obj calendar; /* 时间结构体 */
/**
* @brief RTC设置时间
* @param year :年
* @param mon :月
* @param mday :日
* @param hour :时
* @param min :分
* @param sec :秒
* @retval 无
*/
void rtc_set_time(int year,int mon,int mday,int hour,int min,int sec)
{
struct tm datetime;
/* 设置时间 */
datetime.tm_year = year - 1900;
datetime.tm_mon = mon - 1;
datetime.tm_mday = mday;
datetime.tm_hour = hour;
datetime.tm_min = min;
datetime.tm_sec = sec;
datetime.tm_isdst = -1;
/* 获取1970.1.1以来的总秒数 */
time_t second = mktime(&datetime);
struct timeval val = { .tv_sec = second, .tv_usec = 0 };
/* 设置当前时间 */
settimeofday(&val, NULL);
}
/**
* @brief 获取当前的时间
* @param 无
* @retval 无
*/
void rtc_get_time(void)
{
struct tm *datetime;
time_t second;
/* 返回自(1970.1.1 00:00:00 UTC)经过的时间(秒) */
time(&second);
datetime = localtime(&second);
calendar.hour = datetime->tm_hour; /* 时 */
calendar.min = datetime->tm_min; /* 分 */
calendar.sec = datetime->tm_sec; /* 秒 */
/* 公历年月日周 */
calendar.year = datetime->tm_year + 1900; /* 年 */
calendar.month = datetime->tm_mon + 1; /* 月 */
calendar.date = datetime->tm_mday; /* 日 */
/* 周 */
calendar.week = rtc_get_week(calendar.year, calendar.month, calendar.date);
}
/**
* @brief 将年月日时分秒转换成秒钟数
* @note 输入公历日期得到星期(起始时间为: 公元0年3月1日开始, 输入往后的任何日期, 都可以获取正确的星期)
* 使用 基姆拉尔森计算公式 计算, 原理说明见此贴:
* https://www.cnblogs.com/fengbohello/p/3264300.html
* @param syear : 年份
* @param smon : 月份
* @param sday : 日期
* @retval 0, 星期天; 1 ~ 6: 星期一 ~ 星期六
*/
uint8_t rtc_get_week(uint16_t year, uint8_t month, uint8_t day)
{
uint8_t week = 0;
if (month < 3)
{
month += 12;
--year;
}
week = (day + 1 + 2 * month + 3 * (month + 1) / 5 + year + (year >> 2) - year / 100 + year / 400) % 7;
return week;
}
rtc.h
#ifndef __ESP_RTC_H
#define __ESP_RTC_H
#include "driver/spi_master.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include <sys/time.h>
/* 时间结构体, 包括年月日周时分秒等信息 */
typedef struct
{
uint8_t hour; /* 时 */
uint8_t min; /* 分 */
uint8_t sec; /* 秒 */
/* 公历年月日周 */
uint16_t year; /* 年 */
uint8_t month; /* 月 */
uint8_t date; /* 日 */
uint8_t week; /* 周 */
} _calendar_obj;
extern _calendar_obj calendar; /* 时间结构体 */
/* 函数声明 */
void rtc_set_time(int year,int mon,int mday,int hour,int min,int sec); /* 设置时间 */
void rtc_get_time(void); /* 获取时间 */
uint8_t rtc_get_week(uint16_t year, uint8_t month, uint8_t day); /* 获取周几 */
#endif
IIC
与STM32 HAL 库API的对应关系
| 功能 | ESP32 驱动函数 | STM32 HAL 库函数 |
|---|---|---|
| 初始化 I2C | iic_init | HAL_I2C_Init |
| 单写数据 | i2c_transfer(WRITE) | HAL_I2C_Master_Transmit |
| 单读数据 | i2c_transfer(READ) | HAL_I2C_Master_Receive |
| 写地址 + 读数据 | i2c_transfer(WRITE+READ) | HAL_I2C_Mem_Read |
代码实现
iic.c
#include "iic.h"
i2c_obj_t iic_master[I2C_NUM_MAX]; /* 为IIC0和IIC1分别定义IIC控制块结构体 */
/**
* @brief 初始化IIC
* @param iic_port:I2C编号(I2C_NUM_0 / I2C_NUM_1)
* @retval IIC控制块0 / IIC控制块1
*/
i2c_obj_t iic_init(uint8_t iic_port)
{
uint8_t i;
i2c_config_t iic_config_struct = {0};
if (iic_port == I2C_NUM_0)
{
i = 0;
}
else
{
i = 1;
}
iic_master[i].port = iic_port;
iic_master[i].init_flag = ESP_FAIL;
if (iic_master[i].port == I2C_NUM_0)
{
iic_master[i].scl = IIC0_SCL_GPIO_PIN;
iic_master[i].sda = IIC0_SDA_GPIO_PIN;
}
else
{
iic_master[i].scl = IIC1_SCL_GPIO_PIN;
iic_master[i].sda = IIC1_SDA_GPIO_PIN;
}
iic_config_struct.mode = I2C_MODE_MASTER; /* 设置IIC模式-主机模式 */
iic_config_struct.sda_io_num = iic_master[i].sda; /* 设置IIC_SDA引脚 */
iic_config_struct.scl_io_num = iic_master[i].scl; /* 设置IIC_SCL引脚 */
iic_config_struct.sda_pullup_en = GPIO_PULLUP_ENABLE; /* 配置IIC_SDA引脚上拉使能 */
iic_config_struct.scl_pullup_en = GPIO_PULLUP_ENABLE; /* 配置IIC_SCL引脚上拉使能 */
iic_config_struct.master.clk_speed = IIC_FREQ; /* 设置IIC通信速率 */
i2c_param_config(iic_master[i].port, &iic_config_struct); /* 设置IIC初始化参数 */
/* 激活I2C控制器的驱动 */
iic_master[i].init_flag = i2c_driver_install(iic_master[i].port, /* 端口号 */
iic_config_struct.mode, /* 主机模式 */
I2C_MASTER_RX_BUF_DISABLE, /* 从机模式下接收缓存大小(主机模式不使用) */
I2C_MASTER_TX_BUF_DISABLE, /* 从机模式下发送缓存大小(主机模式不使用) */
0); /* 用于分配中断的标志(通常从机模式使用) */
if (iic_master[i].init_flag != ESP_OK)
{
while(1)
{
printf("%s , ret: %d", __func__, iic_master[i].init_flag);
vTaskDelay(1000);
}
}
return iic_master[i];
}
/**
* @brief IIC读写数据
* @param self:设备控制块
* @param addr:设备地址
* @param n :数据大小
* @param bufs:要发送的数据或者是读取的存储区
* @param flags:读写标志位
* @retval 无
*/
esp_err_t i2c_transfer(i2c_obj_t *self, uint16_t addr, size_t n, i2c_buf_t *bufs, unsigned int flags)
{
int data_len = 0;
esp_err_t ret = ESP_FAIL;
i2c_cmd_handle_t cmd = i2c_cmd_link_create(); /* 创建一个命令链接,将一系列待发送给从机的数据填充命令链接 */
/* 根据器件通信时序去决定flags参数,进而选择如下代码不同的执行情况 */
if (flags & I2C_FLAG_WRITE)
{
i2c_master_start(cmd); /* 启动位 */
i2c_master_write_byte(cmd, addr << 1, ACK_CHECK_EN); /* 从机地址 + 写操作位 */
i2c_master_write(cmd, bufs->buf, bufs->len, ACK_CHECK_EN); /* len个数据 */
data_len += bufs->len;
--n;
++bufs;
}
i2c_master_start(cmd); /* 启动位 */
i2c_master_write_byte(cmd, addr << 1 | (flags & I2C_FLAG_READ), ACK_CHECK_EN); /* 从机地址 + 读/写操作位 */
for (; n--; ++bufs)
{
if (flags & I2C_FLAG_READ)
{
i2c_master_read(cmd, bufs->buf, bufs->len, n == 0 ? I2C_MASTER_LAST_NACK : I2C_MASTER_ACK); /* 读取数据 */
}
else
{
if (bufs->len != 0)
{
i2c_master_write(cmd, bufs->buf, bufs->len, ACK_CHECK_EN); /* len个数据 */
}
}
data_len += bufs->len;
}
if (flags & I2C_FLAG_STOP)
{
i2c_master_stop(cmd); /* 停止位 */
}
ret = i2c_master_cmd_begin(self->port, cmd, 100 * (1 + data_len) / portTICK_PERIOD_MS); /* 触发I2C控制器执行命令链接,即命令发送 */
i2c_cmd_link_delete(cmd); /* 释放命令链接使用的资源 */
return ret;
}
iic.h
#ifndef __IIC_H
#define __IIC_H
#include "driver/gpio.h"
#include "driver/i2c.h"
#include "esp_err.h"
/* IIC控制块 */
typedef struct _i2c_obj_t {
i2c_port_t port;
gpio_num_t scl;
gpio_num_t sda;
esp_err_t init_flag;
} i2c_obj_t;
/* 读写数据结构体 */
typedef struct _i2c_buf_t {
size_t len;
uint8_t *buf;
} i2c_buf_t;
extern i2c_obj_t iic_master[I2C_NUM_MAX];
/* 读写标志位 */
#define I2C_FLAG_READ (0x01) /* 读标志 */
#define I2C_FLAG_STOP (0x02) /* 停止标志 */
#define I2C_FLAG_WRITE (0x04) /* 写标志 */
/* 引脚与相关参数定义 */
#define IIC0_SDA_GPIO_PIN GPIO_NUM_41 /* IIC0_SDA引脚 */
#define IIC0_SCL_GPIO_PIN GPIO_NUM_42 /* IIC0_SCL引脚 */
#define IIC1_SDA_GPIO_PIN GPIO_NUM_5 /* IIC1_SDA引脚 */
#define IIC1_SCL_GPIO_PIN GPIO_NUM_4 /* IIC1_SCL引脚 */
#define IIC_FREQ 400000 /* IIC通信频率 */
#define I2C_MASTER_TX_BUF_DISABLE 0 /* I2C主机不需要缓冲区 */
#define I2C_MASTER_RX_BUF_DISABLE 0 /* I2C主机不需要缓冲区 */
#define ACK_CHECK_EN 0x1 /* I2C master将从slave检查ACK */
/* 函数声明 */
i2c_obj_t iic_init(uint8_t iic_port); /* 初始化IIC */
esp_err_t i2c_transfer(i2c_obj_t *self, uint16_t addr, size_t n, i2c_buf_t *bufs, unsigned int flags); /* IIC读写数据 */
#endif
SPI
代码实现
spi.c
#include "spi.h"
/**
* @brief 初始化SPI
* @param 无
* @retval 无
*/
void spi2_init(void)
{
esp_err_t ret = 0;
spi_bus_config_t spi_bus_conf = {0};
/* SPI总线配置 */
spi_bus_conf.miso_io_num = SPI_MISO_GPIO_PIN; /* SPI_MISO引脚 */
spi_bus_conf.mosi_io_num = SPI_MOSI_GPIO_PIN; /* SPI_MOSI引脚 */
spi_bus_conf.sclk_io_num = SPI_CLK_GPIO_PIN; /* SPI_SCLK引脚 */
spi_bus_conf.quadwp_io_num = -1; /* SPI写保护信号引脚,该引脚未使能 */
spi_bus_conf.quadhd_io_num = -1; /* SPI保持信号引脚,该引脚未使能 */
spi_bus_conf.max_transfer_sz = 320 * 240 * 2; /* 配置最大传输大小,以字节为单位 */
/* 初始化SPI总线 */
ret = spi_bus_initialize(SPI2_HOST, &spi_bus_conf, SPI_DMA_CH_AUTO); /* SPI总线初始化 */
ESP_ERROR_CHECK(ret); /* 校验参数值 */
}
/**
* @brief SPI发送命令
* @param handle : SPI句柄
* @param cmd : 要发送命令
* @retval 无
*/
void spi2_write_cmd(spi_device_handle_t handle, uint8_t cmd)
{
esp_err_t ret;
spi_transaction_t t = {0};
t.length = 8; /* 要传输的位数 一个字节 8位 */
t.tx_buffer = &cmd; /* 将命令填充进去 */
ret = spi_device_polling_transmit(handle, &t); /* 开始传输 */
ESP_ERROR_CHECK(ret); /* 一般不会有问题 */
}
/**
* @brief SPI发送数据
* @param handle : SPI句柄
* @param data : 要发送的数据
* @param len : 要发送的数据长度
* @retval 无
*/
void spi2_write_data(spi_device_handle_t handle, const uint8_t *data, int len)
{
esp_err_t ret;
spi_transaction_t t = {0};
if (len == 0)
{
return; /* 长度为0 没有数据要传输 */
}
t.length = len * 8; /* 要传输的位数 一个字节 8位 */
t.tx_buffer = data; /* 将命令填充进去 */
ret = spi_device_polling_transmit(handle, &t); /* 开始传输 */
ESP_ERROR_CHECK(ret); /* 一般不会有问题 */
}
/**
* @brief SPI处理数据
* @param handle : SPI句柄
* @param data : 要发送的数据
* @retval t.rx_data[0] : 接收到的数据
*/
uint8_t spi2_transfer_byte(spi_device_handle_t handle, uint8_t data)
{
spi_transaction_t t;
memset(&t, 0, sizeof(t));
t.flags = SPI_TRANS_USE_TXDATA | SPI_TRANS_USE_RXDATA;
t.length = 8;
t.tx_data[0] = data;
spi_device_transmit(handle, &t);
return t.rx_data[0];
}
spi.h
#ifndef __SPI_H
#define __SPI_H
#include <string.h>
#include "esp_log.h"
#include "driver/spi_master.h"
#include "driver/gpio.h"
/* 引脚定义 */
#define SPI_MOSI_GPIO_PIN GPIO_NUM_11 /* SPI2_MOSI */
#define SPI_CLK_GPIO_PIN GPIO_NUM_12 /* SPI2_CLK */
#define SPI_MISO_GPIO_PIN GPIO_NUM_13 /* SPI2_MISO */
/* 函数声明 */
void spi2_init(void); /* 初始化SPI2 */
void spi2_write_cmd(spi_device_handle_t handle, uint8_t cmd); /* SPI发送命令 */
void spi2_write_data(spi_device_handle_t handle, const uint8_t *data, int len); /* SPI发送数据 */
uint8_t spi2_transfer_byte(spi_device_handle_t handle, uint8_t byte); /* SPI处理数据 */
#endif
XL9555(IO拓展)
代码实现
xl9555.c
#include "xl9555.h"
i2c_obj_t xl9555_i2c_master;
static uint16_t xl9555_failed = 0;
/**
* @brief 读取XL9555的16位IO值
* @param data:读取数据的存储区
* @param len:读取数据的大小
* @retval ESP_OK:读取成功;其他:读取失败
*/
esp_err_t xl9555_read_byte(uint8_t *data, size_t len)
{
uint8_t memaddr_buf[1];
memaddr_buf[0] = XL9555_INPUT_PORT0_REG;
i2c_buf_t bufs[2] = {
{.len = 1, .buf = memaddr_buf},
{.len = len, .buf = data},
};
return i2c_transfer(&xl9555_i2c_master, XL9555_ADDR, 2, bufs, I2C_FLAG_WRITE | I2C_FLAG_READ | I2C_FLAG_STOP);
}
/**
* @brief 向XL9555写入16位IO值
* @param reg:寄存器地址
* @param data:要写入的数据
* @param len:要写入数据的大小
* @retval ESP_OK:读取成功;其他:读取失败
*/
esp_err_t xl9555_write_byte(uint8_t reg, uint8_t *data, size_t len)
{
i2c_buf_t bufs[2] = {
{.len = 1, .buf = ®},
{.len = len, .buf = data},
};
return i2c_transfer(&xl9555_i2c_master, XL9555_ADDR, 2, bufs, I2C_FLAG_STOP);
}
/**
* @brief 控制某个IO的电平
* @param pin : 控制的IO
* @param val : 电平
* @retval 返回所有IO状态
*/
uint16_t xl9555_pin_write(uint16_t pin, int val)
{
uint8_t w_data[2];
uint16_t temp = 0x0000;
xl9555_read_byte(w_data, 2);
if (pin <= GBC_KEY_IO)
{
if (val)
{
w_data[0] |= (uint8_t)(0xFF & pin);
}
else
{
w_data[0] &= ~(uint8_t)(0xFF & pin);
}
}
else
{
if (val)
{
w_data[1] |= (uint8_t)(0xFF & (pin >> 8));
}
else
{
w_data[1] &= ~(uint8_t)(0xFF & (pin >> 8));
}
}
temp = ((uint16_t)w_data[1] << 8) | w_data[0];
xl9555_write_byte(XL9555_OUTPUT_PORT0_REG, w_data, 2);
return temp;
}
/**
* @brief 获取某个IO状态
* @param pin : 要获取状态的IO
* @retval 此IO口的值(状态, 0/1)
*/
int xl9555_pin_read(uint16_t pin)
{
uint16_t ret;
uint8_t r_data[2];
xl9555_read_byte(r_data, 2);
ret = r_data[1] << 8 | r_data[0];
return (ret & pin) ? 1 : 0;
}
/**
* @brief XL9555的IO配置
* @param config_value:IO配置输入或者输出
* @retval 返回设置的数值
*/
uint16_t xl9555_ioconfig(uint16_t config_value)
{
/* 从机地址 + CMD + data1(P0) + data2(P1) */
/* P00、P01、P14、P15、P16、P17为输入,其他引脚为输出 -->1111 0000 0000 0011 注意:0为输出,1为输入*/
uint8_t data[2];
esp_err_t err;
int retry = 3;
data[0] = (uint8_t)(0xFF & config_value);
data[1] = (uint8_t)(0xFF & (config_value >> 8));
do
{
err = xl9555_write_byte(XL9555_CONFIG_PORT0_REG, data, 2);
if (err != ESP_OK)
{
retry--;
vTaskDelay(100);
ESP_LOGE("IIC", "%s configure %X failed, ret: %d", __func__, config_value, err);
xl9555_failed = 1;
if ((retry <= 0) && xl9555_failed)
{
vTaskDelay(5000);
esp_restart();
}
}
else
{
xl9555_failed = 0;
break;
}
} while (retry);
return config_value;
}
/**
* @brief 初始化XL9555
* @param 无
* @retval 无
*/
void xl9555_init(i2c_obj_t self)
{
uint8_t r_data[2];
if (self.init_flag == ESP_FAIL)
{
iic_init(I2C_NUM_0); /* 初始化IIC */
}
xl9555_i2c_master = self;
gpio_config_t gpio_init_struct = {0};
gpio_init_struct.intr_type = GPIO_INTR_DISABLE;
gpio_init_struct.mode = GPIO_MODE_INPUT;
gpio_init_struct.pin_bit_mask = (1ull << XL9555_INT_IO);
gpio_init_struct.pull_down_en = GPIO_PULLDOWN_DISABLE;
gpio_init_struct.pull_up_en = GPIO_PULLUP_ENABLE;
gpio_config(&gpio_init_struct); /* 配置XL_INT引脚 */
/* 上电先读取一次清除中断标志 */
xl9555_read_byte(r_data, 2);
xl9555_ioconfig(0xF003);
xl9555_pin_write(BEEP_IO, 1);
xl9555_pin_write(SPK_EN_IO, 1);
}
/**
* @brief 按键扫描函数
* @param mode:0->不连续;1->连续
* @retval 键值, 定义如下:
* KEY0_PRES, 1, KEY0按下
* KEY1_PRES, 2, KEY1按下
* KEY2_PRES, 3, KEY2按下
* KEY3_PRES, 4, KEY3按下
*/
uint8_t xl9555_key_scan(uint8_t mode)
{
uint8_t keyval = 0;
static uint8_t key_up = 1; /* 按键按松开标志 */
if (mode)
{
key_up = 1; /* 支持连按 */
}
if (key_up && (KEY0 == 0 || KEY1 == 0 || KEY2 == 0 || KEY3 == 0 )) /* 按键松开标志为1, 且有任意一个按键按下了 */
{
vTaskDelay(10); /* 去抖动 */
key_up = 0;
if (KEY0 == 0)
{
keyval = KEY0_PRES;
}
if (KEY1 == 0)
{
keyval = KEY1_PRES;
}
if (KEY2 == 0)
{
keyval = KEY2_PRES;
}
if (KEY3 == 0)
{
keyval = KEY3_PRES;
}
}
else if (KEY0 == 1 && KEY1 == 1 && KEY2 == 1 && KEY3 == 1) /* 没有任何按键按下, 标记按键松开 */
{
key_up = 1;
}
return keyval; /* 返回键值 */
}
xl9555.h
#ifndef __XL9555_H
#define __XL9555_H
#include "driver/gpio.h"
#include "esp_err.h"
#include "esp_log.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "iic.h"
/* 引脚与相关参数定义 */
#define XL9555_INT_IO GPIO_NUM_40 /* XL9555_INT引脚 */
#define XL9555_INT gpio_get_level(XL9555_INT_IO) /* 读取XL9555_INT的电平 */
/* XL9555命令宏 */
#define XL9555_INPUT_PORT0_REG 0 /* 输入寄存器0地址 */
#define XL9555_INPUT_PORT1_REG 1 /* 输入寄存器1地址 */
#define XL9555_OUTPUT_PORT0_REG 2 /* 输出寄存器0地址 */
#define XL9555_OUTPUT_PORT1_REG 3 /* 输出寄存器1地址 */
#define XL9555_INVERSION_PORT0_REG 4 /* 极性反转寄存器0地址 */
#define XL9555_INVERSION_PORT1_REG 5 /* 极性反转寄存器1地址 */
#define XL9555_CONFIG_PORT0_REG 6 /* 方向配置寄存器0地址 */
#define XL9555_CONFIG_PORT1_REG 7 /* 方向配置寄存器1地址 */
#define XL9555_ADDR 0X20 /* XL9555地址(左移了一位)-->请看手册(9.1. Device Address) */
/* XL9555各个IO的功能 */
#define AP_INT_IO 0x0001
#define QMA_INT_IO 0x0002
#define SPK_EN_IO 0x0004
#define BEEP_IO 0x0008
#define OV_PWDN_IO 0x0010
#define OV_RESET_IO 0x0020
#define GBC_LED_IO 0x0040
#define GBC_KEY_IO 0x0080
#define LCD_BL_IO 0x0100
#define CT_RST_IO 0x0200
#define SLCD_RST_IO 0x0400
#define SLCD_PWR_IO 0x0800
#define KEY3_IO 0x1000
#define KEY2_IO 0x2000
#define KEY1_IO 0x4000
#define KEY0_IO 0x8000
#define KEY0 xl9555_pin_read(KEY0_IO) /* 读取KEY0引脚 */
#define KEY1 xl9555_pin_read(KEY1_IO) /* 读取KEY1引脚 */
#define KEY2 xl9555_pin_read(KEY2_IO) /* 读取KEY2引脚 */
#define KEY3 xl9555_pin_read(KEY3_IO) /* 读取KEY3引脚 */
#define KEY0_PRES 1 /* KEY0按下 */
#define KEY1_PRES 2 /* KEY1按下 */
#define KEY2_PRES 3 /* KEY1按下 */
#define KEY3_PRES 4 /* KEY1按下 */
/* 函数声明 */
void xl9555_init(i2c_obj_t self); /* 初始化XL9555 */
int xl9555_pin_read(uint16_t pin); /* 获取某个IO状态 */
uint16_t xl9555_pin_write(uint16_t pin, int val); /* 控制某个IO的电平 */
esp_err_t xl9555_read_byte(uint8_t* data, size_t len); /* 读取XL9555的16位IO值 */
uint8_t xl9555_key_scan(uint8_t mode); /* 扫描按键值 */
#endif

6733

被折叠的 条评论
为什么被折叠?



