视频链接:[1-1] 课程简介_哔哩哔哩_bilibili
STM32F103c8t6引脚定义表:(橙色是电源相关的引脚,蓝色是最小系统相关的引脚。绿色的是IO口和功能引脚,FT意为能容忍5V电压,主功能是上电默认的功能,默认复用功能是IO口上连接的外设功能引脚,加粗的引脚是能直接用的,没加粗的引脚可能要进行其他配置才能使用)
11-1 SPI通信协议
11-2 W25Q64简介
11-3 软件SPI读写W25Q64
无法正常运行,bug待修改
main.c
#include "stm32f10x.h" // Device header
#include "delay.h"
#include "MYSPI.h"
#include "W25Q128.h"
#include "oled.h"
//任务:软件读写Flash芯片 我的芯片为W25Q128
//接线:PB12接CS PB13接SCK PB14接DO PB15接DI
uint8_t MID;
uint16_t DID;
uint8_t ArrayWrite[] = {0x01,0x02,0x03,0x04};
uint8_t ArrayRead[4];
int main(void)
{
OLED_Init();
W25Q128_Init();
OLED_ShowString(1,1,"HELLO");
Delay_ms(500);
OLED_Clear();
W25Q128_ReadID(&MID,&DID);
OLED_ShowString(1,1,"MID: DID:");
OLED_ShowString(2,1,"W:");
OLED_ShowString(3,1,"R:");
OLED_ShowHexNum(1,5,MID,2);
OLED_ShowHexNum(1,12,DID,4);
W25Q128_SectorErase(0x000000);
W25Q128_PageProgram(0x000000,ArrayWrite,4);
W25Q128_ReadData(0x000000,ArrayRead,4);
OLED_ShowHexNum(2,3,ArrayWrite[0],2);
OLED_ShowHexNum(2,6,ArrayWrite[1],2);
OLED_ShowHexNum(2,9,ArrayWrite[2],2);
OLED_ShowHexNum(2,12,ArrayWrite[3],2);
OLED_ShowHexNum(3,3,ArrayRead[0],2);
OLED_ShowHexNum(3,6,ArrayRead[1],2);
OLED_ShowHexNum(3,9,ArrayRead[2],2);
OLED_ShowHexNum(3,12,ArrayRead[3],2);
while (1)
{
}
}
MySPI.c
#include "MySPI.h"
//SPI写CS线
void MySPI_W_CS(uint8_t x)
{
if(x == 0)
{
GPIO_ResetBits(GPIOB,GPIO_Pin_12);
}
else
{
GPIO_SetBits(GPIOB,GPIO_Pin_12);
}
}
//SPI写SCK线
void MySPI_W_SCK(uint8_t x)
{
if(x == 0)
{
GPIO_ResetBits(GPIOB,GPIO_Pin_13);
}
else
{
GPIO_SetBits(GPIOB,GPIO_Pin_13);
}
}
//SPI写输出线
void MySPI_W_MOSI(uint8_t x)
{
if(x == 0)
{
GPIO_ResetBits(GPIOB,GPIO_Pin_15);
}
else
{
GPIO_SetBits(GPIOB,GPIO_Pin_15);
}
}
//SPI读输入线
uint8_t MySPI_R_MISO(void)
{
return GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_14);
}
//软件SPI初始化
void MySPI_Init(void)
{
//开启时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);
//GPIO初始化,PB12接CS PB13接SCK PB14接DO PB15接DI
//输出引脚配置为推挽输出
//输入引脚配置为浮空或上拉输入
GPIO_InitTypeDef My_GPIO_InitStructure;
My_GPIO_InitStructure.GPIO_Pin = GPIO_Pin_12 | GPIO_Pin_13 | GPIO_Pin_15 ;
My_GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
My_GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB,&My_GPIO_InitStructure);
My_GPIO_InitStructure.GPIO_Pin = GPIO_Pin_14 ;
My_GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
My_GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB,&My_GPIO_InitStructure);
MySPI_W_CS(1);
MySPI_W_SCK(0);
}
void MySPI_Start(void)
{
MySPI_W_CS(0);
}
void MySPI_Stop(void)
{
MySPI_W_CS(1);
}
//SPI模式0的读写函数
uint8_t MySPI_WriteReadByte(uint8_t ByteSend)
{
uint8_t i;
uint8_t ByteRecive = 0x00;
for(i = 0;i < 8;i++)
{
MySPI_W_MOSI(ByteSend & (0x80 >> i));
MySPI_W_SCK(1);
if(MySPI_R_MISO() == 1)
{
ByteRecive |= 0x80;
}
MySPI_W_SCK(0);
}
return ByteRecive;
}
//uint8_t MySPI_WriteReadByte(uint8_t ByteSend)
//{
// uint8_t i;
//
//
// for(i = 0;i < 8;i++)
// {
// MySPI_W_MOSI(ByteSend & 0x80);
// ByteSend <<= 1;
// MySPI_W_SCK(1);
// if(MySPI_R_MISO() == 1)
// {
// ByteSend |= 0x01;
// }
// MySPI_W_SCK(0);
// }
// return ByteSend;
//}
W25Q128.c
#include "W25Q128.h"
void W25Q128_Init(void)
{
MySPI_Init();
}
void W25Q128_ReadID(uint8_t *MID,uint16_t *DID)
{
MySPI_Start();
MySPI_WriteReadByte(W25Q128_JEDEC_ID);
*MID = MySPI_WriteReadByte(W25Q128_DUMMY_BYTE);
*DID = MySPI_WriteReadByte(W25Q128_DUMMY_BYTE);
*DID <<= 8;
*DID |= MySPI_WriteReadByte(W25Q128_DUMMY_BYTE);
MySPI_Stop();
}
void W25Q128_WriteEnable(void)
{
MySPI_Start();
MySPI_WriteReadByte(W25Q128_WRITE_ENABLE);
MySPI_Stop();
}
void W25Q128_WaitBusy(void)
{
uint32_t timeout = 100000;
MySPI_Start();
MySPI_WriteReadByte(W25Q128_READ_STATUS_REGISTER_1);
while((MySPI_WriteReadByte(W25Q128_DUMMY_BYTE) & 0x01) == 0x01)
{
timeout--;
if(timeout == 0)
{
break;
}
}
MySPI_Stop();
}
void W25Q128_PageProgram(uint32_t Address,uint8_t *DataArray,uint16_t count)
{
uint16_t i;
W25Q128_WriteEnable();
MySPI_Start();
MySPI_WriteReadByte(W25Q128_PAGE_PROGRAM);
MySPI_WriteReadByte(Address << 16);
MySPI_WriteReadByte(Address << 8);
MySPI_WriteReadByte(Address);
for(i = 0;i < count;i++)
{
MySPI_WriteReadByte(DataArray[i]);
}
MySPI_Stop();
W25Q128_WaitBusy();
}
void W25Q128_SectorErase(uint32_t Address)
{
W25Q128_WriteEnable();
MySPI_Start();
MySPI_WriteReadByte(W25Q128_SECTOR_ERASE_4KB);
MySPI_WriteReadByte(Address << 16);
MySPI_WriteReadByte(Address << 8);
MySPI_WriteReadByte(Address);
MySPI_Stop();
W25Q128_WaitBusy();
}
void W25Q128_ReadData(uint32_t Address,uint8_t *DataArray,uint32_t count)
{
uint32_t i;
MySPI_Start();
MySPI_WriteReadByte(W25Q128_READ_DATA);
MySPI_WriteReadByte(Address << 16);
MySPI_WriteReadByte(Address << 8);
MySPI_WriteReadByte(Address);
for(i = 0;i < count;i++)
{
DataArray[i] = MySPI_WriteReadByte(W25Q128_DUMMY_BYTE);//读取数据的时候芯片内部的指针会自增
}
MySPI_Stop();
}
11-4 SPI通信外设
时序操作步骤:
1.等待TXE标志位为1,发送寄存器为空,若发送寄存器不为空,则不写寄存器
2.软件写入数据至SPI_DR
3.等待RXNE标志位为1,说明发送完成
4.读取DR,从RDR将交换接收的数据读出来
//恢复初始化
void SPI_I2S_DeInit(SPI_TypeDef* SPIx);
//SPI初始化
void SPI_Init(SPI_TypeDef* SPIx, SPI_InitTypeDef* SPI_InitStruct);
//I2S初始化
void I2S_Init(SPI_TypeDef* SPIx, I2S_InitTypeDef* I2S_InitStruct);
//SPI结构体初始化
void SPI_StructInit(SPI_InitTypeDef* SPI_InitStruct);
//I2S结构体初始化
void I2S_StructInit(I2S_InitTypeDef* I2S_InitStruct);
//SPI使能
void SPI_Cmd(SPI_TypeDef* SPIx, FunctionalState NewState);
//I2S使能
void I2S_Cmd(SPI_TypeDef* SPIx, FunctionalState NewState);
//I2S中断配置
void SPI_I2S_ITConfig(SPI_TypeDef* SPIx, uint8_t SPI_I2S_IT, FunctionalState NewState);
//SPI或I2S的MDA使能
void SPI_I2S_DMACmd(SPI_TypeDef* SPIx, uint16_t SPI_I2S_DMAReq, FunctionalState NewState);
//SPI或I2S发送数据
void SPI_I2S_SendData(SPI_TypeDef* SPIx, uint16_t Data);
//SPI或I2S接收数据
uint16_t SPI_I2S_ReceiveData(SPI_TypeDef* SPIx);
//为选定的SPI软件配置内部NSS管脚
void SPI_NSSInternalSoftwareConfig(SPI_TypeDef* SPIx, uint16_t SPI_NSSInternalSoft);
//使能指定的SPI SS输出
void SPI_SSOutputCmd(SPI_TypeDef* SPIx, FunctionalState NewState);
//设置指定的SPI数据大小
void SPI_DataSizeConfig(SPI_TypeDef* SPIx, uint16_t SPI_DataSize);
//发送SPIx的CRC值
void SPI_TransmitCRC(SPI_TypeDef* SPIx);
//使能或使能指定的SPU的传输字CRC值计算
void SPI_CalculateCRC(SPI_TypeDef* SPIx, FunctionalState NewState);
//返回指定SPI的发送或者接受CRC寄存器值
uint16_t SPI_GetCRC(SPI_TypeDef* SPIx, uint8_t SPI_CRC);
//返回指定 SPI 的 CRC 多项式寄存器值
uint16_t SPI_GetCRCPolynomial(SPI_TypeDef* SPIx);
//选择指定 SPI 在双向模式下的数据传输方向
void SPI_BiDirectionalLineConfig(SPI_TypeDef* SPIx, uint16_t SPI_Direction);
//获取SPI或I2S标志位
FlagStatus SPI_I2S_GetFlagStatus(SPI_TypeDef* SPIx, uint16_t SPI_I2S_FLAG);
//清除SPI或I2S标志位
void SPI_I2S_ClearFlag(SPI_TypeDef* SPIx, uint16_t SPI_I2S_FLAG);
//获取SPI或I2S中断标志位
ITStatus SPI_I2S_GetITStatus(SPI_TypeDef* SPIx, uint8_t SPI_I2S_IT);
//清除SPI或I2S中断标志位
void SPI_I2S_ClearITPendingBit(SPI_TypeDef* SPIx, uint8_t SPI_I2S_IT);
11-5 硬件SPI读写W25Q64
配置硬件SPI步骤:
1.开启时钟,开启RCC和GPIO时钟
2.初始化GPIO口,SCK和MOSI是由硬件外设控制的输出信号,配为复用推挽输出;MISO是硬件外设的输入信号,配置为上拉输入;SS引脚是软件控制的输出引脚,配为推挽输出
3.配置SPI外设,即调用SPI_Init()
4.开启SPI,调用SPI_Cmd()
无法正常运行,有bug
main.c
#include "stm32f10x.h" // Device header
#include "OLED.h"
#include "SPI.h"
#include "W25Q128.h"
//任务:硬件SPI读写W25Q128
//接线:硬件SPI1 PA4接CS PA5接CLK PA6接DO PA7接DI
uint8_t MID;
uint16_t DID;
uint8_t ArrayWrite[] = {0x01, 0x02, 0x03, 0x04};
uint8_t ArrayRead[4];
int main(void)
{
OLED_Init();
W25Q128_Init();
OLED_ShowString(1, 1, "MID: DID:");
OLED_ShowString(2, 1, "W:");
OLED_ShowString(3, 1, "R:");
W25Q128_ReadID(&MID, &DID);
OLED_ShowHexNum(1, 5, MID, 2);
OLED_ShowHexNum(1, 12, DID, 4);
W25Q128_SectorErase(0x000000);
W25Q128_PageProgram(0x000000, ArrayWrite, 4);
W25Q128_ReadData(0x000000, ArrayRead, 4);
OLED_ShowHexNum(2, 3, ArrayWrite[0], 2);
OLED_ShowHexNum(2, 6, ArrayWrite[1], 2);
OLED_ShowHexNum(2, 9, ArrayWrite[2], 2);
OLED_ShowHexNum(2, 12, ArrayWrite[3], 2);
OLED_ShowHexNum(3, 3, ArrayRead[0], 2);
OLED_ShowHexNum(3, 6, ArrayRead[1], 2);
OLED_ShowHexNum(3, 9, ArrayRead[2], 2);
OLED_ShowHexNum(3, 12, ArrayRead[3], 2);
while (1)
{
}
}
SPI.c
#include "SPI.h"
void My_SPI_W_CS(uint8_t BitValue)
{
GPIO_WriteBit(GPIOA,GPIO_Pin_4,(BitAction)BitValue);
}
//初始化的是SPI2
void My_SPI_Init(void)
{
//开启时钟
RCC_APB1PeriphClockCmd(RCC_APB1Periph_SPI2,ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);
//初始化GPIO口:PB12接CS PB13接CLK PB14接SO PB15接SI
//SCK和MOSI是由硬件外设控制的输出信号,配为复用推挽输出;
//MISO是硬件外设的输入信号,配置为上拉输入;
//SS引脚是软件控制的输出引脚,配为推挽输出
GPIO_InitTypeDef My_GPIO_InitStructure;
My_GPIO_InitStructure.GPIO_Pin = GPIO_Pin_13 | GPIO_Pin_15; //CLK和MOSI
My_GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
My_GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB,&My_GPIO_InitStructure);
My_GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4; //MISO
My_GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
My_GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB,&My_GPIO_InitStructure);
My_GPIO_InitStructure.GPIO_Pin = GPIO_Pin_12; //CS
My_GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
My_GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB,&My_GPIO_InitStructure);
//配置硬件SPI
SPI_InitTypeDef My_SPI_InitStructure;
My_SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_2;//时钟分频值
My_SPI_InitStructure.SPI_CPHA = SPI_CPHA_1Edge; //时钟相位
My_SPI_InitStructure.SPI_CPOL = SPI_CPOL_Low; //时钟极性
My_SPI_InitStructure.SPI_CRCPolynomial = 7; //计算CRC,给默认值7
My_SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b;//数据大小
My_SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex; //选择单向或双向传输方式
My_SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB;//指定先行高位还是低位
My_SPI_InitStructure.SPI_Mode = SPI_Mode_Master;//指定SPI模式,主机模式
My_SPI_InitStructure.SPI_NSS = SPI_NSS_Soft; //指定NSS信号
SPI_Init(SPI1,&My_SPI_InitStructure);
//开启SPI
SPI_Cmd(SPI1,ENABLE);
My_SPI_W_CS(1);
}
//SPI开始信号
void My_SPI_Start(void)
{
My_SPI_W_CS(0);
}
//SPI结束信号
void My_SPI_Stop(void)
{
My_SPI_W_CS(1);
}
//SPI交换数据
uint8_t My_SPI_SwapByte(uint8_t ByteSend)
{
uint8_t ReciveByte;
uint32_t TimeOut;
TimeOut = 100000;
while(SPI_I2S_GetFlagStatus(SPI1,SPI_I2S_FLAG_TXE) != SET) //等待TXE为1
{
TimeOut--;
if(TimeOut == 0) //防止超时卡死
{
TimeOut = 100000;
break;
}
}
SPI_I2S_SendData(SPI1,ByteSend); //软件写入数据至SPI_DR
while(SPI_I2S_GetFlagStatus(SPI1,SPI_I2S_FLAG_RXNE) != SET) //等待RXNE为1
{
TimeOut--;
if(TimeOut == 0) //防止超时卡死
{
TimeOut = 100000;
break;
}
}
ReciveByte = SPI_I2S_ReceiveData(SPI1); //读取DR,将交换接收的数据读回
return ReciveByte;
}
W25Q128.c
#include "W25Q128.h"
void W25Q128_Init(void)
{
My_SPI_Init();
}
void W25Q128_ReadID(uint8_t *MID, uint16_t *DID)
{
My_SPI_Start();
My_SPI_SwapByte(W25Q128_JEDEC_ID);
*MID = My_SPI_SwapByte(W25Q128_DUMMY_BYTE);
*DID = My_SPI_SwapByte(W25Q128_DUMMY_BYTE);
*DID <<= 8;
*DID |= My_SPI_SwapByte(W25Q128_DUMMY_BYTE);
My_SPI_Stop();
}
void W25Q128_WriteEnable(void)
{
My_SPI_Start();
My_SPI_SwapByte(W25Q128_WRITE_ENABLE);
My_SPI_Stop();
}
void W25Q128_WaitBusy(void)
{
uint32_t Timeout;
My_SPI_Start();
My_SPI_SwapByte(W25Q128_READ_STATUS_REGISTER_1);
Timeout = 100000;
while ((My_SPI_SwapByte(W25Q128_DUMMY_BYTE) & 0x01) == 0x01)
{
Timeout --;
if (Timeout == 0)
{
break;
}
}
My_SPI_Stop();
}
void W25Q128_PageProgram(uint32_t Address, uint8_t *DataArray, uint16_t Count)
{
uint16_t i;
W25Q128_WriteEnable();
My_SPI_Start();
My_SPI_SwapByte(W25Q128_PAGE_PROGRAM);
My_SPI_SwapByte(Address >> 16);
My_SPI_SwapByte(Address >> 8);
My_SPI_SwapByte(Address);
for (i = 0; i < Count; i ++)
{
My_SPI_SwapByte(DataArray[i]);
}
My_SPI_Stop();
W25Q128_WaitBusy();
}
void W25Q128_SectorErase(uint32_t Address)
{
W25Q128_WriteEnable();
My_SPI_Start();
My_SPI_SwapByte(W25Q128_SECTOR_ERASE_4KB);
My_SPI_SwapByte(Address >> 16);
My_SPI_SwapByte(Address >> 8);
My_SPI_SwapByte(Address);
My_SPI_Stop();
W25Q128_WaitBusy();
}
void W25Q128_ReadData(uint32_t Address, uint8_t *DataArray, uint32_t Count)
{
uint32_t i;
My_SPI_Start();
My_SPI_SwapByte(W25Q128_READ_DATA);
My_SPI_SwapByte(Address >> 16);
My_SPI_SwapByte(Address >> 8);
My_SPI_SwapByte(Address);
for (i = 0; i < Count; i ++)
{
DataArray[i] = My_SPI_SwapByte(W25Q128_DUMMY_BYTE);
}
My_SPI_Stop();
}