藍橋杯嵌入式擴展板模塊之按鍵
硬件介紹
U1S1這是我見到過最NB的硬件電路,我當時還以爲是矩陣鍵盤,沒想到它採用的是ADC採集它的分壓值。。
其中ADC_Key你就把它當做一個ADC採集口,當S按鍵按下後,ADC採集的電壓就會改變,所以你要做的就是根據採集電壓的不同區判斷是哪個按鍵被按下。有人可能會擔心這樣可能不精確會造成誤判什麼的,我後面寫了之後發現根本不會出現。
Button.c
#include "button.h"
u16 Button_Num[length];
void Adc_ButtonInit(void){
ADC_InitTypeDef ADC_InitStructure;
GPIO_InitTypeDef GPIO_InitStructure;
RCC_ADCCLKConfig(RCC_PCLK2_Div6);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1 | RCC_APB2Periph_GPIOA, ENABLE);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;
GPIO_Init(GPIOA, &GPIO_InitStructure);
ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;
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_InitStructure.ADC_NbrOfChannel = 1;
ADC_Init(ADC1, &ADC_InitStructure);
ADC_RegularChannelConfig(ADC1, ADC_Channel_5, 1, ADC_SampleTime_239Cycles5);
ADC_DMACmd(ADC1, ENABLE);
ADC_Cmd(ADC1, ENABLE);
ADC_ResetCalibration(ADC1);
while(ADC_GetResetCalibrationStatus(ADC1));
ADC_StartCalibration(ADC1);
while(ADC_GetCalibrationStatus(ADC1));
}
u16 Adc_GetButtonVal(void){
ADC_SoftwareStartConvCmd(ADC1,ENABLE);
while(!ADC_GetFlagStatus(ADC1,ADC_FLAG_EOC));
ADC_ClearFlag(ADC1,ADC_FLAG_EOC);
return ADC_GetConversionValue(ADC1);
}
u16 Read_Button(void){
u16 i=0,j=0,temp=0;
for(i=0;i<50;i++){
Button_Num[i] = Adc_GetButtonVal();
}
for(i=0;i<length-1;i++){
for(j=0;j<length-i-1;j++){
if(Button_Num[j]>Button_Num[j+1]){
temp = Button_Num[j];
Button_Num[j] = Button_Num[j+1];
Button_Num[j+1] = temp;
}
}
}
if(length%2==0)
return (Button_Num[length/2-1]+Button_Num[length/2])/2;
else
return Button_Num[length/2];
}
u8 Scan_Button(void){
u16 temp = Read_Button();
if(temp<0x0015) return 1;
else if(temp<0x0250) return 2;
else if(temp<0x0500) return 3;
else if(temp<0x0700) return 4;
else if(temp<0x0A00) return 5;
else if(temp<0x0C00) return 6;
else if(temp<0x0E00) return 7;
else if(temp<0x0FC0) return 8;
else return 0;
}
Button.c函數實現的講解
- Button_Num[length]這個數組是用來存放ADC採集的數值的,它的長度length自己設置就可以了,但是最好設置比較大一點,一般情況下50即可。
- Adc_ButtonInit()這個就是一個普通的ADC初始化的步驟,但是需要注意的有兩點,其一是RCC_ADCCLKConfig(RCC_PCLK2_Div6)需要對ADC六分頻,其二是ADC_RegularChannelConfig函數的第四個參數最好是ADC_SampleTime_239Cycles5,這樣會採集的更加精確一些。
- Adc_GetButtonVal()這個函數就是獲取ADC值,沒什麼可說的。
- Read_Button()這個函數中,第一個for循環是用連續讀滿整個數組,第二個就是冒泡排序,後面返回的就是這個數組最中間的數值,說白了這個函數就是簡單的進行個濾波。
- Scan_Button()這個函數就是用來判斷是哪個按鍵被被按下了,temp後面的值就是對應拓展版按鍵按下後ADC採集到的值,而這個值是怎麼來的就很有意思了。有的人選擇直接背下來,我倒是認爲不可,因爲比賽的板子有可能電阻的不同,就會導致相應的偏差,我想到一個辦法就是你挨個試,怎麼試呢?你前面不是已經寫出來Read_Button()這個函數了麼,你把它的值直接顯示在LCD上,然後每次通過按鍵就會發現那個數值的變化,然後你根據那個數值的變化,去選一個相應的範圍就可以了,我認爲這是最快最安全的方法!
Button.h
#ifndef __BUTTON_H
#define __BUTTON_H
#include "stm32f10x.h"
#define length 50
u16 Read_Button(void);
u8 Scan_Button(void);
u16 Adc_GetButtonVal(void);
void Adc_ButtonInit(void);
#endif
總結
這個模塊在思想上可以說非常簡單了,但就是有點點麻煩。因爲我要做的事採集ADC的值要求的就是一個準,因此我們可以選擇浪費一點時間,去讀出最準確的數值。