贵阳网站建设制作公司,佛山小网站建设,网站与平台的开发区别,热点事件营销案例一、基本概念
SPI 是英语 Serial Peripheral interface 的缩写#xff0c;顾名思义就是串行外围设备接口。是 Motorola 首先在其 MC68HCXX 系列处理器上定义的。 SPI 接口主要应用在 EEPROM#xff0c; FLASH#xff0c;实时时 钟#xff0c; AD 转换器#xff0c;还有数…一、基本概念
SPI 是英语 Serial Peripheral interface 的缩写顾名思义就是串行外围设备接口。是 Motorola 首先在其 MC68HCXX 系列处理器上定义的。 SPI 接口主要应用在 EEPROM FLASH实时时 钟 AD 转换器还有数字信号处理器和数字信号解码器之间。 SPI是一种高速的全双工 同步的通信总线并且在芯片的管脚上只占用四根线节约了芯片的管脚同时为 PCB 的布局 上节省空间提供方便正是出于这种简单易用的特性现在越来越多的芯片集成了这种通信 协议 STM32 也有 SPI 接口。 下面我们看看 SPI 的内部简明图图 28.1.1
SPI 接口一般使用 4 条线通信 MISO 主设备数据输入从设备数据输出。 MOSI 主设备数据输出从设备数据输入。 SCLK 时钟信号由主设备产生。 CS 从设备片选信号由主设备控制。 从图中可以看出 主机和从机都有一个串行移位寄存器主机通过向它的 SPI 串行寄存器 写入一个字节来发起一次传输。寄存器通过 MOSI 信号线将字节传送给从机从机也将自己的 移位寄存器中的内容通过 MISO 信号线返回给主机。这样两个移位寄存器中的内容就被交换。 外设的写操作和读操作是同步完成的。如果只进行写操作主机只需忽略接收到的字节反之 若主机要读取从机的一个字节就必须发送一个空字节来引发从机的传输。 SPI 主要特点有 可以同时发出和接收串行数据 可以当作主机或从机工作 提供频率可 编程时钟 发送结束中断标志 写冲突保护 总线竞争保护等。 SPI 总线四种工作方式 SPI 模块为了和外设进行数据交换根据外设工作要求其输出串 行同步时钟极性和相位可以进行配置时钟极性CPOL对传输协议没有重大的影响。如果 CPOL0串行同步时钟的空闲状态为低电平如果 CPOL1串行同步时钟的空闲状态为高电 平。时钟相位 CPHA能够配置用于选择两种不同的传输协议之一进行数据传输。如果 CPHA0在串行同步时钟的第一个跳变沿上升或下降数据被采样如果 CPHA1在串 行同步时钟的第二个跳变沿上升或下降数据被采样。 SPI 主模块和与之通信的外设备时钟 相位和极性应该一致。 二、代码实现 w25qxx.c
#include w25qxx.h
#include main.h
#include delay.h
#include stm32f1xx_hal.h
uint16_t W25QXX_TYPEW25Q128;//默认是W25Q128
//4Kbytes为一个Sector
//16个扇区为1个Block
//W25Q128
//容量为16M字节,共有128个Block,4096个Sector
//SPI2总线读写一个字节
//参数是写入的字节返回值是读出的字节
uint8_t SPI2_ReadWriteByte(uint8_t TxData)
{uint8_t Rxdata;//定义一个变量RxdataHAL_SPI_TransmitReceive(hspi2,TxData,Rxdata,1,1000);//调用固件库函数收发数据return Rxdata;//返回收到的数据
}
void W25QXX_CS(uint8_t a)//软件控制函数0为低电平其他值为高电平
{if(a0)HAL_GPIO_WritePin(GPIOB, GPIO_PIN_12, GPIO_PIN_RESET);else HAL_GPIO_WritePin(GPIOB, GPIO_PIN_12, GPIO_PIN_SET);
}
//初始化SPI FLASH的IO口
uint8_t W25QXX_Init(void)
{uint8_t temp;//定义一个变量tempW25QXX_CS(1);//0片选开启1片选关闭W25QXX_TYPE W25QXX_ReadID();//读取FLASH ID.if(W25QXX_TYPE W25Q256)//SPI FLASH为W25Q256时才用设置为4字节地址模式{temp W25QXX_ReadSR(3);//读取状态寄存器3判断地址模式if((temp0x01)0)//如果不是4字节地址模式,则进入4字节地址模式{W25QXX_CS(0);//0片选开启1片选关闭SPI2_ReadWriteByte(W25X_Enable4ByteAddr);//发送进入4字节地址模式指令W25QXX_CS(1);//0片选开启1片选关闭}}if(W25QXX_TYPEW25Q256||W25QXX_TYPEW25Q128||W25QXX_TYPEW25Q64||W25QXX_TYPEW25Q32||W25QXX_TYPEW25Q16||W25QXX_TYPEW25Q80)return 0; else return 1;//如果读出ID是现有型号列表中的一个则识别芯片成功
}
//读取W25QXX的状态寄存器W25QXX一共有3个状态寄存器
//状态寄存器1
//BIT7 6 5 4 3 2 1 0
//SPR RV TB BP2 BP1 BP0 WEL BUSY
//SPR:默认0,状态寄存器保护位,配合WP使用
//TB,BP2,BP1,BP0:FLASH区域写保护设置
//WEL:写使能锁定
//BUSY:忙标记位(1,忙;0,空闲)
//默认:0x00
//状态寄存器2
//BIT7 6 5 4 3 2 1 0
//SUS CMP LB3 LB2 LB1 (R) QE SRP1
//状态寄存器3
//BIT7 6 5 4 3 2 1 0
//HOLD/RST DRV1 DRV0 (R) (R) WPS (R) (R)
//regno:状态寄存器号范:1~3
//返回值:状态寄存器值
uint8_t W25QXX_ReadSR(uint8_t regno)
{uint8_t byte0,command0;switch(regno){case 1:commandW25X_ReadStatusReg1;//读状态寄存器1指令break;case 2:commandW25X_ReadStatusReg2;//读状态寄存器2指令break;case 3:commandW25X_ReadStatusReg3;//读状态寄存器3指令break;default:commandW25X_ReadStatusReg1;//读状态寄存器1指令break;}W25QXX_CS(0);//0片选开启1片选关闭SPI2_ReadWriteByte(command);//发送读取状态寄存器命令byteSPI2_ReadWriteByte(0Xff);//读取一个字节W25QXX_CS(1);//0片选开启1片选关闭return byte;//返回变量byte
}
//写W25QXX状态寄存器
void W25QXX_Write_SR(uint8_t regno,uint8_t sr)
{uint8_t command0;switch(regno){case 1:commandW25X_WriteStatusReg1;//写状态寄存器1指令break;case 2:commandW25X_WriteStatusReg2;//写状态寄存器2指令break;case 3:commandW25X_WriteStatusReg3;//写状态寄存器3指令break;default:commandW25X_WriteStatusReg1;break;}W25QXX_CS(0);//0片选开启1片选关闭SPI2_ReadWriteByte(command);//发送写取状态寄存器命令SPI2_ReadWriteByte(sr);//写入一个字节W25QXX_CS(1);//0片选开启1片选关闭
}
//W25QXX写使能
//将WEL置位
void W25QXX_Write_Enable(void)
{W25QXX_CS(0);//0片选开启1片选关闭SPI2_ReadWriteByte(W25X_WriteEnable);//发送写使能W25QXX_CS(1);//0片选开启1片选关闭
}
//W25QXX写禁止
//将WEL清零
void W25QXX_Write_Disable(void)
{W25QXX_CS(0);//0片选开启1片选关闭SPI2_ReadWriteByte(W25X_WriteDisable);//发送写禁止指令W25QXX_CS(1);//0片选开启1片选关闭
}
//读取芯片ID
//高8位是厂商代号本程序不判断厂商代号
//低8位是容量大小
//0XEF13型号为W25Q80
//0XEF14型号为W25Q16
//0XEF15型号为W25Q32
//0XEF16型号为W25Q64
//0XEF17型号为W25Q128目前洋桃2号开发板使用128容量芯片
//0XEF18型号为W25Q256
uint16_t W25QXX_ReadID(void)
{uint16_t Temp 0;W25QXX_CS(0);//0片选开启1片选关闭SPI2_ReadWriteByte(0x90);//发送读取ID命令SPI2_ReadWriteByte(0x00);SPI2_ReadWriteByte(0x00);SPI2_ReadWriteByte(0x00);Temp|SPI2_ReadWriteByte(0xFF)8;Temp|SPI2_ReadWriteByte(0xFF);W25QXX_CS(1);//0片选开启1片选关闭return Temp;
}
//读取SPI FLASH
//在指定地址开始读取指定长度的数据
//pBuffer:数据存储区
//ReadAddr:开始读取的地址(24bit)
//NumByteToRead:要读取的字节数(最大65535)
void W25QXX_Read(uint8_t* pBuffer,uint32_t ReadAddr,uint16_t NumByteToRead)
{uint16_t i;W25QXX_CS(0);//0片选开启1片选关闭SPI2_ReadWriteByte(W25X_ReadData);//发送读取命令if(W25QXX_TYPEW25Q256)//如果是W25Q256的话地址为4字节的要发送最高8位{SPI2_ReadWriteByte((uint8_t)((ReadAddr)24));}SPI2_ReadWriteByte((uint8_t)((ReadAddr)16));//发送24bit地址SPI2_ReadWriteByte((uint8_t)((ReadAddr)8));SPI2_ReadWriteByte((uint8_t)ReadAddr);for(i0;iNumByteToRead;i){pBuffer[i]SPI2_ReadWriteByte(0XFF);//循环读数}W25QXX_CS(1);//0片选开启1片选关闭
}
//SPI在一页(0~65535)内写入少于256个字节的数据
//在指定地址开始写入最大256字节的数据
//pBuffer:数据存储区
//WriteAddr:开始写入的地址(24bit)
//NumByteToWrite:要写入的字节数(最大256),该数不应该超过该页的剩余字节数!!!
void W25QXX_Write_Page(uint8_t* pBuffer,uint32_t WriteAddr,uint16_t NumByteToWrite)
{uint16_t i;W25QXX_Write_Enable();//SET WELW25QXX_CS(0);//0片选开启1片选关闭SPI2_ReadWriteByte(W25X_PageProgram);//发送写页命令if(W25QXX_TYPEW25Q256)//如果是W25Q256的话地址为4字节的要发送最高8位{SPI2_ReadWriteByte((uint8_t)((WriteAddr)24));}SPI2_ReadWriteByte((uint8_t)((WriteAddr)16));//发送24bit地址SPI2_ReadWriteByte((uint8_t)((WriteAddr)8));SPI2_ReadWriteByte((uint8_t)WriteAddr);for(i0;iNumByteToWrite;i)SPI2_ReadWriteByte(pBuffer[i]);//循环写数W25QXX_CS(1);//0片选开启1片选关闭W25QXX_Wait_Busy();//等待写入结束
}
//无检验写SPI FLASH
//必须确保所写的地址范围内的数据全部为0XFF,否则在非0XFF处写入的数据将失败!
//具有自动换页功能
//在指定地址开始写入指定长度的数据,但是要确保地址不越界!
//pBuffer:数据存储区
//WriteAddr:开始写入的地址(24bit)
//NumByteToWrite:要写入的字节数(最大65535)
//CHECK OK
void W25QXX_Write_NoCheck(uint8_t* pBuffer,uint32_t WriteAddr,uint16_t NumByteToWrite)
{uint16_t pageremain;pageremain256-WriteAddr%256; //单页剩余的字节数if(NumByteToWritepageremain)pageremainNumByteToWrite;//不大于256个字节while(1){W25QXX_Write_Page(pBuffer,WriteAddr,pageremain);if(NumByteToWritepageremain)break;//写入结束了else //NumByteToWritepageremain{pBufferpageremain;WriteAddrpageremain;NumByteToWrite-pageremain; //减去已经写入了的字节数if(NumByteToWrite256)pageremain256; //一次可以写入256个字节else pageremainNumByteToWrite; //不够256个字节了}};
}
//写SPI FLASH
//在指定地址开始写入指定长度的数据
//该函数带擦除操作!
//pBuffer:数据存储区
//WriteAddr:开始写入的地址(24bit)
//NumByteToWrite:要写入的字节数(最大65535)
uint8_t W25QXX_BUFFER[4096];
void W25QXX_Write(uint8_t* pBuffer,uint32_t WriteAddr,uint16_t NumByteToWrite)
{uint32_t secpos;uint16_t secoff;uint16_t secremain;uint16_t i;uint8_t* W25QXX_BUF;W25QXX_BUFW25QXX_BUFFER;secposWriteAddr/4096;//扇区地址secoffWriteAddr%4096;//在扇区内的偏移secremain4096-secoff;//扇区剩余空间大小//printf(ad:%X,nb:%X\r\n,WriteAddr,NumByteToWrite);//测试用if(NumByteToWritesecremain)secremainNumByteToWrite;//不大于4096个字节while(1){W25QXX_Read(W25QXX_BUF,secpos*4096,4096);//读出整个扇区的内容for(i0;isecremain;i)//校验数据{if(W25QXX_BUF[secoffi]!0XFF)break;//需要擦除}if(isecremain)//需要擦除{W25QXX_Erase_Sector(secpos);//擦除这个扇区for(i0;isecremain;i)//复制{W25QXX_BUF[isecoff]pBuffer[i];}W25QXX_Write_NoCheck(W25QXX_BUF,secpos*4096,4096);//写入整个扇区}else W25QXX_Write_NoCheck(pBuffer,WriteAddr,secremain);//写已经擦除了的,直接写入扇区剩余区间.if(NumByteToWritesecremain)break;//写入结束了else//写入未结束{secpos;//扇区地址增1secoff0;//偏移位置为0pBuffersecremain; //指针偏移WriteAddrsecremain;//写地址偏移NumByteToWrite-secremain;//字节数递减if(NumByteToWrite4096)secremain4096;//下一个扇区还是写不完else secremainNumByteToWrite;//下一个扇区可以写完了}};
}
//擦除整个芯片
//等待时间超长...
void W25QXX_Erase_Chip(void)
{W25QXX_Write_Enable();//SET WELW25QXX_Wait_Busy();//等待忙状态W25QXX_CS(0);//0片选开启1片选关闭SPI2_ReadWriteByte(W25X_ChipErase);//发送片擦除命令W25QXX_CS(1);//0片选开启1片选关闭W25QXX_Wait_Busy();//等待芯片擦除结束
}
//擦除一个扇区
//Dst_Addr:扇区地址 根据实际容量设置
//擦除一个扇区的最少时间:150ms
void W25QXX_Erase_Sector(uint32_t Dst_Addr)
{Dst_Addr*4096;W25QXX_Write_Enable();//SET WELW25QXX_Wait_Busy();W25QXX_CS(0);//0片选开启1片选关闭SPI2_ReadWriteByte(W25X_SectorErase);//发送扇区擦除指令if(W25QXX_TYPEW25Q256)//如果是W25Q256的话地址为4字节的要发送最高8位{SPI2_ReadWriteByte((uint8_t)((Dst_Addr)24));}SPI2_ReadWriteByte((uint8_t)((Dst_Addr)16));//发送24bit地址SPI2_ReadWriteByte((uint8_t)((Dst_Addr)8));SPI2_ReadWriteByte((uint8_t)Dst_Addr);W25QXX_CS(1);//0片选开启1片选关闭W25QXX_Wait_Busy();//等待擦除完成
}
//等待空闲
void W25QXX_Wait_Busy(void)
{while((W25QXX_ReadSR(1)0x01)0x01);//等待BUSY位清空
}
//进入掉电模式
void W25QXX_PowerDown(void)
{W25QXX_CS(0);//0片选开启1片选关闭SPI2_ReadWriteByte(W25X_PowerDown);//发送掉电命令 0xB9W25QXX_CS(1);//0片选开启1片选关闭delay_us(3);//等待TPD
}
//唤醒
void W25QXX_WAKEUP(void)
{W25QXX_CS(0);//0片选开启1片选关闭SPI2_ReadWriteByte(W25X_ReleasePowerDown);//发送电源唤醒指令 0xABW25QXX_CS(1);//0片选开启1片选关闭delay_us(3);//等待TRES1
}//void delay_us(uint32_t us) //利用CPU循环实现的非精准应用的微秒延时函数
//{
// uint32_t delay (HAL_RCC_GetHCLKFreq() / 8000000 * us); //使用HAL_RCC_GetHCLKFreq()函数获取主频值经算法得到1微秒的循环次数
// while (delay--); //循环delay次达到1微秒延时
//}
w25qxx.h
#ifndef W25Q128_W25QXX_H_
#define W25Q128_W25QXX_H_#include stm32f1xx_hal.h //HAL库文件声明//25系列FLASH芯片厂商与容量代号厂商代号EF
#define W25Q80 0XEF13
#define W25Q16 0XEF14
#define W25Q32 0XEF15
#define W25Q64 0XEF16
#define W25Q128 0XEF17
#define W25Q256 0XEF18
#define EX_FLASH_ADD 0x000000 //W25Q128的地址是24位宽
extern uint16_t W25QXX_TYPE;//定义W25QXX芯片型号
extern SPI_HandleTypeDef hspi2;
//
//指令表
#define W25X_WriteEnable 0x06
#define W25X_WriteDisable 0x04
#define W25X_ReadStatusReg1 0x05
#define W25X_ReadStatusReg2 0x35
#define W25X_ReadStatusReg3 0x15
#define W25X_WriteStatusReg1 0x01
#define W25X_WriteStatusReg2 0x31
#define W25X_WriteStatusReg3 0x11
#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 W25X_Enable4ByteAddr 0xB7
#define W25X_Exit4ByteAddr 0xE9
uint8_t SPI2_ReadWriteByte(uint8_t TxData);//SPI2总线底层读写
void W25QXX_CS(uint8_t a);//W25QXX片选引脚控制
uint8_t W25QXX_Init(void);//初始化W25QXX函数
uint16_t W25QXX_ReadID(void);//读取FLASH ID
uint8_t W25QXX_ReadSR(uint8_t regno);//读取状态寄存器
void W25QXX_4ByteAddr_Enable(void);//使能4字节地址模式
void W25QXX_Write_SR(uint8_t regno,uint8_t sr);//写状态寄存器
void W25QXX_Write_Enable(void);//写使能
void W25QXX_Write_Disable(void);//写保护
void W25QXX_Write_NoCheck(uint8_t* pBuffer,uint32_t WriteAddr,uint16_t NumByteToWrite);//无检验写SPI FLASH
void W25QXX_Read(uint8_t* pBuffer,uint32_t ReadAddr,uint16_t NumByteToRead);//读取flash
void W25QXX_Write(uint8_t* pBuffer,uint32_t WriteAddr,uint16_t NumByteToWrite);//写入flash
void W25QXX_Erase_Chip(void);//整片擦除
void W25QXX_Erase_Sector(uint32_t Dst_Addr);//扇区擦除
void W25QXX_Wait_Busy(void);//等待空闲
void W25QXX_PowerDown(void);//进入掉电模式
void W25QXX_WAKEUP(void);//唤醒
//void delay_us(uint32_t us); //C文件中的函数声明
#endif /* W25Q128_W25QXX_H_ */
main.c
/* USER CODE BEGIN Header */
/********************************************************************************* file : main.c* brief : Main program body******************************************************************************* attention** Copyright (c) 2024 STMicroelectronics.* All rights reserved.** This software is licensed under terms that can be found in the LICENSE file* in the root directory of this software component.* If no LICENSE file comes with this software, it is provided AS-IS.********************************************************************************/
/* USER CODE END Header */
/* Includes ------------------------------------------------------------------*/
#include main.h
#include spi.h
#include usart.h
#include gpio.h
#include w25qxx.h
#include stdio.h
#include delay.huint8_t EX_FLASH_BUF[1];/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes *//* USER CODE END Includes */
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{switch(GPIO_Pin) {case GPIO_PIN_3:EX_FLASH_BUF[0]W25QXX_ReadID();printf (芯片ID %x \r\n, EX_FLASH_BUF[0]);printf(3 pressed! \r\n);break;case GPIO_PIN_4:W25QXX_Read(EX_FLASH_BUF,EX_FLASH_ADD,1);EX_FLASH_BUF[0];if(EX_FLASH_BUF[0]200)EX_FLASH_BUF[0]0;W25QXX_Write(EX_FLASH_BUF,EX_FLASH_ADD,1);printf(读出0x00地址数据%d \n\r,EX_FLASH_BUF[0]);printf(4 pressed! \r\n);break;}
}
/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD *//* USER CODE END PTD *//* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD *//* USER CODE END PD *//* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM *//* USER CODE END PM *//* Private variables ---------------------------------------------------------*//* USER CODE BEGIN PV *//* USER CODE END PV *//* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
/* USER CODE BEGIN PFP *//* USER CODE END PFP *//* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 *//* USER CODE END 0 *//*** brief The application entry point.* retval int*/
int main(void)
{/* USER CODE BEGIN 1 *//* USER CODE END 1 *//* MCU Configuration--------------------------------------------------------*//* Reset of all peripherals, Initializes the Flash interface and the Systick. */HAL_Init();/* USER CODE BEGIN Init *//* USER CODE END Init *//* Configure the system clock */SystemClock_Config();/* USER CODE BEGIN SysInit *//* USER CODE END SysInit *//* Initialize all configured peripherals */MX_GPIO_Init();MX_SPI2_Init();MX_USART1_UART_Init();/* USER CODE BEGIN 2 */W25QXX_Init();printf( \r\n);/* USER CODE END 2 *//* Infinite loop *//* USER CODE BEGIN WHILE */while (1){/* USER CODE END WHILE *//* USER CODE BEGIN 3 */}/* USER CODE END 3 */
}/*** brief System Clock Configuration* retval None*/
void SystemClock_Config(void)
{RCC_OscInitTypeDef RCC_OscInitStruct {0};RCC_ClkInitTypeDef RCC_ClkInitStruct {0};/** Initializes the RCC Oscillators according to the specified parameters* in the RCC_OscInitTypeDef structure.*/RCC_OscInitStruct.OscillatorType RCC_OSCILLATORTYPE_HSE;RCC_OscInitStruct.HSEState RCC_HSE_ON;RCC_OscInitStruct.HSEPredivValue RCC_HSE_PREDIV_DIV1;RCC_OscInitStruct.HSIState RCC_HSI_ON;RCC_OscInitStruct.PLL.PLLState RCC_PLL_ON;RCC_OscInitStruct.PLL.PLLSource RCC_PLLSOURCE_HSE;RCC_OscInitStruct.PLL.PLLMUL RCC_PLL_MUL9;if (HAL_RCC_OscConfig(RCC_OscInitStruct) ! HAL_OK){Error_Handler();}/** Initializes the CPU, AHB and APB buses clocks*/RCC_ClkInitStruct.ClockType RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK|RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;RCC_ClkInitStruct.SYSCLKSource RCC_SYSCLKSOURCE_PLLCLK;RCC_ClkInitStruct.AHBCLKDivider RCC_SYSCLK_DIV1;RCC_ClkInitStruct.APB1CLKDivider RCC_HCLK_DIV2;RCC_ClkInitStruct.APB2CLKDivider RCC_HCLK_DIV1;if (HAL_RCC_ClockConfig(RCC_ClkInitStruct, FLASH_LATENCY_2) ! HAL_OK){Error_Handler();}
}/* USER CODE BEGIN 4 *//* USER CODE END 4 *//*** brief This function is executed in case of error occurrence.* retval None*/
void Error_Handler(void)
{/* USER CODE BEGIN Error_Handler_Debug *//* User can add his own implementation to report the HAL error return state */__disable_irq();while (1){}/* USER CODE END Error_Handler_Debug */
}#ifdef USE_FULL_ASSERT
/*** brief Reports the name of the source file and the source line number* where the assert_param error has occurred.* param file: pointer to the source file name* param line: assert_param error line source number* retval None*/
void assert_failed(uint8_t *file, uint32_t line)
{/* USER CODE BEGIN 6 *//* User can add his own implementation to report the file name and line number,ex: printf(Wrong parameters value: file %s on line %d\r\n, file, line) *//* USER CODE END 6 */
}
#endif /* USE_FULL_ASSERT */