微控制器的 C 语言启动代码是什么?何时/为何/如何修改?
What is a startup code in C for Microcontrollers ? When / Why / How it should be modified?
这是关于启动代码的一般性问题。我知道它就像引导加载程序或 运行 在重置或加电后调用主函数的第一件事。
但我想知道它的主要/核心功能。
例如(在 google 中搜索),
目标系统重置后立即执行启动代码。 Keil 启动
代码按顺序执行(可选)以下操作:
Clears internal data memory
Clears external data memory
Clears paged external data memory
Initializes the small model reentrant stack and pointer
Initializes the large model reentrant stack and pointer
Initializes the compact model reentrant stack and pointer
Initializes the 8051 hardware stack pointer
Transfers control to code that initializes global variables or to the main C function if
there are no initialized global variables
注意:启动代码总是用汇编语言编写,因为它依赖于 CPU 目标。
感谢您的宝贵时间
所有计算机程序都假设它们执行的世界是按照它们的期望设置的。
假设您有一个程序 P,它假定变量 X 的值在 P 启动时为零。
如果您将该程序 P 作为代码启动点放入您的微处理器中,它将无法运行....因为 X 的值不能保证为零。 (RAM 内存位置往往会包含垃圾。)
您通过插入使假设成立的启动代码来解决该问题,例如,您在启动时放置代码以将变量 X 归零,然后将控制权传递给您的程序。无论您的程序需要什么假设,您都需要在启动代码中实现。
现在我写了一个非常笼统的描述。当我们谈论真正的微处理器时,通常有一些必须满足的低级假设:
- 如果机器有一个堆栈指针,它指向一些实际内存而不是存放垃圾
- 需要任何 I/O 端口或特殊硬件 "ready to use"(它们通常需要配置;考虑内存库 select 寄存器或优先级中断控制器)
- 您的程序位置已知
如果需要这三个假设,启动代码将通过以下方式解决它们:
- 使用已知为 RAM 地址的常量加载 SP 寄存器
- 执行特殊指令或将各种魔术常量写入必要的I/O端口以配置它们以供使用
- 将 PC 设置为程序的已知位置(通常是 JMP xxxx 指令)
每个 MCU 控制器程序对世界都有不同的假设。您自定义编码您的启动代码以满足这些假设。
一般来说,这些假设并不多,许多 MCU 程序都可以通过一个小的、精心选择的集合来实现。因此,启动指令往往与程序分开处理(否则,您可以简单地将它们添加到您的程序中,有时人们会这样做)。
您提到启动代码调用 main,因此我假设您是在谈论 C/C++ 应用程序的启动代码。启动代码负责为应用程序设置 C 运行-time 环境。其中包括:
- 将初始化变量的初始值从 ROM 复制到 RAM
- 为未初始化的变量清零 RAM
- 设置堆栈指针
- 对于 C++ 应用程序,它为任何全局或静态对象实例调用构造函数
- 最后,调用main
启动代码也可能会进行一些硬件初始化,但这是特定于硬件的,不一定是必需的。例如,如果硬件使用 PLL 来提高时钟频率,则启动代码可能首先设置 PLL,以便其余启动代码全速执行。如果开发板在 address/data 总线上有外部设备,则处理器的外部内存控制器通常在启动代码中配置。或者,如果硬件有看门狗,则启动代码可能会禁用它,这样它就不会在应用程序有机会配置它之前重置。
我通常不会在启动代码中执行特定于应用程序的硬件初始化。相反,我将在调用 main 之后在我的应用程序中初始化 GPIO、定时器和串行端口。
编译器工具链通常为其支持的硬件环境提供启动代码。此示例启动代码可能适用于大多数应用程序,您可能永远不必修改它。但是,如果您的硬件或 运行-time 环境有一些特殊之处,那么您可能需要自定义启动代码。当我不得不自定义启动代码时,我总是采用示例启动代码并对其进行修改以满足我的需要。我不记得曾经从头开始编写启动代码。
这是关于启动代码的一般性问题。我知道它就像引导加载程序或 运行 在重置或加电后调用主函数的第一件事。
但我想知道它的主要/核心功能。
例如(在 google 中搜索),
目标系统重置后立即执行启动代码。 Keil 启动 代码按顺序执行(可选)以下操作:
Clears internal data memory
Clears external data memory
Clears paged external data memory
Initializes the small model reentrant stack and pointer
Initializes the large model reentrant stack and pointer
Initializes the compact model reentrant stack and pointer
Initializes the 8051 hardware stack pointer
Transfers control to code that initializes global variables or to the main C function if
there are no initialized global variables
注意:启动代码总是用汇编语言编写,因为它依赖于 CPU 目标。
感谢您的宝贵时间
所有计算机程序都假设它们执行的世界是按照它们的期望设置的。
假设您有一个程序 P,它假定变量 X 的值在 P 启动时为零。
如果您将该程序 P 作为代码启动点放入您的微处理器中,它将无法运行....因为 X 的值不能保证为零。 (RAM 内存位置往往会包含垃圾。)
您通过插入使假设成立的启动代码来解决该问题,例如,您在启动时放置代码以将变量 X 归零,然后将控制权传递给您的程序。无论您的程序需要什么假设,您都需要在启动代码中实现。
现在我写了一个非常笼统的描述。当我们谈论真正的微处理器时,通常有一些必须满足的低级假设:
- 如果机器有一个堆栈指针,它指向一些实际内存而不是存放垃圾
- 需要任何 I/O 端口或特殊硬件 "ready to use"(它们通常需要配置;考虑内存库 select 寄存器或优先级中断控制器)
- 您的程序位置已知
如果需要这三个假设,启动代码将通过以下方式解决它们:
- 使用已知为 RAM 地址的常量加载 SP 寄存器
- 执行特殊指令或将各种魔术常量写入必要的I/O端口以配置它们以供使用
- 将 PC 设置为程序的已知位置(通常是 JMP xxxx 指令)
每个 MCU 控制器程序对世界都有不同的假设。您自定义编码您的启动代码以满足这些假设。
一般来说,这些假设并不多,许多 MCU 程序都可以通过一个小的、精心选择的集合来实现。因此,启动指令往往与程序分开处理(否则,您可以简单地将它们添加到您的程序中,有时人们会这样做)。
您提到启动代码调用 main,因此我假设您是在谈论 C/C++ 应用程序的启动代码。启动代码负责为应用程序设置 C 运行-time 环境。其中包括:
- 将初始化变量的初始值从 ROM 复制到 RAM
- 为未初始化的变量清零 RAM
- 设置堆栈指针
- 对于 C++ 应用程序,它为任何全局或静态对象实例调用构造函数
- 最后,调用main
启动代码也可能会进行一些硬件初始化,但这是特定于硬件的,不一定是必需的。例如,如果硬件使用 PLL 来提高时钟频率,则启动代码可能首先设置 PLL,以便其余启动代码全速执行。如果开发板在 address/data 总线上有外部设备,则处理器的外部内存控制器通常在启动代码中配置。或者,如果硬件有看门狗,则启动代码可能会禁用它,这样它就不会在应用程序有机会配置它之前重置。
我通常不会在启动代码中执行特定于应用程序的硬件初始化。相反,我将在调用 main 之后在我的应用程序中初始化 GPIO、定时器和串行端口。
编译器工具链通常为其支持的硬件环境提供启动代码。此示例启动代码可能适用于大多数应用程序,您可能永远不必修改它。但是,如果您的硬件或 运行-time 环境有一些特殊之处,那么您可能需要自定义启动代码。当我不得不自定义启动代码时,我总是采用示例启动代码并对其进行修改以满足我的需要。我不记得曾经从头开始编写启动代码。