推广 热搜: 控制  电池  传感器  电子  自动  步进电机  公司  电脑  LED  设备 

SPI总线,实现外部FLASH(W25Q128)读写

   日期:2017-01-02     浏览:333    评论:0    
核心提示:SPI简述:1:SPI是一种高速,全双工的,同步的通信总线;2:四线连接,MISO-主设备数据输入,从设备数据输出;MOSI-主设备数据输出,从设备数据输入;SCLK-时钟信号,由主设
SPI简述:

1:SPI是一种高速,全双工的,同步的通信总线;
2:四线连接,MISO-主设备数据输入,从设备数据输出;MOSI-主设备数据输出,从设备数据输入;SCLK-时钟信号,由主设备产生;CS-从设备片选信号,由主设备控制。
3:主机和从机各有一个串行移位寄存器,两个寄存器中的数据在同一时刻被交换,如果只进行写操作,主机只需要忽略接收到的字节;如果主机要读取从机中的字节,就必须发送一个空字节来引发从机的传输。
4:SPI可以设置输出串行时钟的极性和相位,时钟极性与串行同步时钟的空闲状态的电平有关,CPOL=0,串行同步时钟空闲状态为低电平,反之为高;CPHA可以设置选择两种不同的传输协议,CPHA=0,串行同步时钟的第一个跳边沿数据被采样,反之,在串行同步时钟的第二个跳边沿数据被采样;
5:为了防止MISO总线冲突,同一时间只允许一个从设备与主设备通讯;
6:SPI主机和从机的时钟极性和相位应该一致;
7:在数据传输(数据交换)的过程中,每次接收到的数据必须在下一次数据传输之前被采样.如果之前接收到的数据没有被读取,那么这些已经接收完成的数据将有可能会被丢弃,导致SPI物理模块最终失效。因此,在程序中一般都会在SPI传输完数据后,去读取SPI设备里的数据,即使这些数据(DummyData)在我们的程序里是无用的。


SPI总线网络

 

SPI工作机制:

1:概述
2:时序

SPI初始化步骤:

1:配置相关引脚复用为SPI,使能SPI时钟;
2:设置SPI工作模式,包括主机或者从机、数据格式(高位在前还是低位在前)、设置串行时钟的极性和相位(采样方式)、SPI时钟频率(SPI的传输速度);
3:使能SPI;


code

spi.c-SPI驱动

 #include "spi.h"  void spi_init(void) {     GPIO_InitTypeDef GPIO_InitStruct;                       //定义gpio结构体       SPI_InitTypeDef SPI_InitStruct;                         //SPI结构体初始化     RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);    //使能GPIOB口时钟     RCC_APB1PeriphClockCmd(    RCC_APB1Periph_SPI2,  ENABLE ); //SPI2时钟使能           //GPIO结构体初始化     GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF_PP;            //GPIO复用推挽     GPIO_InitStruct.GPIO_Pin = GPIO_Pin_12 | GPIO_Pin_13 | GPIO_Pin_14 | GPIO_Pin_15;  //SPI2_NSS复用 启用软件从设备管理 NSS引脚上的电平由SI位确定     GPIO_Init(GPIOA,&GPIO_InitStruct);           //SPI结构体初始化     SPI_InitStruct.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_2;      SPI_InitStruct.SPI_CPHA = SPI_CPHA_2Edge;     SPI_InitStruct.SPI_CPOL = SPI_CPOL_High;     SPI_InitStruct.SPI_CRCPolynomial = 7;     SPI_InitStruct.SPI_DataSize = SPI_DataSize_8b;     SPI_InitStruct.SPI_Direction = SPI_Direction_2Lines_FullDuplex;      SPI_InitStruct.SPI_FirstBit = SPI_FirstBit_MSB;     SPI_InitStruct.SPI_Mode = SPI_Mode_Master;     SPI_InitStruct.SPI_NSS = SPI_NSS_Soft;                             //启动软件从设备管理     SPI_Init(SPI2,&SPI_InitStruct);       SPI_Cmd(SPI2,ENABLE);                                               //spi2使能 }  //SPI发送一个字节  void SPI_Send_Byte(uint16_t data) {     uint8_t cnt = 0;     while(SPI_I2S_GetFlagStatus(SPI2,SPI_I2S_FLAG_TXE) == RESET)     {         if(++cnt > 200)             return;     }     SPI_I2S_SendData(SPI2,data); }  //SPI接受一个字节 unsigned char SPI_Receive_Byte(void) {     uint8_t cnt = 0;     while(SPI_I2S_GetFlagStatus(SPI2,SPI_I2S_FLAG_RXNE) == RESET)     {         if(++cnt > 200)             return 0;     }      return SPI_I2S_ReceiveData(SPI2); }

flash.c-w25q128驱动

#include "w25q128.h"  //指令定义 #define Write_Enable 0x06                  //写使能 #define Write_Enable_Volatile_SR 0x50      //易失性寄存器写使能 #define Write_Disable 0x04                 //写禁止 #define Read_SR1 0x05                      //读状态寄存器1 #define Read_SR2 0x35                      //读状态寄存器2   #define Write_Status_Register 0x01         //写状态寄存器 #define Read_Data 0x03                     //读数据                    #define Page_Program 0x02                  //页编程命令  #define Sector_Erase 0x20                  //扇区擦除  #define Block_Erase_32K 0x52               //32K块擦除 #define Block_Erase_64K 0xd8               //64K块擦除  #define Chip_Erase 0xc7                    //芯片擦除 0x60 #define Power_Down 0xB9                    //Power_Down模式 #define Release_Power_Down 0xab            //恢复power_down模式 #define Read_Chip_ID 0x90                  //读取设备ID  #define CS  PBout(12)  //初始化SPI FLASH的IO口 void W25Q128_Init(void) {    GPIO_InitTypeDef  GPIO_InitStructure;    RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB, ENABLE);//使能GPIOB时钟    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_12;   GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;   GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;   GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;   GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;   GPIO_Init(GPIOB, &GPIO_InitStructure);   GPIO_SetBits(GPIOB,GPIO_Pin_12);    CS=1;                                                      //SPI FLASH不选中   SPI1_Init();                                                   //初始化SPI }   //等待空闲 超时退出 void Wait_idle(void)  {     uint8_t cnt = 0;     while((Read_SR1()&0x01) != 0x00)     {         if(cnt++ > 200)   //200超时,退出循环             break;     } }  //写使能  06H void Write_Enable(void) {     CS = 0;                              //拉低时钟     Spi_Send_Receive_Data(Write_Enable); //发送06H     CS = 1;                              //拉高时钟 }  //易失性寄存器写使能  50H void Write_Enable_Volatile_SR(void) {     CS = 0;                                          //拉低时钟     Spi_Send_Receive_Data(Write_Enable_Volatile_SR); //发送50H     CS = 1;                                          //拉高时钟 }  //写禁止 void Write_Disable(void) {     CS = 0;                               //拉低时钟     Spi_Send_Receive_Data(Write_Disable); //发送50H     CS = 1;                               //拉高时钟 }   uint8_t Read_SR1(void) {     uint8_t dat = 0;     CS = 0;     Spi_Send_Receive_Data(Read_SR1);    //发送05H       dat = Spi_Send_Receive_Data(0xff);  //发送一个字节0xff,返回一个字节(寄存器的值)     CS = 1;     return dat; }   uint8_t Read_SR2(void) {     uint8_t dat = 0;     CS = 0;     Spi_Send_Receive_Data(Read_SR2);    //发送35H       dat = Spi_Send_Receive_Data(0xff);  //发送一个字节0xff,返回一个字节(寄存器的值)     CS = 1;     return dat; }   void Write_SR_No_Volatile(uint16_t dat) {     Write_Enable();                                  //写使能 06H     CS = 0;     Spi_Send_Receive_Data(Write_Status_Register);    //发送01H       Spi_Send_Receive_Data((uint8_t)(dat>>8));        //发送dat数据       Spi_Send_Receive_Data((uint8_t)dat);      CS = 1;     Wait_idle();                                     //等待空闲 }   void Write_SR_Volatile(uint16_t dat) {     CS = 0;     Write_Enable_Volatile_SR();                      //写使能 50H     Spi_Send_Receive_Data(Write_Status_Register);    //发送01H       Spi_Send_Receive_Data((uint8_t)(dat>>8));        //发送dat数据       Spi_Send_Receive_Data((uint8_t)dat);      CS = 1;     Wait_idle();                                     //等待空闲 }   void Read_Data(uint8_t *ptr,uint32_t address,uint8_t num) {     CS = 0;     Spi_Send_Receive_Data(Read_Data);                 //0x03指令      Spi_Send_Receive_Data((uint8_t)(address >> 16));  //发送存储器地址 24位     Spi_Send_Receive_Data((uint8_t)(address >> 8));     Spi_Send_Receive_Data((uint8_t)address);     for(;num>0;num--)     {         *(ptr+i) = Spi_Send_Receive_Data(0xff);     }     CS = 1; }    void Page_Program(uint8_t *ptr,uint32_t address,uint32_t num) {     Write_Enable();                                   //写使能 06H     CS = 0;     Spi_Send_Receive_Data(Page_Program);              //发送0x02指令     Spi_Send_Receive_Data((uint8_t)(address >> 16));  //发送存储器地址 24位     Spi_Send_Receive_Data((uint8_t)(address >> 8));     Spi_Send_Receive_Data((uint8_t)address);     for(;num>0;num--)     {         Spi_Send_Receive_Data(*(ptr+i));     }     CS = 1;     Wait_idle();                              //等待空闲  }  //扇区内操作函数  void Handle_Sector(uint8_t *ptr,uint32_t address,uint32_t Byte_Num) {     pageremain = 256 - address%256;           //判断address所在页的剩余字节      if(Byte_Num < 256)         pageremain = Byte_Num;     else         pageremain = 256;     while(1)                                  //使用循环进行页编程     {         Page_Program(ptr,address,pageremain);         if(pageremain == Byte_Num)             break;         address += pageremain;         ptr += pageremain;         Byte_Num -= pageremain;         if(Byte_Num > 256)              pageremain = 256;         else             pageremain = Byte_Num;     } }   uint8_t Flash[4096]; void Write_Flash(uint8_t *ptr,uint32_t address,uint32_t Byte_Num) {     uint8 i;     uint8_t *p;     uint16_t secpos = 0;           //起始地址所在的扇区位置     uint16_t secoff = 0;           //起始地址在当前扇区的偏移值     uint16_t secremain = 0;        //起始地址开始,当前扇区剩余字节数       uint8_t pageremain = 0;      secpos = address/4096;     secoff = address%4096;     secremain = 4096 - secoff;      p = Flash;     if(Byte_Num < 4096)            //当前扇区剩余字节够存放Byte_Num字节的数据         secremain = Byte_Num;     else         secremain = 4096;     while(1)     {         Sector_Erase(secpos);        //擦数当前扇区,判断是都擦除成功          Read_Data(p,secpos*4096,4096);         for(i=0;i<4096;i++)         {             if(p[i] != 0xff)                 break;                   //擦除失败         }         Handle_Sector(ptr,secpos*4096,secremain);         if(secremain == Byte_Num)    //写FLASH结束             break;         else         {             secpos++;             ptr += secremain;             Byte_Num -= secremain;             if(Byte_Num < 4096)                 secremain = Byte_Num;             else                 secremain = 4096;         }     } }  void Sector_Erase(uint32_t address) {     Write_Enable();                                             //写使能 06H     CS = 0;     Spi_Send_Receive_Data(Page_Program);                        //发送0x20指令     Spi_Send_Receive_Data((uint8_t)(address >> 16));            //发送存储器地址 24位     Spi_Send_Receive_Data((uint8_t)(address >> 8));     Spi_Send_Receive_Data((uint8_t)address);     CS = 1;     Wait_idle();                                                //等待空闲  }   void Block_Erase(uint8_t cmd,uint32_t address) {     Write_Enable();                                             //写使能 06H     CS = 0;     Spi_Send_Receive_Data(cmd);           Spi_Send_Receive_Data((uint8_t)(address >> 16));            //发送存储器地址 24位     Spi_Send_Receive_Data((uint8_t)(address >> 8));     Spi_Send_Receive_Data(uint8_t)address);     CS = 1;     Wait_idle();                                                //等待空闲  }   void Chip_Erase(void) {     Write_Enable();                                             //写使能 06H     CS = 0;     Spi_Send_Receive_Data(Chip_Erase);     CS = 1;     Wait_idle();                                                //等待空闲  }   void Power_Down(void) {     CS = 0;     Spi_Send_Receive_Data(Power_Down);     CS = 1;     Wait_idle();                                                //等待空闲  }    void Release_Power_Down(void) {     CS = 0;     Spi_Send_Receive_Data(Release_Power_Down);     CS = 1;     Wait_idle();                                                //等待空闲  }    uint8_t Release_Power_Down(void) {     uint8_t dat;     CS = 0;     Spi_Send_Receive_Data(Release_Power_Down);     Spi_Send_Receive_Data(0xff);     Spi_Send_Receive_Data(0xff);     Spi_Send_Receive_Data(0xff);     dat = Spi_Send_Receive_Data(0xff);     CS = 1;     Wait_idle();                                               //等待空闲      return dat;  }    uint16_t Read_Chip_Id(void) {     uin16_t id = 0;     CS = 0;     Spi_Send_Receive_Data(Read_Chip_ID);     Spi_Send_Receive_Data(0x00);     Spi_Send_Receive_Data(0x00);     Spi_Send_Receive_Data(0x00);     id = Spi_Send_Receive_Data(0xff)<<8;     id |= Spi_Send_Receive_Data(0xff);     CS = 1;     return id; }


文/恰冯同学年少(简书作者)
原文链接:http://www.jianshu.com/p/89f2b6edb810
著作权归作者所有,转载请联系作者获得授权,并标注“简书作者”。
 
打赏
 
更多>同类电子
0相关评论

推荐图文
推荐电子
点击排行

网站首页  |  关于我们  |  联系方式  |  使用协议  |  版权隐私  |  网站地图  |  排名推广  |  广告服务  |  积分换礼  |  网站留言  |  RSS订阅  |  违规举报