
用C++写单片机程序 STM32F103 + 自建固件库 + FreeRTOS 用gcc编译器和newlib(1篇) 1 K" K, ?: k3 V e4 e, e* U 8 D/ l! j) P* |7 i 作为论坛的新人,先自我介绍一下。本人有10年的Win平台开发经验,从VS6.0一直用到VS2019,主要使用C#构建程序。在安徽某大学任教C语言程序设计7年,C语言自然是滚花烂熟。由于工作需要或者说是个人爱好,转战单片机。刚开始学习也是51+Keil用C语言开发,既然是学习实现的功能并不复杂,编写的代码量也不大,但还是体会到什么是一夜回到解放前。Keil的代码自动完成功能太弱鸡,如果做正式的项目效率会非常低。当时也只是想想而已,因为知道自己并不会去做实际的项目。之后当然就是学习STM32了,选择STM32主要是看中了中文资料多,学习人数多学习氛围好。STM32十足让我惊叹,比51复杂N倍的体系架构,超多的片上外设,还有如此低廉的价格。深入学习后发现还是Keil+C语言,至少市面上99%的学习都是基于这一框架的。非常的失望,非常的不适应。因为之前用C#开发Win应用,肯定是面向对象的开发思路,因为.NET Framework(相当于STM32的固件库)就是OO(面相对象)设计的,这么多年用下来,也深深的体会到OO的优势。不光是程序设计思路,还有就是IDE(集成开发环境)对开发者的重要性。我独自开发的Win应用最大的有10万行代码,我可以很轻松的构建,还能很轻松的完成代码维护,这里的代码维护指的是修订BUG,优化功能,增加功能,版本迭代等等。 $ O8 J4 W6 t8 f3 n" i4 ?9 O1 d STM32F103的主频已有72MHz,复杂的系统架构,超多的片上外设。想要喂饱这个MCU或者说是发挥出它强大的功能,肯定是需要大量代码的。当代码数量达到一定规模,自然要面对代码结构的问题。用Keil+C可以做,完成需要的功能肯定没有问题,而且现在绝大多数人都在这么做。但是C语言是面相过程的语言,对代码结构毫无帮助,毫无益处,因为无计可施。达到一定规模后,代码逻辑估计只有设计者很清楚,别人要想看懂是一件很耗费时间的事情。导致这一结果的原因是有大量的宏,更可怕的是宏嵌套、使用全局变量,这个变量会被多少函数共享,在什么时候会被改变分析起来很头疼、大量的函数,这些函数在语言层面是相互独立的,但逻辑上可能是有联系的,在语言级并没有这种逻辑关系的表达。就像官方的HAL库,我读起来想死的心都有。不是我读不懂而是读懂这些我付出的时间代价太大,要在OO思路下,这个要轻松N倍。时间就是金钱,效率就是生命。官方的HAL库其实就是想实现一个硬件抽象层,让开发者对于不同型号的MCU有一个统一的应用层开发界面,提高代码的移植性,缩短开发周期。想法是好的,做法是失败的,因为HAL库还是再用C语言编写,硬是用C语言套OO思路,代码恶心到极致。这也就是STD库还是有人在用的原因。如果使用真OO就可以把逻辑上有关联的函数组织到一起,将一些全局变量限定在部分函数中共享,其实这就OO中的封装,是语言层面提供的支持。还有要吐槽的地方就是IDE,VS的代码自动完成功能真的很好用,只要你按下任何按键都会出现提示列表,按下空格自动填充你还没有输入的内容,还有参数列表的提示。带来的好处就是,我不需要记住所有函数的名称和参数列表,我只要有一个模糊的映像,输入代码的时候IDE协助我完成。让我专注于逻辑思路,代码只是完成逻辑思路的工具而已。对语言再熟悉,对函数再熟悉,没有一个好的逻辑思路仍然写不出优秀的应用。不光是代码自动完成,还有代码着色,代码折叠,代码重构等等IDE功能,对于开发真的是不可或缺。Keil可能也有这方面的支持,但和其它一些IDE相比真的太弱了。再次重申一遍,时间就是金钱,效率就是生命。Keil+C可以吐槽的地方还有很多很多,我可以给你说一整天,我并不想开吐槽大会,只是想告诉大家其实可以有更好的选择。3 U% k( `+ i2 h* o 说一下代码效率问题,这可能是大家非常关注的地方。绝大多数人会认为C++的时间效率不如C,但是你要知道C++是C的超集,使用C++开发并不是什么地方都必须C++,你任然可以写C函数,任然可以写嵌入汇编代码。C++的效率损失主要是函数调用,C语言中会将参数压栈,而C++对象函数除了压栈参数以外还要压栈一个this指针,多一个32位的变量而已。表面上看是多了一个参数,但事实上呢?C语言函数可能需要4个参数,而对象函数的这个this指针却能代表这4个参数,因为C++通过这个this指针去访问私有变量,而这些私有变量恰恰是C函数的那4个参数。以上的说明可能过于抽象,你现在未必需要知道细节,你只要明白一点,效率的损失不是绝对的,有时却是一种效率的提升。再高级一点的效率损失来自于多态。在C99里面已有函数参数的多态支持,即使相同函数名但参数不同,调用时根据给定参数自动选择对应的函数。这种多态在C++里面也是存在的,但这种多态是在编译时完成的,对运行效率毫无影响。而虚函数多态的效率损失才是重点,当使用基类指针指向一个子类并调用虚函数,实际运行的时候会确定实际对象的类型去调用不同版本的虚函数,这需要在运行时查找虚函数表来完成,多了一个查找的过程,在内存中多了一份虚函数表。但是这种特性有助于实现一个优秀的代码结构或设计思路,用纯C可能实现不了或者实现了效率也未必比C++来的高效,而代码的逻辑和结构肯定没有C++的优雅,构建的速度也没有C++来的快。对于虚函数的采用是选择性的,没有什么项目在任何位置都需要虚函数。而一些普通的代码,例如:int a, b; a = 10; b = a + 200; if、for......不管是用C还是C++生成的汇编代码都是一致的。第二是空间效率,上面也分析了时间效率,所以空间效率可能有所损失,但也不是绝对的。当你用到一些特性的时候,执行的代码量必然增加,所以固件的体积肯定会有所增加。C++还有个STL库,里面实现了很多基础类,这些基础类可以直接使用或者进行再封装。当你使用这个库时,固件体积会极速增加。对于一些Flash只有64K或更小的MCU,可能有点紧张。但对于更大容量的Flash完全就不是问题了。使用这个库的好处就是,更少的BUG,更快的构建速度。这也是选择性的,你可以根据实际情况自由选择。而C语言中的常用库,例如printf、abs、floor等在C++中也是可以用的,且不会因为你是C++这些库函数就发生变化。总结起来就是可能会带来一些效率的损失,这些损失大部分是选择性的,当你选择牺牲性能的同时会得到C语言无法实现的功能,得到优雅的代码,得到清晰的逻辑,得到快速的构建。对于当今的MCU性能的损失往往都是可以接受的,你能不能喂饱单片机反而是个问题。当你在C++中需要极致性能的时候,你任然可以像C语言一样,该干嘛干嘛。 1 z' f! {6 E! A# ]" _) ?! q8 J 互联网上可以找到的资料实在是少之又少,断断续续搞了2年,现在已经完整构建了整个环境,打算写一个连载。这是第一篇,主要是阐述这种开发环境的优势。后面将陆续给大家分享开发环境的搭建,为什么要自建固件库,如何自建固件库,FreeRTOS如何与C++联合使用,newlib中如何实现printf重定向到串口,以及在实践过程中遇到的各种零碎的问题。随着芯片科技的不断进步,MCU性能的进一步加强,我相信这种环境将是未来的主流。也希望一些技术大牛不要再保守秘密了,把真正的技术都分享出来,彻底提升MCU编程。目前我已在用这个环境做实际的项目了,下面给一张开发环境的截图。能回复的回复一下啊,让我有写完整篇的动力。* [; B H5 Q: S/ j ![]() 9 R" M; @1 p, T |
分享一个: J# `8 R0 ~' D+ I% U
#ifndef RCC_HPP_
#define RCC_HPP_
' l9 Q4 g+ h6 c
#include "STM32F103.h"
8 F }5 L3 e& U/ S/ x
namespace MCU
{
namespace STM32F103+ d8 @9 L9 Y4 L8 \* x
{+ ]) m) `; g- H8 R) b& \# S% v1 d
struct RegisterRCC
{$ |( o/ ?' A$ z9 F+ Q
volatile union1 ]3 c8 O! U& E( Y
{: k* \2 F9 Z% z2 C
volatile uint32_t Value;
volatile struct: v0 @" h6 r7 z9 ?; s; z
{
volatile uint32_t HSION : 1;
volatile const uint32_t HSIRDY : 1;
volatile uint32_t RESERVED1 : 1;
volatile uint32_t HSITRIM : 5;2 l6 \. b, {/ |) o: K; h
volatile const uint32_t HSICAL : 8;' y( P8 ?$ x" }& a2 j3 F# {5 G
volatile uint32_t HSEON : 1;
volatile const uint32_t HSERDY : 1;$ x% v) H- Y9 O& `5 V3 B: w
volatile uint32_t HSEBYP : 1;
volatile uint32_t CSSON : 1;
volatile uint32_t RESERVDE2 : 4;
volatile uint32_t PLLON : 1;
volatile const uint32_t PLLRDY : 1;) }4 s, |( B* |; I2 \. E! _6 ?
volatile uint32_t RESERVDE3 : 6;
}Bit;
}CR;
5 J9 U- F' i* S+ y# w
volatile union' M0 N7 R1 ?6 L- e" k+ K. m
{
volatile uint32_t Value;
volatile struct2 C9 F- x3 ?/ ]1 ^9 n2 y' x
{. s& E- t+ t# p# C5 ?0 ?: G3 b
volatile uint32_t SW : 2;
volatile const uint32_t SWS : 2;
volatile uint32_t HPRE : 4;8 A* \3 n& O( K) x
volatile uint32_t PPRE1 : 3;
volatile uint32_t PPRE2 : 3;2 h$ x& K: W) a" @8 U0 x
volatile uint32_t ADCPRE : 2;
volatile uint32_t PLLSRC : 1;
volatile uint32_t PLLXTPRE : 1;* C& T6 w: \7 s6 @
volatile uint32_t PLLMUL : 4;& A4 Q/ t5 t+ l7 Y. b; s6 Q+ B
volatile uint32_t USBPRE : 1;
volatile uint32_t RESERVED1 : 1;7 R, I& L2 W+ R
volatile uint32_t MCO : 3;
volatile uint32_t RESERVED2 : 5;
}Bit;
}CFGR;$ C; [/ l* [ w0 [: z" O. j
. a$ m: G+ c9 u; m! M% \
volatile union
{
volatile uint32_t Value;
volatile struct5 e' T+ v! b! V* s7 G6 o
{1 j2 x+ m" H, o# k5 W; b
volatile const uint32_t LSIRDYF : 1;
volatile const uint32_t LSERDYF : 1;
volatile const uint32_t HSIRDYF : 1;
volatile const uint32_t HSERDYF : 1;5 y: {% s5 Z( u6 K8 [, R, E
volatile const uint32_t PLLRDYF : 1;9 I, i( f7 a7 I8 ]9 T% Y9 b1 V
volatile const uint32_t PLL2RDYF : 1;! i" l$ ~4 c6 L3 Z8 G4 u* o" y
volatile const uint32_t PLL3RDYF : 1;3 i! P! x4 Z) M; G. v' O# S
volatile const uint32_t CSSF : 1;
volatile uint32_t LSIRDYIE : 1;7 |- G- [; k1 n& F" E
volatile uint32_t LSERDYIE : 1;
volatile uint32_t HSIRDYIE : 1;
volatile uint32_t HSERDYIE : 1;
volatile uint32_t PLLRDYIE : 1;" [3 o g6 O1 L* g# i4 `
volatile const uint32_t PLL2RDYE : 1;
volatile const uint32_t PLL3RDYE : 1;
volatile uint32_t RESERVED1 : 1;
volatile uint32_t LSIRDYC : 1;/ ^- @9 K9 D" X4 t s: o% D- y% s
volatile uint32_t LSERDYC : 1;
volatile uint32_t HISRDYC : 1;8 o0 `4 k: o( U1 a
volatile uint32_t HSERDYC : 1;
volatile uint32_t PLLRDYC : 1; c2 Z- O& c4 L2 X
volatile uint32_t PLL2RDYC : 1;
volatile uint32_t PLL3RDYC : 1;
volatile uint32_t CSSC : 1;
volatile uint32_t RESERVED2 : 8;; D7 b/ V( g" Q
}Bit; ~2 u6 P1 l- n$ G) O( w* K
}CIR;
volatile union6 e, I2 H2 m( d
{
volatile uint32_t Value;
volatile struct# Z% z7 b; x7 l( s" N+ f
{
volatile uint16_t AFIORST : 1;
volatile uint16_t RESERVED1 : 1;
volatile uint16_t IOPARST : 1;
volatile uint16_t IOPBRST : 1;+ O" T; B) I. ^5 w. z5 T- W O
volatile uint16_t IOPCRST : 1;
volatile uint16_t IOPDRST : 1;; l) }9 ]( n) q
volatile uint16_t IOPERST : 1;
volatile uint16_t IOPFRST : 1;6 J& t: {0 Y/ {6 ]( Q6 E" l
volatile uint16_t IOPGRST : 1;
volatile uint16_t ADC1RST : 1;
volatile uint16_t ADC2RST : 1;
volatile uint16_t TIM1RST : 1;
volatile uint16_t SPI1RST : 1;9 \7 i" t+ Q6 m- q( T
volatile uint16_t TIM8RST : 1;
volatile uint16_t USART1RST : 1;
volatile uint16_t ADC3RST : 1;7 a4 a" Z6 _6 G2 G- k, }6 n- `
volatile uint16_t RESERVED2 : 16;3 s/ Z7 K& ?+ G! k, I
}Bit;1 e: g/ a2 X" Q# I3 C6 X
}APB2RSTR;5 x( f0 v8 l7 i$ N
( P% }# A( k; _
volatile union
{
volatile uint32_t Value;
volatile struct
{' a5 @' b" t0 N+ O
volatile uint32_t TIM2RST : 1;
volatile uint32_t TIM3RST : 1;# b% J$ X( g- s. d
volatile uint32_t TIM4RST : 1;4 { u+ Q- }" W7 S% b; v' y
volatile uint32_t TIM5RST : 1;, Z* S1 q3 r8 T! z
volatile uint32_t TIM6RST : 1;
volatile uint32_t TIM7RST : 1;
volatile uint32_t RESERVED1 : 2;0 N% T o6 K# R2 n. T& d
volatile uint32_t RESERVED2 : 3;
volatile uint32_t WWDGRST : 1;
volatile uint32_t RESERVED3 : 2;+ ~9 }" {" r/ v! Y" ]. c3 T" S
volatile uint32_t SPI2RST : 1;! j+ f8 O$ h0 {' S# T) p
volatile uint32_t SPI3RST : 1;
volatile uint32_t RESERVED4 : 1; G. P3 B* v0 y: z
volatile uint32_t USART2RST : 1;$ r+ [: ]: H/ I& a
volatile uint32_t USART3RST : 1;
volatile uint32_t USART4RST : 1;% B9 a @0 e. M' M. _
volatile uint32_t USART5RST : 1;
volatile uint32_t I2C1RST : 1;
volatile uint32_t I2C2RST : 1;+ \' D' Q; T; m1 a! ~. `
volatile uint32_t USBRST : 1;' c' n' K9 n/ n6 d+ e
volatile uint32_t RESERVED5 : 1;
volatile uint32_t CANRST : 1;
volatile uint32_t RESERVED6 : 1;2 K4 e( l' z( ]# X3 m! T z5 q4 o6 {
volatile uint32_t BKPRST : 1;
volatile uint32_t PWRRST : 1;1 v. m' D) |8 x' c
volatile uint32_t DACRST : 1;
volatile uint32_t RESERVED7 : 2;& F2 g z% g5 W) i. Q, F4 ^. e& A/ H. E
}Bit;
}APB1RSTR;
1 E. T# K6 q. f3 ]
volatile union
{
volatile uint32_t Value;# _0 z' X/ K. K4 Z% @% ~
volatile struct+ D- C+ F* {: O: `, m
{1 H h/ g E" U3 g7 h: D
volatile uint16_t DMA1EN : 1;
volatile uint16_t DMA2EN : 1;
volatile uint16_t SRAMEN : 1;8 Q w6 ?3 a) B
volatile uint16_t RESERVED1 : 1;% u* A6 m) N( }: |
volatile uint16_t FLITFEN : 1;2 f! Z# S" b# R9 y/ O' c
volatile uint16_t RESERVED2 : 1;! |; U/ t" J, ]4 e7 z
volatile uint16_t CRCEN : 1;
volatile uint16_t RESERVED3 : 1;
volatile uint16_t FSMCEN : 1;
volatile uint16_t RESERVED4 : 1;) ]2 J0 X" M6 j) s' E1 D- G+ k
volatile uint16_t SDIOEN : 1;
volatile uint16_t RESERVED5 : 5;
volatile uint16_t RESERVED6 : 16;! K' w$ s& f' `" h0 D
}Bit;/ h3 h, n, B2 d4 S) _( u
}AHBENR;* H" |0 }8 G+ G
volatile union
{
volatile uint32_t Value;5 M6 T) A+ x8 C
2 j; k0 p& o U9 i/ p
volatile struct( j; u( f, B; d+ B* o- l- a2 f
{6 h; u" o3 I) m/ k
volatile uint16_t AFIOEN : 1;2 B6 K V4 q8 O: J8 I& P) X! x
volatile uint16_t RESERVED1 : 1;
volatile uint16_t IOPAEN : 1;
volatile uint16_t IOPBEN : 1;
volatile uint16_t IOPCEN : 1;
volatile uint16_t IOPDEN : 1;
volatile uint16_t IOPEEN : 1;% R, U7 v g1 a, s
volatile uint16_t IOPFEN : 1;
volatile uint16_t IOPGEN : 1;
volatile uint16_t ADC1EN : 1;
volatile uint16_t ADC2EN : 1;7 ~# G1 l6 s7 L$ ~* y9 \
volatile uint16_t TIM1EN : 1;
volatile uint16_t SPI1EN : 1;& s; P* B7 O: u4 |" K O* T
volatile uint16_t TIM8EN : 1;: ]) b/ W; I0 B- f9 T/ ^2 x
volatile uint16_t USART1EN : 1; a) A" b5 l1 H) U
volatile uint16_t ADC3EN : 1;# Q* j0 ]! T3 h$ ~( F
volatile uint16_t RESERVED2 : 16;0 d, w! l$ s% n6 R% |0 K
}Bit;
}APB2ENR;% c! A& K8 L; \
; k) z4 R, z `# S
volatile union
{
volatile uint32_t Value;
volatile struct
{5 Z( y3 ^2 E# w
volatile uint32_t TIM2EN : 1;" q- G# f1 J5 o$ |
volatile uint32_t TIM3EN : 1;' I7 m' t1 V8 m/ ], ~
volatile uint32_t TIM4EN : 1;
volatile uint32_t TIM5EN : 1;
volatile uint32_t TIM6EN : 1;9 o& R9 t7 i: k5 C; u5 h S3 H
volatile uint32_t TIM7EN : 1;, \' J' C/ H6 H5 c4 D' q
volatile uint32_t RESERVED1 : 2;
volatile uint32_t RESERVED2 : 3;
volatile uint32_t WWDGEN : 1;
volatile uint32_t RESERVED3 : 2;
volatile uint32_t SPI2EN : 1;
volatile uint32_t SPI3EN : 1;
volatile uint32_t RESERVED4 : 1;
volatile uint32_t USART2EN : 1;* d/ F; R+ M' \. y
volatile uint32_t USART3EN : 1;4 _4 ?* U7 y8 p9 W; k n' o
volatile uint32_t USART4EN : 1;
volatile uint32_t USART5EN : 1;
volatile uint32_t I2C1EN : 1;
volatile uint32_t I2C2EN : 1;* B/ j9 ~5 n0 T) g, P
volatile uint32_t USBEN : 1;) J) | Q& M7 f. A) H) F6 r7 E
volatile uint32_t RESERVED5 : 1;
volatile uint32_t CANEN : 1;2 u* o7 Q5 U8 `" p; b
volatile uint32_t RESERVED6 : 1;+ H$ w) t3 N. y' J) h3 ^. ~
volatile uint32_t BKPEN : 1;$ `, g3 K9 }3 B) z+ [
volatile uint32_t PWREN : 1;
volatile uint32_t DACEN : 1;
volatile uint32_t RESERVED7 : 2;, H" _- l( u' S$ b5 {4 s
}Bit;
}APB1ENR;6 ]0 r1 h5 e: M+ @6 ~+ f/ z' \
volatile union! E; b0 t) @8 s* P% ]
{% Z a, Q! Z- S0 i: n
volatile uint32_t Value;
1 g( p1 [ @* B$ b
volatile struct A( L7 R5 ]5 ?( n. a! z
{
volatile uint32_t LSEON : 1;
volatile const uint32_t LSERDY : 1;7 S$ n; x2 S. y+ S4 c+ N4 Y5 E7 t* C
volatile uint32_t LSEBYP : 1;# j3 m+ z5 W- z0 i) g. u6 J5 `
volatile uint32_t RESERVED1 : 5;% k+ i! H) c" S3 C/ c7 b: b: K# U5 B
volatile uint32_t RTCSEL : 2;; {8 ?- n ]( z" Q3 G8 f
volatile uint32_t RESERVED2 : 5;
volatile uint32_t RTCEN : 1;
volatile uint32_t BDRST : 1;+ z' N% Y# c$ K! K$ S( p- R
volatile uint32_t RESERVED3 : 7;( F* Q( r3 x$ E+ C+ d
volatile uint32_t RESERVED4 : 8;
}Bit;* x# H) f4 u) m! d* {; a4 w7 `
}BDCR;5 s; p k( N4 v7 y
) Q& e* g6 k4 a5 X
volatile union
{
volatile uint32_t Value;
1 e- f: ^" u7 B5 S- ~
volatile struct
{
volatile uint32_t LSION : 1;. t/ z* l6 G8 k$ k; [7 ?- f
volatile const uint32_t LSIRDY : 1;
volatile uint32_t RESERVED1 : 6;
volatile uint32_t RESERVED2 : 8;
volatile uint32_t RESERVED3 : 8;" q S4 o3 n2 K" h
volatile uint32_t RMVF : 1;
volatile uint32_t RESERVED4 : 1;' R) }5 `5 j$ K/ Y) H; p2 h' U
volatile uint32_t PINRSTF : 1;
volatile uint32_t PORRSTF : 1;4 g v8 f& r, q# o" z2 q+ Q. k- w; N
volatile uint32_t SFTRSTF : 1;4 ^7 l8 f" S/ Y) B
volatile uint32_t IWDGRSTF : 1;
volatile uint32_t WWDGRSTF : 1;
volatile uint32_t LPWRRSTF : 1;
}Bit;
}CSR;; _) b$ z. b7 d' ^* ~
};: l+ Q% D' b, A! }; l- W8 A. \
5 N3 X1 A- R( T2 g: [
enum RCC_PLL_MUL
{
RCC_PLL_MUL2 = 0b0000,
RCC_PLL_MUL3 = 0b0001,
RCC_PLL_MUL4 = 0b0010,% M1 l- w9 H1 d; w M
RCC_PLL_MUL5 = 0b0011,+ C9 W( ?' {, }. n- B* H7 p$ `
RCC_PLL_MUL6 = 0b0100,
RCC_PLL_MUL7 = 0b0101,
RCC_PLL_MUL8 = 0b0110,
RCC_PLL_MUL9 = 0b0111,
RCC_PLL_MUL10 = 0b1000," ~- a4 G7 y6 E8 k* \
RCC_PLL_MUL11 = 0b1001,
RCC_PLL_MUL12 = 0b1010,
RCC_PLL_MUL13 = 0b1011,5 P* _ w% ~& Z7 s8 Q/ M! l
RCC_PLL_MUL14 = 0b1100,
RCC_PLL_MUL15 = 0b1101,) b; W9 |: O, M) X( I* m
RCC_PLL_MUL16 = 0b1110,( R8 y* y' f! T2 y
};- K9 z; u4 x7 I5 b
enum RCC_PLL_XTPRE- V/ F* p8 O: w% g
{: {* K9 l( X/ E7 L! W% P
RCC_PLL_XTPRE_DIV1 = 0b0,' }* _ l% ]" g) d0 l3 g% E' o
RCC_PLL_XTPRE_DIV2 = 0b1,
};; S5 G4 D; m" M( X. b) L
enum RCC_PLL_SRC2 S3 P: |" g! I+ ?4 |: ?* e* N
{
RCC_PLL_SRC_HSI_DIV2 = 0b0,
RCC_PLL_SRC_HSE = 0b1,
};
: f+ N! _9 L8 D. P
enum RCC_HPRE
{
RCC_HPRE_DIV1 = 0b0000,
RCC_HPRE_DIV2 = 0b1000,' N: }: Z; J/ V8 {" w1 A
RCC_HPRE_DIV4 = 0b1001,
RCC_HPRE_DIV8 = 0b1010,
RCC_HPRE_DIV16 = 0b1011,
RCC_HPRE_DIV64 = 0b1100,( S: H" B8 e" n% E5 n- J7 @1 `& m
RCC_HPRE_DIV128 = 0b1101,: F9 H" Y4 x& _# @. t) R
RCC_HPRE_DIV256 = 0b1110,8 w; a6 f" e8 t1 M3 U+ X) R
RCC_HPRE_DIV512 = 0b1111,
};
enum RCC_PPRE
{. y& H0 q8 l: v/ x5 |$ _0 L
RCC_PPRE_DIV1 = 0b000,6 h* E- g( ^- x
RCC_PPRE_DIV2 = 0b100,9 Y6 ~) }6 q+ p; Y) t7 A' n5 r
RCC_PPRE_DIV4 = 0b101, n S" [2 h+ O' o: y( o8 u
RCC_PPRE_DIV8 = 0b110,$ j& l* `# o1 O0 q& F
RCC_PPRE_DIV16 = 0b111,
};
1 K& {+ A5 q: _6 B! Q6 V
enum RCC_SW! W# c* d0 \- h
{0 N7 u: @. T4 \( @9 H6 |
RCC_SW_HSI = 0b00,0 x9 K2 Z7 E0 T D; L9 @0 K' {7 ^* B. l
RCC_SW_HSE = 0b01,$ n7 E. a$ S/ k5 P. [
RCC_SW_PLL = 0b10,
};+ Z5 _5 T q3 `' u
class CRCC
{
public:5 ~5 J* U9 }/ b( R
//保存外部晶体频率 在EnableHSE方法中初始化 用于计算系统频率* `' v4 T' P) N9 f
static uint32_t HSE;
6 }4 z8 ~1 N- Q
volatile struct RegisterRCC *pRegister = (volatile struct RegisterRCC *)(RCC_BASE);3 S& a' g! z2 o V- f) Z$ w
//开启外部高速时钟并等待时钟就绪,如果时钟不能就绪则一直等待
//HSE:晶体频率 单位:Hz2 s5 m1 I& p+ u6 \- Y0 u
void EnableHSE(uint32_t HSE)% t; N5 p' R0 {; @
{2 l+ i4 N5 s) U! [2 Z
CRCC::HSE = HSE;
3 t- C' P( N$ Q4 V' \ I( J, L6 ]
this->pRegister->CR.Bit.HSEON = 1;/ e; D+ u) B' R: R+ t
while(!this->pRegister->CR.Bit.HSERDY);
}
//设置PLL参数,必须在PLL关闭的状态下
void SetPLL(RCC_PLL_MUL MUL, RCC_PLL_XTPRE XTPRE, RCC_PLL_SRC SRC)
{$ ~ r! W0 T2 F R
this->pRegister->CFGR.Bit.PLLMUL = MUL;
this->pRegister->CFGR.Bit.PLLXTPRE = XTPRE;& b; ~0 s+ |* e0 e4 ` d' C$ r
this->pRegister->CFGR.Bit.PLLSRC = SRC;6 T' V; s7 x$ Q3 Y$ F4 I3 ]% t
}: M$ \8 K1 M2 I6 W9 L0 Q. f
//开启PLL时钟并等待时钟就绪,如果时钟不能就绪则一直等待
void EnablePLL(void)
{1 ]9 @! I$ v9 v- e D4 z' ?- |
this->pRegister->CR.Bit.PLLON = 1;
while(!this->pRegister->CR.Bit.PLLRDY);& F$ x- N( x; [' F4 G' T: w
}
, i: g# F7 t% z9 n
//切换系统时钟源并等待切换就绪,如果切换不能就绪则一直等待
void SetSysClkSource(RCC_SW SW)% x5 e0 f$ j6 u/ f- P4 k
{ }5 Q# V4 ?4 C- N& M# d5 g
this->pRegister->CFGR.Bit.SW = SW;
while(this->pRegister->CFGR.Bit.SWS != SW);& g# _9 V! ?) ? w" T4 d5 ^1 _
}
//获取系统时钟频率 单位:Hz: R O# H$ X# z' R Y4 e' c3 Z3 G/ Y
uint32_t GetSYSCLK(void)
{
if(this->pRegister->CFGR.Bit.SWS == RCC_SW_HSI)8 w ?% {! j4 C& N/ I
{/ s* u4 H) i" _" _6 _
//系统时钟为HSI$ g2 J& A( h- @+ W0 x: u4 n5 S
return 8000000;
}7 V7 t" |: D' Z; x/ K K; s
else if(this->pRegister->CFGR.Bit.SWS == RCC_SW_HSE)
{
//系统时钟为HSE9 Y9 f, u- g. ], s2 B% L
return CRCC::HSE;
}
else if(this->pRegister->CFGR.Bit.SWS == RCC_SW_PLL)( F" N# K% ^# b- ^. Y
{
//系统时钟为PLL
uint32_t SRC, XTPRE, MUL;2 ?& {: Y* n, A& d# B
if(this->pRegister->CFGR.Bit.PLLSRC == RCC_PLL_SRC_HSI_DIV2)
{
//PPL输入时钟源为HSI/2 _8 C' m3 W2 G/ l
SRC = 8000000;: A! R) C1 h0 f+ v, `! v- s/ [
XTPRE = 2;
}: o" g9 T- k: z6 F/ E3 R
else; c' f& X$ [) i( V( t5 r# t+ I6 u
{. B+ f6 t B! I2 ?4 |
//PLL输入时钟源为HSE
SRC = CRCC::HSE;5 i5 a/ C, ~1 [* P
& L4 }. a; U$ |/ S" B' R M7 B0 u
if(this->pRegister->CFGR.Bit.PLLXTPRE == RCC_PLL_XTPRE_DIV1)) Q+ x' X1 G! J' P R/ q
{4 |# g4 g& i8 B5 g: S& h5 R
//PLL输入时钟源为HSE/2
XTPRE = 1;
}
else
{
//PLL输入时钟源为HSE/2$ e _: n2 p: P# h0 @7 D
XTPRE = 2;- W2 v3 g0 A- q+ z1 d0 U* `
}
}: V2 b2 l! c8 A% {% F% P; Y
switch((uint32_t)this->pRegister->CFGR.Bit.PLLMUL)6 j" h9 R. O& y1 K4 Q
{
case RCC_PLL_MUL2: MUL = 2; break;
case RCC_PLL_MUL3: MUL = 3; break;
case RCC_PLL_MUL4: MUL = 4; break;
case RCC_PLL_MUL5: MUL = 5; break;
case RCC_PLL_MUL6: MUL = 6; break;, {% _2 w" `' v8 h5 F
case RCC_PLL_MUL7: MUL = 7; break;6 ^& j$ B: h2 ^! Q+ E I- ^* R) ^
case RCC_PLL_MUL8: MUL = 8; break;
case RCC_PLL_MUL9: MUL = 9; break;* H1 x1 D) X9 {8 n0 \' o& D
case RCC_PLL_MUL10: MUL = 10; break;
case RCC_PLL_MUL11: MUL = 11; break;
case RCC_PLL_MUL12: MUL = 12; break;
case RCC_PLL_MUL13: MUL = 13; break;
case RCC_PLL_MUL14: MUL = 14; break;7 x Y8 n$ f1 f Y
case RCC_PLL_MUL15: MUL = 15; break; u! s% ^1 _6 U' B& ^/ G
case RCC_PLL_MUL16: MUL = 16; break;
default: MUL = 16; break;8 w$ H% N- d3 V# Y
}
3 D* G8 \! p; @% @# i' Z
return SRC / XTPRE * MUL;2 L3 `6 S5 @: W; ~' j0 n7 C( h z
}
else* N$ y6 }+ k! G6 ]2 i4 S, R
return 8000000;( q) P1 [$ M! c! K2 i
}
/ P, Q9 p0 q8 @4 P9 f
//获取AHB总线频率 单位:Hz4 U* R0 N; A) r
uint32_t GetHCLK(void)! u% U: I# ?) s5 P% n
{5 A* d4 \) O( d: M% }* D3 ^1 R
uint32_t Prescaler;4 g$ k$ A& e4 Q2 |- W5 O
switch((uint32_t)this->pRegister->CFGR.Bit.HPRE)
{
case RCC_HPRE_DIV1: Prescaler = 1; break;
case RCC_HPRE_DIV2: Prescaler = 2; break;
case RCC_HPRE_DIV4: Prescaler = 4; break;) X( _0 ~: }) g# V' U
case RCC_HPRE_DIV8: Prescaler = 8; break;
case RCC_HPRE_DIV16: Prescaler = 16; break;
case RCC_HPRE_DIV64: Prescaler = 64; break;
case RCC_HPRE_DIV128: Prescaler = 128; break;
case RCC_HPRE_DIV256: Prescaler = 256; break;
case RCC_HPRE_DIV512: Prescaler = 512; break;* M1 s& L; u+ v' d& P' P, F
default: Prescaler = 1; break;$ i5 q, T8 z5 q9 ~9 I
}! |8 \, e* Y6 d/ I- d" P. F7 {& y
return this->GetSYSCLK() / Prescaler;6 m: h8 Y' A& V1 l6 v! L
}' h/ L; D3 j0 M* h# t
//获取APB1总线频率 单位:Hz( ^$ ]3 b' W R9 x4 r- r3 l
uint32_t GetPCLK1(void)
{
uint32_t Prescaler;; A2 r$ I, o0 g% I
switch((uint32_t)this->pRegister->CFGR.Bit.PPRE1)5 l( h6 U/ P/ _& b1 @
{
case RCC_PPRE_DIV1: Prescaler = 1; break;$ k* i' t* m' u% j; M% {8 l2 k
case RCC_PPRE_DIV2: Prescaler = 2; break;
case RCC_PPRE_DIV4: Prescaler = 4; break;# n! q4 z& }9 y/ F* h' q7 C
case RCC_PPRE_DIV8: Prescaler = 8; break;
case RCC_PPRE_DIV16: Prescaler = 16; break;: e9 W( u* [1 o3 H& p* p; c
default: Prescaler = 1; break;
}
( ]0 i7 e# r2 W' H1 y4 N
return this->GetHCLK() / Prescaler;+ S# a. m# ?0 B
}
9 l7 K$ B0 e; b% z0 y* y; l# G
//获取APB2总线频率 单位:Hz
uint32_t GetPCLK2(void), W6 S# a7 n4 ~5 G' R5 o, w! v' \
{
uint32_t Prescaler;' |: m0 m6 Z ]# q
& |' C; p/ d% j. o- ?- h
switch((uint32_t)this->pRegister->CFGR.Bit.PPRE2)( J3 }$ E- q& i4 s& U2 [
{
case RCC_PPRE_DIV1: Prescaler = 1; break;8 H: |- ~, d; d9 `; x' J. e
case RCC_PPRE_DIV2: Prescaler = 2; break;
case RCC_PPRE_DIV4: Prescaler = 4; break;
case RCC_PPRE_DIV8: Prescaler = 8; break;& |0 y: w" @/ S( n6 I$ w
case RCC_PPRE_DIV16: Prescaler = 16; break;
default: Prescaler = 1; break;) A. ?5 m/ G8 S3 ]; }% c& f
}
' ~0 x1 v! d, p6 B
return this->GetHCLK() / Prescaler;/ X2 o- ?, s/ Q5 }0 F7 |
}5 P0 L( @1 G- Z2 _% G
};
. c/ X7 n' L- C5 h, ?
uint32_t CRCC::HSE = 0;
}
}& r( d( U! P/ a8 `- `5 M
#endif // !RCC_HPP_
% r5 p" e; O( e, K
这是对RCC的封装,其实对寄存器的封装只是C++写单片机的小问题.还有几个问题要解决.1 r; @ e# t% E* e+ K/ t
第一:如何把中断连接到任何对象的方法,这个问题本质就是如何利用指针调用对象方法,实现后可以更好的发挥面向对象的多态思路,实现一些高灵活度的编程.我用模板类实现了一个类似C#中的委托对象,然后利用这个对象还可以实现类似C#中的事件,完成消息的多播.当然这是需要付出代价的,这个模板类要用到虚函数表,经过实测这个消耗是可以接受的,多1us左右吧.这个模板类的实现具体可以用Bing国际版搜索FastDelegate.
第二:如何new和delete,这个必须通过OS实现.我使用FreeRTOS,然后全局重载new和delete运算符到pvPortMalloc和pvPortFree.不过能定义声明的对象尽量定义声明,不要频繁new和delete因为FreeRTOS的内存管理还是很弱鸡的.实现的目的只是为了提高程序的灵活度.让一些代码可以更快地适配一些硬件.我通常都是只new不delete- ?+ l5 p+ w5 T4 [/ j( [' a6 v
为了更好更快地更换MCU,应该定义一个HAL层,里面全是虚基类,抽象类似但又不同的硬件.然后顶层基于HAL开发,底层继承HAL的虚基类并做具体实现.不过虚基类又牵涉虚函数表,会有性能损失.在对性能要求高的地方还是针对不同的硬件进行直接控制.# I4 `( x2 k# O7 W
为了更好的进行C++编程,我用gcc编译器.使用这个编译器时一定要注意,编译优化可能导致运行结果不正确,我通常是用O2或Os,一些特殊的函数还需要标记为O0.开发环境是Visual Studio 2019(个人版 免费) + VisualGDB(我买了正版).VisualGDB V5.5R2这个版本真是超级好用,自动完成,代码着色,代码格式化都是杠杠的.这个环境中在线仿真是ST-Link 2 + OpenOCD,可以设置端点,可以实时读取寄存器值,等等.' h, {( q( j! L/ a
7 W( L; v: _$ i% j( D' K
总之,C++配上这个开发环境生产力大大提高,代码的可复用性也大大提高.另外,我的项目已经投产了." j9 Y9 C. T$ r0 a5 J( K" ~7 S6 N
3 `) P' b, M. i2 W0 J0 ~
+ ~& u6 M- Q+ Q: n1 B |7 k/ w
我不是去开发一个IDE,而是用了别人很少用的IDE并且完全实现了我的想法。现在正在用这个IDE以及C++在做一个USB键盘(实际的产品)的项目。其实主要的动机来源于对USB的开发,真的是太复杂了不用C++很难解耦和重用。现在USB键盘已经接近尾声了,等我忙完我会继续写贴教大家怎么搭建开发环境,分享一些在C++过程中的心得。提前透露一下,我的键盘的USB枚举过程比海盗船K70快很多,K70是266ms完成,我键盘是176ms完成。海盗船单次获取设备描述耗时88us,我的键盘是61us,那些国产方案的键盘更是要230us左右。我购买一台USB协议分析仪测量的。虽然这个过程快一点对用户并没有什么卵用,但是说明使用C++程序效率并不会降低。
C#,C++面向对象被以前的那些所谓嵌入式大佬批得体无完肤!让他们得以在嵌入式上守住一片天地。4 S% p8 s" w5 r/ `
而C#,和C++也缺少实际的应用实例(嵌入式),而真正的即懂C#,C++又懂嵌入式的人,几乎没有!
老师望你在嵌入式C#,C++上开辟一片天地。7 q5 {3 q+ W8 U" q# \
还有!老师要实现你的愿景!可能需要一个团队,一个人可能难以完成!也可以把它做成一个项目,才可能有持续下去的源源动力。
个人认为: 关键在于有足够多的实际应用(嵌入式的各个功能)实例服众。/ \* z( C% y2 v
不知道ST自己的IDE(STM32CubeIDE)也是使用GCC编译器,不知道对楼主改用C++开发是否有用。
然后单片机项目主要是UI花费时间重复代码多,应用逻辑的话其实还好。
我整过一个2W行的单片机项目,2/3的代码量花在UI上了,