SPI在CubeMX中配置如下
按下以下步骤创建文件及文件夹放置在工程目录中
w25q256_spi.c中代码(此代码参考正点原子F429开发板)
#include "w25q256_spi.h"
//#include "stm32f4xx_hal_spi.h"
#define W25Q256_CS_HIGH() HAL_GPIO_WritePin(GPIOF,GPIO_PIN_6,GPIO_PIN_SET);
#define W25Q256_CS_LOW() HAL_GPIO_WritePin(GPIOF,GPIO_PIN_6,GPIO_PIN_RESET);
static void BSP_SPI5_GPIO_Init(void);
static void BSP_SPI5_Init(void);
static uint8_t W25Q256_ReadWriteByte(uint8_t TxData);
static uint8_t W25Q256_ReadSR(uint8_t regno);
static void W25Q256_Write_SR(uint8_t regno,uint8_t sr);
static void W25Q256_Write_Enable(void);
static void W25Q256_Write_Disable(void);
static void W25Q256_Wait_Busy(void);
SPI_HandleTypeDef SPI5_Handler;
static void BSP_SPI5_GPIO_Init(void)
{
GPIO_InitTypeDef GPIO_InitStruct = {0};
/* SPI5 clock enable */
__HAL_RCC_SPI5_CLK_ENABLE();
__HAL_RCC_GPIOF_CLK_ENABLE();
/**SPI5 GPIO Configuration
PF6 ------> SPI5_CS
PF7 ------> SPI5_SCK
PF8 ------> SPI5_MISO
PF9 ------> SPI5_MOSI
*/
GPIO_InitStruct.Pin=GPIO_PIN_6;
GPIO_InitStruct.Mode=GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Pull=GPIO_PULLUP;
GPIO_InitStruct.Speed=GPIO_SPEED_FAST;
HAL_GPIO_Init(GPIOF,&GPIO_InitStruct);
GPIO_InitStruct.Pin = GPIO_PIN_7|GPIO_PIN_8|GPIO_PIN_9;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
GPIO_InitStruct.Alternate = GPIO_AF5_SPI5;
HAL_GPIO_Init(GPIOF, &GPIO_InitStruct);
}
static void BSP_SPI5_Init(void)
{
BSP_SPI5_GPIO_Init();
SPI5_Handler.Instance = SPI5;
SPI5_Handler.Init.Mode = SPI_MODE_MASTER;
SPI5_Handler.Init.Direction = SPI_DIRECTION_2LINES;
SPI5_Handler.Init.DataSize = SPI_DATASIZE_8BIT;
SPI5_Handler.Init.CLKPolarity = SPI_POLARITY_HIGH;
SPI5_Handler.Init.CLKPhase = SPI_PHASE_2EDGE;
SPI5_Handler.Init.NSS = SPI_NSS_SOFT;
SPI5_Handler.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_2;
SPI5_Handler.Init.FirstBit = SPI_FIRSTBIT_MSB;
SPI5_Handler.Init.TIMode = SPI_TIMODE_DISABLE;
SPI5_Handler.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE;
SPI5_Handler.Init.CRCPolynomial = 10;
if (HAL_SPI_Init(&SPI5_Handler) != HAL_OK)
{
// Error_Handler();
}
}
//SPI5 读写一个字节
//TxData:要写入的字节
//返回值:读取到的字节
static uint8_t W25Q256_ReadWriteByte(uint8_t TxData)
{
uint8_t Rxdata;
HAL_SPI_TransmitReceive(&SPI5_Handler,&TxData,&Rxdata,1, 1000);
return Rxdata; //返回收到的数据
}
void W25Q256_Init(void)
{
BSP_SPI5_Init();
W25Q256_CS_LOW();
W25Q256_ReadWriteByte(W25X_Enable4ByteAddr);//发送进入4字节地址模式指令
W25Q256_CS_HIGH();
}
//读取芯片ID
//返回值如下:
//0XEF13,表示芯片型号为W25Q80
//0XEF14,表示芯片型号为W25Q16
//0XEF15,表示芯片型号为W25Q32
//0XEF16,表示芯片型号为W25Q64
//0XEF17,表示芯片型号为W25Q128
//0XEF18,表示芯片型号为W25Q256
uint16_t W25Q256_ReadID(void)
{
uint16_t Temp = 0;
W25Q256_CS_LOW();
W25Q256_ReadWriteByte(0x90);//发送读取ID命令
W25Q256_ReadWriteByte(0x00);
W25Q256_ReadWriteByte(0x00);
W25Q256_ReadWriteByte(0x00);
Temp|=W25Q256_ReadWriteByte(0xFF)<<8;
Temp|=W25Q256_ReadWriteByte(0xFF);
W25Q256_CS_HIGH();
return Temp;
}
//读取W25Q256的状态寄存器,W25Q256一共有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 ADP ADS
//regno:状态寄存器号,范:1~3
//返回值:状态寄存器值
static uint8_t W25Q256_ReadSR(uint8_t regno)
{
uint8_t byte=0,command=0;
switch(regno)
{
case 1:
command=W25X_ReadStatusReg1; //读状态寄存器1指令
break;
case 2:
command=W25X_ReadStatusReg2; //读状态寄存器2指令
break;
case 3:
command=W25X_ReadStatusReg3; //读状态寄存器3指令
break;
default:
command=W25X_ReadStatusReg1;
break;
}
W25Q256_CS_LOW(); //使能器件
W25Q256_ReadWriteByte(command); //发送读取状态寄存器命令
byte=W25Q256_ReadWriteByte(0Xff); //读取一个字节
W25Q256_CS_HIGH(); //取消片选
return byte;
}
//写W25Q256状态寄存器
static void W25Q256_Write_SR(uint8_t regno,uint8_t sr)
{
uint8_t command=0;
switch(regno)
{
case 1:
command=W25X_WriteStatusReg1; //写状态寄存器1指令
break;
case 2:
command=W25X_WriteStatusReg2; //写状态寄存器2指令
break;
case 3:
command=W25X_WriteStatusReg3; //写状态寄存器3指令
break;
default:
command=W25X_WriteStatusReg1;
break;
}
W25Q256_CS_LOW(); //使能器件
W25Q256_ReadWriteByte(command); //发送写取状态寄存器命令
W25Q256_ReadWriteByte(sr); //写入一个字节
W25Q256_CS_HIGH(); //取消片选
}
//W25Q256写使能
//将WEL置位
static void W25Q256_Write_Enable(void)
{
W25Q256_CS_LOW(); //使能器件
W25Q256_ReadWriteByte(W25X_WriteEnable); //发送写使能
W25Q256_CS_HIGH(); //取消片选
}
//W25Q256写禁止
//将WEL清零
static void W25Q256_Write_Disable(void)
{
W25Q256_CS_LOW(); //使能器件
W25Q256_ReadWriteByte(W25X_WriteDisable); //发送写禁止指令
W25Q256_CS_HIGH(); //取消片选
}
//等待空闲
static void W25Q256_Wait_Busy(void)
{
while((W25Q256_ReadSR(1)&0x01)==0x01); // 等待BUSY位清空
}
//擦除整个芯片
//等待时间超长...
void W25Q256_Erase_Chip(void)
{
W25Q256_Write_Enable(); //SET WEL
W25Q256_Wait_Busy();
W25Q256_CS_LOW(); //使能器件
W25Q256_ReadWriteByte(W25X_ChipErase); //发送片擦除命令
W25Q256_CS_HIGH(); //取消片选
W25Q256_Wait_Busy(); //等待芯片擦除结束
}
//擦除一个扇区
//Dst_Addr:扇区地址 根据实际容量设置
//擦除一个扇区的最少时间:150ms
void W25Q256_Erase_Sector(uint32_t Dst_Addr)
{
//监视falsh擦除情况,测试用
//printf("fe:%x\r\n",Dst_Addr);
Dst_Addr*=4096;
W25Q256_Write_Enable(); //SET WEL
W25Q256_Wait_Busy();
W25Q256_CS_LOW(); //使能器件
W25Q256_ReadWriteByte(W25X_SectorErase); //发送扇区擦除指令
W25Q256_ReadWriteByte((uint8_t)((Dst_Addr)>>24));
W25Q256_ReadWriteByte((uint8_t)((Dst_Addr)>>16)); //发送24bit地址
W25Q256_ReadWriteByte((uint8_t)((Dst_Addr)>>8));
W25Q256_ReadWriteByte((uint8_t)Dst_Addr);
W25Q256_CS_HIGH(); //取消片选
W25Q256_Wait_Busy(); //等待擦除完成
}
//读取SPI FLASH
//在指定地址开始读取指定长度的数据
//pBuffer:数据存储区
//ReadAddr:开始读取的地址(24bit)
//NumByteToRead:要读取的字节数(最大65535)
void W25Q256_Read(uint8_t* pBuffer,uint32_t ReadAddr,uint16_t NumByteToRead)
{
uint16_t i;
W25Q256_CS_LOW(); //使能器件
W25Q256_ReadWriteByte(W25X_ReadData); //发送读取命令
W25Q256_ReadWriteByte((uint8_t)((ReadAddr)>>24));
W25Q256_ReadWriteByte((uint8_t)((ReadAddr)>>16)); //发送24bit地址
W25Q256_ReadWriteByte((uint8_t)((ReadAddr)>>8));
W25Q256_ReadWriteByte((uint8_t)ReadAddr);
for(i=0;i<NumByteToRead;i++)
{
pBuffer[i]=W25Q256_ReadWriteByte(0XFF); //循环读数
}
W25Q256_CS_HIGH();
}
//SPI在一页(0~65535)内写入少于256个字节的数据
//在指定地址开始写入最大256字节的数据
//pBuffer:数据存储区
//WriteAddr:开始写入的地址(24bit)
//NumByteToWrite:要写入的字节数(最大256),该数不应该超过该页的剩余字节数!!!
void W25Q256_Write_Page(uint8_t* pBuffer,uint32_t WriteAddr,uint16_t NumByteToWrite)
{
uint16_t i;
W25Q256_Write_Enable(); //SET WEL
W25Q256_CS_LOW(); //使能器件
W25Q256_ReadWriteByte(W25X_PageProgram); //发送写页命令
W25Q256_ReadWriteByte((uint8_t)((WriteAddr)>>24));
W25Q256_ReadWriteByte((uint8_t)((WriteAddr)>>16)); //发送24bit地址
W25Q256_ReadWriteByte((uint8_t)((WriteAddr)>>8));
W25Q256_ReadWriteByte((uint8_t)WriteAddr);
for(i=0;i<NumByteToWrite;i++)W25Q256_ReadWriteByte(pBuffer[i]);//循环写数
W25Q256_CS_HIGH(); //取消片选
W25Q256_Wait_Busy(); //等待写入结束
}
//无检验写SPI FLASH
//必须确保所写的地址范围内的数据全部为0XFF,否则在非0XFF处写入的数据将失败!
//具有自动换页功能
//在指定地址开始写入指定长度的数据,但是要确保地址不越界!
//pBuffer:数据存储区
//WriteAddr:开始写入的地址(24bit)
//NumByteToWrite:要写入的字节数(最大65535)
//CHECK OK
void W25Q256_Write_NoCheck(uint8_t* pBuffer,uint32_t WriteAddr,uint16_t NumByteToWrite)
{
uint16_t pageremain;
pageremain=256-WriteAddr%256; //单页剩余的字节数
if(NumByteToWrite<=pageremain)pageremain=NumByteToWrite;//不大于256个字节
while(1)
{
W25Q256_Write_Page(pBuffer,WriteAddr,pageremain);
if(NumByteToWrite==pageremain)break;//写入结束了
else //NumByteToWrite>pageremain
{
pBuffer+=pageremain;
WriteAddr+=pageremain;
NumByteToWrite-=pageremain; //减去已经写入了的字节数
if(NumByteToWrite>256)pageremain=256; //一次可以写入256个字节
else pageremain=NumByteToWrite; //不够256个字节了
}
}
}
//写SPI FLASH
//在指定地址开始写入指定长度的数据
//该函数带擦除操作!
//pBuffer:数据存储区
//WriteAddr:开始写入的地址(24bit)
//NumByteToWrite:要写入的字节数(最大65535)
uint8_t W25Q256_BUFFER[4096];
void W25Q256_Write(uint8_t* pBuffer,uint32_t WriteAddr,uint16_t NumByteToWrite)
{
uint32_t secpos;
uint16_t secoff;
uint16_t secremain;
uint16_t i;
uint8_t * W25Q256_BUF;
W25Q256_BUF=W25Q256_BUFFER;
secpos=WriteAddr/4096;//扇区地址
secoff=WriteAddr%4096;//在扇区内的偏移
secremain=4096-secoff;//扇区剩余空间大小
//printf("ad:%X,nb:%X\r\n",WriteAddr,NumByteToWrite);//测试用
if(NumByteToWrite<=secremain)secremain=NumByteToWrite;//不大于4096个字节
while(1)
{
W25Q256_Read(W25Q256_BUF,secpos*4096,4096);//读出整个扇区的内容
for(i=0;i<secremain;i++)//校验数据
{
if(W25Q256_BUF[secoff+i]!=0XFF)break;//需要擦除
}
if(i<secremain)//需要擦除
{
W25Q256_Erase_Sector(secpos);//擦除这个扇区
for(i=0;i<secremain;i++) //复制
{
W25Q256_BUF[i+secoff]=pBuffer[i];
}
W25Q256_Write_NoCheck(W25Q256_BUF,secpos*4096,4096);//写入整个扇区
}
else W25Q256_Write_NoCheck(pBuffer,WriteAddr,secremain);//写已经擦除了的,直接写入扇区剩余区间.
if(NumByteToWrite==secremain)break;//写入结束了
else//写入未结束
{
secpos++;//扇区地址增1
secoff=0;//偏移位置为0
pBuffer+=secremain; //指针偏移
WriteAddr+=secremain;//写地址偏移
NumByteToWrite-=secremain; //字节数递减
if(NumByteToWrite>4096)secremain=4096; //下一个扇区还是写不完
else secremain=NumByteToWrite; //下一个扇区可以写完了
}
}
}
w25q256_spi.h中代码
#ifndef __W25Q256_SPI_H
#define __W25Q256_SPI_H
//#include "stm32f429xx.h"
//#include "stm32f429xx.h"
#include "stm32f4xx_hal.h"
//W25X系列/Q系列芯片列表
#define W25Q80 0XEF13
#define W25Q16 0XEF14
#define W25Q32 0XEF15
#define W25Q64 0XEF16
#define W25Q128 0XEF17
#define W25Q256 0XEF18
//指令表
#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
void W25Q256_Init(void);
uint16_t W25Q256_ReadID(void);
void W25Q256_Erase_Chip(void);
void W25Q256_Erase_Sector(uint32_t Dst_Addr);
void W25Q256_Read(uint8_t* pBuffer,uint32_t ReadAddr,uint16_t NumByteToRead);
void W25Q256_Write_Page(uint8_t* pBuffer,uint32_t WriteAddr,uint16_t NumByteToWrite);
void W25Q256_Write_NoCheck(uint8_t* pBuffer,uint32_t WriteAddr,uint16_t NumByteToWrite);
void W25Q256_Write(uint8_t* pBuffer,uint32_t WriteAddr,uint16_t NumByteToWrite);
#endif /*__ w25q256_spi_H */
以上完成外部Flash w25q256 spi的驱动
将D:\Keil_v5\Packs\ARM\CMSIS\5.7.0\Device 下 文件夹 _Template_Flash 拷贝至 工程目录
打开 E:\STM32F429_CubeMX6.0.1\Demo9_SPI-FLM_V1.25.0_MDK5_Template_Flash下MDK工程
添加分组及文件
选择芯片型号
配置工程
按照驱动代码配置此选项
以下参考安富莱教程完成
FlashDev.c
struct FlashDevice const FlashDevice = {
FLASH_DRV_VERS, /* 驱动版本,勿修改,这个是MDK定的 */
"STM32F429_SPI_W25Q256", /* 算法名,添加算法到MDK安装目录会显示此名字 */
EXTSPI, /* 设备类型 */
0xC0000000, /* Flash起始地址 */
32 * 1024 * 1024, /* Flash大小,32MB */ 1024, // Programming Page Size
4096, /* 编程页大小 */
0xFF, /* 擦除后的数值 */
6000, /* 页编程等待时间 */
6000, /* 扇区擦除等待时间 */
// Specify Size and Address of Sectors
4 * 1024, 0x000000, /* 扇区大小,扇区地址 */
SECTOR_END
};
FlashPrg.c
#include "FlashOS.h" // FlashOS Structures
#include "stm32f4xx_hal.h"
#include "w25q256_spi.h"
#define SPI_FLASH_MEM_ADDR 0xC0000000
int SystemClock_Config(void)
{
RCC_OscInitTypeDef RCC_OscInitStruct = {0};
RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
HAL_RCC_DeInit();
/** Configure the main internal regulator output voltage
*/
__HAL_RCC_PWR_CLK_ENABLE();
__HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE1);
/** Initializes the RCC Oscillators according to the specified parameters
* in the RCC_OscInitTypeDef structure.
*/
// RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSI;
// RCC_OscInitStruct.HSIState = RCC_HSI_ON;
// RCC_OscInitStruct.HSICalibrationValue = RCC_HSICALIBRATION_DEFAULT;
// RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
// RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSI;
// RCC_OscInitStruct.PLL.PLLM = 8;
// RCC_OscInitStruct.PLL.PLLN = 180;
// RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV2;
// RCC_OscInitStruct.PLL.PLLQ = 4;
//
RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
RCC_OscInitStruct.HSEState = RCC_HSE_ON;
RCC_OscInitStruct.HSICalibrationValue = RCC_HSICALIBRATION_DEFAULT;
RCC_OscInitStruct.HSIState = RCC_HSI_OFF;
RCC_OscInitStruct.LSEState = RCC_LSE_OFF;
RCC_OscInitStruct.LSIState = RCC_LSI_OFF;
RCC_OscInitStruct.PLL.PLLM = 25;
RCC_OscInitStruct.PLL.PLLN = 360;
RCC_OscInitStruct.PLL.PLLP = 2;
RCC_OscInitStruct.PLL.PLLQ = 8;
RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
{
return 1;
}
/** Activate the Over-Drive mode
*/
if (HAL_PWREx_EnableOverDrive() != HAL_OK)
{
return 1;
}
/** 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_DIV4;
RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV2;
if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_5) != HAL_OK)
{
return 1;
}
return 0;
}
//时钟设置函数
//Fvco=Fs*(plln/pllm);
//Fsys=Fvco/pllp=Fs*(plln/(pllm*pllp));
//Fusb=Fvco/pllq=Fs*(plln/(pllm*pllq));
//Fvco:VCO频率
//Fsys:系统时钟频率
//Fusb:USB,SDIO,RNG等的时钟频率
//Fs:PLL输入时钟频率,可以是HSI,HSE等.
//plln:主PLL倍频系数(PLL倍频),取值范围:64~432.
//pllm:主PLL和音频PLL分频系数(PLL之前的分频),取值范围:2~63.
//pllp:系统时钟的主PLL分频系数(PLL之后的分频),取值范围:2,4,6,8.(仅限这4个值!)
//pllq:USB/SDIO/随机数产生器等的主PLL分频系数(PLL之后的分频),取值范围:2~15.
//外部晶振为25M的时候,推荐值:plln=360,pllm=25,pllp=2,pllq=8.
//得到:Fvco=25*(360/25)=360Mhz
// Fsys=360/2=180Mhz
// Fusb=360/8=45Mhz(使用USB时,需设置plln=384,即可得到48Mhz频率)
//返回值:0,成功;1,失败。
int Sys_Clock_Set(uint32_t plln,uint32_t pllm,uint32_t pllp,uint32_t pllq)
{
uint16_t retry=0;
uint8_t status=0;
RCC->CR|=1<<16; //HSE 开启
while(((RCC->CR&(1<<17))==0)&&(retry<0X1FFF))retry++;//等待HSE RDY
if(retry==0X1FFF)status=1; //HSE无法就绪
else
{
RCC->APB1ENR|=1<<28; //电源接口时钟使能
PWR->CR|=3<<14; //高性能模式,时钟可到180Mhz
RCC->CFGR|=(0<<4)|(5<<10)|(4<<13);//HCLK 不分频;APB1 4分频;APB2 2分频.
RCC->CR&=~(1<<24); //关闭主PLL
RCC->PLLCFGR=pllm|(plln<<6)|(((pllp>>1)-1)<<16)|(pllq<<24)|(1<<22);//配置主PLL,PLL时钟源来自HSE
RCC->CR|=1<<24; //打开主PLL
while((RCC->CR&(1<<25))==0);//等待PLL准备好
FLASH->ACR|=1<<8; //指令预取使能.
FLASH->ACR|=1<<9; //指令cache使能.
FLASH->ACR|=1<<10; //数据cache使能.
FLASH->ACR|=5<<0; //5个CPU等待周期.
RCC->CFGR&=~(3<<0); //清零
RCC->CFGR|=2<<0; //选择主PLL作为系统时钟
while((RCC->CFGR&(3<<2))!=(2<<2));//等待主PLL作为系统时钟成功.
}
return status;
}
//系统时钟初始化函数
//plln:主PLL倍频系数(PLL倍频),取值范围:64~432.
//pllm:主PLL和音频PLL分频系数(PLL之前的分频),取值范围:2~63.
//pllp:系统时钟的主PLL分频系数(PLL之后的分频),取值范围:2,4,6,8.(仅限这4个值!)
//pllq:USB/SDIO/随机数产生器等的主PLL分频系数(PLL之后的分频),取值范围:2~15.
int Stm32_Clock_Init(uint32_t plln,uint32_t pllm,uint32_t pllp,uint32_t pllq)
{
RCC->CR|=0x00000001; //设置HISON,开启内部高速RC振荡
RCC->CFGR=0x00000000; //CFGR清零
RCC->CR&=0xFEF6FFFF; //HSEON,CSSON,PLLON清零
RCC->PLLCFGR=0x24003010; //PLLCFGR恢复复位值
RCC->CR&=~(1<<18); //HSEBYP清零,外部晶振不旁路
RCC->CIR=0x00000000; //禁止RCC时钟中断
return Sys_Clock_Set(plln,pllm,pllp,pllq);//设置时钟
}
/*
* Initialize Flash Programming Functions
* Parameter: adr: Device Base Address
* clk: Clock Frequency (Hz)
* fnc: Function Code (1 - Erase, 2 - Program, 3 - Verify)
* Return Value: 0 - OK, 1 - Failed
*/
int Init (unsigned long adr, unsigned long clk, unsigned long fnc) {
/* Add your Code */
int result = 0;
//
// /* 系统初始化 */
SystemInit();
/* 时钟初始化 */
result = SystemClock_Config();
// result = Stm32_Clock_Init(360,25,2,8);
if (result != 0)
{
return 1;
}
W25Q256_Init();
return 0;
}
/*
* De-Initialize Flash Programming Functions
* Parameter: fnc: Function Code (1 - Erase, 2 - Program, 3 - Verify)
* Return Value: 0 - OK, 1 - Failed
*/
int UnInit (unsigned long fnc) {
/* Add your Code */
return (0); // Finished without Errors
}
/*
* Erase complete Flash Memory
* Return Value: 0 - OK, 1 - Failed
*/
int EraseChip (void) {
/* Add your Code */
W25Q256_Erase_Chip();
return (0); // Finished without Errors
}
/*
* Erase Sector in Flash Memory
* Parameter: adr: Sector Address
* Return Value: 0 - OK, 1 - Failed
*/
int EraseSector (unsigned long adr) {
/* Add your Code */
adr -= SPI_FLASH_MEM_ADDR;
W25Q256_Erase_Sector(adr);
return (0); // Finished without Errors
}
/*
* Program Page in Flash Memory
* Parameter: adr: Page Start Address
* sz: Page Size
* buf: Page Data
* Return Value: 0 - OK, 1 - Failed
*/
int ProgramPage (unsigned long adr, unsigned long sz, unsigned char *buf) {
/* Add your Code */
adr -= SPI_FLASH_MEM_ADDR;
W25Q256_Write(buf, adr, sz);
// W25Q256_Write_NoCheck(buf, adr, sz);
return (0); // Finished without Errors
}
unsigned char aux_buf[4096];
unsigned long Verify (unsigned long adr, unsigned long sz, unsigned char *buf)
{
int i;
adr -= SPI_FLASH_MEM_ADDR;
W25Q256_Read(aux_buf, adr, sz);
for (i = 0; i< sz; i++)
{
if (aux_buf[i] != buf[i])
return (adr+i); /* 校验失败 */
}
adr += SPI_FLASH_MEM_ADDR;
return (adr+sz); /* 校验成功 */
}
int BlankCheck (unsigned long adr, unsigned long sz, unsigned char pat)
{
return 0;
}
一:时钟初始化问题
方案一:可以选择正点原子教程基于寄存器版本的时钟初始化
方案二:选择CubeMX生成的初始化方案但需注意以下问题
所有调用函数均屏蔽参数
uint32_t tickstart;
以及获取时间的部分例如
/* Check the HSE State */
if((RCC_OscInitStruct->HSEState) != RCC_HSE_OFF)
{
/* Get Start Tick */
// tickstart = HAL_GetTick();
/* Wait till HSE is ready */
while(__HAL_RCC_GET_FLAG(RCC_FLAG_HSERDY) == RESET)
{
// if((HAL_GetTick() - tickstart ) > HSE_TIMEOUT_VALUE)
// {
// return HAL_TIMEOUT;
// }
}
}
SPI调用库函数也需屏蔽超时部分
一、驱动下载地址:https://download.csdn.net/download/qq992035949/14045332.
二、FLM下载算法工程地址:https://download.csdn.net/download/qq992035949/14045346.