造輪子,還是造鍵盤?

造輪子,還是造鍵盤?


是的,沒錯,我要重新開始造輪子。
最開始萌生此想法的原因是想把一個USB接口的鍵盤改造成藍牙鍵盤(別問我爲什麼不直接買一個)。想改的前提有兩個:一是筆者用的是筆記本,總會碰到USB接口不夠用的尷尬局面;二是筆者之前從事藍牙設備的開發工作,具備將鍵盤改造成藍牙接口的能力。
後來由於種種原因,想法被擱置了。正好最近有個朋友有需求要做一個鍵盤的轉接板,是直接從原始的矩陣鍵盤接口轉到USB接口,就是下面這貨。



好吧,離藍牙接口還有段距離,不過能做成USB的,那就一定做成藍牙的。
言歸正傳,開始動手吧。

一、硬件篇
無非就是個矩陣鍵盤嘛,嗯,看一下這貨的資料,14*8,好傢伙,線挺多的,這是全尺寸鍵盤的節奏?不過IO口多還真不是問題,市面上MCU那麼多,隨便選就好了。找個易用的又帶有USB接口的並且比較熟悉的,關鍵是手頭上就有的,非stm32莫屬了。

後面仔細一看,發現實物和上圖的FPC引腳數對不上 ==!,沒辦法了,總不能拆了鍵盤去看鍵碼吧……先搭個線完成功能吧。
STM32F103C8T6,引腳夠用,成本夠低,開發環境成熟,資源豐富。
下面是最小系統板,萬能的XX上有賣,10+可以搞定,還包郵。

IO分配好,PA0-PA7作爲列(COL)輸入,配置爲下拉輸入,PB0-PB5作爲行(ROW)輸出,默認爲推輓輸出。USB爲PA11、PA12,PA9、PA10分別爲TX、RX作調試用。
硬件就這麼多了。

二、軟件篇
軟件纔是讓人頭大的東西。
1、搭建工程
不過好在ST官方還算給力,做出了一個叫做STM32CubeMX的東西,簡直是想快速搭建工程配置外設者的福音。
打開STM32CubeMX,新建工程,stm32_usb_kb。

之所以選擇C8而不是C6版本,是因爲市場上C8的出貨量大,再就是因爲開發時可以選容量稍大的產品,到後期程序完善了、優化了,可以量產時候去用的更低容量的IC節省成本。
點OK確定,然後來配置外設。
首先配置RCC,選擇使用外部高速時鐘。
再配置SYS,主要使用SWD作爲程序的下載和調試接口,不選JTAG的原因是JTAG要佔用多個IO(主要是把我要用的PB口占用了)。Timebase Source選SysTick,會要用到計時功能 。
然後配置串口,這裏選擇USART1,配置成異步模式。
再配置USB,勾選框框,MiddleWares的USB選擇Human Interface Description
然後在芯片的引腳上配置我們需要用到的引腳,PA及PAB口,直接在IO口上單擊即可選擇需要的功能。
配置好的界面如下所示:


這時發現Clock Configuration選項卡上出現X,這是因爲修改了時鐘源造成的,點進去,會提示是否選擇自動配置,當然選yes了。

切換到配置頁面,配置GPIO,可以統一進行配置。

點OK完成配置,然後在Project菜單中選擇Generate Code

配置好工程名,選擇好存放位置,選擇好IDE,然後確定。

至此,一個最基本的帶有USB功能的支持矩陣鍵盤掃描的工程誕生了。

2、測試矩陣掃描的基本功能
a)首先初始化一下IO口,沒啥好說的。
void matrix_init(void)
{
GPIO_InitTypeDef GPIO_InitStruct;

/* GPIO Ports Clock Enable */
__HAL_RCC_GPIOD_CLK_ENABLE();
__HAL_RCC_GPIOA_CLK_ENABLE();
__HAL_RCC_GPIOB_CLK_ENABLE();

/*Configure GPIO pin Output Level */
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_0|GPIO_PIN_1|GPIO_PIN_2|GPIO_PIN_10
|GPIO_PIN_11|GPIO_PIN_12|GPIO_PIN_13|GPIO_PIN_14
|GPIO_PIN_15|GPIO_PIN_3|GPIO_PIN_4|GPIO_PIN_5
|GPIO_PIN_6|GPIO_PIN_7|GPIO_PIN_8|GPIO_PIN_9, GPIO_PIN_RESET);

/*Configure GPIO pins : PA0 PA1 PA2 PA3
PA4 PA5 PA6 PA7 */
GPIO_InitStruct.Pin = GPIO_PIN_0|GPIO_PIN_1|GPIO_PIN_2|GPIO_PIN_3
|GPIO_PIN_4|GPIO_PIN_5|GPIO_PIN_6|GPIO_PIN_7;
GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
GPIO_InitStruct.Pull = GPIO_PULLDOWN;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);

/*Configure GPIO pins : PB0 PB1 PB2 PB10
PB11 PB12 PB13 PB14
PB15 PB3 PB4 PB5
PB6 PB7 PB8 PB9 */
GPIO_InitStruct.Pin = GPIO_PIN_0|GPIO_PIN_1|GPIO_PIN_2|GPIO_PIN_10
|GPIO_PIN_11|GPIO_PIN_12|GPIO_PIN_13|GPIO_PIN_14
|GPIO_PIN_15|GPIO_PIN_3|GPIO_PIN_4|GPIO_PIN_5
|GPIO_PIN_6|GPIO_PIN_7|GPIO_PIN_8|GPIO_PIN_9;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
}



b)按鍵掃描函數
uint16_t KS_ScanForKeyPress(uint8_t iActiveLine)
{
uint32_t outputRegister = ((uint32_t)1<< iActiveLine );
GPIOB->ODR = outputRegister;
uint32_t scannedInput = (GPIOA->IDR & 0x000000FF);
return scannedInput;
}

uint8_t KS_PrintScanLines(void)
{
uint16_t scannedInput;
uint8_t keyScans = 0;

for(uint8_t i = 0; i < 16; ++i)
{
scannedInput = KS_ScanForKeyPress(i);
if (scannedInput > 0)
{
// detected a key press
printf("Key press detected at %d/0x%04X\n", (i+1), scannedInput);
++keyScans;
}
}
return keyScans;
}
c)測試
int main(void)
{
uint8_t scanCode;

/* Reset of all peripherals, Initializes the Flash interface and the Systick. */
HAL_Init();

/* Configure the system clock */
SystemClock_Config();
/* Initialize all configured peripherals */
MX_GPIO_Init();
MX_USART1_UART_Init();
MX_USB_DEVICE_Init();

/* USER CODE BEGIN 2 */
printf("Hello, STM kb!\r\n");

/* Infinite loop */
while (1)
{
HAL_Delay(10);
scanCode = KS_PrintScanLines();
}
}


用每一行的線分別接觸每一列的線,測試矩陣。

3、處理按鍵的邏輯
a)按鍵衝突問題
買鍵盤的時候,經常會聽到全鍵無衝、六鍵無衝等關鍵字,尤其對喜歡玩遊戲的朋友來說。關於其概念,想要了解的可以自行百度,在這裏只需要要知道相鄰兩行兩列矩陣上的三個鍵按下可能會出現另外一個未按下鍵的鍵值即可(針對是沒在矩陣上做過處理的鍵盤來講,加了二極管的鍵盤不在此列)。程序上要處理這種情況,當然,現在薄膜鍵盤的矩陣設計在一般的辦公場景及大部分遊戲場景很難會出現多鍵衝突的情況。
b)組合鍵處理
如按下ctrl鍵後又點擊C(點擊:點下去,然後立馬釋放)、按下Ctrl+alt+……等等。
c)功能鍵Fn
大多筆電按鍵較少,無法將常用的按鍵一次性佈局,往往會提供Fn+其它鍵實現一些不能佈下鍵的功能,爲實現這種功能,程序上要實現多套鍵碼,動態切換。
d)……(一些未想到的)

說了這麼多,卻沒有敲半句代碼實現。
雖然說是造輪子,但不是說從製造材料開始,那是原始人才會做的事。
我不是代碼的生產者,而是代碼的搬運工。
要善於借鑑前人的經驗。

打開github,輸入關鍵字“USB keyboard”,有一堆開源項目。反覆查看代碼,發現這傢伙的比較適合移植,有很多Demo,平臺近似,提交比較活躍,文檔寫得也比較清楚。
很多DIY機械鍵盤的實現也可用上此工程。

裏面有幾篇文檔值得關注一下,可以詳細瞭解整個程序的結構及框架的實現。

及工程根目錄下的README,通讀好處多多。

4、移植到M3平臺,MDK開發環境
這裏不多說,只與各位看官提提幾個要注意的地方。
a)github的工程是用GNU寫的,在Keil MDK ARM編譯器環境下要設置--gnu的編譯選項,要麼你就自己修改代碼,如匿名結構體。
b)一些語法上的修改,如case Fn1...Fn9: 在ANSI C下是沒有這種寫法的,需要自行更改。其次是Keil下沒有0b00001111這種表示方法。
c)代碼移植選擇tmk_core文件夾的內容即可,裏面有avr平臺的實現,可以對照改爲STM32的實現,主要是初始化,定時器,串口輸出。
d)參照keyboard/hbkb的代碼,自行實現Matrix.c中的矩陣掃描部分,config.h文件中一些宏的開啓等。宏的話,參照此文件下的設置即可,如果要實現更加高級的功能,看官自行研究。keymap.c矩陣的修改。
e)需要實現host_driver_t驅動,主要是報告描述,此結構位於report.h文件下。


貼一個我用到的代碼目錄:

建議:串口輸出一定要實現好,對調試有莫大的幫助。(可在debug.c文件中開啓要調試的部分)

5、測試
鍵碼還沒搞到手,先用單獨的按鍵替代測試。接了按鍵“1”與ctrl鍵,輸出正常,連接電腦也能正常打字,試試組合鍵也還行。

按下“1”

按下ctrl+1

三、總結篇
看似常用且簡單的東西還是是有一些技術含量在裏面的。
1、以前沒有好好寫過矩陣鍵盤的掃描代碼,現在寫過了並且記憶深刻了;
2、瞭解了一些與編譯器相關的關鍵字,如__attirbute__ (weak);
3、KEIL MDK ARM下支持GNU語法編譯方法:project->options->C/C++下,添加--gnu
a)keil中不支持GNU中的匿名結構體,需要在結構體前添加#pragma anon_unions或者在編譯選項中添加--gnu
b)“--gnu”實際上是在ARMCC中加入支持GNU擴展格式,當然實際上Keil是可以添加GCC的編譯器的,通過keil菜單中“Project > Manage > Components, Environment, Books..”添加,前提是你得提前裝好GCC編譯器。官方原文:http://www.keil.com/arm/gnu.asp
4、Keil環境中,用memset()對結構體“清零”時要注意四字節對齊,否則會出錯。可在定義結構體的末尾(;前面)添加__attirbute__ ((aligned(4))),如uint8_t EXTERN_DHCPBUF[1024] __attribute__((aligned(4)));
5、移植過來的代碼與平臺結合性不強,可以移植大部分平臺上去了,也可以採取各種通訊接口及協議。藍牙鍵盤的改造過程有望了。

四、拖拉篇
本來前三節寫好就要發的,結果一直沒拿到鍵碼,拖到現在,於是有了這一節。
很早之前就想買一把機械鍵盤玩玩了,無奈要不就是價格太貴,要不就是按鍵手感不符合。
正好有了之前的基礎,倒是有機會做一把鍵盤了。
一般的機械鍵盤組成部分:軸(關鍵),鍵帽,衛星軸,鍵盤PCBA,底殼,如果用的是3腳的軸還需要按鍵的固定鋼板。
軸的分類不多說(主要是我也說不清楚 ==!),看好介紹,然後買個試軸器自己感受,再決定上手什麼軸。X寶上大把的。
用習慣了筆記本,發現小鍵盤區用得並不多,再就是希望儘可能的節省成本吧,決定做60鍵的鍵盤,小巧,方便攜帶出去裝B。自己做好配列,60鍵還是足夠用的。
GitHub上找找,結果有開源的PCB,但自己用的IC不一樣,還是得改改。
下載後發現是KiCAD格式的,找遍若干方法,還是沒能將其轉換爲AD的格式。最終沒辦法,自己重新畫矩陣,重新畫封裝。
當然,不是傻傻的去弄。
提供一方法:
AD由Gerber反向生成PCB:
1)新建CAM文件,分別導入gerber和drill文件;
2)必要時調整一下Layer Order,Tables->Layers Order;
3)關鍵:Tools->Netlist->Extract;
4)File->Export->Export to PCB;
5)網絡表是重命名的網絡表,可以從原理圖重新導入網絡表。
至此,得到可供“使用”的PCB,僅僅是用於提取封裝、外形尺寸及定位。
對比原設計,我的設計添加了些自己的東西,如添加了燈的接口:

  • 爲每一排按鍵添加了獨立的燈控制電路,限於MCU只有四個PWM口,只好把最後兩排接到同一個PWM口上。這樣,便可獨立控制每一排燈了。
  • 爲CAPS Lock添加了單獨的燈,可以知道當前Caps的狀態。
  • 將Mini USB接口換成了更常用的插件Micro USB(現在安卓手機大行其道,Micro USB線還是更常見一些)。
  • 留出了矩陣及燈的接口,方便用其它平臺打造鍵盤,比如藍牙,wifi等等。
3D預覽的PCB:

實物:



做得比較着急,還是出了一些問題,比如絲印上漏了個W,少打一個孔,少一電阻等等小問題……
目前PCB的功能還只是能打字,還沒有把燈及固件升級的功能做上去,等閒一點的時候再買材料完善了。
等完善好了我會將PCB、原理圖及固件源代碼放到CSDN或Github上,歡迎有需要的人士下載或提出寶貴的建議~



PS:本人踩過的坑也全部在本文寫明,絕不藏私。如果真有願意動手的朋友,相信也會迎難而上的。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章