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

嵌入式软件架构--按键消息队列3(测试)

开始

静态数码管与矩阵按键的联合测试

单击,双击,长按

要达到的效果是16个按键单击双击长按都能在数码管上显示相应的位置码0-F.
这里使用的是一位的共阳数码管只需要关心段选拉低就行。
驱动

//驱动函数给1位的共阳静态数码管,所以不用管位选
//PB8-PB15
void led_write(UINT8 dat)
{LED_PORT->ODR = (dat<<8);
}

显示函数
test函数20ms调用一次

/*
函数功能:按键单击双击长按的测试
参数说明:参数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;
}

这里是任务列表

//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中 不断调用对应的检测函数

void keyMain(void *pMsg)
{KEY_S *p = pMsg;keyScan(p,p->pf());
#if(D_CLICK_EN)keyDScan(p);
#endifkeyLScan(p);
}
组合键

从keyMain函数开始分析整体的流程很简单,按键按下产生按键标志位,检查标志位把按键事件进行分类包装成消息,消息写入按键对象的消息队列,添加一个20毫秒的消息解析函数,解析消息。

1,检测标志位

void keyMain(void *pMsg)
{KEY_S *p = pMsg;keyScan(p,p->pf());
#if(D_CLICK_EN)keyDScan(p);
#endifkeyLScan(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四个按键是控制按键
在这里插入图片描述

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)){//获取按键码在进行映射ASCIImsg.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,解析消息并显示

/*
函数功能:组合键测试
参数说明:参数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,将消息解析函数添加到任务组

//20ms
const TASK_S  taskGroup1[]=
{//{test,&Key.keyLF},{combinationKey,&Key},// {producer,&P2},{NULL,NULL},		 
};

5,最终效果
Ctrl+123456789分别显示’a’‘b’‘c’‘d’…

按键转义

按键转义是在组合键的升级,加入了shift,alt两个键。

1,定义映射表相关宏定义

#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的长按功能

/*
函数功能:生成按键消息(按键转义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];//物理按键码映射到ASCIImsg.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://ctrlmsg.code=ConbCtrl[num];break;case KEY_SHIFT://SHIFTmsg.code=ConbShift[num];break;case KEY_ALT://ALTmsg.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,消息解析显示

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显示cled_write(LedChars[toIdx(msg.code)]);}else{//ctrl键未按下正常显示123。。。if(msg.code >='0'&& msg.code<='9'){led_write(LedChars[msg.code-'0']);}}	}}
}

命令式编程与动态数码管的测试

动态数码管的驱动
//段选
#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;	 //写入输出寄存器。
}
各种命令的测试
//================转换命令===========================
#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在根据命令类型调用对应的函数

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,添加任务到任务列表

//后台软件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,设置窗口

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,闪烁显示仿真

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,长度为8setWinCmd(&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,倒计时显示仿真

	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开始倒计时每秒一次递减
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,移动显示仿真

	setWinNum(&Dleds,1);//只有一个窗口setWinSize(&Dleds,0,0,8);//对窗口0设置显存初始地址为0,长度为8setWinCmd(&Dleds,0,LED_MOVE,0,1,3,NULL);//3表示移动字符有三个writeLed(&Dleds,0,"%s","123");
http://www.dtcms.com/a/516906.html

相关文章:

  • 淘宝导购网站模版上海城隍庙简介
  • 怎么建立企业网站免费的软件项目管理方案
  • 工作流activiti(1)
  • 合泰单片机之点亮开发板的所有LED灯
  • 找不到mfc140d.dll文件
  • Dexmal 原力灵机开源 Dexbotic:具身智能的“Transformers“库来了
  • 毕设做网站有什么题目网络规划设计师攻略
  • 【avalonia教程】15Binding的其他属性(2)
  • 企业网站作用平湖手机网站建设
  • 算法leetcode|96. 不同的二叉搜索树(多语言实现)
  • 快速上手ip link命令:查看你的网络接口信息
  • 视频汇聚平台EasyCVR级联播放偶发失败排查:TCP主动模式下的3秒超时响应差
  • 苏州马可波罗网站建设wordpress单页主题制作视频教程
  • html手机网站怎么做清新织梦淘宝客模板淘客网站程序源码
  • 20.2 图像识别技术革命:多模态模型准确率突破87.6%,传统方案效率飙升32%!
  • 深圳网站建设加盟网站 方案
  • ★ Linux ★ 线程概念与控制
  • 设计师接私单做网站为什么打不开建设银行网站
  • 前端-登录认证技术
  • AI开发结构化输出
  • Leetcode 32
  • eclipse tomcat运行普通web项目发现mysql-connector-java-8.0.30.jar包无法自动部署 的解决办法
  • 【经典算法,限时免费】LeetCode698、划分K个相等的子集(回溯解法)
  • 做网站为职业生存不下去nginx wordpress rewrite
  • RK3568 MIPI 摄像头驱动的 V4L2 多平面视频格式解析
  • 英伟达RTX 6000 Ada 和L40S 对比,哪个更适合做深度学习?
  • 网站开发维护费用学校网站建设工作
  • 华为专利申请的核心指导思想
  • 做配资网站多少钱我想花钱做网站
  • 自适应单行tooltip省略号