昨天寫了一下STM8環境下GPIO的流水燈操作說明以及操作系統的OS_ThreadDelay延時函數,然而流水燈只是最基本的一個小實驗,并沒有太大的實用價值,所以今天的帖子是GPIO操作的升級版,也是操作系統環境下的"管理組件"的驅動開發示例帖,今天的帖子是示范一下系統環境下的矩陣鍵盤驅動編寫以及在STM8環境下的實現。
在Myos操作系統環境下的按鍵驅動接口原型有三個,代碼如下:- /*********************************************************************系統按鍵管理驅動函數*********************************************************************/
- /*
- *函數名:MCU_ButtonIOInit( void )
- *入參:無
- *返回值:無
- *作用域:本地
- *線程訪問:回調
- *函數作用:初始化系統硬件成為可使用的按鍵輸入
- */
- void MCU_ButtonIOInit( void )
- {
- }
- /*
- *函數名:MCU_GetButtonvallower( void )
- *入參:無
- *返回值:按下的按鍵狀態
- *作用域:本地
- *線程訪問:回調
- *函數作用:獲取當前鍵盤中被按下的按鍵低32位
- */
- unsigned int32 MCU_GetButtonvallower( void )
- {
- return 0x00;
- }
- /*
- *函數名:MCU_GetButtonvalhigh( void )
- *入參:無
- *返回值:按下的按鍵狀態
- *作用域:本地
- *線程訪問:回調
- *函數作用:獲取當前鍵盤中被按下的按鍵高32位
- */
- unsigned int32 MCU_GetButtonvalhigh( void )
- {
- return 0x00;
- }
復制代碼 這三個驅動接口原型函數位于Myos_driver文件夾下的myosmanagedriver.c文件中,系統會在組件初始化階段調用MCU_ButtonIOInit函數來執行按鍵的輸入輸出口屬性初始化工作,而在需要進行按鍵檢測的時候通過調用MCU_GetButtonvallower和MCU_GetButtonvalhigh函數來獲取最大64個按鍵的狀態,并在系統內部進行處理,對按鍵進行單擊、長按檢測,并提供一系列的接口來使能或禁能按鍵的連續輸出,具體的功能會在后續的帖子中一一說明。
我所使用的開發板上有一個2x3的矩陣鍵盤,共計6個按鍵,一般情況下入門單片機的時候大家接觸的按鍵一般都是獨立按鍵,也就是每個IO接一個按鍵,通過IO的狀態來判定按鍵的按下或抬起狀態,而在實際的項目中大多使用的是矩陣鍵盤。
矩陣鍵盤的原理實際上是分而治之,舉個例子,大家上小學的時候舉手,一個教室有很多行有很多列的學生,有的學生舉手而有的學生不一定舉手,而老師看的時候也不是同時看的,一般都是先看第一行,然后再看第二行、第三行,以此類推。矩陣鍵盤也是一樣的原理,一行按鍵一行按鍵的檢測。
話不多說,根據我手里開發板的原理進行驅動的編寫,上傳的文件中所有的文件均為只讀文件,請大家找到對應的文件路徑然后解除只讀屬性后進行修改,編寫后的代碼如下:
- /*********************************************************************系統按鍵管理驅動函數*********************************************************************/
- /*
- *函數名:MCU_ButtonIOInit( void )
- *入參:無
- *返回值:無
- *作用域:本地
- *線程訪問:回調
- *函數作用:初始化系統硬件成為可使用的按鍵輸入
- */
- void MCU_ButtonIOInit( void )
- {
- GPIOD->DDR &= ~0x1C; //設置PD2-PD4為輸入
- GPIOD->CR1 |= 0x1C; //設置PD2-PD4為上拉
- GPIOD->CR2 &= ~0x1C; //設置PD2-PD4不中斷
- GPIOA->DDR |= 0x06; //設置PA1-PA2為輸出
- GPIOA->CR1 &= ~0x06; //設置PA1-PA2為開漏
- GPIOA->CR2 |= 0x06; //設置PA1-PA2為10MHZ
- }
- /*
- *函數名:MCU_GetButtonvallower( void )
- *入參:無
- *返回值:按下的按鍵狀態
- *作用域:本地
- *線程訪問:回調
- *函數作用:獲取當前鍵盤中被按下的按鍵低32位
- */
- unsigned int32 MCU_GetButtonvallower( void )
- {
- unsigned dshort16 regist; //寄存變量
- GPIOA->ODR |= 0x06; //設置PA1-PA2為高電平
- GPIOA->ODR &= ~0x04; //設置PA2位低電平,準備掃描第二行
- OS_Delay(0x02); //等待IO穩定
- regist = GPIOD->IDR; //獲取第二行的狀態
- GPIOA->ODR |= 0x06; //設置PA1-PA2為高電平
- GPIOA->ODR &= ~0x02; //設置PA1位低電平,準備掃描第一行
- regist >>= 0x02; //進行按鍵處理同時等待IO穩定
- regist <<= 0x03; //進行按鍵處理同時等待IO穩定
- regist |= 0xFFC0; //進行按鍵處理同時等待IO穩定
- regist |= ((GPIOD->IDR) >> 0x02); //獲取第一行的狀態
- regist = ~regist; //低電平有效轉高位有效
- return regist; //返回按鍵狀態
- }
- /*
- *函數名:MCU_GetButtonvalhigh( void )
- *入參:無
- *返回值:按下的按鍵狀態
- *作用域:本地
- *線程訪問:回調
- *函數作用:獲取當前鍵盤中被按下的按鍵高32位
- */
- unsigned int32 MCU_GetButtonvalhigh( void )
- {
- return 0x00;
- }
復制代碼 MCU_ButtonIOInit函數中對狀態線PD2-PD4初始化為上拉輸入,且不開啟中斷功能,那么這三個IO就相當于是一個高阻輸入的IO再加一個上拉電阻,用來捕獲按鍵的按下狀態,而對掃描線PA1-PA2初始化為開漏輸出,之所以設置為開漏輸出是為了保證沒有高電平輸出,高電平由輸入口的上拉電阻提供而不是由單片機的推挽輸出提供,因為如果按鍵有時候會多個按鍵同時按下,一但PA1和PA2直接導通則會燒毀IO口,開漏輸出由于沒有高電平(扇出電流)輸出所以不會導致IO的燒毀。另外我的板子上只有6個按鍵,所以沒有必要使用高32位的驅動函數,只需要使用低32位的驅動函數來捕獲按鍵狀態既可。
MCU_GetButtonvallower函數里包含了矩陣按鍵的掃描方法,首先將置低PA2掃描第二行,因為我們采用的是左移的方式來處理,先掃描的按鍵值會更大,第一次掃描需要進行一些延時來等待IO狀態穩定,OS_Delay這個函數也是一個系統函數,但是這個函數只是粗略延時,延時時長為:(參數*3)個時鐘周期。而在第二次掃描的時候就可以進行regist變量的處理來模擬延時,同時也可以先進行一部分的處理工作,提高驅動的效率。當驅動寫完后,則按鍵的鍵值編碼如下:第一行(PA1)的第一個(PD2)為1、第二個(PD3)為2、第三個(PD3)為3;第二行第一個(PD2)為4、第二個(PD3)為5、第三個(PD3)為6。
當需要進行按鍵檢測的時候調用函數OS_GetKey或OS_GetChar即可獲取到當前的按鍵狀態,該函數的函數原型為:
- /*
- *函數名:OS_GetKey( void )
- *入參:無
- *返回值:當前按下的鍵值
- *作用域:本地
- *線程訪問:安全
- *函數作用:獲取當前鍵盤的鍵值,該函數不會阻塞線程,如沒有按鍵按下則返回0,如為長按則最高位為1
- */
- unsigned char8 OS_GetKey( void );
- /*
- *函數名:OS_GetChar( void )
- *入參:無
- *返回值:獲取當前按下的按鍵
- *作用域:本地
- *線程訪問:安全
- *函數作用:獲取當前的按鍵情況,該函數會阻塞線程,如果是長按則返回值的最高位為1
- */
- unsigned char8 OS_GetChar( void );
復制代碼 上述兩個函數均可獲取按鍵的按鍵值,不同之處在于OS_GetKey不會阻塞當前的線程,如果沒有按鍵按下則返回0,而OS_GetChar會阻塞線程,如果沒有按鍵按下則一直在本函數中等待,直到按鍵按下為止,而如果發生了長按則兩個函數返回值的最高位(位7)均為1,低7位則是鍵值,鍵值對應關系為1~32為MCU_GetButtonvallower驅動函數返回值的0~31位,而33~64為MCU_GetButtonvalhigh驅動函數返回值的0~31位。
我在例程中使用的函數為OS_GetKey來獲取按鍵值,并且根據按鍵的值改變流水燈的移動速度,代碼如下:
- #include "myos.h"
- #include "osdemo.h"
- /*
- *函數名:OS_ApplicationMainThread(void *arg)
- *入參:
- arg:線程運行參數
- *返回值:無
- *作用域:本地
- *線程訪問:安全
- *函數作用:應用程序主線程
- */
- void OS_ApplicationMainThread(void *arg)
- {
- unsigned char8 key = 0x00; //按鍵寄存
- unsigned char8 regist = 0x00; //寄存變量
- unsigned short16 delay = 1000; //延時寄存
- GPIOC->DDR = 0x1E; //設置PC1-PC4為輸出模式
- GPIOC->CR1 = 0x1E; //設置PC1-PC4為推挽輸出
- GPIOC->CR2 = 0x1E; //設置PC1-PC4位10MHZ輸出
- while(true){
- regist &= 0x0F; //只取4位有效位
- regist = (regist == 0x00) ? 0x01 : (regist << 0x01); //流水燈移動及越界判定
- GPIOC->ODR = (~regist << 0x01); //LED燈點亮賦值
- key = OS_GetKey(); //獲取鍵值
- if(key > 0x00){ //有按鍵按下
- delay = key & 0x7F; //去除長按標識
- delay *= 200; //計算延時時間
- }
- OS_ThreadDelay(delay); //系統提供的延時函數
- }
- OS_ThreadExit(); //線程結束退出
- }
復制代碼 OK,今天的帖子到此為止,下一個帖子的內容是SPI(串行外設接口)的科普帖。
|