Linux环境开发STM32,从环境到调试

    最近因为某些原因,工作环境从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 );
}

   代码很短,也就八十来行。整个工程的结构如下:
Linux环境开发STM32,从环境到调试_第1张图片
    说明整个工程在windows下是能编译通过的。至于在开发板上的测试就不贴出来了,测试的结果就是在串口上可以看到不断地有字母‘A'输出。

    下面说一下整个工程怎么搬到Linux的开发平台上。
    首先,在Linux上打开Eclipse。新建一个C Project.如果环境搭建成功的话,可以看到有如下选项,在Executable中选择STM32F10x C/C++ Project,工具链就选Cross ARM GCC,填写好工程名字TestSTM32后点击下一步:
Linux环境开发STM32,从环境到调试_第2张图片

    由于我的CPU是STM32F103C8T6,所以Chip family是选STM32f10x Medium Density,Flash大小是64KB,RAM大小是20KB。可以根据自己的硬件实际情况进行填写。其它的可以按图配置。点击下一步:

Linux环境开发STM32,从环境到调试_第3张图片

    其实之后两页都可以按下一步,直到最后这一页,一定要选好工具链是GN Tools for ARM Embedded Processors ( arm-none-eabi-gcc ),并且填好工具链位置。最后点击完成。

Linux环境开发STM32,从环境到调试_第4张图片

   工程建立后,可以看到Eclipse已经将工程建好在Project Explorer中了。

Linux环境开发STM32,从环境到调试_第5张图片

    然后把工程中的src、system、include三个文件夹去掉,只剩下ldscripts文件夹。把我们示例程序的代码复制过来。如下图所示:

Linux环境开发STM32,从环境到调试_第6张图片

    其实为什么要把之前说的三个文件夹删去而不是直接在模板上把代码复制过去呢?其实直接在模板上添代码也行,模板用的也是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。如下图所示:
Linux环境开发STM32,从环境到调试_第7张图片

    要这样替换的原因是在windows中,所用的启动文件是针对Keil MDK这个IDE环境的,而现在我们要用GNU的工具链,所以应该要用回针对GNU启动文件。不然链接的时候会有问题。

    接下来就要开始配置一些东西了,右键工程打开属性。首先在C/C++ Build->Settings->Tool Settings标签中,找到Target Processor,在右边找到Instruction set,选择Thumb。找到Endianness,选择Little endian。如下图所示。
Linux环境开发STM32,从环境到调试_第8张图片

    然后,在Cross ARM GNU Assembler、Cross ARM C Compiler和Cross ARM C++ Compiler中的Preprocessor中定义两个宏:STM32F10X_MD和USE_STDPERIPH_DRIVER。其它的全部删去,这一个很重要,关系到后面的编译问题。一开始Eclipse会帮你定义好多宏,但都不需要,只留这两个就行了。

Linux环境开发STM32,从环境到调试_第9张图片

    然后,还是在Cross ARM GNU Assembler、Cross ARM C Compiler和Cross ARM C++ Compiler中的Includes修改为你包含有头文件的路径。如下图所示。

Linux环境开发STM32,从环境到调试_第10张图片

    接下来,是在Cross ARM C++ Linker和General中,把原本工程里的链接脚本路径补全。如下图所示:

Linux环境开发STM32,从环境到调试_第11张图片

    好了,这些设置好了之后,可以编译了吗?理论上是可以的,不过还有些代码要修改。这是由于固件库写的地方有些问题,参考文章如下:
     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",如下图所示:
Linux环境开发STM32,从环境到调试_第12张图片

    好,现在编译可以编译了。

Linux环境开发STM32,从环境到调试_第13张图片

    可以看到最后是编译成功了,而且对比于在Keil MDK里编译的文件大小,代码段所占的大小更小。这个编译效率还是令人满意的。
    编译了程序之后,怎么样才能下载到板子里并且进行调试呢?下面开始说明一下。

    在调试按钮旁边有个下拉箭头,点击箭头后点击Debug Configurations。然后双击Zylin Embedded debug( Native )。这样就会生成一个名称和工程名字相同的调试配置界面。如下图所示。

Linux环境开发STM32,从环境到调试_第14张图片

    在界面中点击Debugger标签。找到GDB debugger的文本框,通过浏览定位到上面提到要安装的arm-linux-gdb,然后点击应用。如下图所示。

Linux环境开发STM32,从环境到调试_第15张图片

    接着点击Commands标签。在'Initalize' commands里输入对JLink的初始化命令。点击应用。如下图所示。

Linux环境开发STM32,从环境到调试_第16张图片

    好了,问题就来了,这个初始化命令是怎么呢?究竟要怎么写呢?在这里我先给出我自己的初始化命令脚本。大家可以根据自己的实际情况进行修改。

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了。不然会调试不了的。
Linux环境开发STM32,从环境到调试_第17张图片

    接下来,在刚才Eclipse的Debug Configurations页面里,点击Debug。开始调试。这样就会跳到Eclipse的调试视图里,并且调试停留在启动文件的第一行汇编指令里。这样就说明调试已经成功了,之后怎么样调试程序就和在eclipse里调试其它pc程序一样。这里就不多说了。

Linux环境开发STM32,从环境到调试_第18张图片

    好了,在Linux环境里开发STM32的整个流程就已经介绍完了。写这一篇文章的目的只是为了记录一下过程,方便日后忘了步骤的时候进行查看,也希望能够帮助其他想在linux环境下开发stm32的人少走些弯路。

你可能感兴趣的:(嵌入式,STM32)