当前位置: 首页 > wzjs >正文

做pc网站软件注册一个网站

做pc网站软件,注册一个网站,网站改版怎么改,网站开发必会语言目录 前言 技术实现 接线图 代码实现 技术要点 引脚操作 SPI初始化 SPI起始信号 SPI终止信号 SPI字节交换 宏替换命令 W25Q64写使能 忙等待 读取设备ID号和制造商ID 页写入 数据读取 实验结果 问题记录 前言 SPI(Serial Peripheral Interf…

目录

前言

技术实现 

接线图 

代码实现 

技术要点 

引脚操作

SPI初始化

SPI起始信号

SPI终止信号

SPI字节交换

宏替换命令 

W25Q64写使能 

忙等待

读取设备ID号和制造商ID

页写入

数据读取

实验结果

问题记录 


前言

SPI(Serial Peripheral Interface,串行外设接口)是一种同步串行通信接口规范,主要用于短距离通信,广泛应用于嵌入式系统中。它使一个主设备能够与一个或多个从设备进行通信。SPI使用四条主要信号线:MOSI(主机输出/从机输入)、MISO(主机输入/从机输出)、SCK(串行时钟)和SS/CS(从选/片选)来实现数据的双向传输。这种接口方式支持全双工通信,具有传输速率高、延迟低的优点,但相比其他一些接口协议,使用的信号线较多。SPI常用于连接传感器、存储器、ADC(模数转换器)等外围设备。

SPI硬件电路

  • 所有SPI设备的SCK,MISO,MOSI分别连在一起。
  • 主机另外引出多条CS控制线分别连接到各从机的CS引脚。
  • 输出引脚配置为推挽输出,输入引脚配置为浮空输入或上拉输入。

W25Q64是华邦公司(Winbond)推出的一款基于SPI通信的大容量闪存产品,其存储容量为64Mb(即8MB)。该芯片支持2.7~3.6V的工作电压范围,并且具备至少10万次的擦写周期和长达20年的数据保存时间。W25Q64以其灵活性和出色的性能著称,非常适合用于存储声音、文本和数据等应用。它被组织为32768个可编程页面,每个页面包含256字节的数据。此外,W25Q64还支持多种擦除命令,包括4KB扇区擦除、32KB块擦除、64KB块擦除以及全片擦除,并且兼容标准SPI模式0和模式3,最高支持133MHz的时钟频率。通过SPI接口,W25Q64可以轻松地与各种微控制器进行集成,实现高效的数据读写操作。

W25Q64由8MB的存储容量,存储区域被划分成了128个块,每个块又被划分成了16个扇区,每个扇区又被划分成了 16个页。

W25Q64写入操作时

  •  在写入操作之前,必须先进行写使能。
  • 每个数据为只能有1改写成0,不能由0改写成1。
  • 写入数据之前必须先擦除,擦除后,所有的数据位都变为1。
  • 擦除必须以最小单元擦除(这里最小擦除单元时4kb扇区)。
  • 连续写入多个字节时最多写入一页的数据(256Byte),超过页尾的数据会回到页首覆盖写入。
  • 写入操作完成后,芯片进入忙状态,不响应新的写操作。

W25Q64读操作时:

  • 直接调取读操作时序,无需写使能,无需其他操作,没有页限制,读取操作完成后芯片不会进入忙状态,但是芯片忙状态时不能进行读操作。

技术实现 

原理图

 

接线图 

代码实现 

main.c

/**********************************************************
1.实验名称:软件SPI读写W25Q64
2.实验环境:STM32F103C8T6最小系统板
3.实验内容:使用软件模拟SPI读取W25Q64	
4.作者;abai
5.实验时间:2025-5-6
**********************************************************/
#include "stm32f10x.h"                  // Device header
#include "Delay.h"						//延时函数
#include "OLED.h"
#include "W25Q64.h"uint8_t MF;
uint16_t ID;
uint8_t SendArry[] = {0x05,0x06,0x07,0x08};
uint8_t ReceiveArry[4];int main(void)
{/*OLED初始化*/OLED_Init();W25Q64_Init();OLED_ShowString(1,1,"MF:");OLED_ShowString(2,1,"ID:");OLED_ShowString(3,1,"W:");OLED_ShowString(4,1,"R:");W25Q64_SectorErase(0x000000);												//写入数据之前必须页擦除W25Q64_PageProgram(0x000000,SendArry,4);W25Q64_ReadData(0x000000,ReceiveArry,4);W25Q64_ReadID(&MF,&ID);OLED_ShowHexNum(3,3,SendArry[0],2);OLED_ShowHexNum(3,5,SendArry[1],2);OLED_ShowHexNum(3,7,SendArry[2],2);OLED_ShowHexNum(3,9,SendArry[3],2);OLED_ShowHexNum(4,3,ReceiveArry[0],2);OLED_ShowHexNum(4,5,ReceiveArry[1],2);OLED_ShowHexNum(4,7,ReceiveArry[2],2);OLED_ShowHexNum(4,9,ReceiveArry[3],2);OLED_ShowHexNum(1,4,MF,2);OLED_ShowHexNum(2,4,ID,4);while(1){}
}

MySPI.h 

#ifndef MYSPI_H
#define MYSPI_H#include "stm32f10x.h"                  // Device headervoid MySPI_Init(void);
void MySPI_CS_W(uint8_t BitValue);
void MySPI_SCK_W(uint8_t BitValue);
void MySPI_MOSI_W(uint8_t BitValue);
uint8_t MySPI_MISO_R(void);
void MySPI_Start(void);
void MySPI_Stop(void);
uint8_t MySPI_SwapByte(uint8_t ByteValue);#endif

 MySPI.c

#include "MySPI.h"/***@brief  SPI初始化*@param  None*@retval None**/
void MySPI_Init(void)
{RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);//GPIOGPIO_InitTypeDef GPIO_InitStruct;GPIO_InitStruct.GPIO_Pin	= GPIO_Pin_4 | GPIO_Pin_5 | GPIO_Pin_7;GPIO_InitStruct.GPIO_Mode	= GPIO_Mode_Out_PP;								//推挽输出GPIO_InitStruct.GPIO_Speed	= GPIO_Speed_50MHz;GPIO_Init(GPIOA,&GPIO_InitStruct);GPIO_InitStruct.GPIO_Pin	= GPIO_Pin_6;GPIO_InitStruct.GPIO_Mode	= GPIO_Mode_IPU;								//上拉输入GPIO_InitStruct.GPIO_Speed	= GPIO_Speed_50MHz;GPIO_Init(GPIOA,&GPIO_InitStruct);//引脚初始化MySPI_CS_W(1);																//片选信号默认为高电平MySPI_SCK_W(0);																//SPI模式0,时钟信号默认为低电平}/***@brief  SPI CS控制*@param  BitValue 要写入CS的位数据*@retval None**/
void MySPI_CS_W(uint8_t BitValue)
{GPIO_WriteBit(GPIOA,GPIO_Pin_4,(BitAction)BitValue);
}/***@brief  SPI SCK控制*@param  BitValue 要写入SCK的位数据*@retval None**/
void MySPI_SCK_W(uint8_t BitValue)
{GPIO_WriteBit(GPIOA,GPIO_Pin_5,(BitAction)BitValue);
}/***@brief  SPI MOSI写操作*@param  BitValue 要写入MOSI的位数据*@retval None**/
void MySPI_MOSI_W(uint8_t BitValue)
{GPIO_WriteBit(GPIOA,GPIO_Pin_7,(BitAction)BitValue);
}/***@brief  SPI MISO读*@param  None*@retval MISO数据线接受到的数据**/
uint8_t MySPI_MISO_R(void)
{return GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_6);
}/***@brief  SPI起始信号*@param  None*@retval None**/
void MySPI_Start(void)
{MySPI_CS_W(0);																//将片选信号拉低发送起始信号
}/***@brief  SPI终止信号*@param  None*@retval None**/
void MySPI_Stop(void)
{MySPI_CS_W(1);																//将片选信号拉高发送终止信号
}/***@brief  SPI交换字节*@param  None*@retval 交换的字节**/
uint8_t MySPI_SwapByte(uint8_t ByteValue)
{//掩码操作uint8_t ByteReceive = 0x00;uint8_t i;for(i=0;i<8;i++){MySPI_MOSI_W((ByteValue<<i) & 0x80);MySPI_SCK_W(1);if(MySPI_MISO_R() == 1)ByteReceive |= (0x80>>i);MySPI_SCK_W(0);}return ByteReceive;//移位操作
//	uint8_t i;
//	for(i=0;i<8;i++)
//	{
//		MySPI_MOSI_W(ByteValue& 0x80);
//		ByteValue <<= 1;
//		MySPI_SCK_W(1);
//		if(MySPI_MISO_R() == 1)
//			ByteValue |= 0x01;
//		MySPI_SCK_W(0);
//	}
//	
//	return ByteValue;
}

W25Q64_Ins.h 

#ifndef W25Q64_INS_H
#define W25Q64_INS_H#define W25Q64_WRITEENABLE 				0x06
#define W25Q64_READSTATUSREGISTER1 		0x05
#define W25Q64_PAGEPROGRAM 				0x02
#define W25Q64_SECTORERASE4KB 			0x20
#define W25Q64_JEDECID 					0X9F
#define W25Q64_READDATA 				0x03
#define W25Q64_DUMMY_BYTE 				0xFF#endif

W25Q64.h 

#ifndef W25Q64_H
#define W25Q64_H#include "stm32f10x.h"                  // Device header
#include "MySPI.h"void W25Q64_Init(void);
void W25Q64_ReadID(uint8_t* MF, uint16_t*ID);
void W25Q64_WriteEnable(void);
void W25Q64_WaitBusy(void);
void W25Q64_PageProgram(uint32_t Address, uint8_t* DataArry, uint16_t Count);
void W25Q64_SectorErase(uint32_t Address);
void W25Q64_ReadData(uint32_t Address,uint8_t* DataArry, uint32_t Count);#endif

W25Q64.c 

#include "W25Q64.h"
#include "W25Q64_Ins.h"/***@brief  W25Q64初始化*@param  None*@retval None**/
void W25Q64_Init(void)
{MySPI_Init();
}/***@brief  W25Q64ID读取*@param  None*@retval None**/
void W25Q64_ReadID(uint8_t* MF, uint16_t*ID)
{MySPI_Start();//发送读取ID指令MySPI_SwapByte(W25Q64_JEDECID);//发送空指令交换数据*MF = MySPI_SwapByte(W25Q64_DUMMY_BYTE);													//随便发送一个数据用于与从机交换数据,一般使用0xFF或0x00*ID = MySPI_SwapByte(W25Q64_DUMMY_BYTE);*ID <<= 8;*ID |= MySPI_SwapByte(W25Q64_DUMMY_BYTE);MySPI_Stop();
}/***@brief  W25Q64ID写使能*@param  None*@retval None**/
void W25Q64_WriteEnable(void)
{MySPI_Start();//发送写使能指令MySPI_SwapByte(W25Q64_WRITEENABLE);MySPI_Stop();
}/***@brief  W25Q64ID忙等待*@param  None*@retval None**/
void W25Q64_WaitBusy(void)
{MySPI_Start();//发送读状态寄存器指令MySPI_SwapByte(W25Q64_READSTATUSREGISTER1);//等待BUSY位清零,则从设备退出忙状态while((MySPI_SwapByte(W25Q64_DUMMY_BYTE) & 0X01) == 0X01);MySPI_Stop();
}/***@brief  W25Q64ID页写入*@param  None*@retval None**/
void W25Q64_PageProgram(uint32_t Address, uint8_t* DataArry, uint16_t Count)
{uint16_t i;W25Q64_WriteEnable();														//写使能MySPI_Start();MySPI_SwapByte(W25Q64_PAGEPROGRAM);//发送24位地址MySPI_SwapByte(Address>>16);MySPI_SwapByte(Address>>8);MySPI_SwapByte(Address);//发送指定数量的数据for(i=0;i<Count;i++){MySPI_SwapByte(DataArry[i]);}MySPI_Stop();W25Q64_WaitBusy();															//事后等待忙状态
}/***@brief  W25Q64ID指定扇区擦除*@param  None*@retval None**/
void W25Q64_SectorErase(uint32_t Address)
{			W25Q64_WriteEnable();MySPI_Start();MySPI_SwapByte(W25Q64_SECTORERASE4KB);//发送24位地址MySPI_SwapByte(Address>>16);MySPI_SwapByte(Address>>8);MySPI_SwapByte(Address);MySPI_Stop();W25Q64_WaitBusy();
}/***@brief  W25Q64ID读取数据*@param  None*@retval None*@note   数据接收的数量也要指定,不然无法停止数据读取操作**/
void W25Q64_ReadData(uint32_t Address,uint8_t* DataArry, uint32_t Count)
{uint32_t i;MySPI_Start();MySPI_SwapByte(W25Q64_READDATA);//发送24位地址MySPI_SwapByte(Address>>16);MySPI_SwapByte(Address>>8);MySPI_SwapByte(Address);for(i=0;i<Count;i++){DataArry[i] = MySPI_SwapByte(W25Q64_DUMMY_BYTE);}MySPI_Stop();
}

 OLED部分代码参照文章《STM32基础教程——OLED显示》

技术要点 

引脚操作

/***@brief  SPI CS控制*@param  BitValue 要写入CS的位数据*@retval None**/
void MySPI_CS_W(uint8_t BitValue)
{GPIO_WriteBit(GPIOA,GPIO_Pin_4,(BitAction)BitValue);
}/***@brief  SPI SCK控制*@param  BitValue 要写入SCK的位数据*@retval None**/
void MySPI_SCK_W(uint8_t BitValue)
{GPIO_WriteBit(GPIOA,GPIO_Pin_5,(BitAction)BitValue);
}/***@brief  SPI MOSI写操作*@param  BitValue 要写入MOSI的位数据*@retval None**/
void MySPI_MOSI_W(uint8_t BitValue)
{GPIO_WriteBit(GPIOA,GPIO_Pin_7,(BitAction)BitValue);
}/***@brief  SPI MISO读*@param  None*@retval MISO数据线接受到的数据**/
uint8_t MySPI_MISO_R(void)
{return GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_6);
}

对单个引脚的操作进行封装,用于模拟SPI引脚电平翻转或引脚电平读取。 

SPI初始化

/***@brief  SPI初始化*@param  None*@retval None**/
void MySPI_Init(void)
{RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);//GPIOGPIO_InitTypeDef GPIO_InitStruct;GPIO_InitStruct.GPIO_Pin	= GPIO_Pin_4 | GPIO_Pin_5 | GPIO_Pin_7;GPIO_InitStruct.GPIO_Mode	= GPIO_Mode_Out_PP;								//推挽输出GPIO_InitStruct.GPIO_Speed	= GPIO_Speed_50MHz;GPIO_Init(GPIOA,&GPIO_InitStruct);GPIO_InitStruct.GPIO_Pin	= GPIO_Pin_6;GPIO_InitStruct.GPIO_Mode	= GPIO_Mode_IPU;								//上拉输入GPIO_InitStruct.GPIO_Speed	= GPIO_Speed_50MHz;GPIO_Init(GPIOA,&GPIO_InitStruct);//引脚初始化MySPI_CS_W(1);																//片选信号默认为高电平MySPI_SCK_W(0);																//SPI模式0,时钟信号默认为低电平}

实验使用软件模拟SPI,PA4引脚模拟SPI的CS引脚,PA5模拟SPI的SCK引脚,PA6模拟MISO引脚,PA7模拟MOSI引脚。根据输出引脚配置为推挽输出,输入引脚配置为上拉输入的要求初始化GPIO。初始化完成后,对CS引脚和SCK引脚初始化。片选信号CS默认为高电平,表示未选中从设备。模拟SPI模式0,SCK初始默认电平为低电平。

SPI起始信号

SPI的起始时序是CS(SS)从高电平切换为低电平 。

/***@brief  SPI起始信号*@param  None*@retval None**/
void MySPI_Start(void)
{MySPI_CS_W(0);																//将片选信号拉低发送起始信号
}

SPI终止信号
 

 SPI的终止时序是CS(SS)从低电平切换为高电平 。

/***@brief  SPI终止信号*@param  None*@retval None**/
void MySPI_Stop(void)
{MySPI_CS_W(1);																//将片选信号拉高发送终止信号
}

SPI字节交换

SPI为同步全双工通信,每一次通信都会进行数据交换。

/***@brief  SPI交换字节*@param  None*@retval 交换的字节**/
uint8_t MySPI_SwapByte(uint8_t ByteValue)
{//掩码操作uint8_t ByteReceive = 0x00;uint8_t i;for(i=0;i<8;i++){MySPI_MOSI_W((ByteValue<<i) & 0x80);MySPI_SCK_W(1);if(MySPI_MISO_R() == 1)ByteReceive |= (0x80>>i);MySPI_SCK_W(0);}return ByteReceive;
}

SPI模式0下,SCK第一个边沿移入数据,所以当第一个边沿到来之前数据已经移出。 

MySPI_MOSI_W((ByteValue<<i) & 0x80);

 所以通过MOSI发送一个数据字节。然后将SCK拉高,SCK的第一个边沿将数据移入。

if(MySPI_MISO_R() == 1)ByteReceive |= (0x80>>i);

 ByteValue默认为0x00,只有当移入的数据为1时将对应位置1,为0时不执行操作。

MySPI_SCK_W(0);

 然后将SCK拉低,发生数据移出操作。

使用for循环完成一个字节的数据交换,最后将数据返回。

宏替换命令 

#ifndef W25Q64_INS_H
#define W25Q64_INS_H#define W25Q64_WRITEENABLE 				0x06
#define W25Q64_READSTATUSREGISTER1 		0x05
#define W25Q64_PAGEPROGRAM 				0x02
#define W25Q64_SECTORERASE4KB 			0x20
#define W25Q64_JEDECID 					0X9F
#define W25Q64_READDATA 				0x03
#define W25Q64_DUMMY_BYTE 				0xFF#endif

将对W25Q64操作的命令使用宏代替, 提高代码的便捷性和可读性。

W25Q64写使能 

/***@brief  W25Q64ID写使能*@param  None*@retval None**/
void W25Q64_WriteEnable(void)
{MySPI_Start();//发送写使能指令MySPI_SwapByte(W25Q64_WRITEENABLE);MySPI_Stop();
}

将写使能操作封装,方便使用。主机与从机交换数据,发送写使能命令,对从机进行写使能操作。

忙等待

/***@brief  W25Q64ID忙等待*@param  None*@retval None**/
void W25Q64_WaitBusy(void)
{MySPI_Start();//发送读状态寄存器指令MySPI_SwapByte(W25Q64_READSTATUSREGISTER1);//等待BUSY位清零,则从设备退出忙状态while((MySPI_SwapByte(W25Q64_DUMMY_BYTE) & 0X01) == 0X01);MySPI_Stop();
}

 发送读取状态寄存器1的命令,判断BUSY位的状态,使用while循环等待芯片退出忙状态将BUSY位清零。

读取设备ID号和制造商ID

/***@brief  W25Q64ID读取*@param  None*@retval None**/
void W25Q64_ReadID(uint8_t* MF, uint16_t*ID)
{MySPI_Start();//发送读取ID指令MySPI_SwapByte(W25Q64_JEDECID);//发送空指令交换数据*MF = MySPI_SwapByte(W25Q64_DUMMY_BYTE);													//随便发送一个数据用于与从机交换数据,一般使用0xFF或0x00*ID = MySPI_SwapByte(W25Q64_DUMMY_BYTE);*ID <<= 8;*ID |= MySPI_SwapByte(W25Q64_DUMMY_BYTE);MySPI_Stop();
}

 主机发送读取JEDEC ID的命令,从机向主机发送数据,数据格式为第一个字节为制造商ID,第二个和第三个字节为设备ID。这里使用指针进行数据操作。

页写入

/***@brief  W25Q64ID页写入*@param  None*@retval None**/
void W25Q64_PageProgram(uint32_t Address, uint8_t* DataArry, uint16_t Count)
{uint16_t i;W25Q64_WriteEnable();														//写使能MySPI_Start();MySPI_SwapByte(W25Q64_PAGEPROGRAM);//发送24位地址MySPI_SwapByte(Address>>16);MySPI_SwapByte(Address>>8);MySPI_SwapByte(Address);//发送指定数量的数据for(i=0;i<Count;i++){MySPI_SwapByte(DataArry[i]);}MySPI_Stop();W25Q64_WaitBusy();															//事后等待忙状态
}

页写入操作,先进行写使能,然后发送起始信号,发送页写入命令。然后发送24位地址,高16位为页地址,低八位为字节地址。使用for循环写入指定数量的数据,数据的数量不应超过256字节。最后发送停止信号,然后进行芯片忙等待操作(忙等待如果加在函数开始,那读取操作也要加忙等待函数调用,这样会使操作更复杂。)。所有写操作函数与本函数类似,不再赘述。

数据读取

/***@brief  W25Q64ID读取数据*@param  None*@retval None*@note   数据接收的数量也要指定,不然无法停止数据读取操作**/
void W25Q64_ReadData(uint32_t Address,uint8_t* DataArry, uint32_t Count)
{uint32_t i;MySPI_Start();MySPI_SwapByte(W25Q64_READDATA);//发送24位地址MySPI_SwapByte(Address>>16);MySPI_SwapByte(Address>>8);MySPI_SwapByte(Address);for(i=0;i<Count;i++){DataArry[i] = MySPI_SwapByte(W25Q64_DUMMY_BYTE);}MySPI_Stop();
}

数据读取操作无需写使能,无需进行其他操作。发送读数据命令,然后发送24位地址,同样高16位地址为页地址,低八位为字节地址。读取操作不受页限制。

实验结果

问题记录 

暂无

http://www.dtcms.com/wzjs/5891.html

相关文章:

  • 网站织梦模板找seo外包公司需要注意什么
  • 给中小企业提供网站建设服务百度推广投诉中心
  • 西安php网站建设关键词seo优化
  • 提供网站制作百度seo排名优化系统
  • 苏州交友网站建设怎么做网页
  • 公司网站开发 nodejs东莞市网络seo推广企业
  • 东莞做网站找微客巴巴网站服务器一年的费用
  • 双井网站建设2023年6月份疫情严重吗
  • 可以随意做配搭的网站广州企业推广
  • wordpress.备份小时seo
  • 为什么做手机网站专业竞价托管哪家好
  • 做模特的网站软文例文 经典软文范例
  • 行业前10的网站建设公司seo专员岗位要求
  • 哪个购物网站最便宜郑州网络推广效果
  • 58南浔做网站网站怎么快速收录
  • 青浦专业做网站公司北京关键词快速排名
  • 国外知名设计网站大全百度收录检测
  • wix网站做图片能折叠吗郑州网站推广多少钱
  • 徐州做网站宁波seo优化排名
  • 全网媒体发布平台seo网站整站优化
  • 大气网站首页企业网络营销推广平台
  • 动态网站建设包括哪些方面郑州专业seo推荐
  • 京东商城网站怎么做最新国际新闻头条今日国际大事件
  • 做初级会计实务题的网站无锡营销型网站建站
  • 两学一做网站无法做题seo需要掌握什么技能
  • wordpress页面文章网站seo分析案例
  • kilu wordpress长沙seo代理
  • 企业网站一般用什么域名一站式网站建设
  • 珠海网站运营西安疫情最新数据消息5分钟前
  • 做网站优化有什么作用济南seo网站关键词排名