将偏移字节数组转换为 C 中的 int32 数组

Casting of offsetted byte array to int32 array in C

背景 我正在使用 IAR Embedded Workbench IDE 和在 STM32F091(ARM Cortex-M0 内核)微控制器上运行的工具链开发一个用 C 语言编写的嵌入式应用程序。应用程序将数据写入微控制器的嵌入式闪存,其中只能输入 32 位字(也许 half-words 也可以)。

问题描述 数据存储在 uint8_t 字节类型数组中,开头有一些 header 信息(在本例中是来自 on-board 调制解调器的 AT 响应代码),不应写入闪存.我想发送一个 uint32_t 指针指向 uint8_t 缓冲区中实际数据开始的位置。但是如果这个偏移量不是 4 字节对齐的,我的应用程序就会崩溃,因为它试图访问一个未对齐的 uint32_t 类型。

这描述了我正在尝试做的事情(不是真正的代码,只是一个例子):

uint8_t modemResponseBuffer[MAX_MODEM_RESPONSE_SIZE];

/* Get the modem response data (including modem response header data) */
size_t modemResponseSize = GetModemResponseData(modemResponseBuffer);

/* Get the actual data size from the header information */
size_t dataSize = GetActualDataSizeFromModemResponseHeader(modemResponseBuffer);

/* Get the offset to where the actual data starts in the modem response */
size_t modemDataOffset = GetModemResponseDataOffset(modemResponseBuffer);

/* Write the data part of the response to embedded flash memory. 
The modemDataOffset can be any number which messes up 4 byte data alignment */
ProgramFlashMemory(DATA_FLASH_STORAGE_ADDRESS, (uint32_t*)&modemResponseBuffer[modemDataoffset],
 dataSize);

ProgramFlashMemory函数内部,循环调用FLASH_ProgramWord标准外设库函数

问题 我如何有效地解决这个问题?我在一个内存量有限(32 kb RAM)的系统上工作,所以我不想将所需内容从 uint8_t 缓冲区复制到 uint32_t 类型的新缓冲区。目前,我已经通过循环手动逐字节对齐数据,但这对我来说似乎相当笨拙。但我还没有想出更好的解决方案,我很想知道我可能会在这里收到什么建议。

另外,如果有人知道,我也想知道为什么在这种情况下应用程序会崩溃。我的核心(或任何核心?)无法处理未对齐数据类型的原因是什么?

这假设在写入负载数据后不再需要 header 数据。

为确保缓冲区的对齐正确,您可能需要这样声明它:

uint32_t modemResponseBuffer[(MAX_MODEM_RESPONSE_SIZE * sizeof (uint8_t) / sizeof (uint32_t)) + 1];

在调用 writer-function:

之前,只需将负载数据移动到缓冲区的开头
memmove(modemResponseBuffer, modemResponseBuffer + modemDataoffset, dataSize);

请注意,memcpy() 在这里不起作用,因为目标和源重叠。

然后这样调用作者:

ProgramFlashMemory(DATA_FLASH_STORAGE_ADDRESS, modemResponseBuffer, dataSize);

ARM Cortex-M0 内核在未对齐访问时崩溃的原因是 that's what it's designed to do。采取硬故障异常实际上是对一些旧内核的改进,这些内核会错误地访问该值,然后继续使用损坏的值执行。一些较新的 ARM 内核对未对齐访问的硬件支持有限。

这里有一些建议。

重新设计 ProgramFlashMemory(),使其接受 uint8_t* 而不是 uint32_t*。为了对单词进行编程,它应该将缓冲区中的各个字节复制到具有正确对齐方式的局部变量中。然后将局部变量copy写入flash。

重新设计 GetModemResponseData(),以便在从流中读取 header 时解析 header。在从流中读取数据之前,它将确定 header 的长度和数据的起始点。当流位于数据的开头时,它应该开始将数据复制到与 header 分开并正确对齐的新缓冲区中。

ProgramFlashMemory() 更改为 void*,然后在内部将其转换为 uint8_t*,然后将其迭代,一次将四个字节转换为 unit32_t然后写入闪存。

void* 允许写入任何对象的地址而无需显式转换。

类似于:

int ProgramFlashMemory( uint32_t* addr, void* data, int length )
{
    int byte = 0 ;
    int word = 0 ;

    while( byte < length )
    {
        uint32_t flash_word = 0 ;

        // Copy four bytes to word
        // Note: little-endian byte order assumed,
        //       reverse for big-endian.  
        // If end is not aligned, fill with 0xff.
        for( b = 0; b < 4; b++ )
        {
            flash_word |= byte < length ? 
                          (uint8_t*)data[byte] << (b<<3) : 
                          0xFF ;
            byte++ ;
        }

        ProgramFlashWord( addr[word], flash_word ) ;
        word++ ;
    }

    // Return bytes written - may be linger than `length` by up-to 
    // three for end-alignment.
    return byte ;
}

您可能希望保留原始 ProgramFlashMemory() 以实现高效对齐写入,在这种情况下可能 ProgramFlashMemoryUnaligned()。请注意,您需要注意的不仅仅是对齐,而且长度不一定能被 4 整除。