在程序设计以及硬件电路设计中,通常使用GPIO连接按键,通过检测外部按键按下的高低电平实现按键的检测。当我们需要多个按键且GPIO资源不够的时候,我们可以利用MCU的ADC功能,实现对按键的检测。
ADC按键检测电路如下:
当没有按键按下时,ADC检测IO口电压为3.3V。
当第1个按键按下时,ADC检测IO口电压为825mV,如下图:
当第2个按键按下时,ADC检测IO口的电压为1.32V,如下图:
以此类推,当第3、4、5个按键按下时,电压分别如下图:
从上图结果可知,不同按键按下,ADC会采集到不到的电压,通过该原理,可以利用ADC实现对按键的检测,具体实现代码可参考下程序:
void adc_Init(void)
{
ADC_InitTypeDef ADC_InitStructure;
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA |RCC_APB2Periph_ADC1 , ENABLE ); //使能GPIOA时钟和ADC
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;
GPIO_Init(GPIOA, &GPIO_InitStructure);
RCC_ADCCLKConfig(RCC_PCLK2_Div6); //设置ADC时钟分频为6分频
ADC_InitStructure.ADC_Mode = ADC_Mode_Independent; //配置ADC为独立模式
ADC_InitStructure.ADC_ScanConvMode = DISABLE; //设置在单通道模式下执行转换
ADC_InitStructure.ADC_ContinuousConvMode = DISABLE; //设置在单次模式下执行转换
ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None; //设置转换不是由外部触发启动
ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right; //设置ADC数据右对齐
ADC_InitStructure.ADC_NbrOfChannel = 1; //顺序进行规则转换的ADC通道的数目
ADC_Init(ADC1, &ADC_InitStructure); //根据ADC_InitStructure中指定的参数初始化ADC1寄存器
ADC_Cmd(ADC1, ENABLE); //使能ADC1
ADC_ResetCalibration(ADC1); //重置ADC1校准寄存器。
while(ADC_GetResetCalibrationStatus(ADC1)); //等待复位校准结束
ADC_StartCalibration(ADC1); //开启AD校准
while(ADC_GetCalibrationStatus(ADC1)); //等待校准结束
}
u16 get_adc(u8 ch)
{
ADC_RegularChannelConfig(ADC1, ch, 1, ADC_SampleTime_239Cycles5 );//为所选ADC常规通道配置其在序列器中的相应列组及其采样时间。
ADC_SoftwareStartConvCmd(ADC1, ENABLE); //使能ADC1软件启动转换
while(!ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC )); //等待转换结束
return ADC_GetConversionValue(ADC1); //返回常规通道的最后一个ADC1转换结果数据
}
u16 Get_Adc_Average(u8 ch,u8 times)
{
u32 temp_val=0;
u8 t;
for(t=0;t<times;t++)
{
temp_val+=get_adc(ch);
Delay_Ms(5);
}
return temp_val/times;
}
/*********************************************************************
* @fn main
*
* @brief Main program.
*
* @return none
*/
int main(void)
{
u16 adc;
u16 sw=0;
u8 key=0;
float temp;
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
Delay_Init();
USART_Printf_Init(115200);
adc_Init();
printf("SystemClk:%d\r\n",SystemCoreClock);
printf("This is printf example\r\n");
while(1)
{
adc=Get_Adc_Average(ADC_Channel_1,10);
temp=(float)adc*(3.3/4096);
// printf("adc:%d\r\n",adc);
// printf("volate:%0.2f\r\n",temp);
if(temp>1.62&&temp<1.66)
{
sw=1;
}
else if(temp>2.86&&temp<2.90)
{
sw=2;
}
else if(temp>2.95&&temp<3.05)
{
sw=3;
}
else if(temp>3.13&&temp<3.15)
{
sw=4;
}
key = sw;
sw=0;
adc=0;
temp=0;
switch(key)
{
case 1:
printf("sw1 press\r\n");
key = 0;
break;
case 2:
printf("sw2 press\r\n");
key = 0;
break;
case 3:
printf("sw3 press\r\n");
key = 0;
break;
case 4:
printf("sw4 press\r\n");
key = 0;
break;
default:
break;
}
Delay_Ms(50);
}
}