01 前言
周末帶小朋友去公園玩耍,別的小孩在池塘里玩飲料瓶做的漂流船,看著他歡樂的跟著跑跳,無比羨慕的眼神,卻又不能上手的小失落,又回想起兒時用泡沫板和小電機以及電池做的小船,和那時對于電驅動產生的無比興趣,我決定升級一下兒時的裝備,基于STM32給小朋友DIY一個遙控小船,使他成為公園里焦點,同時也期待他對電氣控制產生一點點好奇。
基本構想如下:stm32驅動兩個小電機,小電機上安裝兩個螺旋槳,可以實現雙槳前進、后退,單槳轉彎等。供電使用18650電池,通過升壓放電板管理電池的充放電。遙控使用最廉價的紅外遙控,來控制小船的各種動作。同時可以增加一組數碼管作為輸出設備。
在萬能的某寶上可以淘到所有需要的材料,并且價格都十分實惠。
材料 |
單價 |
備注 |
船體 |
0 |
廢舊收納盒 |
驅動電機、螺旋槳 |
10.5 |
|
紅外遙控器、紅外接收器 |
3.5 |
|
18650電池 |
0 |
廢舊小電扇拆件 |
充電升壓放電板 |
5.8 |
|
STM32最小系統板 |
16.2 |
|
4位數碼管顯示器 |
4.3 |
|
電池盒 |
1.8 |
|
線材 |
0 |
廢線材 |
電源開關 |
1.8 |
|
合計 |
43.9 |
02 硬件模塊
紅外遙控模塊
一般遙控玩具使用的是2.4G高頻無線遙控模塊,但是本著能省則省的原則,選用遙控器帶接收頭3.5元還包郵的HX1838紅外遙控模塊。這種遙控器類似電視遙控器,優點是便宜,開發簡單,缺點是控制距離短,并且要像遙控電視一樣指著玩具操作,這些缺陷留著下一代產品迭代時升級。
電機驅動模塊
電機使用直流小電機,3-6V可驅動,使用一片L298N驅動板驅動。STM32輸出PWM可調速,可正反轉。
數碼管顯示
基于TM1637的四位數碼管,用于顯示一些簡單的信息。TM1637是天微公司出品的LED驅動專用芯片,集成簡單,開發方便。除了TM1637,天微公司還有一系列TM16XX的芯片,主要區別就是位輸出和段輸出個數多少。
電池和充放電模塊
18650電池具有容量大,壽命長,安全性高等優點,普遍使用在充電寶,筆記本電池、儀器儀表中,甚至特斯拉的電池組也是由7000多節這樣的電池組合成的。充放電線路板,主要的作用是將電池3.7V的輸出電壓升壓到5V,供給STM32及外圍電路使用,同時還具備給電池充電的功能。實際上等同于一個充電寶,充電寶的原理就是若干個18650電池并聯,再用一塊充放電板管理而已。
03 嵌入式軟件
紅外遙控器驅動
HX1838采用的是 NEC 編碼格式。載波頻率為38khz。
邏輯1是2.25ms,脈沖時間560us;邏輯0為1.12ms,脈沖時間560us。根據脈沖時間長短來解碼。
協議示意圖:
一組數據組成:
起始是9ms的高電平脈沖
4.5ms的低電平
8位地址碼,低位在前
8位地址碼的反碼,用于校驗
8位命令碼,低位在前
8位命令碼的反碼。
需要注意的是1838紅外一體接收頭為了提高接受靈敏度。輸入高電平,其輸出的是相反的低電平。實際測量接收到的按鍵波形如下圖,可以看到電平是相反的。
代碼實現:
使用下降沿外部中斷作為一次按鍵的檢測觸發,然后每個20us讀取一次數據管腳,按照上述的協議邏輯,讀出一個u32的數據。
U8 Infrared_Receiver_Process(void){ U16 nTime_Num = 0; U8 nData = 0; U8 nByte_Num = 0; U8?nBit_Num?= 0; nTime_Num = Infrared_Receiver_GetLowLevelTime();//t0=nTime_Num;if((nTime_Num >= 500) || (nTime_Num = 250) || (nTime_Num = 60) || (nTime_Num =60) && (nTime_Num =10) && (nTime_Num
經過調試,得到遙控器的按鍵鍵值如下:
KEY_OK = 0x38,KEY_UP = 0x18,KEY_DOWN = 0x4a,KEY_LEFT = 0x10,KEY_RIGHT = 0x5a,KEY_1 = 0xa2,KEY_2 = 0x62,KEY_3 = 0xe2,KEY_4 = 0x22,KEY_5 = 0x02,KEY_6 = 0xc2,KEY_7 = 0xe0,KEY_8 = 0xa8,KEY_9 = 0x90,KEY_STAR = 0x68,KEY_0 = 0x98,KEY_SHARP = 0x80,
另外由于NEC編碼格式的紅外遙控廣泛的用于電視遙控,我們也可以找一個電視遙控器,把鍵值讀出來,寫入到stm的程序中,這樣就可以用電視遙控器來操作小船了。?
數碼管顯示驅動
TM1637與其他的一些TM芯片有一些區別,沒有同步通信的STB腳,控制時序也有相應改變。
在輸入數據時當CLK是高電平時,DIO上的信號必須保持不變;只有CLK上的時鐘信號為低電平時,DIO上的信號才能改變。數據輸入的開始條件是CLK為高電平時,DIO由高變低;結束條件是CLK為高時,DIO由低電平變為高電平。TM1637的數據傳輸帶有應答信號ACK,當傳輸數據正確時,會在第八個時鐘的下降沿,芯片內部會產生一個應答信號ACK將DIO管腳拉低,在第九個時鐘結束之后釋放DIO口線。
代碼實現:
void start(void){dio_output();GPIO_SetBits(CLK_GPIO_PORT,CLK_GPIO_PIN);GPIO_SetBits(DIO_GPIO_PORT,DIO_GPIO_PIN);Delay();GPIO_ResetBits(DIO_GPIO_PORT,DIO_GPIO_PIN);Delay();} ?void stop(void){dio_output();/*GPIO_ResetBits(CLK_GPIO_PORT,CLK_GPIO_PIN);Delay();*/GPIO_ResetBits(DIO_GPIO_PORT,DIO_GPIO_PIN);Delay();GPIO_SetBits(CLK_GPIO_PORT,CLK_GPIO_PIN);Delay();GPIO_SetBits(DIO_GPIO_PORT,DIO_GPIO_PIN);Delay();} ?void ack(void){u8 i;dio_input();GPIO_ResetBits(CLK_GPIO_PORT,CLK_GPIO_PIN);Delay();while(readDio()==1&&(i>= 1; GPIO_SetBits(CLK_GPIO_PORT,CLK_GPIO_PIN); Delay();}ack();}void tm1637_DispStr(u8 *str){u8 i;u8 ledCode[5]; ?for(i = 0;i
小船控制邏輯
目前的邏輯比較簡單, 就是收到按鍵后控制電機的轉停。
void dealIrKeyDown(){u8 len;len = irKeyfifo_count;if(len > 0){ irKeyDataOut = irKeyfifo_DataOut(); //按鍵顯示 U8ToHexstr(irKeyDataOut.index,dispStr); U8ToHexstr(irKeyDataOut.cmd,dispStr+2); tm1637_DispStr(dispStr); switch(irKeyDataOut.cmd){ case KEY_OK: case KEY_OK_CHANGHONG: motor1_Stop(); motor2_Stop(); break; case KEY_UP: case KEY_UP_CHANGHONG: motor1_ForwardRun(); motor2_ForwardRun(); break; case KEY_DOWN: case KEY_DOWN_CHANGHONG: motor1_BackwardRun(); motor2_BackwardRun(); break; case KEY_LEFT: case KEY_LEFT_CHANGHONG: motor1_ForwardRun(); motor2_Stop(); state = SHIP_LEFT; break; case KEY_RIGHT: case KEY_RIGHT_CHANGHONG: motor2_ForwardRun(); motor1_Stop(); state = SHIP_RIGHT; break; default: motor1_Stop(); motor2_Stop(); state = SHIP_STOP; break; } }}
04 DIY成品展示和演示視頻
05 總結存在的問題
遙控不靈敏
畢竟紅外遙控的使用場合并不是移動的物體上,再加上距離近,經常會出現遙控要按多次的情況。后期考慮改進stm的協議處理方法,增加容錯性,如果還是不行就考慮升級為2.4G專用的玩具遙控。
防水性問題
正常使用不會有水進入,但是在沒有大人的情況下交給小孩操作,就沒那么保險了。
后期考慮升級防水性能。
沒有聲光電,不夠酷炫。
考慮升級,增加led燈條。