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

开始

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

单击,双击,长按

要达到的效果是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");
相关推荐
阿登林5 小时前
C# iText7与iTextSharp导出PDF对比
开发语言·pdf·c#
迎風吹頭髮5 小时前
Linux服务器编程实践57-功能强大的网络信息函数getaddrinfo:支持IPv4与IPv6
单片机·嵌入式硬件
qq_433554545 小时前
C++ 双向循环链表
开发语言·c++·链表
一只侯子5 小时前
Tuning——CC调试(适用高通)
开发语言·图像处理·笔记·学习·算法
丁浩6665 小时前
Python机器学习---1.数据类型和算法:线性回归
开发语言·python·机器学习·线性回归
那年窗外下的雪.5 小时前
鸿蒙ArkUI布局与样式进阶(十二)——自定义TabBar + class类机制全解析(含手机商城底部导航案例)
开发语言·前端·javascript·华为·智能手机·harmonyos·arkui
马拉萨的春天6 小时前
探索Objective-C中的对象复制:深入理解copy和mutableCopy
开发语言·ios·objective-c
啊森要自信6 小时前
【MySQL 数据库】使用C语言操作MySQL
linux·c语言·开发语言·数据库·mysql
千码君20166 小时前
Go语言:对其语法的一些见解
开发语言·后端·golang