2012-08-09 42 views
3

Misra说,取缔所有的工会。我也知道,只要对它们进行彻底讨论和记录,就可以允许偏差。思考用C工会,关于MISRA

我们有一个微控制器和外部EEPROM存储的统计数据(事件/错误日志,参数设置和诸如此类的东西)。

事件日志包括大约80+事件计数器有些是8,16和32位(所有的无符号)。参数存储由大约200个参数组成,也与8,16和32位值(无符号)混合。

我们重写所有的代码是MISRA兼容和以前定义的这些值如下:

typedef struct 
{ 
    U16BIT eventLogVar1; 
    U32BIT eventLogVar2; 
    U8BIT eventLogVar3; 
    U8BIT eventLogVar4; 
    U32BIT eventLogVar5; 
} EVENT_LOG; 

typedef union 
{ 
    EVENT_LOG log; 
    U8BIT  array[sizeof(EVENT_LOG)]; 
} ELOG; 

ELOG log; 

现在,这是不是真的符合MISRA。参数日志也一样。然而,这是从eeprom读取和写入的最简单的方法,因为我只需读取/写入数组以从eeprom读取/写入。

我们,我们根本不能打破一些其他规则。没有全局(外部)变量(通过头文件)。所有局部变量,如果需要的话只能通过get/set函数来访问。

这意味着,如果我们需要充分写出所有这些参数,这些每个人都应该得到自己的get/set函数用于改变它们在整个应用程序。

上,我已经在想的解决方案是以下几点:

#ifdef EITHER 
enum 
{ 
    eventLogVar1 = 0; /* 00 */ 
    pad01;   /* 01 */ 
    eventLogVar2;  /* 02 */ 
    pad03;   /* 03 */ 
    pad04;   /* 04 */ 
    pad05;   /* 05 */ 
    eventLogVar3;  /* 06 */ 
    eventLogVar4;  /* 07 */ 
    eventLogVar5;  /* 08 */ 
    pad09;   /* 09 */ 
    pad10;   /* 10 */ 
    pad11;   /* 11 */ 
} 
#else /* OR */ 
#define eventLogVar1 0 /* 2 bytes */ 
#define eventLogVar2 2 /* 4 bytes */ 
#define eventLogVar3 6 /* 1 byte */ 
#define eventLogVar4 7 /* 1 byte */ 
#define eventLogVar5 8 /* 4 bytes */ 
#endif 
#define eventLogLastLength 4 

U8BIT eventLog[eventLogVar5 + eventLogLastLength]; 

U8BIT getU8BIT(U8BIT index){} 
U16BIT getU16BIT(U8BIT index){} 
U32BIT getU32BIT(U8BIT index){} 

void setU8BIT(U8BIT index, U8BIT val){} 
void setU16BIT(U8BIT index, U16BIT val){} 
void setU32BIT(U8BIT index, U32BIT val){} 

然而,这造成如果添加或删除值的combersome重构。这也意味着不能使用类型数组的值(并且有少数),如果使用了某种类型的(例如,传感器)或多或少的某些类型的数据,则可以通过定义长度来改变这些值。


你对这个特定问题有什么看法。我/我们是否会更好地记录我们在这个特定情况下与MISRA标准的偏差,并且仅在这个特定地点使用这种偏差还是有更好的解决方案来解决这个问题?

+1

不使用硬件寄存器可以是1-8-16-32位寻址的联合有点傻,但我想我应该从标准委员会那里期待典型的控制器硬件寄存器是一个很好的例子,例如:一个寄存器必须被读写为32位,但实际上是一个令人讨厌的位域集合,一些有用,有些不关心,有些从不写1。 CAN控制器寄存器(汽车中不为人知的东西:)是一个很好的例子。 Soddin'委员会.. – 2012-08-09 15:14:17

+0

@马丁:请记住,工会和比特场是不同的东西。并且C位字段不是用来模拟寄存器或文件格式位字段的好东西 - 就在上周我碰到了一些使用位字段来模拟MPEG标头的代码,并且您知道些什么:x86编译器将它们放在不同的位置比ARM编译器。无论你如何看待工会,避免C位字段。使用明确的转换,掩码和位代替 - 即使你必须将它们隐藏在宏后面。 – 2012-08-13 07:21:14

+0

@马丁 - 我们正在寻找新的志愿者加入,可以随意提供 [R&DFC] – Andrew 2012-11-08 07:34:40

回答

4

你的日志工会正是你应该被允许使用的那种联盟,它是什么做的是数据打包,东西MISRA明确规定是可以接受的偏差,所以偏差是你应该做的。几乎所有使用MISRA的人都以这种方式偏离了这一规则。 (这是一个相当糟糕的规则,它看起来像它会被降级为咨询或在未来MISRA版本完全删除。)

但你需要记录:

  • 是否填充字节和对齐会成为一个问题。
  • 如果代码需要是可移植的,那么是否可能存在endianess问题。

躲闪填充/对齐问题,你可以写这样的:

COMPILE_TIME_ASSERT((sizeof(union_member1)+sizeof(union_member2)+...) == 
        sizeof(union_type)); 

其中COMPILE_TIME_ASSERT是一些宏,如果没有通过积极的值产生一个编译器错误。这确保不存在结构/联合填充。


进一步说明:

枚举是一个坏的解决方案,因为它有许多其自身的缺陷:枚举类型的变量具有实现定义的大小,同时的枚举常数的类型是有符号整数。这将与隐式类型转换的MISRA规则相冲突,您将被迫添加大量类型转换。

所有局部变量,如果需要,只能通过get/set函数来访问。

它们还需要声明为static以减少它们的范围。我从你的片段中注意到你不这样做。 MISRA:2004 8.11执行static

+0

我错误地将它们排除在外。我们所有的文件范围变量都是静态定义的。除了我的例子。我们确实已经选择偏离您的完全相同的提示文件。谢谢回答。 – 2012-08-10 12:24:23

1

是什么MISRA说的memcpy的?为什么不使用EVENT_LOG而不是工会?当您从EEPROM序列化/使用临时数组是这样的:

EVENT_LOG log; 

U8BIT array[sizeof(EVENT_LOG)]; 
// populate array 

memcpy(&log, array, sizeof(EVENT_LOG)); 

// do similar thing when writing to eeprom 

工会混合数据和序列化格式在一起,其中一个功能可能更适合?

void EVENT_LOG_write_to_eeprom(const EVENT_LOG*); 
void EVENT_LOG_read_from_eeprom(EVENT_LOG*); 
+0

合理。我想过了,但是我们缺乏记忆力,只是重写了程序的一半。因此创建额外的数组并不是一个真正的选择,我们不能丢弃大约300字节的堆栈空间。 – 2012-08-09 12:51:34

+0

如果你把序列化放在一个接口(即函数调用)之后,你可以随时将它改为长时间读/写(即分别访问和序列化每个成员)。假设您将始终将EVENT_LOG读取/写入为单个块。我认为这里的“味道”暴露了真正用于序列化的联合数据类型。 – 2012-08-09 13:21:09

+0

存在各种与memcpy()相关的未定义行为 - 谨慎使用!请参阅MISRA C的修订1; 2012 – Andrew 2017-10-13 13:20:50

4

使用正确时,工会是一个非常有用的构造。

他们也有不确定的行为,当一个人试图做聪明的东西与他们。这是为了防止MISRA准则试图阻止的未定义行为。但即使是规则(MISRA C:2004中的18.2)也给出了工会有用的案例。

您给出的例子是有用的情况,这正是MISRA偏差程序存在的情况。


声明:我是MISRA C工作组的成员,但我以个人身份发帖。我的意见不应该被视为官方 MISRA政策。

+2

感谢您向我解释MISRA的官方政策。 ;-) – 2012-11-08 21:04:05