第一次在这发博文,先上一篇干货,记录了如何在MDK下新建一个STM32L系列的工程,虽然事情比较低端,但是加了一些自己的思考,算作记录。

之前都是研究例程或者在官方模板工程上改的程序,没有新建过工程。

昨晚试了下新建工程成功了,但是今天早上打开工程文件发现MDK报错说文件不能读取,猜测可能是虚拟机异常退出导致的。于是重新建了个,加深了下对环境的理解。步骤如下。

  1. 准备好库文件

可以去ST官网http://www.st.com或者官方社区http://www.stmcu.org下载。大力推荐后者,软硬件文档和库都非常齐全,归类有序。不少型号的芯片还有中文文档。

库文件说明

解压下载来的zip,得到的文件或文件夹如下。

MDK下STM32L1系列工程的新建_STM32

几个关键的简单说明一下:

  1. 文件夹Libraries:库的本体,编程的时候只要这个文件夹里面的内容就可以了。

  2. 文件夹Project:官方例程,非常全。但是我只能看看,因为没有和这个例程配套的评估板。

  3. 文件夹Utilities:开发板相关的库函数。有2种评估板,淘宝都要2k多一块,吓尿了,相比之下我买的STM32L-Discovery,80不到还自带ST-Link,简直不要钱。╮(╯▽╰)╭

  4. CHM文件STM32l1xx...:帮助文档,挺有用的。其中大部分内容是根据库中的代码注释自动生成的。

使用MDK新建工程
  1. 点击菜单Project ->New μVision Project,先选择路径。因为想研究一下DMA,所以我把工程新建在C:\L\test\DMA\下,之前有把库文件夹Library也拷贝到test这个目录下。

  2. 之后在弹出的对话框中选择CPU型号。

  3. 这里要说一下,我最早用的是MDK4.1,版本太旧,没有L系列的CPU可以选择,只好更新。但是更新到MDK4.7之后有一个盗版的JLINK用不了,无奈啊。现在和学长2人用1个JLINK不方便。买了ST-Link还在路上,ST-Link只支持ST公司的MCU。

我的Discovery开发板上的MCU是152RB。

MDK下STM32L1系列工程的新建_新建工程_02

导入启动文件

然后会弹出一个对话框,问要不要导入启动文件,这里选择是。

这个文件其实是根据不同型号的MCU选出来的,分为Medium-Density,Medium-Density Plus,High-Density3种。152RB属于Medium-Density,可以看到自动导入的文件是以md结尾。

MDK下STM32L1系列工程的新建_STM32L_03

使用Manage Component功能添加文件

此时可以看到Project窗口中有一个Target叫Target1,其中有一个源文件组叫Source Group 1,这么原生态的必须改掉!

MDK下STM32L1系列工程的新建_新建工程_04

可以在Project窗口的Target上点右键选择Manage Component进入,或者直接点工具栏上品字形的图标进入该功能。这个功能可以方便地添加、删除、修改Target及Target下的Source Groupe以及每个Group下的文件。

这里把Target叫做DMA,3个组,其中StartUp中含有这个启动文件,另外2个组还是空的。

MDK下STM32L1系列工程的新建_STM32_05

然后,

在StartUp组中添加/Libraries/CMSIS/Device/STSTM32L1xx/Source/Templates/目录下的system_stm32l1xx.c文件;

在STM32L1xx_StdPeriph_Drive组中添加需要用到的驱动文件,我暂时只添加了rcc和gpio这2个文件;

在User中添加新建的空文件main.c。

添加完成的Project窗口后如下图。

MDK下STM32L1系列工程的新建_STM32L_06

include stm32l1xx.h

帮助文档是这样写的:

MDK下STM32L1系列工程的新建_STM32_07

第一件事MDK刚帮我们做好,接下来第二件事就是include这个entry point文件了。

直接在main.c里面include之,根据要求还要依照我们的MCU类型,定义一个宏STM32L1XX_MD,下一步会讲。

这时候问题来了,MDK如何知道去×××这个文件呢?详见下一步。

使用Target Option功能配置include path和宏

这个功能可以在Project Window的Target上点右键选择Target Option,或者点击工具栏上的快捷图标或者按快捷键Alt+F7进入。

其中有好多标签,下面据我所知说一下。

  1. Device:选择硬件,如果建工程的时候选错了或者要换硬件,可以在这里进行。

  2. Target:不太清楚干啥用的,没管过。

  3. Output:选择输出的Obj等文件的设置,可以选择输出路径,新建一个会文件夹会比较清爽。另外如果要生成HEX文件的话也在这里选。

  4. Listing:Lis文件的设置,也可以新建个文件夹来存放。

  5. User:这里可以定义一些在编译的时候用户要额外做的事情,比如可以调用个外部指令生成BIN文件之类的,都在这里写指令。

  6. C/C++:include Path就在这里选择,除了工程的当前目录,还需要在库中选择3个,详见下图。另外还需定义两个宏,上一步提到的STM32L1XX_MD和USE_STDPERIPH_DRIVER。这第二个宏要定义了才能用标准库,具体下一步会讲。

    MDK下STM32L1系列工程的新建_STM32L_08

    MDK下STM32L1系列工程的新建_STM32L_09

  7. Asm:汇编代码相关,没管过。

  8. Linker:链接相关,没管过。

  9. Debug:调试相关的选项,默认用的是模拟器。需要根据自己的环境选择相应的在线调试工具。详见后文。

  10. Utilities:烧录相关选项,也要配置,后文细说。

这里还有个坑

此时编译的话是通不过的,会报函数没定义的Error。

打开stm32l1xx.h这个所谓的entry point,在6315行到6317行可以看到以下代码:

#ifdef USE_STDPERIPH_DRIVER
  #include "stm32l1xx_conf.h"
#endif

上一步定义的这个宏,作用其实只是多include一个叫做stm32l1xx_conf.h的文件。而坑爹的是,这个文件在库函数里面是没有的,要自己写!

万幸,在评估板的例程里面,这个文件都是有的,果断复制了一个过来到我的目录下。打开一看,除了include文件,也就定义了一个用来检验参数类型是否符合要求的assert宏而已。

使用Target Option功能配置Debug和Utilities环境

这个时候在main.c里面加一个mian函数做入口,其实已经可以编译成功了,如下图。但是要烧录到板子上或者是在线调试的话,还需要配置。

  1. 在Target Option的Debug标签,根据环境选择合适的调试工具。默认是左边的模拟器,我用的是ST-Link。点击ST-Link边上的Setting按钮,对ST-Link的进行配置,要配成SW模式,配置成功的话可以看到Device信息,如下图。

    MDK下STM32L1系列工程的新建_新建工程_10

    MDK下STM32L1系列工程的新建_STM32_11

  2. 和Debug类似,在Utilities标签,选择根据环境选择合适的下载工具以及Flash Size。Flash Size的话,建议查阅芯片的Datasheet来确定。STM32的话,由芯片型号STM32L1xx之后的第二个字母体现,我用的是B,表示128k,如下图。

    MDK下STM32L1系列工程的新建_STM32_12

测试

不出意外,到此为止一个工程已经新建完毕。写了几行代码,开了时钟,配置了GPIO口,然后循环让PB6这个口无脑跳变。

#include "stm32l1xx.h"
#include "system_stm32l1xx.h"
#include "stm32l1xx_rcc.h"
#include "stm32l1xx_gpio.h"
void LED_Init(void);
int main()
{
  uint8_t led_f = 0;
  SystemInit();
  LED_Init();
  while(1)
  {
    if(led_f == 0){GPIO_SetBits(GPIOB,GPIO_Pin_6); led_f =1;}
    else{GPIO_ResetBits(GPIOB,GPIO_Pin_6); led_f =0;}
  }
}
void LED_Init(void)
{
  GPIO_InitTypeDef GPIO_InitStructure;
                                                                                     
  RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOB,ENABLE);
  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6|GPIO_Pin_7;
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;
  GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
  GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_40MHz;
  GPIO_Init(GPIOB, &GPIO_InitStructure);
}

烧到板子里,reset一按,灯亮了。接上示波器看到下图,说明确实在跳变。

MDK下STM32L1系列工程的新建_STM32_13

顺便,这里的跳变周期和占空比,和指令数有关,具体要研究system_stm32l1xx.c中的函数了。



如果需要特定的占空比和频率的话,可以使用STM32定时器TIM的PWM输出功能。