3819|2

1379

帖子

2

TA的资源

五彩晶圆(初级)

楼主
 

NUCLEO-F410RB 测评第一周: 运行尝试 [复制链接]

本帖最后由 cruelfox 于 2015-12-8 00:03 编辑

拿到管理员送来的 NUCLEO-F410RB 板子刚好一周了,该汇报一下进度了。过去的这个周末家务事占用时间多了点,所以实际上没像预期的那样折腾它。不过还是跑通了第一个demo程序——让LED闪起来。过程并不顺利,我喜欢刨根究底,所以挖来和大家分享一下。
我最近一段时间在使用STM32F0,所以算是接触并稍微了解了STM32系列。拿到NUCLEO-F410RB,板子外观几乎和自己的NUCLEO-F091RC是一样的。不过学习了一下手册,就知道核心的差别大着呢。F091, F072都是ARM Cortex-M0的,核心是单一总线;F410是ARM Cortex-M4,核心有三条总线分别用于指令,数据和外设,何况还有Flash内存加速器,所以就算是同样的时钟下执行同样的指令,F410效率对比F0系列的优势也是明显的。指令集的对比就不用多说了,我看了一晚上M4编程手册,指令真是眼花缭乱!

好在貌似M4系和M0系的外设接口有很多是兼容的,代码挪过来编译应该问题不大。于是我尝试把F072做的最小测试程序移植过来跑一下,就让LED闪亮吧:在NUCLEO上绿色的LED是接在PA5口上的,操作GPIO就可以控制亮和灭了。移植过来的程序是这样的:
  1. #include "stm32f4xx.h"

  2. int main(void)
  3. {

  4.     RCC->AHB1ENR |= RCC_AHB1ENR_GPIOAEN;    // enable GPIO port A clock
  5.     GPIOA->MODER = GPIO_MODER_MODER5_0; // PA5 as general output (LED)

  6.     RCC->APB1ENR |= RCC_APB1ENR_TIM6EN;     // enable basic timer 6
  7.     TIM6->PSC = 9999;       // prescaler
  8.     TIM6->ARR = 399;        // auto reload value
  9.     TIM6->CR1 = TIM_CR1_URS|TIM_CR1_CEN;    // start counter

  10.     while(1)
  11.     {
  12.         static char a=0;
  13.         if(TIM6->SR & TIM_SR_UIF)   // check if overflow
  14.         {
  15.             TIM6->SR &= ~TIM_SR_UIF;    // clear flag
  16.             if(a==0)
  17.             {
  18.                 GPIOA->BSRRL = (1<<5);
  19.                 a=1;
  20.             }
  21.             else
  22.             {
  23.                 GPIOA->BSRRH = (1<<5);
  24.                 a=0;
  25.             }
  26.         }
  27.     }
  28. }
复制代码
在F072上,RCC中控制AHB时钟使能的只是AHBENR寄存器,到了F410上面变成AHB1ENR, 又新出了AHB1LPENR,这里就得改。TIMER6的操作假定是一样的,先试着编译吧。上面这个程序还需要额外的几个文件,包括startup code,都从别的地方挖来用。编译好以后,用STVP下载也成功了,但是,居然LED不亮
在F072上没有问题的,怎么会错呢? 我百思不得其解啊。竟然在这里被卡住了。
后来想还是找现成的例子来试吧,于是去下载STM32CubeF4这个近300M的zip包,从里面把GPIO_Toggle的例子扒出来,把所依赖的文件也搜罗出来,单独弄到一个目录下。编译的命令都写到.bat文件里
  1. arm-none-eabi-gcc -c -O2 -I. -mcpu=cortex-m4 -mthumb -DSTM32F410Rx main.c
  2. arm-none-eabi-gcc -c -O2 -I. -mcpu=cortex-m4 -mthumb -DSTM32F410Rx stm*.c
  3. arm-none-eabi-gcc -c -O2 -I. -mcpu=cortex-m4 -mthumb -DSTM32F410Rx system*.c
  4. arm-none-eabi-gcc -c -mcpu=cortex-m4 -mthumb start*.s
  5. arm-none-eabi-ld *.o -Le:\arm-2014q3\arm-none-eabi\lib\armv7e-m -Le:\arm-2014q3\lib\gcc\arm-none-eabi\4.8.4\armv7e-m -T STM32F410RBTx_FLASH.ld -o blink.elf
  6. arm-none-eabi-objcopy -Oihex blink.elf blink.hex
复制代码

这个测试例子顺利运行了,LED闪亮了。它的主程序也简单,删掉注释是这样
  1. int main(void)
  2. {
  3.   HAL_Init();
  4.   
  5.   SystemClock_Config();
  6.    
  7.   __HAL_RCC_GPIOA_CLK_ENABLE();
  8.   
  9.   GPIO_InitStruct.Pin = GPIO_PIN_5;
  10.   GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
  11.   GPIO_InitStruct.Pull = GPIO_PULLUP;
  12.   GPIO_InitStruct.Speed = GPIO_SPEED_FAST;
  13.   HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
  14.    
  15.   while (1)
  16.   {
  17.     HAL_GPIO_TogglePin(GPIOA, GPIO_PIN_5);
  18.     HAL_Delay(100);
  19.   }
  20. }
复制代码
当然,里面是用了HAL库。对于这样的小程序来说,用HAL无疑是做了很多的不必要的工作。我认为小程序直接操作寄存器就够了,记住几个关键寄存器的用法就可以写出来。回过头来看,既然HAL可以实现的功能,我自己写的又怎么不工作呢?于是使用替换法,排查下来发现问题出在
  RCC->AHB1ENR |= RCC_AHB1ENR_GPIOAEN;
用HAL库书写同样功能的代码是
__HAL_RCC_GPIOA_CLK_ENABLE();
这有什么问题?不就是读-改写一个RCC外设寄存器么。追溯后面这个函数的定义,在stm32f4xx_hal_rcc.h里面有
  1. #define __HAL_RCC_GPIOA_CLK_ENABLE()    do { \
  2.                                         __IO uint32_t tmpreg; \
  3.                                         SET_BIT(RCC->AHB1ENR, RCC_AHB1ENR_GPIOAEN);\
  4.                                         /* Delay after an RCC peripheral clock enabling */ \
  5.                                         tmpreg = READ_BIT(RCC->AHB1ENR, RCC_AHB1ENR_GPIOAEN);\
  6.                                         UNUSED(tmpreg); \
  7.                     } while(0)
复制代码
奇怪了,为什么要这样去写呢?while(0)说明这个程序段并没有循环,再追溯 SET_BIT 宏的定义,就是我直接写的那样一个逻辑或操作。 实在想不出道理,但现实问题是我自己写的没有正确运行,只好反汇编比较了。
下面把代码差异保留到仅仅那一行C代码上,分别编译,使用objdump -S查看main()的内容。我直接写出来是
  1. 80001dc:        4b16              ldr        r3, [pc, #88]        ; (8000238 <main+0x5c>)
  2. 80001de:        4a17              ldr        r2, [pc, #92]        ; (800023c <main+0x60>)
  3. 80001e0:        4917              ldr        r1, [pc, #92]        ; (8000240 <main+0x64>)
  4. 80001e2:        b4f0              push        {r4, r5, r6, r7}
  5. 80001e4:        6b1e              ldr        r6, [r3, #48]        ; 0x30
  6. 80001e6:        4c17              ldr        r4, [pc, #92]        ; (8000244 <main+0x68>)
  7. 80001e8:        f046 0601         orr.w        r6, r6, #1
  8. 80001ec:        f44f 6580         mov.w        r5, #1024        ; 0x400
  9. 80001f0:        f64f 70ff         movw        r0, #65535        ; 0xffff
  10. 80001f4:        631e              str        r6, [r3, #48]        ; 0x30
  11. 80001f6:        6025              str        r5, [r4, #0]
  12. 80001f8:        6160              str        r0, [r4, #20]
  13. 80001fa:        6c1f              ldr        r7, [r3, #64]        ; 0x40
  14. 80001fc:        f242 760f         movw        r6, #9999        ; 0x270f
  15. 8000200:        f240 158f         movw        r5, #399        ; 0x18f
复制代码
使用了HAL库书写的结果是
  1. 80001dc:        4b19              ldr        r3, [pc, #100]        ; (8000244 <main+0x68>)
  2. 80001de:        4a1a              ldr        r2, [pc, #104]        ; (8000248 <main+0x6c>)
  3. 80001e0:        6b18              ldr        r0, [r3, #48]        ; 0x30
  4. 80001e2:        491a              ldr        r1, [pc, #104]        ; (800024c <main+0x70>)
  5. 80001e4:        b4f0              push        {r4, r5, r6, r7}
  6. 80001e6:        f040 0001         orr.w        r0, r0, #1
  7. 80001ea:        6318              str        r0, [r3, #48]        ; 0x30
  8. 80001ec:        6b18              ldr        r0, [r3, #48]        ; 0x30
  9. 80001ee:        4c18              ldr        r4, [pc, #96]        ; (8000250 <main+0x74>)
  10. 80001f0:        b082              sub        sp, #8
  11. 80001f2:        f000 0001         and.w        r0, r0, #1
  12. 80001f6:        9001              str        r0, [sp, #4]
  13. 80001f8:        f44f 6580         mov.w        r5, #1024        ; 0x400
  14. 80001fc:        f64f 70ff         movw        r0, #65535        ; 0xffff
  15. 8000200:        9e01              ldr        r6, [sp, #4]
  16. 8000202:        6025              str        r5, [r4, #0]
  17. 8000204:        6160              str        r0, [r4, #20]
  18. 8000206:        6c1f              ldr        r7, [r3, #64]        ; 0x40
  19. 8000208:        f242 760f         movw        r6, #9999        ; 0x270f
  20. 800020c:        f240 158f         movw        r5, #399        ; 0x18f
复制代码
机器代码有区别是毋庸置疑的,用了HAL的这段代码要长一些,将 AHB1ENR 改写之后又多读了一次。读能影响寄存器的结果?不可能的事。我写的代码编译结果也没有问题,那究竟为什么LED没有亮?

假如我手工写汇编来写这个程序的话,写出来不会是这个样子的。一条条读就能看出来,编译器似乎对指令顺序进行了调整优化——也许是优化了流水线使M4上运行更快。我注意到这两处连续的写操作:
80001f4:        631e              str        r6, [r3, #48]        ; 0x30
80001f6:        6025              str        r5, [r4, #0]

前一个的目标地址是 RCC 的 AHB1ENR, 后一个不用说应该是 GPIOA 的 MODER. 会不会是GPIOA还没有被使能,导致紧接着的 MODER 寄存器操作无效了?我很怀疑这一点。于是,在C程序中,写 AHB1ENR 之后插入一个 NOP 指令,也就是 __NOP(); 结果,果然问题就绕过去了。
其它的,比如改变编译优化选项为 -O 而不用 -O2, 结果代码中没有那两条连续的STR指令,LED也同样闪烁了。这,是因为Cortex-M4运行太快了么?

F410_blink.zip

125.48 KB, 下载次数: 8

最简例子

F410_blink_HAL.zip

203.43 KB, 下载次数: 7

使用HAL的例子

此帖出自stm32/stm8论坛

最新回复

研究的挺深的,汇编也上了,666  详情 回复 发表于 2015-12-8 14:24
点赞 关注(1)
 

回复
举报

6105

帖子

4

TA的资源

版主

沙发
 
赞一个,虽然是小细节。往往是要花时间。卡壳的环节。
此帖出自stm32/stm8论坛
 
 

回复

1403

帖子

1

TA的资源

纯净的硅(中级)

板凳
 
研究的挺深的,汇编也上了,666
此帖出自stm32/stm8论坛
 
个人签名HELLO_WATER
 

回复
您需要登录后才可以回帖 登录 | 注册

查找数据手册?

EEWorld Datasheet 技术支持

相关文章 更多>>
关闭
站长推荐上一条 1/8 下一条

 
EEWorld订阅号

 
EEWorld服务号

 
汽车开发圈

About Us 关于我们 客户服务 联系方式 器件索引 网站地图 最新更新 手机版

站点相关: 国产芯 安防电子 汽车电子 手机便携 工业控制 家用电子 医疗电子 测试测量 网络通信 物联网

北京市海淀区中关村大街18号B座15层1530室 电话:(010)82350740 邮编:100190

电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2025 EEWORLD.com.cn, Inc. All rights reserved
快速回复 返回顶部 返回列表