每次写代码的时候,写外设的初始化都要去复制已经写好的或者是官方的例程,再根据自己的需求来修改,重复性的工作太多,就想到用C++来封装下STM32的库函数,顺便学习下C++,感受下面向对象的强大。4 N$ t. h4 }! S4 l' `4 G
硬件平台:STM32F4-Discovery
. \/ [" x% `8 A 开发平台:MDK5.14(本来是用VS2013 +VisualGDB的,想到坛友基本都是用MDK的,又改成MDK)
' ~/ X8 i( H( X2 e8 z" t# z% r: O* e 先看Main, |; M# ?1 T2 q+ X, W
- #include "system.h"
3 R4 N* B7 v6 r: a x -
; P9 G* N( R$ v2 g( Z5 }3 A - System *stm32f407;//系统接口指针
' o' Z" q5 W0 W: n3 r% ` - 6 N7 E) k4 ^/ ~1 E
- void btnhandle(void *arg) //按键回调处理函数7 Q2 I/ b) X. g8 f( g- d" F: r5 P
- {" L( A% }& a% u& Y' G! i! N
- delay->delay_ms(20);
2 I9 @ |. }- G: {; w$ |' P4 U* C7 }9 f - if (stm32f407->btn->ReadData() == true)
' b9 i* P# w( v - stm32f407->led2->Toggle();$ N4 L3 Q* D0 H" m+ F" L
- }) a. { {7 `4 S
- # p8 F P8 X: ?; a
- void tim6handle(void *arg) //定时器中断处理函数9 \- _' @2 T7 ]- _; N7 I
- {/ y5 @5 O1 f4 i* i0 G' _
- stm32f407->led2->Toggle();
# s# m3 Y2 s( u - }
p7 e* ]( |1 _( V - - z% C2 B5 _& a0 i, h
- int main()/ i9 g! W+ K/ g' I; r9 v( ~
- {
. _' Z7 Q$ E; W5 z9 [( L# `4 [ - stm32f407 = new System;//构建系统接口类- \2 Y' [, \3 F
- stm32f407->tim2->startIT((void*)tim6handle, NULL);//调用TIM类的中断启动函数,只要传入中断回调函数实现自己的功能,并不需要知道中断是要怎么处理的 c M1 ~; M* j5 T5 k) ^% M$ x- V) Z. c
- uint8_t recvdata[100];
9 S: o! J! r/ x9 J2 U - for (;;)! F$ V5 Z2 C; }' p+ w! h
- {$ N5 Q4 E8 A( y
- stm32f407->led1->Toggle();//在system中初始化,就可以直接调用类函数
* \) I* u3 J" B: L1 f4 p - if (true == stm32f407->Debug->IsGetRecv)//串口自动接收标志
) h- `8 \ ]* I! ?6 p - {
" x4 N, x2 j( O/ a5 t4 b7 R& e - stm32f407->Debug->RecvData(recvdata);//串口接收函数,传入Buffer,得到接收的数据+ u+ Y) E0 s. ^, I
- stm32f407->Debug->SendData(recvdata, stm32f407->Debug->RecvDataLen, 100);
4 ^7 b$ O( c0 p$ n2 q. v - }4 ?/ s7 @8 U0 f* t2 K T
- delay->delay_ms(500);5 @- d a/ j C) ]3 @' g
- }
1 d9 p& e+ N' h6 v" K+ U; T. M - }' c- T' D; f2 e! D
复制代码
[8 ]5 { \) @# n8 E 再看System类的初始化
. T. l! P( G. x- void System::init()) p7 R" R- Z1 _" _
- {
) R' G+ |5 A) j - HAL_Init();# G& v/ m* r' z" `6 h( y
- SystemClock_Config();5 Q! [6 b0 z% p7 R/ X
- //外设初始化0 R) p( Z# J4 A4 J; S `
- delay = new timerdelay; //系统延时初始
# e, W9 p+ r7 f5 } q2 H9 @ - Debug = new Usart(USART3, 115200, Usart::USART_IT, 200); //Debug 串口输出初始化,只要传入用的串口号,跟波特率,传输的模式,还有接收Buffer的大小,就可以完成初始化& y; q8 c. q+ i w+ {7 z h k9 W1 B
- led1 = new LED(GPIOD, GPIO_PIN_14, true, LED::highlevel);//LED类,继承OutputPort类,只要传入LED对应的GPIO,跟Pin,还有LED电平控制,就可以完成初始化) n. X a5 z0 M& k* T) L
- led2 = new LED(GPIOD, GPIO_PIN_12, false, LED::highlevel);
L: i5 Q' p6 v/ w( J9 S' h - btn = new InputPort(GPIOA, GPIO_PIN_0, GPIO_MODE_IT_RISING, (void*)btnhandle, NULL); //输入类,传入GPIO,PIN,中断触发模式,回调指针,回调参数,完成初始化
$ F8 S* J8 N0 [3 m - tim2 = new Timer_base(TIM2, 16800, 10000);
" @& ]! W5 y& Q8 r) K - BoardInfo();! I) B: [9 A' g9 X! I1 t
- }
复制代码
6 y, I4 K. J% a1 N9 V0 n( w
1 F& B4 i4 t" q& @/ n7 C& K* P+ D0 G, w/ H8 M8 g
OutputPort类:- class OutputPort
# m9 _/ N r7 v( C; l0 u - {
- {/ z+ v& I# C2 _% s - public:
6 _( t' Y! F% x+ f - OutputPort(GPIO_TypeDef* mPORT, uint16_t mGPIO_PIN, bool initialState);//OutputPort类构建函数,简单传入三个参数
! l6 [& f U( M$ y2 J9 n# ] - OutputPort(GPIO_TypeDef* mPORT, uint16_t mGPIO_PIN, uint32_t GPIO_Speed, uint32_t GPIO_PuPd, bool initialState);//构建函数重载,可以传入更多的参数
3 V! {$ g8 [" l- Q' Y3 s5 N4 h; O - ~OutputPort();1 v1 |5 Q( N7 h; {; u1 U
- void H(void);//类函数,构建的类都是调用同函数就可以实现,减少代码的重复% e4 X4 N# N1 s
- void L(void);: E3 e1 W9 z9 f$ b8 \6 W6 {
- inline void RegH(){PORT->BSRR |= GPIO_PIN; }//内联函数,直接代码替换成寄存器操作,提高执行效率: d8 m z* L2 X5 I
- inline void RegL(){ PORT->BSRR |= GPIO_PIN << 16; }, v/ v0 s9 C7 ~0 B( w. H
- GPIO_TypeDef* PORT;
& Z, |2 r8 P/ e0 k - uint16_t GPIO_PIN;: v6 {/ m0 e/ h" K* i( k
- private:5 S& P. h; g4 w; P
- protected:
* V+ _% T d/ o+ W - / n" O" Y+ [/ X8 d
- };* t0 z, t1 `" r5 Q9 F( M
-
4 S+ c. s! b' [6 L: Y: Y8 O: Y - LED类:
9 \* @- E4 y" ^3 s# o8 G - class LED :public OutputPort
0 l& Y: `# E' N8 F. L - {3 B- G8 I+ o, |: t1 i$ I7 d
- public:- n8 s4 `+ x2 G2 I
- LED(GPIO_TypeDef* mPORT, uint16_t mGPIO_PIN, bool initialState, uint8_t mCtlVolLev) : OutputPort(mPORT, mGPIO_PIN, initialState), CtlVolLev(mCtlVolLev){};//继承OutputPort类,并增加新的参数
+ k! w4 t6 C2 k. X - ~LED();. B n) j5 b9 ?7 }* [& `9 u
- void ON();8 J% i4 N- {1 y5 l
- void OFF();
I' S0 O V) k" E' C. { - void Toggle();
: o: Y8 j5 m) ]- e% O - enum{ lowlevel, highlevel };2 E3 N( w# i2 c/ V2 ^
- private:
, v [2 {( y. D# b8 ?0 ~ _. j - protected:
! x4 S1 w Q3 r( A9 ? - uint8_t CtlVolLev; //LED 1为高电平 0为低电平
; c3 q% j9 N/ [5 n1 F7 Y# ] - };
复制代码 ; f- U0 w9 t& b5 [, w P. D
t5 H/ v3 ?* k2 n
6 f7 y8 o5 R# Z! ~ UART类:: G8 m4 S" [, q' G @
- extern uint8_t UART_Recv_Timeout[5];//Time Tick
) ]. }" b n& ?6 c3 ? - * x: b' T/ U4 S# s/ C
- class Usart& }9 s' Y" J, T7 u8 Y7 T+ }+ ]
- {$ k7 o+ o9 s7 R2 G8 R7 E% j* q7 @& I
- public:
; W0 s2 }7 E) @2 g0 a+ L - enum tranmode
6 F( h0 ^4 o9 W7 a* {; s - {
2 Z% v* R) W7 {3 h! Y5 n - USART_Normal, //普通发送, x2 ^) b ?$ f4 I* ~* L, c
- USART_IT, //中断发送接收,接收自动判断超时完成接收" z3 t6 n; L' }7 r2 L% \
- USART_DMA //DMA发送接收,UART闲时中断自动接收DMA接收完成1 k2 o* R5 Q, c+ E$ b
- };//类构建时传入,类函数根据输入的类别发送接收( X R6 P: G- N, j% t* I
- Usart(USART_TypeDef *USARTx, uint32_t BaudRate, tranmode mmode,uint16_t mRecvBuffSize);. {+ S6 R' ], S4 E3 T0 P% n4 t2 T8 v4 Q
- void SendData(uint8_t data);7 k4 I$ I* D }8 c5 i
- void SendData(uint8_t *pData, uint16_t Size, uint32_t Timeout);. N. t( O7 P/ O) {6 s( q) p, U# r' Q
- uint16_t RecvData(uint8_t *pData);( u7 P4 _% r1 H5 c
- uint16_t RecvDataLen; //接收到数据长度0 g2 k4 `$ X' `6 Y1 C! a
- UART_HandleTypeDef UartHandle;
, l# c- {7 S& o3 v2 N4 I - uint8_t IsGetRecv; //接收标志
! o0 A7 s w( P3 v( K% g - uint8_t isSendOk ; //发送完成标志
! J, P f3 I3 [8 X9 S, |/ h) g - tranmode mode;
0 k7 C# n' j/ `; I d- T# W: A7 z& W - void Printf(const char* fmt, ...);
' ~6 A6 _2 o) t! h - ~Usart();" F2 x4 J) ^: X8 w3 Y, m+ _
- : a- y# C# s! ]8 y
- private:
! Y: H( Z8 C% W; ^- Q! o8 N& m7 | - uint16_t RecvBuffSize;
# I% n2 x3 K4 i. L( d4 B - uint8_t *RecvBuff;/ B# b* ]5 j2 _, m& r7 e
- uint8_t SendBuff[100];
% }0 K+ }9 ~% ?3 N- G8 B - void USART_DMA_Config(UART_HandleTypeDef *UartHandle);
7 D3 n( q: @, J6 O - };
0 i/ x/ S% O9 n( v -
9 a& J7 a5 s, ]7 W) C) f* x - void Do_UART_Recv(uint8_t i);//接收超时处理函数
复制代码 , Y- p2 X9 S0 m: g7 f$ j' v
# d1 I* G( W2 J" |
C++面向对象最大的好处是,只要定义一个类,相同的外设只要传入参数构建类,就可以调用类函数来实现功能,不需要为每个外设再重新写代码。而且封装好了类,只需要知道传入什么参数,可以调用什么接口来实现自己的功能就可以了,不需要再去了解类里面是怎么来实现的。目前就封装了GPIO,UART,TIM Base,SPI等几个外设,可以给大家体验下面向对象的强大,提供一些封装函数的思路,更具体的代码可以下载附件的工程。! L3 C2 |# {- G) J
STM32F4_Cpp.zip
(3.72 MB, 下载次数: 104)
|
未来的趋势就是单片机会越来越多的支持高级语言的开发,比如C#,ST官网也针对F4有例程,基于.NET MICRO FRAMEWORK的,最近一直研究,有兴趣,可以一起搞。
.NET MICRO FRAMEWORK我去年就有研究,写过一些文章,只能是玩玩,实际上没太多的胜任
较LINUX之类的系统,它把硬件层和应用层完全分开,只要写好CLR就可以。