我正在为MSP430F5438A编写一些固件。我希望这段代码主要是MISRA04投诉(我使用的是C99,而不是C90)。我使用的是IAR 5.51,它可以检查MISRA的合规性。
我有以下数据结构:
typedef struct
{
struct
{
uint16_t baud_rate;
uint8_t data_bits;
uint8_t parity;
uint8_t stop_bits;
uint8_t b_flow_control;
} serial_settings;
...
} config_settings_t;
我想在闪存中创建一个可以全局读取的结构实例。我已经有单独的方法写入闪存了。
以下是指向此结构的全局指针的定义:
volatile config_settings_t *gp_app_config = (uint8_t) 0x1800u;
这很好,似乎符合MISRA标准。
现在,我在闪存驱动程序中实现了一组函数,可以写入和读取闪存中的任意段。他们都把uint8_t指针作为参数。
如何调用这样的函数?
flash_segment_erase(uint8_t * p_flash, uint16_t len);
此:
flash_erase_check((uint8_t*)gp_app_config, sizeof(config_settings_t));
编译并且工作正常,但根据MISRA04是禁忌......
Error[Pm141]: a cast should not be performed between a pointer to object type and a different pointer to object type, this casts from type "config_settings_t volatile *" to "uint8_t *" (MISRA C 2004 rule 11.4)
谢谢, 尼克
答案 0 :(得分:2)
考虑使用MISRA-C:2012,因为它支持C99。我不知道IAR是否支持它,MISRA-C:2012于今年春天早些时候发布。
对于MISRA-C:2004,有几件事需要考虑。
1)
声明全局变量是MISRA-C合规性的边界。有两个规则,8.10和8.11,强制文件范围变量为static
“,除非需要外部链接”。在您的情况下是否需要是有点主观的。没有明显的理由说明为什么你需要指针是全局的。
奇怪的是,您将指向闪存的指针声明为读写指针。这没有任何意义。
将地址转换为指向uint8_t的指针是没有意义的,而实际上你需要一个指向config_settings_t的const volatile指针。此外,规则11.5禁止丢弃const或volatile关键字。
因此,我会考虑使用它在模块内声明它是静态的,并使其成为只读。考虑重写这样的代码:
// something.h
const volatile config_settings_t* get_app_config (void);
// use a getter function instead of a global variable
// something.c
static const volatile config_settings_t *
gp_app_config = (const volatile config_settings_t*) 0x1800u;
const volatile config_settings_t* get_app_config (void)
{
return gp_app_config;
}
还考虑将实际指针本身存储在ROM中(是的,它会使该声明更加“邪恶”阅读......):
static const volatile config_settings_t * const
gp_app_config = (const volatile config_settings_t*) 0x1800u;
2)
如何调用这样的函数?
flash_segment_erase(uint8_t * p_flash, uint16_t len);
你不应该。首先,这是NVM编程驱动程序的一部分,因此它将始终处理只读变量。可能没有必要将它们声明为volatile,但在某些编译器上它可以使您免于优化器故障。
C允许野生型演员的所有方式。你的代码的主要问题是你输出了const和volatile。
此外,您将把通用数据编程到闪存中。您的闪存编程驱动程序可能在字节级别上工作,但由于此原因,接口不必是uint8_t*
。将其更改为void*
将节省一天,并使您免于MISRA规则11.2。
您应该将该函数声明为:
void flash_segment_erase (const volatile void* p_flash, uint16_t len);
所以现在你有一个const volatile config_settings_t*
,你想把它传递给一个采用通用const volatile void*
参数的函数。这应该完全兼容MISRA。
3)
请注意,sizeof
会生成size_t
类型的变量,该变量不一定与uint16_t
完全兼容。如果size_t
例如等同于uint32_t
,则会破坏各种MISRA规则。因此,转换为表达式的预期类型:
flash_erase_check (gp_app_config, (uint16_t)sizeof(config_settings_t));
编辑:有各种简单的愚蠢规则,如11.3和11.4,不允许这些转换,也不允许在整数和指针之间转换。由于这些规则将有效地禁止CPU硬件寄存器,NVM编程,引导加载程序,MISRA-C的中断向量表,因此必须忽略它们。否则,MISRA-C不能在现实世界中使用。
显然,这些规则是一些桌面程序员或工具供应商的结果,没有任何嵌入式编程经验,对委员会的影响太大(cough-ldra-cough)。尽管有许多MISRA用户投诉,但同样愚蠢的咨询规则仍然存在于MISRA-C:2012中。