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

Posted

技术标签:

【中文标题】移位与数组索引,更适合 32 位 MCU 上的 uart 接口【英文标题】:Bitshifting vs array indexing, which is more appropriate for usart interfaces on 32bit MCUs 【发布时间】:2019-07-13 15:00:00 【问题描述】:

我有一个带有 USART HAL 的嵌入式项目。此 USART 一次只能发送或接收 8 或 16 位(取决于我选择的 usart 寄存器,即单/双输入/输出)。由于它是一个 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 = '\0';

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;

但是,我现在意识到,这可能会导致比它解决的问题更多的问题,因为我本质上是在内部对这些位进行编码,然后当它们传入/传出不同的数据层时,几乎必须立即对其进行解码。我觉得这是一个聪明而性感的解决方案,但我现在正试图解决一个我一开始就不应该创造的问题。就像在存在偏移时如何提取可变位字段一样,即在 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 句子来说都是不同的,感觉比编程更手动。

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

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

这确实意味着对接口和生成的 gps 模块层进行小幅重写,但这感觉像是在我的项目早期的工作量更少,而且也是一个廉价的教训。

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

【问题讨论】:

tl;博士。如果有一个数组并且您只想访问 8 位边界上的值,索引一个 char-array(或您想要访问的 char* 别名)总是比位移更合适且更容易阅读。 太棒了。感谢您的健全性检查。我想我对这个......坏脑......坏......去坐在角落里,别想太多了! 不要写usart-&gt;TXDATAX = (i == 0 ? (HPI_usart_data.txdata &amp; USART_TXDATA_DATABITS) : (HPI_usart_data.txdata &gt;&gt; SINGLE_BYTE_SHIFT) &amp; USART_TXDATA_DATABITS);之类的代码。这是危险的,不可读的,需要分成几个表达式。 @Swordfish char 数组完全不适合用于字符串以外的任何内容。它永远不应该用于反序列化更大的数据块。如果尝试,当将位运算符与可能有符号的char 混合时,程序员将最终陷入隐式类型提升地狱。 @Medicineman25 我的评论主要是关于“一条线上的大多数运营商都赢得了价格”。当你写出像那行一样的混乱时,你就会发生多个隐含的促销活动。其中大约 8 个。你写那行的时候把所有这些都考虑进去了吗?不?坦率地说,你编写的代码你不知道它做了什么——错误和危险。是的, ?: 运算符增加了一点,通过平衡第二个和第三个操作数,无论哪个被评估。 【参考方案1】:

因为它是一个 32 位的 MCU,我想我不妨绕过 32 位字段

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

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

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

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

通常,在访问 CPU 字长(在本例中为 32 位)的数据时,首选移位。它既快速又便携,因此您不必考虑字节序。它是整数序列化/反序列化的首选方法。

【讨论】:

同意。这是针对代码非常模块化的用例,并且我使用的 gps 层将是开源的,因此我需要考虑到并非每个人都希望将每个 8/16 位数据编码为一个 32 位的字段。无论哪种方式,像这样编码然后立即解码都是浪费功率。

以上是关于移位与数组索引,更适合 32 位 MCU 上的 uart 接口的主要内容,如果未能解决你的问题,请参考以下文章

数组移位/错误索引/i = [x+y*size+z*size*size]

将 32 位整数移位 32 位

C语言 循环移位

8位MCU要被淘汰了吗?

8位MCU要被淘汰了吗?

国民技术MCU与STM32GD32 PintoPin 兼容列表