当前位置: 首页 > news >正文

GD32F407VE天空星开发板的ADC按键(ADKey)的实现

GD32F407VE天空星开发板的ADC按键(ADKey)的实现

一、ADC按键概述

1.1 什么是ADC按键?

ADC按键(ADKey)是一种基于电压分压原理的多按键检测方案。与传统矩阵键盘相比,它只需要一个ADC引脚即可检测多个按键状态,特别适合IO引脚有限的MCU应用场景。

1.2 技术优势

优势具体说明
省IO口只需1个ADC引脚检测多个按键,适合IO紧张的MCU
电路简单无需复杂的行列扫描电路,布线简单
成本低仅需电阻+按钮+ADC,BOM成本低
软件简单不需要复杂扫描算法,只需判断ADC电压范围

1.3 硬件原理

在这里插入图片描述

分压原理表:

按键状态等效电阻理论电压值ADC值(12位)
无按键3.30V4095
SW1R4 = 3kΩ≈ 0.75V940
SW2R5 = 10kΩ≈ 1.65V2060
SW3R6 = 33kΩ≈ 2.48V3140
SW1+SW2R4//R5≈2.31kΩ≈ 0.61V770
SW1+SW3R4//R6≈2.77kΩ≈ 0.68V880
SW2+SW3R5//R6≈7.67kΩ≈ 1.43V1780
全按下R4//R5//R6≈2.15kΩ≈ 0.59V730

二、硬件连接

2.1 接线说明

//////////////////// ADKey ////////////////////
/*  接线说明
交互板           GD32F407K  ===>  PA2//可以接任意的adc采样引脚就好3v3 ===>  3v3GND ===>  GND
*/

2.2 引脚配置

// IN2 PA2
#define ADKEY_RCU           RCU_GPIOA
#define ADKEY_PIN           GPIOA, GPIO_PIN_2
#define ADKEY_CHN           ADC_CHANNEL_2
#define ADKEY_SAMPLETIME    ADC_SAMPLETIME_84

三、ADC驱动实现

3.1 ADC配置

// ADC0.h
#define ADC0_CHNS_LEN       1   // 转换通道个数
#define ADC0_CHNS           { ADC_CHANNEL_2 }  // 通道列表
#define ADC0_CHNS_SAMPLES   { ADC_SAMPLETIME_84 }  // 采样时间// ADC0.c
void ADC0_init() {adc_deinit();  // 重置ADCadc_clock_config(ADC0_CLK);  // 配置时钟// GPIO配置为模拟输入for(uint8_t i = 0; i < ADC0_CHNS_LEN; i++) { if(adc_chns[i] == ADC_CHANNEL_2) {GPIO_analog(CH2_PIN);  // PA2配置为模拟输入}}// ADC基本配置adc_resolution_config(ADC0, ADC0_RESOLUTION);  // 12位分辨率adc_data_alignment_config(ADC0, ADC_DATAALIGN_RIGHT);  // 数据右对齐// 规则通道配置adc_channel_length_config(ADC0, ADC_ROUTINE_CHANNEL, ADC0_CHNS_LEN);for(uint8_t i = 0; i < ADC0_CHNS_LEN; i++) {adc_routine_channel_config(ADC0, i, adc_chns[i], adc_chns_samples[i]);}// 模式配置adc_special_function_config(ADC0, ADC_SCAN_MODE, ENABLE);  // 扫描模式adc_special_function_config(ADC0, ADC_CONTINUOUS_MODE, DISABLE);  // 非连续模式// DMA配置adc_dma_request_after_last_enable(ADC0);adc_dma_mode_enable(ADC0);// 启用ADCadc_enable(ADC0);adc_calibration_enable(ADC0);  // 校准delay_1ms(CALIB_TIME);DMA_config();  // DMA配置
}

3.2 DMA配置

// ADC0.c
static void DMA_config() {rcu_periph_clock_enable(RCU_DMA1);dma_deinit(ADC0_DMA_CH);dma_single_data_parameter_struct init_struct;dma_single_data_para_struct_init(&init_struct);// DMA参数配置init_struct.direction = DMA_PERIPH_TO_MEMORY;  // 外设到内存init_struct.periph_addr = ADC0_DMA_PERIPH_ADDR;  // ADC数据寄存器地址init_struct.periph_inc = DMA_PERIPH_INCREASE_DISABLE;init_struct.memory0_addr = (uint32_t)g_recv_buff;  // 内存缓冲区init_struct.memory_inc = DMA_MEMORY_INCREASE_ENABLE;init_struct.periph_memory_width = DMA_PERIPH_WIDTH_16BIT;init_struct.number = ADC0_CHNS_LEN;init_struct.priority = DMA_PRIORITY_HIGH;init_struct.circular_mode = DMA_CIRCULAR_MODE_ENABLE;  // 循环模式dma_single_data_mode_init(ADC0_DMA_CH, &init_struct);dma_channel_subperipheral_select(ADC0_DMA_CH, ADC0_DMA_SUB_PERIPH);dma_flag_clear(ADC0_DMA_CH, DMA_FLAG_FTF);dma_channel_enable(ADC0_DMA_CH);  // 启动DMA
}

3.3 ADC数据读取

// ADC0.c
uint16_t ADC0_get(uint8_t index) {adc_software_trigger_enable(ADC0, ADC_ROUTINE_CHANNEL);  // 软件触发转换// 等待DMA传输完成while(RESET == dma_flag_get(ADC0_DMA_CH, DMA_FLAG_FTF));dma_flag_clear(ADC0_DMA_CH, DMA_FLAG_FTF);return g_recv_buff[index];  // 返回采样值
}

四、按键检测算法

4.1 按键值定义

// bsp_adkey.c
// 按键按下时ADC的理论值
#define KEY1_VALUE                  950
#define KEY2_VALUE                  2060
#define KEY3_VALUE                  3140
#define KEY1_KEY2_VALUE             770
#define KEY1_KEY3_VALUE             880
#define KEY2_KEY3_VALUE             1780
#define KEY1_KEY2_KEY3_VALUE        730
#define KEY_ALL_UP                  4095#define KEY_RANGE   15  // 判断范围±15
#define KEY1        (1 << 0)  // 按键1状态位
#define KEY2        (1 << 1)  // 按键2状态位  
#define KEY3        (1 << 2)  // 按键3状态位// 判断ADC值是否在指定范围内
#define IS_IN(a, r) (((a) < ((r) + KEY_RANGE)) && ((a) > ((r) - KEY_RANGE)))

4.2 均值滤波算法

// 去极值平均滤波参数
#define ONT_CNT         2      // 每次采样次数
#define SAMPLE_CNT      40     // 总采样次数
#define NUM_CNT         (SAMPLE_CNT/ONT_CNT)  // 采样轮数void ADKey_scan() {static uint8_t i = 0;static uint32_t sum = 0;uint16_t value, adc;static uint16_t max_value = 0;     // 最大值static uint16_t min_value = 4096;  // 最小值uint8_t j;if (i < NUM_CNT) {// 多次采样for(j = 0; j < ONT_CNT; j++) {value = ADC0_get(0);  // 获取ADC采样值sum += value;// 更新极值if (value > max_value) max_value = value;if (value < min_value) min_value = value;}i++;} else {// 计算去极值平均值sum = sum - max_value - min_value;adc = sum / (SAMPLE_CNT-2);// 重置采样状态i = 0;sum = 0;max_value = 0;min_value = 4096;// 按键状态检测key_state = 0;  // 清空当前状态if (IS_IN(adc, KEY1_VALUE)) {key_state |= KEY1;} else if (IS_IN(adc, KEY2_VALUE)) {key_state |= KEY2;} else if (IS_IN(adc, KEY3_VALUE)) {key_state |= KEY3;} else if (IS_IN(adc, KEY1_KEY2_VALUE)) {key_state |= KEY1;key_state |= KEY2;} else if (IS_IN(adc, KEY1_KEY3_VALUE)) {key_state |= KEY1;key_state |= KEY3;} else if (IS_IN(adc, KEY2_KEY3_VALUE)) {key_state |= KEY2;key_state |= KEY3;} else if (IS_IN(adc, KEY1_KEY2_KEY3_VALUE)) {key_state |= KEY1;key_state |= KEY2;key_state |= KEY3;}// 状态变化检测uint8_t diff = key_state ^ last_state;  // 异或检测变化位if (diff != 0) {  // 状态有变化last_state = key_state;  // 更新上次状态// 检测每个按键的状态变化if (diff & KEY1) {if (key_state & KEY1) {printf("key1 down\n");} else {printf("key1 up\n");}}if (diff & KEY2) {if (key_state & KEY2) {printf("key2 down\n");} else {printf("key2 up\n");}}if (diff & KEY3) {if (key_state & KEY3) {printf("key3 down\n");} else {printf("key3 up\n");}}}}
}

五、任务调度系统

5.1 任务配置

// Task.c
Task_t task_list[] = {{0, 500,  App_debug_led188_test_task},     // 500ms:LED测试{0, 2,    App_debug_led188_display_task},  // 2ms:LED显示刷新{0, 1,    App_debug_adkey_scan_task},      // 1ms:按键扫描
};

5.2 按键扫描任务

// App_debug.c
void App_debug_adkey_scan_task() {ADKey_scan(); // 执行按键扫描
}

六、滤波算法详解

6.1 去极值平均滤波原理

算法步骤:

  1. 连续采样40次数据
  2. 找出最大值和最小值(极值)
  3. 去掉极值后求平均值
  4. 用平均值进行按键状态判断

优势:

  • 有效消除偶发干扰
  • 保持响应速度
  • 计算量适中

6.2 ADC转换时间计算

阶段作用耗时
采样时间信号稳定84个ADC时钟周期
量化编码SAR转换12个ADC时钟周期(固定)
总时间一次转换96个ADC时钟周期

七、高级功能扩展

7.1 短按、长按、双击检测

虽然当前代码未实现,但可以基于现有框架扩展:

// 伪代码示例
typedef struct {uint32_t press_time;    // 按下时间uint32_t release_time;  // 释放时间  uint8_t click_count;    // 点击次数uint8_t state;          // 当前状态
} Key_Event_t;void ADCKey_on_click(uint8_t key);        // 单击回调
void ADCKey_on_long_click(uint8_t key);   // 长按回调  
void ADCKey_on_double_click(uint8_t key); // 双击回调

7.2 状态机实现

// 按键状态机
typedef enum {KEY_IDLE,       // 空闲状态KEY_PRESSED,    // 已按下KEY_RELEASED,   // 已释放KEY_LONG_PRESS, // 长按KEY_DOUBLE_WAIT // 等待第二次按下
} Key_State_t;

八、性能优化建议

8.1 采样率优化

// 根据应用场景调整采样参数
#define SAMPLE_CNT      20     // 降低采样次数提高响应速度
#define KEY_RANGE       20     // 增大判断范围提高容错性

8.2 功耗优化

// 动态调整采样频率
void adjust_sample_rate(uint8_t mode) {// 正常模式:高频采样// 休眠模式:低频采样// 根据系统状态动态调整
}

九、常见问题及解决方案

9.1 按键检测不准确

  • 问题:ADC值波动导致误检测
  • 解决:调整滤波参数,增大KEY_RANGE

9.2 响应速度慢

  • 问题:采样次数过多导致延迟
  • 解决:减少SAMPLE_CNT,优化采样算法

9.3 多按键同时按下检测

  • 问题:组合按键检测不稳定
  • 解决:精确测量组合电阻值,优化判断逻辑
http://www.dtcms.com/a/553644.html

相关文章:

  • 使用 Nginx 轻松处理跨域请求(CORS)
  • 2025 年世界职业院校技能大赛机电设备安装与运维赛道备赛
  • 网站备案帐号用织梦做模板网站
  • 我的第一份工作:996
  • 贵州网推传媒有限公司企业网站seo优
  • 矿泉水除溴化物的解决方案
  • Bugku-Web题目-cookies
  • C# 类的方法介绍
  • 打破数据孤岛:制造行业档案管理方案如何实现数据互通与协同?
  • 加强 廉政网站建设衍艺网站建设
  • 中企高呈建设网站律师在哪个网站做推广好
  • 昆山非标设计工厂6名SolidWorks设计师共享一台服务器
  • 未来之窗昭和仙君(四十八)开发商品进销存修仙版——东方仙盟筑基期
  • 佛山设计网站浙江省国有建设用地出让网站
  • 跨厂商(华为 H3C)防火墙 GRE 隧道部署
  • 抖音网站单产品网站
  • 品牌网站源码江阴响应式网站建设
  • Java中wait和await的区别
  • DBeaver连接本地MySQL、创建数据库表的基础操作
  • 优秀网站首页设计上海网站建设定制公司
  • 不只是替代,更是升级:MEMS加速度计精准捕捉振动测量?
  • Kotlin协程 -> Flow和FlowCollector接口声明的意义
  • Postman接口测试: Postman环境变量全局变量设置,多接口顺序执行详解
  • 达梦数据库到Greenplum:用ETL工具实现数据仓库迁移
  • 建设网站需要注意什么问题中国铁建最新消息
  • 【Windows】Anaconda3-2025-x86_64的安装教程,使用【notebook】和【PyCharm】测试环境
  • Rust开发之使用match和if let处理Result错误
  • 九九重阳敬桑榆 众趣科技VR漫游让养老选择更安心
  • 什么是索引?索引对查询性能的影响?
  • 【ESP32 IDF】巴发云实现HTTP OTA功能