WP引脚使用方式
spi flash有一个外部写保护引脚WP, 此引脚并不是直接保护flash上的数据内容,而是保护状态寄存器不被异常改写.flash上的内容写保护是通过状态寄存器的BPxbit的不同组合来实现的. 对于环境相对恶劣的使用场景(供电不稳等.)需要对flash上的内容加以保护.整体的设计思路就是 使用状态寄存器的BPx位组合实现flash上数据内容的局部或整体写保护. 外部WP引脚的电平状态结合状态寄存器的SRP位实现状态寄存器的保护进而间接保护flash上受保护的数据内容.
通过BPx位组合实现的flash写保护,其实是把flash受保护区域置为了只读状态. 这种情况下除非改变flash状态寄存器的BPx位,解除写保护,否则无法擦写受保护区域.
针对相对重要或者是基本不会变动的数据,比如字库等,就可以放置在flash的受保护区域,作为只读数据存在.确保恶劣条件下数据的稳定性.
关键寄存器描述
这里仅贴出一些关键的寄存器,更详细的描述还需要参考数据手册.
状态寄存器写保护测试
通过状态寄存器的写保护位配合外部WP引脚状态,达到状态寄存器的写保护效果.
#define WP_HIGH() GPIO_Set_Output_Data_Bits(GPIOC_SFR,GPIO_PIN_MASK_0,Bit_SET)
#define WP_LOW() GPIO_Set_Output_Data_Bits(GPIOC_SFR,GPIO_PIN_MASK_0,Bit_RESET)
#define SPI_FLASH_RDSR 0x05 /* read status register */
#define SPI_FLASH_WRSR 0x01 /* write status register */
static uint8_t flash_readstatus(uint8_t addr)
{
uint8_t datain, i;
SPI_FLASH_ENABLE();
SpiWrite(&addr, 1);
datain = SpiReadByte();
SPI_FLASH_DISABLE();
return datain;
}
static void flash_writestatus(uint8_t addr, uint8_t status)
{
uint8_t dataout[2] = {0};
SpiSimpleCmd(SPI_FLASH_WREN);
dataout[0] = addr;
dataout[1] = status;
SPI_FLASH_ENABLE();
SpiWrite(dataout, 2);
SPI_FLASH_DISABLE();
while(1){
if ((flash_readstatus(SPI_FLASH_RDSR) & 0x01) == 0) // 等待flash操作完成.
break;
delay_ms(5);
}
}
uint8_t flash_statustest(void)
{
uint8_t get_data = 0;
/*
写入0x80, 此时拉低WP引脚,即状态寄存器处于hardware protedted模式.状态寄存器不可写入.
拉高WP引脚, 状态寄存器处于hardware unprotected,状态寄存器可写.
*/
get_data = flash_readstatus(SPI_FLASH_RDSR);
uart_printf("status = 0x%x\r\n", get_data);
flash_writestatus(SPI_FLASH_WRSR, 0x80);
get_data = flash_readstatus(SPI_FLASH_RDSR);
uart_printf("status = 0x%x\r\n", get_data);
WP_LOW(); // 拉低时, 写入无效.
flash_writestatus(SPI_FLASH_WRSR, 0x8C);
get_data = flash_readstatus(SPI_FLASH_RDSR);
uart_printf("status = 0x%x\r\n", get_data);
WP_HIGH(); // 拉高时, 写入有效.
flash_writestatus(SPI_FLASH_WRSR, 0x8C);
get_data = flash_readstatus(SPI_FLASH_RDSR);
uart_printf("status = 0x%x\r\n\r\n", get_data);
}
//打印结果
status = 0x8c
status = 0x80
status = 0x80 // 拉低后写入0x8C无效 状态寄存器仍为0x80.
status = 0x8c // 拉高后写入0x8C生效
flash数据内容写保护测试
通过BPx位组合实现的flash写保护,其实是把flash受保护区域置为了只读状态. 这种情况下除非改变flash状态寄存器的BPx位,解除写保护,否则无法擦写受保护区域.
uint8_t flash_datatest(void)
{
uint8_t get_data = 0;
uint8_t write_data[3] = {0x01,0x02,0x03};
uint8_t read_data[3] = {0};
/*
1.所有数据区域都不设置保护, 读写flash测试. 正常擦写.
2.所有数据区域都设置保护(flash数据区处于只读状态), 读写flash测试. 擦写无效.
*/
WP_HIGH(); // 拉高时, 写入有效.
flash_writestatus(SPI_FLASH_WRSR, 0x80);
get_data = flash_readstatus(SPI_FLASH_RDSR);
get_data = flash_readstatus(SPI_FLASH_RDSR);
uart_printf("status = 0x%x\r\n", get_data); // 输出0x80
/*
状态寄存器的BPx位组合不保护任何flash数据区域时, 无论外部WP引脚拉高还是拉低,
都数据都能正常写入.
*/
WP_LOW();
flash_sector_erase(0);
flash_write(0, write_data, sizeof (write_data));
flash_read(0, read_data, sizeof (read_data));
uart_printf("get_data = 0x%x 0x%x 0x%x\r\n", read_data[0],read_data[1],read_data[2]);
WP_HIGH();
write_data[0] = 0x04;
write_data[1] = 0x05;
write_data[2] = 0x06;
flash_sector_erase(0);
flash_write(0, write_data, sizeof (write_data));
flash_read(0, read_data, sizeof (read_data));
uart_printf("get_data = 0x%x 0x%x 0x%x\r\n", read_data[0],read_data[1],read_data[2]);
WP_HIGH();
flash_writestatus(SPI_FLASH_WRSR, 0x9C); // 设置写保护所有扇区
get_data = flash_readstatus(SPI_FLASH_RDSR);
get_data = flash_readstatus(SPI_FLASH_RDSR);
uart_printf("status = 0x%x\r\n", get_data); // 输出0x9C
/*
flash状态寄存器对所有的flash数据内容设置了写保护.此时无论外部引脚拉高还是拉低.
都无法对flash数据内容进行擦写. 必须解除写保护,才能正常擦写.
*/
WP_HIGH();
write_data[0] = 0x07;
write_data[1] = 0x07;
write_data[2] = 0x07;
flash_sector_erase(0);
flash_write(0, write_data, sizeof (write_data));
flash_read(0, read_data, sizeof (read_data));
uart_printf("get_data = 0x%x 0x%x 0x%x\r\n", read_data[0],read_data[1],read_data[2]);
WP_LOW();
write_data[0] = 0x07;
write_data[1] = 0x07;
write_data[2] = 0x07;
flash_sector_erase(0);
flash_write(0, write_data, sizeof (write_data));
flash_read(0, read_data, sizeof (read_data));
uart_printf("get_data = 0x%x 0x%x 0x%x\r\n", read_data[0],read_data[1],read_data[2]);
/*
设置状态寄存器保护低8K数据.
*/
WP_HIGH();
flash_writestatus(SPI_FLASH_WRSR, 0xE8); // 设置写保护低8K数据
get_data = flash_readstatus(SPI_FLASH_RDSR);
get_data = flash_readstatus(SPI_FLASH_RDSR);
uart_printf("status = 0x%x\r\n", get_data); // 输出0xE8
/*
设置了保护低8K数据, 即低8K为只读.无法擦写.
*/
write_data[0] = 0x07;
write_data[1] = 0x07;
write_data[2] = 0x07;
flash_sector_erase(0);
flash_write(0, write_data, sizeof (write_data));
flash_read(0, read_data, sizeof (read_data));
uart_printf("get_data = 0x%x 0x%x 0x%x\r\n", read_data[0],read_data[1],read_data[2]);
/*
高于8K地址的数据不受保护,可正常擦写.
*/
flash_sector_erase(0x1000*2);
flash_write(0x1000*2, write_data, sizeof (write_data));
flash_read(0x1000*2, read_data, sizeof (read_data));
uart_printf("get_data = 0x%x 0x%x 0x%x\r\n", read_data[0],read_data[1],read_data[2]);
// 打印结果.
status = 0x80 // 未保护数据区域 flash正常擦写
get_data = 0x1 0x2 0x3
get_data = 0x4 0x5 0x6
status = 0x9c // 保护全部区域 flash无法擦写 保持之前的值
get_data = 0x4 0x5 0x6
get_data = 0x4 0x5 0x6
status = 0xe8 // 保护低8K 0地址保持之前的值 8K之后正常擦写.
get_data = 0x4 0x5 0x6
get_data = 0x7 0x7 0x7
}
总结
本文对spi flash 的WP引脚做了相对深入的分析.结合数据手册给了其基本用法,读者如需更深入的用法, 还需结合数据手册进行深入分析.