前言

  • SPI 介绍为搜集百度资料+个人理解
  • 其余为原创(有误请指正)
  • 集四种模式于一身
  • demo 采用MX25L64的FLASH芯片

bsp_spi.c

/**
******************************************************************************
* @file bsp_spi.c
* @author lzm
* @version V1.0
* @date 2020-09-26
* @brief 采用 软件 SPI
* @attention
*
* 实验平台:LZM
*
******************************************************************************
*/
#include "bsp_spi.h" /*
*********************************************************************************************************
* DEFINE STATIC FUNCTION (API)
*********************************************************************************************************
*/
/* basic */
#define spiOutHi(gpio, pin) {gpio->BSRR=pin;} //输出为高电平
#define spiOutLo(gpio, pin) {gpio->BRR=pin;} //输出为低电平
#define spiOut(gpio, pin, lev) (lev)?(gpio->BSRR=pin):(gpio->BRR=pin)
#define spiIn(gpio, pin) GPIO_ReadInputDataBit(gpio, pin)
/* top */ /* spi 为指针 */
#define spiCsOutHi(spi) spiOutHi(spi->csGpiox, spi->csPin)
#define spiCsOutLo(spi) spiOutLo(spi->csGpiox, spi->csPin)
#define spiSckOutHi(spi) spiOutHi(spi->sckGpiox, spi->sckPin)
#define spiSckOutLo(spi) spiOutLo(spi->sckGpiox, spi->sckPin)
#define spiMosiOutHi(spi) spiOutHi(spi->mosiGpiox, spi->mosiPin)
#define spiMosiOutLo(spi) spiOutLo(spi->mosiGpiox, spi->mosiPin)
#define spiMisoIn(spi) spiIn(spi->misoGpiox, spi->misoPin) /*
*********************************************************************************************************
* DEFINE
*********************************************************************************************************
*/
// spi 驱动元素(驱动表)
spi_t spiDriverElem[spiSPI_DRIVER_COUNT]; /**
* @brief 选出时钟信号线
* @param
* @retval
* @author lzm
*/
static uint32_t __selectClkByGpio(const uint32_t addr)
{
switch(addr)
{
case GPIOA_BASE:
return RCC_APB2Periph_GPIOA;
case GPIOB_BASE:
return RCC_APB2Periph_GPIOB;
case GPIOC_BASE:
return RCC_APB2Periph_GPIOC;
case GPIOD_BASE:
return RCC_APB2Periph_GPIOD;
case GPIOE_BASE:
return RCC_APB2Periph_GPIOE;
case GPIOF_BASE:
return RCC_APB2Periph_GPIOF;
case GPIOG_BASE:
return RCC_APB2Periph_GPIOG;
}
return NULL;
} /**
* @brief 初始化 SPI 引脚
* @param
* @retval
* @author lzm
*/
void spiGpioInit(eSPI_ID id)
{
GPIO_InitTypeDef GPIO_InitStructure; //定义结构体
uint32_t csGpioClk;
uint32_t sckGpioClk;
uint32_t mosiGpioClk;
uint32_t misoGpioClk;
const spi_t * spi = &spiDriverElem[id]; csGpioClk = __selectClkByGpio((uint32_t)(spi->csGpiox));
sckGpioClk = __selectClkByGpio((uint32_t)(spi->sckGpiox));
mosiGpioClk = __selectClkByGpio((uint32_t)(spi->csGpiox));
misoGpioClk = __selectClkByGpio((uint32_t)(spi->misoGpiox)); RCC_APB2PeriphClockCmd(csGpioClk | sckGpioClk | mosiGpioClk | misoGpioClk, ENABLE); //打开时钟 GPIO_InitStructure.GPIO_Pin = spi->csPin; //配置端口及引脚(指定方向)
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(spi->csGpiox, &GPIO_InitStructure); //初始化端口(开往指定方向) GPIO_InitStructure.GPIO_Pin = spi->sckPin;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(spi->sckGpiox, &GPIO_InitStructure); GPIO_InitStructure.GPIO_Pin = spi->mosiPin;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(spi->mosiGpiox, &GPIO_InitStructure); GPIO_InitStructure.GPIO_Pin = spi->misoPin;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
GPIO_Init(spi->misoGpiox, &GPIO_InitStructure); spiCsOutHi(spi);
} /**
* @brief SPI 发送
* @param
* @retval
* @author lzm
*/
void spiWriteOneByte(eSPI_ID id, unsigned char data)
{
unsigned char i;
const spi_t * spi = &spiDriverElem[id]; if(spi->CPHA){
spiOut(spi->sckGpiox, spi->sckPin, spi->CPOL);
}
// spi->delayUsFun(spi->readDelayUsCnt); for(i=0; i<8; i++)
{
spiOut(spi->sckGpiox, spi->sckPin, (spi->CPOL != spi->CPHA));
if(data & 0x80){
spiMosiOutHi(spi);
}
else{
spiMosiOutLo(spi);
}
data <<= 1;
spi->delayUsFun(spi->readDelayUsCnt);
spiOut(spi->sckGpiox, spi->sckPin, (spi->CPOL == spi->CPHA));
}
if(!(spi->CPHA)){
spiOut(spi->sckGpiox, spi->sckPin, spi->CPOL);
}
} /**
* @brief SPI 读取
* @param
* @retval
* @author lzm
*/
unsigned char spiReadOneByte(eSPI_ID id)
{
unsigned char i;
unsigned char ret;
const spi_t * spi = &spiDriverElem[id]; for(i=0; i<8; i++)
{
spiOut(spi->sckGpiox, spi->sckPin, (spi->CPOL != spi->CPHA));
ret <<= 1;
if(spiMisoIn(spi))
ret |= 0x01;
else
ret &= 0xfe;
spi->delayUsFun(spi->readDelayUsCnt);
spiOut(spi->sckGpiox, spi->sckPin, (spi->CPOL == spi->CPHA));
}
spiOut(spi->sckGpiox, spi->sckPin, spi->CPOL);
return ret;
} /**
* @brief SPI 读写
* @param
* @retval
* @author lzm
*/
unsigned char spiRWOneByte(eSPI_ID id, unsigned char data)
{
unsigned char i;
unsigned char ret;
const spi_t * spi = &spiDriverElem[id]; if(spi->CPHA){
spiOut(spi->sckGpiox, spi->sckPin, spi->CPOL);
} for(i=0; i<8; i++)
{
spiOut(spi->sckGpiox, spi->sckPin, (spi->CPOL != spi->CPHA));
if(data & 0x80){
spiMosiOutHi(spi);
}
else{
spiMosiOutLo(spi);
}
data <<= 1;
spi->delayUsFun(spi->readDelayUsCnt);
spiOut(spi->sckGpiox, spi->sckPin, (spi->CPOL == spi->CPHA));
ret <<= 1;
if(spiMisoIn(spi))
ret |= 0x01;
else
ret &= 0xfe;
spi->delayUsFun(spi->readDelayUsCnt);
}
if(!(spi->CPHA)){
spiOut(spi->sckGpiox, spi->sckPin, spi->CPOL);
}
} /**
* @brief SPI 片选使能
* @param
* @retval
* @author lzm
*/
void spiCSOut(eSPI_ID id, unsigned char lev)
{
const spi_t * spi = &spiDriverElem[id]; spiOut(spi->csGpiox, spi->csPin, lev);
} /*
*********************************************************************************************************
* DEFINE [API] FUNCTION
*********************************************************************************************************
*/
/**
* @brief 注册SPI设备
* spiDriverElem[spiID].id = spiID; // 保持下标与ID相等,查找时可以直接定位,实现时间复杂度为O(1);
* @param
* @retval
* @author lzm
*/
void REGISTER_SPI_DEV(spi_t * spi)
{
if(spi->id >= espiSPI_DRIVER_COUNT)
return;
spiDriverElem[spi->id].id = spi->id;
spiDriverElem[spi->id].method = spi->method;
spiDriverElem[spi->id].CPOL = spi->CPOL;
spiDriverElem[spi->id].CPHA = spi->CPHA;
spiDriverElem[spi->id].readDelayUsCnt = spi->readDelayUsCnt;
spiDriverElem[spi->id].delayUsFun = spi->delayUsFun;
spiDriverElem[spi->id].csGpiox = spi->csGpiox;
spiDriverElem[spi->id].csPin = spi->csPin;
spiDriverElem[spi->id].sckGpiox = spi->sckGpiox;
spiDriverElem[spi->id].sckPin = spi->sckPin;
spiDriverElem[spi->id].mosiGpiox = spi->mosiGpiox;
spiDriverElem[spi->id].mosiPin = spi->mosiPin;
spiDriverElem[spi->id].misoGpiox = spi->misoGpiox;
spiDriverElem[spi->id].misoPin = spi->misoPin;
}

bsp_spi.h

/**
******************************************************************************
* @file bsp_spi.h
* @author lzm
* @version V1.0
* @date 2020-09-26
* @brief 采用 硬件 SPI
* @attention
*
* 实验平台:LZM
*
******************************************************************************
*/ #ifndef _BSP_SPI_H_
#define _BSP_SPI_H_ #include "LssAppConfig.h" /*
*********************************************************************************************************
* CONFIG API
*********************************************************************************************************
*/
// [注][spi]实时修改
// spi 设备数量
#define spiSPI_DRIVER_COUNT 1
/* spi id. */
typedef enum
{
espiFLASH_A = 0, espiSPI_DRIVER_COUNT,
}eSPI_ID; /* spi mode. */
typedef enum
{
espiHARDWARE = 0, espiSOFTWARE,
}eSPI_METHOD; /*
*********************************************************************************************************
* BASIC
*********************************************************************************************************
*/
/* spi */
struct SPI_T{
/* id */
eSPI_ID id; /* software spi */
/* method */
eSPI_METHOD method;
/* CPOL CPHA : 00 01 10 11*/
/* cpol */
unsigned char CPOL;
/* cpha */
unsigned char CPHA;
/* delay */
/* cnt */
unsigned int readDelayUsCnt;
/* function */
void ( *delayUsFun )(int cnt); /* hardware spi */
SPI_TypeDef* SPIx; /* pin */
GPIO_TypeDef * csGpiox;
uint16_t csPin;
GPIO_TypeDef * sckGpiox;
uint16_t sckPin;
GPIO_TypeDef * mosiGpiox;
uint16_t mosiPin;
GPIO_TypeDef * misoGpiox;
uint16_t misoPin;
};
typedef struct SPI_T spi_t; /*
*********************************************************************************************************
* BROADCAST
*********************************************************************************************************
*/
/* device table */
extern spi_t spiDriverElem[spiSPI_DRIVER_COUNT]; /* function */
void spiGpioInit(eSPI_ID id);
void spiWriteOneByte(eSPI_ID id, unsigned char data);
unsigned char spiReadOneByte(eSPI_ID id);
void spiCSOut(eSPI_ID id, unsigned char lev);
void REGISTER_SPI_DEV(spi_t * spi); #endif

bsp_flash.c

/**
******************************************************************************
* @file bsp_flash.c
* @author lzm
* @version V1.0
* @date 2020-10-29
* @brief
* @attention
*
* 实验平台:LZM
*
******************************************************************************
*/ #include "bsp_flash.h"
#include "lss_IO.h"
#include "boardInfo.h" /*
*********************************************************************************************************
* DEFINE
*********************************************************************************************************
*/
// flash 设备元素(设备表)
flash_t flashDeviceElem[flashFLASH_DEVICE_COUNT]; /**
* @brief 写一个字节到外挂 Flash
* @param id : flash 设备 id
* @param data : 需要写入的数据
* @retval
* @author lzm
*/
void flashWriteByte(eFLASH_ID id, unsigned char data)
{
eSPI_ID spiid = flashDeviceElem[id].spiID;
spiWriteOneByte(spiid, data);
} /**
* @brief 从外挂flash中读取一个字节
* @param id : flash 设备 id
* @param data : 需要写入的数据
* @retval
* @author lzm
*/
unsigned char flashReadByte(eFLASH_ID id)
{
unsigned char ch;
eSPI_ID spiid = flashDeviceElem[id].spiID;
ch = spiReadOneByte(spiid);
return ch;
} /*
*********************************************************************************************************
* [业务]
*********************************************************************************************************
*/
/**
* @brief 读取flash状态寄存器
* @brief BIT7 6 5 4 3 2 1 0
* @brief SPR RV TB BP2 BP1 BP0 WEL BUSY
* @brief SPR:默认0,状态寄存器保护位,配合WP使用
* @brief TB,BP2,BP1,BP0:FLASH区域写保护设置
* @brief WEL:写使能锁定
* @brief BUSY:忙标记位(1,忙;0,空闲)
* @brief 默认:0x00
* @param id : flash 设备 id
* @author lzm
*/
unsigned char mx25lxxReadSR(eFLASH_ID id)
{
unsigned char ch;
eSPI_ID spiid = flashDeviceElem[id].spiID; spiCSOut(spiid, 0);
spiWriteOneByte(spiid, mx25lxxREAD_STATUS_REG);
ch = spiReadOneByte(spiid);
spiCSOut(spiid, 1); return ch;
} /**
* @brief 写flash状态寄存器
* @brief 只有SPR,TB,BP2,BP1,BP0(bit 7,5,4,3,2)可以写!!!
* @param id : flash 设备 id
* @author lzm
*/
void mx25lxxWriteSR(eFLASH_ID id, unsigned char data)
{
eSPI_ID spiid = flashDeviceElem[id].spiID; spiCSOut(spiid, 0);
spiWriteOneByte(spiid, mx25lxxWRITE_STATUS_REG);
spiWriteOneByte(spiid, data);
spiCSOut(spiid, 1);
} /**
* @brief 等待空闲
* @param id : flash 设备 id
* @author lzm
*/
void mx25lxxWaitBusy(eFLASH_ID id)
{
while((mx25lxxReadSR(id)&0x01)==0x01); // 等待BUSY位清空
} /**
* @brief 写使能
* @param id : flash 设备 id
* @author lzm
*/
void mx25lxxWriteEnable(eFLASH_ID id)
{
eSPI_ID spiid = flashDeviceElem[id].spiID; spiCSOut(spiid, 0);
spiWriteOneByte(spiid, mx25lxxWRITE_ENABLE);
spiCSOut(spiid, 1);
} /**
* @brief 写禁止
* @param id : flash 设备 id
* @author lzm
*/
void mx25lxxWriteDisable(eFLASH_ID id)
{
eSPI_ID spiid = flashDeviceElem[id].spiID; spiCSOut(spiid, 0);
spiWriteOneByte(spiid, mx25lxxWRITE_DISABLE);
spiCSOut(spiid, 1);
} /**
* @brief 读取芯片ID
* @param id : flash 设备 id
* @retval 0Xxx13,表示芯片型号为W25Q80
* @retval 0Xxx14,表示芯片型号为W25Q16
* @retval 0Xxx15,表示芯片型号为W25Q32
* @retval 0Xxx16,表示芯片型号为W25Q64
* @retval 0Xxx17,表示芯片型号为W25Q128
* @author lzm
*/
unsigned int mx25lxxReadID(eFLASH_ID id)
{
unsigned int ret;
eSPI_ID spiid = flashDeviceElem[id].spiID; spiCSOut(spiid, 0); spiWriteOneByte(spiid, 0x90);
spiWriteOneByte(spiid, 0x00);
spiWriteOneByte(spiid, 0x00);
spiWriteOneByte(spiid, 0x00);
ret |= spiReadOneByte(spiid)<<8;
ret |= spiReadOneByte(spiid); spiCSOut(spiid, 1); return ret;
} /**
* @brief 在指定地址开始读取指定长度的数据
* @param id : flash 设备 id
* @param readAddr : 开始读取的地址(24bit)
* @param pReadBuff : 数据存储区
* @param size : 字节数
* @author lzm
*/
void mx25lxxReadBytes(eFLASH_ID id, uint32_t readAddr, uint8_t * pReadBuff, uint16_t size)
{
uint16_t i;
eSPI_ID spiid = flashDeviceElem[id].spiID; spiCSOut(spiid, 0); spiWriteOneByte(spiid, mx25lxxREAD_DATA);
spiWriteOneByte(spiid, (u8)((readAddr)>>16));
spiWriteOneByte(spiid, (u8)((readAddr)>>8));
spiWriteOneByte(spiid, (u8)readAddr);
for(i=0;i<size;i++)
{
pReadBuff[i]=spiReadOneByte(spiid); //循环读数
} spiCSOut(spiid, 1); } /**
* @brief 在指定地址开始写入最大256字节的数据
* @param id : flash 设备 id
* @param writeAddr : 开始写的地址(24bit)
* @param pReadBuff : 数据存储区
* @param size : 字节数
* @author lzm
*/
void mx25lxxWriteBytes(eFLASH_ID id, uint32_t writeAddr, uint8_t * pWriteBuff, uint16_t size)
{
uint16_t i;
eSPI_ID spiid = flashDeviceElem[id].spiID; mx25lxxWriteEnable(id);
spiCSOut(spiid, 0); spiWriteOneByte(spiid, mx25lxxPAGE_PROGRAM);
spiWriteOneByte(spiid, (u8)((writeAddr)>>16));
spiWriteOneByte(spiid, (u8)((writeAddr)>>8));
spiWriteOneByte(spiid, (u8)writeAddr);
for(i=0;i<size;i++)
{
spiWriteOneByte(spiid,pWriteBuff[i]); //循环读数
} spiCSOut(spiid, 1);
} /**
* @brief 无检验写SPI FLASH
* @brief 必须确保所写的地址范围内的数据全部为0XFF,否则在非0XFF处写入的数据将失败!
* @brief 具有自动换页功能
* @brief 在指定地址开始写入指定长度的数据,但是要确保地址不越界!
* @param id : flash 设备 id
* @param readAddr : 开始读取的地址(24bit)
* @param pReadBuff : 数据存储区
* @param size : 字节数
* @author lzm
*/
void mx25lxxWriteNoCheck(eFLASH_ID id, uint32_t writeAddr, uint8_t * pWriteBuff, uint16_t size)
{
uint16_t residueSize; residueSize = 0xFF - writeAddr%0XFF;
if(size<=residueSize) residueSize=size;//不大于256个字节 while(1)
{
mx25lxxWriteBytes(id, writeAddr, pWriteBuff, residueSize);
if(size==residueSize)
break;//写入结束了
else //size>pageremain
{
pWriteBuff+=residueSize;
writeAddr+=residueSize; size-=residueSize; //减去已经写入了的字节数
if(size>256)residueSize=256; //一次可以写入256个字节
else residueSize=size; //不够256个字节了
}
}
} /**
* @brief 擦除一个扇区(擦除一个扇区的最少时间:150ms)
* @param id : flash 设备 id
* @param eraseAddr : 扇区地址 根据实际容量设置
* @author lzm
*/
void mx25lxxEraseSector(eFLASH_ID id, uint32_t eraseAddr)
{
eSPI_ID spiid = flashDeviceElem[id].spiID; eraseAddr*=4096;
mx25lxxWriteEnable(id); //SET WEL
mx25lxxWaitBusy(id);
spiCSOut(spiid, 0); //使能器件
spiWriteOneByte(spiid, mx25lxxSECTOR_ERASE); //发送扇区擦除指令
spiWriteOneByte(spiid, (u8)((eraseAddr)>>16)); //发送24bit地址
spiWriteOneByte(spiid, (u8)((eraseAddr)>>8));
spiWriteOneByte(spiid, (u8)eraseAddr);
spiCSOut(spiid, 1); //取消片选
mx25lxxWaitBusy(id); //等待擦除完成
} /**
* @brief 进入掉电模式
* @param id : flash 设备 id
* @author lzm
*/
void mx25lxxPowerDown(eFLASH_ID id)
{
eSPI_ID spiid = flashDeviceElem[id].spiID; spiCSOut(spiid, 0); //使能器件
spiWriteOneByte(spiid, mx25lxxPOWER_DOWN); //发送掉电命令
spiCSOut(spiid, 1); //取消片选
} /**
* @brief 唤醒
* @param id : flash 设备 id
* @author lzm
*/
void mx25lxxWakeUp(eFLASH_ID id)
{
eSPI_ID spiid = flashDeviceElem[id].spiID; spiCSOut(spiid, 0); //使能器件
spiWriteOneByte(spiid, mx25lxxRELEASE_POWER_DOWN); //发送掉电命令
spiCSOut(spiid, 1); //取消片选
} /**
* @brief 擦除其实就是写 1
* @param id : flash 设备 id
* @param eraseAddr : 开始写的地址(24bit)
* @param pEraseBuff : 数据存储区
* @param size : 字节数
* @author lzm
*/
uint8_t mx25lxxBUFFER[4096];
void mx25lxxEraseBytes(eFLASH_ID id, uint32_t eraseAddr, uint8_t * pEraseBuff, uint16_t size)
{
uint16_t i;
uint32_t secAddr; //扇区地址
uint16_t secOff; //在扇区内的偏移
uint16_t secResidueSize; //扇区剩余空间大小
u8 * mx25lxxBUF; mx25lxxBUF = mx25lxxBUFFER;
secAddr=eraseAddr/4096;//扇区地址
secOff=eraseAddr%4096;//在扇区内的偏移
secResidueSize=4096-secOff;//扇区剩余空间大小
if(size<=secResidueSize)secResidueSize=size;//不大于4096个字节
while(1)
{
mx25lxxReadBytes(id, secAddr*4096, mx25lxxBUFFER, 4096);//读出整个扇区的内容
for(i=0;i<secResidueSize;i++)//校验数据
{
if(mx25lxxBUF[secOff+i]!=0XFF)break;//需要擦除
}
if(i<secResidueSize)//需要擦除
{
mx25lxxEraseSector(id, secAddr); //擦除这个扇区
for(i=0;i<secResidueSize;i++) //复制
{
mx25lxxBUF[i+secOff]=pEraseBuff[i];
}
mx25lxxWriteNoCheck(id,secAddr*4096,mx25lxxBUF,4096);//写入整个扇区 }
else
mx25lxxWriteNoCheck(id,eraseAddr,pEraseBuff,secResidueSize);//写已经擦除了的,直接写入扇区剩余区间. if(size==secResidueSize)
break;//写入结束了
else//写入未结束
{
secAddr++;//扇区地址增1
secOff=0;//偏移位置为0 pEraseBuff+=secResidueSize; //指针偏移
eraseAddr+=secResidueSize; //写地址偏移
size-=secResidueSize; //字节数递减
if(size>4096)
secResidueSize=4096;//下一个扇区还是写不完
else
secResidueSize=size; //下一个扇区可以写完了
}
};
} /**
* @brief 擦除整个芯片(时间较长)
* @param id : flash 设备 id
* @author lzm
*/ void ITC_MX25LXX_EraseChip(eFLASH_ID id)
{
eSPI_ID spiid = flashDeviceElem[id].spiID; mx25lxxWriteEnable(id); //SET WEL
mx25lxxWaitBusy(id);
spiCSOut(spiid, 0); //使能器件
spiWriteOneByte(spiid, mx25lxxCHIP_ERASE); //发送片擦除命令
spiCSOut(spiid, 1); //取消片选
mx25lxxWaitBusy(id); //等待芯片擦除结束
} /**
* @brief 所有外挂FLASH设备初始化
* @param
* @retval
* @author lzm
*/
void flashInit(void)
{
uint8_t flashID;
spi_t spi;
unsigned int devID; // 先注册 SPI 驱动
spi.id = espiFLASH_A;
spi.method = espiSOFTWARE;
spi.CPOL = 1;
spi.CPHA = 1;
spi.readDelayUsCnt = 1;
spi.delayUsFun = dwtDelayUs;
spi.csGpiox = mx25lxxFLASH_SPI_CS_PORT;
spi.csPin = mx25lxxFLASH_SPI_CS_PIN;
spi.sckGpiox = mx25lxxFLASH_SPI_SCK_PORT;
spi.sckPin = mx25lxxFLASH_SPI_SCK_PIN;
spi.mosiGpiox = mx25lxxFLASH_SPI_MOSI_PORT;
spi.mosiPin = mx25lxxFLASH_SPI_MOSI_PIN;
spi.misoGpiox = mx25lxxFLASH_SPI_MISO_PORT;
spi.misoPin = mx25lxxFLASH_SPI_MISO_PIN;
REGISTER_SPI_DEV(&spi);
// 注册 FLASH 设备并绑定 i2c
REGISTER_FLASH_DEV(eMX25lXX_1, espiFLASH_A); for (flashID = 0; flashID < flashFLASH_DEVICE_COUNT; flashID++)
{
// 初始化 I2C
spiGpioInit( (eSPI_ID)(espiFLASH_A + flashID) ); devID = mx25lxxReadID((eFLASH_ID)flashID);
if(devID == 0XFFFF)
{
printf("\r\n[flashInit] [faild] and error devID is [%d]", devID);
}
else
{
printf("\r\n[flashInit] [successful] devID is [%d]", devID);
}
}
}

bsp_flash.h

/**
******************************************************************************
* @file flash.h
* @author lzm
* @version V1.0
* @date 2020-10-29
* @brief
* @attention
*
* 实验平台:ITC
*
******************************************************************************
*/ #ifndef _BSP_FLASH_H_
#define _BSP_FLASH_H_ #include "LssAppConfig.h"
#include "bsp_spi.h" /*
*********************************************************************************************************
* MX25L64 指令表 [业务]
*********************************************************************************************************
*/
#define mx25lxxWRITE_ENABLE 0x06
#define mx25lxxWRITE_DISABLE 0x04
#define mx25lxxREAD_STATUS_REG 0x05
#define mx25lxxWRITE_STATUS_REG 0x01
#define mx25lxxREAD_DATA 0x03
#define mx25lxxFAST_READ_DATA 0x0B
#define mx25lxxFAST_READ_DUAL 0x3B
#define mx25lxxPAGE_PROGRAM 0x02
#define mx25lxxBLOCK_ERASE 0xD8
#define mx25lxxSECTOR_ERASE 0x20
#define mx25lxxCHIP_ERASE 0xC7
#define mx25lxxPOWER_DOWN 0xB9
#define mx25lxxRELEASE_POWER_DOWN 0xAB
#define mx25lxxDEVICE_ID 0xAB
#define mx25lxxMANUFACT_DEVICE_ID 0x90
#define mx25lxxJEDCE_DEVICE_ID 0x9F /*
*********************************************************************************************************
* CONFIG API
*********************************************************************************************************
*/
/* [注][flash]实时修改 */
// flash 设备数量
#define flashFLASH_DEVICE_COUNT 1 /* flash id. */
typedef enum
{
eMX25lXX_1 = 0, // eflashFLASH_DEVICE_COUNT,
}eFLASH_ID; /*
*********************************************************************************************************
* BASIC
*********************************************************************************************************
*/
/* flash */
struct FLASH_T{
/* id */
eFLASH_ID ID;
/* spi id */
eSPI_ID spiID;
};
typedef struct FLASH_T flash_t; /*
*********************************************************************************************************
* DEFINE [API] FUNCTION
*********************************************************************************************************
*/
/**
* @brief 注册IIC设备
* @param
* @retval
* @author lzm
*/
#define REGISTER_FLASH_DEV(flashid, spicid) \
{ \
flashDeviceElem[flashid].ID = flashid; \
flashDeviceElem[flashid].spiID = spicid; \
} /*
*********************************************************************************************************
* BROADCAST
*********************************************************************************************************
*/
/* device table */
extern flash_t flashDeviceElem[flashFLASH_DEVICE_COUNT]; /* function */
unsigned char mx25lxxReadSR(eFLASH_ID id);
void mx25lxxWriteSR(eFLASH_ID id, unsigned char data);
void mx25lxxWaitBusy(eFLASH_ID id);
void mx25lxxWriteEnable(eFLASH_ID id);
void mx25lxxWriteDisable(eFLASH_ID id);
unsigned int mx25lxxReadID(eFLASH_ID id);
void mx25lxxReadBytes(eFLASH_ID id, uint32_t readAddr, uint8_t * pReadBuff, uint16_t size);
void mx25lxxWriteBytes(eFLASH_ID id, uint32_t writeAddr, uint8_t * pWriteBuff, uint16_t size);
void mx25lxxWriteNoCheck(eFLASH_ID id, uint32_t writeAddr, uint8_t * pWriteBuff, uint16_t size);
void mx25lxxEraseBytes(eFLASH_ID id, uint32_t eraseAddr, uint8_t * pEraseBuff, uint16_t size);
void mx25lxxEraseSector(eFLASH_ID id, uint32_t eraseAddr);
void mx25lxxPowerDown(eFLASH_ID id);
void flashInit(void);
#endif

【框架】SPI四种模式+通用设备驱动实现-源码的更多相关文章

  1. 框架-SPI四种模式+通用设备驱动实现

    目录 前言 笔录草稿 SPI介绍 SPI四种模式 ** SPI 驱动框架 ** 框架 前言 SPI 介绍为搜集百度资料+个人理解 其余为原创(有误请指正) 集四种模式于一身 笔录草稿 SPI介绍 SP ...

  2. 小知识:SPI四种模式区别【转】

    转自:http://home.eeworld.com.cn/my/space-uid-80086-blogid-119198.html spi四种模式SPI的相位(CPHA)和极性(CPOL)分别可以 ...

  3. SPI 四种模式

    SPI时钟极性CPOL, = 0表示在没有数据传输时为低电平,= 1表示没有数据传输时为高电平. SPI时钟相位CPHA,= 0表示时钟的第一个沿更新数据.第二个沿锁存数据,= 1表示时钟的第一个沿锁 ...

  4. Asp.net的sessionState四种模式配置方案

    sessionState节点的配置 web.config关于sessionState节点的配置方案,sessionState有四种模式:off,inProc,StateServer,SqlServer ...

  5. 大量数据也不在话下,Spring Batch并行处理四种模式初探

    1 前言 欢迎访问南瓜慢说 www.pkslow.com获取更多精彩文章! Spring相关文章:Springboot-Cloud 前面写了一篇文章<通过例子讲解Spring Batch入门,优 ...

  6. Hibernate 查询MatchMode的四种模式

    Hibernate 查询MatchMode的四种模式 MatchMode.START:字符串在最前面的位置.相当于"like 'key%'" MatchMode.END:字符串在最 ...

  7. Android 文件访问权限的四种模式

    Linux文件的访问权限* 在Android中,每一个应用是一个独立的用户* drwxrwxrwx* 第1位:d表示文件夹,-表示文件* 第2-4位:rwx,表示这个文件的拥有者(创建这个文件的应用) ...

  8. 对称加密和分组加密中的四种模式(ECB、CBC、CFB、OFB)

    一. AES对称加密: AES加密 分组 二. 分组密码的填充 分组密码的填充 e.g.: PKCS#5填充方式 三. 流密码:   四. 分组密码加密中的四种模式: 3.1 ECB模式 优点: 1. ...

  9. OAuth2简易实战(一)-四种模式

    1. OAuth2简易实战(一)-四种模式 1.1. 授权码授权模式(Authorization code Grant) 1.1.1. 流程图 1.1.2. 授权服务器配置 配置授权服务器中 clie ...

随机推荐

  1. js map & Number

    js map & Number const regionIds = `1,2,3`; // "1,2,3" regionIds.split(',').map(Number) ...

  2. Nginx环境下,PHP下载,中文文件,下载失效(英文可以下载)怎么解决呢?

    参考出处: http://www.imooc.com/qadetail/76393 Nginx环境下,PHP下载,中文文件,下载失效(英文可以下载)怎么解决呢? 背景介绍: 文件名  为英文时可以下载 ...

  3. Apple Watch Series 6 无法使用截屏问题和解决方案

    Apple Watch Series 6 无法使用截屏问题和解决方案 shit Apple,无法使用截屏, TMD 根本就不存在 相机胶卷 ! 不好使 解决方案 ??? https://support ...

  4. ECharts Pie All In One

    ECharts Pie All In One 饼图 https://echarts.apache.org/examples/zh/index.html#chart-type-pie 嵌套饼图 http ...

  5. 如何给 GitHub 添加 SSH key, 如何生成 SSH key 详细图文教程!

    如何给 GitHub 添加  SSH key, 如何生成  SSH key 详细图文教程! 一. 生成  SSH key https://ide.c9.io/xgqfrms/ 创建一个空项目:(或使用 ...

  6. H5 APP 页面移动端适配方案

    H5 APP 页面移动端适配方案 https://segmentfault.com/a/1190000011586301 https://juejin.im/post/5cbdee71f265da03 ...

  7. NGK.IO超级节点是我们掌握的下一个财富密码吗?

    从日前NGK.IO发布的新闻中,我们捕捉到了一个非常重要的信息,那就是反复被提到的"超级节点".很多人都对这个"超级节点"一头雾水,这个"超级节点&q ...

  8. django学习-5.获取url参数和name的作用

    1.前言 假如我们要打开这两个博客园地址:[https://www.cnblogs.com/xiamen-momo/archive/2020/11.html].[https://www.cnblogs ...

  9. 死磕以太坊源码分析之EVM如何调用ABI编码的外部方法

    死磕以太坊源码分析之EVM如何调用ABI编码的外部方法 配合以下代码进行阅读:https://github.com/blockchainGuide/ 写文不易,给个小关注,有什么问题可以指出,便于大家 ...

  10. Django之csrf中间件及auth模块使用

    目录 一.基于配置文件的编程思想 1. importlib 模块 2. 配置文件 二.跨站请求伪造(csrf) 1.csrf简介以及由来 2.Django中的csrf中间件如何使用 2.1 普通for ...