前言

  • 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. short URL 短网址实现原理剖析

    short URL 短网址实现原理剖析 意义,简短便于分享,避免出现超长 URL 的字符长度限制问题 原理分析, 使用 HashMap 存储对应的映射关系 (长度不超过7的字符串,由大小写字母加数字共 ...

  2. Android Activity 与 WebView 页面线程不一致 bug​

    Android Activity 与 WebView 页面线程不一致 bug​ refs xgqfrms 2012-2020 www.cnblogs.com 发布文章使用:只允许注册用户才可以访问!

  3. prefetch vs preload vs prerender vs preconnect All In One

    prefetch vs preload vs prerender vs preconnect All In One 前端性能优化 prefetch 预获取 https://developer.mozi ...

  4. Win/Mac 键位映射 & 在 Mac 上更改“键盘”偏好设置

    Win/Mac 键位映射 & 在 Mac 上更改"键盘"偏好设置 PC键盘 在Mac下Command/Option键切换 https://support.apple.com ...

  5. taro router

    taro router https://nervjs.github.io/taro/docs/router.html bug import Taro, { Component, Config } fr ...

  6. macOS & Nginx

    macOS & Nginx ngnix # 使用 brew 安装(如果没有 brew 命令,需要自行安装 brew) $ brew install nginx $ nginx -h # 查看 ...

  7. better-scroll使用参考

    ************better-scroll是基于父元素固定高度,溢出才滚动的,所以父元素务必定高,否则无法滚动***************************************** ...

  8. [Python] 茎叶图和复合饼图的画法

    目录 茎叶图 复合饼图 茎叶图 from itertools import groupby nums2=[225, 232,232,245,235,245,270,225,240,240,217,19 ...

  9. HBase 数据存储结构

    在HBase中, 从逻辑上来讲数据大概就长这样: 单从图中的逻辑模型来看, HBase 和 MySQL 的区别就是: 将不同的列归属与同一个列族下 支持多版本数据 这看着感觉也没有那么太大的区别呀, ...

  10. 力扣119. 杨辉三角 II

    原题 1 class Solution: 2 def getRow(self, rowIndex: int) -> List[int]: 3 ans = [1] 4 for i in range ...