我面临MISRA C 2012违规,我无法理解。以下是代码:
#define I2C_CCRH_FS ((uint8_t)0x80)
#define I2C_CCRH_DUTY ((uint8_t)0x40)
#define I2C_CCRH_CCR ((uint8_t)0x0F)
typedef struct I2C_struct
{
volatile uint8_t CR1;
volatile uint8_t CR2;
volatile uint8_t CCRL;
volatile uint8_t CCRH;
} I2C_TypeDef;
#define I2C_BaseAddress 0x5210
#define I2C ((I2C_TypeDef *) I2C_BaseAddress)
I2C->CCRH &= ~(uint8_t)((I2C_CCRH_FS | I2C_CCRH_DUTY) | I2C_CCRH_CCR);
在之前的代码中,PC-Lint抱怨说:
Unpermitted operand to operator '|' [MISRA 2012 Rule 10.1, required]
Mismatched essential type categories for binary operand [MISRA 2012 Rule 10.4, required]
规则10.1规定ORing unsigned int
应该没有问题。 ( PC-Lint通过第一次OR操作并抱怨第二次操作!! )
规则10.4规定操作的操作数应具有相同的基本类型。
虽然所有操作数都声明为uint8_t
,但我无法理解为什么存在这些违规行为?
我试过在每两个ORed常量周围加上括号。我还尝试将所有这些广告投放到uint8_t
和volatile uint8_t
。未解决违规行为。
答案 0 :(得分:2)
执行按位操作(I2C_CCRH_FS | I2C_CCRH_DUTY)
时,结果将提升为整数。请参阅整数提升规则Here
因此,上述操作的结果与下一个按位OR | I2C_CCRH_CCR
要解决此问题,您需要在BOTH按位OR运算的结果中添加强制转换。
首先需要将~
运算符的结果从int
转换回unsigned
I2C->CCRH &= (uint8_t)~(uint8_t)((uint8_t)(I2C_CCRH_FS | I2C_CCRH_DUTY) | I2C_CCRH_CCR);
解释
I2C->CCRH &= (uint8_t)~ // Required to cast result of ~ operator to uint8_t
(uint8_t) // Casts the result of the 2nd OR to uint8_t
((uint8_t) // Casts the result of the 1st OR
(I2C_CCRH_FS | I2C_CCRH_DUTY) // 1st OR
| I2C_CCRH_CCR); // 2nd OR
答案 1 :(得分:2)
I2C_CCRH_FS | I2C_CCRH_DUTY
本身符合MISRA标准。两个操作数基本上都是无符号的,因此子表达式很好。但是,仍然会将每个操作数隐式转换为int
。实践中的结果是int
类型。
在伪代码中:当您执行(result as int) | I2C_CCRH_CCR
时,隐式促销之前的操作数具有类型int | uint8_t
。 uint8_t
此处也会将整数提升为int
。你有不同签名的操作数。
(我猜这个工具抱怨10.4,因为整数提升是通常的算术转换的一部分,这就是10.4的内容。)
这整个表达在练习中不会引起任何问题,因此警告主要是迂腐的。但是想象一下,如果你完成~(I2C_CCRH_FS | I2C_CCRH_DUTY) | I2C_CCRH_CCR)
没有演员阵容 - 你最终会得到一个负数,就像0xFFFFFFxx
中用2的补码表示的那样。这可能是危险的。
要解决此问题,您有两种选择:
另请注意,~
运算符不应与带符号的操作数一起使用!这违反了规则10.1。回到uint8_t
的演员应该在最后完成。
<强> TL; DR。如何使代码符合MISRA标准:
你要么做得像这样半糟糕的事情:
I2C->CCRH &= (uint8_t) ~ (uint8_t) ((uint8_t)(I2C_CCRH_FS | I2C_CCRH_DUTY) | I2C_CCRH_CCR)
这有点乱。我宁愿提前投。假设32位CPU:
I2C->CCRH &= (uint8_t) ~( (uint32_t)I2C_CCRH_FS | // comment explaining FS
(uint32_t)I2C_CCRH_DUTY) | // comment explaining DUTY
(uint32_t)I2C_CCRH_CCR ); // comment explaining CCR
上述样式在处理MCU寄存器等时非常有用。这段代码是可以接受的,但可以进一步简化。
如果可以将定义更改为#define I2C_CCRH_FS 0x80u
,那么您将获得:
I2C->CCRH &= (uint8_t) ~(I2C_CCRH_FS | I2C_CCRH_DUTY | I2C_CCRH_CCR);
并且它仍然符合MISRA标准,因为MISRA喜欢的后缀很小u
。