违反 MISRA C 2012 规则 11.4

roc*_*23A 1 c embedded misra keil pc-lint

面临与违反 MISRA C 2012 规则 11.4 相关的问题。使用 PC-Lint Plus 进行规则检查。Keil uVision V5.38.0.0

错误:

conversion between object pointer type 'GPIO_Type *' and integer type 'unsigned int' [MISRA 2012 Rule 11.4, advisory]```
uint *L_uc_byte = (uint *)&GPIO->PIN[0];
Run Code Online (Sandbox Code Playgroud)

以下是GPIO相关的详细信息

#define     __IO    volatile             /*!< Defines 'read / write' permissions */
/** Peripheral GPIO base address */
#define GPIO_BASE                                (0x4008C000u)
/** Peripheral GPIO base pointer */
#define GPIO                                     ((GPIO_Type *)GPIO_BASE)

/** GPIO - Register Layout Typedef */
typedef struct {
  __IO uint8_t B[6][32];                           /**< Byte pin registers for all port 0 and 1 GPIO pins, array offset: 0x0, array step: index*0x20, index2*0x1 */
       uint8_t RESERVED_0[3904];
  __IO uint32_t W[6][32];                          /**< Word pin registers for all port 0 and 1 GPIO pins, array offset: 0x1000, array step: index*0x80, index2*0x4 */
       uint8_t RESERVED_1[3328];
  __IO uint32_t DIR[6];                            /**< Direction registers, array offset: 0x2000, array step: 0x4 */
       uint8_t RESERVED_2[104];
  __IO uint32_t MASK[6];                           /**< Mask register, array offset: 0x2080, array step: 0x4 */
       uint8_t RESERVED_3[104];
  __IO uint32_t PIN[6];                            /**< Port pin register, array offset: 0x2100, array step: 0x4 */
       uint8_t RESERVED_4[104];
  __IO uint32_t MPIN[6];                           /**< Masked port register, array offset: 0x2180, array step: 0x4 */
       uint8_t RESERVED_5[104];
  __IO uint32_t SET[6];                            /**< Write: Set register for port Read: output bits for port, array offset: 0x2200, array step: 0x4 */
       uint8_t RESERVED_6[104];
  __O  uint32_t CLR[6];                            /**< Clear port, array offset: 0x2280, array step: 0x4 */
       uint8_t RESERVED_7[104];
  __O  uint32_t NOT[6];                            /**< Toggle port, array offset: 0x2300, array step: 0x4 */
       uint8_t RESERVED_8[104];
  __O  uint32_t DIRSET[6];                         /**< Set pin direction bits for port, array offset: 0x2380, array step: 0x4 */
       uint8_t RESERVED_9[104];
  __O  uint32_t DIRCLR[6];                         /**< Clear pin direction bits for port, array offset: 0x2400, array step: 0x4 */
       uint8_t RESERVED_10[104];
  __O  uint32_t DIRNOT[6];                         /**< Toggle pin direction bits for port, array offset: 0x2480, array step: 0x4 */
} GPIO_Type;
Run Code Online (Sandbox Code Playgroud)

我尝试过多种方法

  1. conversion between object pointer type 'GPIO_Type *' and integer type 'unsigned int' [MISRA 2012 Rule 11.4, advisory]
    uint data = ( ( GPIO->PIN[0] >> 17U ) & 0x03U );
    
    Run Code Online (Sandbox Code Playgroud)
  2. conversion between object pointer type 'uint *' (aka 'unsigned int *') and integer type 'volatile uint32_t' (aka 'volatile unsigned int') [MISRA 2012 Rule 11.4, advisory]
    volatile uint L_uc_byte = *( uint * )(GPIO->PIN[0]);
    
    Run Code Online (Sandbox Code Playgroud)
  3. conversion between object pointer type 'GPIO_Type *' and integer type 'unsigned int' [MISRA 2012 Rule 11.4, advisory]
    uint L_uc_byte = GPIO->PIN[0];
    
    Run Code Online (Sandbox Code Playgroud)
  4. 使用地址文字作为指针
    conversion between object pointer type 'uint *' (aka 'unsigned int *') and integer type 'unsigned int' [MISRA 2012 Rule 11.4, advisory]
    uint L_uc_byte = *( uint * )( 0x40002000U + 0x2100U);
    
    Run Code Online (Sandbox Code Playgroud)

需要解决方案来解决此违反规则的问题。

Lun*_*din 5

  • 您需要uint从代码库中消除这种奇怪的类型 - 不要发明奇怪的非标准、非自记录的整数类型。使用标准 C uint32_t

  • 请注意,最好避免将volatile限定表达式(例如读取/写入寄存器)与其他操作数或子表达式混合(以及另一个 MISRA 违规)。这不利于性能目的和清晰度,而且额外的副作用可能会产生不可预测的结果。尝试使寄存器访问表达式仅执行写入或读取操作,而不执行其他操作。

  • 除非您对 C 有深入的了解,否则切勿执行指针转换。将整数转换为不合格的 auint*永远volatile不会正确。特别是,如果您收到警告,它几乎肯定不是正确的路线。

  • 不应使用诸如此类的“魔术数字” x >> 17 & 42,因为它们会使代码难以阅读,并迫使读者不断地埋头于 MCU 手册的寄存器描述中。相反,使用有意义的名称。如果您使用 MCU 供应商提供的寄存器映射,它通常附带您可以使用的命名常量。

详细信息此处:如何从固件访问硬件寄存器?

此外,MISRA-C 有一些过于迂腐的建议规则,并不真正适用于嵌入式系统。其中一条规则禁止整数和指针之间的转换,但如果不执行此类转换,就无法定义寄存器映射,因此必须忽略该规则。

MISRA 的关键是首先了解他们制定该规则的原因。这种情况下的主要问题是对齐,这uint32_t* data = (uint32_t*)0x0003;会导致指针未对齐。如果您了解为什么这是一个严重的错误,那么您就会了解 MISRA 制定该规则的原因。鉴于您不会提供任何类似的自制地址,因此您可以安全地忽略该规则。

标准做法是,您无需正式偏离即可跳过咨询规则。尽管理想情况下,基于 MISRA 的编码标准应列出所有可以忽略的建议规则,然后应在静态分析器/MISRA 检查器中相应地禁用它们。

结论:

uint data = ( ( GPIO->PIN[0] >> 17U ) & 0x03U );uint32_t data将常量更改为有意义的名称。理想情况下,为了符合 MISRA 要求,您还应该将其分成几个表达式,这有点过分热心,但应该 100% MISRA 兼容:

uint32_t data = GPIO->PIN[0]; // volatile expression as a stand-alone read/write

// just making up some names here, refer to the MCU manual for proper names:
data = (data >> GPIO_POS ) & GPIO_MASK; 
Run Code Online (Sandbox Code Playgroud)

如果我们应该忽略整数到指针的咨询规则:

#define SOME_REGISTER (*(volatile uint32_t*)(0x40002000U + 0x2100U))
...
uint32_t L_uc_byte = SOME_REGISTER;
Run Code Online (Sandbox Code Playgroud)