最近因為某些原因,工作環境從windows向linux遷移了。原本在windows下開發STM32,現在要改用在linux上開發。 首先簡單地描述一下軟硬體開發環境。
宿主機: 作業系統:CentOS7 x86_64 ( grome案頭版 ) IDE:Eclipse Luna JLink驅動:JLink_Linux_V434a 工具鏈:GNU-ARM-Toolchains-4.9-2014q4 GDB:arm-linux-gdb V7.8.1
硬體平台: CPU:STM32F103C8T6 JLink:V8 USB轉串口:Silicon Labs CP2101
環境的搭配都是比較簡單的,編譯工具鏈都比較容易裝。 這裡就不再介紹了。下面就只附上工具鏈的下載連結。 https://launchpad.net/gcc-arm-embedded IDE環境Eclipse Luna,可以直接在官網上下載。還有CDT,也可以在Eclipse官網上下載。 有一個比較重要的 GNU ARM Eclipse開發外掛程式,附上個連結。 http://sourceforge.net/projects/gnuarmeclipse/ 還有一個Eclipse外掛程式,用於GDB調試的,叫 Zylin -embedded CDT。下載網址如下: http://opensource.zylin.com/zylincdt 另外,是JLink的驅動,當中包含GDBServer,我用的是V434a的版本,是買其它教學視頻的時候附上的。Segger官網上好像已經找不到這個下載連結了,可以百度一下,也可以在Segger官網上下載其它版本的。(只要有正版JLink的其實啥版本都沒問題,不過用D版的就要注意一下了,不建議用那麼高的版本,不然會用不了)這裡就不附連結了。 最後還有Arm-linux-GDB,可以從下面的官網上下載。編譯和安裝只要按照README去做就ok了。非常簡單。 http://lists.gnu.org/archive/html/info-gnu/2014-10/msg00018.html
說了這麼久的環境,一開始自己摸索的時候覺得配環境很麻煩,而且也裝了很多不必要的東西,後來發現其實只要裝上述的就夠了,非常簡單。希望能協助大家少走點彎路。下面就用一個簡單的樣本程式來說一下怎麼編譯、下載、調試。在原來的windows環境中,我是用Keil uVision5進行開發的,也留了一些代碼,所以就直接拿了個串口的發送程式作為這一次的樣本,代碼如下。
/*=====================================Include headers=====================================*/#include "stm32f10x.h"/*=====================================Functions definition=====================================*/void UART_DefaultConfiguration( void );/*=====================================Implementation of functions=====================================*//** * @brief * Main program. * @parameter * None * @returnvalue * None */int main(void){ UART_DefaultConfiguration(); while(1) { if( USART_GetFlagStatus( USART1, USART_FLAG_TC ) == SET ) { USART_SendData( USART1, 'A' ); } } }/** * @brief * Initialize the UART with the default configuration. * @parameter * None * @returnvalue * None */void UART_DefaultConfiguration( void ){ GPIO_InitTypeDef GPIO_InitStruct; USART_InitTypeDef USART_InitStruct; /* Turn on the Clock for the UART and GPIO. */ RCC_APB2PeriphClockCmd( RCC_APB2Periph_GPIOA | RCC_APB2Periph_AFIO | RCC_APB2Periph_USART1, ENABLE ); /* Configure the UART1. */ GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF_PP; GPIO_InitStruct.GPIO_Pin = GPIO_Pin_9; /* TX */ GPIO_InitStruct.GPIO_Speed = GPIO_Speed_2MHz; GPIO_Init( GPIOA, &GPIO_InitStruct ); GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IPU; GPIO_InitStruct.GPIO_Pin = GPIO_Pin_10; /* RX */ GPIO_Init( GPIOA, &GPIO_InitStruct ); USART_InitStruct.USART_BaudRate = 115200; USART_InitStruct.USART_HardwareFlowControl = USART_HardwareFlowControl_None; USART_InitStruct.USART_Mode = USART_Mode_Rx | USART_Mode_Tx; USART_InitStruct.USART_Parity = USART_Parity_No; USART_InitStruct.USART_StopBits = USART_StopBits_1; USART_InitStruct.USART_WordLength = USART_WordLength_8b; USART_Init( USART1, &USART_InitStruct ); USART_Cmd( USART1, ENABLE );}
代碼很短,也就八十來行。整個工程的結構如下:
說明整個工程在windows下是能編譯通過的。至於在開發板上的測試就不貼出來了,測試的結果就是在串口上可以看到不斷地有字母‘A'輸出。
下面說一下整個工程怎麼搬到Linux的開發平台上。 首先,在Linux上開啟Eclipse。建立一個C Project.如果環境搭建成功的話,可以看到有如下選項,在Executable中選擇STM32F10x C/C++ Project,工具鏈就選Cross ARM GCC,填寫好工程名字TestSTM32後點擊下一步:
由於我的CPU是STM32F103C8T6,所以Chip family是選STM32f10x Medium Density,Flash大小是64KB,RAM大小是20KB。可以根據自己的硬體實際情況進行填寫。其它的可以按圖配置。點擊下一步:
其實之後兩頁都可以按下一步,直到最後這一頁,一定要選好工具鏈是GN Tools for ARM Embedded Processors ( arm-none-eabi-gcc ),並且填好工具鏈位置。最後點擊完成。
工程建立後,可以看到Eclipse已經將工程建好在Project Explorer中了。
然後把工程中的src、system、include三個檔案夾去掉,只剩下ldscripts檔案夾。把我們樣本程式的代碼複製過來。如下圖所示:
其實為什麼要把之前說的三個檔案夾刪去而不是直接在模板上把代碼複製過去呢。其實直接在模板上添代碼也行,模板用的也是3.5版本的韌體庫,但是要是以後韌體庫升級了或者不想用韌體庫呢。刪去的目的就是為了可以讓我們構建的工程更具有自由性。所以我選擇直接把原來的工程代碼複製過來。 接下來是比較重要的一步,把開機檔案startup_stm32f10x_md.s替換掉,從ST官網上下載的韌體庫包中,把STM32F10x_StdPeriph_Lib_V3.5.0/Libraries/CMSIS/CM3/DeviceSupport/ST/STM32F10x/startup/gcc_ride7/startup_stm32f10x_md.s複製到工程中,並把開機檔案的尾碼s改為大寫的S。如下圖所示:
要這樣替換的原因是在windows中,所用的開機檔案是針對Keil MDK這個IDE環境的,而現在我們要用GNU的工具鏈,所以應該要用回針對GNU開機檔案。不然連結的時候會有問題。
接下來就要開始配置一些東西了,右鍵工程開啟屬性。首先在C/C++ Build->Settings->Tool Settings標籤中,找到Target Processor,在右邊找到Instruction set,選擇Thumb。找到Endianness,選擇Little endian。如下圖所示。
然後,在Cross ARM GNU Assembler、Cross ARM C Compiler和Cross ARM C++ Compiler中的Preprocessor中定義兩個宏:STM32F10X_MD和USE_STDPERIPH_DRIVER。其它的全部刪去,這一個很重要,關係到後面的編譯問題。一開始Eclipse會幫你定義好多宏,但都不需要,只留這兩個就行了。
然後,還是在Cross ARM GNU Assembler、Cross ARM C Compiler和Cross ARM C++ Compiler中的Includes修改為你包含有標頭檔的路徑。如下圖所示。
接下來,是在Cross ARM C++ Linker和General中,把原本工程裡的連結指令碼路徑補全。如下圖所示:
好了,這些設定好了之後,可以編譯了嗎。理論上是可以的,不過還有些代碼要修改。這是由於韌體庫寫的地方有些問題,參考文章如下: https://answers.launchpad.net/gcc-arm-embedded/+question/217817 修改如下: 在core_cm3.c中,找到uint32_t __STREXB(uint8_t value, uint8_t *addr)和uint32_t __STREXH(uint16_t value, uint16_t *addr)。把這兩個函數中的"=r"改為"=&r",如下圖所示:
好,現在編譯可以編譯了。
可以看到最後是編譯成功了,而且對比於在Keil MDK裡編譯的檔案大小,程式碼片段所佔的大小更小。這個編譯效率還是令人滿意的。 編譯了程式之後,怎麼樣才能下載到板子裡並且進行調試呢。下面開始說明一下。
在調試按鈕旁邊有個下拉式箭頭,點擊箭頭後點擊Debug Configurations。然後雙擊Zylin Embedded debug( Native )。這樣就會產生一個名稱和工程名字相同的調試配置介面。如下圖所示。
在介面中點擊Debugger標籤。找到GDB debugger的文字框,通過瀏覽定位到上面提到要安裝的arm-linux-gdb,然後點擊應用。如下圖所示。
接著點擊Commands標籤。在'Initalize' commands裡輸入對JLink的初始化命令。點擊應用。如下圖所示。
好了,問題就來了,這個初始化命令是怎麼呢。究竟要怎麼寫呢。在這裡我先給出我自己的初始化命令指令碼。大家可以根據自己的實際情況進行修改。
target remote localhost:2331 monitor halt monitor interface JTAG monitor speed 1000 monitor endian little monitor flash cpuclock = 72000000 monitor flash device = STM32F103C8 monitor flash download = 1 monitor flash breakpoints = 1 load Debug/TestSTM32.elf monitor reg r13 = (0x00000000) monitor reg pc = (0x00000004)
這個指令碼做了些什麼呢。首先定義了gdb server的連接埠,接下來不斷地往stm32和jlink下達命令:讓stm32停機、設定使用JTAG介面,速度設定為1000kHz等等。最後下載程式並複位stm32。 一開始的時候我也不知道要寫怎麼樣的初始化指令碼以及初始化命令。後來在Segger官網上找到了關於JLinkGDBServer的使用者手冊,其官網頁面及下載地址如下: 官網頁面: https://www.segger.com/jlink-gdb-server.html 使用者手冊下載地址: https://www.segger.com/admin/uploads/productDocs/UM08005_JLinkGDBServer.pdf 重點參考3.4 Debugging on Cortex-M Devices和3.5 Supported remote commands這兩節,指令碼模板和命令解析都在這裡了。 設定好了這個頁面之後不要急著關掉,一會調試就從這個頁面開始的。插上JLink,串連好硬體到PC裡。現在要手動啟動gdb server。在JLink的驅動包裡面,有一個可執行檔叫JLinkGDBServer。通過命令列啟動它(最好就直接用root使用者啟動)。如下圖所示。如果你硬體已經串連好了,驅動什麼的都串連好了之後,就會提示你找到了Cortex-M3的裝置。這樣就正常了。注意,在調試的過程中不能關閉JLinkGDBServer,就讓它這樣開著就ok了。不然會調試不了的。
接下來,在剛才Eclipse的Debug Configurations頁面裡,點擊Debug。開始調試。這樣就會跳到Eclipse的調試視圖裡,並且調試停留在開機檔案的第一行彙編指令裡。這樣就說明調試已經成功了,之後怎麼樣偵錯工具就和在eclipse裡調試其它pc程式一樣。這裡就不多說了。
好了,在Linux環境裡開發STM32的整個流程就已經介紹完了。寫這一篇文章的目的只是為了記錄一下過程,方便日後忘了步驟的時候進行查看,也希望能夠協助其他想在linux環境下開發stm32的人少走些彎路。