如果你认真看了前面的文档,会发现一个我们的教程跟其他教程的不同点:其他教程都是在教你如何用这个设备,我们说的是,如何给这个设备写驱动。其中,我们一直在强调的一点是,要提供良好的接口给应用使用。$ Z0 |6 a' n" Y3 |
为什么呢?因为,一个复杂项目,底层通常是跟应用分开的。2 ^4 m, A" d+ d; c, q# Z
为了应付复杂的程序,通常需要一个大概的程序架构设计。下面,我们一起学习一种常见的程序架构设计。
# W: d5 W7 |. w, `* ~为什么
/ v" m8 }+ L7 }为什么需要架构设计呢?
; k7 {; c( j! T5 d5 c) J! H" L1 N- g 固件更新
9 l0 f3 S, P" e. G! N+ Q2 T: H& j到目前为止,下载程序,我们有两种方法: - 通过串口下载,在下载过程中,需要控制boot管脚。
- 通过JTAG(SW)调试口下载。: [- v7 E- R7 A% w& P( j5 D/ H( x
在真实产品开发中,调试口只存在于PCB上,预留测试点。没见过有产品外露调试口的。
% O+ I" T& g! r% U& B" j6 K& O/ t有外露串口的,也有用牙签顶住一个小洞就能更新固件的,少,很少。
" B& h3 I; O$ u. n产品需要支持其他方式更新固件,例如U盘、USB、无线OTA更新方式。
3 ]2 I6 ~% d9 P* F
怎么做? 当前芯片基本上都支持IAP功能。
+ M+ r3 ]0 e% T% }; v4 C简单的说,就是,在运行程序时,可以通过程序擦写FLASH,写新的程序到FLASH上。7 E: g4 Q% [# a$ I
那么我们可以通过IAP在应用更新固件。 1 i" l# a' V& E' x8 G
应用和驱动分离
" f4 k2 {; N1 n' v0 |6 r1 X+ M1 a: h功能复杂,程序就大,人就多。
/ B3 q5 j: l+ |) m6 n遇到问题就扯皮。6 y8 N/ _( X; r/ l
有的公司,会将底层打包为库,丢给应用使用,应用将自己的程序跟底层库一起打包编译。1 R7 g9 }7 A2 h% u# c4 y2 i
这样的方式,并不能解决问题。
5 [0 l& J$ s: b% V# E: o0 l8 V除了扯皮,也有实际问题。 - 多应用6 a' n) j1 Z% I2 j, H3 X
有很多行业,一个设备,会卖给不同的客户,不同的客户就有不同的需求,就需要不同的程序。( d4 Q) K6 b- P
如果底层已一个BUG,需要修改,那么就需要发布N个程序。 - 升级压力
$ X4 Z. m' o! O5 _) K如果程序比较大,例如驱动有200K,应用有200K,一起400K,那么,OTA就要传输400K。
. N( T/ C! H1 L" ~% v如果两者分开,只有一个修改的时候,只需要OTA 200K。 - 促进发展
) r: J# G8 n! \3 T6 z如果将底层和APP分离,务必就有一套标准的接口。- f5 A( W" ^2 ?8 U, R( e2 q. E
如何做?
6 ^4 G5 K( W) a* D6 j+ k% T% s' v牵涉以下问题: - 空间分配1 }- i4 x" S0 I: Q) w I. E- L1 J3 x
驱动跟应用分离,意思是,驱动一个工程,单独编译。应用是另外一个工程,单独编译。
$ n j. ~$ @; n+ W& P) Y( d$ x那么FLASH和RAM空间就需要分开,通过分散加载文件实现。
$ w) k" N/ {6 \. WRAM,分开的是全局变量空间。临时变量用的是栈,不需要分开。 - 时间片& C! O/ C3 t- [/ a0 B5 p3 \+ k2 ?
如果驱动没有时间片需求,那么驱动,就类似一堆库函数。应用只要调用就行了。
4 P# f9 c7 C# Y! q' F% ^如果驱动有要轮询的任务呢?( i8 {/ D0 Y2 P$ e9 h- }/ U: E" j
- 可以放在一个低优先级的定时器中轮询。-----不建议
- 去不放在一个函数中,让应用带着跑--------如果应用是小循环模式,带着跑很不方便。
- 上RTOS,驱动掌控RTOS,应用做为一个RTOS的一个任务----------强烈建议用这个方法。
2 Z) ?; d$ [* b
- 接口
( c- a0 W: V8 x: Y. |应用如何调用底层接口呢?
$ U* N; B$ M+ D! G2 a5 N8 V" A8 A& NCPU执行一个函数,就是将PC指针指向函数。
( g! z$ z3 g/ ?写程序时,我们经常会用函数指针。/ q$ r( ?' M4 p, M6 Y& }
APP和驱动的沟通,就是通过函数指针来实现。 - 分离的缺陷* S0 M+ w3 z, G
驱动和应用都会链接标准库,代码会大一点。
9 H# P1 u k& [2 W 常见架构
: R4 k% C0 o) p5 @' Z: [+ r0 K我们常见的架构方式是:3段式框架。" N9 R. U& u. C- d _1 w
如下图: 
; ~; z2 j- c( I: X; Qfile:///H:/wujique/%E8%B0%83%E8%AF%95%E4%B8%8E%E5%BC%80%E5%8F%91%E8%AE%B0%E5%BD%95/4.0%20%E7%A8%8B%E5%BA%8F%E6%9E%B6%E6%9E%84%E8%AE%BE%E8%AE%A1/pic/%E6%A1%86%E6%9E%B6.png戏称三权分立 - BOOT+ y! y0 L/ F3 X+ v' k A
最先启动的代码,负责固件更新,并包含最基础的固件下载功能。* x: u: X8 v* n9 L
启动后,跳到CORE运行位置,丢失控制权。 - CORE
4 E" J; U. O) o! Q) O% Z通常包含驱动,测试程序。可能会支持更多的固件下载方式。
8 @$ R0 A9 \: x+ i- h; }* _0 F9 d部分软件模块也放在CORE。例如文件系统等软件模块。
$ o9 v( d5 e6 ^" H+ Z0 ~$ Y. N所有给应用使用的接口,放在固定位置,以便APP调用。
5 F: y( H1 V1 }8 a( S如果没有使用RTOS,右边就不是获取,而是跳到APP任务,一单跳过去,CPU控制器就归APP了。 - 应用程序1 P+ W f/ N r7 m m) J& C
实现应用逻辑,通过调用底层接口控制硬件设备。
) B6 v3 j1 U8 y9 b O" }: Z* R 2 `. P2 |! O' @) Q2 T
关键流程* R0 k) |4 [* ` ~' K/ W0 j! y n
BOOT启动,校验固件,跳转还是更新。
% o4 l5 r. S+ g- Q5 BCORE运行,环境初始化,驱动APP任务。
( V z. H2 a' m* aAPP,其实就是一个函数。
3 t+ L: c0 H2 G* I4 C" s 实现& X9 B9 s: Q5 I( V$ l" L7 }7 n
只讨论基于RTOS的情况。/ |; j9 u! z/ j
硬件基于STM32F407。5 B' A; F: N' ^( Z' G
只讨论关键技术。
- P8 g. E0 W& R/ I 分散加载文件划分/ s0 }' {+ N/ y) ~! V- q2 u: t& ^
/ }/ R2 S; J, r- Y- H7 M/ zBOOT跳到CORE; I0 g g0 ]+ U4 C1 }
7 @8 j6 h& L; j0 \
CORE启动APP任务; Y, e: G; g" d
: `6 l, I9 ^- v! W0 s+ M驱动接口的定义
, s; m& S7 s, {% T2 Y: v# g& f( W1 t. }0 |; M8 ~
IAP |
谢谢,学习了。