嵌入式系统课程作业


第一次作业

  1. 列举目前市面上常用的嵌入式操作系统名字(越多越好)

Android、uClinux、WinCE、PalmOS、Symbian、eCos、uCOS-II、VxWorks、pSOS、Nucleus、ThreadX 、Rtems 、QNX、INTEGRITY、OSE、C ExecuTIve

  1. 列举基于模型的系统工程 与 传统开发流程的优势(至少4点)

(一)基于模型的系统工程(MBSE)是建模方法的形式化应用,以使建模方法支持系统要求、设计、分析、验证和确认等活动,这些活动从概念性设计阶段开始,持续贯穿到设计开发以及后来的所有寿命周期阶段。

优势

(1)改善了开发系统的利益相关者(客户、项目管理人员、系统工程师、软硬件工程师、测试人员和各专业工程学科的人员)之间的沟通;

(2)通过使系统模型能够被从多个侧面进行观察,以及提供变更影响分析的能力, 提高了管理复杂系统的能力;

(3)通过提供可评估一致性、正确性和完善性的无歧义的且精确的系统模型,提升了产品质量;

(4)通过以更加标准化的方式捕获信息并高效地利用模型驱动方法固有的内置抽象机制,增强知识捕获及信息的复用。这会导致缩短开发周期和更低的维护成本,以改进设计。

(二)传统开发流程是一个文档驱动的流程,它将整个软件开发过程划分为顺序相接的几个阶段,每个阶段都必需完成全部规定的任务(文档)后才能够进入下一个阶段。

优势

(1)从系统整体出发,强调在整体优化的条件下“自上而下”地分析和设计,保证了系统的整体性和目标的一致性;

(2)遵循用户至上原则;

(3)每一阶段的工作成果是下一阶段的依据,便于系统开发的管理和控制;

(4)文档规范化,按工程标准建立标准化的文档资料。

  1. 列举RISC-V相对ARM构架的优势和缺陷,各三条。

优势

(1)RISC-V 是一个完全开源且免版税的指令集架构,可以用于任何目的,允许任何人设计、制造以及销售RISC-V芯片和软件,相对ARM构架具有开放的生态;

(2)RISC-V技术是后发技术,能够总结前人的经验教训,做到相对简洁和干净;

(3)RISC-V指令集架构具有低功耗、低成本、开源开放、可模块化、简洁、面积小、速度快等优点,与IoT场景需求碎片化的、可定制化的特性十分契合。

劣势

(1)由于RISC-V诞生时间太短,相关的编译器、开发工具和软件开发环境(IDE)以及其它生态要素还在发展;

(2)RISC-V 定义了一个很小的、带有扩展的 ISA,导致ISA 碎片化;

(3)RISC-V 尚未实现部分性能的提升,如超标量执行等。

  1. 查询自己手机的处理器型号、性能和参数,并描述其ARM版本(或源自的版本),以及指令集的版本。

处理器

image-20230424233851772

型号:HUAWEI Kirin 990 5G

性能:作为华为首款旗舰5G SoC,Kirin 990 5G采用先进的7nm+ EUV工艺制程,将5G Modem集成到SoC芯片中,面积更小,功耗更低;率先支持NSA/SA双架构和TDD/FDD全频段,是领先的全网通5G SoC。麒麟990 5G NR理论下行峰值速率可达2.3Gbps,NR理论上行峰值速率达1.25Gbps,提供5G极速体验。

参数见下表

网络制式 2G/3G/4G/5G
CPU制程 7nm+EUV
CPU主频 2X Cortex-A76 Based @2.86GHz 2X Cortex-A76 Based @2.36GHz 4X Cortex-A55 @1.95GHz
GPU 16 Core Mali-G76
NPU 2 Big-Core +1 Tiny-Core
通用闪存(UFS) UFS 3.0 , UFS 2.1

ARM版本:16 Core Mali G76 GPU

指令集版本:采用的是RISC精简指令集RISC-V

第二次作业

1.深入调研并介绍IIC总线的结构、通信原理、特点(速度、同步性、双工性等)等。从理解的角度分析IIC总线的优缺点(相对SPI和CAN)。

IIC(Inter-Integrated Circuit)总线是一种串行通信协议,由飞利浦公司(Philips)于1980年代初开发,用于连接微控制器和周边设备。

①结构

IIC总线由两条双向线路组成:数据线(SDA)和时钟线(SCL),其中SDA线用于传输数据,SCL线用于传输时钟信号。通信的两端各有一个主设备和一个或多个从设备。主设备控制总线的访问,从设备响应主设备的命令并提供数据。总线上的设备通过IIC地址进行寻址。结构示意图如下:

image-20230424233937803

②通信原理

IIC总线采用主从结构进行通信,主设备可以是任何一台能够控制总线的设备(通常是微控制器),而从设备则是被控制的设备(如温度传感器、EEPROM存储器等)。通信分为两种模式:传输模式和接收模式。在传输模式下,主设备向从设备发送数据,从设备负责接收并响应;在接收模式下,主设备向从设备发送命令,从设备则会发送数据给主设备。

在IIC总线通信时,主设备控制SCL线的时钟信号,而SDA线上的数据则在时钟信号的控制下进行传输。传输时,主设备在SCL线上产生时钟脉冲,同时在SDA线上发送数据位。从设备在时钟脉冲的控制下读取SDA线上的数据位,并在下一个时钟脉冲的前沿将其应答给主设备。如果数据传输成功,则从设备返回应答信号。

③特点

(1)速度

IIC总线的速度取决于SCL线上的时钟频率。在标准模式下,时钟频率为100 kHz,最大数据传输速率为100 kbit/s。在快速模式下,时钟频率为400 kHz,最大数据传输速率为400 kbit/s。在高速模式下,时钟频率可以达到3.4 MHz,最大数据传输速率为3.4 Mbit/s。

(2)同步性

IIC总线是同步通信协议,即数据传输的时序由时钟信号控制。主设备通过控制时钟信号来确保通信的同步性,从而保证数据的准确传输。

(3)双工性

IIC总线支持双向通信,即主设备和从设备都可以发送和接收数据。主设备可以在任何时候控制总线的访问,并根据需要在总线上发送数据或接收数据。从设备只有在主设备允许时才能发送数据,并且只有在主设备发起读操作时才能发送数据。在同一时间内,IIC总线只能进行读或写操作,不能同时进行,属于半双工同步传输型总线。

(4)多主机支持

IIC总线支持多主机系统,即在同一总线上连接多个主设备。在这种情况下,每个主设备都有自己的地址,并且在使用总线之前必须获得总线的控制权。多主机系统可以提高系统的灵活性和可扩展性,可以实现更复杂的系统设计。

(5)适用范围广

IIC总线的应用范围非常广泛,可以用于连接各种类型的设备,如传感器、存储器、显示屏、电机驱动器等。它也可以用于通信距离比较短的应用场景,如板级通信、嵌入式系统等。

④优点

(1)线路简单

相对于SPI总线和CAN总线,IIC总线只需要两条信号线进行通信,因此线路非常简单,易于实现和维护。相比之下,SPI总线需要四条信号线(一个主设备选择线、一个主设备输出线、一个从设备输出线和一个时钟线),而CAN总线需要两条信号线(一个CAN高速线和一个CAN低速线),因此IIC总线在线路复杂度方面具有优势。

(2)多主机支持

IIC总线支持多主机系统,即在同一总线上连接多个主设备,每个主设备都有自己的地址,并且在使用总线之前必须获得总线的控制权。这使得多主机系统可以实现更复杂的系统设计和更高的灵活性,而SPI总线只支持单主机系统,CAN总线虽然支持多主机系统,但需要使用更复杂的控制协议。

⑤缺点

(1)速度慢

相对于SPI总线和CAN总线,IIC总线的速度较慢,最高速度只有400Kbps。因此,在一些需要高速数据传输的应用中,IIC总线可能无法满足要求。相比之下,SPI总线的最高速度可以达到100Mbps,CAN总线的最高速度可以达到1Mbps或更高,因此在速度方面,IIC总线具有劣势。

(2)同步性差

IIC总线的同步性较差,因为IIC总线的时钟由主设备控制,因此在一些高精度时序要求的应用中可能会出现问题。相比之下,SPI总线和CAN总线都具有较好的同步性,因为它们都有独立的时钟线。

(3)双工性差

IIC总线的双工性较差,在同一时间内,IIC总线只能进行读或写操作,不能同时进行;而SPI总线和CAN总线都具有良好的双工性,可以同时进行读和写操作。

由于IIC、SPI、CAN总线各有各的特点和优势,我们可以根据具体的应用场景来选择最适合的通信总线。若需要简单的线路和多主机支持,可以选择IIC总线;若需要高速数据传输和良好的同步性,可以选择SPI总线;若需要长距离通信和实时性要求较高的应用,则可以选择CAN总线。

  1. 结合课堂内容,设异常触发指令的地址为0xFFFF0100,计算一下各个异常模式时,PC、LR、PRI*、Return Code的具体取值,写明分析过程。

(1)Reset模式

在Reset模式下,处理器会执行一系列初始化操作,包括将寄存器清零和设置中断向量表等。PC未更新,则PC值为异常触发指令地址+8,即0xFFFF0108, LR**、PRI*、Return Code**均为N/A(不确定)。

(2)Undefined Instruction模式

当处理器遇到一条无法识别的指令时,它会进入Undefined Instruction异常模式。在这个模式下,处理器会暂停执行,并且会向操作系统发出一个异常信号,以便系统处理这个异常。PC未更新,则PC值为异常触发指令地址+8,即0xFFFF0108,LR值为PC-4=0xFFFF0104,PRI*值为下一条指令地址,为0xFFFF0104。当执行 MOVS PC, R14_und 指令时,将恢复PC(从R14_und)和CPSR(从SPSR_und)的值,并返回到未定义指令后的下一条指令,则Return Code为0xFFFF0104。

(3)Software Interrupt模式

当执行软件中断指令时,处理器会进入Software Interrupt异常模式。该模式用于处理与处理器相关的软件中断请求。PC未更新,则PC值为异常触发指令地址+8,即0xFFFF0108,LR值为PC-4=0xFFFF0104,PRI*值为下一条指令地址,为0xFFFF0104。当执行 MOVS PC, R14_svc 指令时,将恢复PC(从R14_svc)和CPSR(从SPSR_svc)的值,并返回到SWI的下一条指令,则Return Code为0xFFFF0104。

(4)Prefetch Abort模式

当处理器试图执行从内存中读取的非法指令或数据时,会触发Prefetch Abort异常。PC未更新,则PC值为异常触发指令地址+8,即0xFFFF0108,LR值为PC-4=0xFFFF0104,PRI*值为当前指令地址,为0xFFFF0100。当执行SUBS PC,R4_abt,#4后,将重新执行被中止的指令,则Return Code为0xFFFF0100。

(5)Data Abort模式

当处理器试图执行或存储非法数据时,会触发Data Abort异常。这可能是因为数据不在可寻址的内存空间中,或者由于硬件错误而导致读取或写入失败。PC更新,则PC值为异常触发指令地址+12,即0xFFFF010C,LR值为PC-4=0xFFFF0108,PRI*值为当前指令地址,为0xFFFF0100。当执行SUBS PC,R4_abt,#8后,将重新执行被中止的指令,则Return Code为0xFFFF0100。

(6)Interrupt模式

当处理器接收到来自外部设备的中断信号时,会进入Interrupt异常模式。在该模式下,处理器会执行特定的中断服务例程,以响应外部中断请求。PC更新,则PC值为异常触发指令地址+12,即0xFFFF010C,LR值为PC-4=0xFFFF0108,PRI*值为下一条指令地址,为0xFFFF0104。当执行SUBS PC,R14_irq,#4后,将返回到异常指令后的下一条指令,则Return Code为0xFFFF0104。

(7)Fast Interrupt模式

当Fast Interrupt信号被触发时,处理器会立即跳转到Fast Interrupt异常模式,并且禁用中断响应。在该模式下,处理器会执行特定的中断服务例程,以尽快响应高优先级的中断请求。PC更新,则PC值为异常触发指令地址+12,即0xFFFF010C,LR值为PC-4=0xFFFF0108,PRI*值为下一条指令地址,为0xFFFF0104。当执行SUBS PC,R14_fiq,#4后,将返回到异常指令后的下一条指令,则Return Code为0xFFFF0104。

第三次作业

  1. SUB R3,R2,R1,LSR R0

上面汇编指令中,R0,R1,R2,R3分别在32位的指令长度的位数范围?

R0代表寄存器移位位数,对应指令8-11位;R1对应指令0-3位;

R2对应指令16-19位;R3代表指令12-15位。

  1. 读程序,给每一行增加注释,并说明执行过程中R0,R1,R2的值如何变化?
AREA StrCopy, CODE, READONLY ; 定义代码区域,不可修改,只可读
ENTRY            ; 指定程序的入口点
start            ; 程序入口

X EQU 88           ; 定义符号常量X为88
Y EQU 76           ; 定义符号常量Y为76
Z EQU 96           ; 定义符号常量Z为96

STACK_TOP EQU 0X1000     ; 定义符号常量STACK_TOP为0X1000
  MOV R0, #0XAB      ; 把0XAB的值加载到寄存器R0中,R0=0xAB
LOOP1
  MOV R0, R0, ASR #1    ; 把R0右移一位,并把结果存回R0,R0=0x55,第二次执行该语句时变为R0=0x2A
  CMP R0, #0X50      ; 比较R0与0X50的大小
  BGE LOOP1        ; 如果R0大于等于0X50则跳转到LOOP1标记处(此处会跳转)
  MOV R1, #Y        ; 把Y的值加载到寄存器R1中,R1=76=0x4C
  ADD R2, R0, R1, LSL #1  ; 把R0和R1左移一位的结果相加,存入R2中, R2=R0+R1*2=0xC2
  MOV SP, #0X1000     ; 把0X1000的值加载到栈指针寄存器SP中
  STR R2, [SP]       ; 把R2的值存入以栈指针寄存器SP的值为地址的存储单元中
  MOV R0, #Z        ; 把Z的值加载到寄存器R0中,R0=96=0x60
  AND R0, R0, #0XFF    ; 把R0与0XFF进行与运算,存入R0中, R0=0x60
  MOV R1, #Y        ; 把Y的值加载到寄存器R1中,R1=76=0x4C
  ADD R2, R0, R1, LSR #1  ; 把R0和R1右移一位的结果相加,存入R2中,R2=R0+R1/2=0x86
  LDR R0, [SP]       ; 将以栈指针寄存器SP的值为地址的存储单元中的内容加载到寄存器R0中,R0=0xC2
  MOV R1, #0X01      ; 把0X01的值加载到寄存器R1中,R1=0x01
  ORR R0, R0, R1      ; 把R0和R1进行或运算,存入R0中,R0=0xC3
  MOV R1, R2        ; 把R2的值加载到寄存器R1中,R1=0x86
  ADD R2, R0, R1, LSR #1  ; 把R0和R1右移一位的结果相加,存入R2中,R2=R0+R1/2=0x106
STOP
  B STOP          ; 无条件跳转到STOP标记处,即进入死循环
END

程序最终运行结果为:R0=0xC3, R1=0x86, R2=0x106.

第四次作业

任务一: 安装ARM编译环境

image-20230424234227363

对项目代码进行编译,显示0 Errors, 0 Warnings,说明环境配置正常,编译通过:

image-20230424234234468

任务二:运行STM32F746_Experiment_v1.1\01_汇编\3.1_asm1\Asm1_b例程,进入debug模式。

Debug界面如下所示:

image-20230424234301190

阅读代码,计算每行代码的目标寄存器的值。

x     EQU 45               	;/* x=45 */
y     EQU 64               	;/* y=64 */
z     EQU 87               	;/* z=87 */
stack_top EQU 0x30200000   	;/* define the top address for stacks*/

export Reset_Handler

AREA text,CODE,READONLY

Reset_Handler              	;/* code start */
    mov  r0, #x            	; R0=0x2D
    mov  r0, r0, lsl #8    	; R0=0x2D00
    mov  r1, #y        		; R1=0x40
    add  r2, r0, r1, lsr #1 ; R2=0x2D20
    ldr  sp, =stack_top    	; SP=0x30200000
    str  r2, [sp]
    mov  r0, #z        		; R0=0x57
    and  r0, r0, #0xFF    	; R0=0x57
    mov  r1, #y        		; R1=0x40
    add  r2, r0, r1, lsr #1  ; R2=0x77
    ldr  r0, [sp]       	; R0=0x2D20
    mov  r1, #0x01      	; R1=0x01
    orr  r0, r0, r1      	; R0=0x2D21
    mov  r1, R2        		; R1=0x77
    add  r2, r0, r1, lsr #1  ; R2=0x2D5C
stop
	b   stop         		;/* end the code £¬cycling*/
END

第五次作业

一. 阅读发布的Um_s3c2410.pdf开发手册,回答如下问题

  1. 对于I/O端口操作需求,假设GPB0端口连接了一个按键,我需要读按键的值。

1)至少需要操作哪两个寄存器,对应的地址是多少?提示:搜索“GPB0”,找到寄存器名和地址。

①GPBCON寄存器,寄存器的地址为0x56000010;

②GPBDAT寄存器,寄存器的地址为0x56000014。

2)需要操作这两个寄存器的哪几个bit位,如何操作?提示:例如 传感器GP[0:1]=00,按键值=GP*[]*

①寄存器GPBCON[1:0]=01;

②按键值=GPBDAT[0]。

2.解释下列寄存器的含义以及地址

1)ADCCON

含义:ADC控制寄存器,用于控制模数转换器的工作模式和采样率。

地址:0x58000000

2)WTCON

含义:看门狗定时器控制寄存器,用于设置看门狗计时器的计时时钟和计时时间,并控制其启用和禁用。

地址:0x53000000

3)SPPIN0

含义:SPI通道0引脚控制寄存器。

地址:0x59000008

4)BWSCON

含义:总线宽度和等待状态控制寄存器,用于控制外部存储器的总线宽度和等待状态。

地址:0x48000000

5)BANKSIZE

含义:可变内存库大小寄存器,用于存储处理器或计算机系统中内存或存储器大小的寄存器。

地址:0x48000028

二. 以实验包中“STM32F746_Experiment_v1.1\02_GPIO”例程为例子,描述STM32系列嵌入式系统的启动过程。

参考资料如下:

1) STM32F74xx中文参考手册(1).pdf (STM32F746_Experiment_v1.1目录下)
2) 上课讲课的PPT
3) 自行网上搜索。例如:https://blog.csdn.net/luobeihai/article/details/117595762

①设置栈和堆的信息

(1)栈

Stack_Size   EQU   0x00000400
	AREA  STACK, NOINIT, READWRITE, ALIGN=3
	Stack_Mem    SPACE  Stack_Size
__initial_sp

开辟栈的大小为 0X00000400(1KB),栈的作用是用于局部变量、函数调用、函数形参等的开销;标号__initial_sp 紧挨着 SPACE 语句放置,表示栈的结束地址,即栈顶地址。

(2)堆

Heap_Size    EQU   0x00000200
	AREA  HEAP, NOINIT, READWRITE, ALIGN=3
__heap_base
Heap_Mem    SPACE  Heap_Size
__heap_limit
     PRESERVE8
     THUMB

开辟堆的大小为 0X00000200(512 字节),heap_base 表示堆的起始地址, heap_limit 表示堆的结束地址。堆主要用来动态内存的分配。

②定义中断向量表

__Vectors    DCD   __initial_sp    ; Top of Stack
        DCD   Reset_Handler        ; Reset Handler
        DCD   NMI_Handler      	   ; NMI Handler
        DCD   HardFault_Handler    ; Hard Fault Handler
        DCD   MemManage_Handler    ; MPU Fault Handler
        DCD   BusFault_Handler     ; Bus Fault Handler
        DCD   UsageFault_Handler   ; Usage Fault Handler        
		......

ARM 规定向量表的起始位置存放的是栈顶指针 MSP 的地址值,紧接着存放的是复位中断入口函数的地址。当刚上电的时候,硬件会根据向量表的地址找到向量表的具体位置(对于向量表的地址是可以通过 NVIC 中的一个重定位寄存器来设置的,复位时该寄存器的值为0),然后会根据向量表中的这两个数据,设置 SP、PC 的值,这时 CPU 就会从复位中断的入口函数开始取指令运行程序。

③调用SystemInit函数,设置系统时钟

Reset_Handler  PROC
       EXPORT Reset_Handler       [WEAK]
       IMPORT __main
       IMPORT SystemInit
       LDR   R0, =SystemInit
       BLX   R0        			; 调用SystemInit函数
       LDR   R0, =__main
       BX   R0
ENDP

硬件设置好了 SP、PC 的值后,这时CPU 会从 Reset_Handler 处开始取指令运行,首先会调用 SystemInit 函数来初始化系统时钟,该函数是官方固件库提供的一个库函数,在 system_stm32f7xx.c 文件中定义如下:

void SystemInit(void)
{
 /* FPU settings */
 #if (__FPU_PRESENT == 1) && (__FPU_USED == 1)
 SCB->CPACR |= ((3UL << 10*2)|(3UL << 11*2)); /* set CP10 and CP11 Full Access */
 #endif
 
 /* Reset the RCC clock configuration to the default reset state */
 /* Set HSION bit */
 RCC->CR |= (uint32_t)0x00000001;
 
 /* Reset CFGR register */
 RCC->CFGR = 0x00000000;

 /* Reset HSEON, CSSON and PLLON bits */
 RCC->CR &= (uint32_t)0xFEF6FFFF;

 /* Reset PLLCFGR register */
 RCC->PLLCFGR = 0x24003010;

 /* Reset HSEBYP bit */
 RCC->CR &= (uint32_t)0xFFFBFFFF;

 /* Disable all interrupts */
 RCC->CIR = 0x00000000;

#if defined (DATA_IN_ExtSRAM) || defined (DATA_IN_ExtSDRAM)
	SystemInit_ExtMemCtl(); 
#endif /* DATA_IN_ExtSRAM || DATA_IN_ExtSDRAM */

/* Configure the Vector Table location add offset address */
#ifdef VECT_TAB_SRAM
	SCB->VTOR = SRAM1_BASE | VECT_TAB_OFFSET; /* Vector Table Relocation in Internal SRAM */
#else
 	SCB->VTOR = FLASH_BASE | VECT_TAB_OFFSET; /* Vector Table Relocation in Internal FLASH */
#endif
}

该函数完成以下功能:

  • FPU 设置部分:如果芯片支持 FPU(浮点运算器),则设置 CP10 和 CP11 为完全访问权限。

  • RCC 时钟配置部分:将芯片的 RCC 寄存器设置为默认值。具体步骤包括:

    • 打开 HSION(内部高速时钟)位。

    • 重置 CFGR 寄存器为 0;

    • 关闭 HSEON(外部高速时钟)、CSSON(时钟安全系统)和 PLLON(锁相环)位;

    • 将 PLLCFGR 寄存器重置为默认值;

    • 关闭 HSEBYP(外部高速时钟旁路)位;

    • 禁用所有中断。

  • 外部存储器部分:如果使用外部存储器(如外部 SRAM 或 SDRAM),则调用 SystemInit_ExtMemCtl() 函数进行配置。

  • 向量表位置部分:根据宏定义的设置,将向量表位置设置为内部 SRAM 或内部 FLASH。

④调用__main函数

__main 函数是系统自带的库函数,根据反汇编的文件,得到的该函数的部分代码如下:

__main
  _main_stk
	0x08000130:  f8dfd010  ....  LDR   sp,__lit__00000000 ; [0x8000144] = 0x200007a0
	.ARM.Collect$$$$00000004
  _main_scatterload

	0x08000134:  f000f81a  ....  BL    __scatterload ; 0x800016c

接下来就跳转到了 __scatterload 函数,从函数名可以看出这是一个分散加载函数,该函数主要实现的功能就是数据段的重定位和清除 bss 段,初始化栈空间。该函数的反汇编源码如下:

__scatterload
  __scatterload_rt2
    0x0800016c:  4c06    .L   LDR   r4,[pc,#24] ; [0x8000188] = 0x80005d4
    0x0800016e:  4d07    .M   LDR   r5,[pc,#28] ; [0x800018c] = 0x80005f4
    0x08000170:  e006    ..   B    0x8000180 ; __scatterload + 20
    0x08000172:  68e0    .h   LDR   r0,[r4,#0xc]
    0x08000174:  f0400301  @...  ORR   r3,r0,#1
    0x08000178:  e8940007  ....  LDM   r4,{r0-r2}
    0x0800017c:  4798    .G   BLX   r3
    0x0800017e:  3410    .4   ADDS   r4,r4,#0x10
    0x08000180:  42ac    .B   CMP   r4,r5
    0x08000182:  d3f6    ..   BCC   0x8000172 ; __scatterload + 6
    0x08000184:  f7ffffd8  ....  BL    __main_after_scatterload ; 0x8000138

⑤调用main函数

从上面的汇编代码可以看出,scatterload 函数最后调用了 main_after_scatterload 函数,这个函数实现的功能就是跳转到了用户的 main 函数,进入到用户程序。__main_after_scatterload 函数的代码如下:

__main_after_scatterload
  _main_clock
  _main_cpp_init
  _main_init
    0x08000138:  4800    .H   LDR   r0,[pc,#0] ; [0x800013c] = 0x80004c1
    0x0800013a:  4700    .G   BX    r0

可以看出,代码最后跳转到了 r0 寄存器所指示的地址中去了,实际上这个地址就是用户的 main 函数地址。由此完成 STM32 的启动过程,接下来运行的是用户程序的代码。


文章作者: ShiQuLiZhi
版权声明: 本博客所有文章除特别声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 ShiQuLiZhi !
评论
  目录