freeRTOS内核学习笔记(1)-编程标准
在开始具体的学习之前,你应该先了解freeRTOS的编程标准.这能够方便你在接下来的阅读中快速的了解一些内容
的基本信息,并方便记忆.此外,良好的编程风格也是工作效率的保障.
你可以在https://www.freertos.org/FreeRTOS-Coding-Standard-and-Style-Guide.html中找到英文的原文信息.
尽管此前有人翻译过这份文档,但发布时间在很早以前,一些标准已经发生了改变.这里按照此前翻译的思路根据官方文档进行了更新和修订.
编程标准(Coding Standard)
FreeRTOS代码遵循MISRA(Motor Industry Software Reliability Association)标准,但也有些区别.具体来说:
- 为了效率,有两个API函数拥有超过一个的出口点.
- 使用了标准的C数据类型,而不是用
typedef
将其重定义. - 在创建任务时,代码会直接处理堆栈的栈顶和栈底地址.由于不同平台的总线宽度并不相同,这就需要不可避免地对指针变量进行算数运算.
-
trace
宏的定义在默认情况为空,所以不会产生任何代码.因此,MISRA的遵从检查其实是用假的宏定义来运行的.
此外,除了在stdint.h
中,freeRTOS没有使用任何C99标准中的语法和特性.在FreeRTOS/Source/include
目录下有一个名为stdint.readme
的文件,如果你想要使用stdint的数据类型的话,将它重命名为stdint.h
.
命名约定(Naming Conventions)
在freeRTOS的内核和示例中,按照以下约定命名:
- 变量名
-
uint32_t
数据类型的变量的变量名以 ul 为前缀. -
uint16_t
数据类型的变量的变量名以 us 为前缀. -
uint8_t
数据类型的变量的变量名以 uc 为前缀. - 非stdint数据类型的变量的变量名以 x 为前缀.例如,
BaseType_t
和TickType_t
. - 无符号的非stdint数据类型的变量的变量名再添加一个额外的前缀 u .例如,
UBaseType_t
(unsigned BaseType_t)数据类型的变量的变量名以 ux x为前缀. -
size_t
数据类型的变量的变量名也以 x 为前缀. - 指针变量拥有一个额外的前缀 p.例如,
uint16_t
的指针的变量名以 pus为前缀. - 按照MISRA的标准,
char
数据类型只允许储存ASCII字符,以 c 为前缀.char
的指针只允许指向ASCII字符串,以 pc 为前缀.
-
- 函数名
– 文件的内部函数以prv为前缀.
– API函数以返回值类型为前缀,参见前面变量名的命名约定(额外添加了v作为void
返回值的前缀).例如,vTaskDelete
代表这个函数的返回值类型为void
.
– 函数名以所在文件名为开头.例如,vTaskDelete
函数在Task.c
中定义. - 宏
– 宏用所在文件名的一部分以小写作为前缀.例如,configUSE_PRESSMPTION
在FreeRTOSConfig.h
中定义.
– 宏的其余部分用大写字母,用下划线分割.
数据类型
除了以下例外,只使用了stdint和freeRTOS自己定义的数据类型:
- 按照MISRA的标准,
char
数据类型只允许储存ASCII字符. - 按照MISRA的标准,
char
的指针只指向ASCII字符串.
为每种接口,定义了4种数据类型:
- TickType_t
如果configUSE_16_BIT_TICKS
是真值,那么TickType_t
为16-bit.否则则是32-bit. - BaseType_t
该数据类型定义了一个平台下效率最高的原生数据类型.例如,在32位的平台下,BaseType_t
将被定义为32-bit的数据类型.
此外,如果BaseType_t
被定义为了char
,必须注意一定要是有符号的数据类型.因为在作为函数的返回值时,需要使用负值来表示错误. - UBaseType_t
无符号的UBaseType_t
. - StackType_t
用于在栈中储存数据.尽管存在例外,但通常,在16-bit平台下该数据类型为16-bit,而在32-bit平台下为32-bit.
编程风格
- 缩进
使用Tab缩进,一个Tab等于4个空格. - 注释
注释不能超过80行,除非用来描述一个参数.
不使用C++风格的注释(//). - 布局
布局和格式按照以下代码示例.
/* Library includes come first… */
#include <stdlib.h>
/* …followed by FreeRTOS includes… */
#include “FreeRTOS.h”
/* …followed by other includes. */
#include “HardwareSpecifics.h”
/* #defines comes next, bracketed where possible. */
#define A_DEFINITION ( 1 )
/*
* Static (file private) function prototypes appear next, with comments
* in this style – with each line starting with a ‘*’.
*/
static void prvAFunction( uint32_t ulParameter );
/* File scope variables are the last thing before the function definitions.
Comments for variables are in this style (without each line starting with
a ‘*’). */
static BaseType_t xMyVariable;
/* The following separate is used after the closing bracket of each function,
with a blank line following before the start of the next function definition. */
/*———————————————————–*/
void vAFunction( void )
{
/* Function definition goes here – note the separator after the closing
curly bracket. */
}
/*———————————————————–*/
static UBaseType_t prvNextFunction( void )
{
/* Function definition goes here. */
}
/*———————————————————–*/
File Layout
/* Function names are always written on a single line, including the return
type. As always, there is no space before the opening parenthesis. There
is a space after an opening parenthesis. There is a space before a closing
parenthesis. There is a space after each comma. Parameters are given
verbose, descriptive names (unlike this example!). The opening and closing
curly brackets appear on their own lines, lined up underneath each other. */
void vAnExampleFunction( long lParameter1, unsigned short usParameter2 )
{
/* Variable declarations are not indented. */
uint8_t ucByte;
/* Code is indented. Curly brackets are always on their own lines
and lined up underneath each other. */
for( ucByte = 0U; ucByte < fileBUFFER_LENGTH; ucByte++ )
{
/* Indent again. */
}
}
/* For, while, do and if constructs follow a similar pattern. There is no
space before the opening parenthesis. There is a space after an opening
parenthesis. There is a space before a closing parenthesis. There is a
space after each semicolon (if there are any). There are spaces before and
after each operator. No reliance is placed on operator precedence –
parenthesis are always used to make precedence explicit. Magic numbers,
other than zero, are always replaced with a constant or #defined constant.
The opening and closing curly brackets appear on their own lines. */
for( ucByte = 0U; ucByte < fileBUFFER_LENGTH; ucByte++ )
{
}
while( ucByte < fileBUFFER_LENGTH )
{
}
/* There must be no reliance on operator precedence – every condition in a
multi-condition decision must uniquely be bracketed, as must all
sub-expressions. */
if( ( ucByte < fileBUFFER_LENGTH ) && ( ucByte != 0U ) )
{
/* Example of no reliance on operator precedence! */
ulResult = ( ( ulValue1 + ulValue2 ) – ulValue3 ) * ulValue4;
}
/* Conditional compilations are laid out and indented as per any
other code. */
#if( configUSE_TRACE_FACILITY == 1 )
{
/* Add a counter into the TCB for tracing only. */
pxNewTCB->uxTCBNumber = uxTaskNumber;
}
#endif
A space is placed after an opening square bracket, and before a closing
square bracket.
ucBuffer[ 0 ] = 0U;
ucBuffer[ fileBUFFER_LENGTH – 1U ] = 0U;
Formatting of C Constructs