每次写代码的时候,写外设的初始化都要去复制已经写好的或者是官方的例程,再根据自己的需求来修改,重复性的工作太多,就想到用C++来封装下STM32的库函数,顺便学习下C++,感受下面向对象的强大。
- \* W; x9 | f6 ~4 N, K8 G 硬件平台:STM32F4-Discovery
; W) V" l9 m0 K* K; P 开发平台:MDK5.14(本来是用VS2013 +VisualGDB的,想到坛友基本都是用MDK的,又改成MDK)5 M6 _7 Q. E4 s+ H
先看Main+ _. L* Y/ S" F$ T
- #include "system.h"( K6 d. [' a/ `% d2 Q6 `6 b
- % {4 F' N# s: ]' `; z& m# Z. Y
- System *stm32f407;//系统接口指针
& Y" m$ t- u$ b% y! c( j3 I -
! H0 a& J! o% a3 l8 _/ D - void btnhandle(void *arg) //按键回调处理函数3 _! m/ V2 b2 ]
- {
- p& Y0 g, U _: T% }* p& v - delay->delay_ms(20);+ u: Y( R8 x0 o# N6 r0 B
- if (stm32f407->btn->ReadData() == true)
( j2 u! J W3 J/ M8 g2 C - stm32f407->led2->Toggle();
% ]) Z9 C3 w+ R4 U s1 Y( n% E1 \ - }, s% d/ u' `6 j/ a) w$ _3 ]0 x
- 2 w+ x/ P8 P5 t* h$ x5 n+ _
- void tim6handle(void *arg) //定时器中断处理函数( G0 g4 [- r A6 G6 Z& R
- {
F8 c, ^/ l4 p6 a5 \3 t - stm32f407->led2->Toggle();
+ @( g& j' [/ D6 _ V! \ - }
7 O1 M: B" K4 _- e" R -
; }, Z# S' K3 J- C- q - int main()
/ @4 h4 n3 [9 s- E1 ?! m; e8 b - {9 a/ }& @5 i( w( u! i9 b/ g( ?' U
- stm32f407 = new System;//构建系统接口类, u# l, X( p1 n
- stm32f407->tim2->startIT((void*)tim6handle, NULL);//调用TIM类的中断启动函数,只要传入中断回调函数实现自己的功能,并不需要知道中断是要怎么处理的. @' G) g H3 G) Q! a8 M! l
- uint8_t recvdata[100];
5 }& N# _# x- Y6 _1 @, y* x - for (;;); W1 G1 T" M6 R. y
- {
, y) [ |% _5 h2 S2 a; M2 H - stm32f407->led1->Toggle();//在system中初始化,就可以直接调用类函数
, f0 z7 P3 o& c f! F- X5 \ - if (true == stm32f407->Debug->IsGetRecv)//串口自动接收标志
# M5 v8 |8 `; v/ q2 Y! t - {
( A& G# a ]+ k1 Y - stm32f407->Debug->RecvData(recvdata);//串口接收函数,传入Buffer,得到接收的数据8 T$ y* d4 V' v
- stm32f407->Debug->SendData(recvdata, stm32f407->Debug->RecvDataLen, 100);
- z( v9 O1 ~6 d; ^4 R5 I: m - }2 U0 A0 O3 }1 f9 X m9 P& M7 Z
- delay->delay_ms(500);9 @3 K9 Y7 ^- Q# e' `1 e- B
- }
& i6 h. h- R" H, i" w; O - }$ X- |' N5 N/ E$ L+ n# l
复制代码 0 M* l# M' B3 m
再看System类的初始化
6 i3 N: I0 Q6 Z6 N, U0 h- void System::init()9 f1 ~; W0 Z6 q" y/ J4 U* u
- {% J/ b* { v8 ~1 p) k* I8 z& V
- HAL_Init();4 t. n& ^/ c2 e/ Z
- SystemClock_Config();. x8 `# @4 i6 d8 G* _
- //外设初始化" c3 K9 @5 _( z, q( n' }
- delay = new timerdelay; //系统延时初始
" [1 @8 K" A4 U$ k' e - Debug = new Usart(USART3, 115200, Usart::USART_IT, 200); //Debug 串口输出初始化,只要传入用的串口号,跟波特率,传输的模式,还有接收Buffer的大小,就可以完成初始化" C( n6 K2 k! t# W' O
- led1 = new LED(GPIOD, GPIO_PIN_14, true, LED::highlevel);//LED类,继承OutputPort类,只要传入LED对应的GPIO,跟Pin,还有LED电平控制,就可以完成初始化 z2 W' \% h9 F
- led2 = new LED(GPIOD, GPIO_PIN_12, false, LED::highlevel);- a* B7 q( b7 d/ }# w: J% r
- btn = new InputPort(GPIOA, GPIO_PIN_0, GPIO_MODE_IT_RISING, (void*)btnhandle, NULL); //输入类,传入GPIO,PIN,中断触发模式,回调指针,回调参数,完成初始化1 r' y1 M0 x2 [6 |& s% b
- tim2 = new Timer_base(TIM2, 16800, 10000);
4 n" S) Z j8 x8 s) D2 b; A7 h3 e - BoardInfo();' d4 {; V* {( N, a4 q
- }
复制代码
4 F% `9 A2 Z6 s" ~# Z3 N% D+ Z" f# `# A+ u
7 K6 ?: {6 G' F. M
OutputPort类:- class OutputPort( N0 x- }9 Z2 U6 o7 o6 X
- {; b+ l0 {% A! n: `+ d
- public:
/ F4 {. E. U: Z6 e# z - OutputPort(GPIO_TypeDef* mPORT, uint16_t mGPIO_PIN, bool initialState);//OutputPort类构建函数,简单传入三个参数! m3 j8 Y# u' y' q& C3 E0 N
- OutputPort(GPIO_TypeDef* mPORT, uint16_t mGPIO_PIN, uint32_t GPIO_Speed, uint32_t GPIO_PuPd, bool initialState);//构建函数重载,可以传入更多的参数
( [4 d% j0 X2 y, e. p# \ - ~OutputPort();
% G6 \" c/ g& G7 g - void H(void);//类函数,构建的类都是调用同函数就可以实现,减少代码的重复/ ]# J; I* F" Y. J
- void L(void);
4 b( Z& l( C. e* \+ ` - inline void RegH(){PORT->BSRR |= GPIO_PIN; }//内联函数,直接代码替换成寄存器操作,提高执行效率4 K8 E$ t: v# }8 x
- inline void RegL(){ PORT->BSRR |= GPIO_PIN << 16; }1 }6 u! T8 T( D! l
- GPIO_TypeDef* PORT;
& d0 n$ X' @9 v9 j - uint16_t GPIO_PIN;8 ~9 K# |+ w" J# ]$ J1 B2 R
- private:7 R+ i* R) u1 ^1 Y8 u
- protected:
- D# e( y( O% j- e - 1 @9 K8 F/ b: O9 K7 `9 k
- };
# K! k6 y, z$ S1 l# y/ h5 g -
* K" ~, D9 q; k$ C - LED类:
$ O* u5 s1 s8 N7 Y8 b0 | - class LED :public OutputPort
" E' M% q; d; h/ ~: x: n' \ - {; q9 `# K8 _/ x% }, d7 u7 `, A' {
- public:: ?+ m+ }! {% b0 p) V: A/ {1 t* w2 A
- LED(GPIO_TypeDef* mPORT, uint16_t mGPIO_PIN, bool initialState, uint8_t mCtlVolLev) : OutputPort(mPORT, mGPIO_PIN, initialState), CtlVolLev(mCtlVolLev){};//继承OutputPort类,并增加新的参数
0 f% i$ r: x. o% B& i - ~LED();
2 |: [, T3 b- j5 y. v: Z: f' X! F Y - void ON();2 c( s# e: _& {5 ?* s2 L
- void OFF();
5 ~3 U5 [5 W; g% q" g# L4 t5 J - void Toggle();5 s" k, t% r0 j
- enum{ lowlevel, highlevel };' B& M# \4 O! ]+ R/ k3 j
- private:" ^" \. R3 v' G4 `4 l7 ]; E
- protected:
- v$ |; `( S& `+ A3 P - uint8_t CtlVolLev; //LED 1为高电平 0为低电平* P: G$ _* V1 w$ ~
- };
复制代码
! R2 _9 K& V5 I8 I- j( N
+ D( `+ M: S2 |, I
# w; C3 g3 @. U. L UART类:( ?' H; A5 l+ P
- extern uint8_t UART_Recv_Timeout[5];//Time Tick
4 W8 ?8 ~$ b+ j- S8 g* J, a+ W/ x -
+ Y: U( X9 R0 G: P0 ^6 u4 I - class Usart
, [% p# r2 c% @4 x: s9 X - {( A9 e0 k2 O! j5 K
- public:' Y: R. J! j( ^; \9 _4 Q
- enum tranmode
; T/ @9 f; v$ p4 B0 H - {
& T! O! C% J+ t7 p - USART_Normal, //普通发送
6 {/ H$ l9 c/ t1 ]9 p - USART_IT, //中断发送接收,接收自动判断超时完成接收- V# }% T9 U: G7 _% Q) A4 z1 U
- USART_DMA //DMA发送接收,UART闲时中断自动接收DMA接收完成
/ U2 F" i& \% f U- ` - };//类构建时传入,类函数根据输入的类别发送接收
( \! I Q' Y) {6 z/ F# D' T - Usart(USART_TypeDef *USARTx, uint32_t BaudRate, tranmode mmode,uint16_t mRecvBuffSize);
8 q5 _% `. L% P/ ^0 H4 o; U( n - void SendData(uint8_t data);* z ?# B; i4 Y% p8 P+ @
- void SendData(uint8_t *pData, uint16_t Size, uint32_t Timeout); e f2 U+ b- l b" U- o
- uint16_t RecvData(uint8_t *pData);% T* l% _' q' t
- uint16_t RecvDataLen; //接收到数据长度
+ c" X* X! |* o# a - UART_HandleTypeDef UartHandle;
, @, H- a9 c x; d' ~$ X - uint8_t IsGetRecv; //接收标志
9 n7 f2 d. h, A+ g. Z4 Q - uint8_t isSendOk ; //发送完成标志: ^0 @- U5 J0 Q1 B3 I, j( i( u
- tranmode mode;
* I$ e) _2 E5 M5 o m - void Printf(const char* fmt, ...);% B o+ x% C# b0 q
- ~Usart();- |: j) T, Q9 @ U" d) Y* r
-
1 L0 L# o. j! y8 e, s- w; ~0 V, X - private:; f* e& ?1 }' \4 b9 I
- uint16_t RecvBuffSize;
7 C" Y# C6 L- E- a% l - uint8_t *RecvBuff;( ^. s2 }! [# N/ V8 a6 H1 S* _
- uint8_t SendBuff[100];* o+ M/ C5 _7 }" g
- void USART_DMA_Config(UART_HandleTypeDef *UartHandle);
- V, z- X" y; o - };
" X- t+ c- y( |6 w1 V1 R5 p4 G -
$ i2 X: U2 |, S - void Do_UART_Recv(uint8_t i);//接收超时处理函数
复制代码
; M$ g% j; T: X7 `0 |% W0 n$ u
3 c: U9 X( B) G C++面向对象最大的好处是,只要定义一个类,相同的外设只要传入参数构建类,就可以调用类函数来实现功能,不需要为每个外设再重新写代码。而且封装好了类,只需要知道传入什么参数,可以调用什么接口来实现自己的功能就可以了,不需要再去了解类里面是怎么来实现的。目前就封装了GPIO,UART,TIM Base,SPI等几个外设,可以给大家体验下面向对象的强大,提供一些封装函数的思路,更具体的代码可以下载附件的工程。
/ h; F5 W+ v+ z9 g( b# |; @2 w
STM32F4_Cpp.zip
(3.72 MB, 下载次数: 104)
|
未来的趋势就是单片机会越来越多的支持高级语言的开发,比如C#,ST官网也针对F4有例程,基于.NET MICRO FRAMEWORK的,最近一直研究,有兴趣,可以一起搞。
.NET MICRO FRAMEWORK我去年就有研究,写过一些文章,只能是玩玩,实际上没太多的胜任
较LINUX之类的系统,它把硬件层和应用层完全分开,只要写好CLR就可以。