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

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

[复制链接]
小小超 发布时间:2016-12-5 10:32
STM32电机培训online,大佬带你玩电机- p, l) |$ G/ M9 n0 J$ X
3 F) n7 d0 W1 g0 I. I$ c

, u( b: ?- X- E: R工作中经过摸索实验,总结出单片机大致应用程序的架构有三种:
) |* z" H5 [( K! {9 b2 R7 v1. 简单的前后台顺序执行程序,这类写法是大多数人使用的方法,不需用思考程序的具体架构,直接通过执行顺序编写应用程序即可。' f2 k0 p+ o% v9 H+ U5 D0 E/ }$ W. D

' t" P' [# x: T2. 时间片轮询法,此方法是介于顺序执行与操作系统之间的一种方法。; ^( m! {- L% E
" \6 K  p/ H1 U
3. 操作系统,此法应该是应用程序编写的最高境界。
5 s& B& y; W" }  ^/ G8 X) B7 f; f! R6 P( U8 ]
下面就分别谈谈这三种方法的利弊和适应范围等。
' g* o. G% p  T- x9 X一、顺序执行法) ~$ }+ t: C! ~7 |* X- ?
这种方法,这应用程序比较简单,实时性,并行性要求不太高的情况下是不错的方法,程序设计简单,思路比较清晰。但是当应用程序比较复杂的时候,如果没有一个完整的流程图,恐怕别人很难看懂程序的运行状态,而且随着程序功能的增加,编写应用程序的工程师的大脑也开始混乱。即不利于升级维护,也不利于代码优化。本人写个几个比较复杂一点的应用程序,刚开始就是使用此法,最终虽然能够实现功能,但是自己的思维一直处于混乱状态。导致程序一直不能让自己满意。: R0 k4 o% r1 I  x6 ]& a+ x
  \5 U) v. C9 g
这种方法大多数人都会采用,而且我们接受的教育也基本都是使用此法。对于我们这些基本没有学习过数据结构,程序架构的单片机工程师来说,无疑很难在应用程序的设计上有一个很大的提高,也导致了不同工程师编写的应用程序很难相互利于和学习。
* e! y, w- H4 E1 j. j7 ?1 `! P% W" e8 G8 V* ^* [6 g
本人建议,如果喜欢使用此法的网友,如果编写比较复杂的应用程序,一定要先理清头脑,设计好完整的流程图再编写程序,否则后果很严重。当然应该程序本身很简单,此法还是一个非常必须的选择。
" L. T7 w6 Y! K8 v! `. c& W& i
, _! s( F1 S  j6 G3 p( R" p+ u下面就写一个顺序执行的程序模型,方便和下面两种方法对比:
& N8 |, @9 \) g, s6 A/ J1 G, u. B7 t! g8 A+ z
代 码
: r. W4 C9 m7 n9 [* l$ n. B! W6 _/**************************************************************************************8 H% I! ~6 E7 J. z  H
* FunctionName   : main()
) y' R3 b8 `2 e) B6 V, m* Description    : 主函数5 O8 a: U, A7 ^$ D1 F
* EntryParameter : None
  {1 C& P  F8 x  M. f5 s* ReturnValue    : None
' }2 W- p8 g6 C2 b**************************************************************************************/% F# U5 |. l# _
int main(void)
, I+ |8 h4 D. S  i& n{ ( r. U( F  |) W0 Y  R; @* m9 W
    uint8 keyValue;
1 q7 g# C6 L, I9 S) V; g% c! Y( o# I8 o
! P) |7 ?1 A, M    InitSys();                  // 初始化
/ W* G# {# J+ c$ d" r6 E7 d8 q# U
0 O8 v5 R6 u9 e: V- f2 ]    while (1)
  ]. r2 o, h9 z: `/ l    {) n* [7 @! d( g7 |1 W7 Z
        TaskDisplayClock();4 e4 u  P. c* E5 J5 R: s
        keyValue = TaskKeySan();. X% `" S$ ^9 U
        switch (keyValue)
9 |* \7 \3 s, R       {$ c- W+ D5 @# H8 S2 i
            case x: TaskDispStatus(); break;
. b7 k3 o8 v6 o" V1 Q, k            ...
1 g# F# m  F0 ?9 x/ J3 L3 i. [( L            default: break;
- Q& r+ D0 J& `3 s% i! I6 z2 n        }
. B0 P) _4 v* N9 t% h    }
# h, ^5 W  U- L}: A* V3 h* ?8 e2 e6 n9 D
+ J6 ?5 U( E; w9 j1 h
二、时间片轮询法4 z% J! `. u; n4 K) e! {
时间片轮询法,在很多书籍中有提到,而且有很多时候都是与操作系统一起出现,也就是说很多时候是操作系统中使用了这一方法。不过我们这里要说的这个时间片轮询法并不是挂在操作系统下,而是在前后台程序中使用此法。也是本贴要详细说明和介绍的方法。5 C' D7 F1 j% _- d$ z

0 q3 v: W) {8 i) d, E% C- @对于时间片轮询法,虽然有不少书籍都有介绍,但大多说得并不系统,只是提提概念而已。下面本人将详细介绍这种模式,并参考别人的代码建立的一个时间片轮询架构程序的方法,我想将给初学者有一定的借鉴性。
$ Z- e6 h4 a' S/ ^  ?# k3 p
" t: n' L, o) p: S* s在这里我们先介绍一下定时器的复用功能。5 Q: b* {5 |, D* U5 X7 q# a( Y
' v: B  T' z1 Y4 Y" r  x7 ~
使用1个定时器,可以是任意的定时器,这里不做特殊说明,下面假设有3个任务,那么我们应该做如下工作:
& n: M$ c3 D$ G# Y; m2 I
1 g' p0 l4 r5 }* j, `! ~& t5 B1. 初始化定时器,这里假设定时器的定时中断为1ms(当然你可以改成10ms,这个和操作系统一样,中断过于频繁效率就低,中断太长,实时性差)。
% t4 _& }; O  P$ w. N  F* k  U7 N9 K7 D4 h# T
2. 定义一个数值:
) |- O6 D( x6 I' @, r# q代 码
( l+ L1 B1 x/ i5 k0 l" l6 t9 v#define TASK_NUM   (3)                  //  这里定义的任务数为3,表示有三个任务会使用此定时器定时。# O) R( b/ \: G' E1 E2 f: f
) N! P4 C% k. m) A+ P
uint16 TaskCount[TASK_NUM] ;           //  这里为三个任务定义三个变量来存放定时值2 ^0 _4 ?. f1 V' Z4 y8 j
uint8  TaskMark[TASK_NUM];             //  同样对应三个标志位,为0表示时间没到,为1表示定时时间到。: y5 p2 @+ t1 I0 }0 a8 w" w- s
6 c( c+ s$ }9 i+ c: E, \; y9 o( z3 H6 a
2 k% M0 `* u, C6 f% A; V" ?
3. 在定时器中断服务函数中添加:" ~' z: O# c  N5 z7 W# J2 A* ]( J
代 码
( H0 m5 x- q" O8 k4 B/**************************************************************************************2 Y( o- q. B9 [# Y7 f: u7 V
* FunctionName : TimerInterrupt()
. q8 ~! D* ?: j2 h! O) E0 l* Description : 定时中断服务函数
* v  c2 @6 r" b, D6 ?* EntryParameter : None3 ]  O6 u9 m, F# C8 g  B  R7 B
* ReturnValue : None
4 G5 r8 G8 f: X# r& f! J**************************************************************************************/
7 u8 R- X1 j  h3 Ovoid TimerInterrupt(void)- K/ C+ V6 J- ]8 A" N: U$ w
{# J* A3 f, M9 P# Z1 m* _
    uint8 i;1 x" r6 ~9 m2 i, d( S, G

# o! T: j7 o+ R' a( g    for (i=0; i<TASKS_NUM; i++)
' F, i, ?- G* w( V3 }* D    {
$ V% n8 k2 }, |; t, x        if (TaskCount)
" I# V6 P, }& i' [3 V- B) _; j: k        {
. r- h1 @4 |& M              TaskCount--;
+ C) l6 r  t) Z6 c! M' d              if (TaskCount == 0)
; u5 f; j" s7 e( `8 r: G              {
7 [+ V' O% v$ F) ^6 U- ?                    TaskMark = 0x01; 5 E9 o. q' \3 d9 G
              }
* m. [0 c- N6 Z. t) Y) ?        }
4 C8 V8 p3 f/ B   }3 r( H& B1 X) ]8 c/ ?6 k
}+ ~2 I" B- [2 P3 w3 s  Q( w
代码解释:定时中断服务函数,在中断中逐个判断,如果定时值为0了,表示没有使用此定时器或此定时器已经完成定时,不着处理。否则定时器减一,知道为零时,相应标志位值1,表示此任务的定时值到了。
5 \7 E/ F. ^; p( }$ b% Y3 x" R; M' a+ @- F5 O
4. 在我们的应用程序中,在需要的应用定时的地方添加如下代码,下面就以任务1为例:1 i4 y  N- b" R  h7 T3 V6 E

: F. d: ?8 s$ W代 码
! z7 }1 d0 f$ UTaskCount[0] = 20;       // 延时20ms. T4 o. Z" z( X4 r' Y6 E
TaskMark[0]  = 0x00;     // 启动此任务的定时器( Y3 i# C6 y1 F
到此我们只需要在任务中判断TaskMark[0] 是否为0x01即可。其他任务添加相同,至此一个定时器的复用问题就实现了。用需要的朋友可以试试,效果不错哦。。。。。。。。。。。, J6 |8 g! m/ L$ i

6 a5 @$ t$ `6 G0 s) E* N通过上面对1个定时器的复用我们可以看出,在等待一个定时的到来的同时我们可以循环判断标志位,同时也可以去执行其他函数。1 @- h  Z8 p. K9 Z8 A% j  X
+ H& w' T' M2 G% S
循环判断标志位:  U0 k+ x2 L' j( X) D0 T" e& f, B3 R
那么我们可以想想,如果循环判断标志位,是不是就和上面介绍的顺序执行程序是一样的呢?一个大循环,只是这个延时比普通的for循环精确一些,可以实现精确延时。9 G* d6 V- l5 v- m. \2 y* q
# G) Q  \( A, A% r! {0 ^2 n0 v
执行其他函数:' l. X' x5 E- g0 p1 p
那么如果我们在一个函数延时的时候去执行其他函数,充分利用CPU时间,是不是和操作系统有些类似了呢?但是操作系统的任务管理和切换是非常复杂的。下面我们就将利用此方法架构一直新的应用程序。6 b( O2 ^. {2 u% y9 N3 [

6 u0 s  x5 m- }5 l时间片轮询法的架构:( r3 J* r+ g1 I0 G+ N7 l) z! G
1 q; v$ g8 Q; N4 L: ], C* n
1.设计一个结构体:
: p3 Z  D6 x, G2 b) b( |3 t代 码
2 t1 h) P/ K; |9 W  r* X' }// 任务结构  F/ V1 U4 ?. u" _
typedef struct _TASK_COMPONENTS3 p. A5 L2 V; f1 B. y0 n- o; I
{0 g0 m) d+ l( T) C- Y+ H
    uint8 Run;                 // 程序运行标记:0-不运行,1运行5 u& n, ]8 D4 k. I' r
    uint8 Timer;              // 计时器
! c: M4 y7 d3 e3 y1 \: g    uint8 ItvTime;              // 任务运行间隔时间! s5 N$ M: @# Q* b% p
    void (*TaskHook)(void);    // 要运行的任务函数
9 `* X: Z( e! s. P2 q% B} TASK_COMPONENTS;       // 任务定义5 s6 {: A, w6 R+ @0 L# L0 k$ U# G
这个结构体的设计非常重要,一个用4个参数,注释说的非常详细,这里不在描述。
. `- r' m# l$ u5 Q! q" @8 ]3 d) o
% ~% f! B  s" j8 O! Z6 w/ T  K4 B2. 任务运行标志出来,此函数就相当于中断服务函数,需要在定时器的中断服务函数中调用此函数,这里独立出来,并于移植和理解。
3 y4 A# s" t2 Q: ~+ e代 码) j* e# c+ b0 L
/**************************************************************************************+ ?1 J- o0 m& C) W* ]
* FunctionName   : TaskRemarks()
6 r/ J: i" D. ]% T# Y+ |* Description    : 任务标志处理! T8 }% _) `) [, x- ^
* EntryParameter : None& D$ O2 s, c0 [' A' @
* ReturnValue    : None- @4 V5 Y6 a1 d. E  {
**************************************************************************************/  R3 G( {4 ^9 T) I- X
void TaskRemarks(void)
! [6 n1 l& U8 }6 }% B% m{
+ n$ T- O+ S( ~3 H- U1 \- O1 ~    uint8 i;
" w/ c( s7 w" X; G. D, F    for (i=0; i<TASKS_MAX; i++)          // 逐个任务时间处理( s" ^6 i. {$ c: z% G7 E
    {5 F# v) j# A" M( I% X" x
         if (TaskComps.Timer)          // 时间不为0
  ~5 Q  I  ^2 O, t: n        {; t8 @' @3 P' P/ g  L5 b: Z
            TaskComps.Timer--;         // 减去一个节拍& x) @. P6 x" E9 u9 a
            if (TaskComps.Timer == 0)       // 时间减完了8 {9 I( R3 S! e/ L0 y
            {" n  Q4 U4 z4 P$ [5 j5 X) k
                 TaskComps.Timer = TaskComps.ItvTime;       // 恢复计时器值,从新下一次
$ d$ L& U6 b( k8 l  g                 TaskComps.Run = 1;           // 任务可以运行
6 ~4 U. T+ k$ G: T, _            }  [, G# E6 F: V  l" `  b8 Y
        }9 x0 O$ f. |/ ^7 E& Q6 o9 c
   }
+ r2 B1 _1 I; T) o}) b5 ]# h" l3 U4 T1 t% Q# y2 k
大家认真对比一下次函数,和上面定时复用的函数是不是一样的呢?$ G$ j5 p( A& ?9 b' H3 c% W  t& C, |
8 s; t1 G% M5 n/ J% F5 A
3. 任务处理:5 A. U. e. B/ H6 o/ q9 i) X& v
代 码+ A9 I) ]& n) _" H' }
/**************************************************************************************
5 S% i+ L! o7 D; |* FunctionName   : TaskProcess()
, ?" y/ g0 Z$ o" |' C; [* Description    : 任务处理
" z7 A' A! M4 E6 S8 O! q* EntryParameter : None" o( Q5 b! a3 i, D. @* d
* ReturnValue    : None+ s/ a1 C$ @* \' p6 G- E
**************************************************************************************/
2 c# `4 |' P% [* Y( _void TaskProcess(void), h( Y6 X# D7 W6 q5 o
{, N1 q! w  N3 P! y/ Q$ E1 E& l/ {
    uint8 i;* Q9 w3 |$ W* x! |  V2 |
    for (i=0; i<TASKS_MAX; i++)           // 逐个任务时间处理
6 S3 T, h* ?; \8 R" E    {
; k, j: q& Y8 j! R         if (TaskComps.Run)           // 时间不为0
0 @8 @5 [! p# G+ f        {; I0 L8 ?6 k/ \" }4 x
             TaskComps.TaskHook();         // 运行任务" L6 m. R2 V" p% k+ G
             TaskComps.Run = 0;          // 标志清09 G$ ]3 Z+ z/ d5 t
        }
) k3 Q8 Q- [4 b" ~. }+ N! y    }   
# a& g  x9 e- O  {}! a( {0 B, s. C) y0 b0 J9 `: C% Q
6 a8 C: a$ k$ Q$ V5 Q
此函数就是判断什么时候该执行那一个任务了,实现任务的管理操作,应用者只需要在main()函数中调用此函数就可以了,并不需要去分别调用和处理任务函数。3 u, Z3 u0 B1 T  q! D
. G1 ~! j( u7 i! L
到此,一个时间片轮询应用程序的架构就建好了,大家看看是不是非常简单呢?此架构只需要两个函数,一个结构体,为了应用方面下面将再建立一个枚举型变量。
6 r& F6 h/ M# \8 G* G' s5 X4 t7 B- O
下面就说说怎样应用吧,假设我们有三个任务:时钟显示,按键扫描,和工作状态显示。# [  ^1 N- y! U5 A

+ z6 Z) ^' h7 h1 K# N5 ^1. 定义一个上面定义的那种结构体变量:
7 x- ^7 Z+ V& b" h
: o. n: f  u/ |; T代 码1 ^2 C. f8 @$ l+ V
/**************************************************************************************- ]. h9 y/ o+ t- U; V# W- P0 h
* Variable definition                            ( Y0 g6 ~5 q8 @# ]( }0 {
**************************************************************************************/* H  J" U. e( F
static TASK_COMPONENTS TaskComps[] = $ Y9 n+ l( V3 Z
{6 h1 R7 ]) S; ^! L$ h* F; t: s: W
    {0, 60, 60, TaskDisplayClock},            // 显示时钟
4 O/ z/ Q; l% ~2 k$ j& I8 n    {0, 20, 20, TaskKeySan},               // 按键扫描' ^% `3 \# f6 B
    {0, 30, 30, TaskDispStatus},            // 显示工作状态( }. O* H7 p! |
     // 这里添加你的任务。。。。
, F* M  k$ Y! h};
( Q! y! t8 s5 P9 p: V5 i" G1 s在定义变量时,我们已经初始化了值,这些值的初始化,非常重要,跟具体的执行时间优先级等都有关系,这个需要自己掌握。  E; p- C9 P" W7 I

% G) l/ V4 ]& O7 ]$ e①大概意思是,我们有三个任务,没1s执行以下时钟显示,因为我们的时钟最小单位是1s,所以在秒变化后才显示一次就够了。
% l1 L0 u4 J9 r7 d8 v6 E  K5 P& D. L. O
②由于按键在按下时会参数抖动,而我们知道一般按键的抖动大概是20ms,那么我们在顺序执行的函数中一般是延伸20ms,而这里我们每20ms扫描一次,是非常不错的出来,即达到了消抖的目的,也不会漏掉按键输入。! `( a3 G; r" T; D; u
* |2 ^% P( c. O0 Z8 V
③为了能够显示按键后的其他提示和工作界面,我们这里设计每30ms显示一次,如果你觉得反应慢了,你可以让这些值小一点。后面的名称是对应的函数名,你必须在应用程序中编写这函数名称和这三个一样的任务。
9 O0 N: `/ `, }( F7 C8 o$ Y# J" s4 b* D7 O1 T
2. 任务列表:
" c$ e: I: a% V  t' j1 X1 a代 码
  l# _3 Y2 O! S3 W/ [. b: j// 任务清单
0 B; c' U" o& j% c+ l4 n1 Btypedef enum _TASK_LIST) A$ V  k# a# u# Z
{; [# x  D# K: [) x& ~6 r' }$ Y
    TAST_DISP_CLOCK,            // 显示时钟
# e0 p. w4 D+ h- {1 t    TAST_KEY_SAN,             // 按键扫描
! U: U. h7 B& n9 N" @2 x1 U4 W    TASK_DISP_WS,             // 工作状态显示
/ w& N5 V) w  O     // 这里添加你的任务。。。。
: J5 R! a/ Y, K0 v, i     TASKS_MAX                                           // 总的可供分配的定时任务数目
- V9 L# [2 H# }} TASK_LIST;
# o7 q* x' m) a& ?* P好好看看,我们这里定义这个任务清单的目的其实就是参数TASKS_MAX的值,其他值是没有具体的意义的,只是为了清晰的表面任务的关系而已。
  J+ A; b3 z2 z0 y7 J5 T6 s) a# k& F/ {" i- L" \% V% n5 W
3. 编写任务函数:
2 O  g# h/ T0 U代 码- F: S" K1 l3 i( A6 }
/**************************************************************************************- m$ d- ~# Q0 r3 L5 X! s( s
* FunctionName   : TaskDisplayClock()4 T# H0 F/ J/ b# t3 Y
* Description    : 显示任务
' P5 @+ I+ ^: a* EntryParameter : None0 G" d' x! L3 k2 A% g  y
* ReturnValue    : None
6 C0 v7 p! ?5 b0 W! m$ V: E**************************************************************************************/4 X) h* i( ~# V- Y# b5 G, ~1 ]
void TaskDisplayClock(void)
$ F  z$ I% P1 E- n) d! z2 D- ]{
7 b/ Z% w( q2 D+ G6 `$ D! }' X3 `
}
. {- A+ |6 G$ u$ b/**************************************************************************************/ V7 f! j7 G, O* L- O
* FunctionName   : TaskKeySan(); h/ h4 O2 i# X( Y3 Y
* Description    : 扫描任务
5 b2 ^8 Y: Z% K" N* EntryParameter : None
6 S' v8 d6 C+ L2 l* ReturnValue    : None7 Z; T7 T' @" x4 o( V
**************************************************************************************/
. [7 ]! u$ h8 g; }& Cvoid TaskKeySan(void)8 u+ r8 l  o0 y) v9 B
{
" s9 x! B4 z; E* U
; d! u( C2 [% a) u}
: @7 l% d4 y& I& q2 ^% y0 M9 |/**************************************************************************************
$ P) K$ ]8 ^$ k' M# P* FunctionName   : TaskDispStatus()
5 ]( g) @0 ~1 O( {* Description    : 工作状态显示
6 E) `8 y, g/ \0 D* EntryParameter : None
( D- T: a+ }& _+ f! B" ]/ V5 s& O$ v* ReturnValue    : None" N! p& e( m/ A  D9 n2 [* U4 S
**************************************************************************************// J! O+ M( p7 I- |" r* M" n' z1 K9 Q
void TaskDispStatus(void)
/ Z5 ?4 E2 ]# n1 _+ r: G{
% m, S2 [% F% e3 Y$ `  U3 D
8 c9 t: @6 e, l# }- K}
0 m' w$ @. ^6 h0 Y% Y// 这里添加其他任务。。。。。。。。。
7 L. |$ J1 c0 k( d8 a/ K2 j
. o/ S( n! z8 O- u/ T7 W% @% ^现在你就可以根据自己的需要编写任务了。1 _- G* E4 {" G1 \0 i% D

4 d1 |/ Z% x: P' u) T6 W4. 主函数:# X; G6 }) K7 J# I
代 码' w9 T+ B8 k) l! L: j' F
/**************************************************************************************
+ K4 N0 L) _. R* j. W9 C* FunctionName   : main()
& O7 M1 K% h! T5 x! U' p* Description    : 主函数
2 n1 I, f! f  y% F0 i5 Y9 l8 g7 v* EntryParameter : None2 v/ O/ M9 r4 ^4 p7 N0 l2 d3 B" Q
* ReturnValue    : None
5 V2 g" P+ s, e9 J) o**************************************************************************************/
% e$ z/ I( B! Gint main(void) 2 Q& W. N% k# A' Z/ Q7 _
{
4 g/ j' t  [# O- g; S    InitSys();                  // 初始化
7 Y2 M) C9 e( a4 o% d2 ?6 t    while (1)$ f( Y5 @7 F* q/ D/ G7 s. L
    {1 D% E0 C3 ~; y7 x/ _
        TaskProcess();             // 任务处理6 _5 p& i/ [2 U9 p
    }0 I( e  V  ~: ~8 B5 h- o2 R; D
}
* N, i8 I' I1 H到此我们的时间片轮询这个应用程序的架构就完成了,你只需要在我们提示的地方添加你自己的任务函数就可以了。是不是很简单啊,有没有点操作系统的感觉在里面?1 z/ e8 [& e' z6 C6 o
) r* z0 C4 W) Y
不防试试把,看看任务之间是不是相互并不干扰?并行运行呢?当然重要的是,还需要,注意任务之间进行数据传递时,需要采用全局变量,除此之外还需要注意划分任务以及任务的执行时间,在编写任务时,尽量让任务尽快执行完成。。。。。。。。
; o  s; V, N, ^' S$ b4 e9 G! B8 C% _: E+ s  \
三、操作系统' A2 h9 T. F- R" ~8 P
操作系统的本身是一个比较复杂的东西,任务的管理,执行本事并不需要我们去了解。但是光是移植都是一件非常困难的是,虽然有人说过“你如果使用过系统,将不会在去使用前后台程序”。但是真正能使用操作系统的人并不多,不仅是因为系统的使用本身很复杂,而且还需要购买许可证(ucos也不例外,如果商用的话)。9 C+ h/ P/ u' i& m
" o' }5 X; S3 J$ H
这里本人并不想过多的介绍操作系统本身,因为不是一两句话能过说明白的,下面列出UCOS下编写应该程序的模型。大家可以对比一下,这三种方式下的各自的优缺点。
4 I: m  Z7 _4 U5 k$ A
2 K2 |3 `0 A; M" ~代 码0 m; c4 Q8 F1 i7 L$ W
/**************************************************************************************
# s) d. @2 G) i9 F3 `* FunctionName   : main()$ N: X! S4 C1 K) e/ W; k4 V- n
* Description    : 主函数
1 }/ b! Z/ F" l. h, T3 t* EntryParameter : None% n( I2 a3 b  c8 ^2 F, C
* ReturnValue    : None
$ i8 j$ d; O! j" \$ R3 k**************************************************************************************/
; D- U% f6 _$ v5 F5 q% aint main(void) ) \8 i7 R+ E3 ~7 q4 c) r0 D5 z
{
. j5 @( a$ n# J& d    OSInit();                // 初始化uCOS-II& }/ W7 V# ]( Q# A  V7 [# \3 C# k1 K
    OSTaskCreate((void (*) (void *)) TaskStart,        // 任务指针
  U& p1 Q3 ~& C2 v( ?+ [                (void   *) 0,            // 参数
6 ]# l2 X; C) `. C* A                (OS_STK *) &TaskStartStk[TASK_START_STK_SIZE - 1], // 堆栈指针: |+ ^( |  K) `$ k- {+ u  C
                (INT8U   ) TASK_START_PRIO);        // 任务优先级8 j' Q4 l1 G+ \, Q( D
    OSStart();                                       // 启动多任务环境0 [+ l' `+ i# l+ O8 D
0 l6 v  q; J, ]7 x$ c9 x1 k3 p9 y
    return (0);
, U- d& _% a: G# F& L}
* v- Q/ g: y6 U  W6 ]& E, q8 I$ l8 r
" {) u( w5 D6 c* Z+ H( [  n# S代 码
. t0 ]- p% y, t/**************************************************************************************
/ f* E% m  q& h- f, {: E* FunctionName   : TaskStart()          8 Z6 m* Z; y( p, o& ]+ ?& K& N* M" ]
* Description    : 任务创建,只创建任务,不完成其他工作3 b0 E' r( m+ D; c" k4 S3 T! R
* EntryParameter : None
3 L, ~( `; L& A7 `- r; X: d* ReturnValue    : None2 U. A2 i$ @$ |
**************************************************************************************/% J8 ^6 U( f8 G0 I4 `! x6 ?
void TaskStart(void* p_arg)! A6 d6 H1 X% P0 G
{
0 p% z8 f# ~1 L    OS_CPU_SysTickInit();                                       // Initialize the SysTick.5 w/ H: Y8 U4 o. L! B/ e. h
#if (OS_TASK_STAT_EN > 0)* S  C9 b1 d5 Z8 ~( {9 U0 ^
    OSStatInit();                                               // 这东西可以测量CPU使用量 ' @, z8 G  V8 a' \0 `
#endif2 c, [- V& r% O/ [7 r- }4 D" U3 B
OSTaskCreate((void (*) (void *)) TaskLed,     // 任务1
  [; A: V9 z" G& ~9 o) I                (void   *) 0,               // 不带参数3 `6 }5 |& \' L3 h: [
                (OS_STK *) &TaskLedStk[TASK_LED_STK_SIZE - 1],  // 堆栈指针
0 `8 Z( i0 b  P; W8 `* B) G3 D; z                (INT8U   ) TASK_LED_PRIO);         // 优先级
6 W% V7 y9 m) B2 R// Here the task of creating your
/ {5 i- H$ a+ W- R4 }( A* ^' f
5 @2 Y/ ?' E8 A5 T( M, p    while (1)4 A+ R, }" W: C9 M3 }2 b  n
    {) W9 p2 ?' D' B  s
        OSTimeDlyHMSM(0, 0, 0, 100);  L0 l2 D9 L2 e( A( D
    }& _4 f3 V1 k; r, X, L9 I
}9 O8 U: s; L; k) Y4 C
不难看出,时间片轮询法优势还是比较大的,即由顺序执行法的优点,也有操作系统的优点。结构清晰,简单,非常容易理解。* v7 l0 _- C3 z7 w1 V+ X$ o* e

/ c0 c% i- D" a2 O, \

评分

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

查看全部评分

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

举报

21个回答
echenlong 回答时间:2018-5-12 10:09:34
新手入门中。感谢分享。
0 D6 g/ |" H' B/ W9 L# _/ o, C% m( {3 i9 }6 M  l& n
里面有一个地方,我觉得按照上下文的理解,应该值有错误:1 \- X( [, I. `6 G  |9 C+ E
原文:{0, 60, 60, TaskDisplayClock},            // 显示时钟,60ms刷新一次。- w6 |& {! S2 C/ o$ u( n9 ^
按照描述:{0, 1000, 1000, TaskDisplayClock},            // 显示时钟,一秒刷新一次
wolfgang 回答时间:2017-7-9 23:35:08
时间轮转有一个问题,中断处理超过轮转的时长是否继续处理?
) E3 O' ]- o- Z/ [$ y如果等下一轮转任务处理后来处理,某些寄存器状态会超时。数据不能保真。。。
西点钟灵毓秀 回答时间: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管理
行使您的权利
官方最新发布
STM32N6 AI生态系统
STM32MCU,MPU高性能GUI
ST ACEPACK电源模块
意法半导体生物传感器
STM32Cube扩展软件包
关注我们
st-img 微信公众号
st-img 手机版