若是說,SharePoint 的價值之一在於提供了幾乎開箱即用的 innovation 環境,那麼,智能設備的開發平臺也同樣。沒必要每次都從頭開始,因此須要固定的工做室和開發平臺做爲創新的起點,這樣就會每次比從零開始「高一點點」。php
固然,這裏不是沒有糾結的。平臺畢竟不是最終的產品,平臺太弱當然難以支撐創新,但平臺太強則臃腫和僵化一樣也會限制創新:面對成百上千的類型、接口的時候,即便作一個小玩意兒也要學上一年半載,任何人都會畏懼的。有那個時間,不如本身寫一個出來了。因此成功的開發平臺如 jQuey 和 jQuery UI 是拆開的,Bootstrap 是能夠按模塊自定義下載的。html
與 SharePoint 在企業協做領域的一家獨大不一樣,智能控制設備的平臺可謂五花八門,這就有點兒像 python 世界的 web framework。看似很繁榮,可是缺乏標準,哪一個都難以讓人下決心深刻鑽研(貌似如今有往 django 靠攏的趨勢,這是件好事)。因此,在試過了網上各類開發板之後,Jony 下了決心要搭建本身的開發平臺,知足本身的實際須要。node
在這裏,我想起來《西遊記》裏面的故事:孫悟空學藝歸來回到花果山之後,第一件事情就是去找一件稱手的兵器,而後幾番周折,最終獲得瞭如意金箍棒。認真對待技術的人,搭建本身的開發平臺,好像也有點兒這個意思。從這個角度說,《西遊記》真的不是一本簡單的魔幻現實主義小說。python
智能控制設備的開發平臺,有 2 個大的技術決策:1,用什麼 MCU(微控制器);2,用什麼操做系統。linux
主流的 MCU 陣營有這麼幾個:git
ARM 9, 11 系列(包括樹莓派)已經不是簡單的控制器了(那就是小電腦),太過龐大,做爲微處理器還行,僅僅拿來作控制器則太過複雜、並且耗電。github
並且都用到那個級別了,我爲何不弄個微型 PC 吶?!直接上 .net 也好拯救我幾個腦細胞。web
補充:雖然 LPC 43xx 那麼貴,那麼複雜,那麼沒有性價比,但不知道爲何,我仍是常常會想起它。 django
複雜的控制器平臺上面固然要有操做系統啦!編程
20k 字節的內存那顯然是跑不起 windows 的(DOS 都不行),且架構也不一樣。可是,微控制器用的開源操做系統已經有不少了,夠用了。
備選的有這麼幾個:
相比「裸機」跑代碼來講,在微控制器上面跑操做系統,設計和實現的難度瞬間上去了,但它能顯著下降應用的難度,這個紮實的地基雖然很難挖,但能支撐百層的高樓。凡是繞不過去的,仍是早點兒開始接觸、掌握好。
萬能的 Github:https://github.com/RT-Thread/rt-thread
rt-thread 的官網:http://www.rt-thread.org。文檔呢,官網是有的,不過,真的是隻能做爲參考,很明顯是開發人員的過後開發筆記整理的。目前仍是隻能經過看代碼來理解詳細的使用方式,從文檔和論壇的隻言片語裏面,是難以還原真相的。不過,就如上面所說,rt-thread 的好處就是它的版本還比較小,即使缺少文檔,也是能夠看源碼看下去的。謝天謝地。
如何從意法半導體的官網 http://www.st.com 上找到資料並下載下來,是個技術活兒。該官網的搜索功能是個奇葩,比百度的搜索結果還難以讓人理解。
基本上,只能先輸入想找的芯片的型號,而後從結果裏面再找下載。
STM32 的固件庫封裝了不少針對芯片的操做以及寄存器設置,若是不用固件庫,你要讓芯片的某個引腳輸出高電平,要這樣寫:GPIOD->BSRR = 0x00000005; 用了固件庫,你就能夠這樣寫:GPIO_SetBits(GPIOD, GPIO_Pin_0|GPIO_Pin_2); (提示:2^0 + 2^2 = 5)。雖然能夠對應起來,但實在沒有必要折磨本身,仍是用固件庫吧。
曾經有高人說過「給我看他的數據結構,我就知道他的代碼是怎麼寫的。」我說「給我看他的目錄結構,我就知道他的方案是怎麼設計的。」(說到數據結構這件事,我不得不說,json 在這方面作出了巨大貢獻,如同 c 語言的 struct,只不過是跨語言平臺的。哪怕是作 SharePoint 項目,我也會先考慮定義好適當的 json 對象。)
當我說到「工程」的時候,實際上是指 workspace、solution,而非「項目」。「項目」叫作 project。一個工程能夠包含多個項目。「分而治之」是人類處理復瑣事物的基本策略。
問:哪些會變化,哪些不會?
答:全部固件庫、開源組件都被認爲不會由於我本身的應用須要而變化,只有本身應用寫的會。前者只須要隨着發行方升級便可,Github 上面拉下來也好、從官網下載文件解壓覆蓋也好,均可以。
工程目錄佈局規則以下:
ST 微控制器 MCU 的開發 IDE,有 3 大陣營
不知道爲何,我選了 IAR。這中間固然有不少故事,由於個人確 3 種都嘗試過,不過,年紀大了,已經記不得這些經歷了。不幸的是,rt-thread 雖然有針對 IAR 的移植,可是開發團隊用的是 MDK(Keil),因而在配置工程的時候多了一些波折。
下面是配置的步驟和對應的選項。
打開配置選項窗口 | |
選擇合適的 Device | |
勾上「Using CMSIS」 | |
輸出全部的彙編連接信息 | |
設置編譯器的頭文件搜索路徑以及與定義的宏 | 頭文件搜索路徑以下: $PROJ_DIR$\..\..\STLib\STM32F10x\CMSIS\CM3\DeviceSupport\ST\STM32F10x 預約義的宏以下: STM32F10X_MD 其中: STM32F10X_MD 表示採用的 MCU 系列和密度,能夠在芯片手冊裏面查到。 HSE_VALUE 是 MCU 外部晶振的頻率,按照你的實際須要修改。 SYSCLK_FREQ_24MHZ 是總線頻率,也是按照實際須要修改。 |
指定默認的連接器配置文件 | 須要使用 rt-thread 的 bsp 目錄下對應的 MCU 的配置文件。不然燒錄進去的程序在芯片中的佈局會不正確,致使 finsh 組件沒法正確運行:finsh 移植遇到了問題,始終輸出"Null node" |
指定調試工具 | 我用的 ST-LINK,因此選擇 ST-LINK。 |
USE Flash Loader | |
設置 ST-LINK | 好了之後能夠點 OK 關閉選項設置對話框,而後開始項目文件設置。 |
引用 ST 固件庫 | firmware 文件組下面的文件所有來自 STLib 文件夾,引用便可。可是,注意 system_stm32f10x.c 文件,這裏麪包含 MCU 初始化時鐘的設置。若是你沒有如上面步驟同樣在編譯器的預約義宏裏面定義時鐘,那麼,能夠修改這個文件來實現。 |
引用 rt-thread 文件(不一樣的組件放在不一樣的子文件組裏面) | 注意,這裏面的 rtconfig.h 是拷貝到項目目錄下面的 RT-Thread 子目錄裏面的那個,由於須要結合應用的須要進行修改。 |
拷貝 rt-thread bsp 目錄裏面的驅動程序到 Driver 目錄下 |
按照上面的方式創建好工程和項目之後,已經能夠開始一個簡單的小程序作測試了,讓板上的 LED 每一秒閃爍一次並經過串口來顯示 finsh。
首先,在 Board 文件組下面創建 board.h、board.c 來定義本身的硬件環境的配置。
board.h 裏面的宏定義 STM32_SRAM_SIZE 很重要,須要根據你所用的 MCU 的實際內存大小來設置。
1: /* board.h */
2: #ifndef __BOARD_H__
3: #define __BOARD_H__
4:
5: #define STM32_EXT_SRAM 0
6: // <o>Begin Address of External SRAM
7: // <i>Default: 0x68000000
8: #define STM32_EXT_SRAM_BEGIN 0x68000000 /* the begining address of external SRAM */
9: // <o>End Address of External SRAM
10: // <i>Default: 0x68080000
11: #define STM32_EXT_SRAM_END 0x68080000 /* the end address of external SRAM */
12: // </e>
13:
14: // <o> Internal SRAM memory size[Kbytes] <8-64>
15: // <i>Default: 64
16: #define STM32_SRAM_SIZE 20
17: #define STM32_SRAM_END (0x20000000 + STM32_SRAM_SIZE * 1024)
18:
19: void board_initiate();
20:
21: /* LED */
22: #define USING_LED1
23: #define led1_periph_clock RCC_APB2Periph_GPIOC
24: #define led1_port GPIOC
25: #define led1_pin (GPIO_Pin_13)
26:
27: #endif
1: /* board.c */
2: #include <rthw.h>
3: #include <rtthread.h>
4:
5: #include <stm32f10x.h>
6: #include "board.h"
7: #include "usart.h"
8:
9: void board_initiate(){
10: SystemInit();
11: SysTick_Config( SystemCoreClock / RT_TICK_PER_SECOND );
12:
13: rt_hw_usart_init();
14: rt_console_set_device(RT_CONSOLE_DEVICE_NAME);
15: }
16:
17: void SysTick_Handler(void)
18: {
19: rt_interrupt_enter();
20: rt_tick_increase();
21: rt_interrupt_leave();
22: }
而後,在 Driver 文件組裏面創建 led.h、led.c 的驅動。
1: /* led.h */
2: #ifndef __LED_H__
3: #define __LED_H__
4:
5: #include <stdint.h>
6:
7: void led_initiate(void);
8: void led_on(uint8_t led);
9: void led_off(uint8_t led);
10:
11: #endif
1: /* led.c */
2: #include <stm32f10x.h>
3: #include "led.h"
4: #include <board.h>
5:
6: void led_initiate(void)
7: {
8: GPIO_InitTypeDef GPIO_InitStructure;
9: GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
10: GPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz;
11:
12: #ifdef USING_LED1
13: RCC_APB2PeriphClockCmd(led1_periph_clock,ENABLE);
14: GPIO_InitStructure.GPIO_Pin = led1_pin;
15: GPIO_Init(led1_port, &GPIO_InitStructure);
16: #endif
17: }
18:
19: void led_on(uint8_t n)
20: {
21: #ifdef USING_LED1
22: if(n!=0)return;
23: GPIO_SetBits(led1_port, led1_pin);
24: #endif
25: }
26: void led_off(uint8_t n)
27: {
28: #ifdef USING_LED1
29: if(n!=0)return;
30: GPIO_ResetBits(led1_port, led1_pin);
31: #endif
32: }
而後,在 App 文件組裏面創建 app.h、app.c 文件,裏面的內容和具體應用相關。
1: /* app.h */
2: #ifndef __APP_H__
3: #define __APP_H__
4:
5: int app_initiate(void);
6:
7: #endif /* __APP_H__ */
1: /* app.c */
2: #include <rthw.h>
3: #include <rtthread.h>
4:
5: #include "app.h"
6: #include "led.h"
7:
8: #ifdef RT_USING_COMPONENTS_INIT
9: #include <components.h>
10: #endif /* RT_USING_COMPONENTS_INIT */
11:
12: static rt_uint8_t led_stack[256];
13: static struct rt_thread led_thread;
14: static void led_thread_entry(void* parameter)
15: {
16: led_initiate();
17: while (1)
18: {
19: led_on(0);
20: rt_thread_delay( RT_TICK_PER_SECOND/2 );
21: led_off(0);
22: rt_thread_delay( RT_TICK_PER_SECOND/2 );
23:
24: }
25: }
26:
27: int app_initiate(void)
28: {
29: rt_err_t result;
30: /* init led thread */
31: result = rt_thread_init(&led_thread,
32: "led",
33: led_thread_entry,
34: RT_NULL,
35: (rt_uint8_t*)&led_stack[0],
36: sizeof(led_stack),
37: 20,
38: 5);
39: if (result == RT_EOK)
40: {
41: rt_thread_startup(&led_thread);
42: }
43:
44: #ifdef RT_USING_COMPONENTS_INIT
45: /* initialization RT-Thread Components */
46: rt_components_init();
47: #endif
48:
49: return 0;
50: }
其中 led 線程的堆棧長度是 256 字節,這是實踐出來的。一開始,能夠給一個儘可能大的堆棧尺寸,而後,運行時經過 finsh 的 list_thread() 命令輸出其實際使用的最大的堆棧尺寸,最後回來修改。
接下來打開 rtconfig.h 確保裏面的 #define RT_USING_FINSH 沒有被註釋掉。
最後,在 main.c 裏面加入啓動代碼。
1: /* main.c */
2: #include <rthw.h>
3: #include <rtthread.h>
4: #ifdef __CC_ARM
5: extern int Image$$RW_IRAM1$$ZI$$Limit;
6: #elif __ICCARM__
7: #pragma section="HEAP"
8: #else
9: extern int __bss_end;
10: #endif
11:
12: #include <stdint.h>
13: #include "board.h"
14: #include "app.h"
15:
16: int main()
17: {
18: board_initiate();
19: #ifdef RT_USING_HEAP
20: #if STM32_EXT_SRAM
21: rt_system_heap_init((void*)STM32_EXT_SRAM_BEGIN, (void*)STM32_EXT_SRAM_END);
22: #else
23: #ifdef __CC_ARM
24: rt_system_heap_init((void*)&Image$$RW_IRAM1$$ZI$$Limit, (void*)STM32_SRAM_END);
25: #elif __ICCARM__
26: rt_system_heap_init(__segment_end("HEAP"), (void*)STM32_SRAM_END);
27: #else
28: /* init memory system */
29: rt_system_heap_init((void*)&__bss_end, (void*)STM32_SRAM_END);
30: #endif
31: #endif /* STM32_EXT_SRAM */
32: #endif /* RT_USING_HEAP */
33: /* init scheduler system */
34: rt_system_scheduler_init();
35: /* initialize timer */
36: rt_system_timer_init();
37: /* init timer thread */
38: rt_system_timer_thread_init();
39: /* init application */
40: app_initiate();
41: /* init idle thread */
42: rt_thread_idle_init();
43: /* start scheduler */
44: rt_system_scheduler_start();
45: /* never reach here */
46: return 0;
47: }
48:
49: void assert_failed(uint8_t* file, uint32_t line)
50: {
51: while (1) ;
52: }
本着省錢、夠用的原則,我選擇了 STM32F103C8T6,不到 10 元一片,管腳對於個人測試應用足夠。
爲了確保 MCU 芯片能夠知足須要,建議先用 ST 自家出的 MicroXplorer 來規劃一下芯片上面外設和 IO 的分配,作到心中有數。
圍繞着芯片的選型,就能夠開始電路設計了。
下面是 MCU 的核心電路:
核心電路的 P1 是 SWD 的編程接口,其管腳的排列是與 ST 的 STM32F4 Discovery 開發板兼容的,這樣就能夠直接經過 STM32F4 Discovery 開發板對本身的實驗電路進行調試了。
而後,就能夠編譯並下載到芯片中運行了。
下面貼一個實際的效果(裏面不只僅有 LED 了)。
跑一下 finsh,能夠看到不少有價值的系統運行數據,如內存的使用狀況,各個線程的堆棧使用狀況等等。
這樣,開發平臺搭建完成,接下來就看本身的想象力了。
P.S. 這段時間看似有點兒遊手好閒了,後面會把 SharePoint 的時間補回來的。
■ 本文結束