单片机的存储器结构是其硬件架构中的重要组成部分,它直接决定了单片机能执行哪些任务以及如何处理数据。通常,单片机内部包含两种主要类型的存储器:Flash(程序存储空间)和RAM(数据存储空间)。这两种存储器各有其特性与用途,共同支撑着单片机的功能实现。
Flash 存储器
Flash存储器是一种非易失性存储技术,意味着即使在断电情况下,它也能保持所存储的数据不变。这种特性使得Flash非常适合用来存放单片机的固件——即运行于单片机上的程序代码。当单片机上电启动时,它会从Flash中加载并开始执行预存的程序。由于Flash具有可擦写的能力,因此可以通过编程接口更新或修改其中的内容,以实现对单片机软件的升级维护。
Flash 写入保护机制
为了防止意外写入或删除Flash中的内容,大多数单片机都提供了一定形式的写入保护措施。例如,一些单片机可能需要特定的序列或命令来解锁Flash区域进行写操作;而在其他情况下,则可能是通过配置特殊的寄存器位来启用或禁用写保护功能。
示例代码 - 解锁STM32的Flash编程
```c
#include "stm32f1xx_hal.h"
void unlock_flash(void) {
// 解除写保护
HAL_FLASH_Unlock();
// 清除所有Flash错误标志
__HAL_FLASH_CLEAR_FLAG(FLASH_FLAG_EOP | FLASH_FLAG_OPERR |
FLASH_FLAG_WRPERR | FLASH_FLAG_PGAERR |
FLASH_FLAG_PGPERR | FLASH_FLAG_PGSERR);
}
```
RAM 存储器
不同于Flash,RAM是一种易失性存储器,用于保存正在运行的应用程序所需的临时数据。每当单片机执行一个函数调用、创建变量或者缓存某些计算结果时,都会使用到RAM资源。因为RAM的速度远快于Flash,所以它是处理实时数据的理想选择。然而,一旦电源被切断,RAM中的信息就会丢失,这也是为什么我们需要将关键数据保存至非易失性的存储介质如Flash的原因之一。
动态内存分配
对于某些复杂的应用场景,可能会涉及到动态分配内存的需求。C语言提供了标准库函数`malloc()`和`free()`用于申请和释放堆区中的内存块。需要注意的是,在嵌入式系统中过度依赖动态内存分配可能导致碎片化问题,进而影响系统的稳定性和性能。
示例代码 - 使用malloc()分配数组
```c
#include
int* create_array(int size) {
int* array = (int*)malloc(size * sizeof(int));
if (array == NULL) {
// 内存分配失败处理
return NULL;
}
// 初始化数组...
for (int i = 0; i < size; ++i) {
array[i] = i;
}
return array;
}
// 记得在适当时候调用free(array)释放内存
```
数据存储与读取
在实际应用中,我们经常需要将数据从RAM转移到Flash以便长期保存,或者反过来从Flash加载数据到RAM供即时处理。这一过程涉及到了单片机内部的数据总线及相应的指令集架构。对于简单的数据项,可以直接利用MOV类指令完成传输;而对于更复杂的结构体或大块连续数据,则可以借助DMA(直接存储器访问)控制器来提高效率,减少CPU负担。
数据存储示例 - 将整数写入Flash
```c
#include "stm32f1xx_hal.h"
#define ADDR_FLASH_PAGE_128 ((uint32_t)0x0800F800) /* Base @ of Page 128 */
void write_to_flash(uint32_t address, uint32_t data) {
HAL_StatusTypeDef status;
// 锁定Flash编程
HAL_FLASH_Lock();
// 解锁Flash编程
HAL_FLASH_Unlock();
// 清除所有Flash错误标志
__HAL_FLASH_CLEAR_FLAG(FLASH_FLAG_EOP | FLASH_FLAG_OPERR |
FLASH_FLAG_WRPERR | FLASH_FLAG_PGAERR |
FLASH_FLAG_PGPERR | FLASH_FLAG_PGSERR);
// 单字编程
status = HAL_FLASH_Program(FLASH_TYPEPROGRAM_WORD, address, data);
// 检查是否成功
if (status != HAL_OK) {
// 处理错误...
}
// 锁定Flash编程
HAL_FLASH_Lock();
}
```
数据读取示例 - 从Flash读取整数
```c
uint32_t read_from_flash(uint32_t address) {
uint32_t data;
// 直接从Flash地址读取数据
data = *(__IO uint32_t*)address;
return data;
}
```
系统启动过程中的存储器初始化
当单片机上电后,它首先会执行复位向量指向的启动代码。这段代码负责初始化各种硬件资源,包括但不限于设置系统时钟、配置外设引脚以及最重要的——初始化存储器。特别是对于带有外部扩展存储器接口的单片机来说,正确的初始化步骤能够确保后续应用程序能够正确无误地访问这些额外的存储空间。
启动文件配置 - 设置栈顶指针
```assembly
.global _estack
_estack:
.word 0x20005000 /* 假设这是SRAM的顶部地址 */