#第十一章 SPI# 基于江科大的stm32单片机学习笔记

 视频链接:[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();
}

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值