2

我正在为 MSP430F5438A 编写一些固件。我希望这段代码主要是 MISRA04 投诉(我使用的是 C99,而不是 C90)。我正在使用可以检查 MISRA 合规性的 IAR 5.51。

我有以下数据结构:

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) 

谢谢,尼克

4

1 回答 1

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 关键字。

    因此,我会考虑在使用它的模块内将其声明为静态,并将其设为只读。考虑像这样重写代码:

// 某事.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)

我怎样才能调用这样的函数?

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 中仍然存在同样愚蠢的咨询规则。

于 2013-05-03T14:28:04.377 回答