每次写代码的时候,写外设的初始化都要去复制已经写好的或者是官方的例程,再根据自己的需求来修改,重复性的工作太多,就想到用C++来封装下STM32的库函数,顺便学习下C++,感受下面向对象的强大。
$ Q; v9 t% v' x+ P$ K1 {/ Q 硬件平台:STM32F4-Discovery$ [9 ~; B* H5 Y# `0 M F" c. e
开发平台:MDK5.14(本来是用VS2013 +VisualGDB的,想到坛友基本都是用MDK的,又改成MDK)" E. d& m+ ~0 j" H% K
先看Main
% l# F7 c4 k3 A- ] - #include "system.h"; T ~7 w. h3 Q ^3 a- i* r
- / e+ R" N3 [9 Y) q6 J2 X6 f
- System *stm32f407;//系统接口指针% o$ ]6 i6 U+ Q9 l$ d4 \
- : w0 ~& l$ a: }8 y/ C
- void btnhandle(void *arg) //按键回调处理函数
3 S$ ^ ^+ {! I+ [& G9 F$ x - {! Z9 Z' I- C1 |4 g
- delay->delay_ms(20);- ~8 u4 ]. Z& w1 P
- if (stm32f407->btn->ReadData() == true)2 m- N) }% c: ~; U: h
- stm32f407->led2->Toggle();5 |' i5 A5 m1 d1 g5 l0 r6 i
- }
6 m( Z! Q2 H' T6 f# |. [ - 6 F7 ]; s* a; c" ?8 u6 g4 w
- void tim6handle(void *arg) //定时器中断处理函数
8 I$ {. F3 @8 E0 \' j: w5 q. w - {
1 D2 h# S4 w5 @) b7 i2 w4 |5 D - stm32f407->led2->Toggle();
5 _4 P( [( {( P/ M0 ^1 f - }: d1 ^$ S& u7 ^) O. A
- & Y1 L: i% } X' a0 B
- int main()
6 y2 i8 c: N+ V/ x$ F# Z1 p7 T0 x - {
" |7 J9 N4 P2 y3 R) b+ S. f - stm32f407 = new System;//构建系统接口类; y# R7 g- d- u& o
- stm32f407->tim2->startIT((void*)tim6handle, NULL);//调用TIM类的中断启动函数,只要传入中断回调函数实现自己的功能,并不需要知道中断是要怎么处理的
9 q; J' U$ Z) m5 X* }6 ? - uint8_t recvdata[100];* ^2 H( Q4 o$ ~ K J/ r
- for (;;) Q0 C5 U) x* e4 @+ z
- {
3 p0 L" T5 V3 R( s6 y; G) N - stm32f407->led1->Toggle();//在system中初始化,就可以直接调用类函数
$ v9 ~. K9 T) }9 p8 Z, V - if (true == stm32f407->Debug->IsGetRecv)//串口自动接收标志7 H! ^% i" K) z1 H* U5 ^
- {
3 r; C6 j/ T- ] - stm32f407->Debug->RecvData(recvdata);//串口接收函数,传入Buffer,得到接收的数据4 c; ^ p9 n+ v3 k
- stm32f407->Debug->SendData(recvdata, stm32f407->Debug->RecvDataLen, 100);9 B' Q5 _8 c9 v9 k* Q: _
- }
0 @" v+ L+ m, @& f5 t - delay->delay_ms(500);
( s/ }- I3 K: N [$ r - }6 G* ?2 m/ D- D
- }
$ Q; b* V/ H& B
复制代码
0 N a: C4 R4 o* s 再看System类的初始化- P1 [6 k0 o9 ?3 t7 u# W
- void System::init()
$ c' W9 y6 w/ o; ?7 ^# n - {0 }0 [3 g1 q* J% I: S8 `
- HAL_Init();' \. d$ e3 v g
- SystemClock_Config();( r; R, O; ^9 ~- X3 \0 J% P+ P+ A
- //外设初始化
; s) P6 Y8 j2 }! {9 Q( m - delay = new timerdelay; //系统延时初始0 S1 B4 k9 o8 b
- Debug = new Usart(USART3, 115200, Usart::USART_IT, 200); //Debug 串口输出初始化,只要传入用的串口号,跟波特率,传输的模式,还有接收Buffer的大小,就可以完成初始化( ?( l! ]8 E. E6 [2 m7 I: R
- led1 = new LED(GPIOD, GPIO_PIN_14, true, LED::highlevel);//LED类,继承OutputPort类,只要传入LED对应的GPIO,跟Pin,还有LED电平控制,就可以完成初始化
# b1 L8 L' g: K4 L4 K: I3 s; | - led2 = new LED(GPIOD, GPIO_PIN_12, false, LED::highlevel);
3 h) [0 n) p2 C' ]- F - btn = new InputPort(GPIOA, GPIO_PIN_0, GPIO_MODE_IT_RISING, (void*)btnhandle, NULL); //输入类,传入GPIO,PIN,中断触发模式,回调指针,回调参数,完成初始化
4 X* p8 K3 n$ e2 O( Y0 Q$ E - tim2 = new Timer_base(TIM2, 16800, 10000);
: X- z. V" T0 _1 N& ^& R( f2 r - BoardInfo();
* r8 ~ t5 { F9 {( i6 M7 w - }
复制代码
3 V* E* s0 K/ e+ F, g/ w8 C$ s0 W4 T9 S
1 V) w9 A5 `! z- A
OutputPort类:- class OutputPort4 U) V' U* h! q$ V8 j9 I
- {- } G: k! K4 p/ J' o
- public:
0 [ \6 E h& @% T* m+ ? - OutputPort(GPIO_TypeDef* mPORT, uint16_t mGPIO_PIN, bool initialState);//OutputPort类构建函数,简单传入三个参数
+ S2 o6 X2 r* q - OutputPort(GPIO_TypeDef* mPORT, uint16_t mGPIO_PIN, uint32_t GPIO_Speed, uint32_t GPIO_PuPd, bool initialState);//构建函数重载,可以传入更多的参数
0 O1 L. W( W2 H# [' s$ @ - ~OutputPort();
' I/ l/ u3 c2 M3 t/ G9 ]. C - void H(void);//类函数,构建的类都是调用同函数就可以实现,减少代码的重复
- l# g$ l0 D- A9 h - void L(void);6 R3 U6 h) c, w- w% f1 g' S/ D
- inline void RegH(){PORT->BSRR |= GPIO_PIN; }//内联函数,直接代码替换成寄存器操作,提高执行效率' c, ]9 j ?2 v( Q6 T9 N& k
- inline void RegL(){ PORT->BSRR |= GPIO_PIN << 16; }
$ C* p! K, T2 S0 K# d - GPIO_TypeDef* PORT;3 v8 d# i+ M. g$ L4 r9 g( a
- uint16_t GPIO_PIN;
2 x/ A6 |( k8 j; N2 U - private:5 V: Q5 J: V. F2 T* j
- protected:
! @! G% p) S. K. I/ Y - 5 ^8 _- x& }! l% `
- };
1 s9 X# k; q" o+ ~: f) R% H' | - 7 O9 Q# e- e1 U$ Q% W5 r2 }+ O* K
- LED类:
) U3 y8 c; w( Q, H1 H8 J4 b L - class LED :public OutputPort
, t5 \: [# U A% A - {
: [: E2 \: l4 c& ?2 m* i0 ]- _ - public:' r d& {$ ^ {3 ]/ ]+ V v6 h
- LED(GPIO_TypeDef* mPORT, uint16_t mGPIO_PIN, bool initialState, uint8_t mCtlVolLev) : OutputPort(mPORT, mGPIO_PIN, initialState), CtlVolLev(mCtlVolLev){};//继承OutputPort类,并增加新的参数) c1 _) @* r9 O$ ^
- ~LED();2 a; X% I8 X* U8 Q4 x
- void ON();. |4 Z* Q; d: ]' E
- void OFF();
8 E3 i3 W% N* \3 H+ v$ A - void Toggle();9 O: C: L1 a$ x/ @6 P
- enum{ lowlevel, highlevel };: [2 W, N4 X8 l1 R6 G$ A
- private:% @% P1 Y& ~6 Z
- protected:+ W8 ?. E3 l5 `& G5 F
- uint8_t CtlVolLev; //LED 1为高电平 0为低电平
: O9 C; c4 I+ w% z - };
复制代码 F% S% h7 }' x6 d+ P2 g# K8 L
- G# ^: o) T! n' u* U- S
+ b) ` H" U1 ~1 F3 S$ r; t
UART类:% C4 f+ M+ q( w; A0 Y! w
- extern uint8_t UART_Recv_Timeout[5];//Time Tick- U8 b5 S% [! a8 v7 X2 h
-
4 N3 ?' k" y. O7 m, `9 z - class Usart
* S4 [. ]* D: ~" _ - {2 z( U9 [: I2 U! q
- public:
7 I6 x+ w4 u" n5 V+ Q6 ~9 d7 r - enum tranmode8 z& q% r8 g+ q! z
- {
0 ^' g0 Y9 F! `3 w1 p5 Y - USART_Normal, //普通发送
6 Y: ?- o0 v: B8 a - USART_IT, //中断发送接收,接收自动判断超时完成接收
0 _0 K. Z& U- Z+ E - USART_DMA //DMA发送接收,UART闲时中断自动接收DMA接收完成2 L* n/ O4 S+ Y4 U8 t: a$ Q& D
- };//类构建时传入,类函数根据输入的类别发送接收* T3 O& h7 f* u+ P5 ] ]
- Usart(USART_TypeDef *USARTx, uint32_t BaudRate, tranmode mmode,uint16_t mRecvBuffSize);2 i5 D4 F0 I5 [* f
- void SendData(uint8_t data);
' u- m: o$ k% w m1 c& M - void SendData(uint8_t *pData, uint16_t Size, uint32_t Timeout);7 A% e; H; j9 S. F, w- _
- uint16_t RecvData(uint8_t *pData);/ \! x [, }; @$ @
- uint16_t RecvDataLen; //接收到数据长度
2 Z+ i3 q- |5 r- V - UART_HandleTypeDef UartHandle;+ V# I" z7 y! D8 S& O: i, v" ^
- uint8_t IsGetRecv; //接收标志
3 {9 U3 H1 I, Q8 ?2 | - uint8_t isSendOk ; //发送完成标志" j6 Q5 g% R. y) j- \0 h
- tranmode mode;7 \0 \" Z1 \& }% ]5 O4 s
- void Printf(const char* fmt, ...);; e8 T, Y: |! R
- ~Usart();6 P, Y; Z/ q9 U. Z0 ]$ e
- $ R I' O1 X8 Z0 k
- private:2 e# g# f/ c# ?& s
- uint16_t RecvBuffSize;
6 Z: l3 z- h; s# b& |; I- f: W - uint8_t *RecvBuff;/ N9 X" o1 S2 g2 T
- uint8_t SendBuff[100];2 a! l( [0 G! y. ~+ @6 e9 |/ K9 T
- void USART_DMA_Config(UART_HandleTypeDef *UartHandle);
% H3 R1 S1 C- K: g - };+ k- D3 m7 l; c2 s Q
-
' T" j+ U- m; F* Y* k6 Q - void Do_UART_Recv(uint8_t i);//接收超时处理函数
复制代码
) u4 V* u6 n$ X7 F6 W; [& k! `' N3 `( U+ r8 N6 Z3 I- ~+ { Q
C++面向对象最大的好处是,只要定义一个类,相同的外设只要传入参数构建类,就可以调用类函数来实现功能,不需要为每个外设再重新写代码。而且封装好了类,只需要知道传入什么参数,可以调用什么接口来实现自己的功能就可以了,不需要再去了解类里面是怎么来实现的。目前就封装了GPIO,UART,TIM Base,SPI等几个外设,可以给大家体验下面向对象的强大,提供一些封装函数的思路,更具体的代码可以下载附件的工程。, e7 h' v. b- S9 ]
STM32F4_Cpp.zip
(3.72 MB, 下载次数: 104)
|
未来的趋势就是单片机会越来越多的支持高级语言的开发,比如C#,ST官网也针对F4有例程,基于.NET MICRO FRAMEWORK的,最近一直研究,有兴趣,可以一起搞。
.NET MICRO FRAMEWORK我去年就有研究,写过一些文章,只能是玩玩,实际上没太多的胜任
较LINUX之类的系统,它把硬件层和应用层完全分开,只要写好CLR就可以。