零、瞎BB一些
最近真的是.....过得...些许艰难...
从实习到正式入职,在公司的项目组待了3个月左右了,同事、项目、代码、逻辑、架构都熟悉了,毕业后入职1个月的时间里,能给项目组改了几个bug,能接小小的需求写代码,测试,联调,上线功能,正在自我感觉良好一切都渐渐适应,每天干活也开心得飞起的时候,leader通知我换部门....
来了新部门,一切都变了,项目不一样,代码的架构完全变了,之前项目组的代码庞大,是基于rtos的消息通知机制的架构,所以我直接关注到业务逻辑;而新的项目组的代码,没上rtos,是个逻辑代码,业务代码就是HAL库的代码,很底层,就是使用HAL库的spi、dma、tim、usart等等驱动直接些业务代码,而这些我都掌握的不太好,这太底层了。新项目中还用到的六轴传感器,这也是一个大坑,我的新导师都说这个东西很难,我看了几天六轴的相关资料、文档、驱动算法、正点原子的开源飞行器的书籍和代码,真的是...一头雾水....
新部门的人....也不熟悉,我自己又是孤僻的一批.... 感觉也没人爱搭理我...
不过,唯一的好处就是:这些又带来了新的挑战!之前的项目组我惊叹于FreeRTOS系统的魅力,系统架构在RTOS上样子,基于消息通知机制的实时任务系统是这样的结构,并且业务代码也相当的复杂。而现在的项目,更接近底层,我正好能学一学嵌入式的基础,STM32底层的驱动开发,这些太基础了,是一个学生在校期间就英爱学会了,而我是来了公司才接触嵌入式,底层的也就大概的看了看就去看项目代码了,项目代码中将底层的驱动层层封装,根本就没机会看到底层接口,上层接口的业务逻辑就够喝一壶了。
这次这个机会,正好!
STM32就是学习各类基础知识,原理啊,通讯协议啊之类的。
各种外设初始化也是走那个流程:使能时钟、配置句柄(xxx_HandleTpyeDef)、配置硬件底层相关(HAL_xxx_MspInit),用HAL库函数初始化句柄(其实就是讲配置好的内容写进寄存器,因为HAL库本来就是对寄存器的上层的封装)。
这里多加一个HAL_xxx_MspDeInit,项目代码里很多DeInit函数,其实就是【失能】掉一个外设的函数,因为初始化的函数相当于要用一个外设就要使能,等不用的时候就要失能。
各类外设要先掌握各原理,什么通信协议,那些总线(原理图)
比如SPI:通讯时就使用3条总线和一条片选信号线:SCK MOSI MISO NSS
UART的话比较简单:就是TX RX 两条通信时的引脚
DMA的话,有很多通道,要去芯片手册中看DMA的哪个通道能连接哪个外设,叫【DMA请求映像表】,DMA没什么引脚要配置,使用时就注意结构体的成员的配置:什么传输方向之类,最后配置完用__HAL_LINKDMA() 将dma句柄和外设句柄连接起来,啧啧啧多形象的函数命令,多好理解。
感觉之前学STM走了些弯路,一直用标准外设库去写,没早点用一下HAL库,对HAL库的不熟悉,让我到了新项目组一直学习学习看书看书,没有参与项目。而且师兄说标准外设库都快淘汰了...
代码也粘一下,毕竟都是自己手把手敲的,虽然....都是野火的教程里的代码。
一、UART
#ifndef BSP_USART_H #define BSP_USART_H #include "stm32f1xx.h" #include <stdio.h> //串口波特率 #define DEBUG_USART_BAUDRATE 115200 //引脚定义 /*******************************************************/ #define DEBUG_USART USART1 #define DEBUG_USART_CLK_ENABLE() __HAL_RCC_USART1_CLK_ENABLE() #define DEBUG_USART_RX_GPIO_PORT GPIOA #define DEBUG_USART_RX_GPIO_CLK_ENABLE() __HAL_RCC_GPIOA_CLK_ENABLE() #define DEBUG_USART_RX_PIN GPIO_PIN_10 #define DEBUG_USART_TX_GPIO_PORT GPIOA #define DEBUG_USART_TX_GPIO_CLK_ENABLE() __HAL_RCC_GPIOA_CLK_ENABLE() #define DEBUG_USART_TX_PIN GPIO_PIN_9 #define DEBUG_USART_IRQHandler USART1_IRQHandler #define DEBUG_USART_IRQ USART1_IRQn void Usart_SendString(uint8_t *str); void DEBUG_USART_Config(void); int fputc(int ch, FILE *f); int fgetc(FILE *f); extern UART_HandleTypeDef UartHandle; #endif
#include "bsp_usart.h" // UartHandle管理串口所有配置 UART_HandleTypeDef UartHandle; //配置与硬件底层无关内容:如串口协议,其中包括波特率,奇偶校验,停止位 void DEBUG_USART_Config() { UartHandle.Instance = DEBUG_USART; //波特率,8位字长,1停止位,无奇偶校验,无硬件控制,收发模式 UartHandle.Init.BaudRate = DEBUG_USART_BAUDRATE; UartHandle.Init.WordLength = UART_WORDLENGTH_8B; UartHandle.Init.StopBits = UART_STOPBITS_1;//stm32f1xx_hal_uart.h UartHandle.Init.Parity = UART_PARITY_NONE; UartHandle.Init.HwFlowCtl = UART_HWCONTROL_NONE; UartHandle.Init.Mode = UART_MODE_TX_RX; HAL_UART_Init(&UartHandle); //使能串口接收中断 __HAL_UART_ENABLE_IT(&UartHandle,UART_IT_RXNE); } // mcu 底层硬件相关的配置如引脚、时钟、DMA、中断 //实际被HAL_UART_Init(stm32f1xx_hal_uart.c)该函数调用 void HAL_UART_MspInit(UART_HandleTypeDef *huart) { GPIO_InitTypeDef GPIO_Init; //串口时钟 GPIO时钟 DEBUG_USART_CLK_ENABLE(); DEBUG_USART_RX_GPIO_CLK_ENABLE(); DEBUG_USART_TX_GPIO_CLK_ENABLE(); //配置引脚复用功能 TX GPIO_Init.Pin = DEBUG_USART_TX_PIN; GPIO_Init.Mode = GPIO_MODE_AF_PP; GPIO_Init.Pull = GPIO_PULLUP; GPIO_Init.Speed = GPIO_SPEED_FREQ_HIGH; HAL_GPIO_Init(DEBUG_USART_TX_GPIO_PORT, &GPIO_Init); //配置Rx GPIO_Init.Pin = DEBUG_USART_RX_PIN; GPIO_Init.Mode = GPIO_MODE_AF_INPUT;//复用输入模式 HAL_GPIO_Init(DEBUG_USART_RX_GPIO_PORT, &GPIO_Init); HAL_NVIC_SetPriority(DEBUG_USART_IRQ ,0,1); //抢占优先级0,子优先级1 HAL_NVIC_EnableIRQ(DEBUG_USART_IRQ ); //使能USART1中断通道 } /***************** 发送字符串 **********************/ /* HAL_UART_Transmit 函数(这是一个阻塞的发送函数,无需重复判断串口是否发送完成) 发送每个字符,直到遇到 空字符才停止发送。*/ void Usart_SendString(uint8_t *str) { unsigned int k=0; do { HAL_UART_Transmit(&UartHandle,(uint8_t *)(str + k) ,1,1000); k++; } while(*(str + k)!='\0'); } //重定向c库函数printf到串口DEBUG_USART,重定向后可使用printf函数 int fputc(int ch, FILE *f) { /* 发送一个字节数据到串口DEBUG_USART */ HAL_UART_Transmit(&UartHandle, (uint8_t *)&ch, 1, 1000); return (ch); } //重定向c库函数scanf到串口DEBUG_USART,重写向后可使用scanf、getchar等函数 int fgetc(FILE *f) { int ch; HAL_UART_Receive(&UartHandle, (uint8_t *)&ch, 1, 1000); return (ch); }
//stm32f1xx_it.c //中断函数 void DEBUG_USART_IRQHandler(void) { uint8_t ch = 1; if (__HAL_UART_GET_FLAG( &UartHandle, UART_FLAG_RXNE ) != RESET) { ch=( uint16_t)READ_REG(UartHandle.Instance->DR); WRITE_REG ( UartHandle.Instance->DR,ch); } }
二、DMA
dma直接读取存储器(RAM)的内容 给 usart,uart的tx接收到dma的数据后,tx是发送端,会将数据发给电脑,上位机可看到日志输出。
#ifndef BSP_DMA_H #define BSP_DMA_H #include "stm32f1xx.h" //DMA #define SENDBUFF_SIZE 1000//发送的数据量 #define DEBUG_USART_DMA_CLK_ENABLE() __HAL_RCC_DMA1_CLK_ENABLE() #define DEBUG_USART_DMA_STREAM DMA1_Channel4 void USART_DMA_Config(void); #endif
#include "bsp_dma.h" //DMA 读取RAM中数据后 ,发送给usart(串口接收到数据打印出来) uint8_t SendBuff[SENDBUFF_SIZE]; DMA_HandleTypeDef DMA_Handle; //DMA句柄 extern UART_HandleTypeDef UartHandle; //USRT句柄(定义在bsp_usart.c中) void USART_DMA_Config(void) { //1.使能时钟 2.配置句柄 3.HAL库函数初始化句柄 4.连接两个句柄 __HAL_RCC_DMA1_CLK_ENABLE(); DMA_Handle.Instance = DMA1_Channel4;//通道4(再数据手册中查看DMA的通道请求映像表) DMA_Handle.Init.Direction = DMA_MEMORY_TO_PERIPH; //存储器到外设 DMA_Handle.Init.PeriphInc = DMA_PINC_DISABLE; //外设非增量模式 DMA_Handle.Init.MemInc = DMA_MINC_ENABLE; //存储器增量模式 DMA_Handle.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE; //外设数据长度:8位 DMA_Handle.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE; //存储器数据长度:8位 DMA_Handle.Init.Mode = DMA_NORMAL; //外设普通模式 DMA_Handle.Init.Priority = DMA_PRIORITY_MEDIUM; //中等优先级 HAL_DMA_Init(&DMA_Handle); //连接DMA句柄 __HAL_LINKDMA(&UartHandle,hdmatx,DMA_Handle); }
三、SPI
spi野火的教程是用来读取Flash,这部分的调试 遇到了一些问题.... 代码调不通,读取不到flash的内容.....
#ifndef BSP_SPI_H #define BSP_SPI_H #include "stm32f1xx.h" //FLASH相关 #define sFLASH_ID 0XEF4017 //W25Q64 #define SPI_FLASH_PageSize 256 #define SPI_FLASH_PerWritePageSize 256 #define W25X_WriteEnable 0x06 #define W25X_WriteDisable 0x04 #define W25X_ReadStatusReg 0x05 #define W25X_WriteStatusReg 0x01 #define W25X_ReadData 0x03 #define W25X_FastReadData 0x0B #define W25X_FastReadDual 0x3B #define W25X_PageProgram 0x02 #define W25X_BlockErase 0xD8 #define W25X_SectorErase 0x20 #define W25X_ChipErase 0xC7 #define W25X_PowerDown 0xB9 #define W25X_ReleasePowerDown 0xAB #define W25X_DeviceID 0xAB #define W25X_ManufactDeviceID 0x90 #define W25X_JedecDeviceID 0x9F #define WIP_Flag 0x01 /* Write In Progress (WIP) flag */ #define Dummy_Byte 0xFF //spi1参数定义:spi号 和 GPIO等 #define SPIx SPI1 #define SPIx_CLK_ENABLE() __HAL_RCC_SPI1_CLK_ENABLE() #define SPIx_SCK_GPIO_CLK_ENABLE() __HAL_RCC_GPIOA_CLK_ENABLE() #define SPIx_MISO_GPIO_CLK_ENABLE() __HAL_RCC_GPIOA_CLK_ENABLE() #define SPIx_MOSI_GPIO_CLK_ENABLE() __HAL_RCC_GPIOA_CLK_ENABLE() #define SPIx_CS_GPIO_CLK_ENABLE() __HAL_RCC_GPIOC_CLK_ENABLE() //强制或解除APB2外围设备复位 #define SPIx_FORCE_RESET() __HAL_RCC_SPI1_FORCE_RESET() #define SPIx_RELEASE_RESET() __HAL_RCC_SPI1_RELEASE_RESET() //SPI1引脚宏定义 SCK MISO MOSI NSS(CS) #define SPIx_SCK_PIN GPIO_PIN_5 #define SPIx_SCK_GPIO_PORT GPIOA #define SPIx_MISO_PIN GPIO_PIN_6 #define SPIx_MISO_GPIO_PORT GPIOA #define SPIx_MOSI_PIN GPIO_PIN_7 #define SPIx_MOSI_GPIO_PORT GPIOA #define FLASH_CS_PIN GPIO_PIN_0 #define FLASH_CS_GPIO_PORT GPIOC //控制 CS(NSS)引脚输出电平的宏(配置产生起始和停止信号) #define digitalHi(p,i) {p->BSRR=i;} //设置为高电平 #define digitalLo(p,i) {p->BSRR=(uint32_t)i << 16;} //输出低电平 #define SPI_FLASH_CS_LOW() digitalLo(FLASH_CS_GPIO_PORT,FLASH_CS_PIN ) #define SPI_FLASH_CS_HIGH() digitalHi(FLASH_CS_GPIO_PORT,FLASH_CS_PIN ) /*等待超时时间*/ #define SPIT_FLAG_TIMEOUT ((uint32_t)0x1000) #define SPIT_LONG_TIMEOUT ((uint32_t)(10 * SPIT_FLAG_TIMEOUT)) /*信息输出*/ #define FLASH_DEBUG_ON 1 #define FLASH_INFO(fmt,arg...) printf("<<-FLASH-INFO->> "fmt"\n",##arg) #define FLASH_ERROR(fmt,arg...) printf("<<-FLASH-ERROR->> "fmt"\n",##arg) #define FLASH_DEBUG(fmt,arg...) do{\ if(FLASH_DEBUG_ON)\ printf("<<-FLASH-DEBUG->> [%d]"fmt"\n",__LINE__, ##arg);\ }while(0) /*************************************************************************************************************/ //初始化 void SPI_FLASH_Init(void); // void SPI_FLASH_SectorErase(uint32_t SectorAddr); void SPI_FLASH_BulkErase(void); void SPI_FLASH_PageWrite(uint8_t* pBuffer, uint32_t WriteAddr, uint16_t NumByteToWrite); void SPI_FLASH_BufferWrite(uint8_t* pBuffer, uint32_t WriteAddr, uint16_t NumByteToWrite); void SPI_FLASH_BufferRead(uint8_t* pBuffer, uint32_t ReadAddr, uint16_t NumByteToRead); uint32_t SPI_FLASH_ReadID(void); uint32_t SPI_FLASH_ReadDeviceID(void); void SPI_FLASH_StartReadSequence(uint32_t ReadAddr); void SPI_Flash_PowerDown(void); void SPI_Flash_WAKEUP(void); uint8_t SPI_FLASH_ReadByte(void); uint8_t SPI_FLASH_SendByte(uint8_t byte); uint16_t SPI_FLASH_SendHalfWord(uint16_t HalfWord); void SPI_FLASH_WriteEnable(void); void SPI_FLASH_WaitForWriteEnd(void); #endif
#include "bsp_spi.h" /* 初始化: GPIO 结构体 SPI 结构体 写应用接口: 读flash 写flash 擦除... */ //SPI句柄结构体 SPI_HandleTypeDef HandleSPI; static __IO uint32_t SPITimeout = SPIT_LONG_TIMEOUT; static uint16_t SPI_TIMEOUT_UserCallback(uint8_t errorCode); //配置硬件资源 //初始化SPI GPIO部分引脚 GPIO 引脚模式初始化 复用功能(用户定义强函数) void HAL_SPI_MspInit(SPI_HandleTypeDef* hspi) { GPIO_InitTypeDef GPIO_InitStruct; //使能GPIO 和SPI 时钟 __HAL_RCC_GPIOA_CLK_ENABLE(); __HAL_RCC_GPIOC_CLK_ENABLE(); __HAL_RCC_SPI1_CLK_ENABLE(); //配置4个GPIO引脚 SCK MOSI MISO NSS GPIO_InitStruct.Pin = SPIx_SCK_PIN; GPIO_InitStruct.Mode = GPIO_MODE_AF_PP; GPIO_InitStruct.Pull = GPIO_PULLUP; GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH; HAL_GPIO_Init(SPIx_SCK_GPIO_PORT, &GPIO_InitStruct); GPIO_InitStruct.Pin = SPIx_MISO_PIN; HAL_GPIO_Init(SPIx_MISO_GPIO_PORT, &GPIO_InitStruct); GPIO_InitStruct.Pin = SPIx_MOSI_PIN; HAL_GPIO_Init(SPIx_MOSI_GPIO_PORT, &GPIO_InitStruct); GPIO_InitStruct.Pin = FLASH_CS_PIN; GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP; HAL_GPIO_Init(SPIx_MOSI_GPIO_PORT, &GPIO_InitStruct);//向寄存器写入参数(完成GPIO初始化) } //初始化SPI模式: void SPI_FLASH_Init(void) { HandleSPI.Instance = SPI1; // SPI 外设配置为主机端,双线全双工模式,数据帧长度为 8位,使用 SPI 模式 3(CLKPolarity =1,CLKPhase =1),NSS 引脚由软件控制以及 MSB 先行模式 HandleSPI.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_4; HandleSPI.Init.Direction = SPI_DIRECTION_2LINES; HandleSPI.Init.CLKPhase = SPI_PHASE_2EDGE; HandleSPI.Init.CLKPolarity = SPI_POLARITY_HIGH; HandleSPI.Init.CRCPolynomial = 7; HandleSPI.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE; HandleSPI.Init.DataSize = SPI_DATASIZE_8BIT; HandleSPI.Init.FirstBit = SPI_FIRSTBIT_MSB; HandleSPI.Init.NSS = SPI_NSS_SOFT; HandleSPI.Init.TIMode = SPI_TIMODE_DISABLE; HandleSPI.Init.Mode = SPI_MODE_MASTER;//主模式 //配置写进寄存器、使能SPI HAL_SPI_Init(&HandleSPI); __HAL_SPI_ENABLE(&HandleSPI); } /** * @brief 使用SPI发送一个字节的数据 * @param byte:要发送的数据 * @retval 返回接收到的数据 */ uint8_t SPI_FLASH_SendByte(uint8_t byte) { SPITimeout = SPIT_FLAG_TIMEOUT; //等待发送缓冲区为空 txe事件(Tx buff empty) while(RESET == __HAL_SPI_GET_FLAG(&HandleSPI,SPI_FLAG_TXE)) { if ((SPITimeout--) == 0) return SPI_TIMEOUT_UserCallback(0); } //写入数据寄存器,把要写入的数据写入发送缓冲区 WRITE_REG(HandleSPI.Instance->DR,byte); SPITimeout = SPIT_FLAG_TIMEOUT; //等待接收缓冲区非空 RXNE事件(Rx buffer empty flag) while(RESET == __HAL_SPI_GET_FLAG(&HandleSPI,SPI_FLAG_RXNE)) { if((SPITimeout--) == 0) return SPI_TIMEOUT_UserCallback(1); } //读取数据寄存区 获取接收缓冲区数据 return READ_REG(HandleSPI.Instance->DR); } /** * @brief 使用SPI读取一个字节的数据 * @param 无 * @retval 返回接收到的数据 */ uint8_t SPI_FLASH_ReadByte(void) { return (SPI_FLASH_SendByte(Dummy_Byte)); } /** * @brief 读取FLASH Device ID * @param 无 * @retval FLASH Device ID */ uint32_t SPI_FLASH_ReadDeviceID(void) { uint32_t Temp = 0; /* Select the FLASH: Chip Select low */ SPI_FLASH_CS_LOW(); /* Send "RDID " instruction */ SPI_FLASH_SendByte(W25X_DeviceID); printf("recv1 = %x\n",SPI_FLASH_SendByte(Dummy_Byte)); printf("recv2 = %x\n",SPI_FLASH_SendByte(Dummy_Byte)); printf("recv3 = %x\n",SPI_FLASH_SendByte(Dummy_Byte)); /* Read a byte from the FLASH */ Temp = SPI_FLASH_SendByte(Dummy_Byte); printf("recv4 = %x\n",Temp); /* Deselect the FLASH: Chip Select high */ SPI_FLASH_CS_HIGH(); return Temp; } /** * @brief 读取FLASH ID * @retval FLASH ID */ uint32_t SPI_FLASH_ReadID(void) { uint32_t temp = 0,temp1 = 0,temp0 = 0,temp2 = 0; //开始通讯:CS低电平 SPI_FLASH_CS_LOW(); //发送JEDEC指令 SPI_FLASH_SendByte(W25X_JedecDeviceID); //读取数据(结果) temp0 = SPI_FLASH_SendByte(Dummy_Byte); temp1 = SPI_FLASH_SendByte(Dummy_Byte); temp2 = SPI_FLASH_SendByte(Dummy_Byte); //停止通讯:CS高电平 SPI_FLASH_CS_HIGH(); //数据组合起来 返回 temp = (temp0 << 16) | (temp1 << 8) | temp2; return temp; } //发送 “写使能”给flash(之后可以对flash芯片存储矩阵写入数据) void SPI_FLASH_WriteEnable() { SPI_FLASH_CS_LOW(); //开始通讯:CS低电平 SPI_FLASH_SendByte(W25X_WriteEnable); //发送xxx指令 SPI_FLASH_CS_HIGH(); //停止通讯:CS高电平 } //WIP(busy)标志 flash内粗正在写入 #define WIP_Flag 0x01 /* *@brief 读取状态寄存器等待FLASH芯片空闲 * */ void SPI_FLASH_WaitForWriteEnd(void) { uint8_t FLASH_Status = 0; SPI_FLASH_CS_LOW(); SPI_FLASH_SendByte(W25X_ReadStatusReg); do //若flash忙 则等待 { FLASH_Status = SPI_FLASH_SendByte(Dummy_Byte); }while(SET == (FLASH_Status & WIP_Flag)); SPI_FLASH_CS_HIGH(); } /** * @brief 擦除FLASH扇区 * @param SectorAddr:要擦除的扇区地址 * @retval 无 */ void SPI_FLASH_SectorErase(uint32_t SectorAddr) { /* 发送FLASH写使能命令 */ SPI_FLASH_WriteEnable(); SPI_FLASH_WaitForWriteEnd(); /* 擦除扇区 */ /* 选择FLASH: CS低电平 */ SPI_FLASH_CS_LOW(); /* 发送扇区擦除指令*/ SPI_FLASH_SendByte(W25X_SectorErase); /*发送擦除扇区地址的高位*/ SPI_FLASH_SendByte((SectorAddr & 0xFF0000) >> 16); /* 发送擦除扇区地址的中位 */ SPI_FLASH_SendByte((SectorAddr & 0xFF00) >> 8); /* 发送擦除扇区地址的低位 */ SPI_FLASH_SendByte(SectorAddr & 0xFF); /* 停止信号 FLASH: CS 高电平 */ SPI_FLASH_CS_HIGH(); /* 等待擦除完毕*/ SPI_FLASH_WaitForWriteEnd(); } /** * @brief 擦除FLASH扇区,整片擦除 * @param 无 * @retval 无 */ void SPI_FLASH_BulkErase(void) { /* 发送FLASH写使能命令 */ SPI_FLASH_WriteEnable(); /* 整块 Erase */ /* 选择FLASH: CS低电平 */ SPI_FLASH_CS_LOW(); /* 发送整块擦除指令*/ SPI_FLASH_SendByte(W25X_ChipErase); /* 停止信号 FLASH: CS 高电平 */ SPI_FLASH_CS_HIGH(); /* 等待擦除完毕*/ SPI_FLASH_WaitForWriteEnd(); } /** * @brief 对FLASH按页写入数据,调用本函数写入数据前需要先擦除扇区 * @param pBuffer,要写入数据的指针 * @param WriteAddr,写入地址 * @param NumByteToWrite,写入数据长度,必须小于等于SPI_FLASH_PerWritePageSize * @retval 无 */ void SPI_FLASH_PageWrite(uint8_t* pBuffer, uint32_t WriteAddr, uint16_t NumByteToWrite) { /* 发送FLASH写使能命令 */ SPI_FLASH_WriteEnable(); /* 选择FLASH: CS低电平 */ SPI_FLASH_CS_LOW(); /* 写页写指令*/ SPI_FLASH_SendByte(W25X_PageProgram); /*发送写地址的高位*/ SPI_FLASH_SendByte((WriteAddr & 0xFF0000) >> 16); /*发送写地址的中位*/ SPI_FLASH_SendByte((WriteAddr & 0xFF00) >> 8); /*发送写地址的低位*/ SPI_FLASH_SendByte(WriteAddr & 0xFF); if(NumByteToWrite > SPI_FLASH_PerWritePageSize) { NumByteToWrite = SPI_FLASH_PerWritePageSize; FLASH_ERROR("SPI_FLASH_PageWrite too large!"); } /* 写入数据*/ while (NumByteToWrite--) { /* 发送当前要写入的字节数据 */ SPI_FLASH_SendByte(*pBuffer); /* 指向下一字节数据 */ pBuffer++; } /* 停止信号 FLASH: CS 高电平 */ SPI_FLASH_CS_HIGH(); /* 等待写入完毕*/ SPI_FLASH_WaitForWriteEnd(); } /** * @brief 对FLASH写入数据,调用本函数写入数据前需要先擦除扇区 * @param pBuffer,要写入数据的指针 * @param WriteAddr,写入地址 * @param NumByteToWrite,写入数据长度 * @retval 无 */ void SPI_FLASH_BufferWrite(uint8_t* pBuffer, uint32_t WriteAddr, uint16_t NumByteToWrite) { uint8_t NumOfPage = 0, NumOfSingle = 0, Addr = 0, count = 0, temp = 0; /*mod运算求余,若writeAddr是SPI_FLASH_PageSize整数倍,运算结果Addr值为0*/ Addr = WriteAddr % SPI_FLASH_PageSize; /*差count个数据值,刚好可以对齐到页地址*/ count = SPI_FLASH_PageSize - Addr; /*计算出要写多少整数页*/ NumOfPage = NumByteToWrite / SPI_FLASH_PageSize; /*mod运算求余,计算出剩余不满一页的字节数*/ NumOfSingle = NumByteToWrite % SPI_FLASH_PageSize; /* Addr=0,则WriteAddr 刚好按页对齐 aligned */ if (Addr == 0) { /* NumByteToWrite < SPI_FLASH_PageSize */ if (NumOfPage == 0) { SPI_FLASH_PageWrite(pBuffer, WriteAddr, NumByteToWrite); } else /* NumByteToWrite > SPI_FLASH_PageSize */ { /*先把整数页都写了*/ while (NumOfPage--) { SPI_FLASH_PageWrite(pBuffer, WriteAddr, SPI_FLASH_PageSize); WriteAddr += SPI_FLASH_PageSize; pBuffer += SPI_FLASH_PageSize; } /*若有多余的不满一页的数据,把它写完*/ SPI_FLASH_PageWrite(pBuffer, WriteAddr, NumOfSingle); } } /* 若地址与 SPI_FLASH_PageSize 不对齐 */ else { /* NumByteToWrite < SPI_FLASH_PageSize */ if (NumOfPage == 0) { /*当前页剩余的count个位置比NumOfSingle小,写不完*/ if (NumOfSingle > count) { temp = NumOfSingle - count; /*先写满当前页*/ SPI_FLASH_PageWrite(pBuffer, WriteAddr, count); WriteAddr += count; pBuffer += count; /*再写剩余的数据*/ SPI_FLASH_PageWrite(pBuffer, WriteAddr, temp); } else /*当前页剩余的count个位置能写完NumOfSingle个数据*/ { SPI_FLASH_PageWrite(pBuffer, WriteAddr, NumByteToWrite); } } else /* NumByteToWrite > SPI_FLASH_PageSize */ { /*地址不对齐多出的count分开处理,不加入这个运算*/ NumByteToWrite -= count; NumOfPage = NumByteToWrite / SPI_FLASH_PageSize; NumOfSingle = NumByteToWrite % SPI_FLASH_PageSize; SPI_FLASH_PageWrite(pBuffer, WriteAddr, count); WriteAddr += count; pBuffer += count; /*把整数页都写了*/ while (NumOfPage--) { SPI_FLASH_PageWrite(pBuffer, WriteAddr, SPI_FLASH_PageSize); WriteAddr += SPI_FLASH_PageSize; pBuffer += SPI_FLASH_PageSize; } /*若有多余的不满一页的数据,把它写完*/ if (NumOfSingle != 0) { SPI_FLASH_PageWrite(pBuffer, WriteAddr, NumOfSingle); } } } } /** * @brief 读取FLASH数据 * @param pBuffer,存储读出数据的指针 * @param ReadAddr,读取地址 * @param NumByteToRead,读取数据长度 * @retval 无 */ void SPI_FLASH_BufferRead(uint8_t* pBuffer, uint32_t ReadAddr, uint16_t NumByteToRead) { /* 选择FLASH: CS低电平 */ SPI_FLASH_CS_LOW(); /* 发送 读 指令 */ SPI_FLASH_SendByte(W25X_ReadData); /* 发送 读 地址高位 */ SPI_FLASH_SendByte((ReadAddr & 0xFF0000) >> 16); /* 发送 读 地址中位 */ SPI_FLASH_SendByte((ReadAddr& 0xFF00) >> 8); /* 发送 读 地址低位 */ SPI_FLASH_SendByte(ReadAddr & 0xFF); /* 读取数据 */ while (NumByteToRead--) { /* 读取一个字节*/ *pBuffer = SPI_FLASH_SendByte(Dummy_Byte); /* 指向下一个字节缓冲区 */ pBuffer++; } /* 停止信号 FLASH: CS 高电平 */ SPI_FLASH_CS_HIGH(); } /******************************************************************************* * Function Name : SPI_FLASH_StartReadSequence * Description : Initiates a read data byte (READ) sequence from the Flash. * This is done by driving the /CS line low to select the device, * then the READ instruction is transmitted followed by 3 bytes * address. This function exit and keep the /CS line low, so the * Flash still being selected. With this technique the whole * content of the Flash is read with a single READ instruction. * Input : - ReadAddr : FLASH's internal address to read from. * Output : None * Return : None *******************************************************************************/ void SPI_FLASH_StartReadSequence(uint32_t ReadAddr) { /* Select the FLASH: Chip Select low */ SPI_FLASH_CS_LOW(); /* Send "Read from Memory " instruction */ SPI_FLASH_SendByte(W25X_ReadData); /* Send the 24-bit address of the address to read from -----------------------*/ /* Send ReadAddr high nibble address byte */ SPI_FLASH_SendByte((ReadAddr & 0xFF0000) >> 16); /* Send ReadAddr medium nibble address byte */ SPI_FLASH_SendByte((ReadAddr& 0xFF00) >> 8); /* Send ReadAddr low nibble address byte */ SPI_FLASH_SendByte(ReadAddr & 0xFF); } /******************************************************************************* * Function Name : SPI_FLASH_SendHalfWord * Description : Sends a Half Word through the SPI interface and return the * Half Word received from the SPI bus. * Input : Half Word : Half Word to send. * Output : None * Return : The value of the received Half Word. *******************************************************************************/ uint16_t SPI_FLASH_SendHalfWord(uint16_t HalfWord) { SPITimeout = SPIT_FLAG_TIMEOUT; /* Loop while DR register in not emplty */ while (__HAL_SPI_GET_FLAG( &HandleSPI, SPI_FLAG_TXE ) == RESET) { if((SPITimeout--) == 0) return SPI_TIMEOUT_UserCallback(2); } /* Send Half Word through the SPIx peripheral */ WRITE_REG(HandleSPI.Instance->DR, HalfWord); SPITimeout = SPIT_FLAG_TIMEOUT; /* Wait to receive a Half Word */ while (__HAL_SPI_GET_FLAG( &HandleSPI, SPI_FLAG_RXNE ) == RESET) { if((SPITimeout--) == 0) return SPI_TIMEOUT_UserCallback(3); } /* Return the Half Word read from the SPI bus */ return READ_REG(HandleSPI.Instance->DR); } //进入掉电模式 void SPI_Flash_PowerDown(void) { /* 选择 FLASH: CS 低 */ SPI_FLASH_CS_LOW(); /* 发送 掉电 命令 */ SPI_FLASH_SendByte(W25X_PowerDown); /* 停止信号 FLASH: CS 高 */ SPI_FLASH_CS_HIGH(); } //唤醒 void SPI_Flash_WAKEUP(void) { /*选择 FLASH: CS 低 */ SPI_FLASH_CS_LOW(); /* 发上 上电 命令 */ SPI_FLASH_SendByte(W25X_ReleasePowerDown); /* 停止信号 FLASH: CS 高 */ SPI_FLASH_CS_HIGH(); //等待TRES1 } /** * @brief 等待超时回调函数 * @param None. * @retval None. */ static uint16_t SPI_TIMEOUT_UserCallback(uint8_t errorCode) { /* 等待超时后的处理,输出错误信息 */ FLASH_ERROR("SPI 等待超时!errorCode = %d",errorCode); return 0; }
##