• ARM Cortex-M3从汇编到C,从Boot到应用的教程


    ARM Cortex-M3从汇编到C,从Boot到应用的教程

    作者将狼才鲸
    创建日期2022-11-05

    Gitee工程和源码地址:才鲸嵌入式 / ARM-Cortex-M3从汇编到C_从Boot到应用教程
    CSDN文章阅读地址:ARM Cortex-M3从汇编到C,从Boot到应用的教程
    Bilibili视频讲解地址(待完成):才鲸嵌入式


    工程名称作用
    01_Hello_world使用Keil的模拟器在虚拟终端输出Hello world
    02_Keil_boot_commentsKeil自带汇编boot的注释
    03_Self_assembler_boot自行实现汇编boot
    04_Uart_loopback串口收发回环

    一、前言

    1)本仓库的目的

    • 本仓库计划实现的内容:

      • 描述Cortex-M3的指令集和通用寄存器。
      • 针对M3内核,使用汇编从复位开始写boot引导C语言main()函数。
      • 不使用任何芯片厂商提供的开发包,自己写Boot,自行移植C语言库函数,自己写所有驱动和应用。
      • 移植一款RTOS操作系统。
    • 本仓库面向的目标读者:

      • 使用M3的某一款芯片写过驱动或应用,但是对M3的boot过程和底层代码不熟悉的。
      • 没接触过ARM,对32位芯片的工作流程感兴趣的。
      • 对整个嵌入式裸机软件结构感兴趣的。
    • 不适用本仓库的读者:

      • 想尽快用ARM芯片写出一个项目的(这时应该直接使用STM32,调用它丰富且使用简洁的库)。
      • 不想使用模拟器,而是想直接使用一款硬件运行程序的。

    2)M3介绍

    • M3由ARM公司于2004年推出,至今仍是很多单片机芯片使用的内核,如STM32F10x系列。

    • ST公司于2007年首次使用ARM公司的内核,产品是F1,随后凭借其简洁易用的软件开发包逐渐发展出著名的STM32系列,累计出货量近百亿颗。

    • M3属于ARMv7架构,ARMv7是ARM11之后的版本。

    • M3属于Cortex系列,该系列有三类:A、R和M,比如熟悉的Cortex-A9。

    • M3内核仅33000门。

    • M3不能使用ARM指令集,而是使用Thumb或Thumb-2指令集。

      • M3使用Cortex微控制器软件接口标准 (CMSIS)作为硬件抽象层。
      • M3最大支持512M代码,512M SRAM,1G外部RAM,详见芯片文档的“3.4 Processor memory model”,芯片文档下载地址见本文档下面第三章。
      • M3的R0~R15通用寄存器介绍详见芯片文档的“3.8 Processor core register summary”。
      • M3的系统控制寄存器介绍详见芯片文档的“4.1 System control registers”,共有36个,如定时器、中断控制、系统状态、内存模式、指令集设置。
    • 参考资料:

    MCU缺货涨价后的国产化浪潮(三):全球 MCU 市场高度集中,多因素共振加速国产替代 文章里也列出了全球和国内的MCU厂商和所有嵌入式的行业。
    ARM CORTEX-M3简介
    ARM Cortex-M3
    ARM发布适于高性能、低成本应用的Cortex-M3处理器

    3)开发环境

    • 本仓库所有工程均在Keil下创建,并使用Keil的模拟器运行,不使用具体的硬件开发板。
    • 后续可能会在QEMU模拟器上运行。

    二、ARM-MDK IDE集成开发环境下载

    • MDK-arm软件社区版官方介绍(无代码大小限制,不能用于商用,需要先获取社区版许可证,也就是在官网注册账号后再下载):MDK-社区版概述
    • 获取社区版许可证:Log in with your Arm or Mbed account
    • 下载地址主界面:MDK Community Edition
    • 下载地址举例:https://www.keil.com/fid/comahow53j1j1wriguw1y56me9lv1dgw3o3fd1/files/eval/mdk536.exe
    • MKD-arm评估版软件官方下载地址(也就是不注册账号就下载,有32K代码限制):mdk536.exe
    • 安装的时候会自动下载各种芯片包。
    • MKD里面没有硬件模拟器,可以直接运行和调试程序,你也可以编译完生成可执行程序后在QEMU软件里面仿真运行。
    • Keil创建M3工程的流程可以网上自行搜索,创建时可以添加ARM官方提供的各个模块的代码,可以节省开发时间。

    三、M3指令集和寄存器介绍

    1)M3文档在线阅读及下载

    2)其它ARM核指令集介绍

    四、Keil汇编伪指令介绍

    五、软件工程及源码

    1)01_Hello_world

    2)02_Keil_boot_comments

    • 给Keil自带的boot加上注释
    • 工程和源码在本文档同级目录\src\02_Keil_boot_comments\下
      • Keil自带的boot代码的汇编底层在Keil自带的工具包中,看不到,只注释能看到的C代码部分
      • Keil自带的boot代码的类汇编文件是.svt
      • Keil自带的头文件也在自己的工具包中,不能更改
      • 这个工程看不到boot的完整流程,下个工程会演示从第一行汇编代码引导到main.c的过程
      • 注意:ARMCM3_ac6.sct文件的注释使用了GB2312编码,已经配置了UTF-8的Keil中直接打开会显示乱码,其它的文件都是UTF-8格式

    3)03_Self_assembler_boot

    ; 使用Keil自动生成时,也可用纯C写Boot相关的配置
    
                    INCLUDE YOUR_CONFIG.INC ; #include "YOUR_NAME.INC" 包含头文件
    ; 用户自定义宏定义
    YOUR_CONFIG1    EQU 0x01    ; 类似于#define宏定义,用不同的配置选项配置程序
    YOUR_CONFIG2    EQU 0x01
    
                    PRESERVE8   ; 指定当前文件要求堆栈八字节对齐
                    THUMB       ; 使用THUMB指令集,不使用ARM指令集
    
    ; 定义堆,堆是malloc主动分配内存的位置
    Heap_Size       EQU 0       ; Heap_Size是MDK指定的堆空间长度名称;不用malloc分配的堆没什么用,所以长度设置为0
                    IF Heap_Size != 0       ; IF ELSE ENDIF和同名宏定义的含义类似
                    AREA     HEAP, NOINIT, READWRITE, ALIGN=3 ; 申明开辟名为HEAP的内存,不写入值初始化,可读可写,2^3 8字节对齐
                    EXPORT   __heap_base    ; MDK指定的名称,堆起始地址位置
                    EXPORT   __heap_limit   ; MDK指定的名称,堆结束地址位置
    __heap_base
    Heap_Mem        SPACE    Heap_Size      ; 开始分配指定长度的内存
    __heap_limit
                    ENDIF
    
    ; 定义栈,栈是为全局变量自动分配空间的位置
    Stack_Size      EQU      (4096)         ; Stack_Size是MDK指定的栈空间长度名称
    
                    AREA     STACK, NOINIT, READWRITE, ALIGN=3 ; 申明开辟名为STACK的内存,不写入值初始化,可读可写,2^3 8字节对齐
                    EXPORT   __stack_limit  ; MDK指定的名称,栈起始地址位置
                    EXPORT   __initial_sp   ; MDK指定的名称,栈结束地址位置
    
    __stack_limit
    Stack_Mem       SPACE    Stack_Size     ; 开始分配指定长度的一片连续的内存
    __initial_sp
    
                    AREA     RESET, DATA, READONLY      ; 定义数据段,名字为RESET;上电后首先运行的函数地址
                    EXPORT   __Vectors                  ; 输出ARM CMSIS中需要用到的一些标号,__Vectors函数在后续定义
                    EXPORT   __Vectors_End
                    EXPORT   __Vectors_Size
                    EXPORT   Default_Interrupt_Handler  ; 中断入口
                    IMPORT   __initial_sp
    
    ; 申明异常和中断入口
    __Vectors       DCD      __initial_sp               ;     Top of Stack
                    DCD      Reset_Handler              ;     Reset Handler
                    DCD      NMI_Handler                ; -14 NMI Handler
                    DCD      HardFault_Handler          ; -13 Hard Fault Handler
                    DCD      MemManage_Handler          ; -12 MPU Fault Handler
                    DCD      BusFault_Handler           ; -11 Bus Fault Handler
                    DCD      UsageFault_Handler         ; -10 Usage Fault Handler
                    DCD      0                          ;     Reserved
                    DCD      0                          ;     Reserved
                    DCD      0                          ;     Reserved
                    DCD      0                          ;     Reserved
                    DCD      SVC_Handler                ;  -5 SVCall Handler
                    DCD      DebugMon_Handler           ;  -4 Debug Monitor Handler
                    DCD      0                          ;     Reserved
                    DCD      PendSV_Handler             ;  -2 PendSV Handler
                    DCD      SysTick_Handler            ;  -1 SysTick Handler
    
                    ; Interrupts
                    DCD      Interrupt_Handler_0
                    ; …… 省略其它中断 ……
                    DCD      Interrupt_Handler_45       ; BCD:分配存储单元
    __Vectors_End
    __Vectors_Size  EQU      __Vectors_End - __Vectors
    
                    ; 类似于宏定义函数
                    MACRO                               ; 宏定义函数的开始 
                    Set_Default_Handler $Handler_Name   ; 前一个时宏定义函数的名字,后面是要操作的对象
    $Handler_Name   PROC                                ; 定义子程序
                    EXPORT   $Handler_Name [WEAK]       ; 输出函数名;[WEAK]虚函数,可定义可不定义
                    B        .                          ; 死循环
                    ENDP                                ; 子程序定义结束
                    MEND                                ; 宏定义函数结束
    
                    AREA     |.text|, CODE, READONLY    ; 定义.text代码段,只读
    
                    ; 申明默认的异常和中断处理函数
                    Set_Default_Handler  Reset_Handler
                    Set_Default_Handler  NMI_Handler
                    Set_Default_Handler  HardFault_Handler
                    Set_Default_Handler  MemManage_Handler
                    Set_Default_Handler  BusFault_Handler
                    Set_Default_Handler  UsageFault_Handler
                    Set_Default_Handler  SVC_Handler
                    Set_Default_Handler  DebugMon_Handler
                    Set_Default_Handler  PendSV_Handler
                    Set_Default_Handler  SysTick_Handler
                    Set_Default_Handler  Default_Interrupt_Handler
                    Set_Default_Handler  Interrupt_Handler_0
                    ; …… 省略其它中断 ……
                    Set_Default_Handler  Interrupt_Handler_45
     
                   ; 各种程序
                    ; Reset_Handler是板子上电后首先执行的位置,它由异常中断的跳转来实现
    Reset_Handler   PROC                    ; 程序名 PROC 程序内容 ENDP 程序结束
                    EXPORT   Reset_Handler
                    IMPORT	 __hardwareInit ; 自己编写的C程序,在里面初始化各种硬件配置
                    IMPORT   __main         ; main()函数入口
                    BL       __hardwareInit ; 调用初始化硬件的汇编函数
                    BL       __main         ; 跳转到main()函数
                    BL       cpuStall       ; 程序退出后一直死循环
                    ALIGN
                    ENDP
    
    cpuStall        PROC
                    EXPORT   cpuStall
                    B        .              ; 死循环
                    ENDP
                    END                     ; 通知编译器已经到了该源文件的结尾了
    
    __hardwareInit  PROC
                    EXPORT   __hwInitialize
                    PUSH     {R0,R1,R2,R3,LR} ; 压栈
                    ; 配置GPIO输出
                    ; 配置PLL系统主频,将主频从晶振原有的频率提高到实际的工作频率
                    ; 初始化串口
                    ; 初始化其它外设
                    POP      {R0,R1,R2,R3,LR} ; 弹栈
                    BX       LR               ; 跳转到LR寄存器里的地址执行,也就是跳转回被调用的地方
                    ALIGN
                    ENDP
    
                    ; 其它的.inc汇编头文件中要做的事
                    ; 定义各个硬件模块的地址
                    ; 定义所有中断号
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124

    4)04_Uart_loopback

    • 串口收发回环

    5)……

  • 相关阅读:
    sq练习2
    《Java编程思想》读书笔记(五)
    第十一届蓝桥杯C++青少年组中/高级组选拔赛2019年真题解析
    HTL6033是一款专用于3串锂电池或聚合物电池的保护芯片
    教你实现多线程案例定时器
    【openEuler系列】配置本地yum源
    使用canvas 和 webgl2制作界面系统(一)
    线性回归模型用于波士顿房价预测的(普通VSsklearn库方法)比较
    第一章 计算机系统概述 八、虚拟机
    关于安装ubuntu 18.06.6 系统时出现[Errno 5] - Input/output error 错误的解决方案
  • 原文地址:https://blog.csdn.net/qq582880551/article/details/127711475