赞
踩
移位寄存器:右边的数据低位,一位一位从MOSI移出去,然后MISO的数据,一位一位移入到左边的数据高位。显然移位寄存器是一个右移的状态,所以图上表示的低位先行的配置。
LSBFIRST控制位:它可以控制是低位先行还是高位先行。
发送缓冲区:就是发送数据寄存器TDR
接收缓冲区:就是接收数据寄存器RDR
接收和发送缓冲区:实际上就是数据寄存器DR。TDR和RDR占用同一个地址,统一叫作DR。
写入DR时:数据从数据总线,写入到TDR
读取DR时:数据从RDR读出
移位寄存器配合数据寄存器实现连续数据流的过程:就是发送数据先写入到TDR,再转到移位寄存器发送,发送的同时,接收数据。接收到的数据,转到RDR,我们再从RDR读取数据。数据寄存器和移位寄存器配合,可以实现无延迟的连续传输。
波特率发生器:这个主要就是用来产生SCK时钟。它的内部,主要就是一个分频器。输入时钟是PCLK,72M或36M,经过分频器之后,输出到SCK引脚。这里生成的时钟肯定是和移位寄存器同步的,没产生一个周期的时钟,移入移出一个bit。
CR1寄存器:BR0、BR1、BR2这三位是用来控制分频系数。分频之后,就是SCK时钟。
SPE,是SPI使能,就是SPI_Cmd函数配置的位。
MSTR:配置主从模式。1是主模式,0是从模式,一般使用主模式。
SR状态寄存器:TXE,发送寄存器空,RXNE,接收寄存器非空。
这里的移位寄存器是左移,高位移出去,通过GPIO,到MOSI,从MOSI输出,显然这是SPI的主机。之后移入的数据,从MISO进来,通过GPIO,到移位寄存器的低位,这样循环8次,就能实现主机和从机交换一个字节。
TDR数据,整体移入寄存器的时刻,置TXE标志位。移位寄存器数据,整体移入RDR的时刻,置RXNE标志位。
SCK默认高电平。发送数据时,如果检测到TXE=1,TDR为空。就软件写入0xF1至SPI_DR,这时TDR的值就变为F1,TXE变为0。目前移位寄存器也是空,所以这个F1会立刻转入移位寄存器开始发送,波形产生,并且TXE置回1,表示可以把下一个数据放在TDR了。TXE=1,并不是立刻把数据写进去,而是一直等待,等第一个字节时序结束。时序结束,意味着接收第一个字节也完成了。这时接收的RXNE会置1,等RXNE置1后,先把接收后的数据读出来,之后,再写入下一个字节数据。
- #include "led.h"
- #include "delay.h"
- #include "key.h"
- #include "usart.h"
- #include "stdio.h"
- #include "myiic.h"
- #include "at24c0x.h"
- #include "BS8116_IIC1.h"
- #include "BS8116.h"
- #include "SPI1.h"
- #include "W25Q64.h"
-
- uint8_t Tx_Buffer[6]={0x01,0x02,0x03,0x04,0x05,0x06};
- uint8_t Rx_Buffer[6];
-
- int main(void)
- {
- // uint8_t key;
- NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
- SysTick_Config(SystemCoreClock/1000); //配置1ms的中断
- Led_Config();
- USART1_Config();
- // At24c02_Config();
- // BS8116_Config();
- W25Q64_Config();
- W25Q64_Read_ID();
-
- W25Q64_SectorErase(0x123000);
- W25Q64_PageProgram(0x123000,Tx_Buffer,6);
- W25Q64_ReadData(0x123000,Rx_Buffer,6);
- for(uint8_t i = 0; i<6; i++)
- {
- printf("%x\r\n",Rx_Buffer[i]);
- }
- while(1)
- {
- // if(LED_Period[0]>=LED_Period[1])
- // {
- // LED_Period[0]=0;
- // led3_T();
- // led4_T();
- // }
- // if(!BS8116_IRQ)//判断按键是否按下 //电容按键
- // {
- // key=BS8116ReadKey();
- // if(key&&key!=0xFF)
- // {
- // printf("按键:%c\r\n",key);
- // }
- // while(!BS8116_IRQ);
- // }
- }
-
-
- }

- #include "spi1.h"
-
-
- /*************************
- PA4 SPI1_CS 通用推挽输出
- PA5 SPI1_SCLK 复用推挽输出
- PA6 SPI1_MISO 浮空输入
- PA7 SPI1_MOSI 复用推挽输出
- **************************/
-
- void SPI1_Config(void)
- {
- //开启GPIOA的时钟和SPI1的时钟
- RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA,ENABLE);
- RCC_APB2PeriphClockCmd(RCC_APB2Periph_SPI1,ENABLE);
-
-
- //配置GPIOA
- GPIO_InitTypeDef GPIO_InitStruct;
- //GPIOA 初始化设置: 复用功能输出
- GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF; //复用
- GPIO_InitStruct.GPIO_OType = GPIO_OType_PP; //推挽
- GPIO_InitStruct.GPIO_Pin = GPIO_Pin_5|GPIO_Pin_6|GPIO_Pin_7;
- GPIO_InitStruct.GPIO_Speed = GPIO_Fast_Speed;
- GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_NOPULL; //浮空
- GPIO_Init(GPIOA,&GPIO_InitStruct);
-
- GPIO_InitStruct.GPIO_Mode = GPIO_Mode_OUT; //输出
- GPIO_InitStruct.GPIO_OType = GPIO_OType_PP; //推挽
- GPIO_InitStruct.GPIO_Pin = GPIO_Pin_4;
- GPIO_InitStruct.GPIO_Speed = GPIO_Fast_Speed;
- GPIO_Init(GPIOA,&GPIO_InitStruct);
-
-
-
- //配置引脚复用映射
- GPIO_PinAFConfig(GPIOA,GPIO_PinSource5,GPIO_AF_SPI1); //PB5 复用为 SPI1
- GPIO_PinAFConfig(GPIOA,GPIO_PinSource6,GPIO_AF_SPI1); //PB5 复用为 SPI1
- GPIO_PinAFConfig(GPIOA,GPIO_PinSource7,GPIO_AF_SPI1); //PB7 复用为 SPI1
-
- //配置SPI1
- SPI_InitTypeDef SPI_InitStruct;
-
- SPI_InitStruct.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_256; //波特率分频,选择256分频
- SPI_InitStruct.SPI_CPHA = SPI_CPHA_2Edge; //时钟相位 第二个跳变沿数据被采样
- SPI_InitStruct.SPI_CPOL = SPI_CPOL_High; //时钟极性 时钟极性默认高电平
- SPI_InitStruct.SPI_CRCPolynomial = 0; //0; //CRC校验
- SPI_InitStruct.SPI_DataSize = SPI_DataSize_8b; // SPI 发送接收 8 位帧结构
- SPI_InitStruct.SPI_Direction = SPI_Direction_2Lines_FullDuplex; //双线双向全双工
- SPI_InitStruct.SPI_FirstBit = SPI_FirstBit_MSB; //数据传输从高位开始
- SPI_InitStruct.SPI_Mode = SPI_Mode_Master; //主 SPI
- SPI_InitStruct.SPI_NSS = SPI_NSS_Soft; //NSS 信号由软件控制
-
- SPI_Init(SPI1,&SPI_InitStruct);
-
- //使能SPI1
- SPI_Cmd(SPI1,ENABLE);
-
- //拉高片选线,让从机处于未选中状态
- GPIO_SetBits(GPIOA,GPIO_Pin_4);
- }
-
-
- uint8_t SPI1_ReadWriteByte(uint8_t TXdata)
- {
- //上一次未发送完成 在这里等待
- while(!(SPI_I2S_GetFlagStatus(SPI1,SPI_I2S_FLAG_TXE)));
- //上一次发送完成,发送新的数据
- SPI_I2S_SendData(SPI1,TXdata);
- //判断有没有接收完成 未接收完成在这里等待
- while(!(SPI_I2S_GetFlagStatus(SPI1,SPI_I2S_FLAG_RXNE)));
- //接收完成 接收新的数据
- return SPI_I2S_ReceiveData(SPI1);
- }
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-

- #ifndef _SPI1_H_
- #define _SPI1_H_
-
- #include "stm32f4xx.h"
-
- void SPI1_Config(void);
- uint8_t SPI1_ReadWriteByte(uint8_t TXdata);
-
- #endif
-
- #include "W25Q64.h"
- #include "spi1.h"
- #include "stdio.h"
-
- void W25Q64_Config(void)
- {
- SPI1_Config();
- }
-
- void W25Q64_Read_ID(void)
- {
- uint8_t buff[3];
- //拉低片选线
- GPIO_ResetBits(GPIOA,GPIO_Pin_4);
-
- //发送命令
- SPI1_ReadWriteByte(W25Q64_JEDEC_ID);
- //连续发送3个字节
- buff[0] = SPI1_ReadWriteByte(W25Q64_DUMMY_BYTE);
- buff[1] = SPI1_ReadWriteByte(W25Q64_DUMMY_BYTE);
- buff[2] = SPI1_ReadWriteByte(W25Q64_DUMMY_BYTE);
-
- //将片选拉高
- GPIO_SetBits(GPIOA,GPIO_Pin_4);
-
- printf("厂家信息:%x\r\n",buff[0]);
- printf("Mermory Type ID:%x\r\n",buff[1]);
- printf("Capacity ID:%x\r\n",buff[2]);
- }
-
- //W25Q64写使能
- void W25Q64_WriteEnable(void)
- {
- //拉低片选线,让从机被选中
- GPIO_ResetBits(GPIOA,GPIO_Pin_4);
- //交换发送写使能的指令
- SPI1_ReadWriteByte(W25Q64_WRITE_ENABLE);
- //将片选拉高
- GPIO_SetBits(GPIOA,GPIO_Pin_4);
- }
-
- //W25Q64等待忙
- void W25Q64_WaitBusy(void)
- {
- uint32_t Timeout;
-
- //拉低片选线,让从机被选中
- GPIO_ResetBits(GPIOA,GPIO_Pin_4);
-
- //交换发送读状态寄存器1的指令
- SPI1_ReadWriteByte(W25Q64_READ_STATUS_REGISTER_1);
-
- Timeout = 100000; //给定超时计数时间
-
- //循环等待忙标志位
- while ((SPI1_ReadWriteByte(W25Q64_DUMMY_BYTE) & 0x01) == 0x01)
- {
- Timeout --; //等待时,计数值自减
- if (Timeout == 0) //自减到0后,等待超时
- {
- /*超时的错误处理代码,可以添加到此处*/
- break; //跳出等待,不等了
- }
- }
- //将片选拉高
- GPIO_SetBits(GPIOA,GPIO_Pin_4);
- }
-
-
- /**
- * 函 数: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(); //写使能
-
- //拉低片选线,让从机被选中
- GPIO_ResetBits(GPIOA,GPIO_Pin_4);
- SPI1_ReadWriteByte(W25Q64_PAGE_PROGRAM); //交换发送页编程的指令
- SPI1_ReadWriteByte(Address >> 16); //交换发送地址23~16位
- SPI1_ReadWriteByte(Address >> 8); //交换发送地址15~8位
- SPI1_ReadWriteByte(Address); //交换发送地址7~0位
- for (i = 0; i < Count; i ++) //循环Count次
- {
- SPI1_ReadWriteByte(DataArray[i]); //依次在起始地址后写入数据
- }
- //将片选拉高
- GPIO_SetBits(GPIOA,GPIO_Pin_4);
-
- W25Q64_WaitBusy(); //等待忙
- }
-
- /**
- * 函 数:W25Q64扇区擦除(4KB)
- * 参 数:Address 指定扇区的地址,范围:0x000000~0x7FFFFF
- * 返 回 值:无
- */
- void W25Q64_SectorErase(uint32_t Address)
- {
- W25Q64_WriteEnable(); //写使能
-
- //拉低片选线,让从机被选中
- GPIO_ResetBits(GPIOA,GPIO_Pin_4);
- SPI1_ReadWriteByte(W25Q64_SECTOR_ERASE_4KB); //交换发送扇区擦除的指令
- SPI1_ReadWriteByte(Address >> 16); //交换发送地址23~16位
- SPI1_ReadWriteByte(Address >> 8); //交换发送地址15~8位
- SPI1_ReadWriteByte(Address); //交换发送地址7~0位
- //将片选拉高
- GPIO_SetBits(GPIOA,GPIO_Pin_4);
-
- 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;
- //拉低片选线,让从机被选中
- GPIO_ResetBits(GPIOA,GPIO_Pin_4);
- SPI1_ReadWriteByte(W25Q64_READ_DATA); //交换发送读取数据的指令
- SPI1_ReadWriteByte(Address >> 16); //交换发送地址23~16位
- SPI1_ReadWriteByte(Address >> 8); //交换发送地址15~8位
- SPI1_ReadWriteByte(Address); //交换发送地址7~0位
- for (i = 0; i < Count; i ++) //循环Count次
- {
- DataArray[i] = SPI1_ReadWriteByte(W25Q64_DUMMY_BYTE); //依次在起始地址后读取数据
- }
- //将片选拉高
- GPIO_SetBits(GPIOA,GPIO_Pin_4);
- }
-

- #ifndef _W25Q64_H_
- #define _W25Q64_H_
-
- #include "stm32f4xx.h"
-
- #define W25Q64_WRITE_ENABLE 0x06
- #define W25Q64_WRITE_DISABLE 0x04
- #define W25Q64_READ_STATUS_REGISTER_1 0x05
- #define W25Q64_READ_STATUS_REGISTER_2 0x35
- #define W25Q64_WRITE_STATUS_REGISTER 0x01
- #define W25Q64_PAGE_PROGRAM 0x02
- #define W25Q64_QUAD_PAGE_PROGRAM 0x32
- #define W25Q64_BLOCK_ERASE_64KB 0xD8
- #define W25Q64_BLOCK_ERASE_32KB 0x52
- #define W25Q64_SECTOR_ERASE_4KB 0x20
- #define W25Q64_CHIP_ERASE 0xC7
- #define W25Q64_ERASE_SUSPEND 0x75
- #define W25Q64_ERASE_RESUME 0x7A
- #define W25Q64_POWER_DOWN 0xB9
- #define W25Q64_HIGH_PERFORMANCE_MODE 0xA3
- #define W25Q64_CONTINUOUS_READ_MODE_RESET 0xFF
- #define W25Q64_RELEASE_POWER_DOWN_HPM_DEVICE_ID 0xAB
- #define W25Q64_MANUFACTURER_DEVICE_ID 0x90
- #define W25Q64_READ_UNIQUE_ID 0x4B
- #define W25Q64_JEDEC_ID 0x9F
- #define W25Q64_READ_DATA 0x03
- #define W25Q64_FAST_READ 0x0B
- #define W25Q64_FAST_READ_DUAL_OUTPUT 0x3B
- #define W25Q64_FAST_READ_DUAL_IO 0xBB
- #define W25Q64_FAST_READ_QUAD_OUTPUT 0x6B
- #define W25Q64_FAST_READ_QUAD_IO 0xEB
- #define W25Q64_OCTAL_WORD_READ_QUAD_IO 0xE3
-
- #define W25Q64_DUMMY_BYTE 0xFF
-
- void W25Q64_Config(void);
- void W25Q64_Read_ID(void);
- void W25Q64_PageProgram(uint32_t Address, uint8_t *DataArray, uint16_t Count);
- void W25Q64_SectorErase(uint32_t Address);
- void W25Q64_ReadData(uint32_t Address, uint8_t *DataArray, uint32_t Count);
-
-
-
-
- #endif
-

Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。