LPC1100 系列_8.闪存与存储

8. 闪存与存储

在嵌入式系统设计中,闪存和存储管理是至关重要的部分。NXP LPC1100 系列单片机基于 ARM Cortex-M0 核心,提供了丰富的闪存和存储资源。本节将详细介绍 LPC1100 系列的闪存与存储管理,包括闪存的结构、存储器映射、编程与擦除操作、存储器保护机制以及使用外部存储器的扩展方法。
在这里插入图片描述

8.1 闪存结构

LPC1100 系列单片机的闪存结构设计用于存储程序代码和常量数据。闪存的大小根据具体的型号而有所不同,但通常在 32KB 到 256KB 之间。闪存被组织成多个扇区,每个扇区的大小和数量也因型号而异。例如,LPC1114 型号的闪存被分为 16 个 4KB 的扇区。

8.1.1 闪存扇区

闪存扇区是闪存的基本编程单位。每个扇区可以独立进行擦除和编程操作。在 LPC1100 系列中,闪存扇区的擦除和编程操作通常需要特定的时序和命令序列。扇区擦除操作会将整个扇区的数据清零,而编程操作则将数据写入指定的闪存地址。

8.1.2 闪存编程与擦除

闪存的编程和擦除操作需要遵循特定的步骤。以下是一个简单的示例,展示如何在 LPC1100 系列单片机上进行闪存编程和擦除:

#include "LPC11xx.h"

// 定义闪存起始地址
#define FLASH_START_ADDR 0x00000000
// 定义闪存扇区大小
#define FLASH_SECTOR_SIZE 512

// 定义要编程的数据
uint32_t data_to_program[] = {0x12345678, 0x9ABCDEF0};

// 闪存编程函数
void flash_program(uint32_t *addr, uint32_t *data, uint32_t len) {
    // 检查地址是否对齐
    if ((uint32_t)addr % 4 != 0) {
        // 地址未对齐
        return;
    }

    // 检查数据长度是否为 4 的倍数
    if (len % 4 != 0) {
        // 数据长度未对齐
        return;
    }

    // 启用闪存编程模式
    LPC_FLASH->FLMCR = 1;

    // 编程数据
    for (uint32_t i = 0; i < len; i += 4) {
        *addr = *data;
        addr++;
        data++;
    }

    // 禁用闪存编程模式
    LPC_FLASH->FLMCR = 0;
}

// 闪存扇区擦除函数
void flash_erase_sector(uint32_t sector_addr) {
    // 检查地址是否对齐
    if (sector_addr % FLASH_SECTOR_SIZE != 0) {
        // 地址未对齐
        return;
    }

    // 启用闪存编程模式
    LPC_FLASH->FLMCR = 1;

    // 擦除扇区
    LPC_FLASH->FLAR = (1 << 24) | (sector_addr & 0x000FFFFF);

    // 等待擦除完成
    while (LPC_FLASH->FLSR & (1 << 0));

    // 禁用闪存编程模式
    LPC_FLASH->FLMCR = 0;
}

int main() {
    // 擦除闪存第 0 扇区
    flash_erase_sector(FLASH_START_ADDR);

    // 编程闪存第 0 扇区
    flash_program((uint32_t *)FLASH_START_ADDR, data_to_program, sizeof(data_to_program));

    // 读取编程后的数据
    uint32_t read_data[2];
    for (uint32_t i = 0; i < 2; i++) {
        read_data[i] = ((uint32_t *)FLASH_START_ADDR)[i];
    }

    // 检查编程是否成功
    if (read_data[0] == data_to_program[0] && read_data[1] == data_to_program[1]) {
        // 编程成功
    } else {
        // 编程失败
    }

    while (1) {
        // 主循环
    }
}

8.1.3 闪存保护

为了防止意外写入或擦除闪存,LPC1100 系列提供了闪存保护机制。可以通过配置闪存保护寄存器(FLPWP)来启用或禁用保护。以下是一个示例,展示如何配置闪存保护:

#include "LPC11xx.h"

// 启用闪存保护
void enable_flash_protection() {
    // 启用闪存编程模式
    LPC_FLASH->FLMCR = 1;

    // 启用保护
    LPC_FLASH->FLPWP = 1;

    // 禁用闪存编程模式
    LPC_FLASH->FLMCR = 0;
}

// 禁用闪存保护
void disable_flash_protection() {
    // 启用闪存编程模式
    LPC_FLASH->FLMCR = 1;

    // 禁用保护
    LPC_FLASH->FLPWP = 0;

    // 禁用闪存编程模式
    LPC_FLASH->FLMCR = 0;
}

int main() {
    // 禁用闪存保护
    disable_flash_protection();

    // 擦除闪存第 0 扇区
    flash_erase_sector(FLASH_START_ADDR);

    // 编程闪存第 0 扇区
    flash_program((uint32_t *)FLASH_START_ADDR, data_to_program, sizeof(data_to_program));

    // 启用闪存保护
    enable_flash_protection();

    while (1) {
        // 主循环
    }
}

8.2 存储器映射

LPC1100 系列单片机的存储器映射包括闪存、SRAM、外设寄存器等。以下是一个典型的存储器映射表,展示了各个区域的地址范围:

存储器区域地址范围大小
闪存0x00000000 - 0x0003FFFF256KB
SRAM0x10000000 - 0x10007FFF32KB
IO 外设0x40000000 - 0x5FFFFFFF256MB

8.2.1 SRAM

SRAM 是单片机的内部随机存取存储器,用于存储运行时的数据。LPC1100 系列的 SRAM 大小通常为 8KB 到 32KB,具体取决于型号。SRAM 的地址范围从 0x10000000 开始。

8.2.2 外部存储器扩展

LPC1100 系列单片机可以通过外部存储器接口(如 SPI、I2C 或 UART)扩展存储器。以下是一个使用 SPI 扩展外部闪存的示例:

#include "LPC11xx.h"
#include "spi.h"

// 定义外部闪存的 SPI 配置
#define SPI_MOSI 0
#define SPI_MISO 1
#define SPI_SCK  2
#define SPI_CS   3

// 初始化 SPI 接口
void spi_init() {
    // 配置 SPI 引脚
    LPC_SCU->SFSPINSEL[SPI_MOSI] = (LPC_SCU->SFSPINSEL[SPI_MOSI] & 0xFFFF0FFF) | 0x0000A000;
    LPC_SCU->SFSPINSEL[SPI_MISO] = (LPC_SCU->SFSPINSEL[SPI_MISO] & 0xFFFF0FFF) | 0x0000A000;
    LPC_SCU->SFSPINSEL[SPI_SCK]  = (LPC_SCU->SFSPINSEL[SPI_SCK]  & 0xFFFF0FFF) | 0x0000A000;
    LPC_SCU->SFSPINSEL[SPI_CS]   = (LPC_SCU->SFSPINSEL[SPI_CS]   & 0xFFFF0FFF) | 0x0000A000;

    // 初始化 SPI 控制器
    LPC_SPI->CR = 0x00000002; // 使能 SPI
    LPC_SPI->DIV = 0x00000004; // 设置时钟分频
    LPC_SPI->TCR = 0x00000001; // 设置数据传输模式
}

// 读取外部闪存数据
uint8_t spi_read(uint8_t addr) {
    // 选择外部闪存
    LPC_GPIO1->FIOCLR = (1 << SPI_CS);

    // 发送读取命令
    uint8_t command = 0x03;
    LPC_SPI->DR = command;

    // 等待传输完成
    while (!(LPC_SPI->SR & (1 << 5)));

    // 发送地址
    LPC_SPI->DR = addr;
    while (!(LPC_SPI->SR & (1 << 5)));

    // 读取数据
    uint8_t data = LPC_SPI->DR;
    while (!(LPC_SPI->SR & (1 << 5)));

    // 取消选择外部闪存
    LPC_GPIO1->FIOSET = (1 << SPI_CS);

    return data;
}

// 写入外部闪存数据
void spi_write(uint8_t addr, uint8_t data) {
    // 选择外部闪存
    LPC_GPIO1->FIOCLR = (1 << SPI_CS);

    // 发送写入命令
    uint8_t command = 0x02;
    LPC_SPI->DR = command;

    // 等待传输完成
    while (!(LPC_SPI->SR & (1 << 5)));

    // 发送地址
    LPC_SPI->DR = addr;
    while (!(LPC_SPI->SR & (1 << 5)));

    // 写入数据
    LPC_SPI->DR = data;
    while (!(LPC_SPI->SR & (1 << 5)));

    // 取消选择外部闪存
    LPC_GPIO1->FIOSET = (1 << SPI_CS);
}

int main() {
    // 初始化 SPI 接口
    spi_init();

    // 写入数据到外部闪存
    spi_write(0x00, 0x55);

    // 读取外部闪存数据
    uint8_t read_data = spi_read(0x00);

    while (1) {
        // 主循环
    }
}

8.3 存储器保护机制

LPC1100 系列单片机提供了多种存储器保护机制,以确保系统的安全性和可靠性。这些保护机制包括内存保护单元(MPU)、闪存保护寄存器(FLPWP)和外部存储器保护寄存器(EMC)。

8.3.1 内存保护单元(MPU)

内存保护单元(MPU)可以配置为保护特定的内存区域,防止非法访问。以下是一个配置 MPU 的示例:

#include "LPC11xx.h"

// 配置 MPU 保护区域
void configure_mpu(uint32_t base_addr, uint32_t size, uint32_t access_perms) {
    // 启用 MPU
    LPC_MPU->CTRL = 0x00000001;

    // 配置 MPU 区域
    LPC_MPU->RNR = 0; // 区域编号
    LPC_MPU->RBAR = (base_addr & 0xFFFF8000) | 0x00000001; // 基地址
    LPC_MPU->RASR = (size & 0x00000FFE) | access_perms; // 区域大小和访问权限

    // 使能 MPU
    LPC_MPU->CTRL = 0x00000001;
}

int main() {
    // 配置 MPU 保护 SRAM 区域,只允许读写访问
    configure_mpu(0x10000000, 0x00008000, 0x00000003);

    while (1) {
        // 主循环
    }
}

8.3.2 外部存储器保护寄存器(EMC)

外部存储器保护寄存器(EMC)可以用于保护外部存储器的访问。以下是一个配置 EMC 的示例:

#include "LPC11xx.h"

// 配置外部存储器保护
void configure_emc_protection(uint32_t base_addr, uint32_t size, uint32_t access_perms) {
    // 配置 EMC 保护区域
    LPC_EMC->PROTECTION[0] = (base_addr & 0xFFFF0000) | (size & 0x000000FF) | (access_perms & 0x00000003);

    // 使能 EMC 保护
    LPC_EMC->PROTECTION[1] = 1;
}

int main() {
    // 配置 EMC 保护外部闪存区域,只允许读写访问
    configure_emc_protection(0x40000000, 0x00008000, 0x00000003);

    while (1) {
        // 主循环
    }
}

8.4 存储器管理最佳实践

在嵌入式系统中,合理管理存储器资源是至关重要的。以下是一些存储器管理的最佳实践:

8.4.1 闪存使用注意事项

  1. 扇区擦除:每次编程前必须先擦除相应的扇区。
  2. 数据对齐:确保编程和读取的数据地址和长度对齐。
  3. 编程顺序:遵循正确的编程时序和命令序列。
  4. 保护机制:启用闪存保护以防止意外写入或擦除。

8.4.2 SRAM 使用注意事项

  1. 初始化:在使用 SRAM 之前,确保其已正确初始化。
  2. 数据缓存:合理使用数据缓存,提高系统性能。
  3. 堆栈管理:确保堆栈大小和位置合理,避免堆栈溢出。

8.4.3 外部存储器使用注意事项

  1. 接口配置:正确配置外部存储器接口(如 SPI、I2C、UART)。
  2. 时序要求:遵循外部存储器的时序要求,确保数据传输的可靠性。
  3. 电源管理:确保外部存储器的电源管理,避免因电源问题导致数据丢失。

8.5 存储器故障诊断

在嵌入式系统开发过程中,存储器故障是常见的问题。以下是一些常用的存储器故障诊断方法:

8.5.1 闪存故障诊断

  1. 读写验证:在编程后读取数据,验证是否正确写入。
  2. 保护检查:检查闪存保护寄存器的配置,确保保护机制正常。
  3. 擦除检查:在擦除后读取数据,确保扇区已清零。

8.5.2 SRAM 故障诊断

  1. 初始化检查:确保 SRAM 已正确初始化。
  2. 数据一致性:检查 SRAM 中的数据一致性,确保没有意外更改。
  3. 堆栈检查:检查堆栈使用情况,确保没有堆栈溢出。

8.5.3 外部存储器故障诊断

  1. 通信检查:检查与外部存储器的通信是否正常。
  2. 数据校验:使用校验和或 CRC 校验外部存储器的数据完整性。
  3. 电源检查:检查外部存储器的电源是否稳定,确保正常工作。

8.6 存储器性能优化

为了提高嵌入式系统的性能,合理优化存储器的使用是必要的。以下是一些存储器性能优化的方法:

8.6.1 闪存性能优化

  1. 批量擦除:尽可能批量擦除扇区,减少擦除次数。
  2. 预编程:在系统启动时预编程常用数据,减少运行时的编程操作。
  3. 代码优化:优化程序代码,减少闪存的访问次数。

8.6.2 SRAM 性能优化

  1. 数据缓存:合理使用数据缓存,减少 SRAM 的访问延迟。
  2. 堆栈管理:优化堆栈管理,避免频繁的堆栈操作。
  3. 内存分配:合理分配内存,避免内存碎片。

8.6.3 外部存储器性能优化

  1. 批量传输:尽可能批量传输数据,减少通信次数。
  2. 数据压缩:使用数据压缩技术,减少外部存储器的存储需求。
  3. 接口选择:选择合适的外部存储器接口,如 SPI、I2C 或 UART,以满足性能需求。

8.7 存储器应用示例

以下是一些存储器应用的示例,展示如何在实际项目中使用闪存和外部存储器。

8.7.1 闪存日志记录

在嵌入式系统中,日志记录是一个常见的功能。闪存可以用于存储日志数据,以便在系统重启或故障时进行调试。以下是一个使用闪存记录日志的示例:

#include "LPC11xx.h"

// 定义日志存储区域的起始地址和大小
#define LOG_START_ADDR 0x0003F000
#define LOG_SIZE 1024

// 定义日志条目结构
typedef struct {
    uint32_t timestamp;
    char message[64];
} LogEntry;

// 日志条目计数器
uint32_t log_counter = 0;

// 闪存编程函数
void flash_program(uint32_t *addr, uint32_t *data, uint32_t len) {
    // 检查地址是否对齐
    if ((uint32_t)addr % 4 != 0) {
        // 地址未对齐
        return;
    }

    // 检查数据长度是否为 4 的倍数
    if (len % 4 != 0) {
        // 数据长度未对齐
        return;
    }

    // 启用闪存编程模式
    LPC_FLASH->FLMCR = 1;

    // 编程数据
    for (uint32_t i = 0; i < len; i += 4) {
        *addr = *data;
        addr++;
        data++;
    }

    // 禁用闪存编程模式
    LPC_FLASH->FLMCR = 0;
}

// 闪存扇区擦除函数
void flash_erase_sector(uint32_t sector_addr) {
    // 检查地址是否对齐
    if (sector_addr % 1024 != 0) {
        // 地址未对齐
        return;
    }

    // 启用闪存编程模式
    LPC_FLASH->FLMCR = 1;

    // 擦除扇区
    LPC_FLASH->FLAR = (1 << 24) | (sector_addr & 0x000FFFFF);

    // 等待擦除完成
    while (LPC_FLASH->FLSR & (1 << 0));

    // 禁用闪存编程模式
    LPC_FLASH->FLMCR = 0;
}

// 记录日志到闪存
void log_to_flash(LogEntry *log_entry) {
    // 计算日志存储的地址
    uint32_t log_addr = LOG_START_ADDR + log_counter * sizeof(LogEntry);

    // 检查是否需要擦除扇区
    if (log_addr % 1024 == 0) {
        flash_erase_sector(log_addr);
    }

    // 编程日志数据
    flash_program((uint32_t *)log_addr, (uint32_t *)log_entry, sizeof(LogEntry));

    // 增加日志计数器
    log_counter++;
}

int main() {
    // 初始化日志条目
    LogEntry log_entry = {
        .timestamp = 123456789,
        .message = "System started"
    };

    // 记录日志
    log_to_flash(&log_entry);

    while (1) {
        // 主循环
    }
}

8.7.2 外部存储器数据存储

外部存储器可以用于存储大量数据,例如传感器数据、历史记录等。以下是一个使用 SPI 扩展外部闪存存储数据的示例:

#include "LPC11xx.h"
#include "spi.h"

// 定义外部闪存的 SPI 配置
#define SPI_MOSI 0
#define SPI_MISO 1
#define SPI_SCK  2
#define SPI_CS   3

// 初始化 SPI 接口
void spi_init() {
    // 配置 SPI 引脚
    LPC_SCU->SFSPINSEL[SPI_MOSI] = (LPC_SCU->SFSPINSEL[SPI_MOSI] & 0xFFFF0FFF) | 0x0000A000;
    LPC_SCU->SFSPINSEL[SPI_MISO] = (LPC_SCU->SFSPINSEL[SPI_MISO] & 0xFFFF0FFF) | 0x0000A000;
    LPC_SCU->SFSPINSEL[SPI_SCK]  = (LPC_SCU->SFSPINSEL[SPI_SCK]  & 0xFFFF0FFF) | 0x0000A000;
    LPC_SCU->SFSPINSEL[SPI_CS]   = (LPC_SCU->SFSPINSEL[SPI_CS]   & 0xFFFF0FFF) | 0x0000A000;

    // 初始化 SPI 控制器
    LPC_SPI->CR = 0x00000002; // 使能 SPI
    LPC_SPI->DIV = 0x00000004; // 设置时钟分频
    LPC_SPI->TCR = 0x00000001; // 设置数据传输模式
}

// 读取外部闪存数据
uint8_t spi_read(uint8_t addr) {
    // 选择外部闪存
    LPC_GPIO1->FIOCLR = (1 << SPI_CS);

    // 发送读取命令
    uint8_t command = 0x03;
    LPC_SPI->DR = command;

    // 等待传输完成
    while (!(LPC_SPI->SR & (1 << 5)));

    // 发送地址
    LPC_SPI->DR = addr;
    while (!(LPC_SPI->SR & (1 << 5)));

    // 读取数据
    uint8_t data = LPC_SPI->DR;
    while (!(LPC_SPI->SR & (1 << 5)));

    // 取消选择外部闪存
    LPC_GPIO1->FIOSET = (1 << SPI_CS);

    return data;
}

// 写入外部闪存数据
void spi_write(uint8_t addr, uint8_t data) {
    // 选择外部闪存
    LPC_GPIO1->FIOCLR = (1 << SPI_CS);

    // 发送写入命令
    uint8_t command = 0x02;
    LPC_SPI->DR = command;

    // 等待传输完成
    while (!(LPC_SPI->SR & (1 << 5)));

    // 发送地址
    LPC_SPI->DR = addr;
    while (!(LPC_SPI->SR & (1 << 5)));

    // 写入数据
    LPC_SPI->DR = data;
    while (!(LPC_SPI->SR & (1 << 5)));

    // 取消选择外部闪存
    LPC_GPIO1->FIOSET = (1 << SPI_CS);
}

// 记录传感器数据到外部存储器
void log_sensor_data(uint8_t addr, uint16_t data) {
    // 写入数据
    spi_write(addr, (uint8_t)(data >> 8));
    spi_write(addr + 1, (uint8_t)(data & 0xFF));
}

int main() {
    // 初始化 SPI 接口
    spi_init();

    // 记录传感器数据
    log_sensor_data(0x00, 0x1234);

    while (1) {
        // 主循环
    }
}

8.7.3 存储器应用总结

通过上述示例,我们可以看到闪存和外部存储器在嵌入式系统中的重要应用。闪存主要用于存储程序代码和关键数据,而外部存储器则可以扩展存储容量,存储大量数据。合理使用这些存储资源,可以提高系统的性能和可靠性。

8.8 存储器管理总结

在嵌入式系统设计中,闪存和存储管理是至关重要的部分。LPC1100 系列单片机提供了丰富的闪存和存储资源,通过合理的配置和使用,可以确保系统的高效运行和数据的安全。以下是一些关键点总结:

  1. 闪存结构:了解闪存的扇区结构和大小,合理规划存储空间。
  2. 闪存编程与擦除:遵循正确的时序和命令序列进行编程和擦除操作。
  3. 闪存保护:启用保护机制,防止意外写入或擦除。
  4. 存储器映射:了解存储器映射表,合理使用各个存储区域。
  5. 外部存储器扩展:通过外部接口扩展存储容量,满足数据存储需求。
  6. 存储器保护机制:使用 MPU 和 EMC 等保护机制,确保系统的安全性和可靠性。
  7. 存储器最佳实践:注意闪存、SRAM 和外部存储器的使用注意事项,优化存储器性能。
  8. 存储器故障诊断:掌握常用的存储器故障诊断方法,确保系统的正常运行。

通过这些方法和技巧,可以有效地管理和优化 LPC1100 系列单片机的存储资源,提高系统的整体性能和可靠性。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值