基于ESP32的智能遥控设计
ADC1用于设备的joystick模块;一共需要使用2个摇杆,也就是4个ADC通道,这里可以使用32到39这8个中的四个IO口。I2C设备:MPU6050,OLED;按键12个:12个IO口。34,35,36,37,38,39只有输入无输出无内部上下电阻的IO口。可编程LED4个:4个I口。板子启动会短暂输出电平PWM信号的io口5,12,14,15。硬件资源:摇杆模块2个:4个ADC通道IO口;
·
硬件资源:摇杆模块2个:4个ADC通道IO口;
按键12个: 12个IO口
可编程LED,多余IO口全部用于灯的设计
电源LED1个
I2C设备:MPU6050,OLED;
蜂鸣器1个
ESP32IO口
2,4.13,16-33
34,35,36,37,38,39只有输入无输出无内部上下拉电阻的IO口
板子启动会短暂输出电平PWM信号的io口5,12,14,15
定义



ADC1用于设备的joystick模块;一共需要使用2个摇杆,也就是4个ADC通道,这里可以使用32到39这8个中的四个IO口
GPIO口5启动时需要默认上拉状态,(12,14,15)可以用作按键
软件代码
classicBT.c
#include <stdint.h>
#include <string.h>
#include <stdbool.h>
#include <stdio.h>
#include <inttypes.h>
#include "nvs.h"
#include "nvs_flash.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_log.h"
#include "esp_bt.h"
#include "esp_bt_main.h"
#include "esp_gap_bt_api.h"
#include "esp_bt_device.h"
#include "esp_spp_api.h"
#include "console_uart.h"
#include "time.h"
#include "sys/time.h"
#define SPP_TAG "SPP_INITIATOR_DEMO"
#define EXAMPLE_DEVICE_NAME "ESP_SPP_INITIATOR"
static uint32_t spp_handle;
//打开蓝牙必须定义的变量
static const esp_spp_mode_t esp_spp_mode = ESP_SPP_MODE_CB;
static const bool esp_spp_enable_l2cap_ertm = true;
static struct timeval time_new, time_old;
static long data_num = 0;
//SPP配对认证模式与设备角色
static const esp_spp_sec_t sec_mask = ESP_SPP_SEC_NONE;
static const esp_spp_role_t role_master = ESP_SPP_ROLE_MASTER;
//配对设备信息
esp_bd_addr_t peer_bd_addr = {0};
static uint8_t peer_bdname_len;
static char peer_bdname[ESP_BT_GAP_MAX_BDNAME_LEN + 1];
static const char remote_device_name[] = "JDY-31-SPP";
static const esp_bt_inq_mode_t inq_mode = ESP_BT_INQ_MODE_GENERAL_INQUIRY;
static const uint8_t inq_len = 30;
static const uint8_t inq_num_rsps = 0;
#if (SPP_SHOW_MODE == SPP_SHOW_DATA)
#define SPP_DATA_LEN 20
#else
#define SPP_DATA_LEN ESP_SPP_MAX_MTU
#endif
uint8_t spp_data[SPP_DATA_LEN];
static uint8_t *s_p_data = NULL; /* data pointer of spp_data */
static char *bda2str(uint8_t * bda, char *str, size_t size)
{
if (bda == NULL || str == NULL || size < 18) {
return NULL;
}
uint8_t *p = bda;
sprintf(str, "%02x:%02x:%02x:%02x:%02x:%02x",
p[0], p[1], p[2], p[3], p[4], p[5]);
return str;
}
//打印速度函数
static void print_speed(void)
{
float time_old_s = time_old.tv_sec + time_old.tv_usec / 1000000.0;
float time_new_s = time_new.tv_sec + time_new.tv_usec / 1000000.0;
float time_interval = time_new_s - time_old_s;
float speed = data_num * 8 / time_interval / 1000.0;
ESP_LOGI(SPP_TAG, "speed(%fs ~ %fs): %f kbit/s" , time_old_s, time_new_s, speed);
data_num = 0;
time_old.tv_sec = time_new.tv_sec;
time_old.tv_usec = time_new.tv_usec;
}
//从蓝牙设备的扩展查询响应(EIR)数据中提取设备名称。EIR数据是蓝牙设备在广播时发送的附加信息,包含了设备的各种属性,如设备名称、服务UUID等。
static bool get_name_from_eir(uint8_t *eir, char *bdname, uint8_t *bdname_len)
{
uint8_t *rmt_bdname = NULL;
uint8_t rmt_bdname_len = 0;
if (!eir) {
return false;
}
rmt_bdname = esp_bt_gap_resolve_eir_data(eir, ESP_BT_EIR_TYPE_CMPL_LOCAL_NAME, &rmt_bdname_len);
if (!rmt_bdname) {
rmt_bdname = esp_bt_gap_resolve_eir_data(eir, ESP_BT_EIR_TYPE_SHORT_LOCAL_NAME, &rmt_bdname_len);
}
if (rmt_bdname) {
if (rmt_bdname_len > ESP_BT_GAP_MAX_BDNAME_LEN) {
rmt_bdname_len = ESP_BT_GAP_MAX_BDNAME_LEN;
}
if (bdname) {
memcpy(bdname, rmt_bdname, rmt_bdname_len);
bdname[rmt_bdname_len] = '\0';
}
if (bdname_len) {
*bdname_len = rmt_bdname_len;
}
return true;
}
return false;
}
//SPP事件回调函数
static void esp_spp_cb(esp_spp_cb_event_t event, esp_spp_cb_param_t *param)
{
uint8_t i = 0;
char bda_str[18] = {0};
switch (event) {
case ESP_SPP_INIT_EVT:
if (param->init.status == ESP_SPP_SUCCESS) {
ESP_LOGI(SPP_TAG, "ESP_SPP_INIT_EVT");
esp_bt_gap_set_device_name(EXAMPLE_DEVICE_NAME);
esp_bt_gap_set_scan_mode(ESP_BT_CONNECTABLE, ESP_BT_GENERAL_DISCOVERABLE);
esp_bt_gap_start_discovery(inq_mode, inq_len, inq_num_rsps);
} else {
ESP_LOGE(SPP_TAG, "ESP_SPP_INIT_EVT status:%d", param->init.status);
}
break;
case ESP_SPP_DISCOVERY_COMP_EVT:
if (param->disc_comp.status == ESP_SPP_SUCCESS) {
ESP_LOGI(SPP_TAG, "ESP_SPP_DISCOVERY_COMP_EVT scn_num:%d", param->disc_comp.scn_num);
for (i = 0; i < param->disc_comp.scn_num; i++) {
ESP_LOGI(SPP_TAG, "-- [%d] scn:%d service_name:%s", i, param->disc_comp.scn[i],
param->disc_comp.service_name[i]);
}
/* We only connect to the first found server on the remote SPP acceptor here */
esp_spp_connect(sec_mask, role_master, param->disc_comp.scn[0], peer_bd_addr);
} else {
ESP_LOGE(SPP_TAG, "ESP_SPP_DISCOVERY_COMP_EVT status=%d", param->disc_comp.status);
}
break;
case ESP_SPP_OPEN_EVT:
if (param->open.status == ESP_SPP_SUCCESS) {
ESP_LOGI(SPP_TAG, "ESP_SPP_OPEN_EVT handle:%"PRIu32" rem_bda:[%s]", param->open.handle,
bda2str(param->open.rem_bda, bda_str, sizeof(bda_str)));
/* Start to write the first data packet */
//将handle存储起来
spp_handle=param->open.handle;
esp_spp_write(param->open.handle, SPP_DATA_LEN, spp_data);
s_p_data = spp_data;
gettimeofday(&time_old, NULL);
} else {
ESP_LOGE(SPP_TAG, "ESP_SPP_OPEN_EVT status:%d", param->open.status);
}
break;
case ESP_SPP_CLOSE_EVT:
ESP_LOGI(SPP_TAG, "ESP_SPP_CLOSE_EVT status:%d handle:%"PRIu32" close_by_remote:%d", param->close.status,
param->close.handle, param->close.async);
break;
case ESP_SPP_START_EVT:
ESP_LOGI(SPP_TAG, "ESP_SPP_START_EVT");
break;
case ESP_SPP_CL_INIT_EVT:
if (param->cl_init.status == ESP_SPP_SUCCESS) {
ESP_LOGI(SPP_TAG, "ESP_SPP_CL_INIT_EVT handle:%"PRIu32" sec_id:%d", param->cl_init.handle, param->cl_init.sec_id);
} else {
ESP_LOGE(SPP_TAG, "ESP_SPP_CL_INIT_EVT status:%d", param->cl_init.status);
}
break;
case ESP_SPP_DATA_IND_EVT:
ESP_LOGI(SPP_TAG, "ESP_SPP_DATA_IND_EVT");
break;
//发生时机
// 数据发送完成:当通过SPP协议成功发送数据后,ESP_SPP_WRITE_EVT事件通常会被触发。
// 这表示数据已经从发送缓冲区被成功发送出去,并且接收端可能已经接收到这些数据(但这一点取决于通信的可靠性和接收端的处理速度)。
// 作用与意义
// 确认数据发送:ESP_SPP_WRITE_EVT事件的主要作用是确认数据已经成功发送。这对于确保通信的可靠性和完整性至关重要。
// 触发后续操作:在某些情况下,当数据发送完成后,可能需要执行一些后续操作,如更新发送状态、发送下一条数据或关闭连接等。ESP_SPP_WRITE_EVT事件可以作为一个触发点,用于执行这些后续操作。
case ESP_SPP_WRITE_EVT:
if (param->write.status == ESP_SPP_SUCCESS) {
if (s_p_data + param->write.len == spp_data + SPP_DATA_LEN) {
/* Means the previous data packet be sent completely, send a new data packet */
//数据发送完成,将数据指针重置
s_p_data = spp_data;
} else {
/*
* Means the previous data packet only be sent partially due to the lower layer congestion, resend the
* remainning data.
*/
s_p_data += param->write.len;
}
#if (SPP_SHOW_MODE == SPP_SHOW_DATA)
/*
* We only show the data in which the data length is less than 128 here. If you want to print the data and
* the data rate is high, it is strongly recommended to process them in other lower priority application task
* rather than in this callback directly. Since the printing takes too much time, it may stuck the Bluetooth
* stack and also have a effect on the throughput!
*/
ESP_LOGI(SPP_TAG, "ESP_SPP_WRITE_EVT len:%d handle:%"PRIu32" cong:%d", param->write.len, param->write.handle,
param->write.cong);
if (param->write.len < 128) {
esp_log_buffer_hex("", spp_data, param->write.len);
/* Delay a little to avoid the task watch dog */
vTaskDelay(pdMS_TO_TICKS(10));
}
#else
gettimeofday(&time_new, NULL);
data_num += param->write.len;
if (time_new.tv_sec - time_old.tv_sec >= 3) {
print_speed();
}
#endif
} else {
/* Means the previous data packet is not sent at all, need to send the whole data packet again. */
ESP_LOGE(SPP_TAG, "ESP_SPP_WRITE_EVT status:%d", param->write.status);
}
if (!param->write.cong) {
//发送新的数据包的逻辑
/* The lower layer is not congested, you can send the next data packet now. */
//esp_spp_write(param->write.handle, spp_data + SPP_DATA_LEN - s_p_data, s_p_data);
} else {
/*
* The lower layer is congested now, don't send the next data packet until receiving the
* ESP_SPP_CONG_EVT with param->cong.cong == 0.
*/
;
}
/*
* If you don't want to manage this complicated process, we also provide the SPP VFS mode that hides the
* implementation details. However, it is less efficient and will block the caller until all data has been sent.
*/
break;
case ESP_SPP_CONG_EVT:
#if (SPP_SHOW_MODE == SPP_SHOW_DATA)
ESP_LOGI(SPP_TAG, "ESP_SPP_CONG_EVT cong:%d", param->cong.cong);
#endif
if (param->cong.cong == 0) {
/* Send the privous (partial) data packet or the next data packet. */
esp_spp_write(param->write.handle, spp_data + SPP_DATA_LEN - s_p_data, s_p_data);
}
break;
case ESP_SPP_SRV_OPEN_EVT:
ESP_LOGI(SPP_TAG, "ESP_SPP_SRV_OPEN_EVT");
break;
case ESP_SPP_UNINIT_EVT:
ESP_LOGI(SPP_TAG, "ESP_SPP_UNINIT_EVT");
break;
default:
break;
}
}
//GAP事件回调函数
static void esp_bt_gap_cb(esp_bt_gap_cb_event_t event, esp_bt_gap_cb_param_t *param)
{
switch(event){
case ESP_BT_GAP_DISC_RES_EVT:
ESP_LOGI(SPP_TAG, "ESP_BT_GAP_DISC_RES_EVT");
esp_log_buffer_hex(SPP_TAG, param->disc_res.bda, ESP_BD_ADDR_LEN);
/* Find the target peer device name in the EIR data */
for (int i = 0; i < param->disc_res.num_prop; i++){
if (param->disc_res.prop[i].type == ESP_BT_GAP_DEV_PROP_EIR
&& get_name_from_eir(param->disc_res.prop[i].val, peer_bdname, &peer_bdname_len)){
esp_log_buffer_char(SPP_TAG, peer_bdname, peer_bdname_len);
if (strlen(remote_device_name) == peer_bdname_len
&& strncmp(peer_bdname, remote_device_name, peer_bdname_len) == 0) {
memcpy(peer_bd_addr, param->disc_res.bda, ESP_BD_ADDR_LEN);
/* Have found the target peer device, cancel the previous GAP discover procedure. And go on
* dsicovering the SPP service on the peer device */
esp_bt_gap_cancel_discovery();
esp_spp_start_discovery(peer_bd_addr);
}
}
}
break;
case ESP_BT_GAP_DISC_STATE_CHANGED_EVT:
ESP_LOGI(SPP_TAG, "ESP_BT_GAP_DISC_STATE_CHANGED_EVT");
break;
case ESP_BT_GAP_RMT_SRVCS_EVT:
ESP_LOGI(SPP_TAG, "ESP_BT_GAP_RMT_SRVCS_EVT");
break;
case ESP_BT_GAP_RMT_SRVC_REC_EVT:
ESP_LOGI(SPP_TAG, "ESP_BT_GAP_RMT_SRVC_REC_EVT");
break;
case ESP_BT_GAP_AUTH_CMPL_EVT:{
if (param->auth_cmpl.stat == ESP_BT_STATUS_SUCCESS) {
ESP_LOGI(SPP_TAG, "authentication success: %s", param->auth_cmpl.device_name);
esp_log_buffer_hex(SPP_TAG, param->auth_cmpl.bda, ESP_BD_ADDR_LEN);
} else {
ESP_LOGE(SPP_TAG, "authentication failed, status:%d", param->auth_cmpl.stat);
}
break;
}
case ESP_BT_GAP_PIN_REQ_EVT:{
ESP_LOGI(SPP_TAG, "ESP_BT_GAP_PIN_REQ_EVT min_16_digit:%d", param->pin_req.min_16_digit);
if (param->pin_req.min_16_digit) {
ESP_LOGI(SPP_TAG, "Input pin code: 0000 0000 0000 0000");
esp_bt_pin_code_t pin_code = {0};
esp_bt_gap_pin_reply(param->pin_req.bda, true, 16, pin_code);
} else {
ESP_LOGI(SPP_TAG, "Input pin code: 1234");
esp_bt_pin_code_t pin_code;
pin_code[0] = '1';
pin_code[1] = '2';
pin_code[2] = '3';
pin_code[3] = '4';
esp_bt_gap_pin_reply(param->pin_req.bda, true, 4, pin_code);
}
break;
}
#if (CONFIG_EXAMPLE_SSP_ENABLED == true)
case ESP_BT_GAP_CFM_REQ_EVT:
ESP_LOGI(SPP_TAG, "ESP_BT_GAP_CFM_REQ_EVT Please compare the numeric value: %"PRIu32, param->cfm_req.num_val);
ESP_LOGW(SPP_TAG, "To confirm the value, type `spp ok;`");
break;
case ESP_BT_GAP_KEY_NOTIF_EVT:
ESP_LOGI(SPP_TAG, "ESP_BT_GAP_KEY_NOTIF_EVT passkey:%"PRIu32, param->key_notif.passkey);
ESP_LOGW(SPP_TAG, "Waiting response...");
break;
case ESP_BT_GAP_KEY_REQ_EVT:
ESP_LOGI(SPP_TAG, "ESP_BT_GAP_KEY_REQ_EVT Please enter passkey!");
ESP_LOGW(SPP_TAG, "To input the key, type `spp key xxxxxx;`");
break;
#endif
case ESP_BT_GAP_MODE_CHG_EVT:
ESP_LOGI(SPP_TAG, "ESP_BT_GAP_MODE_CHG_EVT mode:%d", param->mode_chg.mode);
break;
default:
break;
}
}
void classicBT_Init()
{
esp_err_t ret = ESP_OK;
//地址数组
char bda_str[18] = {0};
//发送数据数组初始化
for (int i = 0; i < SPP_DATA_LEN; ++i) {
spp_data[i] = i;
}
spp_data[0] = 0xFF;
spp_data[19] = 0xFE;
ret = nvs_flash_init();
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();
}
ESP_ERROR_CHECK( ret );
// 释放蓝牙资源
ESP_ERROR_CHECK(esp_bt_controller_mem_release(ESP_BT_MODE_BLE));
// 初始化蓝牙控制器
esp_bt_controller_config_t bt_cfg = BT_CONTROLLER_INIT_CONFIG_DEFAULT();
if ((ret = esp_bt_controller_init(&bt_cfg)) != ESP_OK) {
ESP_LOGE(SPP_TAG, "%s initialize controller failed: %s", __func__, esp_err_to_name(ret));
return;
}
// 启用蓝牙控制器
if ((ret = esp_bt_controller_enable(ESP_BT_MODE_CLASSIC_BT)) != ESP_OK) {
ESP_LOGE(SPP_TAG, "%s enable controller failed: %s", __func__, esp_err_to_name(ret));
return;
}
// 初始化蓝牙协议栈
esp_bluedroid_config_t bluedroid_cfg = BT_BLUEDROID_INIT_CONFIG_DEFAULT();
#if (CONFIG_EXAMPLE_SSP_ENABLED == false)
bluedroid_cfg.ssp_en = false;
#endif
if ((ret = esp_bluedroid_init_with_cfg(&bluedroid_cfg)) != ESP_OK) {
ESP_LOGE(SPP_TAG, "%s initialize bluedroid failed: %s", __func__, esp_err_to_name(ret));
return;
}
// 启用蓝牙协议栈
if ((ret = esp_bluedroid_enable()) != ESP_OK) {
ESP_LOGE(SPP_TAG, "%s enable bluedroid failed: %s", __func__, esp_err_to_name(ret));
return;
}
// 注册蓝牙GAP事件回调函数
if ((ret = esp_bt_gap_register_callback(esp_bt_gap_cb)) != ESP_OK) {
ESP_LOGE(SPP_TAG, "%s gap register failed: %s", __func__, esp_err_to_name(ret));
return;
}
#if (CONFIG_EXAMPLE_SSP_ENABLED == true)
/* Set default parameters for Secure Simple Pairing */
esp_bt_sp_param_t param_type = ESP_BT_SP_IOCAP_MODE;
esp_bt_io_cap_t iocap = ESP_BT_IO_CAP_IN;
esp_bt_gap_set_security_param(param_type, &iocap, sizeof(uint8_t));
if (iocap == ESP_BT_IO_CAP_IN || iocap == ESP_BT_IO_CAP_IO) {
console_uart_init();
vTaskDelay(pdMS_TO_TICKS(500));
}
#endif
// 注册蓝牙SPP事件回调函数
if ((ret = esp_spp_register_callback(esp_spp_cb)) != ESP_OK) {
ESP_LOGE(SPP_TAG, "%s spp register failed: %s", __func__, esp_err_to_name(ret));
return;
}
// 初始化蓝牙SPP协议栈
esp_spp_cfg_t bt_spp_cfg = {
.mode = esp_spp_mode,
.enable_l2cap_ertm = esp_spp_enable_l2cap_ertm,
.tx_buffer_size = 0, /* Only used for ESP_SPP_MODE_VFS mode */
};
//启用蓝牙SPP协议栈
if ((ret = esp_spp_enhanced_init(&bt_spp_cfg)) != ESP_OK) {
ESP_LOGE(SPP_TAG, "%s spp init failed: %s", __func__, esp_err_to_name(ret));
return;
}
/*
* Set default parameters for Legacy Pairing
* Use variable pin, input pin code when pairing
*/
esp_bt_pin_type_t pin_type = ESP_BT_PIN_TYPE_FIXED;
esp_bt_pin_code_t pin_code;
esp_bt_gap_set_pin(pin_type, 0, pin_code);
ESP_LOGI(SPP_TAG, "Own address:[%s]", bda2str((uint8_t *)esp_bt_dev_get_address(), bda_str, sizeof(bda_str)));
}
void send_data_task_exampel(void *arg)
{
while(1)
{
vTaskDelay(pdMS_TO_TICKS(1000));
esp_spp_write(spp_handle,SPP_DATA_LEN,s_p_data);
printf("send data success!\n");
}
}
void send_data_task_create() {
xTaskCreate(send_data_task_exampel, "send_data_task_example", 2048, NULL, 10, NULL);
}
classicBT.h
#ifndef __CLASSICBT_H__
#define __CLASSICBT_H__
#include <stdint.h>
#include <stdio.h>
#include "esp_bt.h"
#include "esp_bt_main.h"
#include "esp_gap_bt_api.h"
#include "esp_bt_device.h"
#include "esp_spp_api.h"
#include "console_uart.h"
extern uint8_t spp_data[];
extern uint8_t *s_p_data ; /* data pointer of spp_data */
void classicBT_Init();
void send_data_task_create() ;
#endif
button.c
控制蜂鸣器的按钮
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "driver/gpio.h"
#include "freertos/queue.h"
#include "button.h"
#include "classicBT.h"
#define BUTTON_PIN GPIO_NUM_5 // 根据实际连接修改引脚号
void button_Init() {
gpio_config_t io_conf = {
.pin_bit_mask = (1ULL << BUTTON_PIN),
.mode = GPIO_MODE_INPUT,
.pull_up_en = GPIO_PULLUP_ENABLE,
.pull_down_en = GPIO_PULLDOWN_DISABLE,
.intr_type = GPIO_INTR_DISABLE
};
gpio_config(&io_conf);
}
//蜂鸣器按键任务,每隔20ms检测一次按键状态,检测为按下时发送0x00,检测为松开时发送0x01
//按键状态保存在spp_data[4]中
void button_task(void *pvParam) {
button_Init();
while(1)
{
vTaskDelay(pdMS_TO_TICKS(20));
if(gpio_get_level(BUTTON_PIN)==0)
{
spp_data[4]=0x00;
}
else
{
spp_data[4]=0x01;
}
}
}
void button_task_create()
{
xTaskCreate(button_task, "button_task", 2048, NULL, 10, NULL);
}
button.h
#ifndef __BUTTON_H__
#define __BUTTON_H__
void button_task(void *pvParam);
void button_task_create();
#endif
joystick.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/queue.h"
#include "soc/soc_caps.h"
#include "esp_log.h"
#include "esp_adc/adc_oneshot.h"
#include "esp_adc/adc_cali.h"
#include "esp_adc/adc_cali_scheme.h"
#include "math.h"
#include <stdio.h>
#include <math.h>
const static char *TAG = "EXAMPLE";
#define EXAMPLE_ADC1_CHAN0 ADC_CHANNEL_4
#define EXAMPLE_ADC1_CHAN1 ADC_CHANNEL_5
#define EXAMPLE_ADC_ATTEN ADC_ATTEN_DB_12
typedef struct {
int x_voltage;
int y_voltage;
} joystick_message_t ;
static int adc_raw[2][10];
static int voltage[2][10];
QueueHandle_t joystick_event_queue;
adc_oneshot_unit_handle_t adc1_handle;
bool do_calibration1_chan0 ;
bool do_calibration1_chan1 ;
adc_cali_handle_t adc1_cali_chan0_handle =NULL;
adc_cali_handle_t adc1_cali_chan1_handle =NULL;
static bool example_adc_calibration_init(adc_unit_t unit, adc_channel_t channel, adc_atten_t atten, adc_cali_handle_t *out_handle);
static void example_adc_calibration_deinit(adc_cali_handle_t handle);
void joystick_Init(void)
{
adc_oneshot_unit_init_cfg_t init_config1 = {
.unit_id = ADC_UNIT_1,
};
ESP_ERROR_CHECK(adc_oneshot_new_unit(&init_config1, &adc1_handle));
adc_oneshot_chan_cfg_t config = {
.bitwidth =ADC_BITWIDTH_DEFAULT,
.atten = EXAMPLE_ADC_ATTEN,
};
ESP_ERROR_CHECK(adc_oneshot_config_channel(adc1_handle, EXAMPLE_ADC1_CHAN0, &config));
ESP_ERROR_CHECK(adc_oneshot_config_channel(adc1_handle, EXAMPLE_ADC1_CHAN1, &config));
do_calibration1_chan0 = example_adc_calibration_init(ADC_UNIT_1, EXAMPLE_ADC1_CHAN0, EXAMPLE_ADC_ATTEN, &adc1_cali_chan0_handle);
do_calibration1_chan1 = example_adc_calibration_init(ADC_UNIT_1, EXAMPLE_ADC1_CHAN1, EXAMPLE_ADC_ATTEN, &adc1_cali_chan1_handle);
printf("----------------------%d\n",do_calibration1_chan0);
printf("----------------------%d\n",do_calibration1_chan1);
joystick_event_queue = xQueueCreate(10, sizeof(joystick_message_t));
}
void joystick_task_example(void *arg)
{
joystick_message_t jmsg;
while (1)
{
ESP_ERROR_CHECK(adc_oneshot_read(adc1_handle, EXAMPLE_ADC1_CHAN0, &adc_raw[0][0]));
ESP_LOGI(TAG, "ADC%d Channel[%d] Raw Data: %d", ADC_UNIT_1 + 1, EXAMPLE_ADC1_CHAN0, adc_raw[0][0]);
if (do_calibration1_chan0) {
ESP_ERROR_CHECK(adc_cali_raw_to_voltage(adc1_cali_chan0_handle, adc_raw[0][0], &voltage[0][0]));
jmsg.x_voltage=voltage[0][0];
ESP_LOGI(TAG, "ADC%d Channel[%d] Cali Voltage: %d mV", ADC_UNIT_1 + 1, EXAMPLE_ADC1_CHAN0, voltage[0][0]);
}
vTaskDelay(pdMS_TO_TICKS(100));
ESP_ERROR_CHECK(adc_oneshot_read(adc1_handle, EXAMPLE_ADC1_CHAN1, &adc_raw[0][1]));
ESP_LOGI(TAG, "ADC%d Channel[%d] Raw Data: %d", ADC_UNIT_1 + 1, EXAMPLE_ADC1_CHAN1, adc_raw[0][1]);
printf("%d\n",do_calibration1_chan1);
if (do_calibration1_chan1) {
ESP_ERROR_CHECK(adc_cali_raw_to_voltage(adc1_cali_chan1_handle, adc_raw[0][1], &voltage[0][1]));
jmsg.y_voltage=voltage[0][1];
ESP_LOGI(TAG, "ADC%d Channel[%d] Cali Voltage: %d mV", ADC_UNIT_1 + 1, EXAMPLE_ADC1_CHAN1, voltage[0][1]);
xQueueSend(joystick_event_queue, &jmsg, portMAX_DELAY);
}
vTaskDelay(pdMS_TO_TICKS(100));
// Tear Down
// ESP_ERROR_CHECK(adc_oneshot_del_unit(adc1_handle));
// if (do_calibration1_chan0) {
// example_adc_calibration_deinit(adc1_cali_chan0_handle);
// }
// if (do_calibration1_chan1) {
// example_adc_calibration_deinit(adc1_cali_chan1_handle);
// }
// vTaskDelete(NULL);
}
}
static bool example_adc_calibration_init(adc_unit_t unit, adc_channel_t channel, adc_atten_t atten, adc_cali_handle_t *out_handle)
{
adc_cali_handle_t handle = NULL;
esp_err_t ret = ESP_FAIL;
bool calibrated = false;
#if ADC_CALI_SCHEME_CURVE_FITTING_SUPPORTED
if (!calibrated) {
ESP_LOGI(TAG, "calibration scheme version is %s", "Curve Fitting");
adc_cali_curve_fitting_config_t cali_config = {
.unit_id = unit,
.chan = channel,
.atten = atten,
.bitwidth = ADC_BITWIDTH_DEFAULT,
};
ret = adc_cali_create_scheme_curve_fitting(&cali_config, &handle);
if (ret == ESP_OK) {
calibrated = true;
}
}
#endif
#if ADC_CALI_SCHEME_LINE_FITTING_SUPPORTED
if (!calibrated) {
ESP_LOGI(TAG, "calibration scheme version is %s", "Line Fitting");
adc_cali_line_fitting_config_t cali_config = {
.unit_id = unit,
.atten = atten,
.bitwidth = ADC_BITWIDTH_DEFAULT,
};
ret = adc_cali_create_scheme_line_fitting(&cali_config, &handle);
if (ret == ESP_OK) {
calibrated = true;
}
}
#endif
*out_handle = handle;
if (ret == ESP_OK) {
ESP_LOGI(TAG, "Calibration Success");
} else if (ret == ESP_ERR_NOT_SUPPORTED || !calibrated) {
ESP_LOGW(TAG, "eFuse not burnt, skip software calibration");
} else {
ESP_LOGE(TAG, "Invalid arg or no memory");
}
return calibrated;
}
static void example_adc_calibration_deinit(adc_cali_handle_t handle)
{
#if ADC_CALI_SCHEME_CURVE_FITTING_SUPPORTED
ESP_LOGI(TAG, "deregister %s calibration scheme", "Curve Fitting");
ESP_ERROR_CHECK(adc_cali_delete_scheme_curve_fitting(handle));
#elif ADC_CALI_SCHEME_LINE_FITTING_SUPPORTED
// ESP_LOGI(TAG, "deregister %s calibration scheme", "Line Fitting");
ESP_ERROR_CHECK(adc_cali_delete_scheme_line_fitting(handle));
#endif
}
void joystick_task_create() {
xTaskCreate(joystick_task_example, "joystick_task_example", 2048, NULL, 10, NULL);
}
//根据xy轴两个电压,输出两个轮子的速度
//轮子速度在-100到100之间
void voltage_to_speeds(int x, int y, int *left_speed, int *right_speed) {
// 检查输入值是否在有效范围内
if (x < 130 || x > 2460 || y < 130 || y > 2460) {
printf("输入值必须在140到2450之间\n");
return;
}
// 将x和y的值映射到-100到100的速度范围内
float x_speed = (float)(x - 1225) / 11.05;
float y_speed = (float)(y - 1225) / 11.05;
//计算小车轮速的基准值;为x轴映射速度和y轴映射速度平方和开根号*0.6,0.6为系数可以自己根据实际运动状态调节
float base_speed = 0.5*sqrt(pow(x_speed, 2) + pow(y_speed, 2));//+
// 计算左右轮的速度差值,两轮的速度差值由x轴的输出量单独控制系数为0.3可以自己调节
float speed_diff =0.1*x_speed ;//+
// 计算左右轮的实际速度
*left_speed = (int)(base_speed - speed_diff);
*right_speed = (int)(base_speed + speed_diff);
// 确保速度值在-100到100之间
*left_speed = (*left_speed > 100) ? 100 : (*left_speed < -100) ? -100 : *left_speed;
*right_speed = (*right_speed > 100) ? 100 : (*right_speed < -100) ? -100 : *right_speed;
if(y_speed<0)
{
*left_speed=-*left_speed;
*right_speed=-*right_speed;
}
}
// 使用示例
// joystick_Init();
// joystick_message_t jmsg;
// joystick_task_create();
// while(1)
// {
// vTaskDelay(100);
// if(xQueueReceive(joystick_event_queue, &jmsg, portMAX_DELAY)){
// printf("x_voltage:%d\n",jmsg.x_voltage);
// printf("y_voltage:%d\n",jmsg.y_voltage);
// }
// }
joystick.h
#ifndef __JOYSTICK_H__
#define __JOYSTICK_H__
#include "freertos/FreeRTOS.h"
#include "freertos/queue.h"
typedef struct {
int x_voltage;
int y_voltage;
} joystick_message_t ;
extern QueueHandle_t joystick_event_queue;
void joystick_Init(void);
void joystick_task_example(void *arg);
void joystick_task_create();
void voltage_to_speeds(int x, int y, int *left_speed, int *right_speed);
#endif
led.c
#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"
#define LED1 GPIO_NUM_2
//LED闪烁模式
/*
0:关
1:开
2:慢闪
3:快闪
*/
uint8_t LED_MODE;
void Led_Init()
{
//zero-initialize the config structure.
gpio_config_t io_conf = {};
//disable interrupt
io_conf.intr_type = GPIO_INTR_DISABLE;
//set as output mode
io_conf.mode = GPIO_MODE_INPUT_OUTPUT;
//bit mask of the pins that you want to set,e.g.GPIO18/19
io_conf.pin_bit_mask = 1ULL<< LED1;
//disable pull-down mode
io_conf.pull_down_en = 0;
//disable pull-up mode
io_conf.pull_up_en = 0;
//configure GPIO with the given settings
gpio_config(&io_conf);
}
void LED_ON()
{
gpio_set_level(LED1,1);
}
void LED_OFF()
{
gpio_set_level(LED1,0);
}
void Led_Turn()
{
gpio_set_level(LED1,!gpio_get_level(LED1));
}
void led_set_mode(uint8_t LED_NUM)
{
LED_MODE=LED_NUM;
}
void led_task_example(void* arg)
{
for (;;)
{
vTaskDelay(pdMS_TO_TICKS(20));
if(LED_MODE==0)
{
LED_OFF();
}
if(LED_MODE==1)
{
LED_ON();
}
if(LED_MODE==2)
{
LED_ON();
vTaskDelay(pdMS_TO_TICKS(1000));
LED_OFF();
vTaskDelay(pdMS_TO_TICKS(980));
}
if(LED_MODE==3)
{
LED_ON();
vTaskDelay(pdMS_TO_TICKS(500));
LED_OFF();
vTaskDelay(pdMS_TO_TICKS(480));
}
}
}
void led_task_create()
{
xTaskCreate(led_task_example, "led_task_example", 2048, NULL, 10, NULL);
}
led.h
#ifndef __LED_H__
#define __LED_H__
void Led_Init();
void LED_ON();
void LED_OFF();
void Led_Turn();
void led_task_create();
void led_task_example(void* arg);
void led_set_mode(uint8_t LDE_NUM);
#endif
mpu6050_Reg.h
#ifndef __MPU6050_REG_H
#define __MPU6050_REG_H
#define MPU6050_SMPLRT_DIV 0x19
#define MPU6050_CONFIG 0x1A
#define MPU6050_GYRO_CONFIG 0x1B
#define MPU6050_ACCEL_CONFIG 0x1C
#define MPU6050_ACCEL_XOUT_H 0x3B
#define MPU6050_ACCEL_XOUT_L 0x3C
#define MPU6050_ACCEL_YOUT_H 0x3D
#define MPU6050_ACCEL_YOUT_L 0x3E
#define MPU6050_ACCEL_ZOUT_H 0x3F
#define MPU6050_ACCEL_ZOUT_L 0x40
#define MPU6050_TEMP_OUT_H 0x41
#define MPU6050_TEMP_OUT_L 0x42
#define MPU6050_GYRO_XOUT_H 0x43
#define MPU6050_GYRO_XOUT_L 0x44
#define MPU6050_GYRO_YOUT_H 0x45
#define MPU6050_GYRO_YOUT_L 0x46
#define MPU6050_GYRO_ZOUT_H 0x47
#define MPU6050_GYRO_ZOUT_L 0x48
#define MPU6050_PWR_MGMT_1 0x6B
#define MPU6050_PWR_MGMT_2 0x6C
#define MPU6050_WHO_AM_I 0x75
#endif
mpu6050.c
#include "mpu6050_Reg.h"
#include "myi2c.h"
#include <stdint.h>
#include "esp_err.h"
#include "esp_log.h"
#include "freertos/FreeRTOS.h"
#define MPU6050_ADDRESS 0xD0
void mpu6050_Init(void)
{
i2c_master_init();
int ret;
//电源管理寄存器1,取消睡眠模式,选择时钟源为X轴陀螺仪
uint8_t data=0x01;
if((ret=i2c_master_write_reg(MPU6050_ADDRESS,MPU6050_PWR_MGMT_1,&data,1))!=ESP_OK)
{
ESP_LOGE("mpu6050","mpu6050_Init failed:%d",ret);
}
//电源管理寄存器2,保持默认值0,所有轴均不待机
data=0x00;
i2c_master_write_reg(MPU6050_ADDRESS,MPU6050_PWR_MGMT_2,&data,1);
//采样率分频寄存器,配置采样率
data=0x09;
i2c_master_write_reg(MPU6050_ADDRESS,MPU6050_SMPLRT_DIV,&data,1);
//配置寄存器,配置DLPF
data=0x06;
i2c_master_write_reg(MPU6050_ADDRESS,MPU6050_CONFIG,&data,1);
// 量程选择
data=0x18;
i2c_master_write_reg(MPU6050_ADDRESS,MPU6050_GYRO_CONFIG,&data,1);
i2c_master_write_reg(MPU6050_ADDRESS,MPU6050_ACCEL_CONFIG,&data,1);
}
// 将MPU6050的ID读取到data中
esp_err_t MPU6050_GetID(uint8_t*data)
{
return i2c_master_read_reg(MPU6050_ADDRESS,MPU6050_WHO_AM_I,&data,1); //返回WHO_AM_I寄存器的值
}
// void MPU6050_GetData(int16_t *AccX, int16_t *AccY, int16_t *AccZ,
// int16_t *GyroX, int16_t *GyroY, int16_t *GyroZ)
// {
// uint8_t DataH, DataL; //定义数据高8位和低8位的变量
// DataH = MPU6050_ReadReg(MPU6050_ACCEL_XOUT_H); //读取加速度计X轴的高8位数据
// DataL = MPU6050_ReadReg(MPU6050_ACCEL_XOUT_L); //读取加速度计X轴的低8位数据
// *AccX = (DataH << 8) | DataL; //数据拼接,通过输出参数返回
// DataH = MPU6050_ReadReg(MPU6050_ACCEL_YOUT_H); //读取加速度计Y轴的高8位数据
// DataL = MPU6050_ReadReg(MPU6050_ACCEL_YOUT_L); //读取加速度计Y轴的低8位数据
// *AccY = (DataH << 8) | DataL; //数据拼接,通过输出参数返回
// DataH = MPU6050_ReadReg(MPU6050_ACCEL_ZOUT_H); //读取加速度计Z轴的高8位数据
// DataL = MPU6050_ReadReg(MPU6050_ACCEL_ZOUT_L); //读取加速度计Z轴的低8位数据
// *AccZ = (DataH << 8) | DataL; //数据拼接,通过输出参数返回
// DataH = MPU6050_ReadReg(MPU6050_GYRO_XOUT_H); //读取陀螺仪X轴的高8位数据
// DataL = MPU6050_ReadReg(MPU6050_GYRO_XOUT_L); //读取陀螺仪X轴的低8位数据
// *GyroX = (DataH << 8) | DataL; //数据拼接,通过输出参数返回
// DataH = MPU6050_ReadReg(MPU6050_GYRO_YOUT_H); //读取陀螺仪Y轴的高8位数据
// DataL = MPU6050_ReadReg(MPU6050_GYRO_YOUT_L); //读取陀螺仪Y轴的低8位数据
// *GyroY = (DataH << 8) | DataL; //数据拼接,通过输出参数返回
// DataH = MPU6050_ReadReg(MPU6050_GYRO_ZOUT_H); //读取陀螺仪Z轴的高8位数据
// DataL = MPU6050_ReadReg(MPU6050_GYRO_ZOUT_L); //读取陀螺仪Z轴的低8位数据
// *GyroZ = (DataH << 8) | DataL; //数据拼接,通过输出参数返回
// }
mpu6050.h
#ifndef __MPU6050_H__
#define __MPU6050_H__
#include "driver/i2c.h"
#include "esp_log.h"
#define MPU6050_ADDR 0x68
#define MPU6050_SMPLRT_DIV 0x19
void mpu6050_Init(void);
esp_err_t MPU6050_GetID(uint8_t*data);
#endif
myi2c.c
#include "driver/i2c.h"
#include <stdio.h>
#include "esp_log.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "mpu6050_Reg.h"
// #include "esp_mac.h"
// #include <math.h>
#define I2C_MASTER_SCL_IO 22 // GPIO number for I2C master clock
#define I2C_MASTER_SDA_IO 21 // GPIO number for I2C master data
#define I2C_MASTER_NUM I2C_NUM_0 // I2C port number
#define I2C_MASTER_FREQ_HZ 100000 // I2C master clock frequency
#define I2C_MASTER_TX_BUF_DISABLE 0 // I2C master doesn't need buffer
#define I2C_MASTER_RX_BUF_DISABLE 0 // I2C master doesn't need buffer
#define I2C_MASTER_TIMEOUT_MS 1000 // I2C master timeout in milliseconds
static const char *TAG = "I2C_DRIVER";
// /**
// * @brief Initialize I2C master
// */
void i2c_master_init()
{
int i2c_master_port = I2C_MASTER_NUM;
i2c_config_t conf = {
.mode = I2C_MODE_MASTER,
.sda_io_num = I2C_MASTER_SDA_IO,
.scl_io_num = I2C_MASTER_SCL_IO,
.sda_pullup_en = GPIO_PULLUP_ENABLE,
.scl_pullup_en = GPIO_PULLUP_ENABLE,
.master.clk_speed = I2C_MASTER_FREQ_HZ,
};
//IIC控制器编号i2c_master_port
i2c_param_config(i2c_master_port, &conf);
i2c_driver_install(i2c_master_port, conf.mode, I2C_MASTER_RX_BUF_DISABLE, I2C_MASTER_TX_BUF_DISABLE, 0);
}
// /**
// * @brief Write data to I2C slave
// *
// * @param slave_addr I2C slave address
// * @param data Pointer to data bufferdata
// * @param data_len Length of data in bytes
// * @return esp_err_t ESP_OK on success, otherwise an error code
// */
// //slave_addr 从设备地址
esp_err_t i2c_master_write_slave(uint8_t slave_addr, uint8_t *data, size_t data_len)
{
int ret;
i2c_cmd_handle_t cmd = i2c_cmd_link_create();
// i2c起始信号
i2c_master_start(cmd);
// i2c发送地址
i2c_master_write_byte(cmd, (slave_addr << 1) | I2C_MASTER_WRITE, true);
// i2c发送数据
i2c_master_write(cmd, data, data_len, true);
// i2c停止信号
i2c_master_stop(cmd);
// 发送命令,这个函数是真正实现上面代码内容的函数
ret = i2c_master_cmd_begin(I2C_MASTER_NUM, cmd, I2C_MASTER_TIMEOUT_MS/portTICK_PERIOD_MS);
i2c_cmd_link_delete(cmd);
return ret;
}
// /**
// * @brief Read data from I2C slave
// *
// * @param slave_addr I2C slave address
// * @param data Pointer to data buffer
// * @param data_len Length of data in bytes
// * @return esp_err_t ESP_OK on success, otherwise an error code
// */
esp_err_t i2c_master_read_slave(uint8_t slave_addr, uint8_t *data, size_t data_len)
{
int ret;
i2c_cmd_handle_t cmd = i2c_cmd_link_create();
i2c_master_start(cmd);
ret=i2c_master_write_byte(cmd, (slave_addr << 1) | I2C_MASTER_WRITE, true);
if(ret!=ESP_OK)
{
printf("i2c_master_write_byte error\n");
}
i2c_master_write_byte(cmd, data + data_len - 1, I2C_MASTER_NACK);
i2c_master_stop(cmd);
ret = i2c_master_cmd_begin(I2C_MASTER_NUM, cmd, I2C_MASTER_TIMEOUT_MS / portTICK_PERIOD_MS );
i2c_cmd_link_delete(cmd);
return ret;
}
esp_err_t i2c_master_write_reg(uint8_t slave_addr, uint8_t reg_addr, uint8_t *data, size_t data_len)
{
int ret;
i2c_cmd_handle_t cmd = i2c_cmd_link_create();
i2c_master_start(cmd);
i2c_master_write_byte(cmd, (slave_addr << 1) | I2C_MASTER_WRITE, true);
i2c_master_write_byte(cmd, reg_addr, true);
i2c_master_write(cmd, data, data_len, true);
i2c_master_stop(cmd);
ret = i2c_master_cmd_begin(I2C_MASTER_NUM, cmd, I2C_MASTER_TIMEOUT_MS / portTICK_PERIOD_MS );
i2c_cmd_link_delete(cmd);
return ret;
}
esp_err_t i2c_master_read_reg(uint8_t slave_addr, uint8_t reg_addr, uint8_t *data, size_t data_len)
{
int ret;
i2c_cmd_handle_t cmd = i2c_cmd_link_create();
i2c_master_start(cmd);
i2c_master_write_byte(cmd, (slave_addr << 1) | I2C_MASTER_WRITE, true);
i2c_master_write_byte(cmd, reg_addr, true);
i2c_master_start(cmd);
i2c_master_write_byte(cmd, (slave_addr << 1) | I2C_MASTER_READ, true);
if (data_len > 1) {
i2c_master_read(cmd, data, data_len - 1, I2C_MASTER_ACK);
}
i2c_master_read_byte(cmd, data + data_len - 1, I2C_MASTER_NACK);
i2c_master_stop(cmd);
ret = i2c_master_cmd_begin(I2C_MASTER_NUM, cmd, I2C_MASTER_TIMEOUT_MS / portTICK_PERIOD_MS);
i2c_cmd_link_delete(cmd);
return ret;
}
// 代码解释:
// i2c_master_init: 初始化I2C总线,设置SCL和SDA引脚,以及I2C时钟频率。
// i2c_master_write_slave: 向I2C从设备写入数据。
// i2c_master_read_slave: 从I2C从设备读取数据。
// 使用示例:
// main.c
// Apply
// void app_main(void)
// {
// // 初始化I2C总线
// i2c_master_init();
// uint8_t slave_addr = 0x3C; // I2C从设备地址
// uint8_t data_to_write[4] = {0x01, 0x02, 0x03, 0x04};
// uint8_t data_to_read[4];
// // 向从设备写入数据
// esp_err_t ret = i2c_master_write_slave(slave_addr, data_to_write, sizeof(data_to_write));
// if (ret != ESP_OK) {
// ESP_LOGE(TAG, "I2C write failed: %d", ret);
// return;
// }
// // 从从设备读取数据
// ret = i2c_master_read_slave(slave_addr, data_to_read, sizeof(data_to_read));
// if (ret != ESP_OK) {
// ESP_LOGE(TAG, "I2C read failed: %d", ret);
// return;
// }
// // 打印读取的数据
// ESP_LOGI(TAG, "Read data: 0x%02X 0x%02X 0x%02X 0x%02X", data_to_read[0], data_to_read[1], data_to_read[2], data_to_read[3]);
// }
myi2c.h
#ifndef __MYI2C_H__
#define __MYI2C_H__
#include <stdio.h>
#include "esp_log.h"
#include "esp_err.h"
void i2c_master_init();
esp_err_t i2c_master_write_slave(uint8_t slave_addr, uint8_t *data, size_t data_len);
esp_err_t i2c_master_read_slave(uint8_t slave_addr, uint8_t *data, size_t data_len);
esp_err_t i2c_master_write_reg(uint8_t slave_addr, uint8_t reg_addr, uint8_t *data, size_t data_len);
esp_err_t i2c_master_read_reg(uint8_t slave_addr, uint8_t reg_addr, uint8_t *data, size_t data_len);
#endif
key.c
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/queue.h"
#include "driver/gpio.h"
#include "esp_log.h"
#define KEY1 GPIO_NUM_4
#define KEY2 GPIO_NUM_16 //yuanbenwei5
#define KEY3 GPIO_NUM_15
#define KEY4 GPIO_NUM_19
#define KEY5 GPIO_NUM_22
#define LONG_PRESS_TIME_MS 1000 //长按时间阈值,单位为毫秒
typedef enum {
KEY_EVENT_NONE,
KEY_EVENT_CLICK,
KEY_EVENT_LONG_PRESS_START,
KEY_EVENT_LONG_PRESS_CONTINUE,
KEY_EVENT_LONG_PRESS_END
} key_event_t;
typedef struct {
uint8_t key_num;
key_event_t event;
} key_message_t;
QueueHandle_t key_event_queue;
// 中断注册
static void IRAM_ATTR gpio_isr_handler(void* arg) {
uint32_t gpio_num = (uint32_t) arg;
xQueueSendFromISR(key_event_queue, &gpio_num, NULL);
}
// 按键任务
void key_task_example(void* arg) {
uint32_t io_num;
key_message_t msg;
TickType_t press_start_time;
bool long_press_detected = false;
for (;;) {
vTaskDelay(pdMS_TO_TICKS(20));
if (xQueueReceive(key_event_queue, &io_num, portMAX_DELAY)) {
msg.key_num = io_num;
msg.event = KEY_EVENT_NONE;
press_start_time = xTaskGetTickCount();
while (gpio_get_level(io_num) == 0) {
if (xTaskGetTickCount() - press_start_time > pdMS_TO_TICKS(LONG_PRESS_TIME_MS)) {
if (!long_press_detected) {
msg.event = KEY_EVENT_LONG_PRESS_START;
long_press_detected = true;
} else {
msg.event = KEY_EVENT_LONG_PRESS_CONTINUE;
}
xQueueSend(key_event_queue, &msg, portMAX_DELAY);
}
vTaskDelay(pdMS_TO_TICKS(20));
}
if (long_press_detected) {
msg.event = KEY_EVENT_LONG_PRESS_END;
xQueueSend(key_event_queue, &msg, portMAX_DELAY);
long_press_detected = false;
} else {
msg.event = KEY_EVENT_CLICK;
xQueueSend(key_event_queue, &msg, portMAX_DELAY);
}
}
}
}
// 按键初始化
void Key_Init() {
gpio_config_t io_conf = {};
io_conf.intr_type = GPIO_INTR_NEGEDGE; // 下降沿触发中断
io_conf.mode = GPIO_MODE_INPUT;
io_conf.pin_bit_mask = (1ULL<<KEY1)|(1ULL<<KEY2)|(1ULL<<KEY3)|(1ULL<<KEY4)|(1ULL<<KEY5);
io_conf.pull_down_en = 0;
io_conf.pull_up_en = 1;
gpio_config(&io_conf);
// 创建按键事件队列
key_event_queue = xQueueCreate(10, sizeof(key_message_t));
// 注册中断服务
gpio_install_isr_service(0);
gpio_isr_handler_add(KEY1, gpio_isr_handler, (void*) KEY1);
gpio_isr_handler_add(KEY2, gpio_isr_handler, (void*) KEY2);
gpio_isr_handler_add(KEY3, gpio_isr_handler, (void*) KEY3);
gpio_isr_handler_add(KEY4, gpio_isr_handler, (void*) KEY4);
gpio_isr_handler_add(KEY5, gpio_isr_handler, (void*) KEY5);
}
// 创建按键任务
void key_task_create() {
xTaskCreate(key_task_example, "key_task_example", 2048, NULL, 10, NULL);
}
key.h
#ifndef __KEY_H__
#define __KEY_H__
#include "freertos/FreeRTOS.h"
#include "freertos/queue.h"
#include "driver/gpio.h"
#define KEY1 GPIO_NUM_4
#define KEY2 GPIO_NUM_15
#define KEY3 GPIO_NUM_17
#define KEY4 GPIO_NUM_18
#define KEY5 GPIO_NUM_22
#define LONG_PRESS_TIME_MS 1000 // 长按时间阈值,单位毫秒
typedef enum {
KEY_EVENT_NONE,
KEY_EVENT_CLICK,
KEY_EVENT_LONG_PRESS_START,
KEY_EVENT_LONG_PRESS_CONTINUE,
KEY_EVENT_LONG_PRESS_END
} key_event_t;
typedef struct {
uint8_t key_num;
key_event_t event;
} key_message_t;
extern QueueHandle_t key_event_queue;
void Key_Init();
void key_task_example(void* arg);
void key_task_create();
#endif
更多推荐



所有评论(0)