你的浏览器版本过低,可能导致网站不能正常访问!
为了你能正常使用网站功能,请使用这些浏览器。

【分享经验】牛人总结的单片机应用程序架构  

[复制链接]
小小超 发布时间:2016-12-5 10:32
STM32电机培训online,大佬带你玩电机8 Y+ e" S$ ?# y2 l
3 k# C1 ^1 u9 P+ a' B
7 S# Y; R( V' t0 S! Y  E
工作中经过摸索实验,总结出单片机大致应用程序的架构有三种:
# h: ~, I  Y1 ^& ~2 ]5 B1. 简单的前后台顺序执行程序,这类写法是大多数人使用的方法,不需用思考程序的具体架构,直接通过执行顺序编写应用程序即可。, [0 }! S) E8 F0 [" m/ M* e

5 m3 C  ?* M! q2 a4 }* |& U2. 时间片轮询法,此方法是介于顺序执行与操作系统之间的一种方法。6 z5 f; y0 u7 M( r9 S1 K; p
0 v* _+ y+ }+ J& A3 ?2 J
3. 操作系统,此法应该是应用程序编写的最高境界。
0 |& m1 {$ K! X# r2 D2 R/ Z+ L. d8 N# _
下面就分别谈谈这三种方法的利弊和适应范围等。
' i- q' y3 X- r0 U一、顺序执行法
( ?) y- F' r  t3 u2 R1 M0 \) v! m: X这种方法,这应用程序比较简单,实时性,并行性要求不太高的情况下是不错的方法,程序设计简单,思路比较清晰。但是当应用程序比较复杂的时候,如果没有一个完整的流程图,恐怕别人很难看懂程序的运行状态,而且随着程序功能的增加,编写应用程序的工程师的大脑也开始混乱。即不利于升级维护,也不利于代码优化。本人写个几个比较复杂一点的应用程序,刚开始就是使用此法,最终虽然能够实现功能,但是自己的思维一直处于混乱状态。导致程序一直不能让自己满意。- m4 U, @7 l3 W' U' }- F% B

" Q- J. Q) w7 X  K这种方法大多数人都会采用,而且我们接受的教育也基本都是使用此法。对于我们这些基本没有学习过数据结构,程序架构的单片机工程师来说,无疑很难在应用程序的设计上有一个很大的提高,也导致了不同工程师编写的应用程序很难相互利于和学习。
0 C/ ]0 h( n  i; y2 \$ J5 r2 z- O. N; f% V
本人建议,如果喜欢使用此法的网友,如果编写比较复杂的应用程序,一定要先理清头脑,设计好完整的流程图再编写程序,否则后果很严重。当然应该程序本身很简单,此法还是一个非常必须的选择。' K9 S( D' }* }5 T, H

% G4 c) `! ]. G! q: n5 \下面就写一个顺序执行的程序模型,方便和下面两种方法对比:
+ |( h# n% n1 z: C( @
, _7 @& q4 m% Z. s3 k代 码
" f1 ~4 y6 S3 G" ~4 G; m4 x2 Q/**************************************************************************************
. `7 L' F8 p& [  ~" o5 D9 C* FunctionName   : main(), j- h: E% Z6 d, T5 y* Q
* Description    : 主函数0 ?- ^# H( c$ s' a8 y
* EntryParameter : None
7 z: K) \2 o# G5 c- G( l, e* ReturnValue    : None
! j. b" u; D: }" P( C, w- v6 p**************************************************************************************/
* c1 |' |3 Y! \# ~5 Vint main(void) . C9 c. [2 K: J& Q
{
( R& [; F6 z% v/ A5 J+ t# V    uint8 keyValue;; E# q& r* R& M/ s! K

, l5 _+ L) m# S$ o" d    InitSys();                  // 初始化2 B- P; d, B5 e, j8 m
* _# @1 K: w* J2 i
    while (1)
- m% \% v& a. ?+ h  n( {    {) T6 |5 g/ H" m8 C
        TaskDisplayClock();* }& L- ~2 p3 T
        keyValue = TaskKeySan();6 @9 {) ^& c* _' ~) M, {1 M" Q
        switch (keyValue)! B* F. c8 S' C) q# G3 M
       {- e$ R+ S9 Y# M" }- N+ Q/ }
            case x: TaskDispStatus(); break;: }- v* [/ R6 M0 I% T- h3 j
            ...! F3 H+ t& [  @5 n1 T
            default: break;1 h7 i1 e0 _1 U
        }; f# j% k( R" w4 |7 m
    }  p# F' t- f) M
}
8 u7 A! m1 w) z  T- C7 a! p5 k1 p% S' e; n6 k: S1 K4 s1 T9 s* n* I
二、时间片轮询法  a: w6 o. _" u
时间片轮询法,在很多书籍中有提到,而且有很多时候都是与操作系统一起出现,也就是说很多时候是操作系统中使用了这一方法。不过我们这里要说的这个时间片轮询法并不是挂在操作系统下,而是在前后台程序中使用此法。也是本贴要详细说明和介绍的方法。
# Q7 t! P9 s0 s# B- R6 ^. {+ U# p+ l1 a2 E
对于时间片轮询法,虽然有不少书籍都有介绍,但大多说得并不系统,只是提提概念而已。下面本人将详细介绍这种模式,并参考别人的代码建立的一个时间片轮询架构程序的方法,我想将给初学者有一定的借鉴性。9 z& V& T3 G# f' f

0 i2 C+ F7 h3 I  @5 M6 w在这里我们先介绍一下定时器的复用功能。
; n/ E& w+ x4 S
: y$ C) k5 h. X) r) T, ?4 O4 t! _$ W" {使用1个定时器,可以是任意的定时器,这里不做特殊说明,下面假设有3个任务,那么我们应该做如下工作:
: p1 G7 P  L7 }& I9 W* h9 h) Y% V. o3 J' Y: H  m
1. 初始化定时器,这里假设定时器的定时中断为1ms(当然你可以改成10ms,这个和操作系统一样,中断过于频繁效率就低,中断太长,实时性差)。
( o, ~9 y( S) ~  ~
. P" |5 ?8 u/ o, M( x0 W3 o% O2. 定义一个数值:0 J# e1 j) B) ]. R  q  T0 T
代 码
- A8 `& t6 B+ L  X4 r# M#define TASK_NUM   (3)                  //  这里定义的任务数为3,表示有三个任务会使用此定时器定时。( P) Q" e" t4 @. W( n. B/ w/ f

  ?5 p3 D0 w# a  luint16 TaskCount[TASK_NUM] ;           //  这里为三个任务定义三个变量来存放定时值# @' \/ r* j8 U& ?3 @! m
uint8  TaskMark[TASK_NUM];             //  同样对应三个标志位,为0表示时间没到,为1表示定时时间到。
" \/ X- k3 F, g- h/ k0 W" I- T4 s: V6 W

# I  H; ?! ]' ^0 m4 i; \3. 在定时器中断服务函数中添加:* y; S/ T) B1 U" A
代 码1 M' ~1 f  i1 f0 i  Z
/**************************************************************************************
& f6 U  [& P) [/ ]8 |7 P0 M* FunctionName : TimerInterrupt()! x: b" p" }5 W; r7 l
* Description : 定时中断服务函数. m, B: k. x/ U" B( m
* EntryParameter : None
* K* x1 Y1 z+ W. N! b; h* ReturnValue : None4 j, {- i5 ?1 ~, W! R9 b
**************************************************************************************/
, ]# \+ A. [4 }% N8 L1 S8 ivoid TimerInterrupt(void)
! }! G" T- ]+ r5 O7 `{
* ?* @! M- d! b( ^/ S    uint8 i;) V5 R4 u4 |3 [& l, {# K

+ S2 O* H& T1 g8 Z/ I    for (i=0; i<TASKS_NUM; i++)
, f: T8 F. H2 w) M* J2 H; S    {, g4 y9 \. C# s' o! E, e, ]
        if (TaskCount) 6 o3 Z1 o, e5 X: w' w
        {' {$ ~# \* L+ A! Y( i
              TaskCount--;
7 ?5 Y5 a: _5 i! F, D1 n              if (TaskCount == 0) + p: z' a  w! ]- {' T9 r$ ?
              {
$ Q2 a' F" L- S( A$ h: q                    TaskMark = 0x01;
/ k( ]1 t1 W- k1 [! U; ~              }
9 v: W! M0 e# l0 g; {3 B        }
' `3 D3 Y9 a" a& j7 a) v   }
* f+ X9 n' U4 j% t}
5 ^0 ^9 s$ Q# y8 l2 e代码解释:定时中断服务函数,在中断中逐个判断,如果定时值为0了,表示没有使用此定时器或此定时器已经完成定时,不着处理。否则定时器减一,知道为零时,相应标志位值1,表示此任务的定时值到了。
% z$ g. M" r- H1 p2 e; T8 O4 U( {" ~6 _% ^( g) g6 |& P
4. 在我们的应用程序中,在需要的应用定时的地方添加如下代码,下面就以任务1为例:  E/ ~9 o7 h0 b

5 s$ ~9 R4 n* G% \$ e6 y" x代 码
$ J1 K  V+ n5 `/ |0 F3 g7 pTaskCount[0] = 20;       // 延时20ms6 O2 g& K# I9 ?" r6 F
TaskMark[0]  = 0x00;     // 启动此任务的定时器6 d/ z- S0 ^- p" t  k  z( Q
到此我们只需要在任务中判断TaskMark[0] 是否为0x01即可。其他任务添加相同,至此一个定时器的复用问题就实现了。用需要的朋友可以试试,效果不错哦。。。。。。。。。。。7 W4 }; }+ l6 s& X5 U1 S

3 M5 b8 x, B' P& X通过上面对1个定时器的复用我们可以看出,在等待一个定时的到来的同时我们可以循环判断标志位,同时也可以去执行其他函数。
6 R/ Z8 Y# a* B2 Z$ |- q! j7 W! j7 T8 {
循环判断标志位:
; p- Z; }: D$ X- Q; x% n9 T+ u那么我们可以想想,如果循环判断标志位,是不是就和上面介绍的顺序执行程序是一样的呢?一个大循环,只是这个延时比普通的for循环精确一些,可以实现精确延时。
9 P8 I* v$ A* h8 b1 C0 b" C+ Y3 S. u. f
执行其他函数:8 J/ z' ~8 N: i4 A/ H
那么如果我们在一个函数延时的时候去执行其他函数,充分利用CPU时间,是不是和操作系统有些类似了呢?但是操作系统的任务管理和切换是非常复杂的。下面我们就将利用此方法架构一直新的应用程序。1 |& A: N2 ?" L& |, |

: T# v/ p! \% a1 o) _! H0 J时间片轮询法的架构:
6 U* @$ t: L9 j& {. A. y1 R
. V2 \  P: j3 _' C' M. b7 l0 D9 p: \1.设计一个结构体:; W; L" Y: G; _3 G0 ?
代 码
( B& ]. |# M+ N1 @. G// 任务结构
. G# [* j8 q* i) r  ~typedef struct _TASK_COMPONENTS" T& W7 ]: X) m
{
  y4 {# h4 ]: K) g$ S' L    uint8 Run;                 // 程序运行标记:0-不运行,1运行
9 X0 s6 _& z; M, h" F4 L    uint8 Timer;              // 计时器; Y2 R4 i! N4 E7 {
    uint8 ItvTime;              // 任务运行间隔时间
8 X/ C' }* t$ k8 G& g    void (*TaskHook)(void);    // 要运行的任务函数' H. K2 q% ?& B+ k- C
} TASK_COMPONENTS;       // 任务定义
% ?: S$ L3 u  x, O: W2 G9 N这个结构体的设计非常重要,一个用4个参数,注释说的非常详细,这里不在描述。
+ ]5 z- y: O+ K1 s1 {4 F% y8 [! Z! a, R' N; ~. \8 Q
2. 任务运行标志出来,此函数就相当于中断服务函数,需要在定时器的中断服务函数中调用此函数,这里独立出来,并于移植和理解。
3 U% B+ c- j# k) M& u. x( g代 码
7 S! Z% r0 C9 I& v5 R7 J  N) M/**************************************************************************************1 |, {9 P6 U( J/ Y* F
* FunctionName   : TaskRemarks()+ e' ^9 Q+ e3 ?) ~2 k" _
* Description    : 任务标志处理8 O: l. t; y  _  M2 ?/ k8 |
* EntryParameter : None
+ q' {' `/ {% q4 \* ReturnValue    : None
/ O3 ]# U' |* Y; T**************************************************************************************/
6 X! u$ A8 A# P, r$ v8 Gvoid TaskRemarks(void)% w! D3 c, F9 I; ?
{
9 v: G3 y$ f' r9 c- V( j6 ~    uint8 i;/ E4 x) n3 q- g0 K4 j' e! Z3 n6 I
    for (i=0; i<TASKS_MAX; i++)          // 逐个任务时间处理
7 _6 k. ]5 Z1 T8 N5 ^    {
  X4 _' W) u" N! z$ p5 Q( X, |         if (TaskComps.Timer)          // 时间不为0
  q' O, t# x3 k        {
) s# z0 ~6 A8 X            TaskComps.Timer--;         // 减去一个节拍$ r* P4 p0 f7 g2 {8 O7 N  F
            if (TaskComps.Timer == 0)       // 时间减完了
) u2 K1 X  B# q" @$ [. S            {5 ~2 Z+ j* n2 @! i9 n
                 TaskComps.Timer = TaskComps.ItvTime;       // 恢复计时器值,从新下一次
% W5 N; K) Q  v. t- j0 r                 TaskComps.Run = 1;           // 任务可以运行
: U7 S$ u5 |" {            }
! {0 B0 j* |. o) U        }, v/ A$ \6 s/ N$ Y% v$ ?' n
   }8 R: x& @5 h( x4 ~+ G% P
}
$ {) X% ]5 G. K- n7 Y. S, x7 W大家认真对比一下次函数,和上面定时复用的函数是不是一样的呢?
0 ?# l; g: x* s& }0 c+ k
" S+ R, [1 ?3 f7 C8 K3. 任务处理:
5 D: y' s# k1 R代 码
3 s3 n' n! z5 q5 S/**************************************************************************************
' R& K5 ^0 v+ v0 P! e* FunctionName   : TaskProcess()
! k) \2 }0 W# s5 V4 S* Description    : 任务处理
7 g2 i; F  i1 m8 c* EntryParameter : None3 e9 Z( D3 L- ~% ?! l
* ReturnValue    : None
6 @" f+ X. t/ @4 Z* M**************************************************************************************/
. F) ?# Q5 g2 {1 Y) S* Evoid TaskProcess(void)
# ]1 z+ j' G8 k( F+ ~2 F# d{! K5 Z3 z; |2 Q- Q* ^5 e, E, B) h
    uint8 i;
8 A7 |; j6 A1 @; T    for (i=0; i<TASKS_MAX; i++)           // 逐个任务时间处理
0 E& c7 T; y  W" J+ V4 n0 f) J    {$ z# I* `& ~% F3 U' Z6 M
         if (TaskComps.Run)           // 时间不为0
; [, |  ^& Y3 \% Y& O        {9 b4 i9 {# p0 D* a: {, h% l
             TaskComps.TaskHook();         // 运行任务0 v' H0 t$ _# n& ?
             TaskComps.Run = 0;          // 标志清0
8 C. D4 _3 H! T6 E        }  }( X" V+ q6 s) h* E
    }   0 h7 S+ `6 q5 `# u/ V2 x9 C9 ~
}
# K! W% v0 A- X  i5 N5 Q! m  t. f
此函数就是判断什么时候该执行那一个任务了,实现任务的管理操作,应用者只需要在main()函数中调用此函数就可以了,并不需要去分别调用和处理任务函数。
+ H# F% B% ^; y4 b$ y# n" \  H7 Q' i
到此,一个时间片轮询应用程序的架构就建好了,大家看看是不是非常简单呢?此架构只需要两个函数,一个结构体,为了应用方面下面将再建立一个枚举型变量。
* C  ~! H' I6 }1 |, h7 L0 D  e
7 D6 K' l) l+ t/ Y下面就说说怎样应用吧,假设我们有三个任务:时钟显示,按键扫描,和工作状态显示。4 c) h# a# a. R* a5 a

) a# z, @9 f% ^4 O: H  [6 K1. 定义一个上面定义的那种结构体变量:! s4 g9 O8 D0 ~8 `& ]

" S% [5 [( i  v代 码
$ e6 ]7 o( {1 `: A6 ], R; Z2 Y/**************************************************************************************
6 V/ ]5 |+ v9 }* Variable definition                            , `0 L5 v! T$ R" c7 S: x* g
**************************************************************************************/
5 J- _$ S$ G( \$ {: _  j: x5 N" Xstatic TASK_COMPONENTS TaskComps[] = 0 l4 _5 P3 a; A, O
{
6 M7 O# g- E7 \* q; Z    {0, 60, 60, TaskDisplayClock},            // 显示时钟6 A; ?; ^4 H% M  T/ `
    {0, 20, 20, TaskKeySan},               // 按键扫描
5 R8 c8 @" i& B    {0, 30, 30, TaskDispStatus},            // 显示工作状态8 L- n4 {4 _# n% R/ p
     // 这里添加你的任务。。。。6 b3 B3 |' T$ _6 t% l; ]6 C( O/ x  Q6 k
};. \0 A2 a. W+ B% c3 S" `
在定义变量时,我们已经初始化了值,这些值的初始化,非常重要,跟具体的执行时间优先级等都有关系,这个需要自己掌握。/ h+ J+ w6 q; K% w
7 W4 Q. |6 G0 @0 n) c" n# E4 p
①大概意思是,我们有三个任务,没1s执行以下时钟显示,因为我们的时钟最小单位是1s,所以在秒变化后才显示一次就够了。
% {8 ~; i6 s  h& q4 C! r
& l! k( \9 a/ K/ k②由于按键在按下时会参数抖动,而我们知道一般按键的抖动大概是20ms,那么我们在顺序执行的函数中一般是延伸20ms,而这里我们每20ms扫描一次,是非常不错的出来,即达到了消抖的目的,也不会漏掉按键输入。7 j7 ]/ ~0 V; X5 K
7 A# k7 J9 G$ T7 g
③为了能够显示按键后的其他提示和工作界面,我们这里设计每30ms显示一次,如果你觉得反应慢了,你可以让这些值小一点。后面的名称是对应的函数名,你必须在应用程序中编写这函数名称和这三个一样的任务。& Y" K* j1 }4 A. P) D* }
' \7 V# K6 v; ^  S+ ~# X# }  ]
2. 任务列表:
% P) `1 d# P  M* z$ r2 q) A- i代 码
5 ?/ z. {( t7 {. Z1 f  Y// 任务清单
# u8 E" w3 t# d' k0 ctypedef enum _TASK_LIST
! _' ~# C, z+ C# N* o* t" r{
& e8 Y6 B4 j( r' b! X1 S4 Z/ q    TAST_DISP_CLOCK,            // 显示时钟
' t5 v  o& G: D    TAST_KEY_SAN,             // 按键扫描3 x3 w1 D8 h( P5 w3 C
    TASK_DISP_WS,             // 工作状态显示
) [) W" w( t+ ~     // 这里添加你的任务。。。。1 H1 U! n, r; H+ C
     TASKS_MAX                                           // 总的可供分配的定时任务数目# e# L$ |# X! K1 f9 m( g1 R; @# h
} TASK_LIST;1 _( v! i3 t) r) h! [& p: `; v
好好看看,我们这里定义这个任务清单的目的其实就是参数TASKS_MAX的值,其他值是没有具体的意义的,只是为了清晰的表面任务的关系而已。
4 {& v; J0 y) Z8 l/ {' U% r$ C9 r1 n, C6 Q/ s; j
3. 编写任务函数:( `# H' I8 W( y: `
代 码* ~3 Y) Y4 |; Z4 S9 d0 K
/**************************************************************************************# F0 ?4 M0 ]" A' Y7 C
* FunctionName   : TaskDisplayClock()
8 u, T1 C! {$ b  P4 Z" L& S* Description    : 显示任务
1 u7 p4 E6 Y! I5 K* EntryParameter : None
. `6 ?1 V! @# _% c+ _" W) t* ReturnValue    : None
7 C$ J; U, W3 L* P3 i**************************************************************************************/- g/ D9 N2 d$ T% ]5 k: m3 V4 i% L
void TaskDisplayClock(void)4 o. I3 ]: @, r; B6 \7 W
{8 M* P6 A$ Z0 F# c2 T/ V
9 J; b! n4 m) S" k, d7 r
}
& {6 l* v6 m* L4 Q+ ]/**************************************************************************************0 ?/ e0 a8 `- |- ~& ?7 {1 D- D3 f
* FunctionName   : TaskKeySan()
5 x, c) {4 a+ x* Description    : 扫描任务% V. T. k, ~4 l5 r( H( l
* EntryParameter : None( i! j" P7 E9 D5 p: Y
* ReturnValue    : None
  C4 h" F  F' q**************************************************************************************/
7 R; h6 g, {5 r* T  V+ M3 Yvoid TaskKeySan(void)0 T6 H! w* F- d! z6 f0 ~+ |" r
{
% [7 Z6 Z' w: _  }- N
% {7 R. v5 _- M, @; @}
; |( b4 j3 l9 N. M/**************************************************************************************- i0 C5 p' U5 D7 {; F
* FunctionName   : TaskDispStatus()
  y& @. r7 J4 u2 F% |* Description    : 工作状态显示" a, L" {* l, @5 j' h3 X3 p
* EntryParameter : None$ P% J0 v  y  |: K' R6 V) x/ x* P
* ReturnValue    : None6 ~6 M. N' F" l8 W
**************************************************************************************// _' f* Y# y7 l  v5 T& X( m2 ]
void TaskDispStatus(void)2 z  e% M. F0 l$ h
{
. V. q  j5 ~( p; L# I- h) s* j! B0 v) R) a1 s7 F& |7 [4 A
}
; d% |3 ~! F- U) ?& _! {// 这里添加其他任务。。。。。。。。。
- l3 }/ R3 c- r: p, z4 `
) G% C3 i$ K. j现在你就可以根据自己的需要编写任务了。
; D2 z: O; T+ o* Z6 S7 `
) Q' e8 d7 S) `, `2 h$ c4. 主函数:
) p  @  O3 m* u" U. o7 s代 码
5 \2 B3 x& T- b# q" I/**************************************************************************************
1 `- N2 H3 ~. h" u6 A  {% ^" |2 I* FunctionName   : main()- U1 Y* J* r8 S3 x+ |
* Description    : 主函数
/ Z6 z# G" `7 U% w9 }# w8 q! N* EntryParameter : None
2 j( h& ?' Z. d# h2 P! z* ReturnValue    : None
3 B: f$ Y/ L" |2 c1 n**************************************************************************************/6 n4 E5 _( ~3 y7 H8 V$ v% Q! Q
int main(void) 8 L" ^  Z( f% _3 Y2 C
{ ) b! g7 f0 m- v
    InitSys();                  // 初始化' ^% Q: d% z2 c# x3 `: k
    while (1)
& d0 A$ P7 {, b    {0 w! r( \6 F& R- h, k
        TaskProcess();             // 任务处理
" S5 y. l# L, c# k7 z    }/ m0 W8 z% K+ H; I! {
}+ i2 [; Z: @' G, Y4 N' M
到此我们的时间片轮询这个应用程序的架构就完成了,你只需要在我们提示的地方添加你自己的任务函数就可以了。是不是很简单啊,有没有点操作系统的感觉在里面?9 q$ E3 {" [6 w" ~

: r3 P+ g" w! L0 h1 k- y7 U不防试试把,看看任务之间是不是相互并不干扰?并行运行呢?当然重要的是,还需要,注意任务之间进行数据传递时,需要采用全局变量,除此之外还需要注意划分任务以及任务的执行时间,在编写任务时,尽量让任务尽快执行完成。。。。。。。。
; }4 i! X, X- y% }+ j' b* p; {
8 s& u5 x$ ]# u3 j9 y5 m! i$ H三、操作系统( R- c: [) U% x  O" V7 l1 K! K/ |
操作系统的本身是一个比较复杂的东西,任务的管理,执行本事并不需要我们去了解。但是光是移植都是一件非常困难的是,虽然有人说过“你如果使用过系统,将不会在去使用前后台程序”。但是真正能使用操作系统的人并不多,不仅是因为系统的使用本身很复杂,而且还需要购买许可证(ucos也不例外,如果商用的话)。9 h8 T! F4 b: ?$ t  @. s9 _( A

5 l0 Y7 G/ j8 d! y1 t2 _' s这里本人并不想过多的介绍操作系统本身,因为不是一两句话能过说明白的,下面列出UCOS下编写应该程序的模型。大家可以对比一下,这三种方式下的各自的优缺点。
. O9 |' c$ x/ y; v% z3 u( ~+ Q
9 Z! m8 o- K( F4 ?$ w代 码, A; l3 {4 ^3 I% I
/**************************************************************************************+ r, t$ _# }" Q! j# Q5 p/ ~' p/ n
* FunctionName   : main()
& y: }( F! u+ N# h2 W* Description    : 主函数1 m% l7 M; P9 [2 p
* EntryParameter : None, }" k/ W) u* a# r- E9 f3 m
* ReturnValue    : None2 d5 M" _3 z; O4 h
**************************************************************************************/
0 b. @" Y+ k( [9 ]1 n0 lint main(void) + B( ?# y! U  x$ U3 W
{ 5 V; F* @; y: \, o) A% K- B
    OSInit();                // 初始化uCOS-II0 W" H) T9 q$ \- W$ \- ~2 p
    OSTaskCreate((void (*) (void *)) TaskStart,        // 任务指针! _; \/ u) D% ?9 u* F/ d
                (void   *) 0,            // 参数
/ X& J* W$ I+ b                (OS_STK *) &TaskStartStk[TASK_START_STK_SIZE - 1], // 堆栈指针
1 d0 f$ G0 t7 \6 j2 }) K' I- e                (INT8U   ) TASK_START_PRIO);        // 任务优先级
) n5 l' }$ ?0 I    OSStart();                                       // 启动多任务环境
# `4 l3 r, n0 L+ h, B% t3 c; L; I
    return (0);
9 h) P, U& m' k! m7 Z: L}
+ l+ x) i8 W) B0 i$ H4 I
' |' T3 J3 B7 R代 码
: R& V3 |, _% b+ U0 {, ?4 r/**************************************************************************************$ ^$ `, d. T& M+ h( q" v
* FunctionName   : TaskStart()         
% a9 g) F9 U% E9 V/ }. ]& H* Description    : 任务创建,只创建任务,不完成其他工作
9 t  a7 e; m9 I3 {5 s3 w+ W* EntryParameter : None
1 G- W( _9 D( Q+ I8 L7 Y* ReturnValue    : None- `: [6 o! U: x( r4 @# X* K: T
**************************************************************************************/2 b! a: _8 X. b# z/ j
void TaskStart(void* p_arg)7 V$ [2 D# V! K
{. b3 G1 R+ _$ W# n0 O
    OS_CPU_SysTickInit();                                       // Initialize the SysTick.3 R% Y' k/ P* h5 @; c( k
#if (OS_TASK_STAT_EN > 0)
9 n) M6 o, b4 n! x, X+ A" c0 I2 x/ T    OSStatInit();                                               // 这东西可以测量CPU使用量 : o+ h0 K# B* U
#endif6 E7 O4 ^8 A  K* x8 e
OSTaskCreate((void (*) (void *)) TaskLed,     // 任务1
& C2 g+ p4 N' _! D9 ^0 U                (void   *) 0,               // 不带参数6 p' m: K; M4 _
                (OS_STK *) &TaskLedStk[TASK_LED_STK_SIZE - 1],  // 堆栈指针7 M/ f$ f# s0 U
                (INT8U   ) TASK_LED_PRIO);         // 优先级" d& x, q  R- b5 w- J% P) Z$ Y/ g
// Here the task of creating your( @& B" S0 X; S5 D- G2 g
# m5 G$ k* I: C2 d0 V, v2 F2 b8 j9 @
    while (1)% o2 ?( Y. l6 O$ m& e
    {/ f2 x* p$ h% d) P: x
        OSTimeDlyHMSM(0, 0, 0, 100);$ b1 |- y. w, y7 R* b( J
    }# f  X$ B2 t& E0 A5 y* L
}
' r. f3 L7 h: T不难看出,时间片轮询法优势还是比较大的,即由顺序执行法的优点,也有操作系统的优点。结构清晰,简单,非常容易理解。' }) F/ z! \+ p: B! c8 p' R

6 C) n1 v$ Q! V

评分

参与人数 2 ST金币 +12 收起 理由
logigy + 2
zero99 + 10

查看全部评分

3 收藏 14 评论21 发布时间:2016-12-5 10:32

举报

21个回答
echenlong 回答时间:2018-5-12 10:09:34
新手入门中。感谢分享。" ~$ M# K: M' w. a0 q6 ]4 w
/ q2 ?+ ^9 w9 x. W" M. f
里面有一个地方,我觉得按照上下文的理解,应该值有错误:9 y9 c8 F) d$ `' f9 X$ a1 B
原文:{0, 60, 60, TaskDisplayClock},            // 显示时钟,60ms刷新一次。7 o8 y8 h9 x( m) f6 \  t0 M. L' x! S
按照描述:{0, 1000, 1000, TaskDisplayClock},            // 显示时钟,一秒刷新一次
wolfgang 回答时间:2017-7-9 23:35:08
时间轮转有一个问题,中断处理超过轮转的时长是否继续处理?
  I$ k9 Y- t. m7 x! K, G5 X如果等下一轮转任务处理后来处理,某些寄存器状态会超时。数据不能保真。。。
西点钟灵毓秀 回答时间:2018-3-6 00:25:47
【分享经验】牛人总结的单片机应用程序架构,我也来移植试试
sailorx 回答时间:2016-12-6 10:01:03
写的真不错,有系统的概念。学习了
dear祝子 回答时间:2016-12-6 13:19:43
在群里共享过,代码都移植好了,比较不错
andypanfan 回答时间:2016-12-7 08:45:54
写得比较详细  非常好 谢谢了!!!!!
明月小楼 回答时间:2017-7-9 22:15:50
不错。
五哥1 回答时间:2017-7-10 00:41:26
真不错,有系统的概念。
cetcnav 回答时间:2018-1-4 09:59:22
好帖,学习了!
hxembed 回答时间:2018-2-26 14:18:55
不错,学习学习!
yzj7604 回答时间:2018-3-3 23:13:55
学习了,谢谢!
SXW101320 回答时间:2018-3-4 08:24:13
学习了,谢谢楼主
zhongmayitong 回答时间:2018-3-4 23:19:45
学习
板子粉丝 回答时间:2018-3-5 09:37:11
好帖,很有启发
板子粉丝 回答时间:2018-3-5 09:38:40
对整理一下自己程序思路,有帮助
cdt2000 回答时间:2018-3-5 09:52:15
通俗易懂
12下一页
关于意法半导体
我们是谁
投资者关系
意法半导体可持续发展举措
创新和工艺
招聘信息
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
关注我们
st-img 微信公众号
st-img 手机版