野火电子论坛

 找回密码
 注册

QQ登录

只需一步,快速开始

查看: 4529|回复: 1

HAL库关于SPI访问FLASH的学习心得,关键代码已贴出

[复制链接]
发表于 2023-3-8 16:10:46 | 显示全部楼层 |阅读模式
直接上代码。
我再总结几点:
    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);
        }
}


回复

使用道具 举报

发表于 2023-3-8 18:04:54 | 显示全部楼层
有价值的帖子,值得好好学习!
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 注册

本版积分规则

联系站长|手机版|野火电子官网|野火淘宝店铺|野火电子论坛 ( 粤ICP备14069197号 ) 大学生ARM嵌入式2群

GMT+8, 2024-11-23 08:26 , Processed in 0.026774 second(s), 23 queries , Gzip On.

Powered by Discuz! X3.4

Copyright © 2001-2021, Tencent Cloud.

快速回复 返回顶部 返回列表