本帖最后由 cruelfox 于 2015-11-21 16:18 编辑
 
 STM32F0xx 系列是ARM Cortex-M0架构,地址空间32位,也就是4G Bytes的访问范围。数据和代码使用同一编址,下图是地址空间的布局:
  
 
 
 实际上单片机用到的资源很少,地址空间大部分都没有内容。我使用的STM32F072C8T6带有64kB的Flash ROM, 16kB的SRAM,起始分别是 0x08000000 和 0x20000000. (由于有硬件映射功能,在0x00000000也就是最低地址,还可以访问ROM或者RAM的内容). 单片机片上外设的寄存器,则分布在更高的地址空间。读写这些寄存器,在CPU看来和读写内存(RAM)操作是一样的。
 
 所以,C语言访问设备寄存器,和访问内存中的一个变量一样。只要知道寄存器的地址,通过一个指针访问就可以实现读写。上一贴子我的程序中引用了 RCC, GPIOA, TIM6 这三个(结构)指针,它们的值(也就是地址)以及类型(代表访问的内容)定义在 stm32f0xx.h 这个头文件中。因为设备寄存器太多了哇,如果每一个都定义一个指针就太烦琐了,所以把按功能划分定义成组,每组用一个C语言的结构类型表示,写起来也更清晰。而寄存器里面的位描述也可以定义成一些宏,在读程序的时候就知道是什么意思了。如果有兴趣,可以把 stm32f0xx.h 文件和STM32F0的手册对照着阅读。
 
 好,假设已经熟悉寄存器操作了,知道怎么配置寄存器实现想要的功能,那么就可以写C程序让STM32工作了。现在需要一个工具来将C程序翻译成机器代码——编译器,或者是叫做工具链(Tool chain)。Keil MDK-ARM 或者 IAR-EWARM 开发环境都带有各自的编译器,不过我更偏向于用开源的GCC-ARM. 在launchpad.net上可以下载到编译好的arm-gcc工具链zip包,将它解压缩,加到PATH里面就可以直接用了,很方便(很精简吧)。
 
 
 OK,现在来编译上面那个mini.c文件,命令行:
 arm-none-eabi-gcc -c -Os -mcpu=cortex-m0 -mthumb mini.c
 gcc的参数 -c 是表示仅编译,-Os 是优化代码大小,-mcpu=cortex-m0 -mthumb 是指定指令集的,因为ARM有不同的版本。对了,include的头文件还没弄到呢。要编译通过需要把 stm32f0xx.h 这个文件找来。我的建议是下载ST提供的 "STM32F0x2 USB FS Device Library" 程序库(URL http://www.st.com/st-web-ui/static/active/en/st_prod_software_internet/resource/technical/software/firmware/stsw-stm32092.zip),把里面需要的头文件等等扒出来。在 stm32f0xx.h 中还包含了另外几个头文件,一并弄出来放到工程目录下。
 
 如果编译成功,将得到 mini.o 目标文件。可以用 arm-none-eabi-objdump -S mini.o 反汇编看看翻译成什么代码了。
 
   -  E:armtest072mini>arm-none-eabi-objdump -S mini.o
-   
-  mini.o:     file format elf32-littlearm
-   
-   
-  Disassembly of section .text.startup:
-   
-  00000000 <main>:
-     0:   4b16            ldr     r3, [pc, #88]   ; (5c <main+0x5c>)
-     2:   2280            movs    r2, #128        ; 0x80
-     4:   6959            ldr     r1, [r3, #20]
-     6:   0292            lsls    r2, r2, #10
-     8:   430a            orrs    r2, r1
-     a:   b510            push    {r4, lr}
-     c:   2180            movs    r1, #128        ; 0x80
-     e:   615a            str     r2, [r3, #20]
-    10:   2290            movs    r2, #144        ; 0x90
-    12:   0249            lsls    r1, r1, #9
-    14:   05d2            lsls    r2, r2, #23
-    16:   6011            str     r1, [r2, #0]
-    18:   69da            ldr     r2, [r3, #28]
-    1a:   2110            movs    r1, #16
-    1c:   430a            orrs    r2, r1
-    1e:   61da            str     r2, [r3, #28]
-    20:   4b0f            ldr     r3, [pc, #60]   ; (60 <main+0x60>)
-    22:   4a10            ldr     r2, [pc, #64]   ; (64 <main+0x64>)
-    24:   851a            strh    r2, [r3, #40]   ; 0x28
-    26:   2290            movs    r2, #144        ; 0x90
-    28:   32ff            adds    r2, #255        ; 0xff
-    2a:   62da            str     r2, [r3, #44]   ; 0x2c
-    2c:   2205            movs    r2, #5
-    2e:   801a            strh    r2, [r3, #0]
-    30:   4a0d            ldr     r2, [pc, #52]   ; (68 <main+0x68>)
-    32:   7812            ldrb    r2, [r2, #0]
-    34:   8a1c            ldrh    r4, [r3, #16]
-    36:   2101            movs    r1, #1
-    38:   4809            ldr     r0, [pc, #36]   ; (60 <main+0x60>)
-    3a:   420c            tst     r4, r1
-    3c:   d0fa            beq.n   34 <main+0x34>
-    3e:   8a04            ldrh    r4, [r0, #16]
-    40:   438c            bics    r4, r1
-    42:   8204            strh    r4, [r0, #16]
-    44:   2090            movs    r0, #144        ; 0x90
-    46:   2480            movs    r4, #128        ; 0x80
-    48:   05c0            lsls    r0, r0, #23
-    4a:   408c            lsls    r4, r1
-    4c:   2a00            cmp     r2, #0
-    4e:   d102            bne.n   56 <main+0x56>
-    50:   6184            str     r4, [r0, #24]
-    52:   1c0a            adds    r2, r1, #0
-    54:   e7ee            b.n     34 <main+0x34>
-    56:   8504            strh    r4, [r0, #40]   ; 0x28
-    58:   2200            movs    r2, #0
-    5a:   e7eb            b.n     34 <main+0x34>
-    5c:   40021000        .word   0x40021000
-    60:   40001000        .word   0x40001000
-    64:   0000270f        .word   0x0000270f
-    68:   00000000        .word   0x00000000
-   
  复制代码   如上,其实里面就一个main函数。但是 main 的入口地址还没有确定,而且它还使用了一个static型的内存变量,地址也还没有确定。可以用 arm-none-eabi-nm mini.o 来查看模块里面的全局符号表:    -  E:armtest072mini>arm-none-eabi-nm mini.o
-  00000000 b a.4686
-  00000000 T main
  复制代码   那么,怎么让程序放到ROM中合适的地址,并运行呢?如果熟悉C语言编程就知道还有一步——链接,才能确定符号的地址。但是,到目前为止我们还没有告诉GCC地址的布局,也就是RAM从哪里开始,代码放在哪里。因为ARM的器件很多,这并不是统一的,所以需要提供一些信息给链接程序。具体地,需要一个Linker script, 可以从软件包中找到 STM32F072C8_FLASH.ld (或者用近似的来修改得到)   OK,下面就是链接了,使用命令 arm-none-eabi-ld mini.o -Le:arm-2014q3arm-none-eabilibarmv6-m -Le:arm-2014q3libgccarm-none-eabi4.8.4armv6-m -T STM32F072C8_FLASH.ld -o mini.elf 这里面 -L 参数是添加标准库文件的搜索路径,虽然暂时并没有用到C标准库里面的东西,但是Linker script里面引用了标准库文件。-o 指定输出的目标文件。这么就快要得到最终的机器码了,不过好象还缺少了什么…… arm-none-eabi-ld: warning: cannot find entry symbol Reset_Handler; defaulting to  08000000  linker给了一个警告:找不到入口地址 Reset_Handler 的值,设成了默认 0x08000000. 下面再用objdump -S反汇编看一下    -  E:armtest072mini>arm-none-eabi-objdump -S mini.elf
-   
-  mini.elf:     file format elf32-littlearm
-   
-   
-  Disassembly of section .text:
-   
-  08000000 <main>:
-  8000000:       4b16            ldr     r3, [pc, #88]   ; (800005c <main+0x5c>)
-  8000002:       2280            movs    r2, #128        ; 0x80
-  8000004:       6959            ldr     r1, [r3, #20]
-  8000006:       0292            lsls    r2, r2, #10
-  8000008:       430a            orrs    r2, r1
-  800000a:       b510            push    {r4, lr}
-  800000c:       2180            movs    r1, #128        ; 0x80
-  800000e:       615a            str     r2, [r3, #20]
-  8000010:       2290            movs    r2, #144        ; 0x90
-  8000012:       0249            lsls    r1, r1, #9
-  8000014:       05d2            lsls    r2, r2, #23
-  8000016:       6011            str     r1, [r2, #0]
-  8000018:       69da            ldr     r2, [r3, #28]
-  800001a:       2110            movs    r1, #16
-  800001c:       430a            orrs    r2, r1
-  800001e:       61da            str     r2, [r3, #28]
-  8000020:       4b0f            ldr     r3, [pc, #60]   ; (8000060 <main+0x60>)
-  8000022:       4a10            ldr     r2, [pc, #64]   ; (8000064 <main+0x64>)
-  8000024:       851a            strh    r2, [r3, #40]   ; 0x28
-  8000026:       2290            movs    r2, #144        ; 0x90
-  8000028:       32ff            adds    r2, #255        ; 0xff
-  800002a:       62da            str     r2, [r3, #44]   ; 0x2c
-  800002c:       2205            movs    r2, #5
-  800002e:       801a            strh    r2, [r3, #0]
-  8000030:       4a0d            ldr     r2, [pc, #52]   ; (8000068 <main+0x68>)
-  8000032:       7812            ldrb    r2, [r2, #0]
-  8000034:       8a1c            ldrh    r4, [r3, #16]
-  8000036:       2101            movs    r1, #1
-  8000038:       4809            ldr     r0, [pc, #36]   ; (8000060 <main+0x60>)
-  800003a:       420c            tst     r4, r1
-  800003c:       d0fa            beq.n   8000034 <main+0x34>
-  800003e:       8a04            ldrh    r4, [r0, #16]
-  8000040:       438c            bics    r4, r1
-  8000042:       8204            strh    r4, [r0, #16]
-  8000044:       2090            movs    r0, #144        ; 0x90
-  8000046:       2480            movs    r4, #128        ; 0x80
-  8000048:       05c0            lsls    r0, r0, #23
-  800004a:       408c            lsls    r4, r1
-  800004c:       2a00            cmp     r2, #0
-  800004e:       d102            bne.n   8000056 <main+0x56>
-  8000050:       6184            str     r4, [r0, #24]
-  8000052:       1c0a            adds    r2, r1, #0
-  8000054:       e7ee            b.n     8000034 <main+0x34>
-  8000056:       8504            strh    r4, [r0, #40]   ; 0x28
-  8000058:       2200            movs    r2, #0
-  800005a:       e7eb            b.n     8000034 <main+0x34>
-  800005c:       40021000        .word   0x40021000
-  8000060:       40001000        .word   0x40001000
-  8000064:       0000270f        .word   0x0000270f
-  8000068:       20000000        .word   0x20000000
  复制代码  现在 main() 被放到ROM最开始去了,这好象是对的?如果了解ARM Cortex-M0下就知道这样错了,因为最开始应该是中断向量表。我们还没有编写Linker script中的 .isr_vectors 段的内容。而且,一上来初始化堆栈指针等工作都没有做就直接运行 main() 了也不合适吧?还缺少了初始化代码。  在软件包中搜刮一个 startup_stm32f072.s 汇编文件    -  /**
-    ******************************************************************************
-    * @file      startup_stm32f072.s
-    * @author    MCD Application Team
-    ******************************************************************************
-    */
-   
-    .syntax unified
-    .cpu cortex-m0
-    .fpu softvfp
-    .thumb
-   
-  .global g_pfnVectors
-  .global Default_Handler
-   
-  /* start address for the initialization values of the .data section.
-  defined in linker script */
-  .word _sidata
-  /* start address for the .data section. defined in linker script */
-  .word _sdata
-  /* end address for the .data section. defined in linker script */
-  .word _edata
-  /* start address for the .bss section. defined in linker script */
-  .word _sbss
-  /* end address for the .bss section. defined in linker script */
-  .word _ebss
-   
-    .section .text.Reset_Handler
-    .weak Reset_Handler
-    .type Reset_Handler, %function
-  Reset_Handler:
-    ldr   r0, =_estack
-    mov   sp, r0          /* set stack pointer */
-   
-  /*Check if boot space corresponds to test memory*/
-   
-      LDR R0,=0x00000004
-      LDR R1, [R0]
-      LSRS R1, R1, #24
-      LDR R2,=0x1F
-      CMP R1, R2
-      BNE ApplicationStart
-   
-  /*SYSCFG clock enable*/
-   
-      LDR R0,=0x40021018
-      LDR R1,=0x00000001
-      STR R1, [R0]
-   
-  /*Set CFGR1 register with flash memory remap at address 0*/
-      LDR R0,=0x40010000
-      LDR R1,=0x00000000
-      STR R1, [R0]
-   
-  ApplicationStart:
-  /* Copy the data segment initializers from flash to SRAM */
-    movs r1, #0
-    b LoopCopyDataInit
-   
-  CopyDataInit:
-    ldr r3, =_sidata
-    ldr r3, [r3, r1]
-    str r3, [r0, r1]
-    adds r1, r1, #4
-   
-  LoopCopyDataInit:
-    ldr r0, =_sdata
-    ldr r3, =_edata
-    adds r2, r0, r1
-    cmp r2, r3
-    bcc CopyDataInit
-    ldr r2, =_sbss
-    b LoopFillZerobss
-  /* Zero fill the bss segment. */
-  FillZerobss:
-    movs r3, #0
-    str  r3, [r2]
-    adds r2, r2, #4
-   
-   
-  LoopFillZerobss:
-    ldr r3, = _ebss
-    cmp r2, r3
-    bcc FillZerobss
-   
-  /* Call the application's entry point.*/
-    bl main
-    
-  LoopForever:
-      b LoopForever
-   
-   
-  .size Reset_Handler, .-Reset_Handler
-   
-  /**
-  * @brief  This is the code that gets called when the processor receives an
-  *         unexpected interrupt.  This simply enters an infinite loop, preserving
-  *         the system state for examination by a debugger.
-  *
-  * @param  None
-  * @retval : None
-  */
-      .section .text.Default_Handler,"ax",%progbits
-  Default_Handler:
-  Infinite_Loop:
-    b Infinite_Loop
-    .size Default_Handler, .-Default_Handler
-  /******************************************************************************
-  *
-  * The minimal vector table for a Cortex M0.  Note that the proper constructs
-  * must be placed on this to ensure that it ends up at physical address
-  * 0x0000.0000.
-  *
-  ******************************************************************************/
-     .section .isr_vector,"a",%progbits
-    .type g_pfnVectors, %object
-    .size g_pfnVectors, .-g_pfnVectors
-   
-  g_pfnVectors:
-    .word _estack
-    .word Reset_Handler
-   
-    .word NMI_Handler
-    .word HardFault_Handler
-    .word 0
-    .word 0
-    .word 0
-    .word 0
-    .word 0
-    .word 0
-    .word 0
-    .word SVC_Handler
-    .word 0
-    .word 0
-    .word PendSV_Handler
-    .word SysTick_Handler
-   
-   
-    .word WWDG_IRQHandler
-    .word PVD_VDDIO2_IRQHandler
-    .word RTC_IRQHandler
-    .word FLASH_IRQHandler
-    .word RCC_CRS_IRQHandler
-    .word EXTI0_1_IRQHandler
-    .word EXTI2_3_IRQHandler
-    .word EXTI4_15_IRQHandler
-    .word TSC_IRQHandler
-    .word DMA1_Channel1_IRQHandler
-    .word DMA1_Channel2_3_IRQHandler
-    .word DMA1_Channel4_5_6_7_IRQHandler
-    .word ADC1_COMP_IRQHandler 
-    .word TIM1_BRK_UP_TRG_COM_IRQHandler
-    .word TIM1_CC_IRQHandler
-    .word TIM2_IRQHandler
-    .word TIM3_IRQHandler
-    .word TIM6_DAC_IRQHandler
-    .word TIM7_IRQHandler    
-    .word TIM14_IRQHandler
-    .word TIM15_IRQHandler
-    .word TIM16_IRQHandler
-    .word TIM17_IRQHandler
-    .word I2C1_IRQHandler
-    .word I2C2_IRQHandler
-    .word SPI1_IRQHandler
-    .word SPI2_IRQHandler
-    .word USART1_IRQHandler
-    .word USART2_IRQHandler
-    .word USART3_4_IRQHandler 
-    .word CEC_CAN_IRQHandler
-    .word USB_IRQHandler
-   
-  /*******************************************************************************
-  *
-  * Provide weak aliases for each Exception handler to the Default_Handler.
-  * As they are weak aliases, any function with the same name will override
-  * this definition.
-  *
-  *******************************************************************************/
-   
-    .weak NMI_Handler
-    .thumb_set NMI_Handler,Default_Handler
-   
-    .weak HardFault_Handler
-    .thumb_set HardFault_Handler,Default_Handler
-   
-    .weak SVC_Handler
-    .thumb_set SVC_Handler,Default_Handler
-   
-    .weak PendSV_Handler
-    .thumb_set PendSV_Handler,Default_Handler
-   
-    .weak SysTick_Handler
-    .thumb_set SysTick_Handler,Default_Handler
-   
-    .weak WWDG_IRQHandler
-    .thumb_set WWDG_IRQHandler,Default_Handler
-   
-    .weak PVD_VDDIO2_IRQHandler
-    .thumb_set PVD_VDDIO2_IRQHandler,Default_Handler
-    
-    .weak RTC_IRQHandler
-    .thumb_set RTC_IRQHandler,Default_Handler
-    
-    .weak FLASH_IRQHandler
-    .thumb_set FLASH_IRQHandler,Default_Handler
-    
-    .weak RCC_CRS_IRQHandler
-    .thumb_set RCC_CRS_IRQHandler,Default_Handler
-    
-    .weak EXTI0_1_IRQHandler
-    .thumb_set EXTI0_1_IRQHandler,Default_Handler
-    
-    .weak EXTI2_3_IRQHandler
-    .thumb_set EXTI2_3_IRQHandler,Default_Handler
-    
-    .weak EXTI4_15_IRQHandler
-    .thumb_set EXTI4_15_IRQHandler,Default_Handler
-    
-    .weak TSC_IRQHandler
-    .thumb_set TSC_IRQHandler,Default_Handler
-    
-    .weak DMA1_Channel1_IRQHandler
-    .thumb_set DMA1_Channel1_IRQHandler,Default_Handler
-    
-    .weak DMA1_Channel2_3_IRQHandler
-    .thumb_set DMA1_Channel2_3_IRQHandler,Default_Handler
-    
-    .weak DMA1_Channel4_5_6_7_IRQHandler
-    .thumb_set DMA1_Channel4_5_6_7_IRQHandler,Default_Handler
-    
-    .weak ADC1_COMP_IRQHandler
-    .thumb_set ADC1_COMP_IRQHandler,Default_Handler
-     
-    .weak TIM1_BRK_UP_TRG_COM_IRQHandler
-    .thumb_set TIM1_BRK_UP_TRG_COM_IRQHandler,Default_Handler
-    
-    .weak TIM1_CC_IRQHandler
-    .thumb_set TIM1_CC_IRQHandler,Default_Handler
-    
-    .weak TIM2_IRQHandler
-    .thumb_set TIM2_IRQHandler,Default_Handler
-    
-    .weak TIM3_IRQHandler
-    .thumb_set TIM3_IRQHandler,Default_Handler
-    
-    .weak TIM6_DAC_IRQHandler
-    .thumb_set TIM6_DAC_IRQHandler,Default_Handler
-    
-    .weak TIM7_IRQHandler
-    .thumb_set TIM7_IRQHandler,Default_Handler
-   
-    .weak TIM14_IRQHandler
-    .thumb_set TIM14_IRQHandler,Default_Handler
-    
-    .weak TIM15_IRQHandler
-    .thumb_set TIM15_IRQHandler,Default_Handler
-    
-    .weak TIM16_IRQHandler
-    .thumb_set TIM16_IRQHandler,Default_Handler
-    
-    .weak TIM17_IRQHandler
-    .thumb_set TIM17_IRQHandler,Default_Handler
-    
-    .weak I2C1_IRQHandler
-    .thumb_set I2C1_IRQHandler,Default_Handler
-    
-    .weak I2C2_IRQHandler
-    .thumb_set I2C2_IRQHandler,Default_Handler
-    
-    .weak SPI1_IRQHandler
-    .thumb_set SPI1_IRQHandler,Default_Handler
-    
-    .weak SPI2_IRQHandler
-    .thumb_set SPI2_IRQHandler,Default_Handler
-    
-    .weak USART1_IRQHandler
-    .thumb_set USART1_IRQHandler,Default_Handler
-    
-    .weak USART2_IRQHandler
-    .thumb_set USART2_IRQHandler,Default_Handler
-   
-    .weak USART3_4_IRQHandler
-    .thumb_set USART3_4_IRQHandler,Default_Handler
-    
-    .weak CEC_CAN_IRQHandler
-    .thumb_set CEC_CAN_IRQHandler,Default_Handler
-   
-    .weak USB_IRQHandler
-    .thumb_set USB_IRQHandler,Default_Handler
-   
-  /************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/
-   
  复制代码  原来是这样,中断向量表在这里进行了描述,还有设置堆栈,初始化全局变量的代码,然后跳转到 main 执行。好了,这样就该差不多了。这个汇编程序是GNU AS的语法,可以用 arm-none-eabi-gcc 来直接汇编 arm-none-eabi-gcc -c startup_stm32f072.s 链接两个目标模块 arm-none-eabi-ld mini.o startup_stm32f072.o -Le:arm-2014q3arm-none-eabilibarmv6-m -Le:arm-2014q3libgccarm-none-eabi4.8.4armv6-m -T STM32F072C8_FLASH.ld -o mini.elf 最后转换出一个 HEX 文件 arm-none-eabi-objcopy -Oihex mini.elf mini.hex 可以进行烧写了。  我这个是最简化的例子,使用最简化的软件工具,不过已经包含了基本的C语言框架。后面随着我本人的学习,我会继续分享怎么开发一个简单的 USB 设备。
			
			
			
			             
									  查看评论  回复		
						  	
			
			
						  		| 匿名 | 2018-07-18 14:21:54 | 
 	 	| stm32F042开发用什么开发工具好 | 
 
1楼  
回复本楼