S32K1xx 系列 MCU 启动过程

lichongyang
lichongyang
lichongyang
111
文章
0
评论
2020年4月26日13:03:21 评论 579 次浏览

1. S32K1xx 系列 MCU 启动过程详解(startup_S32K144.S)

由于 S32K1xx 系列 MCU 都是使用的 ARM Cortex M 系列内核,其 SRAM 和 Flash 使用同样的 IP,它们的启动过程都是相同的,只是 SRAM 和 Flash 的 memory 大小不同。所以这里仅以 S32K144 为例进行介绍。

在 S32DS for ARM v2018.R1 IDE 中新建的 S32K144 应用工程如下,在其工程浏览器(Project Explorer)窗口中 Project_SettingsStartup_Code 目录下的存放着整个工程的启动文件--startup_S32K144.S:

每次 MCU 复位后,从其复位向量--Reset_Handler开始运行到用户程序的 main()函数之前, CPU 启动过程还做了如下准备工作:

  • 关闭 CPU 全局中断
    通过汇编指令“cpsid i”,关闭 CPU 全局中断的目的是避免启动过程中中断的影响;因为此时中断向量表还未建立好,无法响应外设中断;
  • 清零 CPU 内核寄存器 R1~R12
    每次复位后, CPU 内核寄存器的值是随机不确定的,所以需要将其清零;
  • 初始化 SRAM 的 ECC
       由于 S32K1xx 系列 MCU 除 4KB FlexRAM 外的 SRAM 带有 ECC 功能,所以必须在使用之前对其进入任意写操作以产生正确的 ECC 结果,从而完成 ECC 初始化;
  • 初始化堆栈
        ARM Cortex M 系列 CPU 内核有 MSP 和 PSP 两个 32-bit 的堆栈,由于中断和异常处理时使用 MSP 所以必须在发生中断/异常之前将其初始化,其初始化值来自默认向量表的 0 地址偏移,即 0x0000 地址存放的 4 个字节;
  • 系统初始化
        在完成了以上堆栈初始化之后, CPU 就可以运行 C 代码了,所以此时通过调用定义在工程 SDKàplatformàdeviceàS32K144àstartup 目录下的 system_S32K144.c 中的系统初始化函数--SystemInit():
    根据工程配置完成:①CPU 内核 FPU 配置和使能(如果创建应用工程时选择浮点数运算使用硬件 FPU)、 ②关闭看门狗(默认配置)和③使能 CPU 内核指令缓冲(I-Cache)等 MCU 硬件平台配置:
  • RAM 初始化
        接下来,启动文件会调用定义在 SDKàplatformàdevices 目录下 startup.c 中的
    init_data_bss()函数完成应用工程运行所需的 RAM 初始化:
    在 startup.c 中通过申明外部变量(extern)的方式,可以引用定义在工程链接文件中的__DATA_ROM、__DATA_RAM、__DATA_END、__CODE_RAM、__CODE_ROM、__CODE_END、__BSS_START 和__BSS_END 符号,获得工程链接结果中.data 段(有初始化值)、 .bss 段(未初始化和初始化值为 0)的全局变量以及重定向到 RAM 中运行的.code 段代码/函数在 Flash 和RAM 中的起始地址和长度(结束地址-开始地址):然后再通过数据指针的方式实现全局变量初始化值和重映射代码从 Flash 到 RAM 中的拷贝以及.bss 段的清零:
  • 打开 CPU 全局中断
        在完成 RAM 初始化和中断向量表初始化后,就可以打开 CPU 全局中断,响应外设中断了;
    打开 ARM Cortex M 系列 CPU 内核的全局中断通过汇编语句--“cpsie i”完成
  • 跳转到应用程序 main()函数
    在完成以上准备工作之后,启动过程的最后一步是跳转到应用程序 main()函数;

重映射代码/函数到 RAM 中运行的方法和步骤

1.在 S32K1xx 系列 MCU 的启动过程,会自动将定义在.code 段中的代码/函数从其 Flash 储存地址拷贝到 RAM 中的运行时地址。

2.只有将用户代码分配到 Flash 中的编译目标,即使用 S32K1xx_xx_flash.ld 链接文件的编译目标才存在代码重映射。若是将应用工程编译结果代码分配到 RAM 的编译目标(使用 S32K1xx_xx_ram.ld 链接文件),其编译的函数/代码本身就是储存在 RAM 中的,所以无需重映射。

3.将 想 要 重 映 射 的 代 码 / 函 数 通 过__attribute__((section(".code_ram")))指定到.code_ram 段

只需要在 C 代码中,将想要重映射的代码/函数通过__attribute__ ((section(".code_ram")))指定到.code_ram 段即可。

4.在应用工程编译结果 map 文件中查看重映射结果

完成以上指定后,重新编译应用工程即可在应用工程的编译结果 map文件中看到 main()函数已经被成功重定向了,其长度为 0x20 字节,在 Flash 中的存储地址(也称作加载地址--load address)为 0x000005bc,而在 RAM 中的重映射运行时地址为 0x1fff8404:

重定 MCU 的 UART 模块为标准输入输出设备

通用异步收发传输器(Universal Asynchronous Receiver/Transmitter),简称 UART,是一种低成本的异步全双工通信,其通过 PL 2303、 CH341 等廉价的 USB 转串口芯片即可与 PC 的 COM 互联通信,在 PC 上使用串口调试助手即可查看和控制(收发)UART 的通信数据,在嵌入式系统开发中广泛使用。
   NXP 的 32-bit Qorivva MPC57xx 和 S32R 系列汽车级 MCU 使用的LINFlexD/eSCI 模块和 S32K1xx 系列使用的 LPUART 都可以通过简单的配置支持 UART 通信。

在完成以上准备工作之后,在应用工程中只需要如下简单步骤即可使用 printf()函数了:
首先,在需要调用 printf 的 C 文件中包含标准输入输出库头文件--stdio.h
#include "stdio.h"
然后,就可以调用 printf 函数,格式化打印输出调试信息了。

使用 printf()打印调试信息的优缺点

1. 利用 C 语言标准库(比如 EWL_C 库)的 printf()函数,可以实现嵌入式应用程序调试信息/数据的多种格式转换,轻松实现调试信息的格式化输出,但需要占用额外的 Flash 存储空间,因此,不建议在小尺寸 Flash 的 MCU 应用工程中使printf();
 2. 使用调试接口(比如 JTAG 和 ARM Cortex M 系列 MCU 的 SWD 接口)的半主机通信,不占用 MCU 外设通信接口资源,但由于复用了正常的调试接口信号线,会占用调试器通信带宽,所以会降低调试速度,进而影响调试效率,建议使用能够支持较高调试速度的调试器(比如 Multilink FX,调试速度 Multilink FX > Multilink > OSBDM/OpenSDA);另外,此种方法, 脱机不能运行,所以量产时必须去掉应用程序中的所有 printf()函数或者将其定义为空函数;若将其重映射到串口或者其他外设,则会占用相应的硬件资源,造成浪费,但好处是与调试接口独立,无需连接调试器和打开调试软件,使用串口调试助手等作为上位机软件即可,通信速度快且稳定。

  • 我的微信
  • 这是我的微信扫一扫
  • weinxin
  • 我的微信小程序
  • 我的微信小程序扫一扫
  • weinxin
lichongyang
  • 本文由 发表于 2020年4月26日13:03:21
  • 转载请务必保留本文链接:https://www.chongyangli.com/archives/s32k1xx-%e7%b3%bb%e5%88%97-mcu-%e5%90%af%e5%8a%a8%e8%bf%87%e7%a8%8b/
S32 SDK 的外设驱动组件 NXP-S32K144

S32 SDK 的外设驱动组件

在 S32 SDK 中每一个外设驱动组件都有一个组件的全局状态结构体,以<组件/外设名>_state_t 命名,用于存放该外设/组件的工作状态和硬件资源使用信息,主要包含以下信息: ① 外设模块...
匿名

发表评论

匿名网友 填写信息

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen: