stm32学习-8.spi读取W25Q64的ID信息

本文详细介绍了STM32中的IIC和SPI通信,包括SPI驱动OLED显示、W25Q4存储器的配置和使用流程,以及处理SS、SCK、MOSI和MISO信号的函数。还讨论了寻址问题、从机应答和延迟初始化的重要性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

前提知识

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进制显示。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值