01. FreeRTOS编码标准
2 R5 ^5 P5 a. ]+ z2 n) t8 n% RFreeRTOS核心的源代码遵从MISRA编码标准指南。这个标准篇幅稍长,大家可以在MISRA官方网站找到对应的文档。: p0 m7 L" @! G. d. D. m8 B/ L3 X
) L0 z/ \: @0 X4 h
FreeRTOS源代码不符合MISRA标准的项目如下所示:. r4 J+ d. v, E' H1 P5 F0 O
7 K& {" P: w: v" S
1.有两个API函数有多个返回点。MISRA编码标准强制规定:一个函数在其结尾应该有单一的返回点。: [+ p4 q9 c1 _0 w6 E- Y x0 n$ ]% [
]6 y4 U/ A* d/ R, P" L+ b
2.指针算数运算,在创建任务时,为了兼容8、16、20、24、32位总线,不可避免的使用了指针算数运算。MISRA编码标准强制规定:指针的算术运算只能用在指向数组或数组元素的指针上。
1 z: b3 \$ U# y* t" j+ @
8 {8 T+ N' r! A3.默认情况下,跟踪宏为空语句,因此不符合MISRA的规定。MISRA编码标准强制规定:预处理指令在句法上应该是有意义的。( X j& P+ S) a1 K! }
2 M. \; `" \2 h. D) Z: U7 mFreeRTOS可以在很多不同编译器中编译,其中的一些编译器比同类有更高级特性。因为这个原因,FreeRTOS不使用任何非C语言标准的特性或语法。一个例外情况是头文件stdint.h。在文件夹FreeRTOS/include下包含一个叫做stdint.readme的文件,如果你的编译器不提供stdint类型定义,可以将stdint.readme文件重命名为stdint.h。( [ K9 W3 @) _/ _
3 r5 `7 o+ F3 [% x6 C& N- {/ z
02. FreeRTOS变量命名规则0 e# A/ b# J7 h4 c( D
uint32_t类型的变量使用前缀ul,这里’u’表示’unsigned’,’l’表示’long’+ p! U/ M- i4 p
uint16_t类型的变量使用前缀us,这里’u’表示’unsigned’,’s’表示’short’
; E5 H. D1 q6 H [2 v' e- D1 w3 w* ^uint8_t类型的变量使用前缀uc,这里’u’表示’unsigned’,’c’表示’char’
; |* c1 ?' w5 W3 I9 D5 _, j非stdint类型的变量使用前缀x,比如基本的Type_t和TickType_t类型,这些类型在移植层定义,定义成符合处理器架构的最高效类型;
: {- _; N: {! k8 p' N. J5 q非stdint类型的无符号变量使用前缀ux,比如UbaseType_t(unsigned BaseType_t)- j; Z5 o0 q0 r! c; f9 s1 D
size_t类型的变量使用前缀x;
3 P7 b) i, f" j2 @3 t" H枚举类型变量使用前缀e
' N% h2 r8 ^1 Q% ^8 g指针类型变量在类型基础上附加前缀p,比如指向uint16_t的指针变量前缀为pus6 l( Y2 g- k6 N+ X* Q1 o8 ^# F
与MISRA指南一致,char类型变量仅被允许保存ASCII字符,前缀为c
/ y% s0 `% Z$ i8 p7 i+ Y与MISRA指南一致,char *类型变量仅允许指向ASCII字符串,前缀为pc
5 Y! }9 g9 ?9 D8 e03. FreeRTOS函数命名规则
: t, u, I" k. B, t# O: {在文件作用域范围的函数前缀为prv. |3 h& Q" L' ?: I a
API函数的前缀为它们的返回类型,当返回为空时,前缀为v) `8 k2 ], R6 k# ~/ W
API函数名字起始部分为该函数所在的文件名。比如vTaskDelete函数定义在tasks.c,并且该函数返回空。1 J7 U8 D1 ^. N2 b$ n1 H
5 K1 G I4 y" d1 X9 q& ^$ M
04. FreeRTOS宏命名规则( s& i9 f1 j n1 [6 X4 y4 M
宏的名字起始部分为该宏定义所在的文件名的一部分。比如configUSE_PREEMPTION定义在FreeRTOSConfig.h文件中。4 C' I$ ]& P p
除了前缀,宏剩下的字母全部为大写,两个单词间用下划线(’_’)隔开。0 [ c2 l9 J# R8 b2 _
* q, ]* ~5 C' @. o6 }05. FreeRTOS数据类型
0 M$ x! B' {' u& G, ]! Y只有stdint.h和RTOS自己定义的数据类型可以使用,但也有例外情况,如下所示:
4 V0 i$ X9 w4 s% k- z- t, X7 A4 l% u* k, ochar:与MISRA编码标准指南一致,char类型变量仅被允许保存ASCII字符: d3 h V+ G" H- G2 I
char *:与MISRA编码标准指南一致,char *类型变量仅允许指向ASCII字符串。当标准库函数期望一个char *参数时,这样做可以消除一些编译器警告;特别是考虑到有些编译器将char类型当做signed类型,还有些编译器将char类型当做unsigned类型。
K, E7 y z% I
/ V) t$ \! o+ \0 t有三种类型会在移植层定义,它们是:
# B$ C% h) K2 T/ q( y5 D S+ YTickType_t:如果configUSE_16_BIT_TICKS为非零(条件为真),TickType_t定义为无符号16位类型。如果configUSE_16_BIT_TICKS为零(条件为假),TickType_t定义为无符号32位类型。注:32位架构的微处理器应设置configUSE_16_BIT_TICKS为零。
* o7 X( E) {% T" y* ? H5 K, a ~BaseType_t:定义为微处理器架构效率最高的数据类型。比如,在32位架构处理器上,BaseType_t应该定义为32位类型。在16位架构处理器上,BaseType_t应该定义为16位类型。如果BaseType_t定义为char,对于函数返回值一定要确保使用的是signed char,否则可能造成负数错误。6 m; k" o2 P9 N5 \, z
UbaseType_t:这是一个无符号BaseType_t类型$ B! w) k; h' t
! D d3 _! I. D# G06. 其它' l' d( V" x+ U1 U7 C
风格说明+ O3 c0 j( |- k2 R- q/ W3 ?" ]2 d% ^
1.缩进:缩进使用制表符,一个制表符等于4个空格。, A7 X, W( Y# v) c
2.注释:注释单行不超过80列,特殊情况除外。不使用C++风格的双斜线(//)注释- j6 m4 J k# u4 E
3.布局:FreeRTOS的源代码被设计成尽可能的易于查看和阅读。下面的代码片中,第一部分展示文件布局,第二部分展示C代码设计格式。
* i& G( Z+ m/ ~0 U# E b8 k: v' K. g% c
示例, q, @- _) T- G; {
( F7 I: d- T, x% ~& \7 z# h6 O) S# K( N- /* 首先在这里包含库文件... */
! H$ q% s. N) r5 @ - #include <stdlib.h> , C5 T( G% V3 F, C6 E1 V
4 d" D; V* O3 J3 E& s- ^3 G; P4 k- /* 然后是FreeRTOS的头文件... */
: B" `0 b4 ?9 `- B2 V7 B* y" K9 ?) Z - #include "FreeRTOS.h"
; |3 k/ T5 f& z5 s/ @& e - % K: Q& K3 F' A- l+ y
- /* 紧接着包含其它头文件. */
+ n! \/ L# R, C2 R6 l2 ]9 N4 M - #include "HardwareSpecifics.h"
3 L1 Y2 x$ B( P8 |
1 f5 \( M! h- J% e' z- /* 随后是#defines, 在合理的位置添加括号. */
/ y3 t3 n! {0 x4 a - #define A_DEFINITION ( 1 ) 6 [ m2 K# x2 Z9 O4 B
8 Q) q3 ]; u) W/ I T- /*
( W" b3 }* B# I1 `; r - * 随后是Static (文件内部的)函数原型, 5 _7 S: Y' a6 c
- * 如果注释有多行,参照本条注释风格---每一行都以’*’起始. 7 J a1 E2 l7 e1 G1 I
- */
/ a0 b5 q: ^% {2 E6 d; `4 g - static void prvAFunction( uint32_t ulParameter ); ; _. O% ~8 b" n6 `% r+ X
- . n2 @- `5 f8 q, |5 f7 [, u
- /* 文件作用域变量(本文件内部使用)紧随其后,要在函数体定义之前. */ + U. e2 {# r2 d+ G) k. Z
- static BaseType_t xMyVariable.
# \4 F2 J: o( g* M) p3 ]2 B. i - % A/ C% j7 C/ C
- /* 每一个函数的结束都有一行破折号,破折号与下面的第一个函数之间留一行空白。*/ 4 d% i: R1 \1 B0 l
- & T$ v2 a0 ]; S$ j
- /*-----------------------------------------------------------*/
- }0 _' ]3 s3 L T - / e+ K+ }; q1 S+ j6 h
- void vAFunction( void )
% e* Z! {1 K/ x2 R1 j9 | O - {
; Z; J9 c3 B4 T& x4 P0 v) k, s - /* 函数体在此定义,注意要用大括号括住 */
. w6 h: T6 V8 [; u - }
$ @* ]5 g/ M1 m) O: L7 D - /*-----------------------------------------------------------*/ - n2 Z: h3 H& {. f6 f3 y
2 q; I V7 u W2 p- static UBaseType_t prvNextFunction( void ) # W9 H g6 q z- l5 A
- {
& L+ J4 ~+ q9 x - /* 函数体在此定义. */ }/ L( v1 Y9 _$ W# `3 t2 L5 J
- }
7 ]' `& W6 B$ ?9 I, L - /*-----------------------------------------------------------*/
- U2 n( X8 z6 | u
0 m8 _, M: o7 F- l1 w- /*
, K7 E5 B5 O, g - * 函数名字总是占一行,包括返回类型。 左括号之前没有空格左括号之后有一个空格,
) T% F5 z+ o8 N; _5 E - * 每个参数后面有一个空格参数的命名应该具有一定的描述性.
0 w. l# a+ m( L0 I y% `/ Y - */ 7 Q0 c# _: c% z8 @, P( i
- void vAnExampleFunction( long lParameter1, unsigned short usParameter2 )
5 X! i8 `# `. O$ [% t: v - {
4 o- e# t1 s8 u4 K+ D - /* 变量声明没有缩进. */ 0 W! r6 J9 `; w) i+ i7 ?
- uint8_t ucByte;
* M @$ l0 ?. t6 D* f
6 d5 u& y& D8 V0 Z* }- u1 k- ~- /* 代码要对齐. 大括号占独自一行. */ 7 I$ e* X& i: H p5 @7 {- ]3 H/ o- [
- for( ucByte = 0U; ucByte < fileBUFFER_LENGTH; ucByte++ )
" _2 ]" P1 e3 W k" A, c' s' [. l - {
' p& v: R; I( M8 Z/ d0 W1 Z - /* 这里再次缩进. */
! e( m6 t( x* R - } $ `. ^3 t5 o1 H. C
- } + C! M- J( c4 q$ ]! k
9 m: G) N) p. Q$ j' |. h9 {- /*
1 A! W; v0 A a0 | - * for、while、do、if结构具有相似的模式。这些关键字和左括号之间没有空格。. ?) x5 c$ G6 z" `. }1 J
- * 左括号之后有一个空格,右括号前面也有一个空格,每个分号后面有一个空格。/ b6 }% p9 s k3 g( _# D' w
- * 每个运算符的前后各一个空格。使用圆括号明确运算符的优先级。不允许有0
$ k" }& l4 `" g! _! o! A1 Y - * 以外的数字(魔鬼数)出现,必要时将这些数字换成能表示出数字含义的常量或
: I. l9 P$ ^ u( S9 z+ R {8 V* O: @ - * 宏定义。5 p- W$ y1 E7 K7 S
- */ * e$ | {/ J q8 K3 o
- for( ucByte = 0U; ucByte < fileBUFFER_LENGTH; ucByte++ ) 7 c& ?, Q0 q1 v6 d, D( W
- {
& O3 K+ R3 u; R# L - } & Y# c( S6 _7 P! m) t1 @' s
& G3 M: d5 Y5 c+ Z2 H T8 Y6 c" G2 z- while( ucByte < fileBUFFER_LENGTH )
- Y2 z; @" F" u8 v3 ]3 E' X - { 9 I: g' l! q# o
- }
! N" g% @3 X8 D$ u1 q: E - 3 a2 `1 ^0 `* I* A6 O
- /*
/ r8 b" x7 O8 e7 E - * 由于运算符优先级的复杂性,我们不能相信自己对运算符优先级时刻保持警惕
) z/ T9 p0 S- l6 Y3 n3 k - * 并能正确的使用,因此对于多个表达式运算时,使用括号明确优先级顺序
. F0 _: c: a% ]0 L0 e& u - */ ' [5 s/ u) `# ?3 F& ^/ e) Y- x' T
- if( ( ucByte < fileBUFFER_LENGTH ) && ( ucByte != 0U ) ) 5 j( H5 G# e, D2 w1 I2 m
- { + n2 L$ C! [, s H( r4 P2 W
- ulResult = ( ( ulValue1 + ulValue2 ) - ulValue3 ) * ulValue4;
- G. ~ i, B! o4 M" Z8 u7 e - } . r" W+ a- I* |* H L$ O3 D1 H4 r
Z+ V# J8 s! n8 r# ?- /* 条件表达式也要像其它代码那样对齐。 */ * |3 |1 \. G/ V
- #if( configUSE_TRACE_FACILITY == 1 )
! W2 E+ ?$ P9 V- w9 m - {
4 q" l: A( H/ f' I# M7 j - /* 向TCB增加一个用于跟踪的计数器. */ " f1 _: j( H0 j" z1 N
- pxNewTCB->uxTCBNumber = uxTaskNumber;
+ D2 D# @. i; v. S: k$ R6 | - } $ K; E/ h9 _8 W) Z9 ?; _) m
- #endif , H8 K y$ T- m8 ~
- ! M; b2 u, }4 A2 {4 b+ y3 m
- /*方括号前后各留一个空格*/ " [ Q! u& i0 R/ H; O R
- ucBuffer[ 0 ] = 0U; 0 E' z& f$ E3 `( b# f
- ucBuffer[ fileBUFFER_LENGTH - 1U ] = 0U;
5 D2 |, Q4 V5 q; R
复制代码
. s& k" X2 y9 y9 q* B8 C) G$ R F* W( g/ E- W
, D' q, A) {; P |