前提知识
1.stm32学习记录-7.iic驱动0.96寸oled:https://blog.csdn.net/qq_45417579/article/details/135832938?spm=1001.2014.3001.5501
1.背景知识
1.1spi通信介绍
SPI的数据通信其实是数据交换,两个输入输出线和主从设备的数据寄存器构成一个回路,每一次数据位移出和移入通过边缘触发(移入移出分开进行的,完成一位数据交换需要两个边沿)。
1.1.1硬件电路
1.1.2关键参数说明
1.移位寄存器(spi通信中设备输入输出共用一个寄存器)
2.起始条件:ss从高电平变为低电平
3.终止条件:ss从低电平变为高电平
4.交换一个字节
1.1.3spi基本特点

1.1.4通信流程
1.2 w25q4
一种低成本、小型化、使用简单的易失性存储器。掉电数据不丢失。
引脚说明
2.配置流程
2.1 spi配置流程
3.使用流程
硬件连接
w25q64-CS->PA2;
w25q64-SCK->PA5;
w25q64-SO->PA6;
w25q64-SI->PA7;
oled-SCL->PB8
oled-SDA->PB9
oled-3V3->3V3
oled-GND->GND
3.1spi使用流程
就是通信流程,根据不同从机发送对应指令码,然后读取(交换)数据。
3.2.w25q64使用流程
指令具体说明查看手册
4.代码
spi.c
#include "spi.h"
//SPI引脚初始化
void spiInit()
{
GPIO_InitTypeDef GPIO_InitStructure;//创建GPIO初始化结构体
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);//使能引脚对应时钟
GPIO_InitStructure.GPIO_Mode=GPIO_Mode_Out_PP;//输出引脚配置为推挽输出
GPIO_InitStructure.GPIO_Pin=GPIO_Pin_2|GPIO_Pin_5|GPIO_Pin_7;//PA2-ss,PA5-SCL,PA7-MOSI
GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;//频率50mhz
GPIO_Init(GPIOA,&GPIO_InitStructure);//初始化端口
GPIO_InitStructure.GPIO_Mode=GPIO_Mode_IN_FLOATING;//输入引脚配置为浮空输入
GPIO_InitStructure.GPIO_Pin=GPIO_Pin_6;//PA6-MISO
GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;//频率50mhz
GPIO_Init(GPIOA,&GPIO_InitStructure);//初始化端口
GPIO_SetBits(GPIOA,GPIO_Pin_2);//初始时SS引脚为高,不使能
GPIO_ResetBits(GPIOA,GPIO_Pin_5);//模式0,初始时SCL为低电平
}
/**
* 函 数:SPI写SS引脚电平
* 参 数:BitValue 协议层传入的当前需要写入SS的电平,范围0~1
* 返 回 值:无
* 注意事项:此函数需要用户实现内容,当BitValue为0时,需要置SS为低电平,当BitValue为1时,需要置SS为高电平
*/
void SPI_W_SS(uint8_t BitValue)
{
GPIO_WriteBit(GPIOA,GPIO_Pin_2,BitValue);//写BitValue给PA2
}
/**
* 函 数:SPI写SCK引脚电平
* 参 数:BitValue 协议层传入的当前需要写入SCK的电平,范围0~1
* 返 回 值:无
* 注意事项:此函数需要用户实现内容,当BitValue为0时,需要置SCK为低电平,当BitValue为1时,需要置SCK为高电平
*/
void SPI_W_SCL(uint8_t BitValue)
{
GPIO_WriteBit(GPIOA,GPIO_Pin_5,BitValue);//写BitValue给PA5
}
/**
* 函 数:SPI写MOSI引脚电平
* 参 数:BitValue 协议层传入的当前需要写入MOSI的电平,范围0~0xFF
* 返 回 值:无
* 注意事项:此函数需要用户实现内容,当BitValue为0时,需要置MOSI为低电平,当BitValue非0时,需要置MOSI为高电平
*/
void SPI_W_MOSI(uint8_t BitValue)
{
GPIO_WriteBit(GPIOA,GPIO_Pin_7,BitValue);//写BitValue给PA7
}
/**
* 函 数:I2C读MISO引脚电平
* 参 数:无
* 返 回 值:协议层需要得到的当前MISO的电平,范围0~1
* 注意事项:此函数需要用户实现内容,当前MISO为低电平时,返回0,当前MISO为高电平时,返回1
*/
u8 SPI_R_MISO(void)
{
return GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_6);//读PA6数据
}
/**
* 函 数:SPI起始
* 参 数:无
* 返 回 值:无
*/
void SPI_Start()
{
SPI_W_SS(0);//SS低位有效
}
/**
* 函 数:SPI终止
* 参 数:无
* 返 回 值:无
*/
void SPI_End()
{
SPI_W_SS(1);//SS高位失效,结束通信
}
/**
* 函 数:SPI交换传输一个字节,使用SPI模式0,把接收数据通过移位直接存入ByteSend变量中,提高效率
* 参 数:ByteSend 要发送的一个字节
* 返 回 值:接收的一个字节
*/
u8 SPI_SwapByte(u8 ByteSend)
{
u8 i=0;
for(;i<8;i++)
{
SPI_W_MOSI(ByteSend&0x80);//将发送数据放在线路上
ByteSend<<=1;//腾出位置接收数据
SPI_W_SCL(1);//开启一个时钟,边沿自动触发从机接收一位数据
ByteSend|=SPI_R_MISO();//接收一位数据
SPI_W_SCL(0);//结束一个时钟,边沿触发从机自动将下一位数据放到线路上
}
return ByteSend;//返回交换得到的值
}
spi.h
#ifndef __SPI_H
#define __SPI_H
#include "sys.h"
void SPI_Start();
void SPI_End();
u8 SPI_SwapByte(u8 ByteSend);
void spiInit();
#endif
main.c
#include "stm32f10x.h"
#include "led.h"
#include "oled.h"
#include "delay.h"
#include "spi.h"
#include "usart.h"
int main(void)
{
u8 MID;
u16 DID=0;
delay_init();//延时初始化,iic使用了延时函数
spiInit();//spi初始化端口
oledInit();//oled初始化化端口,及基本配置
//读取W25q64设备id信息
SPI_Start();//开始spi
SPI_SwapByte(0x9f);//发送指令0x9f获取id信息,一共发送3个数据,1.制造商id-2.地址id高八位-3.地址id低八位
MID=SPI_SwapByte(0xff);//交换获得制造商id,发送什么无意义,一般给0xff就行
DID=SPI_SwapByte(0xff);//交换获得地址信息高八位
DID<<=8;//移动到高八位
DID|=SPI_SwapByte(0xff);//获得地址信息第八位
SPI_End();//spi结束
while(1)
{
//显示id信息
oledShowString(0,0,"MID:",8);
oledShowNum(32,0,MID,8);
oledShowString(0,16,"DID",8);
oledShowNum(32,16,DID,8);
oledUpdate();
}
}
5.问题总结
1.寻址不是还是要ss发送完整地址信息吗
主机有多个ss线,用哪个从设备就控制对应ss口置低位
2.没有从机应答,那从机数据没准备好就开始读,不是就冲突了吗
有个状态标志位,可以先读这个数据再判断是否进行数据交换
3.读取w25q64设备id失败
4.oled屏无法正常显示
iic初始化使用了delay函数,必须先初始化delay_init函数
结果,十进制显示id信息,可以自行试试修改为16进制显示。