Codeing Standard
freeRTOS的源代码符合MISRA编码标准,这个编码标准老长了,而且想要得到这玩意还有向MISRA付一点点小钱,所以下面只复制了一小部分。
脱离MISRA标准的部分:
- 两个API有多余一个出口。在这两种情况下,出于对临界效率的原因,同标准的偏差是允许的
- 当在创建任务的时候,源代码在为任务创建堆栈的时候,需要操作内存去定位起始与结束地址,因为源代码需要在所有支持的架构上工作,这些架构有8、16、24、32-。当这些指针运算使用时,运算结果由源代码编程检查正确性
- 追踪宏在默认下是空的,所以不要生成任何代码。因此,MISRA符合性检查在虚拟宏定义下进行
freeRTOS构建于多种不同的编译器,其中的一些比其他的先进。因此,freeRTOS不使用任何已经或自C99标准引入到C语言中的功能或语法。在使用stdin.h
这个头文件时是个例外,在FreeRTOS/Source/include
路径下由一个文件名为stdint.readme
的文件,可以重命名这个文件为stdin.h
,以提供必要的编译FreeRTOS的stdint
类型定义,前提是要你的编译器不提供自己的。
命名约定
RTOS内核与实例代码遵循如下约定:
变量
uint32_t
类型的变量,命名前缀ul
,u
代表usigned
(下同),l
代表long
。uint16_t
类型的变量,命名前缀us
,s
代表short
。uint8_t
类型的变量,命名前缀uc
,c
代表char
。- 对于非
stdint
类型的变量前缀为x
,比如BaseType_t
和TickType_t
,分别是用于为不同架构定义的基础数据类型以方便移植,和用于保存RTOS tick计数值的变量类型。同理,对于无符号的类型,例如UBaseType_t
前缀为ux
。 size_t
类型的变量,命名前缀x
- 枚举类型变量前缀为
e
- 对于指针拥有而外的前缀
p
,例如一个uint16_t
类型的指针变量,前缀为pus
- 与MISRA标准一致,
char
型变量只允许用于ASCII码,并且前缀为c
- 同理
char *
指针只允许指向ASCII码字符,前缀为pc
函数
- 文件范围的静态函数(
static
限定)前缀prv
- API函数的前缀由它们的返回值类型决定,具体参照变量的命名约定。同时,对于
void
前缀为v
- API函数的名字除根据返回值类型确定的前缀外,名称由其所在的文件名作为开始。例如
vTaskDelete
,代表其返回值类型为void
,这个API函数保存在task.c
文件中
宏
- 同API函数类似,宏的开头是定义它的文件名,前缀统一使用小写。比如,
configUSE_PREEMPTION
就是一个定义于freeRTOSConfig.h
文件的宏 - 除了前缀,宏的其他部分统一使用大写,并且每个单词之间由下划线分隔
数据类型
仅stdint.h
中的类型和RTOS自身的typedef定义的数据类型被使用,当然,有例外:
char
char *
>
In line with MISRA guides, unqualified character pointers are permitted, but only when they are used to point to ASCII strings. This removes the need to suppress benign compiler warnings when standard library functions that expect char * parameters are used, especially considering some compilers default unqualified char types to be signed while other compilers default unqualified char types to be unsigned.
在不同的分支下不同的数据类型定义由如下四种:
TickType_t
如果configUSE_16_BIT_TICKS
被定义为非零值,则TickType_t被定义为无符号的16位整型变量,否则被定义位32位无符号整型变量。对于32位的架构应该将configUSE_16_BIT_TICKS
始终设置为0BaseType_t
这是用来定义最高效的、自然的类型对于不同的架构。比如,对于32位的架构,BaseType_t
被定义位32-bit。如果BaseType_t
被定义位char类型,此时需要特别注意保证当signed char被当作返回值时值为负的时候代表的是一个错误情况UBaseType_t
无符号的BaseType_t
,此时被定义为char类型,不需要特别关注为负的情况StackType_t
定义堆栈保存数据的数据类型,通常情况下就是架构的位数。当然也有例外,比如freeRTOS内部使用的堆栈
代码分割
- 缩进:使用
Tab
缩进,1 tab == 4 spaces
- 注释:注释的长度不会超过80列,除非紧接着变量、描述。不使用C++的双斜线(
//
)进行注释。译者注:防止代码格式错乱导致源代码被注释 - 布局:目的是尽可能的方便书写与阅读,以下是简单的示例:
文件布局
/* 首先是库文件.. */
#include <stdlib.h>
/* ...紧接着是freeRTOS自身的头文件包含... */
#include "FreeRTOS.h"
/* ...其他头文件. */
#include "HardwareSpecifics.h"
/* #defines 宏定义,尽可能的使用括号,防止边界效应. */
#define A_DEFINITION ( 1 )
/*
* static限定的文件作用域函数,这部分的注释风格是每行
* 都是以‘*’开头.
*/
static void prvAFunction( uint32_t ulParameter );
/* 文件作用域变量,注释风格与文件作用域函数不同,每行
不在以‘*’开头*/
static BaseType_t xMyVariable.
/* 下面的这个分割行应用于所有的函数之后,同时紧接着一个空白行,然后
才是下一个函数 */
/*-----------------------------------------------------------*/
void vAFunction( void )
{
/* 函数定义放在这,注意将花括号闭合 */
}
/*-----------------------------------------------------------*/
static UBaseType_t prvNextFunction( void )
{
/* 函数定义. */
}
/*-----------------------------------------------------------*/
C文件格式
/* 函数名称总是写在一行,包括返回值类型。 左括号之前没有空格,左括号之后
有空格,每个逗号后有空格。参数必须详尽给出,描述性的名称,自解释。如果括号
不在同一行,每行排开,列对齐*/
void vAnExampleFunction( long lParameter1, unsigned short usParameter2 )
{
/* 变量声明不需要缩进 */
uint8_t ucByte;
/* 代码缩进,花括号独占一行,列对齐*/
for( ucByte = 0U; ucByte < fileBUFFER_LENGTH; ucByte++ )
{
/* 再次缩进. */
}
}
/* for while do 和 if语句有相同的模式,左括号后有空格,右括号前有空隔,
每个分号后有空格,操作符的优先级显式表现出来,不要依赖默认优先级*/
for( ucByte = 0U; ucByte < fileBUFFER_LENGTH; ucByte++ )
{
}
while( ucByte < fileBUFFER_LENGTH )
{
}
/* 不要依靠操作符优先级,每个条件用括号包含 */
if( ( ucByte < fileBUFFER_LENGTH ) && ( ucByte != 0U ) )
{
/* 不依赖操作符优先级的例子 */
ulResult = ( ( ulValue1 + ulValue2 ) - ulValue3 ) * ulValue4;
}
/* 条件编译列出来,并按照代码缩进风格*/
#if( configUSE_TRACE_FACILITY == 1 )
{
/* 添加一个用于跟踪的计数器到TCB. */
pxNewTCB->uxTCBNumber = uxTaskNumber;
}
#endif
/*左方括号后空格,右方括号前空格*/
ucBuffer[ 0 ] = 0U;
ucBuffer[ fileBUFFER_LENGTH - 1U ] = 0U;