我喜欢根据BARR-C:2018标准编写嵌入式C固件,最近我开始使用PC-Lint来检查代码。我目前使用的是PIC 32处理器,我想配置一个寄存器,使其:
1.易于阅读,
1.不生成linter消息,
1.编译成少量的汇编程序。
我将在下面列出我的选择,有优点和缺点。我很想听听你的解决方案。我选择用来说明这个问题的寄存器并不重要。
Microchip提供头的位字段
✓没有lint消息,易于阅读,易于更改个人设置
20个汇编指令(O 0)(增加更复杂的寄存器)
ADC0TIMEbits.SAMC = 25U;
ADC0TIMEbits.ADCDIV = 50U;
ADC0TIMEbits.BCHEN = 0U;
ADC0TIMEbits.SELRES = 2U;
ADC0TIMEbits.ADCEIS = 6U;
简单赋值
✓无lint消息,2个汇编指令(O 0)
不容易阅读,不容易更改个人设置
ADC0TIME = 0x1A190019U;
明确考虑每个寄存器中的每个位
✓没有lint消息,2个装配说明(O 0),易于更改个人设置
不易验证移位值是否正确
ADC0TIME = ((25U << 0U) | /* ADC0 Sample Time bits */
(50U << 16U) | /* ADC0 Clock Divisor bits */
(0U << 23U) | /* Buffer Channel Enable bit */
(2U << 24U) | /* ADC0 Resolution Select bits */
(6U << 26U)); /* ADC0 Early Interrupt Select bits */
使用Microchip提供的头文件中的定义明确考虑每个寄存器中的每个位
✓ 2个装配说明(O 0),易于更改个人设置,换档值由制造商控制,并且易于阅读
提供的头中的值是有符号的,这导致Lint info 8524:使用运算符'<<'组合有符号和无符号类型[BARR-C:2018规则5.3c]
ADC0TIME = ((25U << _ADC0TIME_SAMC_POSITION) | /* ADC0 Sample Time bits */
(50U << _ADC0TIME_ADCDIV_POSITION) | /* ADC0 Clock Divisor bits */
(0U << _ADC0TIME_BCHEN_POSITION) | /* Buffer Channel Enable bit */
(2U << _ADC0TIME_SELRES_POSITION) | /* ADC0 Resolution Select bits */
(6U << _ADC0TIME_ADCEIS_POSITION)); /* ADC0 Early Interrupt Select bits */
如上+转换常量以摆脱linter消息
✓ 2个装配说明(O 0),易于更改个人设置,换档值由制造商控制,并且是人类可读的,没有棉绒信息
铸造看起来繁琐
ADC0TIME = ((25U << (uint32_t)_ADC0TIME_SAMC_POSITION) | /* ADC0 Sample Time bits */
(50U << (uint32_t)_ADC0TIME_ADCDIV_POSITION) | /* ADC0 Clock Divisor bits */
(0U << (uint32_t)_ADC0TIME_BCHEN_POSITION) | /* Buffer Channel Enable bit */
(2U << (uint32_t)_ADC0TIME_SELRES_POSITION) | /* ADC0 Resolution Select bits */
(6U << (uint32_t)_ADC0TIME_ADCEIS_POSITION)); /* ADC0 Early Interrupt Select bits */
或者,我可以修改Microchip提供的头文件,并将常量定义为无符号。这意味着头部是非标准的,我需要将其包含在我的项目存储库中。有问题吗
编辑:从Microchip头文件中提取。值有符号:
#define _ADC0TIME_SAMC_POSITION 0x00000000
#define _ADC0TIME_SAMC_MASK 0x000003FF
#define _ADC0TIME_SAMC_LENGTH 0x0000000A
#define _ADC0TIME_ADCDIV_POSITION 0x00000010
#define _ADC0TIME_ADCDIV_MASK 0x007F0000
#define _ADC0TIME_ADCDIV_LENGTH 0x00000007
#define _ADC0TIME_BCHEN_POSITION 0x00000017
#define _ADC0TIME_BCHEN_MASK 0x00800000
#define _ADC0TIME_BCHEN_LENGTH 0x00000001
#define _ADC0TIME_SELRES_POSITION 0x00000018
#define _ADC0TIME_SELRES_MASK 0x03000000
#define _ADC0TIME_SELRES_LENGTH 0x00000002
#define _ADC0TIME_ADCEIS_POSITION 0x0000001A
#define _ADC0TIME_ADCEIS_MASK 0x1C000000
#define _ADC0TIME_ADCEIS_LENGTH 0x00000003
3条答案
按热度按时间bvn4nwqk1#
我如何配置微控制器寄存器没有linter消息
使用选项
As above + casting the constants to get rid of the linter message
。ogq8wdun2#
O0
下的汇编指令数量无关紧要。但是,由于这些是硬件外围寄存器,并且是volatile
限定的,因此优化器在启用时只能对代码做这么多。带有Microchip提供的报头的位字段
就目前而言,这绝对是解决任何问题的错误解决方案,因为您最终会多次写入同一个寄存器。并且位字段由于许多其他原因也是有问题的。只有当将所有结果存储到RAM中的临时非
volatile
变量中,然后通过一次赋值将其复制到实际寄存器中时,才能以合理的方式使用此方法。ADC0TIME = 0x1A190019U;
这是一个不可读的混乱的“神奇数字”,因此显然应该避免像瘟疫。
明确考虑每个寄存器中的每一位
这是一个好一点,但你仍然在使用“魔术数字”。
使用Microchip提供的头文件中的定义明确考虑每个寄存器中的每个位
这接近于使用硬件寄存器的“事实上的”标准方式。这是相当可读的,但左操作数的移位应命名为常数或至少解释与注解。
如上+转换常量以摆脱linter消息
这是一种无稽之谈。如果提供的Microchip常量是一个小整数类型,那么它们将隐式地获得提升为
int
的整数,我认为它在您的MCU上是32位。我不知道Michael Barr标准,但它基本上是MISRA C的精简版本,在MISRA C中,不允许有隐式整数提升。MISRA C不够具体,无法对移位的右操作数进行例外处理。
因为移位的右操作数是无害的。它 * 是 * 整数提升,但其他方面根本不影响最终结果。移位的结果总是(提升的)* left * 操作数的类型。
请注意,MISRA/Barr对移位的所有关注主要集中在使用有符号的左操作数上,这几乎总是错误的。或者更牵强,也许是一个没有意义的负右操作数。
你应该做的:
使用“使用Microchip提供的头文件中的定义”版本,但如果可能的话,使用更少的魔术数字。
在32位MCU上,则使用32位无符号常量。如果Microchip提供的常量不是无符号的32,那么它们的库就被破坏了。如果他们是,但你的linter是抱怨他们仍然,那么你的linter是坏了。
可能存在具有针对该特定MCU的寄存器支持的商业编译器,这可能是在Microchip库被破坏的情况下的一个选项。否则,推出自己的寄存器Map并不是那么难,我已经通过解析pdf手册中的文本并将其翻译为C源代码多次做到了这一点。
其他提示和技巧可以在这里找到:How to access a hardware register from firmware?
qni6mghb3#
这不是一个好的解决方案。但是,当最后一个语句对您来说太长时,您可以创建一个复杂的宏网络来自动生成该语句。
考虑这个怪物(从https://stackoverflow.com/a/5048661/6082851偷来的):
如果你把它放在一个header中,你可以使用这个更短的方法来设置一个寄存器:
这将导致
编辑:自动添加
_<registername>_
和_POSITION
。