前面说了STM32的I2C,作为STM32的另外一个串行接口就不得不提到了——SPI。与I2C差不多,同样有硬件接口,有库函数支持,我们要做的就是结合SPI接口芯片调用库函数,就能实现SPI驱动了。一切看代码,你会懂的,注释非常详细的,很适合初学者。代码能够直接用到实际项目工程里面去的。SPI芯片选用W25X系列。。。
演示效果使用超级终端或者SecureCRT 5.5(这货真的不错)
工程结构图:
1、工程里面的beep.c led.c usart1.c 与《STM32 基于库函数控制按键&nb… 》《STM32 串口例程之查询收》里面完全一样,这里就不在上代码。
2、main.c
//程序功能:主要用于测试SPI W25X16驱动是否正常
#include"stm32f10x.h"
#include"user_usart1.h"
#include"user_led.h"
#include"user_beep.h"
#include"user_spi_w25x16.h"
#include
#define FLASH_ID1 0xef3015
#define FLASH_ID2 0xef4015
#define FLASH_WriteAddress 0x000000
#define FLASH_ReadAddress FLASH_WriteAddress
#define FLASH_SectorErase FLASH_WriteAddress
#define CountOf(a) (sizeof(a)/sizeof(*(a)))
#define DataSize (CountOf(TxDataTable)-1)
u8 TxDataTable[]="
Hello,I am wgchnln,我 爱 ARM,I will persist in learning ARM,坚决不放弃
";
u8 RxDataTable[DataSize];
//=============================================
#ifdef __GNUC__
#define PUTCHAR_PROTOTYPE int __io_putchar(int ch)
#else
#define PUTCHAR_PROTOTYPE int fputc(int ch, FILE *f)
#endif
//=============================================
TestStatus User_SPI_DataCompare(u8 *TxData,u8 *RxData,u8 Num)
{
while(Num--)
{
if(*TxData!=*RxData)
{
return Failed;
}
RxData++;
TxData++;
}
return Successed;
}
void User_SPI_Test(void)
{
vu32 ID,DeviceID;
u8 Buffer;
volatile TestStatus Test1=Failed;
volatile TestStatus Test2=Successed; //存放两次测试的结果
printf("
正在努力读取ID号.....");
User_LedSpark(Led1,2);
ID=User_SPI_W25X16_ReadID();
printf("flashID:%x
",ID);
printf("
正在努力读取DeviceID号......");
User_LedSpark(Led1,2);
DeviceID=User_SPI_W25X16_ReadDeviceID();
printf("flashDeviceID:%x
",DeviceID);
if((ID==FLASH_ID1)||(ID==FLASH_ID2))
{
printf("
ARM在芯片擦除......");
User_LedSpark(Led1,2);
User_SPI_W25X16_SectorErase(FLASH_SectorErase);
printf("完成
");
printf("
你要写入的数据:%s
",TxDataTable);
printf("
努力为你芯片写入中......");
User_LedSpark(Led1,2);
User_SPI_W25X16_ChipWrite(TxDataTable,FLASH_WriteAddress,DataSize);
printf("完成
");
printf("
芯片数据读取......");
User_LedSpark(Led1,2);
User_SPI_W25X16_ChipRead(RxDataTable,FLASH_ReadAddress,DataSize);
printf("完成
");
User_LedSpark(Led1,2);
printf("
为您读取的数据:%s
",RxDataTable);
printf("
为您做数据比较中......");
User_LedSpark(Led1,2);
Test1=User_SPI_DataCompare(RxDataTable,TxDataTable,DataSize);
if(Test1==Successed)
{
printf("数据相同
");
}
else
{
printf("数据不相同
");
User_LedSpark(Led2,2);
printf("为您分析可能原因:数据未写入、读取错误、或者两者同时存在
");
}
printf("
再一次芯片擦除......");
User_LedSpark(Led1,2);
User_SPI_W25X16_SectorErase(FLASH_SectorErase);
printf("完成
");
printf("
又一次芯片读取......");
User_LedSpark(Led1,2);
User_SPI_W25X16_ChipRead(RxDataTable,FLASH_ReadAddress,DataSize);
printf("完成
");
printf("
判断是否擦除掉......");
User_LedSpark(Led1,1);
for(Buffer=0;Buffer
{
if(RxDataTable[Buffer]!=0xff)
{
Test2=Failed;
}
}
if(Test2==Failed)
{
printf("失败
");
printf("为您分析的可能原因:读取错误、擦除失败、或者两者同时存在
");
}
else
{
printf("擦除OK
");
}
printf("
------------为您展示此次测试结果------------
");
if((Test1==Successed)&&((Test2==Successed)))
{
User_LedSpark(Led1,2);
printf("
恭喜你,SPI W25X16驱动测试通过啦
");
}
else
{
User_LedSpark(Led2,2);
printf("
糟糕,SPI功能演示失败了......原因可能是读写数据不一致、软件擦除失败
");
}
}
else
{
User_LedSpark(Led2,2);
printf("
悲剧了,SPI功能演示失败啦.....原因是芯片ID号码读取出错哦
");
}
}
int main(void)
{
User_USART1Config();
printf("
串口1配置......");
printf("完成
");
printf("
蜂鸣器初始化...");
User_BeepConfig();
printf("
蜂鸣器测试......");
User_BeepStatus(BeepStatus_TurnOn);
printf("完成
");
printf("
LED初始化...");
User_LedConfig();
printf("
LED测试......");
User_LedSpark(Led0,2);
printf("完成
");
printf("
SPI初始化...");
User_SPI_Config();
User_LedSpark(Led0,2);
printf("完成
");
User_SPI_Test();
while(1);
}
//==================================================
PUTCHAR_PROTOTYPE
{
USART_SendData(USART1, (uint8_t) ch);
while (USART_GetFlagStatus(USART1, USART_FLAG_TC) == RESET)
{}
return ch;
}
//==================================================
3、user_spi_w25x16.c
//program function:SPI1 Init && FLASF Chip W25X16 driver
#include"stm32f10x.h"
#include"user_spi_w25x16.h"
#include
#define WriteEnable 0x06
#define WriteDisable 0x04
#define ReadStatusRegister 0x05
#define WriteStatusRegister 0x01
#define ReadData 0x03
#define FastRead 0x0b
#define FsatReadDualOutput 0x3b
#define PageProgram 0x02
#define BlukErase 0xd8
#define SectorErase 0x20
#define ChipErase 0xc7
#define PowerDown 0xb9
#define WakeUp 0xab
#define DeviceID 0xab
#define ManufatureID 0x90
#define JedecID 0x9f
#define JudgeCode 0x01 //用于判断通讯是否结束用
#define NoneCode 0xa5 //无意义的指令,用于:接收数据时,发送这个质量来产生接收时候的时钟
void User_SPI_Config(void)
{
SPI_InitTypeDef SPI_InitStructure;
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(SPI_24G_CS_Clock,ENABLE);
GPIO_InitStructure.GPIO_Pin =SPI_24G_CS_Pin;
GPIO_InitStructure.GPIO_Mode =GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Speed =GPIO_Speed_50MHz;
GPIO_Init(SPI_24G_CS_Port,&GPIO_InitStructure);
GPIO_SetBits(SPI_24G_CS_Port,SPI_24G_CS_Pin);
RCC_APB2PeriphClockCmd(SPI_VS1003B_CS_Clock,ENABLE);
GPIO_InitStructure.GPIO_Pin =SPI_VS1003B_CS_Pin;
GPIO_InitStructure.GPIO_Mode =GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Speed =GPIO_Speed_50MHz;
GPIO_Init(SPI_VS1003B_CS_Port,&GPIO_InitStructure);
GPIO_SetBits(SPI_VS1003B_CS_Port,SPI_VS1003B_CS_Pin);
RCC_APB2PeriphClockCmd(SPI_W25X16_Clock,ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);
RCC_APB2PeriphClockCmd(SPI_W25X16_CS_Clock,ENABLE);
GPIO_InitStructure.GPIO_Pin =SPI_W25X16_CS_Pin;
GPIO_InitStructure.GPIO_Mode =GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Speed =GPIO_Speed_50MHz;
GPIO_Init(SPI_W25X16_CS_Port,&GPIO_InitStructure);
SPI_W25X16_CS_DisSelect;
RCC_APB2PeriphClockCmd(SPI_W25X16_SCK_Clock,ENABLE);
GPIO_InitStructure.GPIO_Pin =SPI_W25X16_SCK_Pin;
GPIO_InitStructure.GPIO_Mode =GPIO_Mode_AF_PP;
GPIO_InitStructure.GPIO_Speed =GPIO_Speed_50MHz;
GPIO_Init(SPI_W25X16_SCK_Port,&GPIO_InitStructure);
RCC_APB2PeriphClockCmd(SPI_W25X16_MISO_Clock,ENABLE);
GPIO_InitStructure.GPIO_Pin =SPI_W25X16_MISO_Pin;
GPIO_Init(SPI_W25X16_MISO_Port,&GPIO_InitStructure);
RCC_APB2PeriphClockCmd(SPI_W25X16_MOSI_Clock,ENABLE);
GPIO_InitStructure.GPIO_Pin =SPI_W25X16_MOSI_Pin;
GPIO_Init(SPI_W25X16_MOSI_Port,&GPIO_InitStructure);
SPI_InitStructure.SPI_Direction =SPI_Direction_2Lines_FullDuplex; //通讯模式:双向全双工模式
SPI_InitStructure.SPI_Mode =SPI_Mode_Master; //主从:主模式
SPI_InitStructure.SPI_DataSize =SPI_DataSize_8b; //数据帧长度:8bits
SPI_InitStructure.SPI_CPOL =SPI_CPOL_High; //空闲时置高
SPI_InitStructure.SPI_CPHA =SPI_CPHA_2Edge; //第二个时钟采样
SPI_InitStructure.SPI_NSS =SPI_NSS_Soft; //NSS控制选择:软件控制
SPI_InitStructure.SPI_BaudRatePrescaler =SPI_BaudRatePrescaler_2; //波特率分配系数:2分频
SPI_InitStructure.SPI_FirstBit =SPI_FirstBit_MSB; //数据帧格式:MSB在前
SPI_InitStructure.SPI_CRCPolynomial =7; //CRC效验多项式
SPI_Init(SPI1,&SPI_InitStructure);
SPI_Cmd(SPI1,ENABLE);
}
u8 User_SPI_W25X16_SendByte(u8 SendByteData)
{
u8 ReceiveData;
while(SPI_I2S_GetFlagStatus(SPI1,SPI_I2S_FLAG_TXE)!=SET);
SPI_I2S_SendData(SPI1,SendByteData);
while(SPI_I2S_GetFlagStatus(SPI1,SPI_I2S_FLAG_RXNE)!=SET);
ReceiveData=SPI_I2S_ReceiveData(SPI1);
return ReceiveData;
}
u8 User_SPI_W25X16_ReadByte(void)
{
u8 ReceiveData;
ReceiveData=User_SPI_W25X16_SendByte(NoneCode);
return ReceiveData;
}
vu16 User_SPI_W25X16_SendHalfWord(u16 HalfWord)
{
vu16 ReceiveData;
while(SPI_I2S_GetFlagStatus(SPI1,SPI_I2S_FLAG_TXE)!=SET);
SPI_I2S_SendData(SPI1,HalfWord);
while(SPI_I2S_GetFlagStatus(SPI1,SPI_I2S_FLAG_RXNE)!=SET);
ReceiveData=SPI_I2S_ReceiveData(SPI1);
return ReceiveData;
}
void User_SPI_W25X16_WriteEnable(void)
{
SPI_W25X16_CS_Select;
User_SPI_W25X16_SendByte(WriteEnable);
SPI_W25X16_CS_DisSelect;
}
[page]
void User_SPI_W25X16_WaitForWriteEnd(void)
{
u8 ReceiveStatus; //用于存放W25X16返回的状态非零就是操作结束
SPI_W25X16_CS_Select;
User_SPI_W25X16_SendByte(ReadStatusRegister);
do
{
ReceiveStatus=User_SPI_W25X16_SendByte(NoneCode);
}
while(ReceiveStatus & JudgeCode==SET);
SPI_W25X16_CS_DisSelect;
}
void User_SPI_W25X16_SectorErase(vu32 SectorAddress)
{
User_SPI_W25X16_WriteEnable();
SPI_W25X16_CS_Select;
User_SPI_W25X16_SendByte(SectorErase);
User_SPI_W25X16_SendByte((SectorAddress & 0xff0000)>>16); //发送最高8位
User_SPI_W25X16_SendByte((SectorAddress & 0xff00)>>8); // 中间8位
User_SPI_W25X16_SendByte(SectorAddress & 0xff); //发送最低8位
SPI_W25X16_CS_DisSelect;
User_SPI_W25X16_WaitForWriteEnd();
}
void User_SPI_W25X16_BulkErase(void)
{
User_SPI_W25X16_WriteEnable();
SPI_W25X16_CS_Select;
User_SPI_W25X16_SendByte(ChipErase);
SPI_W25X16_CS_DisSelect;
User_SPI_W25X16_WaitForWriteEnd();
}
void User_SPI_W25X16_PageWrite(u8 *DataTable,vu32 WriteAddress,vu16 NumberOfWrite)
{
User_SPI_W25X16_WriteEnable();
SPI_W25X16_CS_Select;
User_SPI_W25X16_SendByte(PageProgram);
User_SPI_W25X16_SendByte((WriteAddress & 0xff0000)>>16); //最高8位地址
User_SPI_W25X16_SendByte((WriteAddress & 0xff00)>>8); //中间8位地址
User_SPI_W25X16_SendByte(WriteAddress & 0xff); //最低8位地址
if(NumberOfWrite > SPI_W25X16_PerPageWriteSize) //W25X16采用的是页写入方式,最多一次性写入256个数据,然后内部地址指针归零
{
NumberOfWrite=SPI_W25X16_PerPageWriteSize;
printf("
哦偶,一次性写入的数据太多,不能超过256的啦,ARM将为你写入前256个数据
");
}
while(NumberOfWrite--)
{
User_SPI_W25X16_SendByte(*DataTable);
DataTable++; //数组指针 +1
}
SPI_W25X16_CS_DisSelect;
User_SPI_W25X16_WaitForWriteEnd();
}
void User_SPI_W25X16_ChipWrite(u8 *DataTable,vu32 WriteAddress,vu16 NumberOfWrite)
{
u8 AddressRemainder =0;
u8 NumberOfPage =0;
u8 Count =0; //存放地址所在页需要写入的最多数据
u8 NumberOfSingle =0; //写入数据的最后一些需要写入的数据个数
u8 Buffer =0; //保留
AddressRemainder =WriteAddress % SPI_W25X16_PageSize;
Count =SPI_W25X16_PageSize - AddressRemainder;
NumberOfPage =NumberOfWrite / SPI_W25X16_PageSize;
NumberOfSingle =NumberOfWrite % SPI_W25X16_PageSize;
if(AddressRemainder==0)
{
if(NumberOfPage==0) //NumberOfWrite < SPI_W25X16_PageSize
{
User_SPI_W25X16_PageWrite(DataTable,WriteAddress,NumberOfWrite);
}
else
{
while(NumberOfPage--)
{
User_SPI_W25X16_PageWrite(DataTable,WriteAddress,SPI_W25X16_PageSize); //一次性写入256个
DataTable+=SPI_W25X16_PageSize; //接着写下一个256数据
WriteAddress+=SPI_W25X16_PageSize; //地址就移到下一页(256为单位)
}
User_SPI_W25X16_PageWrite(DataTable,WriteAddress,NumberOfSingle);
}
}
else
{
if(NumberOfPage==0)
{
if(NumberOfWrite < Count)
{
User_SPI_W25X16_PageWrite(DataTable,WriteAddress,NumberOfWrite);
}
else
{
User_SPI_W25X16_PageWrite(DataTable,WriteAddress,Count); //起始地址所在页只需要写入Count个
Buffer=NumberOfWrite-Count; //计算出下一页要写入的数据数量
DataTable+=Count;
WriteAddress+=Count;
User_SPI_W25X16_PageWrite(DataTable,WriteAddress,Buffer);
}
}
else
{
User_SPI_W25X16_PageWrite(DataTable,WriteAddress,Count);
//之后需要重新计算以下参数 原因:数据量超过一页分两种情况:1、数据量不会覆盖完起始地址下一页;2、数据量会完全覆盖起始地址下一页
//重新计算就是看是否会覆盖掉下一页,如果会就进入while()循环,否则就不进入
DataTable+=Count;
WriteAddress+=Count;
NumberOfWrite-=Count;
NumberOfPage=NumberOfWrite / SPI_W25X16_PageSize;
NumberOfSingle=NumberOfWrite % SPI_W25X16_PageSize;
while(NumberOfPage--)
{
User_SPI_W25X16_PageWrite(DataTable,WriteAddress,SPI_W25X16_PageSize);
DataTable+=SPI_W25X16_PageSize;
WriteAddress+=SPI_W25X16_PageSize;
}
if(NumberOfSingle != 0)
{
User_SPI_W25X16_PageWrite(DataTable,WriteAddress,NumberOfSingle);
}
}
}
}
void User_SPI_W25X16_ChipRead(u8 *DataTable,vu32 ReadAddress,vu16 NumberOfRead)
{
//虽然要发送地址,但是不需要说明有个写操作(I2C才是这样的时序)
SPI_W25X16_CS_Select;
User_SPI_W25X16_SendByte(ReadData);
User_SPI_W25X16_SendByte((ReadAddress & 0xff0000) >> 16); //最高8位地址
User_SPI_W25X16_SendByte((ReadAddress & 0xff00) >> 8); //中间8位
User_SPI_W25X16_SendByte(ReadAddress & 0xff); //最低8位
while(NumberOfRead--)
{
*DataTable = User_SPI_W25X16_SendByte(NoneCode);
DataTable++;
}
SPI_W25X16_CS_DisSelect;
}
vu32 User_SPI_W25X16_ReadID(void)
{
vu32 ID =0;
vu32 IDBuffer1 =0;
vu32 IDBuffer2 =0;
vu32 IDBuffer3 =0;
SPI_W25X16_CS_Select;
User_SPI_W25X16_SendByte(JedecID);
IDBuffer1 =User_SPI_W25X16_SendByte(NoneCode); //读取高位
IDBuffer2 =User_SPI_W25X16_SendByte(NoneCode); //读取中位
IDBuffer3 =User_SPI_W25X16_SendByte(NoneCode); //读取低位
SPI_W25X16_CS_DisSelect;
ID=(IDBuffer1<<16)|(IDBuffer2<<8)|IDBuffer3;
return ID;
}
vu32 User_SPI_W25X16_ReadDeviceID(void)
{
vu32 ID =0;
vu32 IDBuffer1 =0;
vu32 IDBuffer2 =0;
vu32 IDBuffer3 =0;
SPI_W25X16_CS_Select;
User_SPI_W25X16_SendByte(ManufatureID);
IDBuffer1 =User_SPI_W25X16_SendByte(NoneCode); //读取高位
IDBuffer2 =User_SPI_W25X16_SendByte(NoneCode); //读取中位
IDBuffer3 =User_SPI_W25X16_SendByte(NoneCode); //读取低位
SPI_W25X16_CS_DisSelect;
ID=(IDBuffer1<<16)|(IDBuffer2<<8)|IDBuffer3;
return ID;
}
void User_SPI_W25X16_StartReadSequence(vu32 ReadAddress)
{
SPI_W25X16_CS_Select;
User_SPI_W25X16_SendByte(ReadData);
User_SPI_W25X16_SendByte((ReadAddress & 0xff0000) >> 16); //最高8位地址
User_SPI_W25X16_SendByte((ReadAddress & 0xff00) >> 8); //中间8位
User_SPI_W25X16_SendByte(ReadAddress & 0xff); //最低8位
//SPI_W25X16_DisSelect;
}
void User_SPI_W25X16_PowerDown(void)
{
SPI_W25X16_CS_Select;
User_SPI_W25X16_SendByte(PowerDown);
SPI_W25X16_CS_DisSelect;
}
void User_SPI_W25X16_WakeUp(void)
{
SPI_W25X16_CS_Select;
User_SPI_W25X16_SendByte(WakeUp);
SPI_W25X16_CS_DisSelect;
}
user_spi_w25x16.h
//SPI FLASH chip W25X16 头文件
#ifndef _USER_SPI_W25X16_H
#define _USER_SPI_W25X16_H
#include"stm32f10x.h"
typedef enum{Failed=0,Successed=!Failed}TestStatus; //声明定义枚举型变量 用于表示测试失败与成功
#define SPI_W25X16_SectorSize 4096
#define SPI_W25X16_PageSize 256 //W25X16每页数据长度
#define SPI_W25X16_PerPageWriteSize 256 //每页最多写入的数据个数
#define SPI_W25X16 SPI1
#define SPI_W25X16_Clock RCC_APB2Periph_SPI1
#define SPI_W25X16_CS_Clock RCC_APB2Periph_GPIOC
#define SPI_W25X16_CS_Port GPIOC
#define SPI_W25X16_CS_Pin GPIO_Pin_4
#define SPI_W25X16_SCK_Clock RCC_APB2Periph_GPIOA
#define SPI_W25X16_SCK_Port GPIOA
#define SPI_W25X16_SCK_Pin GPIO_Pin_5
#define SPI_W25X16_MISO_Clock RCC_APB2Periph_GPIOA
#define SPI_W25X16_MISO_Port GPIOA
#define SPI_W25X16_MISO_Pin GPIO_Pin_6
#define SPI_W25X16_MOSI_Clock RCC_APB2Periph_GPIOA
#define SPI_W25X16_MOSI_Port GPIOA
#define SPI_W25X16_MOSI_Pin GPIO_Pin_7
#define SPI_24G_CS_Clock RCC_APB2Periph_GPIOB
#define SPI_24G_CS_Port GPIOB
#define SPI_24G_CS_Pin GPIO_Pin_2
#define SPI_VS1003B_CS_Clock RCC_APB2Periph_GPIOB
#define SPI_VS1003B_CS_Port GPIOB
#define SPI_VS1003B_CS_Pin GPIO_Pin_0
#define SPI_W25X16_CS_Select GPIO_ResetBits(SPI_W25X16_CS_Port,SPI_W25X16_CS_Pin)
#define SPI_W25X16_CS_DisSelect GPIO_SetBits(SPI_W25X16_CS_Port,SPI_W25X16_CS_Pin)
u8 User_SPI_W25X16_SendByte(u8 SendByteData) ; //send data that is byte
u8 User_SPI_W25X16_ReadByte(void); //Read ByteData from chip W25X16
vu16 User_SPI_W25X16_SendHalfWord(u16 HalfWord) ; //send data ,is halfword
void User_SPI_W25X16_WriteEnable(void); //write enable for W25X16
void User_SPI_W25X16_WaitForWriteEnd(void); //wait the end about write for chip W25X16
void User_SPI_Config(void); //SPI1 init configuration
void User_SPI_W25X16_SectorErase(vu32 SectorAddress);
void User_SPI_W25X16_BulkErase(void); //erase the W25X16
void User_SPI_W25X16_PageWrite(u8 *DataTable,vu32 WriteAddress,vu16 NumberOfWrite);
void User_SPI_W25X16_ChipWrite(u8 *DataTable,vu32 WriteAddress,vu16 NumberOfWrite);
void User_SPI_W25X16_ChipRead(u8 *DataTable,vu32 ReadAddress,vu16 NumberOfRead);
vu32 User_SPI_W25X16_ReadID(void); //read chip ID
vu32 User_SPI_W25X16_ReadDeviceID(void); //read manufacture device ID
void User_SPI_W25X16_StartReadSequence(vu32 ReadAddress);
void User_SPI_W25X16_PowerDown(void);
void User_SPI_W25X16_WakeUp(void);
#endif
以上,结束。