向韦东山学:裸板_SPI_FLASH 三部曲

go spi系列:裸板_SPI_FLASH 三部曲

作者:titer1 
联系:1307316一九六八(仅接受短信) 
声明:本文采用以下协议进行授权: 自由转载-非商用-非衍生-保持署名|Creative Commons BY-NC-ND 3.0 ,转载请注明作者及出处。

本文所有代码版权归原作者所有 


读 设备id

Read Manufacturer / Device 0x90 
The Read Manufacturer/Device ID Instruction is a n alternative to the Release fro m Power-down / Device ID instruction that provides both th e JEDEC assigned manufacturer ID and the specific device ID.

The Read Manufacturer/Devlce ID instruction is ve ry similar to th e Release from Powe r-dow n / Device IDinstruction. The instruction IS initiated by driving the /CS pin low and shifting the instruction code 90h ” followed by a 24-blt address (A23 · AO ) of 000000h .

After which, the Manufacturer ID for Winbond (EFh) and the Device ID are shifted out on the falling edge Of CLK with most significant bit (MSB) first as shown 
In Figure below .

The Device ID values for the W25Q16DV is listed in Manufacturer and Device Identification 
table. The Manufacturer and Device IDs can be read continuously, alternating from one to the other. The instruction IS completed by driving /CS high

  • 取设备id时 ,针对这里winbond的address,传送的是0. 
    最简单的实现是:
void SPIFlashReadID(int *pMID, int *pDID) {
    SPIFlash_Set_CS(0); /* 选中SPI FLASH */

SPISendByte(0x90);

SPISendByte(0);
SPISendByte(0);
 SPISendByte(0);

*pMID = SPIRecvByte();
*pDID = SPIRecvByte();

SPIFlash_Set_CS(1);
}

不过为了更有扩展性:

static void SPIFlashSendAddr(unsigned int addr) {
    SPISendByte(addr >> 16);
    SPISendByte(addr >> 8);
    SPISendByte(addr & 0xff);
}

void SPIFlashReadID(int *pMID, int *pDID) {
    SPIFlash_Set_CS(0); /* 选中SPI FLASH */

  SPISendByte(0x90);

  SPIFlashSendAddr(0);

  *pMID = SPIRecvByte();
  *pDID = SPIRecvByte();

  SPIFlash_Set_CS(1);
}

成员函数实现

片选引脚设置实现

原理就是 flash连到gpg pin2

spi 读函数的实现

看手册 理清时序 


//pin:gpg5 master in
static char SPI_Get_DI(void) {
    if (GPGDAT & (1<<5))
        return 1;
    else 
        return 0;
}


unsigned char SPIRecvByte(void) {
    int i;
    unsigned char val = 0;
    for (i = 0; i < 8; i++)
    {
        val <<= 1;
        SPI_Set_CLK(0);
        if (SPI_Get_DI())
            val |= 1;
        SPI_Set_CLK(1);
    }
    return val;    
}

测试函数@main

摘取片段

SPIFlashReadID(&mid, &pid);
printf("SPI Flash : MID = 0x%02x, PID = 0x%02x\n\r", mid, pid);

sprintf(str, "SPI : %02x, %02x", mid, pid);
OLEDPrint(2,0,str);

小节:如果读id操作顺利,说明读写spi的逻辑都是通过的

实现spi 读 写 三大步

guideline

  • 去除写保护 : 写使能 写状态寄存器
  • 擦除
  • 编写

环节1 之使能

禁用 
 
使能 

实现如下:

static void SPIFlashWriteEnable(int enable) {
    if (enable)
    {
        SPIFlash_Set_CS(0);
        SPISendByte(0x06);
        SPIFlash_Set_CS(1);
    }
    else
    {
        SPIFlash_Set_CS(0);
        SPISendByte(0x04);
        SPIFlash_Set_CS(1);
    }
}

环节1之 状态寄存器

以上是状态寄存器的内容 
分别使用命令字 0x5 / 0x35来读取 1/2状态寄存器的值。

读状态

static unsigned char SPIFlashReadStatusReg1(void) {
    unsigned char val;
    SPIFlash_Set_CS(0);
    SPISendByte(0x05);
    val = SPIRecvByte();
    SPIFlash_Set_CS(1);
    return val;
}

static unsigned char SPIFlashReadStatusReg2(void) {
...
    SPISendByte(0x35);
...
    return val;
}

写状态

简明说到就是 命令字1+状态寄存器1值+状态寄存器2值

static void SPIFlashWaitWhenBusy(void) {
    while (SPIFlashReadStatusReg1() & 1);//bit0 表征busy 。。将会加载write封装命令的后面
}

static void SPIFlashWriteStatusReg(unsigned char reg1, unsigned char reg2) {    
    SPIFlashWriteEnable(1);  

    SPIFlash_Set_CS(0);
    SPISendByte(0x01);
    SPISendByte(reg1);
    SPISendByte(reg2);
    SPIFlash_Set_CS(1);

    SPIFlashWaitWhenBusy();
}

应用 清理状态寄存器保护

看懂状态寄存器的图示就是基础。

应用 清理状态数据寄存器保护

看懂状态寄存器的图示就是基础。 

本质就是将图是的红框中都设置为0

static void SPIFlashClearProtectForData(void) {
    /* cmp=0,bp2,1,0=0b000 */
    unsigned char reg1, reg2;

    reg1 = SPIFlashReadStatusReg1();
    reg2 = SPIFlashReadStatusReg2();

    reg1 &= ~(7<<2);//关键:我的理解:bit 3 /2/1 都为1,bit0不管这里,
    reg2 &= ~(1<<6); 

    SPIFlashWriteStatusReg(reg1, reg2);
}

以上就是三部曲之一:使能+状态寄存器清除

三部曲之二 :擦除

擦除大小是4k为单位,关键就是找到命令字 
更新:我认为这里传入地址是起始地址。(any suggestion,please tell me)

/* erase 4K */
void SPIFlashEraseSector(unsigned int addr) {
    SPIFlashWriteEnable(1);  

    SPIFlash_Set_CS(0);
    SPISendByte(0x20);
    SPIFlashSendAddr(addr);
    SPIFlash_Set_CS(1);

    SPIFlashWaitWhenBusy();
}

三部曲之三:编写

原理如图:

  • 这里命令字0x02h
  • 看起来写得数据大小可大于256byte。从文档看起来超过256字节,buffer将会回绕。
/* program */
void SPIFlashProgram(unsigned int addr, unsigned char *buf, int len) {
    int i;

    SPIFlashWriteEnable(1);  

    SPIFlash_Set_CS(0);
    SPISendByte(0x02);
    SPIFlashSendAddr(addr);

    for (i = 0; i < len; i++)
        SPISendByte(buf[i]);

    SPIFlash_Set_CS(1);

    SPIFlashWaitWhenBusy();

}

最终集成

去除写保护

void SPIFlashInit(void) {
    SPIFlashClearProtectForStatusReg();
    SPIFlashClearProtectForData();
}

测试函数

SPIFlashInit();//各宗使能,状态寄存器护理
SPIFlashEraseSector(4096);
SPIFlashProgram(4096, "100ask", 7);
SPIFlashRead(4096, str, 7);
printf("SPI Flash read from 4096: %s\n\r", str);
OLEDPrint(4,0,str);

做个标记,至此,视频播放在41:00 
所有的代码在代码附件中“03th_spi_i2c_adc_jz2440_flash”可以找到, 
下一步目标是控制可调电压,然后让他的值在oled上显示,有兴趣的同学可在上述文件夹找到解决方案。

小节

完整讲解 spi + flash的操作。 
第一阶段就是认识 三部曲。


你可能感兴趣的:(向韦东山学:裸板_SPI_FLASH 三部曲)