直接上代码。
我再总结几点:
1.HAL库自带的发送函数对NSS高低电平的管理不太好用,不管用软件还是硬件管理,都需要自己管理NSS引脚的电平。
2.发送写或擦除命令前,都需要使能后才可以生效。
3.擦除操作后需要等待,我测试写入数据不需要,马上读数据就有了。1个扇区需要等待两万一千多个循环周期。擦除数据,后面的地址不是开始,只要是指定扇区的地址即可。20H + 0~4095都是擦除第一个扇区。
4.读取数据无太多限制,想从哪个地址,随便读多少个长度都可以。
5.写入数据就要注意了,页写入从指定地址开始写,如果长度超出当前页了,HAL库没有帮你跳转下一页,会继续从当前页头开始写,反复循环直到写完指定长度的数据。
所以我们写入的时候就要注意地址和页对齐。
欢迎交流,有疑问的也可以问我。
uint32_t waitCout = 0;
// 处理发送指令
static void FLASH_Send_CMD(uint8_t code,uint8_t*sendBuf,uint16_t sendSize,uint8_t*recvBuf,uint16_t recvSize)
{
// 擦除或写入指令处理前要先使能
if(code == CMD_SectorErase || code == CMD_PageProgram){
FLASH_Send_CMD(CMD_WriteEnable,sendBuf,1,recvBuf,0);
}
uint16_t i = 0;
*sendBuf = code;
# if 1
printf("发送指令 : ");
for(i=0; i<sendSize; i++) {
printf("%02X ",sendBuf[i]);
}
printf("---end\n");
# endif
SPI_FLASH_CS_LOW;
HAL_SPI_Transmit(&hspi1,sendBuf,sendSize,1000);
if(recvSize>0){
HAL_SPI_Receive(&hspi1,recvBuf,recvSize,1000);
if(code == CMD_ReadStatusReg){
// printf("状态等待值为:%02XH\n",*recvBuf);
while((*recvBuf)&0x01){
waitCout++;
HAL_SPI_Receive(&hspi1,recvBuf,1,1000);
// printf("状态等待值为:%02XH\n",*recvBuf);
}
printf("等待次数:%d\n",waitCout);
}
# if 1
printf("接收到数据 : ");
for(i=0; i<recvSize; i++) {
printf("%02X ",recvBuf[i]);
}
printf("---end\n");
# endif
}
SPI_FLASH_CS_HIGH;
// 擦除或写入指令处理后要等待忙状态变空闲
// if(code == CMD_SectorErase || code == CMD_PageProgram){ // 读寄存器状态,主要是看是否在处理数据,也就是BUSY位
if(code == CMD_SectorErase){ // 测试多次,写入数据未出现BUSY不需要等待
FLASH_Send_CMD(CMD_ReadStatusReg,sendBuf,1,recvBuf,1);
}
}
void FLASH_SendRecv(uint8_t code,uint8_t*sendBuf,uint16_t sendSize,uint8_t*recvBuf,uint16_t recvSize)
{
// printf("指令:%02X 长度:%d\n",code,sendSize);
uint16_t i = 0,j = 0;
uint8_t dummySize = 0;
uint8_t addrSize = 0;
if(code == CMD_Power_DeviceID) { // 电源唤醒和读取DeviceID功能
dummySize = 3;
sendSize = 1 + dummySize;
recvSize = 1;
} else if(code == CMD_JEDEC_ID) { // 读出数据与0XEF4017匹配,看看是否是成功启动Flash
sendSize = 1 + dummySize;
recvSize = 3;
} else if(code == CMD_SectorErase) { // 擦除1个扇区
addrSize = 3;
sendSize = 1 + addrSize;
} else if(code == CMD_PageProgram) { // 按页(256Byte)来写数据,超出的会循环覆盖
addrSize = 3;
sendSize += (1 + addrSize);
} else if(code == CMD_ReadData) { // 读指定地址开始指定长度的数据 读数据无长度限制
addrSize = 3;
sendSize = 1 + addrSize;
} else{
printf("未整理指令%02XH\n",code);
return;
}
// sendBuf数据整理
for(i = 0; i<dummySize; i++) {
sendBuf[1+i] = CMD_Dummy;
}
// 如果地址不对齐或长度超出256Byte,写入到当前页最后,不会换页,会从当前页头接着写,多数据循环进行
uint32_t dataSize = sendSize - 4; // 获取要写入的数据长度
uint32_t addr = (sendBuf[1]<<16) + (sendBuf[2]<<8) + sendBuf[3];
if((code == CMD_PageProgram) && (dataSize > SPI_FLASH_PageSize || dataSize + addr > SPI_FLASH_PageSize)){
uint8_t firstSize = SPI_FLASH_PageSize - addr%SPI_FLASH_PageSize; // 发出的第一段长度
printf("firstSize:%d\n",firstSize);
if(firstSize > 0){
// 先把第一段多出的部分发出去
FLASH_Send_CMD(CMD_PageProgram,sendBuf,firstSize+4,recvBuf,0);
}
// 剩下的如果不是正好满页,多出的部分要再发一次,剩下的地址对齐
addr += firstSize;
uint16_t cycle = (dataSize-firstSize)/SPI_FLASH_PageSize; // 剩下分成几段发送
if((dataSize-firstSize)%SPI_FLASH_PageSize != 0){
cycle++;
}
uint8_t pageWrite[SPI_FLASH_PageSize+4]; // 剩下的数据存储到pageWrite数据位一起发出去
for(i=0;i<cycle;i++){
for(j=0;(j<SPI_FLASH_PageSize+4) && (j+i*SPI_FLASH_PageSize < sendSize - firstSize);j++){
if(j == 0){
pageWrite[j] = sendBuf[j];
}else if(j == 1){
pageWrite[j] = addr>>16&0xff;
}else if(j == 2){
pageWrite[j] = addr>>8&0xff;
}else if(j == 3){
pageWrite[j] = addr&0xff;
}else if(j >= 4){
pageWrite[j] = sendBuf[j+i*SPI_FLASH_PageSize+firstSize];
}
}
addr += SPI_FLASH_PageSize;
FLASH_Send_CMD(CMD_PageProgram,pageWrite,j,recvBuf,0);
}
}else{
// 一般发送指令 一次
FLASH_Send_CMD(code,sendBuf,sendSize,recvBuf,recvSize);
}
}
|