每次写代码的时候,写外设的初始化都要去复制已经写好的或者是官方的例程,再根据自己的需求来修改,重复性的工作太多,就想到用C++来封装下STM32的库函数,顺便学习下C++,感受下面向对象的强大。7 |; \7 n2 M. i6 a1 y8 y$ J; p
硬件平台:STM32F4-Discovery
1 b i! d( Z( ]; n 开发平台:MDK5.14(本来是用VS2013 +VisualGDB的,想到坛友基本都是用MDK的,又改成MDK). b3 B, I0 r8 L9 x3 j) S. {; F
先看Main
. y2 Q. Y6 O. @0 X& t5 A - #include "system.h"2 y4 Q, K" D8 t9 H+ B$ O6 c
- 1 v, G& s( H/ N' i: R
- System *stm32f407;//系统接口指针
$ x0 p/ F- z' Q* T - 7 a k7 H. ^0 Z
- void btnhandle(void *arg) //按键回调处理函数" _2 u! M0 [' c r ~' s- r# Y0 H- }
- {. z7 b! k1 c, ^, b6 a, K
- delay->delay_ms(20);4 w+ W7 L5 C/ P, ^
- if (stm32f407->btn->ReadData() == true)6 ^) f% u! q9 U# Z9 P
- stm32f407->led2->Toggle();7 \) f- m$ q3 G6 j
- }2 e- d( n% W( b& v. ^4 z( c
-
2 {- h" S' @+ O* P1 D0 U; C! S - void tim6handle(void *arg) //定时器中断处理函数
' Y N) R6 p3 l9 G. O+ [ - {
7 ?: N1 e1 v# A - stm32f407->led2->Toggle();9 b- n8 c# k5 v9 u: q6 b
- }) A3 a5 t3 R0 p. |6 Q) a/ F
-
8 N% p, |* i6 R) c$ r- q6 T4 m - int main()
" B4 q; ~% |& |* T% Q7 |& f - {
i2 _! W2 o* N0 N - stm32f407 = new System;//构建系统接口类! k5 c. ?& ~/ P6 O, G2 ?
- stm32f407->tim2->startIT((void*)tim6handle, NULL);//调用TIM类的中断启动函数,只要传入中断回调函数实现自己的功能,并不需要知道中断是要怎么处理的6 V4 Q% i6 q, _2 R+ V
- uint8_t recvdata[100];: }3 c4 l3 e! i" ?
- for (;;)
- ]8 X9 M6 N i3 c$ _ - {, v$ N6 N+ ^* t; m. O- G
- stm32f407->led1->Toggle();//在system中初始化,就可以直接调用类函数
) U& ?2 l% P K Q. D' Z9 a7 O - if (true == stm32f407->Debug->IsGetRecv)//串口自动接收标志, r& F6 `( |6 E* d
- {8 ~/ i0 s+ ^) k* d
- stm32f407->Debug->RecvData(recvdata);//串口接收函数,传入Buffer,得到接收的数据& H5 Y2 o1 q( g7 \( Z3 i/ J
- stm32f407->Debug->SendData(recvdata, stm32f407->Debug->RecvDataLen, 100);
, Y0 q# J( R( D: q$ Y - }
3 L1 L5 D5 I2 q6 K - delay->delay_ms(500);# Z9 {' @9 }( X: j' |
- }
8 d5 A. p% Y0 o! Y" K( e* i6 N - }5 P0 I: N" q7 i: q" F# Q! _
复制代码
9 C* R7 B: p9 r7 ^; e 再看System类的初始化* @; ^2 U k: d/ q+ k% q
- void System::init()
" l+ B. t( p' s9 B" w! } - {5 z( e, A0 G+ g/ v% f
- HAL_Init();
; p( i; k6 x; O7 |- o8 h - SystemClock_Config();, ~% q5 l; |" k" O
- //外设初始化
( A, }& W8 e* Z, ?- j, p0 A9 T - delay = new timerdelay; //系统延时初始
1 c( J) Z+ E4 p - Debug = new Usart(USART3, 115200, Usart::USART_IT, 200); //Debug 串口输出初始化,只要传入用的串口号,跟波特率,传输的模式,还有接收Buffer的大小,就可以完成初始化3 V' g8 y9 e5 x% W) c$ B( [
- led1 = new LED(GPIOD, GPIO_PIN_14, true, LED::highlevel);//LED类,继承OutputPort类,只要传入LED对应的GPIO,跟Pin,还有LED电平控制,就可以完成初始化' X$ t$ E" |% j( z8 i. X. ^
- led2 = new LED(GPIOD, GPIO_PIN_12, false, LED::highlevel);- L! A/ Y& a# `- P+ m
- btn = new InputPort(GPIOA, GPIO_PIN_0, GPIO_MODE_IT_RISING, (void*)btnhandle, NULL); //输入类,传入GPIO,PIN,中断触发模式,回调指针,回调参数,完成初始化
5 [- k5 E4 l- X, I9 \& I - tim2 = new Timer_base(TIM2, 16800, 10000);
9 G# o4 n3 X/ e7 D% n. M, U" o6 I. B - BoardInfo();( a3 X0 A$ j7 g! e7 V+ s* P$ P4 I
- }
复制代码 3 h; B! @3 K' a- v* C
$ ^1 d }6 [ E7 j
! v4 F& i' ]7 ~" [) y3 a9 ]/ @
OutputPort类:- class OutputPort
7 o) j$ }: U4 E% Q. y - {
! ]7 R6 B- c0 I+ Y2 i' N3 u - public:: J7 ^" w) B& u
- OutputPort(GPIO_TypeDef* mPORT, uint16_t mGPIO_PIN, bool initialState);//OutputPort类构建函数,简单传入三个参数
& A r& _" E C) S - OutputPort(GPIO_TypeDef* mPORT, uint16_t mGPIO_PIN, uint32_t GPIO_Speed, uint32_t GPIO_PuPd, bool initialState);//构建函数重载,可以传入更多的参数
) P) m' X0 \' D# h9 W3 r - ~OutputPort();
' t8 I0 A6 K; ]* X1 }: r$ w/ X - void H(void);//类函数,构建的类都是调用同函数就可以实现,减少代码的重复
3 l+ N4 P: s4 w" C$ C( u7 ] - void L(void); s. I0 S! [. y$ ?' c
- inline void RegH(){PORT->BSRR |= GPIO_PIN; }//内联函数,直接代码替换成寄存器操作,提高执行效率7 }% f. g' p0 T2 Q
- inline void RegL(){ PORT->BSRR |= GPIO_PIN << 16; }' n4 x, j6 {2 @! r
- GPIO_TypeDef* PORT;
. F+ ~) x Q( n B! J4 C - uint16_t GPIO_PIN;
& C4 Y3 i( y* [ - private:
R9 c% w* X) @+ i0 s1 ` - protected:
+ z6 d4 M: L: e d" l, a; p8 h -
3 n" W1 I1 Y& w9 X( f3 I: u - };- u9 _3 B$ J% l" d& Y0 p' n
-
0 ]- V3 d6 }/ z6 `. }& B5 E7 `7 t - LED类:
& t& C* S6 ]4 w3 a8 c - class LED :public OutputPort
: ?# @; u+ i/ ^2 F - {
) a& o& Z3 i7 i: y7 b+ F+ O6 I - public:# T9 c4 Y. S+ D3 H' \3 r2 d1 y7 S! J
- LED(GPIO_TypeDef* mPORT, uint16_t mGPIO_PIN, bool initialState, uint8_t mCtlVolLev) : OutputPort(mPORT, mGPIO_PIN, initialState), CtlVolLev(mCtlVolLev){};//继承OutputPort类,并增加新的参数' X; y5 U, w+ w- M: b
- ~LED();) B1 e- h# O* m: X, B- ~
- void ON();
. T) m2 I g- B& {/ Y, S9 S$ g* E - void OFF();
- M' d [$ \: q O - void Toggle();- p4 O5 b4 h0 U
- enum{ lowlevel, highlevel };% F+ q5 r& l, N5 E. y
- private:
% ^& f5 H+ H8 {- \ - protected:8 K, L! j. v0 ], F4 L- i* W
- uint8_t CtlVolLev; //LED 1为高电平 0为低电平
2 U+ d4 i; v1 ~9 a4 Q9 M& t7 ] - };
复制代码
8 ^4 d w! J: `# n% [1 k" D {4 X* ]) [7 Q4 `7 D
" O/ }# h" [/ ^ W; q UART类:
# r- L/ Y9 f0 Q5 ?- e" ?! Q4 E- extern uint8_t UART_Recv_Timeout[5];//Time Tick8 Q: ~, E3 v7 A" l
-
& M" Z4 s1 q2 n5 S) a- v8 y - class Usart8 L: k- f. n. w. Y( t# y( J/ ]- L
- {6 [* ]) N6 q& Z+ ^2 X4 B% U
- public:
; P) h7 e! L+ J6 Q - enum tranmode
3 c! V- y9 y1 o% s8 A& D/ {1 k$ y/ w - {
" B/ S" y1 W8 j - USART_Normal, //普通发送
" ^% v8 V+ I! l9 L5 x# K* o7 l$ z- V - USART_IT, //中断发送接收,接收自动判断超时完成接收
; g( i- f; f* U0 F8 [3 w } - USART_DMA //DMA发送接收,UART闲时中断自动接收DMA接收完成8 `$ E; {6 t7 H- e' g
- };//类构建时传入,类函数根据输入的类别发送接收! _+ S' l( [# M# a
- Usart(USART_TypeDef *USARTx, uint32_t BaudRate, tranmode mmode,uint16_t mRecvBuffSize);
3 B! e! \% g9 ?3 |' y `# Q8 w - void SendData(uint8_t data);
1 N1 n: d" k0 `3 L - void SendData(uint8_t *pData, uint16_t Size, uint32_t Timeout);. e, F4 E- F) b7 T- T
- uint16_t RecvData(uint8_t *pData);7 _7 [) `# y3 o& D5 Q- U
- uint16_t RecvDataLen; //接收到数据长度6 S Q5 c/ }. `% q! A
- UART_HandleTypeDef UartHandle;$ A0 v# s& N9 s, ?( ]
- uint8_t IsGetRecv; //接收标志0 u6 @9 W: H6 f& r5 c8 H4 d d/ }6 C
- uint8_t isSendOk ; //发送完成标志
* n% ^& F/ r9 y& a6 A - tranmode mode;7 W9 ^: m) a2 { o4 _0 \- B
- void Printf(const char* fmt, ...);
- X5 k* ?" u! p0 k% {: _4 d - ~Usart();2 m9 y3 H& }$ K/ q; O5 C
- + L0 G! T9 C1 H) K3 F
- private:# ~, K; H, }/ g# `; u+ i# c
- uint16_t RecvBuffSize;1 h' `. X# t# U+ B/ a% O4 G5 X' n
- uint8_t *RecvBuff;
5 s+ C' D: I, l: `$ w' L6 g. w - uint8_t SendBuff[100];
, B# @0 A! w* M# ?. U& x - void USART_DMA_Config(UART_HandleTypeDef *UartHandle);
6 M+ B6 M6 y5 D: I% y& I7 h - };$ W: e1 [3 J' d; w
-
1 u/ `: m) Y$ _) s& W - void Do_UART_Recv(uint8_t i);//接收超时处理函数
复制代码 ( @- D$ @5 p! D" @% R! T
" ] D1 J. U9 j3 y2 J6 f( U9 t% Q
C++面向对象最大的好处是,只要定义一个类,相同的外设只要传入参数构建类,就可以调用类函数来实现功能,不需要为每个外设再重新写代码。而且封装好了类,只需要知道传入什么参数,可以调用什么接口来实现自己的功能就可以了,不需要再去了解类里面是怎么来实现的。目前就封装了GPIO,UART,TIM Base,SPI等几个外设,可以给大家体验下面向对象的强大,提供一些封装函数的思路,更具体的代码可以下载附件的工程。( \% S) e5 ?. |' n* `* k
STM32F4_Cpp.zip
(3.72 MB, 下载次数: 104)
|
未来的趋势就是单片机会越来越多的支持高级语言的开发,比如C#,ST官网也针对F4有例程,基于.NET MICRO FRAMEWORK的,最近一直研究,有兴趣,可以一起搞。
.NET MICRO FRAMEWORK我去年就有研究,写过一些文章,只能是玩玩,实际上没太多的胜任
较LINUX之类的系统,它把硬件层和应用层完全分开,只要写好CLR就可以。