完善资料让更多小伙伴认识你,还能领取20积分哦, 立即完善>
本实验使用硬件的SPI读写W25Q16JV,通过硬件SPI可减轻CPU负担。这个W25Qx通常用来存储程序,可在W25Qx内部直接运行程序,无需移动到单片机的内部RAM中,当然用来存储数据也行的。
这里W25Qx的芯片类型有点多,大体操作都相同,每种类型都有一丢丢区别,建议用哪一块就去找对应的芯片(本人就被坑了一次,啊哈哈哈哈~)。 1. 实验内容及步骤: 1. 配置GPIO,MOSI(复用推挽输出)、MISO(浮空输入)、CS(推挽输出)、SCK(复用输出); 2. 软件SPI读写Byte(模式0或模式3); 3. 读取W25Q16的制造商ID和设备ID; 4. 通过标准SPI对W25Q16擦除-写入-读取; 2. 硬件说明 CS -> PB12 CLK -> PB13 MISO -> PB14 MOSI -> PB15 根据STM32数据中文参考手册可知,PB12-PB15可复用为SPI2 3. 步骤详细讲解 3.1 RCC 根据STM32中文参考手册,可知SPI2挂在在APB1(36MHz)。 3.2 配置GPIO 硬件SPI的GPIO配置如下图所示。 SPIx_SCK -> 推挽复用输出 SPIx_MOSI -> 推挽复用输出 SPIx_MISO -> 浮空输入或带上拉输入 SPIx_NSS -> 推挽复用输出 3.3 硬件SPI配置 主要配置思路: 配置SPI2为主SPI模式,全双工通讯。根据STM32F10x中文(P462)可知配置思路如下: 配置步骤1:串行时钟波特率 已知SPI2时钟接在APB1上,时钟为(36MHz) 这里我们配置为9M,即8分频36M/8=4.5M SPI2_CR1 &= ~((u32)0x07<<3); //清空配置 SPI2_CR1 |= (u32)0x02<<3; //8分频 配置步骤2:选择CPOL和CPHA位 W25Qx芯片的驱动为SPI模式0和SPI模式3,即:(详细可以看SPI原理讲解) 模式(CPOL,CPHA) Mode 0 CPOL=0, CPHA=0(0,0) Mode 3 CPOL=1, CPHA=1(1,1) 在本实验使用模式3 CPOL,CPHA分别由SPI_CR1寄存器的第1、0位控制。 使用模式3,(CPOL,CPHA)=(1,1) SPI2_CR1 |= (u32)0x03<<0; //模式3 配置步骤3:设置DFF位来定义8位或16位数据帧格式 DFF数据帧格式由SPI_CR1寄存器的第11位控制 从SPI(W25Qx芯片)是使用8位数据帧。 SPI2_CR1 &= ~((u32)0x01<<11); //8位数据帧 配置步骤4:配置SPI_CR1寄存器的LSBFIRST位定义帧格式 LSBFIRST位是由SPI_CR1寄存器的第7位控制,即控制高位先传输还是地位先传输 从SPI(W25Qx芯片)是使用高位先数传模式(MSB)。 SPI2_CR1 &= ~((u32)0x01<<7); // MSB 配置步骤5:NSS引脚工作在软件输出 软件NSS模式:可以通过设置SPI_CR1寄存器的SSM位来使能这种模式。在这种模式下NSS引脚可以用作它用,而内部NSS信号电平可以通过写SPI_CR1的SSI位来驱动 通过SSM开启软件从设备管理,并使能内部从设备选择(否则主设备不会输出) SPI2->CR1 |= (u32)1<<9; //软件SSM SPI2->CR1 |= (u32)1<<8; //使能NSS 配置步骤6:设置MSTR位和SPE位 MSTR为主设备选择由SPI_CR1寄存器的第二位控制;SPE为SPI使能由SPI_CR1寄存器的第六位控制; 配置为主设备,并使能SPI SPI_CR1 |= 0x01<<2; //主设备 SPI_CR1 |= 0x01<<6; //使能SPI 3.4 发送和接收过程 数据发送过程 当写入数据至发送缓冲器时,发送过程开始。在发送第一个数据位时,数据字被并行地(通过内部总线)传入移位寄存器,而后串行地移出到MOSI脚上;MSB在先还是LSB在先,取决于SPI_CR1寄存器中的LSBFIRST位的设置。数据从发送缓冲器传输到移位寄存器时TXE标志将被置位,如果设置了SPI_CR1寄存器中的TXEIE位,将产生中断。 数据接收过程 对于接收器来说,当数据传输完成时: ● 传送移位寄存器里的数据到接收缓冲器,并且RXNE标志被置位。 ● 如果设置了SPI_CR2寄存器中的RXNEIE位,则产生中断。 在最后采样时钟沿,RXNE位被设置,在移位寄存器中接收到的数据字被传送到接收缓冲器。读SPI_DR寄存器时,SPI设备返回接收缓冲器中的数据。读SPI_DR寄存器将清除RXNE位。 发送一个字节如上所示,先判断TEX标志位,为1(则表示发送完成)就写入DR寄存器,并等待RXNE标志位为1(则接收完成),获取DR寄存器接收的值。 一旦传输开始,如果下一个将发送的数据被放进了发送缓冲器,就可以维持一个连续的传输 流。在试图写发送缓冲器之前,需确认TXE标志应该为’1’。 4. 程序设计(寄存器) 程序中的W25Qx读写部分与软件SPI读写W25Qx一致,不同的是底层SPI,这里使用的是硬件SPI。 源码:这里只列出配置硬件SPI部分 //硬件GPIO配置 void hard_SPI_GPIO_Config(void) { //时钟 RCC->APB2ENR |= HARD_CS_RCC_CLK; //CS时钟 RCC->APB2ENR |= HARD_SCK_RCC_CLK; //SCK时钟 RCC->APB2ENR |= HARD_MISO_RCC_CLK; //MISO时钟 RCC->APB2ENR |= HARD_MOSI_RCC_CLK; //MOSI时钟 //CS 推挽复用输出 GPIOX_PIN_SetMode(HARD_CS_GPIOX,HARD_CS_PIN_NUM,GPIOx_PP|GPIOx_OUT_10M); //推挽输出、速度10MHz //SCK 推挽复用输出 GPIOX_PIN_SetMode(HARD_SCK_GPIOX,HARD_SCK_PIN_NUM,GPIOx_AF_PP|GPIOx_OUT_10M); //推挽输出、速度10MHz //MISO 浮空输入或带上拉输入 GPIOX_PIN_SetMode(HARD_MISO_GPIOX,HARD_MISO_PIN_NUM,GPIOx_IN); //输入 浮空模式 //MOSI 推挽复用输出 GPIOX_PIN_SetMode(HARD_MOSI_GPIOX,HARD_MOSI_PIN_NUM,GPIOx_AF_PP|GPIOx_OUT_10M); //推挽输出、速度10MHz } //硬件SPI配置 void hard_SPI_Config(void) { //时钟 RCC->APB1ENR |= RCC_APB1Periph_SPI2; //SPI2时钟 //配置步骤1:波特率 8分频 36M/8=4.5M SPI2->CR1 &= ~((u32)0x07<<3); //清空配置 SPI2->CR1 |= (u32)0x02<<3; //8分频 //配置步骤2:选择CPOL和CPHA位 SPI2->CR1 |= (u32)0x03<<0; //模式3 //配置步骤3:设置DFF位来定义8位或16位数据帧格式 SPI2->CR1 &= ~((u32)0x01<<11); //8位数据帧 //配置步骤4:配置SPI_CR1寄存器的LSBFIRST位定义帧格式 SPI2->CR1 &= ~((u32)0x01<<7); // MSB //配置步骤5:NSS引脚软件输出 HARD_CS_OUT = 1; SPI2->CR1 |= (u32)1<<9; //软件SSM SPI2->CR1 |= (u32)1<<8; //使能NSS //SPI2->CR1 &= ~((u32)0x01<<8); // 配置步骤6:设置MSTR位和SPE位 SPI2->CR1 |= 0x01<<2; //主设备 SPI2->CR1 |= 0x01<<6; //使能SPI } 5. 程序设计(标准库) 标准库中与寄存器库的最大不同点在于: 1. GPIO使用GPIO_Init函数进行初始化 2. SPI使用结构体,并分别使用: SPI_Init函数:初始化 SPI_NSSInternalSoftwareConfig函数:软件使能NSS SPI_Cmd函数:使能SPI 源码:这里只列出配置硬件SPI配置部分 //硬件GPIO配置 void hard_SPI_GPIO_Config(void) { GPIO_InitTypeDef GPIO_InitStruct; //时钟 RCC_APB2PeriphClockCmd(HARD_CS_RCC_CLK,ENABLE); //CS时钟 RCC_APB2PeriphClockCmd(HARD_SCK_RCC_CLK,ENABLE); //SCK时钟 RCC_APB2PeriphClockCmd(HARD_MISO_RCC_CLK,ENABLE); //MISO时钟 RCC_APB2PeriphClockCmd(HARD_MOSI_RCC_CLK,ENABLE); //MOSI时钟 //CS 推挽输出 GPIO_InitStruct.GPIO_Mode = HARD_CS_Mode; GPIO_InitStruct.GPIO_Pin = HARD_CS_PIN; GPIO_InitStruct.GPIO_Speed = GPIO_Speed_10MHz; GPIO_Init(HARD_CS_GPIOX,&GPIO_InitStruct); //SCK(推挽输出、速度10MHz) GPIO_InitStruct.GPIO_Mode = HARD_SCK_Mode; GPIO_InitStruct.GPIO_Pin = HARD_SCK_PIN; GPIO_InitStruct.GPIO_Speed = GPIO_Speed_10MHz; GPIO_Init(HARD_SCK_GPIOX,&GPIO_InitStruct); //MISO(输入 浮空模式) GPIO_InitStruct.GPIO_Mode = HARD_MISO_Mode; GPIO_InitStruct.GPIO_Pin = HARD_MISO_PIN; GPIO_Init(HARD_MISO_GPIOX,&GPIO_InitStruct); //MOSI(推挽输出、速度10MHz) GPIO_InitStruct.GPIO_Mode = HARD_MOSI_Mode; GPIO_InitStruct.GPIO_Pin = HARD_MOSI_PIN; GPIO_InitStruct.GPIO_Speed = GPIO_Speed_10MHz; GPIO_Init(HARD_MOSI_GPIOX,&GPIO_InitStruct); } //硬件SPI配置 void hard_SPI_Config(void) { SPI_InitTypeDef SPI_InitStruct; //时钟 RCC_APB1PeriphClockCmd(RCC_APB1Periph_SPI2,ENABLE); //SPI2时钟 //配置步骤1:波特率 8分频 36M/8=4.5M SPI_InitStruct.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_8; //配置步骤2:选择CPOL和CPHA位(模式3) SPI_InitStruct.SPI_CPHA = SPI_CPHA_2Edge; SPI_InitStruct.SPI_CPOL = SPI_CPOL_High; //配置步骤3:设置DFF位来定义8位或16位数据帧格式 SPI_InitStruct.SPI_DataSize = SPI_DataSize_8b; //配置步骤4:配置SPI_CR1寄存器的LSBFIRST位定义帧格式 SPI_InitStruct.SPI_FirstBit = SPI_FirstBit_MSB; //配置步骤5:NSS引脚软件输出 SPI_InitStruct.SPI_NSS = SPI_NSS_Soft; //配置步骤6:设置MSTR位和SPE位 SPI_InitStruct.SPI_Direction = SPI_Direction_2Lines_FullDuplex; SPI_InitStruct.SPI_Mode = SPI_Mode_Master; //SPI初始化 SPI_Init(SPI2,&SPI_InitStruct); //软件使能NSS SPI_NSSInternalSoftwareConfig(SPI2,SPI_NSSInternalSoft_Set); //使能SPI SPI_Cmd(SPI2,ENABLE); HARD_CS_OUT = 1; } //发送一个字节数据 u8 hard_SPI_Byte(u8 data) { while(!(SPI2->SR&SPI_I2S_FLAG_TXE)); SPI2->DR = data; while(!(SPI2->SR&SPI_I2S_FLAG_RXNE)); return SPI2->DR; } 6. 程序设计(HAL库) HAL库与寄存器库的最大不同点在于: 1. 时钟使用__HAL_RCC_xxxx_CLK_ENABLE函数配置; 2. GPIO使用HAL_GPIO_Init函数进行初始化; 3. 硬件SPI使用HAL_SPI_Init函数配置; 使用__HAL_SPI_ENABLE函数使能SPI; 4. 使用__HAL_SPI_GET_FLAG函数获取发送完成、接收完成标志位。 源码:这里只列出配置硬件SPI配置部分 //SPI结构体 SPI_HandleTypeDef HSPIStruct; //硬件GPIO配置 void hard_SPI_GPIO_Config(void) { GPIO_InitTypeDef GPIO_InitStruct; //时钟 HARD_CS_CLK_ENABLE(); //CS时钟 HARD_SCK_CLK_ENABLE(); //SCK时钟 HARD_MISO_CLK_ENABLE(); //MISO时钟 HARD_MOSI_CLK_ENABLE(); //MOSI时钟 //CS(推挽输出、速度10MHz) GPIO_InitStruct.Mode = HARD_CS_Mode; GPIO_InitStruct.Pin = HARD_CS_PIN; GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_MEDIUM; HAL_GPIO_Init(HARD_CS_GPIOX,&GPIO_InitStruct); //SCK(复用推挽输出、速度10MHz) GPIO_InitStruct.Mode = HARD_SCK_Mode; GPIO_InitStruct.Pin = HARD_SCK_PIN; GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_MEDIUM; HAL_GPIO_Init(HARD_SCK_GPIOX,&GPIO_InitStruct); //MISO(输入 浮空模式) GPIO_InitStruct.Mode = HARD_MISO_Mode; GPIO_InitStruct.Pin = HARD_MISO_PIN; GPIO_InitStruct.Pull = GPIO_NOPULL; HAL_GPIO_Init(HARD_MOSI_GPIOX,&GPIO_InitStruct); //MOSI(复用推挽输出、速度10MHz) GPIO_InitStruct.Mode = HARD_MOSI_Mode; GPIO_InitStruct.Pin = HARD_MOSI_PIN; GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_MEDIUM; HAL_GPIO_Init(HARD_MOSI_GPIOX,&GPIO_InitStruct); } //硬件SPI配置 void hard_SPI_Config(void) { //时钟 __HAL_RCC_SPI2_CLK_ENABLE(); //SPI2时钟 HSPIStruct.Instance = SPI2; //配置步骤1:波特率 8分频 36M/8=4.5M HSPIStruct.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_8; //配置步骤2:选择CPOL和CPHA位 HSPIStruct.Init.CLKPolarity = SPI_POLARITY_HIGH; HSPIStruct.Init.CLKPhase = SPI_PHASE_2EDGE; //配置步骤3:设置DFF位来定义8位或16位数据帧格式 HSPIStruct.Init.DataSize = SPI_DATASIZE_8BIT; //配置步骤4:配置SPI_CR1寄存器的LSBFIRST位定义帧格式 HSPIStruct.Init.FirstBit = SPI_FIRSTBIT_MSB; //配置步骤5:NSS引脚软件输出 HSPIStruct.Init.NSS = SPI_NSS_SOFT; // 配置步骤6:设置MSTR位和SPE位 HSPIStruct.Init.Direction = SPI_DIRECTION_2LINES; HSPIStruct.Init.Mode = SPI_MODE_MASTER; //禁止校验 HSPIStruct.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE; //硬件SPI初始化 HAL_SPI_Init(&HSPIStruct); //使能SPI __HAL_SPI_ENABLE(&HSPIStruct); HARD_CS_OUT = 1; } //发送一个字节数据 u8 hard_SPI_Byte(u8 data) { while(!__HAL_SPI_GET_FLAG(&HSPIStruct, SPI_FLAG_TXE)); SPI2->DR = data; while(!__HAL_SPI_GET_FLAG(&HSPIStruct, SPI_FLAG_RXNE)); return SPI2->DR; } 7. 实验结果 第一部分先进行整体擦除,再进行写入(存储,读取正常) 第二部分边擦除边写入,(存储,读取正常) |
|
|
|
只有小组成员才能发言,加入小组>>
3323 浏览 9 评论
3000 浏览 16 评论
3498 浏览 1 评论
9073 浏览 16 评论
4093 浏览 18 评论
1194浏览 3评论
614浏览 2评论
const uint16_t Tab[10]={0}; const uint16_t *p; p = Tab;//报错是怎么回事?
603浏览 2评论
用NUC131单片机UART3作为打印口,但printf没有输出东西是什么原因?
2342浏览 2评论
NUC980DK61YC启动随机性出现Err-DDR是为什么?
1902浏览 2评论
小黑屋| 手机版| Archiver| 电子发烧友 ( 湘ICP备2023018690号 )
GMT+8, 2024-12-30 11:04 , Processed in 1.589269 second(s), Total 50, Slave 41 queries .
Powered by 电子发烧友网
© 2015 bbs.elecfans.com
关注我们的微信
下载发烧友APP
电子发烧友观察
版权所有 © 湖南华秋数字科技有限公司
电子发烧友 (电路图) 湘公网安备 43011202000918 号 电信与信息服务业务经营许可证:合字B2-20210191 工商网监 湘ICP备2023018690号