开始
静态数码管与矩阵按键的联合测试
单击,双击,长按
要达到的效果是16个按键单击双击长按都能在数码管上显示相应的位置码0-F.
这里使用的是一位的共阳数码管只需要关心段选拉低就行。
驱动
c
//驱动函数给1位的共阳静态数码管,所以不用管位选
//PB8-PB15
void led_write(UINT8 dat)
{
LED_PORT->ODR = (dat<<8);
}
显示函数
test函数20ms调用一次
c
/*
函数功能:按键单击双击长按的测试
参数说明:
参数1:按键单击双击长按标志位(keyF,keyDF,keyLF)
返回值:void
*/
void test(void *pMsg){
UINT8 i;
KEY_TYPE keyF=(*(KEY_TYPE *)pMsg);
for(i=0;i<KEY_NUM;i++){
if(keyF&(1<<i)){
led_write(LedChars[i]);
}
}
(*(KEY_TYPE *)pMsg)=0x00;
}
这里是任务列表
c
//10ms
const TASK_S taskGroup0[]=
{
{keyMain,&Key},
{NULL,NULL},
};
//20ms
const TASK_S taskGroup1[]=
{
{test,&Key.keyLF},//这里切换检测模式单击双击长按
{NULL,NULL},
};
任务参数可以是以下标志位:单击Key.keyF,双击Key.keyDF,长按Key.keyLF
然后在keyMain中 不断调用对应的检测函数
c
void keyMain(void *pMsg)
{
KEY_S *p = pMsg;
keyScan(p,p->pf());
#if(D_CLICK_EN)
keyDScan(p);
#endif
keyLScan(p);
}
组合键
从keyMain函数开始分析整体的流程很简单,按键按下产生按键标志位,检查标志位把按键事件进行分类包装成消息,消息写入按键对象的消息队列,添加一个20毫秒的消息解析函数,解析消息。
1,检测标志位
c
void keyMain(void *pMsg)
{
KEY_S *p = pMsg;
keyScan(p,p->pf());
#if(D_CLICK_EN)
keyDScan(p);
#endif
keyLScan(p);
//keyF 按键单击标志位
if(p->keyF){//按键单击事件
creatKeyMsg(p,&p->keyF,KEY_CHAR_EVENT);
}
if(p->keyDF){//按键双击事件
creatKeyMsg(p,&p->keyDF,KEY_D_EVENT);
}
if(p->keyLF){//按键长按事件
creatKeyMsg(p,&p->keyLF,KEY_L_EVENT);
}
}
2,包装消息写入队列
右侧映射到左侧,Alt,Shift,Ctrl,Enter四个按键是控制按键
c
UINT8 const KeyMap[16] =
{
'1', '2', '3', VK_ENTER,
'4', '5', '6', VK_CONTROL,
'7', '8', '9', VK_SHIFT,
'*', '0', '#', VK_ALT
};
//按键码高效获取
UINT8 getKeyNum(KEY_TYPE *pKey)
{
UINT8 y,x,num;
KEY_TYPE key = *pKey;
x = (key & 0xff)? 0 : 1;
y = pro[(UINT8)((key) >> (x << 3))];
num = y + x*8;
*pKey &= ~(1 << num);//注意打开
return num;
}
/*
函数功能:生成按键消息
参数说明:
参数1:按键对象
餐数2:按键码
参数3:按键事件
返回值:void
*/
void creatKeyMsg(KEY_S* p,KEY_TYPE *pKeyF,UINT8 event){
MSG_S msg;
//pKeyF每一位都表示一个按键是否按下,为了应对多个按键一起按下需要循环检测
while(*pKeyF && (isMsgQueueFull(&p->msgQueue)==FALSE)){
//获取按键码在进行映射ASCII
msg.code=KeyMap[getKeyNum(pKeyF)];
//记录按键按下时间
msg.time=TimeStamp;
//判断按键事件类型,双击长按
if(event == KEY_D_EVENT || event ==KEY_L_EVENT){
msg.event=event;
}else{
if((msg.code>=32)&&(msg.code<=126)){
msg.event=KEY_CHAR_EVENT;//字符事件
}else{
msg.event=KEY_DOWN_EVENT;//控制事件
}
}
saveMsgQueue(&p->msgQueue,&msg);
}
}
3,解析消息并显示
c
/*
函数功能:组合键测试
参数说明:
参数1:按键对象指针
返回值:void
*/
void combinationKey(void *pMsg){
KEY_S * p=(KEY_S *)pMsg;
MSG_S msg;
if(msgQueueNum(&p->msgQueue)){//有消息
getMsgQueue(&p->msgQueue,&msg);
if(msg.event ==KEY_CHAR_EVENT){
if(p->keyDown &KEY_CONTROL){//ctrl键按下 1显示a,2显示b,3显示c
//这里需要对输入做一下限定,因为还有* #键
if(msg.code >='0'&& msg.code<='9'){
//LedChars偏移
led_write(LedChars[msg.code-'0'+9]);
}
}else{//ctrl键未按下正常显示1234567890 * #不检测组合
if(msg.code >='0'&& msg.code<='9'){
led_write(LedChars[msg.code-'0']);
}
}
}
}
}
4,将消息解析函数添加到任务组
c
//20ms
const TASK_S taskGroup1[]=
{
//{test,&Key.keyLF},
{combinationKey,&Key},
// {producer,&P2},
{NULL,NULL},
};
5,最终效果
Ctrl+123456789分别显示'a''b''c''d'...
按键转义
按键转义是在组合键的升级,加入了shift,alt两个键。
1,定义映射表相关宏定义
c
#define VK_ENTER 0x0d
#define VK_CONTROL 0x11
#define VK_SHIFT 0x10
#define VK_ALT 0x12
#define VK_UP 24
#define VK_DOWN 25
#define VK_LEFT 27
#define VK_RIGHT 26
#define KEY_ENTER 0x08
#define KEY_CONTROL 0x80
#define KEY_SHIFT 0x0800
#define KEY_ALT 0x8000
UINT8 const ConbCtrl[16] =
{
'a', 'b', 'c', VK_ENTER,
'd', 'e', 'f', VK_CONTROL,
'g', 'h', 'i', VK_SHIFT,
'j', 'k', 'l', VK_ALT
};
UINT8 const ConbShift[16] =
{
'm', 'n', 'o', VK_ENTER,
'p', 'q', 'r', VK_CONTROL,
's', 't', 'u', VK_SHIFT,
'v', 'w', 'x', VK_ALT
};
UINT8 const ConbAlt[16] =
{
'y', 'x', ',', VK_ENTER,
'.', '?', '!', VK_CONTROL,
'+', '-', VK_UP, VK_SHIFT,
VK_DOWN, VK_LEFT, VK_RIGHT, VK_ALT
};
2,keyMain函数没有修改正常检测三种标志位并调用消息生成函数
3,消息生成函数加入shift,alt的长按功能
c
/*
函数功能:生成按键消息(按键转义shift,ctrl,alt)
参数说明:
参数1:按键对象
餐数2:按键码
参数3:按键事件
返回值:void
*/
void creatKeyMsg(KEY_S * p,KEY_TYPE *pKeyF,UINT8 event){
MSG_S msg;
UINT8 num;
//有按键按下并且消息队列有空间存储
//这里每次只产生一个消息并清零标志位
if(*pKeyF && (isMsgQueueFull(&p->msgQueue)) ==FALSE){
num=getKeyNum(pKeyF);//获取物理按键码
msg.code=KeyMap[num];//物理按键码映射到ASCII
msg.time=TimeStamp;
//双击长按事件
if(event ==KEY_D_EVENT || event == KEY_L_EVENT){
msg.event= event;
}else{
//先把enter ,ctrl,shift,alt四个按键刨除
if(num!=0x07 && num !=0x0b && num !=0x0f && num !=0x03){
switch(p->keyDown & 0x8880){
case KEY_CONTROL://ctrl
msg.code=ConbCtrl[num];
break;
case KEY_SHIFT://SHIFT
msg.code=ConbShift[num];
break;
case KEY_ALT://ALT
msg.code=ConbAlt[num];
break;
default :
break;
}
if(msg.code >=32 && msg.code <=126){
msg.event =KEY_CHAR_EVENT;
}else{
msg.event =KEY_DOWN_EVENT;
}
}
}
saveMsgQueue(&p->msgQueue,&msg);
*pKeyF &= ~(1 << num);//清零对应标志位
}
}
4,消息解析显示
c
void combinationKey(void *pMsg){
KEY_S * p=(KEY_S *)pMsg;
MSG_S msg;
if(msgQueueNum(&p->msgQueue)){//有消息
getMsgQueue(&p->msgQueue,&msg);
if(msg.event ==KEY_CHAR_EVENT){
if(p->keyDown &KEY_CONTROL || p->keyDown & KEY_SHIFT || p->keyDown & KEY_ALT){//ctrl键按下 1显示a,2显示b,3显示c
led_write(LedChars[toIdx(msg.code)]);
}else{//ctrl键未按下正常显示123。。。
if(msg.code >='0'&& msg.code<='9'){
led_write(LedChars[msg.code-'0']);
}
}
}
}
}
命令式编程与动态数码管的测试
动态数码管的驱动
c
//段选
#define LED_PORT GPIOB
#define LED_PIN (GPIO_Pin_8|GPIO_Pin_9|GPIO_Pin_10|GPIO_Pin_11|GPIO_Pin_12|GPIO_Pin_13|GPIO_Pin_14|GPIO_Pin_15)
#define LED_PORT_RCC RCC_APB2Periph_GPIOB
//片选
#define LED_CHIP_PORT GPIOB
#define LED_CHIP_PIN (GPIO_Pin_5|GPIO_Pin_6|GPIO_Pin_7)
#define LED_CHIP_PORT_RCC RCC_APB2Periph_GPIOB
//驱动
void ledDrv(UINT8 dat,UINT8 chip)
{
UINT16 temp;
GPIOB->BRR = 0xff00;//所有段选拉低,因为是共阴数码管,消影
temp = GPIOB->ODR;//读取输出寄存器值,防止修改低八位
temp &= 0x001f;//PB5-PB15清零
temp |= chip << 5;//写入位选值
temp |= (~dat) << 8;//字模是共阳的所以需要取反
GPIOB->ODR = temp; //写入输出寄存器。
}
各种命令的测试
c
//================转换命令===========================
#define LED_TEXT 0X00 //普通文本显示
#define LED_FLASH 0x01 //闪烁显示
#define LED_FLASH_N 0x02 //多次闪烁
#define LED_DEC 0x03 //倒计时
#define LED_MOVE 0x04 //滚动
#define LED_PIC 0x05 //图片显示
#define LED_PASSWORD 0x06 //显示**
流程说明:
后台软件入口ledMain每100ms调用一次,ledMain调用ledcmd,ledcmd在根据命令类型调用对应的函数
c
void ledMain(void *pMsg){
UINT8 i;
LED_S *pLed=(LED_S *)pMsg;
for(i=0;i<pLed->cnt;i++){
ledCmd(pLed,i);
}
}
void ledCmd(LED_S *pLed,UINT8 winNum)
{
switch(pLed->pWin[winNum].cmd)
{
case LED_FLASH:
{
disLedFlash(pLed,winNum);
break;
}
case LED_FLASH_N:
{
disLedFlashN(pLed,winNum);
break;
}
case LED_DEC:
{
ledDec(pLed,winNum);
break;
}
case LED_MOVE:
{
ledMove(pLed,winNum);
break;
}
case LED_PIC:
{
disLedGif(pLed,winNum);
break;
}
case LED_PASSWORD:
{
disLedPassword(pLed,winNum);
break;
}
default:
{
disLedText(pLed,winNum);
break;
}
}
}
图片显示测试步骤:
1,添加任务到任务列表
c
//后台软件100ms调用一次
const TASK_S taskGroup2[]=
{
//{consumer,&C1},
{ledMain,&Dleds},
{NULL,NULL},
};
//驱动函数3ms调用一次
const TASK_S taskGroup3[]=
{
//{consumer,&C2},
{updataLed,&Dleds},
{NULL,NULL},
};
const TASK_GROUP_S TaskList[]=
{
{10, taskGroup0},
{20,taskGroup1},
{100,taskGroup2},
{3,taskGroup3},
};
2,设置窗口
c
setWinNum(&Dleds,1);//只有一个窗口
setWinSize(&Dleds,0,0,8);//对窗口0设置显存初始地址为0,长度为8
setWinCmd(&Dleds,0,LED_PIC,0,1,0,(void*)MyGif);//图片显示,cnt=0,reload=1,attr=0;
仿真
1,闪烁显示仿真
c
int main(void)
{
bspInit();
initKey(&Key,50,200,arrayKeyCode);
SysTick_Init(72);
initQueue(&Queue);
initMultiTask();
setWinNum(&Dleds,1);//只有一个窗口
setWinSize(&Dleds,0,0,8);//对窗口0设置显存初始地址为0,长度为8
setWinCmd(&Dleds,0,LED_FLASH_N,0,1,3,NULL);//闪烁显示3次,reload 1秒,
writeLed(&Dleds,0,"%s","123");
while (1)
{
ledCmd(&Dleds,0);
//task_exec();
}
}
可以看到显存中前三位交替变化三次
2,倒计时显示仿真
c
setWinNum(&Dleds,1);//只有一个窗口
setWinSize(&Dleds,0,0,2);//对窗口0设置显存初始地址为0,长度为2,即使用两位数码管,显存起始地址的对应每一位数码管
setWinCmd(&Dleds,0,LED_DEC,0,1,3,NULL);//倒计时显示,cnt=0,reload=1,attr=3即从3开始倒计时每秒一次递减
c
void ledDec(LED_S *pLed,UINT8 winNum){
if(pLed->pWin[winNum].attr !=0xfe){//结束条件
if(ledDelay(pLed,winNum)==TRUE){//延迟时间到
UINT8 fmt[4]="% d";
fmt[1]=pLed->pWin[winNum].len +'0';
if(pLed->pWin[winNum].attr ==0xff || pLed->pWin[winNum].attr ==0xfe){
writeLed(pLed,winNum,(const char *)fmt,0);
} else{
writeLed(pLed,winNum,(const char *)fmt,pLed->pWin[winNum].attr);
}
disLedText(pLed,winNum);
pLed->pWin[winNum].attr--;
}
}
}
buf[1]依次写入0x0d,0x25,0x9f,0x03,最终结束后attr值为0xfe
3,移动显示仿真
c
setWinNum(&Dleds,1);//只有一个窗口
setWinSize(&Dleds,0,0,8);//对窗口0设置显存初始地址为0,长度为8
setWinCmd(&Dleds,0,LED_MOVE,0,1,3,NULL);//3表示移动字符有三个
writeLed(&Dleds,0,"%s","123");