由于公司的电路是前辈画的,只能使用模拟spi中如图所示
上图是stm32所对应的引脚
上图是w25q128的引脚
当读取的时候ID号一直是0xffff,在网上查了各种方法都试过了都不行,我这个情况稍微特殊,就是使用了PB3、PB4这两个引脚上电复位默认是作为调试端口使用的。所以得先关闭JTAG功能才行
GPIO_PinRemapConfig(GPIO_Remap_SWJ_JTAGDisable,ENABLE);//关闭pb3、4,pa15的JTAG功能,打开sw调试功能
初始化时,PB3、PB4做普通io需打开复用功能文章来源:https://www.toymoban.com/news/detail-765976.html
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE); //pb3,4做普通io需打开复用功能
#include "spi_flash.h"
#define W25Q32_PageSize 256
//指令表
#define W25X_PowerDown 0xb9 //休眠
#define W25X_ReleasePowerDown 0xab //唤醒
#define W25X_ReadStatusReg 0x05 //读控制和状态寄存器
#define W25X_WriteStatusReg 0x01 //写控制与状态寄存器
#define W25X_WriteEnable 0x06 //写使能
#define W25X_WriteDisable 0x04 //写禁用
#define W25X_ReadData 0x03 //读数据
#define W25X_PageProgram 0x02 //页编程
#define W25X_ChipErase 0x60 //整页擦除
#define W25X_SectorErase 0x20 //扇区擦除
#define DO_H() (GPIO_SetBits(GPIOB, GPIO_Pin_3))
#define DO_L() (GPIO_ResetBits(GPIOB, GPIO_Pin_3))
#define SCK_H() (GPIO_SetBits(GPIOD, GPIO_Pin_2))
#define SCK_L() (GPIO_ResetBits(GPIOD, GPIO_Pin_2))
#define DI_STATE() (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_5))
#define SPIFLASH_CS_LOW() (GPIO_ResetBits(GPIOB, GPIO_Pin_4))
#define SPIFLASH_CS_HIGH() (GPIO_SetBits(GPIOB, GPIO_Pin_4))
static void SpiWriteByte(uint8_t data) //写字节
{
register unsigned char i;
SCK_L();
for (i = 0; i < 8; i++)
{
delay_us(4);
// MOSI
if (data & 0x80)
DO_H();
else
DO_L();
delay_us(4);
SCK_H();
delay_us(4);
data <<= 1;
SCK_L();
delay_us(4);
}
//delay_ms(1);
}
static uint8_t SpiReadByte(void) //读字节
{
register unsigned char i, out;
out = 0;
for (i = 0; i < 8; i++)
{
delay_us(4);
SCK_H();
out <<= 1;
delay_us(4);
if (DI_STATE())
out |= 0x01;
delay_us(4);
SCK_L();
delay_us(4);
}
return out;
}
void SPI_Flash_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB|RCC_APB2Periph_GPIOD, ENABLE); //使能USART1,GPIOA时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE); //pb3,4做普通io需打开复用功能
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3 | GPIO_Pin_4;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_Init(GPIOB, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_Init(GPIOD, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
GPIO_Init(GPIOB, &GPIO_InitStructure);
SPIFLASH_CS_HIGH();
SPI_Flash_WAKEUP(); //唤醒
//W25QXX_Flash_ReadID(); //读ID
}
//读取W25QXX的状态寄存器
//BIT7 6 5 4 3 2 1 0
//SPR RV TB BP2 BP1 BP0 WEL BUSY
//SPR:默认0,状态寄存器保护位,配合WP使用
//TB,BP2,BP1,BP0:FLASH区域写保护设置
//WEL:写使能锁定
//BUSY:忙标记位(1,忙;0,空闲)
//默认:0x00
uint8_t W25QXX_Flash_ReadSR(void) //读控制和状态寄存器
{
uint8_t byte = 0;
SPIFLASH_CS_LOW(); //
SpiWriteByte(W25X_ReadStatusReg); //
byte = SpiReadByte(); //
SPIFLASH_CS_HIGH(); //
return byte;
}
/*static void SPI_FLASH_Write_SR(uint8_t sr)
{
SPIFLASH_CS_LOW();//
SpiWriteByte(W25X_WriteStatusReg);//
SpiWriteByte(sr);//
SPIFLASH_CS_HIGH();//
}*/
//写W25QXX状态寄存器
//只有SPR,TB,BP2,BP1,BP0(bit 7,5,4,3,2)可以写!!!
static void SPI_FLASH_Write_Enable(void)
{
SPIFLASH_CS_LOW(); //
SpiWriteByte(W25X_WriteEnable); //
SPIFLASH_CS_HIGH(); //
}
/*static void SPI_FLASH_Write_Disable(void)
{
SPIFLASH_CS_LOW();//
SpiWriteByte(W25X_WriteDisable);//
SPIFLASH_CS_HIGH();//
}*/
//读取芯片ID
//返回值如下:
//0XEF13,表示芯片型号为W25Q80
//0XEF14,表示芯片型号为W25Q16
//0XEF15,表示芯片型号为W25Q32
//0XEF16,表示芯片型号为W25Q64
//0XEF17,表示芯片型号为W25Q128
uint16_t W25QXX_Flash_ReadID(void)
{
uint16_t Temp = 0;
SPIFLASH_CS_LOW();
SpiWriteByte(0x90);
SpiWriteByte(0x00);
SpiWriteByte(0x00);
SpiWriteByte(0x00);
Temp |= SpiReadByte() << 8;
Temp |= SpiReadByte();
SPIFLASH_CS_HIGH();
return Temp;
}
//读取SPI FLASH
//在指定地址开始读取指定长度的数据
//pBuffer:数据存储区
//ReadAddr:开始读取的地址(24bit)
//NumByteToRead:要读取的字节数(最大65535)
void SPI_Flash_Read(uint32_t ReadAddr, uint16_t *pBuffer, uint16_t NumByteToRead)
{
uint16_t i;
SPIFLASH_CS_LOW(); //
SpiWriteByte(W25X_ReadData); //
SpiWriteByte((uint8_t)((ReadAddr) >> 16)); //
SpiWriteByte((uint8_t)((ReadAddr) >> 8));
SpiWriteByte((uint8_t)ReadAddr);
for (i = 0; i < NumByteToRead; i++)
{
pBuffer[i] = (SpiReadByte() << 8) & 0xff00; //
pBuffer[i] |= (SpiReadByte() & 0xff);
}
SPIFLASH_CS_HIGH();
}
//SPI在一页(0~65535)内写入少于256个字节的数据
//在指定地址开始写入最大256字节的数据
//pBuffer:数据存储区
//WriteAddr:开始写入的地址(24bit)
//NumByteToWrite:要写入的字节数(最大256),该数不应该超过该页的剩余字节数!!!
void SPI_Flash_Write_Page(uint16_t *pBuffer, uint32_t WriteAddr, uint16_t NumByteToWrite)
{
uint16_t i;
SPI_FLASH_Write_Enable(); //SET WEL
SPIFLASH_CS_LOW(); //
SpiWriteByte(W25X_PageProgram); //
SpiWriteByte((uint8_t)((WriteAddr) >> 16)); //
SpiWriteByte((uint8_t)((WriteAddr) >> 8));
SpiWriteByte((uint8_t)WriteAddr);
for (i = 0; i < NumByteToWrite; i++)
{
SpiWriteByte((pBuffer[i] >> 8) & 0xff); //
SpiWriteByte(pBuffer[i] & 0xff); //
}
SPIFLASH_CS_HIGH(); //
SPI_Flash_Wait_Busy(); //
}
//无检验写SPI FLASH
//必须确保所写的地址范围内的数据全部为0XFF,否则在非0XFF处写入的数据将失败!
//具有自动换页功能
//在指定地址开始写入指定长度的数据,但是要确保地址不越界!
//pBuffer:数据存储区
//WriteAddr:开始写入的地址(24bit)
//NumByteToWrite:要写入的字节数(最大65535)
//CHECK OK
void SPI_Flash_Write_NoCheck(uint16_t *pBuffer, uint32_t WriteAddr, uint16_t NumByteToWrite)
{
uint16_t pageremain;
pageremain = 128 - (WriteAddr % 256) / 2;
if (NumByteToWrite <= pageremain)
pageremain = NumByteToWrite; //
while (1)
{
SPI_Flash_Write_Page(pBuffer, WriteAddr, pageremain);
if (NumByteToWrite == pageremain)
break; //
else //NumByteToWrite>pageremain
{
pBuffer += pageremain;
WriteAddr += pageremain * 2;
NumByteToWrite -= pageremain; //
if (NumByteToWrite > 128)
pageremain = 128; //
else
pageremain = NumByteToWrite; //
}
};
}
//写SPI FLASH
//在指定地址开始写入指定长度的数据
//该函数带擦除操作!
//pBuffer:数据存储区
//WriteAddr:开始写入的地址(24bit)
//NumByteToWrite:要写入的字节数(最大65535)
#define SPIFLASH_SEC_SIZE (4096)
#define SPIFLASH_BASE (0)
#define SPIFLASH_SIZE (128 / 8 * 1024 * 1024) //16MBytes
uint16_t SPI_FLASH_BUFFER[SPIFLASH_SEC_SIZE / 2];
void SPI_Flash_Write(uint32_t WriteAddr, uint16_t *pBuffer, uint16_t NumByteToWrite)
{
uint32_t secpos;
uint16_t secoff;
uint16_t secremain;
uint16_t i;
uint32_t offaddr;
if (WriteAddr >= SPIFLASH_SIZE)
return;
offaddr = WriteAddr - SPIFLASH_BASE;
secpos = offaddr / SPIFLASH_SEC_SIZE; //扇区地址
secoff = (offaddr % SPIFLASH_SEC_SIZE) / 2;//在扇区内的偏移
secremain = SPIFLASH_SEC_SIZE / 2 - secoff;//扇区剩余空间大小
if (NumByteToWrite <= secremain) //不大于4096个字节
secremain = NumByteToWrite;
while (1)
{
// IWDG_ReloadCounter();
SPI_Flash_Read(secpos * SPIFLASH_SEC_SIZE + SPIFLASH_BASE, SPI_FLASH_BUFFER, SPIFLASH_SEC_SIZE / 2);//读出整个扇区的内容
for (i = 0; i < secremain; i++)//校验数据
{
if (SPI_FLASH_BUFFER[secoff + i] != 0XFFFF)
break;
}
if (i < secremain)
{
SPI_Flash_Erase_Sector_addr((secpos * SPIFLASH_SEC_SIZE + SPIFLASH_BASE) / SPIFLASH_SEC_SIZE);
for (i = 0; i < secremain; i++)
{
SPI_FLASH_BUFFER[i + secoff] = pBuffer[i];
}
SPI_Flash_Write_NoCheck(SPI_FLASH_BUFFER, secpos * SPIFLASH_SEC_SIZE + SPIFLASH_BASE, SPIFLASH_SEC_SIZE / 2);
}
else
SPI_Flash_Write_NoCheck(pBuffer, WriteAddr, secremain);
if (NumByteToWrite == secremain)
break;
else
{
secpos++;
secoff = 0;
pBuffer += secremain;
WriteAddr += secremain;
NumByteToWrite -= secremain;
if (NumByteToWrite > (SPIFLASH_SEC_SIZE / 2))
secremain = SPIFLASH_SEC_SIZE / 2;
else
secremain = SPIFLASH_SEC_SIZE;
}
};
}
//擦除整个芯片
//等待时间超长...
void SPI_Flash_Erase_Chip(void) //擦除芯片
{
SPI_FLASH_Write_Enable(); //SET WEL
SPI_Flash_Wait_Busy();
SPIFLASH_CS_LOW(); //
SpiWriteByte(W25X_ChipErase); //
SPIFLASH_CS_HIGH(); //
SPI_Flash_Wait_Busy(); //
}
//擦除一个扇区
//Dst_Addr:扇区地址 根据实际容量设置
//擦除一个扇区的最少时间:150ms
void SPI_Flash_Erase_Sector_addr(uint32_t Dst_Addr) //擦除扇区地址
{
Dst_Addr *= 4096;
SPI_FLASH_Write_Enable(); //SET WEL
SPI_Flash_Wait_Busy();
SPIFLASH_CS_LOW(); //
SpiWriteByte(W25X_SectorErase); //
SpiWriteByte((uint8_t)((Dst_Addr) >> 16)); //
SpiWriteByte((uint8_t)((Dst_Addr) >> 8));
SpiWriteByte((uint8_t)Dst_Addr);
SPIFLASH_CS_HIGH(); //
SPI_Flash_Wait_Busy(); //
}
FLASH_Status SPI_Flash_Erase_Sector(uint32_t Dst_Addr)//擦除扇区
{
//Dst_Addr *= 4096;
SPI_FLASH_Write_Enable(); //SET WEL
SPI_Flash_Wait_Busy();
SPIFLASH_CS_LOW(); //
SpiWriteByte(W25X_SectorErase); //
SpiWriteByte((uint8_t)((Dst_Addr) >> 16)); //
SpiWriteByte((uint8_t)((Dst_Addr) >> 8));
SpiWriteByte((uint8_t)Dst_Addr);
SPIFLASH_CS_HIGH(); //
SPI_Flash_Wait_Busy(); //
return FLASH_COMPLETE;
}
//等待空闲
void SPI_Flash_Wait_Busy(void)
{
uint16_t ii = 0;
while ((W25QXX_Flash_ReadSR() & 0x01) == 0x01)
{
ii++; //
if (ii > 10000)
{
ii = 10001;
}
else
{
delay_ms(1);
// IWDG_ReloadCounter();
}
}
}
//进入掉电模式
void SPI_Flash_PowerDown(void)
{
SPIFLASH_CS_LOW(); //
SpiWriteByte(W25X_PowerDown); //
SPIFLASH_CS_HIGH(); //
delay_us(3); //
}
//唤醒
void SPI_Flash_WAKEUP(void)
{
SPIFLASH_CS_LOW(); //
SpiWriteByte(W25X_ReleasePowerDown); //
SPIFLASH_CS_HIGH(); //
delay_us(3); //
}
详细配置看这位大佬:STM32F10××× PB3,PB4,PA13,PA14,PA15的使用_RZA的博客-CSDN博客文章来源地址https://www.toymoban.com/news/detail-765976.html
到了这里,关于使用stm32的模拟spi读取w25q128读取设备ID时一直出现0xFF的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!