硬件资源:摇杆模块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 

Logo

智能硬件社区聚焦AI智能硬件技术生态,汇聚嵌入式AI、物联网硬件开发者,打造交流分享平台,同步全国赛事资讯、开展 OPC 核心人才招募,助力技术落地与开发者成长。

更多推荐