发新帖我要提问
12
返回列表
打印
[STM32F2]

基于STM32与NOR FLASH的SPI通信

[复制链接]
楼主: yutingwei
手机看帖
扫描二维码
随时随地手机跟帖
21
yutingwei|  楼主 | 2024-3-31 21:30 | 只看该作者 |只看大图 回帖奖励 |倒序浏览
这个函数是将用户要写的数据进行以扇区为单位写入,一个扇区为4096字节大小,以扇区为单位写,函数为:
void SPI_FLASH_Write_Sector(uint8_t* Buf, uint32_t WriteAddr, uint16_t WriteCnt)
{
    //uint32_t PageNum;
    uint16_t PageOffset = WriteAddr % 256;      //目标地址在单页的偏移
    uint16_t PageRemainder = 256 - PageOffset;  //单页剩余的空间

    if (WriteCnt <= PageRemainder)
        PageRemainder = WriteCnt;

    while (1)
    {
        //按照页的区域来写,其内部实现会再以字节为单位去写
        SPI_FLASH_Write_Page(Buf, WriteAddr, PageRemainder);

        if (PageRemainder == WriteCnt)
            break;
        else
        {
            WriteCnt -= PageRemainder;

            if (WriteCnt > 256)
                PageRemainder = 256;
            else
                PageRemainder = WriteCnt;
        }   
    }
}

使用特权

评论回复
22
yutingwei|  楼主 | 2024-3-31 21:31 | 只看该作者
  该函数调用到以页为单位写的函数SPI_FLASH_Write_Page(),一页的空间大小为256字节。
//按页写
void SPI_FLASH_Write_Page(uint8_t* Buf, uint32_t WriteAddr, uint16_t WriteCnt)
{
    int i;

    SPI_FLASH_WriteEnable();
    FLASH_SPI_CS_LOW();
    SPI_FLASH_RecvSendByte(W25X_PageProgram);

    SPI_FLASH_RecvSendByte(WriteAddr >> 16);
    SPI_FLASH_RecvSendByte(WriteAddr >> 8);
    SPI_FLASH_RecvSendByte(WriteAddr);

    for (i = 0; i < WriteCnt; i++)
    {
        SPI_FLASH_RecvSendByte(Buf[i]);
    }
    FLASH_SPI_CS_HIGH();
    SPI_FLASH_Wait_Busy();
}

使用特权

评论回复
23
yutingwei|  楼主 | 2024-3-31 21:31 | 只看该作者
 以页为单位的写,会调用以字节为单位的写,即前面的SPI_FLASH_RecvSendByte()函数。

使用特权

评论回复
24
yutingwei|  楼主 | 2024-3-31 21:31 | 只看该作者
使能FLASH写
  上面往FLASH写数据,在调用SPI_FLASH_RecvSendByte()写之前需要调用SPI_FLASH_WriteEnable()使能FLASH的写使能,这样才可以写入数据:
void SPI_FLASH_WriteEnable(void)
{
    FLASH_SPI_CS_LOW();

    SPI_FLASH_RecvSendByte(W25X_WriteEnable);   

    FLASH_SPI_CS_HIGH();
}

使用特权

评论回复
25
yutingwei|  楼主 | 2024-3-31 21:31 | 只看该作者
等待写操作完成
  写FLASH时一个操作过程,FLASH提供操作命令W25X_ReadStatusReg供给用户判断是否写完成。在等待期写完成后用户才去进行对FLASH的其他操作。
//等待写操作执行完毕
void SPI_FLASH_WaitForWriteEnd(void)
{
    uint8_t ret;

    FLASH_SPI_CS_LOW();

    SPI_FLASH_RecvSendByte(W25X_ReadStatusReg);

    do
    {
        ret = SPI_FLASH_RecvSendByte(NOTUSEDAT);
    }while (ret == RESET);

    FLASH_SPI_CS_HIGH();
}

使用特权

评论回复
26
yutingwei|  楼主 | 2024-3-31 21:31 | 只看该作者
擦除FLASH
  读/写FLASH的函数实现完毕,看擦除FLASH的实现。注意,擦除FLASH只能按扇区擦除,该函数的参数为指定哪一块扇区。W25Q64容量为8M,分为128块,每块有16个扇区,每个扇区4096字节。所以参数Addr不能大于2048。
//擦除整个扇区
void SPI_FLASH_Erase_Sector(uint32_t Addr)
{
    Addr *= 4096;
    SPI_FLASH_WriteEnable();
    SPI_FLASH_Wait_Busy();

    FLASH_SPI_CS_LOW();
    SPI_FLASH_RecvSendByte(W25X_SectorErase);
    SPI_FLASH_RecvSendByte(Addr >> 16);
    SPI_FLASH_RecvSendByte(Addr >> 8);
    SPI_FLASH_RecvSendByte(Addr);
    FLASH_SPI_CS_HIGH();

    SPI_FLASH_Wait_Busy();
}

使用特权

评论回复
27
yutingwei|  楼主 | 2024-3-31 21:32 | 只看该作者
main()函数
uint8_t TxBuf[] = "HelloWorld";

#define BUFSZ (sizeof(TxBuf) / sizeof(uint8_t))

uint8_t RxBuf[BUFSZ];

int main(void)
{
    LED_GPIO_Config();

    USART1_Config();
    SPI_FLASH_Init();

    printf("SPI测试实践: \r\n\r\n");

    if (SPI_FLASH_ReadID() == FLASHID)
    {
        printf("找到FLASH设备,型号为W25X64\r\n\r\n");
        LED_Flicker();

        //printf("擦出扇区中...\r\n\r\n");
        //SPI_FLASH_Erase_Sector(0x00);
        //printf("擦除完毕!\r\n\r\n");

        printf("写入的数据为: %s \r\n\r\n", TxBuf);

        SPI_FLASH_Write(TxBuf, 0x00000, BUFSZ);

        SPI_FLASH_Read(RxBuf, 0x00000, BUFSZ);
        printf("读出的数据为: %s \r\n\r\n", TxBuf);
    }
    else
    {
        printf("找不到FLASH设备\r\n\r\n");
    }

    while (1);
    return 0;
}

使用特权

评论回复
28
yutingwei|  楼主 | 2024-3-31 21:32 | 只看该作者
编译运行:

使用特权

评论回复
29
中国龙芯CDX| | 2024-3-31 21:46 | 只看该作者
往FLASH写数据,有3种写范围,写一整个扇区、写一整页、写一个字节

使用特权

评论回复
30
610u| | 2024-7-30 23:25 | 只看该作者
nSS引脚不采用硬件SPI专用的nSS引脚

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则