以读取一个按键状态为例
1.宏开关:只能支持一个版本按键
#define HARDWARE_VER 1
// key.c
// 返回值: 0表示被按下, 1表示被松开
int read_key(void)
{
GPIO_PinState key;
#if (HARDWARE_VER == 1)
key = HAL_GPIO_ReadPin(GPIOF, GPIO_PIN_6);
#else
key = HAL_GPIO_ReadPin(GPIOF, GPIO_PIN_7);
#endif
if (key == GPIO_PIN_RESET)
return 0;
else
return 1;
}
2.使用函数指针
int (*read_key)(void);
// 返回值: 0表示被按下, 1表示被松开
int read_key_ver1(void)
{
GPIO_PinState key;
key = HAL_GPIO_ReadPin(GPIOF, GPIO_PIN_6);
if (key == GPIO_PIN_RESET)
return 0;
else
return 1;
}
// 返回值: 0表示被按下, 1表示被松开
int read_key_ver2(void)
{
GPIO_PinState key;
key = HAL_GPIO_ReadPin(GPIOF, GPIO_PIN_6);
if (key == GPIO_PIN_RESET)
return 0;
else
return 1;
}
void key_init()
{
int ver = read_hardware_ver(); //读取硬件版本
if (ver == 1)
read_key = read_key_ver1;
else
read_key = read_key_ver2;
}
// main.c
void main(void)
{
int key;
key_init();
while (1)
{
key = read_key();
if (key == UP)
led_on();
else
led_off();
}
}
3.但如果要同时支持多个版本,并且容易拓展,最好是使用结构体并且函数分层模块化
typedef struct key{
char *name;
int (*init)(struct key *k);
int (*read)(struct key *k);
}key,*p_key;
定义一个key结构体,包涵名字,初始化函数和读取函数。让每个按键都实现结构体
//key1.c
key k1 = {"k1".NULL,read_key1};
//key2.c
key k2 = {"k2".NULL,read_key2};
//key3.c
key k3 = {"k3".NULL,read_key3};
将底层分为k1.,k2,k3按键的初始化+读取,将每个按键的结构体数据放在中间层进行注册管理(key.manager),最后将key.manager处理得到的按键数据传回应用层供main使用。
//key.manager.c
int key_cnt = 0;
key *keys[32];
int register_key(key *k) //注册按键,也就是把按键数据存入数组/链表
{
keys[key_cnt] = k;
key_cnt ++;
}
void key_init(void)
{
k1_init;
k2_init;
}
key *get_key(char *name) //得到按键数据
{
ibt i = 0;
for(i=0;i<key_cnt;i++)
{
if(strcmp(name,key[i)->name) == 0)
{
return keys[i];
}
}
return NULL;
}
int read_key(void)
{
int val;
int i = 0;
for (i = 0; i < key_cnt; i++)
{
val = keys[i]->read(keys[i]);
if (val == -1)
continue;
else
return val;
}
return -1;
}
//k1.c k2同理
static int read_key1(void)
{
GPIO_PinState key;
key = HAL_GPIO_ReadPin(GPIOF, GPIO_PIN_6);
if (key == GPIO_PIN_RESET)
return 0;
else
return 1;
}
static key k1 = {"k1", k1_init, read_key1};
void k1_init(void)
{
register_key(&k1);
}
//main.c
int main()
{
key_init();
key *k;
k = get_key("k1");
if(k == NULL)
return ;
while(1)
{
if(k->read(k) == 0)
led_on();
else led_off();
}
}
这样可以通过修改get_key传入的参数来获取按键的值,但是如何做到自动分辨按键值并且支持多个按键呢?
首先是在结构体里再加入一个code元素来分辨按键。
typedef struct key {
char *name;
int code;
void (*init)(struct key *k);
int (*read)(struct key *k);
}key, *p_key;
key k1={"k1",0xA,k1_init,read_key1};
并且修改read_key函数,通过main轮询查按键
//k1.c
static int read_key1(void)
{
GPIO_PinState key;
static GPIO_PinState pre_key;
key = HAL_GPIO_ReadPin(GPIOF, GPIO_PIN_6);
if (key == pre_key) //按键无变化
return -1;
pre_key = key;//按键发生变化
if(key == GPIO_PIN_RESET)
return 0xA | (1<<7); //按下按键
else
return 0xA;
}
//key.manager.c
int read_key(void)
{
int val;
int i=0;
for(i=o;i<key_cnt;i++)
{
val = keys[i]->read(keys[i])
if(val == -1)
continue;
else
return val;
}
return -1;
}
//main.c
void main(void)
{
key *k;
int val;
key_init();
while(1)
{
val = read_key();
if(val == -1)
{}
else
{
switch(val)
{
case 0xA: /* k1 松开 */
case 0xA|(1<<7) : /* k1 按下 */
case ...
}
}
}
但是轮询的方式远不如用中断效率高,采用中断函数来保存每个按键的val,并且将val保存到一个buf数组里,在manager里直接通过读取buf数组就可以知道按键值。
//k1.c
void k1_isr(void)
{
GPIO_PinState key;
key = HAL_GPIO_ReadPin(GPIOF, GPIO_PIN_6);
/* 按键发生了变化 */
if (key == GPIO_PIN_RESET)
put_buf(0xA | (1<<7)); //将数值存入buf
else
put_buf(0xA) ;
}
//keymanager.c
int data_buf[100];
int read_key(void)
{
return get_buf(); //直接读取buf
}
int put_buf(int buf) //将数据存入data_buf
{
}
int get_buf( ) //把数据从data_buf拿出
{
}
大致结构如下