LevelX在STM32+NorFlash的移植
开发平台:
IDE:Atollic TrueSTUDIO for STM32 + STM32Cubemx
MCU:STM32F767IGT6;
NorFlash:W25Q256;
SPI:四线。
本教程是基于STM32 + SPI NorFlash 完成的,实际使用过程中与芯片没什么紧密关系,均可使用,也不局限与SPI NorFlash 。实际上无论什么接口、什么类型的FLASH,除移植过程中对接口操作进行修改其余均可参照此教程进行移植。这里仅仅使用 SPI NorFlash进行举例,其余均可举一反三进行对照移植。
STM32CubeMX中NorFlash SPI的配置参数:
移植操作
LevelX将一个NorFlash的物理Block(块)主动分成由N个每128字的大小的扇区组成,所用的NorFlash实际扇区一般都不等于128字即512字节,在移植过程中,只需要将操作的地址进行转换一下,至于绝对操作地址相关的接口即可。
移植接口
只要移植的接口有7个函数接口:
UINT nor_driver_initialize(LX_NOR_FLASH *nor_flash);
UINT nor_driver_read_sector(ULONG *flash_address, ULONG *destination, ULONG words);
UINT nor_driver_write_sector(ULONG *flash_address, ULONG *source, ULONG words);
UINT nor_driver_block_erase(ULONG block, ULONG erase_count);
UINT nor_driver_block_erased_verify(ULONG block);
UINT nor_flash_system_error(UINT error_code, ULONG block, ULONG sector);
UINT nor_driver_initialize(LX_NOR_FLASH *nor_flash)
驱动程序初始化:
这些服务是通过在驱动的初始化函数中设置LX_NOR_FLASH实例的函数指针来设置的。驱动程序的初始化函数。
- 指定闪存的基本地址。
- 指定闪存的总块数和每个块的字数。
- 一个RAM缓冲区(nor_sector_memory),用于读取flash的一个扇区(512字节),并为ULONG访问而对齐。
在返回LX_SUCCESS之前,驱动初始化函数可能还会执行额外的设备和/或特定实现的初始化任务。
UINT nor_driver_initialize(LX_NOR_FLASH *nor_flash)
{
//dev_dbg(dev, "%s\n", __func__);
/* Setup the base address of the flash memory. */
nor_flash -> lx_nor_flash_base_address = (ULONG *) 0;
/* Setup geometry of the flash. */
nor_flash -> lx_nor_flash_total_blocks = TOTAL_BLOCKS;
nor_flash -> lx_nor_flash_words_per_block = W25Q256FV_BLOCK_SIZE / sizeof(ULONG);
/* Setup function pointers for the NOR flash services. */
nor_flash -> lx_nor_flash_driver_read = nor_driver_read_sector;
nor_flash -> lx_nor_flash_driver_write = nor_driver_write_sector;
nor_flash -> lx_nor_flash_driver_block_erase = nor_driver_block_erase;
nor_flash -> lx_nor_flash_driver_block_erased_verify = nor_driver_block_erased_verify;
/* Setup local buffer for NOR flash operation. This buffer must be the sector size of the NOR flash memory. */
nor_flash -> lx_nor_flash_sector_buffer = &nor_sector_memory[0];
/* Return success. */
return(LX_SUCCESS);
}
UINT nor_driver_read_sector(ULONG *flash_address, ULONG *destination, ULONG words)
驱动程序读取扇区:
LevelX NOR 驱动程序 "读取扇区 "服务负责读取一个 在NOR闪存的特定块中的特定扇区。所有的错误检查和 纠正逻辑是驱动服务完成的。
如果成功,则 LevelX NOR 驱动程序返回 LX_SUCCESS。
如果不成功,LevelX NOR 驱动程序返回 LX_ERROR。
其中 "flash_address "指定了一个逻辑扇区的地址。NOR闪存块,"目标 "和 "字 "指定在哪里。放置扇区的内容和读取多少个32位字。
UINT nor_driver_read_sector(ULONG *flash_address, ULONG *destination, ULONG words)
{
uint8_t Nor_Status = 0;
Nor_Status = BSP_W25Q256_Read((uint8_t*) destination, (uint32_t)flash_address, words * 4);
if(W25Q256_OK == Nor_Status)
{
return (LX_SUCCESS);
}
else
{
dev_err(dev, " Read Sector Error!!!");
return (LX_ERROR);
}
}
UINT nor_driver_write_sector(ULONG *flash_address, ULONG *source, ULONG words)
驱动器写扇区:
LevelX NOR驱动 "写扇区 "服务负责写一个 块的NOR闪存中。所有的错误检查都是 驱动服务的责任。
如果成功,LevelX NOR驱动程序 返回 LX_SUCCESS。
如果不成功,LevelX NOR驱动程序将返回 LX_ERROR。
其中,“ flash_address”指定NOR闪存块中逻辑扇区的地址,而“ source”和“ words”指定写入的源以及要写入多少个32位字。
注意:LevelX依赖驱动程序来验证写入扇区是否成功。 通常通过读回编程值以确保其与要写入的请求值相匹配来完成此操作。
UINT nor_driver_write_sector(ULONG *flash_address, ULONG *source, ULONG words)
{
uint8_t Nor_Status = 0;
/* Loop to write flash. */
Nor_Status = BSP_W25Q256_Write((uint8_t*) source, (uint32_t) flash_address, words * 4);
if(W25Q256_OK == Nor_Status)
{
return (LX_SUCCESS);
}
else
{
dev_err(dev, " Write Sector Error!!!");
return (LX_ERROR);
}
}
UINT nor_driver_block_erase(ULONG block, ULONG erase_count)
驱动器块擦除
LevelX NOR驱动的 "块擦除 "服务负责擦除 “块”。NOR闪存的指定块。
如果成功,LevelX NOR驱动程序 返回 LX_SUCCESS。
如果不成功,LevelX NOR驱动程序将返回 LX_ERROR.LevelX NOR驱动的原型是 “块擦除”。
其中 "block "表示要擦除的NOR块。参数 "erase_count "是为了诊断目的而提供的。例如 驱动程序可能希望在以下情况下提醒应用软件的另一部分。擦除次数超过特定的阈值。
注意:LevelX依赖驱动程序检查块的所有字节,以确保将其擦除(包含所有字节)。
UINT nor_driver_block_erase(ULONG block, ULONG erase_count)
{
uint8_t Nor_Status = 0;
Nor_Status = BSP_W25Q256_Erase_Sector(block * W25Q256FV_BLOCK_SIZE);
if(W25Q256_OK == Nor_Status)
{
return (LX_SUCCESS);
}
else
{
dev_err(dev, " Erase Sector Error!!!");
return (LX_ERROR);
}
}
UINT nor_driver_block_erased_verify(ULONG block)
驱动程序块被擦除 验证
LevelX NOR驱动的 "块擦除验证 "服务主要负责 验证NOR闪存的指定块是否已被擦除。
如果已被擦除,则: LevelX NOR驱动程序返回LX_SUCCESS。
如果块没有被清除。的LevelX NOR驱动程序返回LX_ERROR。
其中 "block "指定要验证哪个block是否被擦除。
注意:LevelX依赖驱动程序检查指定字节的所有字节,以确保将其擦除(包含所有字节)。
UINT nor_driver_block_erased_verify(ULONG block)
{
ULONG *word_ptr;
ULONG words;
static uint8_t erase_verify_buf[W25Q256FV_SECTOR_SIZE] = {0};
u32 i = 0, Sectors_per_block = W25Q256FV_SECTORS_PER_BLOCK;
uint8_t Nor_Status = 0;
for(i = 0; i< Sectors_per_block; i++)
{
/*每次只读取其中一个扇区*/
Nor_Status = BSP_W25Q256_Read(erase_verify_buf, block * W25Q256FV_BLOCK_SIZE + i * W25Q256FV_SECTOR_SIZE, W25Q256FV_SECTOR_SIZE);
if(W25Q256_OK != Nor_Status)
{
return (LX_ERROR);
}
words = W25Q256FV_SECTOR_SIZE / sizeof(ULONG);
word_ptr = (ULONG *)&erase_verify_buf[0];
while (words--)
{
/* Is this word erased? */
if (*word_ptr++ != 0xFFFFFFFF)
{
return(LX_ERROR);
}
}
}
/* Return success. */
return(LX_SUCCESS);
}
UINT nor_driver_system_error(UINT error_code, ULONG block, ULONG sector)
驱动程序系统错误处理
LevelX NOR驱动程序 "系统错误处理 "服务负责设置处理LevelX检测到的系统错误。该例程中的处理是依赖于应用程序的。
如果成功,LevelX NOR驱动程序返回LX_SUCCESS。
如果不成功,LevelX NOR驱动程序返回LX_ERROR。
其中 "error_code "代表发生的错误。
UINT nor_driver_system_error(UINT error_code, ULONG block, ULONG sector)
{
LX_PARAMETER_NOT_USED(error_code);
LX_PARAMETER_NOT_USED(block);
LX_PARAMETER_NOT_USED(sector);
/* Custom processing goes here... all errors are fatal. */
return(LX_ERROR);
}
LevelX的使用
正如均衡磨损的那篇文章中介绍的那样,LevelX并不依赖与ThreadX 或者是FileX,仅仅依赖与部分ThreadX的数据类型,所以添加需要的数据类型后,我们就可以直接使用了。
LevelX最终目的就是实现均衡磨损,用户只需要知道当前LevelX提供的虚拟操作扇区,无需知道实际操作的物理扇区。
下面是一个使用LeveX的一个小demo:
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_USART1_UART_Init();
MX_SPI5_Init();
MX_FMC_Init();
/* USER CODE BEGIN 2 */
BSP_W25Q256_Reset();
BSP_W25Q256_Init();
_lx_nor_flash_initialize();
_lx_nor_flash_open(&nor_flash, "sim nor flash", nor_driver_initialize);
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
HAL_Delay(1000);
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
if(WD_TEST)
{
/*更改/初始化要写入的数据*/
for(i = 0; i< LX_NOR_SECTOR_SIZE/4; i++)
{
WrBuf[i] = i+1;
}
/*将要写入的内容写入虚拟扇区“1”, 大小128字*/
_lx_nor_flash_sector_write(&nor_flash, 1, WrBuf);
/*从虚拟扇区“1”中读取大小128字数据*/
_lx_nor_flash_sector_read(&nor_flash, 1, RdBuf);
/*打印出读取的数据*/
for(i = 0; i< LX_NOR_SECTOR_SIZE/4; i++)
{
dev_dbg(dev, "%ld ", RdBuf[i]);
}
dev_dbg(dev, "\n ");
}
}
/* USER CODE END 3 */
}
如果每次写入同一个“扇区”于不同的数据,但读取数据与写入数据相同,根据FLASH的写入特性,则表明移植成功。也可以查看底层操作扇区接口,打印出实际使用物理扇区或地址,确保移植成功。
本次工程文件:LevelX移植工程