要将SPI闪存与新唐N9H20微控制器的非操作系统BSP(Board Support Package)配合使用,需直接配置硬件寄存器和实现底层驱动。以下是详细步骤:
1. 硬件连接确认
- 确保SPI闪存(如W25Q128)正确连接至N9H20的SPI引脚:
- SCLK:时钟线
- MOSI:主机输出从机输入
- MISO:主机输入从机输出
- CS:片选信号(通过GPIO控制)
- 参考N9H20数据手册确认SPI控制器编号(如SPI0、SPI1)。
2. 配置BSP中的SPI控制器
(1) 初始化SPI外设
在BSP中启用SPI时钟,配置GPIO复用模式:
#include "N9H20.h" // BSP头文件
void SPI_Init(void) {
// 启用SPI模块时钟(假设使用SPI0)
outp32(REG_AHBCLK, inp32(REG_AHBCLK) | SPI0_CKE);
outp32(REG_APBCLK, inp32(REG_APBCLK) | SPI0_CKE);
// 配置GPIO为SPI功能
GPIO_SetMode(PA, BIT12, GPIO_MODE_AF5); // MOSI (PA12)
GPIO_SetMode(PA, BIT13, GPIO_MODE_AF5); // MISO (PA13)
GPIO_SetMode(PA, BIT14, GPIO_MODE_AF5); // SCLK (PA14)
// 配置CS为GPIO输出
GPIO_SetMode(PB, BIT0, GPIO_MODE_OUTPUT); // CS (PB0)
GPIO_SetOutput(PB, BIT0, 1); // 初始状态取消选中
}
(2) 设置SPI参数
void SPI_Config(void) {
// 配置SPI控制寄存器 (REG_SPI_CTL0)
outp32(REG_SPI_CTL0,
SPI_MASTER | // 主模式
SPI_ENABLE | // 使能SPI
SPI_CPHA_HIGH | // CPHA=1
SPI_CPOL_HIGH | // CPOL=1
SPI_TX_NEG); // 数据在下降沿采样
// 设置时钟分频 (66MHz主频 / 16 = 4.125MHz)
outp32(REG_SPI_CLKDIV, 0x10);
}
3. 实现SPI闪存驱动
(1) 基本读写函数
// 发送/接收单字节
uint8_t SPI_TransferByte(uint8_t data) {
outp32(REG_SPI_TX0, data); // 写入发送寄存器
while (!(inp32(REG_SPI_STATUS) & SPI_TX_READY)); // 等待发送完成
return (uint8_t)inp32(REG_SPI_RX0); // 读取接收数据
}
// 写入数据(带片选控制)
void SPI_Write(uint8_t *data, uint32_t len) {
GPIO_ClrOutput(PB, BIT0); // 选中闪存 (CS=0)
for (uint32_t i = 0; i < len; i++) {
SPI_TransferByte(data[i]);
}
GPIO_SetOutput(PB, BIT0); // 取消选中 (CS=1)
}
// 读取数据
void SPI_Read(uint8_t *buf, uint32_t len) {
GPIO_ClrOutput(PB, BIT0);
for (uint32_t i = 0; i < len; i++) {
buf[i] = SPI_TransferByte(0xFF); // 发送dummy字节读取数据
}
GPIO_SetOutput(PB, BIT0);
}
(2) 实现闪存指令
// 发送写使能指令 (0x06)
void Flash_WriteEnable(void) {
uint8_t cmd = 0x06;
SPI_Write(&cmd, 1);
}
// 读取状态寄存器 (0x05)
uint8_t Flash_ReadStatus(void) {
uint8_t cmd = 0x05;
uint8_t status;
GPIO_ClrOutput(PB, BIT0);
SPI_TransferByte(cmd);
status = SPI_TransferByte(0xFF);
GPIO_SetOutput(PB, BIT0);
return status;
}
// 页编程 (0x02)
void Flash_PageProgram(uint32_t addr, uint8_t *data, uint32_t len) {
uint8_t cmd[4] = {
0x02, // 指令
(uint8_t)(addr >> 16), // 地址高8位
(uint8_t)(addr >> 8), // 地址中8位
(uint8_t)(addr) // 地址低8位
};
Flash_WriteEnable(); // 写使能
SPI_Write(cmd, 4); // 发送指令+地址
SPI_Write(data, len); // 写入数据
while (Flash_ReadStatus() & 0x01); // 等待写入完成 (BUSY=1)
}
// 数据读取 (0x03)
void Flash_ReadData(uint32_t addr, uint8_t *buf, uint32_t len) {
uint8_t cmd[4] = {0x03, (uint8_t)(addr >> 16), (uint8_t)(addr >> 8), (uint8_t)addr};
GPIO_ClrOutput(PB, BIT0);
SPI_Write(cmd, 4); // 发送指令+地址
SPI_Read(buf, len); // 读取数据
GPIO_SetOutput(PB, BIT0);
}
4. 在应用中使用闪存
int main() {
SPI_Init(); // 初始化SPI
SPI_Config(); // 配置SPI参数
uint8_t data[256] = "Hello, N9H20 SPI Flash!";
uint8_t rdata[256] = {0};
// 写入数据至地址0x000000
Flash_PageProgram(0x000000, data, sizeof(data));
// 从地址0x000000读取数据
Flash_ReadData(0x000000, rdata, sizeof(rdata));
while(1);
}
关键注意事项
- 时钟配置:确保SPI时钟频率在闪存支持的范围内(通常≤50MHz)。
- 忙状态检查:写/擦除操作后需轮询状态寄存器(BUSY位)。
- 指令序列:严格遵循闪存数据手册中的指令顺序(如写操作前需
WRITE_ENABLE)。
- 跨页处理:页编程时避免跨页(如W25Q的页大小为256字节)。
- 擦除操作:根据需要实现扇区擦除(0x20)或块擦除(0xD8)。
通过以上步骤,即可在N9H20的非操作系统环境下直接操作SPI闪存。建议结合具体闪存型号的数据手册调整指令细节和时序要求。