c
//G0B0 flash大小 0x08000000-0x0807FFFF 512K(0400 1K)
//2k 1页
//初始化标记数据地址 放最前面 脱机烧写器可擦除掉
#define CONST_INITMARKDATA_ADDRESS (0x0807D000UL)
//2k 1页
//射频数据地址
#define CONST_FREQDATA_ADDRESS (0x0807F000UL)
//2k 1页
//本振数据地址
#define CONST_BWLODATA_ADDRESS (0x0807F800UL)
//用户数据标记
#define CONST_USERDATA_ACTIVECOOKIE (0xFEDCCDEF)
========================================================================
__align(8) static uint8_t flashWriteBuf[2048];
__align(8)
: 用于指定存储在内存中的变量的对齐方式为 8 字节。对齐的目的是为了优化内存访问,确保变量在内存中的地址是某个值的倍数(这里是 8 字节)。这通常对于某些硬件和特定的数据结构是必要的。static
: 用于指示该变量的存储持续时间为整个程序的执行期间。在这里,flashWriteBuf
是一个静态变量,它在程序的整个执行期间都存在,并且其生命周期延伸到整个程序的运行时间。
这行代码定义了一个静态的、对齐为 8 字节的、包含 2048 个无符号8位整数的数组 flashWriteBuf
。
=========================================================================
为什么要使用 __align(8),以及对齐方式在内存中的表现是什么样的?
在C语言中,对齐方式是指变量在内存中的存储起始位置相对于地址的偏移量。
对齐的主要目的是优化内存访问速度,特别是对于一些体系结构或硬件,它们可能要求某些数据类型的变量从特定地址开始。
-
访问速度: 在某些架构中,对齐的数据访问速度更快。例如,某些处理器可能要求访问 4 字节整数从4字节对齐的地址开始,否则可能会引发性能损失或错误。
-
缓存行: 许多处理器以缓存行为单位加载数据到缓存中。如果变量的首地址是缓存行对齐的,那么对该变量的访问可能会更加高效。
-
硬件限制: 某些硬件要求特定数据类型从特定对齐方式的地址开始。如果不满足这些要求,可能会导致硬件异常或性能下降。
在具体的应用场景中,选择对齐方式通常是为了优化性能或者满足硬件的要求。然而,过度的对齐可能会导致内存空间的浪费。在实际开发中,对齐的选择需要根据具体的硬件和性能需求进行权衡。
=========================================================================
1. 实现逻辑:
c
uint8_t* coreFlashAcquireWriteBuffer(void)
{
return flashWriteBuf;
}
该函数返回一个 uint8_t
类型的指针。函数体内只有一条语句,即返回名为 flashWriteBuf
的静态数组的首地址。
主要目的是获取可用于写入的缓冲区。由于返回的是指向 uint8_t
的指针,可以将这个指针用于写入字节数据。
2. 主要目的和适用场景:
这段代码的主要目的是提供一个接口,允许外部模块或函数获取一个可用于写入的缓冲区。
这种模式常见于需要进行数据写入或者数据缓冲的情境,例如将数据写入到 Flash 存储器中。
适用场景可能包括需要向 Flash 存储器写入数据的任务。通过调用 coreFlashAcquireWriteBuffer
函数,程序可以获取一个缓冲区,然后将待写入的数据写入该缓冲区。
这种设计允许程序灵活地控制数据的写入,同时提供了一种有效的机制来管理数据的写入操作。
====================================================================
1. 实现逻辑:
c
static uint32_t getPage(uint32_t addr)
{
uint32_t page = 0;
/* Bank 1 */
page = (addr - FLASH_BASE) / FLASH_PAGE_SIZE;
return page;
}
这段代码定义了一个静态函数 getPage
,接受一个 uint32_t
类型的地址参数,计算并返回与该地址相关的页面号(page number)。在这个实现中,页面号被计算为地址相对于基地址 FLASH_BASE
的偏移量除以页面大小 FLASH_PAGE_SIZE
。
2. 主要目的和适用场景:
主要目的是根据给定的地址计算出相应的页面号。
在嵌入式系统中,Flash存储器通常分为多个页面,每个页面存储一定量的数据。
获取地址对应的页面号可以用于确定数据在Flash中的位置。
适用场景可能包括在程序中管理Flash存储器的情况,比如需要存储和检索数据到Flash中的特定页面。通过调用 getPage
函数,程序可以获得给定地址所在的页面号,然后使用这个页面号进行相应的操作,例如擦除或写入数据。
总体而言,这个函数是一个用于获取地址对应页面号的工具函数,可以用于在嵌入式系统中管理Flash存储器中的数据。
========================================================================
1. 实现逻辑:
c
int32_t coreFlashWrite(void* dstAddr, const void* srcAddr, const uint32_t size)
{
//...(变量定义和初始化)
//初始化FLASH_EraseInitTypeDef
FLASH_EraseInitTypeDef eraseInitStruct = {0x00};
int32_t ret = ERR_FLASHOP_OK; // 返回值
//判断入参正确
if (!srcAddr || !dstAddr || 0 == size)
{
return ERR_FLASHOP_PARAM_INVAL;
}
if (0 != ((uint32_t)srcAddr & 0x00000007))
{
return ERR_FLASHOP_SRCADDR_UNALIGN;
}
if (0 != ((uint32_t)dstAddr & 0x00000007))
{
return ERR_FLASHOP_DSTADDR_UNALIGN;
}
//获取要擦除的第一页
firstPage = getPage((uint32_t)dstAddr);
//获取要从第一页擦除的页数
nbOfPages = size / FLASH_PAGE_SIZE + (size % FLASH_PAGE_SIZE ? 1 : 0);
eraseInitStruct.TypeErase = FLASH_TYPEERASE_PAGES;
eraseInitStruct.Page = firstPage; // 擦除起始页
eraseInitStruct.NbPages = nbOfPages; // 擦除页数
HAL_FLASH_Unlock(); // Flash解锁
__disable_irq(); // 擦除前需要关闭中断
if (HAL_FLASHEx_Erase(&eraseInitStruct, &pageError) != HAL_OK)
{
__enable_irq();
ret = -ERR_FLASHOP_FAIL_ERASE;
goto exit;
}
__enable_irq();
//开始写入Flash
flashAddress = (uint32_t)dstAddr;
nWriteAll = size;
while (nHasWrite < nWriteAll)
{
// 以64位为单位写入数据
srcDW = *(uint64_t*)((uint32_t)srcAddr + nHasWrite);
if (HAL_FLASH_Program(FLASH_TYPEPROGRAM_DOUBLEWORD, flashAddress, srcDW) == HAL_OK)
{
flashAddress += 8;
nHasWrite += 8;
}
else
{
ret = ERR_FLASHOP_FAIL_PROG;
goto exit;
}
}
exit:
HAL_FLASH_Lock(); // Flash加锁
return ret;
}
2. 主要目的和适用场景:
这段代码的主要目的是实现Flash存储器的写入操作。该函数接受目标地址 dstAddr
、源地址 srcAddr
和写入大小 size
作为参数,首先擦除目标地址所在的Flash页面,然后将数据从源地址写入到目标地址。
适用场景可能涉及到需要在嵌入式系统中进行Flash存储器的写入操作,例如在固件更新或配置保存等场景。
3. 使用的关键字和修饰符:
static
: 该函数没有使用static
关键字,因此在其他文件中可以访问。uint32_t
,void
,const
: 这些是数据类型,用于声明参数和变量的类型。FLASH_EraseInitTypeDef
: 这是一个结构体类型,用于配置Flash擦除的初始化参数。HAL_FLASH_Unlock()
,HAL_FLASH_Lock()
: 这些是由HAL(Hardware Abstraction Layer)提供的函数,用于解锁和锁定Flash。
总体而言,这是一个用于在嵌入式系统中进行Flash存储器写入的函数,包括擦除和写入两个步骤。============================================================================
1. 实现逻辑:
c
bool initMarkDataIsActive(void)
{
const volatile initMarkDataFlash_t* rawPtr = (const volatile initMarkDataFlash_t*)CONST_INITMARKDATA_ADDRESS;
return (rawPtr->active == CONST_USERDATA_ACTIVECOOKIE);
}
这段代码定义了一个函数 initMarkDataIsActive
,该函数返回一个布尔值。函数内部首先声明了一个指向 initMarkDataFlash_t
结构体类型的 volatile
常量指针 rawPtr
,然后将其初始化为指向一个特定地址 CONST_INITMARKDATA_ADDRESS
。
函数最后通过比较 rawPtr->active
和 CONST_USERDATA_ACTIVECOOKIE
的值来确定初始化标记数据是否处于激活状态。
2. 主要目的和适用场景:
主要目的是检查初始化标记数据是否处于激活状态。在嵌入式系统中,可能会有一些标记数据用于指示系统的初始化状态,这个函数就是用于检查这些标记数据是否处于激活状态。
适用场景可能包括在系统启动时检查某些初始化数据,以确保系统处于已初始化的状态。
3. 使用的关键字和修饰符:
const
,volatile
: 这些关键字用于修饰指针rawPtr
,指明该指针指向的数据是常量且易变的,这通常用于防止编译器进行一些优化,确保每次访问都会从内存中读取最新的值。
总体而言,这个函数用于检查初始化标记数据是否处于激活状态,返回一个布尔值表示检查结果。
=========================================================================
1. 实现逻辑:
c
void initMarkDataReadFlash(void)
{
const volatile initMarkDataFlash_t *rawPtr = (const volatile initMarkDataFlash_t*)CONST_INITMARKDATA_ADDRESS;
if (initMarkDataIsActive())
{
memcpy(&initMarkDataFlash, (void*)rawPtr, sizeof(initMarkDataFlash_t));
}
}
函数内部首先声明了一个指向 initMarkDataFlash_t
结构体类型的 volatile
常量指针 rawPtr
,然后将其初始化为指向一个特定地址 CONST_INITMARKDATA_ADDRESS
。
接着,通过调用 initMarkDataIsActive
函数检查初始化标记数据是否处于激活状态。如果激活,那么通过 memcpy
函数将 Flash 中的数据复制到一个全局变量 initMarkDataFlash
中。
2. 主要目的和适用场景:
主要目的是在系统启动时从 Flash 中读取初始化标记数据。在嵌入式系统中,初始化标记数据可能包含一些配置信息或者系统状态,该函数的目的是将这些数据读取到内存中,以便系统可以使用这些数据进行初始化。
适用场景可能包括在系统启动时加载先前保存的配置或状态信息,以确保系统处于正确的状态。
总体而言,这个函数用于从 Flash 中读取初始化标记数据,如果数据处于激活状态,则将其复制到全局变量中。
=======================================================================
1. 实现逻辑:
c
int32_t initMarkDataSyncFlash(void)
{
int32_t ret = 0;
//用户数据写入
ret = coreFlashWrite((void*)CONST_INITMARKDATA_ADDRESS, (void*)&initMarkDataFlash, sizeof(initMarkDataFlash_t));
return ret;
}
函数内部首先声明了一个整数变量 ret
,用于存储函数执行的结果。
接着,通过调用 coreFlashWrite
函数将全局变量 initMarkDataFlash
中的数据写入到 Flash 中的指定地址 CONST_INITMARKDATA_ADDRESS
。
最后,函数返回执行的结果。
2. 主要目的和适用场景:
主要目的是将全局变量 initMarkDataFlash
中的数据同步到 Flash 中的指定地址。在嵌入式系统中,这可能用于将配置或状态信息保存到 Flash 中,以便在系统重新启动时恢复状态。
适用场景可能包括在系统运行时修改了一些配置或状态信息,希望将这些更改保存到持久性存储中。
总体而言,这个函数用于将全局变量中的数据写入到 Flash 中的指定地址,返回一个表示执行结果的整数值。