我正在为 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)
谢谢, 尼克
最佳答案
考虑使用 MISRA-C:2012,因为它支持 C99。我不知道 IAR 是否支持它,MISRA-C:2012 已于今年 Spring 早些时候发布。
对于 MISRA-C:2004,有几件事需要考虑。
1)
声明全局变量接近 MISRA-C 合规性。有两条规则,8.10 和 8.11,强制文件范围变量为
静态
“除非需要外部链接”。在你的情况下是否需要是有点主观的。没有明显的理由说明为什么您需要该指针是全局的。你将闪存的指针声明为读写指针,这很奇怪。这没有任何意义。
将地址转换为指向 uint8_t 的指针是没有意义的,而您实际上想要一个指向 config_settings_t 的 const volatile 指针。此外,规则 11.5 禁止丢弃 const 或 volatile 关键字。
因此,我会考虑在使用它的模块内将其声明为静态,并使其只读。考虑像这样重写代码:
//某事.h
const volatile config_settings_t* get_app_config (void);
// use a getter function instead of a global variable
//某事.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)
How can I call a function like this?
flash_segment_erase(uint8_t * p_flash, uint16_t len);
你不应该。首先,这是 NVM 编程驱动程序的一部分,因此它将始终处理只读变量。将它们声明为 volatile 可能没有必要,但在某些编译器上它可以使您避免优化器事故。
C 允许各种方式的野生型强制转换。您的代码的主要问题是您抛弃了 const 和 volatile。
此外,您还将把通用数据编程到闪存中。您的闪存编程驱动程序可能在字节级别工作,但接口(interface)不必是 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,它们不允许像这样的强制转换,也不允许在整数和指针之间进行强制转换。由于这些规则将有效地禁止来自 MISRA-C 的 CPU 硬件寄存器、NVM 编程、引导加载程序、中断 vector 表,因此必须忽略它们。否则,MISRA-C 无法在现实世界中使用。
显然,这些规则是一些桌面程序员或工具供应商的结果,没有任何嵌入式编程经验,对委员会的影响太大了(咳-ldra-咳)。尽管有许多 MISRA 用户提示,但 MISRA-C:2012 中仍然存在同样愚蠢的建议规则。
关于c - MISRA04 将 typedef 结构指针转换为 int 指针,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/16346623/