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

适用于单片机的接口适配器模式的实现

[复制链接]
gaosmile 发布时间:2020-6-1 17:54
前言
4 i3 c6 y9 K. ~0 C3 j4 B1 \: K

    通俗的讲,适配器模式是将一个类的接口转换成客户希望的另外一个接口,在我们编写程序的时候,尤其是在我们使用到单片机做项目的时候,经常会用到。

微信图片_20200601175338.png

    但是往往我们做项目写程序的时候,并没有想到那么多,如果在不带操作系统的情况下,想要整个框架易于移植,易于理解,那么我们真的需要好好想想这个设计模式怎么写了。

    下面我根据自己的项目经验,来说说适用于单片机的接口适配器模式的实现。大佬勿扰,多多指教。

一般实现

    在我们做项目的时候,一般的实现,可能我们会这样写代码

  1. <font color="#001000"><font style="background-color:rgb(255, 255, 255)"><font face="Tahoma">! Q. G. Z3 h8 d9 t: D- j1 v# T
  2. // FileName: test.c. Z9 O: E1 B, S: j3 q8 K# l. \
  3. // 来源:公众号【技术让梦想更伟大】2 H$ Y. H- d$ d+ \4 u+ R
  4. #include <stdio.h>
    * S; o9 d- g6 P; z) \
  5. #include “ExternModule.h”
    / G$ M2 g* |5 i6 k
  6. $ S3 g$ ^- `4 E4 v8 R8 J+ X
  7. int main(void), q% g* u9 x/ S* u. y
  8. {3 f1 e5 c6 ]* }" w* a7 ]+ h/ f1 h

  9. , L& |- |7 x9 J) w" D
  10.   /*初始化*/" f. m5 [2 Q0 \3 [9 a9 {
  11.   vAllInit();8 Z; P4 V7 x( ~# X8 g! O
  12.   
    1 G; M& N+ c- W! E8 A; Q
  13.   while(1)
    * _7 L, U; R7 m3 ]. t
  14.   {, ~0 _$ E1 r+ W6 l  f/ R( ~
  15.   
    6 G6 C6 s9 u; V9 D* F2 k0 Y8 u
  16.     /*项目逻辑*/2 u, U. s. K" c/ g' X
  17.     vLogicModule1();
    7 ^2 j* M; M0 q0 o
  18.     6 T- T0 \' G& Z5 k" @' `& L3 j- s
  19.     vLogicModule2();
    1 n# n7 e, }' `, C5 E4 j! R
  20.    
    ) D1 P  t+ l4 M$ E$ A; v9 m# a
  21.   }9 l2 X( d! b$ B6 C
  22. }</font></font></font>3 A2 \# |) L2 p8 x( T' {2 I
复制代码

1 t% h# H: d4 B

    在其外部文件中,调用相对应的初始化函数以及逻辑函数,但是当我们项目很复杂的时候,逻辑关系也层层覆盖、交替的时候,这样的写法就有些不是很好看了。

接口适配器

    首先我们还是要来定义数据结构,一般这样的项目分为这样几个步骤:

  • 初始化
  • 输入
  • 处理
  • 输出1 J- V1 F7 T( u

$ X" C2 |5 p) ?. a* z

    我们把这四个步骤封装起来,再定义数据结构如下:

  1. <font color="#001000"><font style="background-color:rgb(255, 255, 255)"><font face="Tahoma">
    3 X7 G3 M, J5 V( g7 Y6 }
  2. // FileName: test1.c
    " _" y% |! J) E0 n& L
  3. // 来源:公众号【技术让梦想更伟大】
    0 f6 W) _5 C; d4 F8 v# L
  4. /* 适配器类型定义 */
    0 K- U, {8 _9 a1 D/ E
  5. struct _ADAPTER
    9 V) v: i; u; A( ~
  6. {, Z$ r/ j3 x2 m
  7.     void (*Init    )( void );  //初始化函数/ R0 s/ \0 c; k
  8.     void (*Input   )( void );  //输入转换函数' Z( m: V7 F9 G; O" L7 ^
  9.     void (*Process )( void );  //处理函数# G; A  e- E0 [8 b
  10.     void (*Output  )( void );  //输出转换函数
    / _) z: C6 {+ {# F1 A5 r
  11. };; g3 _- D% I7 _
  12. typedef struct _ADAPTER ADAPTER ;</font></font></font>; m: w* y" t1 d3 Y
复制代码

5 P' B) C# s0 W

    那么初始化函数,我们先来这样定义


  1. 5 c3 M, c$ B' R& f( S. q, A6 m. X% k
  2. // FileName: test1.c, R8 M) @7 y# G. k
  3. // 来源:公众号【技术让梦想更伟大】7 d+ }+ Y4 `/ \4 d) P
  4. /* 模块初始化 */
    # \& m* h  l" o9 j5 q- o! c
  5. void moduleInit( ADAPTER *module )
    $ T, x/ h: h6 k& W) s  J
  6. {
    ' V" `0 }4 q! R) u6 D, t9 M* C" S9 f" W: c
  7.     if( module->Init != NULL )
    ' A3 Q2 O3 F, Q$ O3 @/ ~; d
  8.     {/ D! F. M. B- m. I& ]! T$ e
  9.         module->Init();
    - [3 g5 d6 n9 l; n
  10.     }( K4 }" h7 v3 _. V, ^) @: O
  11. }
复制代码

6 b  X! C$ \  s3 L# C

    模块的逻辑运行,我们可以这样使用

  1. <font color="#001000"><font style="background-color:rgb(255, 255, 255)"><font face="Tahoma">
    . G+ W/ o7 R9 j& T
  2. // FileName: test1.c7 Q! _( |0 d0 Y4 M$ ~# E
  3. // 来源:公众号【技术让梦想更伟大】
    0 c% s( H) D. ^" w- l! Z0 Y
  4. /* 模块逻辑运行 */
    ) E' G/ O" Y( z, o$ X! c5 M9 S9 a
  5. void moduleRun( ADAPTER *module )
    : @# p* V" P; a
  6. {: A% _; k# d- _% K4 a2 u
  7.     // 模块输入适配接口不为空,则执行输入适配操作
    9 V) h  D- @2 |9 C# y' J9 c
  8.     if( module->Input != NULL )4 _$ Z. j' q  x/ U; [- e& s
  9.     {7 O5 c0 B; E. A: H
  10.         module->Input();
    1 f' w( c2 X" K* h' e1 G
  11.     }# j  q( c! z7 Z; C5 e0 D3 k

  12. 5 p) x  E! S8 j
  13.     // 模块处理接口不为空,则执行处理操作   
    + s0 y: R0 K. i* ^' I* ~
  14.     : S* e! t4 r$ g; q/ T
  15.     // 模块输出适配接口不为空,则执行输出适配操作
    ( J) X% P# _; l0 w) J
  16. }</font></font></font># j1 V2 ]- n/ Y1 H
复制代码


5 |0 i, k% R; Q

    在定好了这些数据结构以及封装之后,我们在每个子模块中都只需要调用这个模式即可。例如有一个需求,需要点一个灯,我们建立独立文件,在文件中申明

  1. $ r+ |6 Q: f( u
  2. // FileName: led.c
    ' Z& G/ d! o8 f
  3. // 来源:公众号【技术让梦想更伟大】
    - m9 e1 b7 T; Y. w0 B
  4. /*led灯运行 */& u0 ?7 a0 k3 e1 g1 ~
  5. ADAPTER LedModule = { vLedInit, NULL, vLedRunModule, NULL };
复制代码

& f2 h7 e1 {5 k- d) Y1 }

    那么接下来只需要对初始化函数,逻辑运行函数进行描述就可以了。同理,我们需要一个按键的功能,在另一个独立文件申请


  1. : g. |! M/ B) }
  2. // FileName: key.c
    / Z9 r( r9 ], l( d( s! d
  3. // 来源:公众号【技术让梦想更伟大】, l  H* A" [+ T; t9 \/ g
  4. /*按键运行 */
    ( Y, L) m3 W. J: m
  5. ADAPTER KeyModule = { vKeyInit, NULL, vKeyRunModule, NULL };
复制代码

! V1 |) [* {: _0 F9 t/ H2 J: y3 u

    这样的话就便于我们拆分需求,便于移植,同时程序也就模块化了,最后我们在main文件中做的就是调用这些函数就行。我们需要这样做。

  1. 5 H& p, e; \2 g$ \* k$ ^) ?# s
  2. // FileName: main.c" d# ?9 z2 Q. G9 [- X
  3. // 来源:公众号【技术让梦想更伟大】
    % ~" x/ I7 y4 ?& |  G* r% r
  4. /*主函数 */8 G4 ?2 P1 t6 C% d" ^6 c' D
  5. void main( void )! I& I9 h2 }. J0 _1 C
  6. {
    9 v/ G8 h  B- }

  7. " J$ l* b' K1 W; Y6 {! Y
  8.     moduleInit( &LedModule         );
    9 c4 v4 o! i$ Y# H+ V( T
  9.     moduleInit( &keyModule         );! W+ Y/ I5 C; c, U' {" Y. p
  10.    
    5 H; R. W* S/ G# r% k# z# m
  11.     while( 1 )
    ) g' G( K: x& H
  12.     {9 a( x2 p$ B- b' ?
  13.         moduleRun( &LedModule        );
    ( b0 z1 Q4 w" O
  14.         moduleRun( &keyModule        );
    4 B. l4 u% G3 C# D6 a3 Q/ H$ e
  15.     }+ \: e# N% D! o- {% z7 a; S- _
  16.    
    0 f( _) ?3 i0 y
  17. }
复制代码
: l: G, {: q5 g
最后
    main函数就是这么简单了,整个架构也是很清晰,体现出编程之美。
赞 收藏 评论2 发布时间:2020-6-1 17:54

举报

2个回答
李康1202 回答时间:2020-6-2 09:05:13
谢谢分享
goyhuan 回答时间:2020-6-2 11:05:04
好厉害哦

所属标签

关于
我们是谁
投资者关系
意法半导体可持续发展举措
创新与技术
意法半导体官网
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
官方最新发布
人形机器人运动控制、感知与智能配电
半导体创新技术与应用方向
EE架构与软件定义汽车
12V/48V 汽车智能配电(SPD)
区域控制单元(ZCU)与分区架构
关注我们
st-img 微信公众号
st-img 手机版