位移位与数组索引,更适合 32 位 MCU 上的 uart 接口

Bitshifting vs array indexing, which is more appropriate for usart interfaces on 32bit MCUs

我有一个带有 USART HAL 的嵌入式项目。这个 USART 一次只能发送或接收 8 位或 16 位(取决于我选择的 usart 寄存器,即 single/double in/out)。由于它是一个 32 位 MCU,我想我也可以传递 32 位字段,因为(据我了解)这是 MPU 位的更有效使用。同样适用于 64 位 MPU,即传递 64 位整数。也许这是错误的建议,或者是断章取义的建议。

考虑到这一点,我通过位移将 8 位打包到 32 位字段中。我对 usart 上的 tx 和 rx 都这样做。

8位寄存器的代码如下(16位寄存器只有一半的移位轮数):

int zg_usartTxdataWrite(USART_data*         MPI_buffer,
                        USART_frameconf*    MPI_config,
                        USART_error*        MPI_error)
{

MPI_error = NULL;

if(MPI_config != NULL){
    zg_usartFrameConfWrite(MPI_config);
}

HPI_usart_data.txdata = MPI_buffer->txdata;

    for (int i = 0; i < USART_TXDATA_LOOP; i++){
        if((USART_STATUS_TXC & usart->STATUS) > 0){
            usart->TXDATAX = (i == 0 ? (HPI_usart_data.txdata & USART_TXDATA_DATABITS) : (HPI_usart_data.txdata >> SINGLE_BYTE_SHIFT) & USART_TXDATA_DATABITS);
        }
        usart->IFC |= USART_STATUS_TXC;
    }
    return 0;
}

编辑:重新输入上述代码的逻辑并添加定义以明确注释部分中讨论的三元运算符隐式提升问题

(HPI_usart 和 USART_data 结构相同,只是级别不同,我已经删除了 HPI_usart 层,但为了这个例子,我将把它留在)

#define USART_TXDATA_LOOP 4
#define SINGLE_BYTE_SHIFT 8

typedef struct HPI_USART_DATA{

   ...
   uint32_t txdata;
   ...

}HPI_usart

HPI_usart HPI_usart_data = {'[=11=]'};

const uint8_t USART_TXDATA_DATABITS = 0xFF;

int zg_usartTxdataWrite(USART_data*         MPI_buffer,
                        USART_frameconf*    MPI_config,
                        USART_error*        MPI_error)
{

MPI_error = NULL;

if(MPI_config != NULL){
    zg_usartFrameConfWrite(MPI_config);
}

HPI_usart_data.txdata = MPI_buffer->txdata;

    for (int i = 0; i < USART_TXDATA_LOOP; i++){
        if((USART_STATUS_TXC & usart->STATUS) > 0){
            usart->TXDATAX = (i == 0 ? (HPI_usart_data.txdata & USART_TXDATA_DATABITS) : (HPI_usart_data.txdata >> SINGLE_BYTE_SHIFT) & USART_TXDATA_DATABITS);
        }
        usart->IFC |= USART_STATUS_TXC;
    }
    return 0;
}

但是,我现在意识到这可能导致的问题多于它解决的问题,因为我本质上是在内部编码这些位,然后当它们通过 to/from 不同的数据层时几乎必须立即解码。我觉得这是一个聪明而性感的解决方案,但我现在正试图解决一个我一开始就不应该创造的问题。就像当存在偏移量时如何提取可变位字段,即在 gps nmea 句子中,前 8 位可能是一个相关字段,然后其余是 32 位字段。所以它最终是这样的:

32 位数组成员 0:

 bits 24-31      bits 15-23          bits 8-15            bits 0-7

| 8 位值 | 32 位值 A,位 24-31 | 32 位值 A,位 16-23 | 32 位值 A,位 8-15 |

32 位数组成员 1:

 bits 24-31             bits 15-23           bits 8-15               bits 0-7

| 32 位值 A,位 0-7 | 32 位值 B,位 24-31 | 32 位值 B,位 16-23 | 32 位值 B,位 8-15 |

32 位数组成员 2:

 bits 24-31        15-23 8-15 ...

| 32 位值 B,位 0-7 |等等... | .... | .... |

上面的例子需要手动解码,我想这很好,但每个 nmea 句子都不一样,感觉比编程更手动。

我的问题是:位移位与数组索引,哪个更合适?

我是否应该将每个 incoming/outgoing 值分配给一个 32 位数组成员,然后以这种方式进行索引?我觉得这就是解决方案,因为它不仅可以更轻松地遍历其他层上的数据,而且我可以消除所有这些位移逻辑,然后 rx 或 tx 函数之间的唯一区别就是数据的方向。

这确实意味着对界面和生成的 gps 模块层进行了小幅重写,但这感觉工作量减少了,而且在我的项目早期也是一个便宜的教训。

此外,任何关于此的想法和一般经验都会很棒。

Since it's a 32-bit MCU, I figured I might as well pass around 32-bit fields

这不是真正的程序员的要求。将 8 位或 16 位变量放入结构中。如果需要,让编译器添加填充。或者,您可以使用 uint_fast8_tuint_fast16_t.

My question is this: bitshifting vs array indexing, which is more appropriate?

数组索引用于访问数组。如果您有数组,请使用它。如果没有,那就不要。

虽然可以逐字节咀嚼更大的数据块,但必须更加小心地编写此类代码,以防止运行出现各种微妙的类型转换和指针别名错误。

一般来说,当访问数据达到 CPU 的字长(在本例中为 32 位)时,首选位移位。它速度快且便携,因此您不必考虑字节顺序。这是 serialization/de-serialization 整数的首选方法。