【SPI】软件SPI驱动

软件实现SPI:通过普通GPIO电平实现通信
代码整体框架:
1、建立SPI通信层的.c和.h模块,编写SPI底层的GPIO初始化和3个时序基本单元(起始、终止、交换一个字节 )
2、建立W25Q64的.c和.h模块,基于SPI通信模块实现发送指令、指定地址读、指定地址写
3、在main.c里调用W25Q64模块,初始化、拿到数据、显示数据

【基本驱动代码】

1、完成SPI驱动文件导入操作和编写驱动程序基本代码(参考之前文章)
//将【MySPI】驱动文件放在【Hardware】文件夹中
//【MySPI】名称是为了防止与库函数里面的SPI重复
2、在 MySPI.c中初始化函数 MySPI_Init
//定义函数,对操作端口的库函数进行封装
//SPI初始化
void MySPI_Init(void)
{
    //主机输出引脚配置为【推挽输出】、主机输入引脚配置为【浮空/上拉输入】
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
    
    GPIO_InitTypeDef GPIO_InitStructure;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;//推挽输出
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4 | GPIO_Pin_5 | GPIO_Pin_7;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOA,&GPIO_InitStructure);
    
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;//上拉输入
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOA,&GPIO_InitStructure);
    
    //置引脚默认电平
    MySPI_W_SS(1);//不选中从机
    MySPI_W_SCK(0);//使用SPI模式0,默认时钟为低电平
}
时序单元函数
//写从机选择
void MySPI_W_SS(uint8_t BitValue)
{
    //参考接线,从机选择对应PA4引脚
    GPIO_WriteBit(GPIOA,GPIO_Pin_4,(BitAction)BitValue);
}
//写时钟
void MySPI_W_SCK(uint8_t BitValue)
{
    //参考接线,时钟对应PA5引脚
    GPIO_WriteBit(GPIOA,GPIO_Pin_5,(BitAction)BitValue);
}
//写主机输出从机输入
void MySPI_W_MOSI(uint8_t BitValue)
{
    //参考接线,主机输出从机输入对应PA7引脚
    GPIO_WriteBit(GPIOA,GPIO_Pin_7,(BitAction)BitValue);
}
//读主机输入从机输出
uint8_t MySPI_R_MISO(void)
{
    //参考接线,主机输入从机输出对应PA6引脚
    return GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_6);
}
/**********************
SPI时序逻辑
**********************/
//起始信号
void MySPI_Start(void)
{
    //将SS置低电平
    MySPI_W_SS(0);
}
//终止信号
void MySPI_Stop(void)
{
    //将SS置高电平
    MySPI_W_SS(1);
}
//交换一个字节(函数可读、可写)
//SPI共有四种模式【0、1、2、3】
//W25Q64支持模式【0、3】
uint8_t MySPI_SwapByte(uint8_t ByteSend)
{
    uint8_t ByteReceive = 0x00;
    
    uint8_t i;
    
    for(i = 0;i < 8;i ++)
    {
        //SS下降沿后——主机和从机同时移出数据,但从机移出不需要管理
        MySPI_W_MOSI(ByteSend & ( 0x80 >> i ) );//发送最高位
        
        MySPI_W_SCK(1);//SS产生上升沿——从机自动把MOSI的数据读走,主机则需要读MISO
        if ( MySPI_R_MISO() == 1 )
        {
            ByteReceive |= ( 0x80 >> i );//最高位存在ByteReceive
        }
        
        MySPI_W_SCK(0);//SS产生下降沿
    }
    return ByteReceive;
}
3、在MySPI.h中声明初始化函数 MySPI_Init及其 时序单元函数
void MySPI_Init(void);
void MySPI_Start(void);
void MySPI_Stop(void);
uint8_t MySPI_SwapByte(uint8_t ByteSend);

【W25Q64部分】

1、完成W25Q64驱动文件导入操作和编写驱动程序基本代码(参考之前文章)
//将【W25Q64】驱动文件放在【Hardware】文件夹中
2、在W25Q64.c中初始化函数 W25Q64 _Init 读取ID函数
#include "MySPI.h"
void W25Q64_Init(void)
{
    //调用底层SPI
    MySPI_Init();
}
读取ID函数
//读取ID函数——函数有两个返回值,用【指针】实现多返回值
/*
读取ID号的时序:起始、交换【发送】指令9F、连续交换【接收】3个字节、停止
连续交换接收3个字节——字节1:厂商ID、字节2&3:设备ID
*/
//输出参数1:8位厂商ID
//输出参数2:16位设备ID
void W25Q64_ReadID(uint8_t *MID,uint16_t *DID)
{
    //起始信号——SS低电平
    MySPI_Start();
    
    //目的:交换【发送】指令9F(读取ID)
    MySPI_SwapByte(0x9F);
    //返回值:交换【接收】在此处没有意义——接收什么不重要
    
    //目的:交换【接收】从机返回的ID(字节1:厂商ID)
    *MID = MySPI_SwapByte(0xFF);
    //交换【发送】在此处没有意义——发送什么不重要
    
    //目的:交换【接收】从机返回的ID(字节2:设备ID高8位)
    *DID = MySPI_SwapByte(0xFF);
    //交换【发送】在此处没有意义——发送什么不重要
    
    //将第一次【DID】读到的数据移到高8位
    *DID <<= 8;
    
    //目的:交换【接收】从机返回的ID(字节3:设备ID低8位)
    *DID |= MySPI_SwapByte(0xFF);
    //交换【发送】在此处没有意义——发送什么不重要
    
    //结束信号——SS高电平
    MySPI_Stop();
}
3、在W25Q64.h中声明初始化函数 W25Q64 _Init 读取ID函数
void W25Q64_Init(void);
void W25Q64_ReadID(uint8_t *MID,uint16_t *DID);
4、在主程序main.c中 #include  "W25Q64 .h "
#include "W25Q64.h"
5、在主循环中编写程序主体
//存ID号的变量
uint8_t MID;
uint16_t DID;
int main(void)
{
    OLED_Init();
    W25Q64_Init();
    
    //获取ID号以验证SPI驱动代码是否正确
    W25Q64_ReadID(&MID,&DID);
    
    OLED_ShowHexNum(1,1,MID,2);
    OLED_ShowHexNum(1,8,DID,4);
    
    while(1)
    {
    }
}
实现功能:初始化OLED和W25Q64(包括底层SPI),上电后SPI发送读取ID指令并接收数据,通过OLED显示厂商ID【EF】和设备号【4017】

  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值