如何配置没有linter消息的微控制器寄存器(嵌入式C)

vsikbqxv  于 2023-06-21  发布在  其他
关注(0)|答案(3)|浏览(101)

我喜欢根据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
bvn4nwqk

bvn4nwqk1#

我如何配置微控制器寄存器没有linter消息
使用选项As above + casting the constants to get rid of the linter message

ogq8wdun

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?

qni6mghb

qni6mghb3#

这不是一个好的解决方案。但是,当最后一个语句对您来说太长时,您可以创建一个复杂的宏网络来自动生成该语句。
考虑这个怪物(从https://stackoverflow.com/a/5048661/6082851偷来的):

//Multiple macros to create a single statement to set a SFR
//v is the name of the SFT, a the Position in the register and b the value to set
#define CREATE_STATEMENT_1(v,a,b)  ( (b) << (uint32_t) ( _ ## v ## _ ## a ## _POSITION )  ) 

#define CREATE_STATEMENT_2(v,a,b,c,d) CREATE_STATEMENT_1(v,a,b) | CREATE_STATEMENT_1(v,c,d)
#define CREATE_STATEMENT_3(v,a,b,...) CREATE_STATEMENT_1(v,a,b) | CREATE_STATEMENT_2(v,__VA_ARGS__)
#define CREATE_STATEMENT_4(v,a,b,...) CREATE_STATEMENT_1(v,a,b) | CREATE_STATEMENT_3(v,__VA_ARGS__)
#define CREATE_STATEMENT_5(v,a,b,...) CREATE_STATEMENT_1(v,a,b) | CREATE_STATEMENT_4(v,__VA_ARGS__)
#define CREATE_STATEMENT_6(v,a,b,...) CREATE_STATEMENT_1(v,a,b) | CREATE_STATEMENT_5(v,__VA_ARGS__)
#define CREATE_STATEMENT_7(v,a,b,...) CREATE_STATEMENT_1(v,a,b) | CREATE_STATEMENT_6(v,__VA_ARGS__)
#define CREATE_STATEMENT_8(v,a,b,...) CREATE_STATEMENT_1(v,a,b) | CREATE_STATEMENT_7(v,__VA_ARGS__)

//MACRO_SELECTOR(...) evaluates to the literal number of the passed-in arguments divided by 2 
//(because we need 2 parameters per macro).
#define MACRO_SELECTOR_N1(X,_8,_8_1,_7,_7_1,_6,_6_1,_5,_5_1,_4,_4_1,_3,_3_1,_2,_2_1,_1,_1_1,N,...) N
#define MACRO_SELECTOR(...) MACRO_SELECTOR_N1(0, __VA_ARGS__ ,8,9,7,7,6,6,5,5,4,4,3,3,2,2,1,1,0,0)

#define CREATE_STATEMENT_N2(V,N,...)  CREATE_STATEMENT_ ## N(V,__VA_ARGS__)
#define CREATE_STATEMENT_N1(V,N,...)  CREATE_STATEMENT_N2(V,N,__VA_ARGS__)
//Create a single statement to set a SFR.
//V is the name of the register, after that is a list of position in that register and after that,
// the value it should be set to.
#define CREATE_STATEMENT(V,...)       V=CREATE_STATEMENT_N1(V,MACRO_SELECTOR(__VA_ARGS__),__VA_ARGS__);

如果你把它放在一个header中,你可以使用这个更短的方法来设置一个寄存器:

CREATE_STATEMENT
  (
    ADC0TIME,
    SAMC,     25U,
    ADCDIV,   50U,
    BCHEN,     0U,
    SELRES,    2U,
    ADCEIS,    6U
  );

这将导致

ADC0TIME=( (25U) << (uint32_t) (_ADC0TIME_SAMC_POSITION) ) | ( (50U) << (uint32_t) (_ADC0TIME_ADCDIV_POSITION) ) | ( (0U) << (uint32_t) (_ADC0TIME_BCHEN_POSITION) ) | ( (2U) << (uint32_t) (_ADC0TIME_SELRES_POSITION) ) | ( (6U) << (uint32_t) (_ADC0TIME_ADCEIS_POSITION) );

编辑:自动添加_<registername>__POSITION

相关问题