做汽配的网站,电子商务网站的规划与建设论文,聊城菜鸟网站建设公司,网站颜色正确搭配实例一、W25Q64简介 1、W25Q64的内存空间结构: 一页256字节#xff0c;4K(4096 字节)为一个扇区#xff0c;16个扇区为1块#xff0c;容量为8M字节#xff0c;共有128个块#xff0c;2048 个扇区。 2、W25Q64每页大小由256字节组成#xff0c;每页的256字节用一次页编程指…一、W25Q64简介 1、W25Q64的内存空间结构: 一页256字节4K(4096 字节)为一个扇区16个扇区为1块容量为8M字节共有128个块2048 个扇区。 2、W25Q64每页大小由256字节组成每页的256字节用一次页编程指令即可完成。 3、擦除指令分别支持: 16页1个扇区、128页、256页、全片擦除。 二、电路图
1、软件模拟的SPI线可以任意接
2、硬件模拟的SPI要按以下方式连接 3、本次软件模拟和硬件模拟使用同一个电路图方便切换 CS片选PA4 DO从机输出PA6
CLK时钟PA5 DI从机输入PA7
三、软件SPI读写W25Q64
1、SPI.c初始化寄存器实现读取一个字节的功能
#include stm32f10x.h // Device headervoid MySPI_W_SS(uint8_t BitValue)
{GPIO_WriteBit(GPIOA, GPIO_Pin_4, (BitAction)BitValue);
}void MySPI_W_SCK(uint8_t BitValue)
{GPIO_WriteBit(GPIOA, GPIO_Pin_5, (BitAction)BitValue);
}void MySPI_W_MOSI(uint8_t BitValue)
{GPIO_WriteBit(GPIOA, GPIO_Pin_7, (BitAction)BitValue);
}uint8_t MySPI_R_MISO(void)
{return GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_6);
}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); //SS默认高电平下降沿为开始工作低电平状态为工作中上升沿为结束工作MySPI_W_SCK(0); //SCK默认为低电平上升沿移入数据下降沿移出数据
}void MySPI_Start(void)
{MySPI_W_SS(0);
}void MySPI_Stop(void)
{MySPI_W_SS(1);
}//模式0
uint8_t MySPI_SwapByte(uint8_t ByteSend)
{uint8_t i, ByteReceive 0x00;for (i 0; i 8; i ){//先SS下降沿移出数据SCK上升沿移入数据再SCK下降沿移出数据下面只管主机MySPI_W_MOSI(ByteSend (0x80 i)); //将数据移出到MOSI线MySPI_W_SCK(1); //上升沿移入数据//当MISO为1时置变量指定位为1当MISO为0时不做处理指定位为默认的初值0if (MySPI_R_MISO() 1){ByteReceive | (0x80 i);} //将移入的数据读取出来MySPI_W_SCK(0); //下降沿移出数据}return ByteReceive; //读取出来的数据
}
/*1、for循环的优化MySPI_W_MOSI(ByteSend 0x80 ); //将数据移出到MOSI线ByteSend 1; //将数据左移动1位去掉最高位最低位置0MySPI_W_SCK(1); //上升沿移入数据if (MySPI_R_MISO() 1){ByteSend | 0x01;} //将移入的数据读取出来如果是0不管如果是1将最低位置1MySPI_W_SCK(0); //下降沿移出数据2、模式1MySPI_W_SCK(1); //上升沿移出来数据MySPI_W_MOSI(ByteSend (0x80 i)); //将数据移出到MOSI线MySPI_W_SCK(0); //下降沿移入数据if (MySPI_R_MISO() 1){ByteReceive | (0x80 i);} //将移入的数据读取出来
*/
2、W25Q64.c
#include stm32f10x.h // Device header
#include MySPI.h
#include W25Q64_Ins.h/*** 函 数W25Q64初始化* 参 数无* 返 回 值无*/
void W25Q64_Init(void)
{MySPI_Init(); //先初始化底层的SPI
}/*** 函 数MPU6050读取ID号* 参 数MID 工厂ID使用输出参数的形式返回* 参 数DID 设备ID使用输出参数的形式返回* 返 回 值无*/
void W25Q64_ReadID(uint8_t *MID, uint16_t *DID)
{MySPI_Start(); //SPI起始MySPI_SwapByte(W25Q64_JEDEC_ID); //交换发送读取ID的指令*MID MySPI_SwapByte(W25Q64_DUMMY_BYTE); //交换接收MID通过输出参数返回*DID MySPI_SwapByte(W25Q64_DUMMY_BYTE); //交换接收DID高8位*DID 8; //高8位移到高位*DID | MySPI_SwapByte(W25Q64_DUMMY_BYTE); //或上交换接收DID的低8位通过输出参数返回MySPI_Stop(); //SPI终止
}/*** 函 数W25Q64写使能* 参 数无* 返 回 值无*/
void W25Q64_WriteEnable(void)
{MySPI_Start(); //SPI起始MySPI_SwapByte(W25Q64_WRITE_ENABLE); //交换发送写使能的指令MySPI_Stop(); //SPI终止
}/*** 函 数W25Q64等待忙* 参 数无* 返 回 值无*/
void W25Q64_WaitBusy(void)
{uint32_t Timeout;MySPI_Start(); //SPI起始MySPI_SwapByte(W25Q64_READ_STATUS_REGISTER_1); //交换发送读状态寄存器1的指令Timeout 100000; //给定超时计数时间while ((MySPI_SwapByte(W25Q64_DUMMY_BYTE) 0x01) 0x01) //循环等待忙标志位{Timeout --; //等待时计数值自减if (Timeout 0) //自减到0后等待超时{/*超时的错误处理代码可以添加到此处*/break; //跳出等待不等了}}MySPI_Stop(); //SPI终止
}/*** 函 数W25Q64页编程* 参 数Address 页编程的起始地址范围0x000000~0x7FFFFF* 参 数DataArray 用于写入数据的数组* 参 数Count 要写入数据的数量范围0~256* 返 回 值无* 注意事项写入的地址范围不能跨页*/
void W25Q64_PageProgram(uint32_t Address, uint8_t *DataArray, uint16_t Count)
{uint16_t i;W25Q64_WriteEnable(); //写使能MySPI_Start(); //SPI起始MySPI_SwapByte(W25Q64_PAGE_PROGRAM); //交换发送页编程的指令MySPI_SwapByte(Address 16); //交换发送地址23~16位MySPI_SwapByte(Address 8); //交换发送地址15~8位MySPI_SwapByte(Address); //交换发送地址7~0位for (i 0; i Count; i ) //循环Count次{MySPI_SwapByte(DataArray[i]); //依次在起始地址后写入数据}MySPI_Stop(); //SPI终止W25Q64_WaitBusy(); //等待忙
}/*** 函 数W25Q64扇区擦除4KB* 参 数Address 指定扇区的地址范围0x000000~0x7FFFFF* 返 回 值无*/
void W25Q64_SectorErase(uint32_t Address)
{W25Q64_WriteEnable(); //写使能MySPI_Start(); //SPI起始MySPI_SwapByte(W25Q64_SECTOR_ERASE_4KB); //交换发送扇区擦除的指令MySPI_SwapByte(Address 16); //交换发送地址23~16位MySPI_SwapByte(Address 8); //交换发送地址15~8位MySPI_SwapByte(Address); //交换发送地址7~0位MySPI_Stop(); //SPI终止W25Q64_WaitBusy(); //等待忙
}/*** 函 数W25Q64读取数据* 参 数Address 读取数据的起始地址范围0x000000~0x7FFFFF* 参 数DataArray 用于接收读取数据的数组通过输出参数返回* 参 数Count 要读取数据的数量范围0~0x800000* 返 回 值无*/
void W25Q64_ReadData(uint32_t Address, uint8_t *DataArray, uint32_t Count)
{uint32_t i;MySPI_Start(); //SPI起始MySPI_SwapByte(W25Q64_READ_DATA); //交换发送读取数据的指令MySPI_SwapByte(Address 16); //交换发送地址23~16位MySPI_SwapByte(Address 8); //交换发送地址15~8位MySPI_SwapByte(Address); //交换发送地址7~0位for (i 0; i Count; i ) //循环Count次{DataArray[i] MySPI_SwapByte(W25Q64_DUMMY_BYTE); //依次在起始地址后读取数据}MySPI_Stop(); //SPI终止
}3、main.c
#include stm32f10x.h // Device header
#include Delay.h
#include OLED.h
#include W25Q64.huint8_t MID; //定义用于存放MID号的变量
uint16_t DID; //定义用于存放DID号的变量uint8_t ArrayWrite[] {0x01, 0x02, 0x03, 0x04}; //定义要写入数据的测试数组
uint8_t ArrayRead[4]; //定义要读取数据的测试数组int main(void)
{/*模块初始化*/OLED_Init(); //OLED初始化W25Q64_Init(); //W25Q64初始化/*显示静态字符串*/OLED_ShowString(1, 1, MID: DID:);OLED_ShowString(2, 1, W:);OLED_ShowString(3, 1, R:);/*显示ID号*/W25Q64_ReadID(MID, DID); //获取W25Q64的ID号OLED_ShowHexNum(1, 5, MID, 2); //显示MIDOLED_ShowHexNum(1, 12, DID, 4); //显示DIDW25Q64_SectorErase(0x000000); //扇区擦除/*验证扇区擦除功能(方法注释掉这一句如果不擦除一开始写入AA、BB、CC、DD后面再次写入55、66、77、88则读出来00、22、44、88即如果不进行擦除则读出的数据原始数据写入的数据*/W25Q64_PageProgram(0x000000, ArrayWrite, 4); //将写入数据的测试数组写入到W25Q64中W25Q64_ReadData(0x000000, ArrayRead, 4); //读取刚写入的测试数据到读取数据的测试数组中/*数据是不能跨页写入验证若写入55 66 77 88 W25Q64_PageProgram(0x0000FF, ArrayWrite, 4); //数据不能跨页写入66 77 88返回到页首写入W25Q64_ReadData(0x0000FF, ArrayRead, 4); //读取数据可以跨页读出则读出的是55 FF FF FF FF为第二页的数据第二页是擦除了的没有写入默认是FFW25Q64_PageProgram(0x0000FF, ArrayWrite, 4);W25Q64_ReadData(0x000000, ArrayRead, 4); //读出66 77 88 FF*//*显示数据*/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){}
}四、硬件读写I2C
只需要在软件的基础上添加以下的代码
#include stm32f10x.h // Device headervoid MySPI_W_SS(uint8_t BitValue)//SS还是软件模拟
{GPIO_WriteBit(GPIOA, GPIO_Pin_4, (BitAction)BitValue);
}/*初始化步骤1、开启时钟2、初始化GPIO口1SCK、MOSI是由硬件外设控制的输出信号复用推挽输出2MISO是硬件外设的输入信号上拉输入输入设备可以有很多个不存在复用输入3SS为软件控制的输出信号配置为通用推挽输出3、配置SPI外设 用结构体4、开关控制调用SPI_Cmd给SPI使能
*/
void MySPI_Init(void)
{RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);RCC_APB2PeriphClockCmd(RCC_APB2Periph_SPI1, ENABLE);GPIO_InitTypeDef GPIO_InitStructure;GPIO_InitStructure.GPIO_Mode GPIO_Mode_Out_PP;//通用推挽输出GPIO_InitStructure.GPIO_Pin GPIO_Pin_4;GPIO_InitStructure.GPIO_Speed GPIO_Speed_50MHz;GPIO_Init(GPIOA, GPIO_InitStructure);GPIO_InitStructure.GPIO_Mode GPIO_Mode_AF_PP;//复用推挽输出GPIO_InitStructure.GPIO_Pin 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);SPI_InitTypeDef SPI_InitStructure;SPI_InitStructure.SPI_Mode SPI_Mode_Master; //主机SPI_InitStructure.SPI_Direction SPI_Direction_2Lines_FullDuplex; //双线全双工SPI_InitStructure.SPI_DataSize SPI_DataSize_8b; //8位数据帧SPI_InitStructure.SPI_FirstBit SPI_FirstBit_MSB; //高位先行SPI_InitStructure.SPI_BaudRatePrescaler SPI_BaudRatePrescaler_128; //波特率预分频器配置SCK时钟的频率.SPI1:72MHz/128,SPI2:36MHz/128SPI_InitStructure.SPI_CPOL SPI_CPOL_Low; //默认低电平空闲默认低电平SPI_InitStructure.SPI_CPHA SPI_CPHA_1Edge; //第一个边沿开始采样(移入)SPI_InitStructure.SPI_NSS SPI_NSS_Soft; //一般选择软件NSS模式不用了解SPI_InitStructure.SPI_CRCPolynomial 7; //随便填SPI_Init(SPI1, SPI_InitStructure);SPI_Cmd(SPI1, ENABLE);MySPI_W_SS(1);//默认不选中从机
}void MySPI_Start(void)
{MySPI_W_SS(0);
}void MySPI_Stop(void)
{MySPI_W_SS(1);
}/*等待TXE为1发送寄存器为空发送寄存器不为空先不着急写过程写入数据到TDR--转移到移位寄存器——一旦移位寄存器有数据时序波形就会自动产生则ByteSend就会通过MOSI一位一位地移出去——在MOSI线上就自动产生发送的时序波形由于是非连续传输时序产生的时间内不必提前把下个数据放到TDR直接等待这段时间过去就行在发送的同时MISO会移位进行接收发送和接收是同步接收移位完成时会收到一个字节数据这时会置标志位置RXNE
*/
/*步骤总结完成一个字节的交换1、等待TXE为12、写发送的数据至TDR一旦TDR写出数据来。时序就会自动生成3、等待RXNE为1,发送完成即接收完成RXNE置14、读取RDR接收的数据就是置换接收的一个字节
注意1必须是发送同时接收要先写东西触发时序2根据手册发送缓冲器空闲标志TXE:写入DR时会顺便执行清楚TXE的操作无须手动清除接收缓冲器非空RXNE读取SPI数据寄存器可以清除此标志
*/
uint8_t MySPI_SwapByte(uint8_t ByteSend)
{while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_TXE) ! SET);//若没有数据则MOSI线为SETSPI_I2S_SendData(SPI1, ByteSend);//ByteSend为要写入到DR,即TDR的数据要发送的数据之后会转入到移位寄存器while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_RXNE) ! SET);//接收数据完毕的时候MISO线有标志位RXNEreturn SPI_I2S_ReceiveData(SPI1);//读取DR从RDR中把交换接收的数据读取出来返回值为RDR接收的数据
}