本帖最后由 香水城 于 2017-8-16 14:53 编辑
使用mbed 进行STM32 开发及STM32F0的时钟问题
前言
STM32 Nucleo 开发板作为STM32 的开发板之一,具有很多新颖的特点:不仅扩展了ST Morpho 全部I/O 接口,还完全兼容Arduino Uno Rev.3 接口;支持mbed 开发平台进行开发;自带ST-LINK/V2-1 调试编程工具,并集成了虚拟串口功能。如此种种,给开发者带来很大的便利性。
问题
使用NUCLEO-F030R8 开发板,在mbed 平台进行开发的时候,使用例程进行编译下载,发现LED 灯闪烁的频率以及串口的波特率都不对。就这个问题,我们对此平台的时钟处理来进行粗浅地探讨并解决问题。
调研
1.使用mbed 进行项目开发
1) 要使用mbed 开发平台,首先要访问https://developer.mbed.org/网站,点击“Login or signup”先进行注册或登录。
2) 登录后,点击Compiler 便可打开mbed 在线开发平台(如下图),进行开发。
3) 首先,要先选择开发板,点击右上角的“No device selected ”按钮来进行开发板的选择。
弹出对话框如下:
选择右下角的“Add Platform”按钮,进入https://developer.mbed.org/platforms/页面进行平台选择。
在“Target Vendor”中将“STMicroelectronics”打勾,点一下下面的“Filter”按钮,右边便显示出所支持的所有的ST 的开发板。本次实验使用NUCLEO-F030R8 开发板,所以点一下此开发板。
此时,进入NUCLEO-F030R8 的介绍页面,在这里,介绍了此开发板的概览,MCU 的特点,Nucleo 的特点,以及引脚分配(包括Arduino 接口和Morpho 接口),等等。这里有一个重要的信息,不可错过。
此处显示了一些重要的接口信息。
此时,点击页面右边的“Add to your mbed Compiler”蓝色按钮,就可以把这个NUCLEO-F030R8 开发板加入到编译器了。然后在相同位置再点一次“Open mbed Compiler”就可以进入编译器了。
4) 此时,在编译器的右上角已经可以看到“NUCLEO-F030R8”。由于是第一个项目,所以编译器自动弹出“Create new program”对话框,选择“Blink y LED test for the ST Nucleo boards”例程来看一下闪灯实验。
5) 点击“OK”后,项目便建立完成了。如下图:
左侧程序工作区里可以看到Nucleo_blink_led 底下包含main.cpp 和mbed。mbed 为库编译文件,而main.cpp 才是需要开发人员来进行撰写代码的。打开main.cpp 可以看到主程序代码:
这是一个闪灯程序,LED 灯先点亮200ms 再熄灭1s,如此循环。
6) 点击上面的“Compile”进行编译,编译完成后会生成一个.bin 文件,例如此例子为Nucleo_blink_led_NUCLEO_F030R8.bin。插上NUCLEO 开发板,驱动完成后,电脑中会出现新的盘符:
NODE_F030R8。将这个bin 文件下载到这个盘符里边,就烧写成功,开始运行程序了。如下图:
7) 此时就可以看到NUCLEO-F030R8 上的LD2 灯在进行闪烁了。
2.mbed库函数中STM32F0所存在的时钟问题
1) 观察刚才的程序在NUCLEO 板的运行情况,程序本身的目的是“LED 灯先点亮200ms 再熄灭1s,如此循环。”但是很明显灯的闪烁频率要比这个快!我们需要探究这个问题。
2) 再做一个实验“串口打印”的实验:点击“New”按钮新建一个项目,这次选择“Display a message on PC usingUART”例程,点击“OK”。
打开main.app,可以看到以下代码:
此程序的目的是串口配置为:波特率9600,数据位1 位,无奇偶校验位。向PC 打印“Hello World!”后每一秒再循环打印一次“This program runs since &d seconds.”
对其进行编译下载,然后打开电脑的上串口调试软件,直接使用NUCLEO-F030R8 自带的虚拟串口功能(在板上,ST-LINK 的串口已经连接到STM32F030R8 的PA2&PA3 上,可以直接使用)。按波特率9600 配置后打开接收STM32F030 发送过来的字符,结果发现收到的全是乱码。
使用示波器对串口通讯进行观察,发现波特率并非9600,而是57600,将串口调试软件的波特率修改成57600 后就
能正常接收到字符了,可是,“This program runs since &d seconds.”也不是1 秒打印1 次!
3.问题调研
在串口实验中,我们发现实际的波特率57600 是目标波特率9600 的6 倍。这让我们可以联想到48MHz 主频和8MHz 主频的关系,也就是说,很有可能此STM32F0 的mbed 库本身应该是基于8MHz 下进行的,而实际上STM32F030 工作在48MHz 的主频上,且时钟相关参数是按8MHz 来设计的。对于这种怀疑我们进行探讨。
由于已经怀疑STM32F0 在mbed 库中的程序可能存在问题,所以就必须把库函数源文件挖出来看,而不是使用库函数编译文件了。以串口例程为例,进行调研。
1) 程序工作区选中“Nucleo_printf”项目,点击上面的“Import”按钮。
在 “Search”按钮左边输入“mbed”,点击“Search ”按钮,可以看到以下列表:
其中mbed-src 就是我们想要的mbed 库源程序了,双击它将它加入到Nucleo_printf 项目中。并在项目原mbed 编译文件上点右键,选择“delete”将其删掉。(如果不删掉,由于库的重复,将会导致编译无法通过)。新的项目结构 如下图:
api 文件夹包含着mbed API 的头文件,common 文件夹包含mbed API 的源文件,hal 文件夹包含硬件抽象层文件,targets 文件夹才是包含cmsis 和各个厂商各个目标板的相应底层库。我们使用的是NUCLEO-F030R8,所以需要查找的是TARGET_STM32F0 的内容以及TARGET_NUCLEO_F030R8 的内容。
2) 由于mbed 开发平台无法单步运行等操作,为了探究问题的所在,我们将其输出到其他IDE 上进行调试,比如Keil IDE。在Nucleo_printf 项目上点右键,选择“Export Program ”。
弹出对话框,此处选择“Keil uVision 4”,点击“Export”按钮,开始输出项目文件。
将输出的“Nucleo_printf_uvision_nucleo_f030r8.zip”文件下载下来,进行解压缩,得到Nucleo_printf 的文件夹。
双击Nucleo_printf.uvproj 打开项目。项目文件其实很清爽,只有一个Src 文件夹,包含了所有的程序文件。
3) 由于怀疑的是时钟有问题,所以自然而然,先打开时钟配置文件“system_stm32f0xx.c”先查看一番。在此代码中,首先看到一个很清楚的错误的问题:
在NUCLEO-F030R8 的板子上HSE 是没有晶振的,也不是使用外部时钟,所以在这个位置不应该将USE_PLL_HSE_EXTC 和USE_PLL_HSE_XTAL 设置为0,需要将它们都改成0。
再研究一下几个函数的代码:
· void SystemInit(void)
此函数进行系统的初始化,并未进行配置。
· void SystemCoreClockUpdate (void)
根据时钟寄存器的值更新系统时钟参数。
· void SetSysClock(void)
配置系统时钟,USE_PLL_HSE_EXTC 和USE_PLL_HSE_XTAL 就是在这里被使用的。如果没有配置USE_PLL_HSE_EXTC 和USE_PLL_HSE_XTAL,这个程序将系统时钟源配置成使用HSI+PLL 得到48MHz 的主频,这对于遇到的问题非常重要。
4) 再来看时钟配置的函数SetSysClock 和SystemCoreClockUpdate 是在什么地方被调用的,打开“mbed_overrides.c”文件,可以看到以下代码:
SetSysClock 和SystemCoreClockUpdate 是在mbed_sdk_init()中被调用的,调用时间在RAM 初始化之后,main之前。
5) 在“Retarget.cpp”中也可以看到:
6) 在“serial_api.c”中可以找到了串口的初始化函数:void serial_init(serial_t *obj, PinName tx, PinName rx),它在“serialBase.app”中被调用。
7) 在“startup_stm32f030x8.S”中,来看一下复位之后的运行代码:
8) 重要的事情来了,我们要研究一下程序运行的顺序,在单步运行和断点的帮助下,我们可以看到程序的运行情况:
A. 复位后,从“LDR R0, =SystemInit”先调用void SystemInit(void)进行初始化配置,此时时钟源仍然为8MHz。
B. 往下跑,先进入了serial_init()进行串口的初始化,波特率为默认为9600,基于8MHz 的配置。
C. 接下来,进入mbed_sdk_init()开始初始化,调用SetSysClock()和SystemCoreClockUpdate()将系统时钟配置为48MHz。
D. 系统时钟的改变之后没有对串口进行重新初始化,直接进入了main()函数运行代码,造成UART 的波特率不对的情况。
3.问题解决
从两个工作主频分别使用两种方法来解决这个问题。
1) 设置工作主频为8MHz。在“mbed_overrides.c”文件,此处已经将SystemCoreClock 设置成8000000,如下:
编译,测试,发现可以正常的工作,LED 为每1 秒亮或灭,串口波特率恢复到正常的9600。
2) 设置工作主频为48MHz。使用48MHz 造成的问题,主要是由于串口的初始化发生在时钟配置为48MHz 之前,所以最快的解决办法就是把顺序调过来。于是方法如下:
结论
STM32F0 的mbed 库函数中在时钟处理上存在一些问题,导致外设频率不正常。
处理
放弃使用mbed 库编译文件,使用mbed 库源程序,对程序中的时钟配置上做些修改,修复这个问题。
建议
建议一:请注意在使用NUCLEO-F030R8 时,先参考NUCLEO 的用户手册UM1724。其中重要的一点是:
由于SB62 和SB63 没有连接,所以,PA2 和PA3 并没有连接到Arduino 接口和Morpho 接口上,如果需要从这两个接口来进行测试,请自行连接。当然,PA2 和PA3 是有连接到ST-LINK 的,所以使用ST-LINK 自带的虚拟串口调试起来最容易。
建议二:如果需要知道在main.app 中定义的外设所对应的I/O 口,可以参考以下“PinNames.h”文件,位于:mbed_src/targets/hal/TARGET_STM/TARGET_STM32F0/TARGET_NUCLEO_F030R8/
对应PDF:使用mbed进行STM32开发及STM32F0的时钟问题
更多实战经验请看:【ST MCU实战经验汇总贴】
|