LevelX
Azure RTOS也就是ThreadX。
Azure RTOS LevelX 向嵌入式应用程序提供 NAND 和 NOR 闪存提供实现磨损均衡的手段。由于NAND和NOR闪存都只能进行有限次数的擦除,均衡分配闪存的使用至关重要,这通常被称为磨损均衡,通过LevelX实现。
这段话来自 Azure RTOS LevelX 用户手册。可以知道 Azure RTOS通过LevelX组件实现对NAND
和NOR
两种flash实现磨损平衡,LevelX组件只是一个组件,与任何硬件、文件系统以及操作系统都没有关系,那便可以移植出来使用。
它的github地址:https://github.com/azure-rtos/levelx
LevelX源码目录结构
源码都在common目录下
- inc,头文件目录
- src,源文件目录
LevelX 使用ANSI C编写,其中每个函数包含在其自己单独的 C 文件中。
没看手册前第一次看到这个源码真的有点蒙,咋这么多C文件,很独特,每个函数单独一个文件实现。。。
从文件名前缀可以看出文件对应的是nand还是nor flash。
inc目录下有一个头文件lx_user_sample.h,这个是用户配置头文件,里面已经定义了很多宏,通过这些就可以配置LevelX。
LevelX NAND 和 NOR 实例的模拟器和 FileX 驱动程序示例:
LevelX移植
移植主要是就是对接自己的flash的硬件驱动程序。
LevelX divides each NOR flash block into 512-byte logical sectors.
LevelX 将nor flash物理块划分为512字节大小的逻辑扇区。
0、驱动程序移植方法
通过调用_lx_nor_flash_open
,给函数指针nor_driver_initialize
指定函数,通过nor_driver_initialize
调用将驱动程序注册进LevelX。
UINT _lx_nor_flash_open(LX_NOR_FLASH *nor_flash, CHAR *name, UINT (*nor_driver_initialize)(LX_NOR_FLASH *))
怎么在指定驱动程序呢?可以nor_driver_initialize
看到有一个参数类型为LX_NOR_FLASH
typedef struct LX_NOR_FLASH_STRUCT
{
ULONG lx_nor_flash_state;
ULONG lx_nor_flash_total_blocks; /* 总块数 */
ULONG lx_nor_flash_words_per_block; /* 每块有多少字,也就是多少4字节 */
ULONG lx_nor_flash_total_physical_sectors; /* 总的物理扇区数 */
ULONG lx_nor_flash_physical_sectors_per_block; /* 每个块有多少个物理扇区 */
ULONG *lx_nor_flash_base_address; /* flash基地址 */
ULONG lx_nor_flash_block_free_bit_map_offset;
ULONG lx_nor_flash_block_bit_map_words;
ULONG lx_nor_flash_block_bit_map_mask;
ULONG lx_nor_flash_block_physical_sector_mapping_offset;
ULONG lx_nor_flash_block_physical_sector_offset;
ULONG lx_nor_flash_free_physical_sectors;
ULONG lx_nor_flash_mapped_physical_sectors;
ULONG lx_nor_flash_obsolete_physical_sectors;
ULONG lx_nor_flash_minimum_erase_count;
ULONG lx_nor_flash_maximum_erase_count;
ULONG lx_nor_flash_free_block_search;
ULONG lx_nor_flash_found_block_search;
ULONG lx_nor_flash_found_sector_search;
ULONG lx_nor_flash_write_requests;
ULONG lx_nor_flash_read_requests;
ULONG lx_nor_flash_sector_mapping_cache_hits;
ULONG lx_nor_flash_sector_mapping_cache_misses;
ULONG lx_nor_flash_physical_block_allocates;
ULONG lx_nor_flash_physical_block_allocate_errors;
ULONG lx_nor_flash_diagnostic_system_errors;
ULONG lx_nor_flash_diagnostic_system_error;
ULONG lx_nor_flash_diagnostic_initial_format;
ULONG lx_nor_flash_diagnostic_erased_block;
ULONG lx_nor_flash_diagnostic_re_erase_block;
ULONG lx_nor_flash_diagnostic_sector_being_obsoleted;
ULONG lx_nor_flash_diagnostic_sector_obsoleted;
ULONG lx_nor_flash_diagnostic_mapping_invalidated;
ULONG lx_nor_flash_diagnostic_mapping_write_interrupted;
ULONG lx_nor_flash_diagnostic_sector_not_free;
ULONG lx_nor_flash_diagnostic_sector_data_not_free;
UINT (*lx_nor_flash_driver_read)(ULONG *flash_address, ULONG *destination, ULONG words);
UINT (*lx_nor_flash_driver_write)(ULONG *flash_address, ULONG *source, ULONG words);
UINT (*lx_nor_flash_driver_block_erase)(ULONG block, ULONG erase_count);
UINT (*lx_nor_flash_driver_block_erased_verify)(ULONG block);
UINT (*lx_nor_flash_driver_system_error)(UINT error_code);
ULONG *lx_nor_flash_sector_buffer;
UINT lx_nor_flash_sector_mapping_cache_enabled;
LX_NOR_SECTOR_MAPPING_CACHE_ENTRY
lx_nor_flash_sector_mapping_cache[LX_NOR_SECTOR_MAPPING_CACHE_SIZE];
#ifndef LX_NOR_DISABLE_EXTENDED_CACHE
UINT lx_nor_flash_extended_cache_entries;
LX_NOR_FLASH_EXTENDED_CACHE_ENTRY
lx_nor_flash_extended_cache[LX_NOR_EXTENDED_CACHE_SIZE];
ULONG lx_nor_flash_extended_cache_hits;
ULONG lx_nor_flash_extended_cache_misses;
#endif
#ifdef LX_THREAD_SAFE_ENABLE
/* When this conditional is used, the LevelX code utilizes a ThreadX mutex for thread
safe operation. Generally, this is not required since FileX ensures thread safe operation at
a higher layer. */
TX_MUTEX lx_nor_flash_mutex;
#endif
/* Define the NOR flash control block open next/previous pointers. */
struct LX_NOR_FLASH_STRUCT *lx_nor_flash_open_next,
*lx_nor_flash_open_previous;
} LX_NOR_FLASH;
- 跟驱动程序相关的几个成员函数:
UINT (*lx_nor_flash_driver_read)(ULONG *flash_address, ULONG *destination, ULONG words);
UINT (*lx_nor_flash_driver_write)(ULONG *flash_address, ULONG *source, ULONG words);
UINT (*lx_nor_flash_driver_block_erase)(ULONG block, ULONG erase_count);
UINT (*lx_nor_flash_driver_block_erased_verify)(ULONG block);
UINT (*lx_nor_flash_driver_system_error)(UINT error_code);
1、添加源码
由于flash是nor flash,所以只添加nor flash相关源码。nor 和 nand的实现是分开的,如果使用的nand flash,添加前缀带nand的源码即可。
编译,会提示如下错误:
..\Components\levelx-6.1.10_rel\common\inc\lx_api.h(102): error: #5: cannot open source input file "tx_api.h": No such file or directory
搜索源码目录可以发现没有tx_api.h
头文件。
查看lx_api.h
头文件内容:
/* Determine if the optional LevelX user define file should be used. */
#ifdef LX_INCLUDE_USER_DEFINE_FILE
/* Yes, include the user defines in lx_user.h. The defines in this file may
alternately be defined on the command line. */
#include "lx_user.h"
#endif
/* Include the ThreadX api file. */
#ifndef LX_STANDALONE_ENABLE
#include "tx_api.h"
#endif
#ifdef LX_STANDALONE_ENABLE
/* Define compiler library include files. */
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
...
...
#endif
从注释Include the ThreadX api file
,可以看出tx_api.h
是ThreadX 的头文件。
以及从LX_STANDALONE_ENABLE
宏定义名字STANDALONE可以知道,单定义该宏时表示独立使用而不依赖于其他东西。
直接在lx_api.h
定义LX_STANDALONE_ENABLE
?
从lx_api.h
头文件开头可以看到还包含了#include "lx_user.h"
,inc目录下有一个头文件lx_user_sample.h
,文件内容都是一些宏定义,LX_STANDALONE_ENABLE
也就是宏定义,很容易就可以猜到需要把lx_user_sample.h
重命名为lx_user.h
包含lx_user.h
: lx_api.h
头文件定义LX_INCLUDE_USER_DEFINE_FILE
#define LX_INCLUDE_USER_DEFINE_FILE
修改lx_user.h
文件:取消LX_STANDALONE_ENABLE
宏的注释
#define LX_STANDALONE_ENABLE
再次编译发现已经没有错误。
2、驱动程序移植
实现这几个函数:
UINT (*nor_driver_initialize)(LX_NOR_FLASH *)
UINT (*lx_nor_flash_driver_read)(ULONG *flash_address, ULONG *destination, ULONG words);
UINT (*lx_nor_flash_driver_write)(ULONG *flash_address, ULONG *source, ULONG words);
UINT (*lx_nor_flash_driver_block_erase)(ULONG block, ULONG erase_count);
UINT (*lx_nor_flash_driver_block_erased_verify)(ULONG block);
UINT (*lx_nor_flash_driver_system_error)(UINT error_code);
从LevelX手册可以知道,实现nor_driver_initialize
的时候除了实现跟硬件操作的几个函数,还要初始化指定:
- nor flash的基地址
- 块总数和每个块有多少个字
- 开辟RAM缓冲区,以 ULONG对齐进行读取512字节扇区。
#include "lx_api.h"
#include "bsp_w25qxx.h"
#include "rtthread.h"
#include "slog.h"
#define FLASH_SIZE_PER_BLOCK 32 /* Kbyte */
#define FLASH_SECTORS_PER_BLOCK (32/4) /* 每个块包含的扇区数 */
#define FLASH_TOTAL_SECTORS 128
#define FLASH_NAME "XM25Q128A"
#define FLASH_BASE_ADDR 0x00 /* Flash base address */
#define FLASH_TOTAL_BLOCKS FLASH_TOTAL_SECTORS /* 总块数 */
#define FLASH_SIZE_KB_PER_SECTOR 4 /* 扇区Kbyte大小 */
#define FLASH_SIZE_BYTE_PER_SECTOR (FLASH_SIZE_KB_PER_SECTOR * 1024) /* 扇区字节大小 */
#define FLASH_WORDS_PER_SECTOR (FLASH_SIZE_BYTE_PER_SECTOR/sizeof(ULONG)) /* word = 4 byte */
static ULONG sector_buffer[FLASH_WORDS_PER_SECTOR]; /* 开辟的RAM缓冲区 */
/* 读flash */
static UINT lx_nor_flash_driver_read(ULONG *flash_address, ULONG *destination, ULONG words)
{
W25QXX_Read((uint8_t *)destination, (uint32_t)flash_address, (uint16_t)(words*4));
return (LX_SUCCESS);
}
/* 写flash */
static UINT lx_nor_flash_driver_write(ULONG *flash_address, ULONG *source, ULONG words)
{
W25QXX_Write_NoCheck((uint8_t *)source, (uint32_t)flash_address, (uint16_t)(words*4));
return (LX_SUCCESS);
}
/* 擦除 */
static UINT lx_nor_flash_driver_block_erase(ULONG block, ULONG erase_count)
{
LX_PARAMETER_NOT_USED(erase_count);
W25QXX_EraseSector(block * FLASH_SIZE_BYTE_PER_SECTOR);
return (LX_SUCCESS);
}
/* 用于校验指定的块是否擦除成功 */
static UINT lx_nor_flash_driver_block_erased_verify(ULONG block)
{
LX_PARAMETER_NOT_USED(block);
return (LX_SUCCESS);
}
/* 用于检测系统错误,依赖于驱动程序 */
static UINT lx_nor_flash_driver_system_error(UINT error_code)
{
LX_PARAMETER_NOT_USED(error_code);
return (LX_ERROR);
}
static UINT nor_driver_initialize(LX_NOR_FLASH *nf)
{
/* Setup the base address of the flash memory. */
nf->lx_nor_flash_base_address = (ULONG *)FLASH_BASE_ADDR;
/* Setup geometry of the flash. */
nf->lx_nor_flash_total_blocks = FLASH_TOTAL_BLOCKS;
nf->lx_nor_flash_words_per_block = FLASH_WORDS_PER_SECTOR;
/* Setup driver’s initialization function */
nf->lx_nor_flash_driver_read = lx_nor_flash_driver_read;
nf->lx_nor_flash_driver_write = lx_nor_flash_driver_write;
nf->lx_nor_flash_driver_block_erase = lx_nor_flash_driver_block_erase;
nf->lx_nor_flash_driver_block_erased_verify = lx_nor_flash_driver_block_erased_verify;
nf->lx_nor_flash_driver_system_error = lx_nor_flash_driver_system_error;
/* Setup local buffer for NOR flash operation. This buffer must be the sector size of the NOR flash memory. */
nf->lx_nor_flash_sector_buffer = sector_buffer;
return(LX_SUCCESS);
}
nf->lx_nor_flash_total_blocks = FLASH_TOTAL_BLOCKS; 设置总的块数大小,
这里的blocks是LevelX定义的块跟nor flash的块不一样,
在这里将norflash的扇区当作LevelX的块来处理。
测试代码:
LX_NOR_FLASH XM25Q128A;
void LX_NOR_FLASH_Init(void)
{
_lx_nor_flash_initialize();
_lx_nor_flash_open(&XM25Q128A, FLASH_NAME, nor_driver_initialize);
}
void LX_NOR_FLASH_ReadWrite_Test(void)
{
uint8_t wbuf[512] = "http://www.optomedic.com";
uint8_t rbuf[512] = {0};
uint8_t wbuf_other[512] = "Who am I?";
_lx_nor_flash_sector_write(&XM25Q128A, 1, wbuf);
_lx_nor_flash_sector_write(&XM25Q128A, 1, wbuf_other);
_lx_nor_flash_sector_read(&XM25Q128A, 1, rbuf);
printf("lx read %s\r\n", rbuf);
}
uint8_t wbuf[50] = "http://www.optomedic.com";
uint8_t rbuf[50] = {0};
刚开始一直定义 50大小的缓冲区进行读写测试,必定进入hardfault。
问题找了很久,用了stlink调试也找不出导致hardfault的代码。
偶然看到了其接口参数注释:
/* nor_flash NOR flash instance */
/* logical_sector Logical sector number */
/* buffer Pointer to buffer to read into*/
/* (the size is 512 bytes) */
注意!!!:
读写接口的buffer的大小一定要设置为512字节(128字),否则hardfault。