開始
靜態數碼管與矩陣按鍵的聯合測試
單擊,雙擊,長按
要達到的效果是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);
#endif
keyLScan(p);
}
組合鍵
從keyMain函數開始分析整體的流程很簡單,按鍵按下產生按鍵標誌位,檢查標誌位把按鍵事件進行分類包裝成消息,消息寫入按鍵對象的消息隊列,添加一個20毫秒的消息解析函數,解析消息。
1,檢測標誌位
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四個按鍵是控制按鍵
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,解析消息並顯示
/*
函數功能:組合鍵測試
參數説明:
參數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];//物理按鍵碼映射到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,消息解析顯示
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']);
}
}
}
}
}
命令式編程與動態數碼管的測試
動態數碼管的驅動
//段選
#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,長度為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,倒計時顯示仿真
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,長度為8
setWinCmd(&Dleds,0,LED_MOVE,0,1,3,NULL);//3表示移動字符有三個
writeLed(&Dleds,0,"%s","123");