
用C++写单片机程序 STM32F103 + 自建固件库 + FreeRTOS 用gcc编译器和newlib(1篇). k: b' _) ]& ]" x 作为论坛的新人,先自我介绍一下。本人有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,优化功能,增加功能,版本迭代等等。 6 @7 r) n6 V+ K# R/ K4 J 5 L& b# [% L1 W1 q6 f7 o+ p 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可以吐槽的地方还有很多很多,我可以给你说一整天,我并不想开吐槽大会,只是想告诉大家其实可以有更好的选择。) |( F: I5 Z* \2 L7 P6 c 1 r& R+ O b [! X9 l3 ?9 j : {8 H$ c' _9 K1 H+ _$ A1 p 说一下代码效率问题,这可能是大家非常关注的地方。绝大多数人会认为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语言一样,该干嘛干嘛。# {. U* p6 F2 \* {5 I9 r 2 A$ m4 c7 i% F3 B 互联网上可以找到的资料实在是少之又少,断断续续搞了2年,现在已经完整构建了整个环境,打算写一个连载。这是第一篇,主要是阐述这种开发环境的优势。后面将陆续给大家分享开发环境的搭建,为什么要自建固件库,如何自建固件库,FreeRTOS如何与C++联合使用,newlib中如何实现printf重定向到串口,以及在实践过程中遇到的各种零碎的问题。随着芯片科技的不断进步,MCU性能的进一步加强,我相信这种环境将是未来的主流。也希望一些技术大牛不要再保守秘密了,把真正的技术都分享出来,彻底提升MCU编程。目前我已在用这个环境做实际的项目了,下面给一张开发环境的截图。能回复的回复一下啊,让我有写完整篇的动力。 ![]() |
分享一个
; A' t' i6 v2 n5 z+ {
#ifndef RCC_HPP_
#define RCC_HPP_
9 w* M$ i) R) v
#include "STM32F103.h" u. T5 Y, [* G# P. {$ W
namespace MCU
{
namespace STM32F103
{
struct RegisterRCC
{
volatile union1 @; s) ^' L: X- K3 ]- ]) y4 f
{! s2 ^ Q, O( H# K
volatile uint32_t Value;0 f3 S( Z8 k! }4 I: [2 }3 Z
volatile struct _% L9 k: q/ q" I
{
volatile uint32_t HSION : 1;
volatile const uint32_t HSIRDY : 1;
volatile uint32_t RESERVED1 : 1;
volatile uint32_t HSITRIM : 5;- p4 M/ y5 X3 X4 A8 |4 J
volatile const uint32_t HSICAL : 8;2 m2 X1 u( l/ o' O9 M6 ?- t
volatile uint32_t HSEON : 1;/ V% n2 D0 Q1 h) U2 L! G
volatile const uint32_t HSERDY : 1;
volatile uint32_t HSEBYP : 1;( k. E/ s8 w' ]4 i5 ^6 s% r# ?8 R
volatile uint32_t CSSON : 1;" J8 }* X* v, F6 J
volatile uint32_t RESERVDE2 : 4;
volatile uint32_t PLLON : 1;
volatile const uint32_t PLLRDY : 1;
volatile uint32_t RESERVDE3 : 6;8 y/ r8 A& Z ~ }% n
}Bit;1 {) S4 N- v/ ?
}CR;
$ @. l$ K3 W% N) v8 T
volatile union
{4 X" o* _- X3 r7 ]% N, _, N) j
volatile uint32_t Value;
8 M1 V7 h# m& y$ _$ T' E: j
volatile struct1 h( w- @; J% O0 ?: V9 P) B
{4 |3 [- s( P! u; D) i4 i+ y5 w
volatile uint32_t SW : 2;) L- i1 h& H3 ~1 ~6 h k
volatile const uint32_t SWS : 2;' g6 R9 ]4 i7 G9 C/ L& q% O
volatile uint32_t HPRE : 4;
volatile uint32_t PPRE1 : 3;
volatile uint32_t PPRE2 : 3;0 A+ W: s8 u) R3 \4 D8 K1 T
volatile uint32_t ADCPRE : 2;. l! @# `( \( I0 j" H# h
volatile uint32_t PLLSRC : 1;+ y3 ?6 V9 x; ~$ m" ~9 O6 Q
volatile uint32_t PLLXTPRE : 1;
volatile uint32_t PLLMUL : 4;
volatile uint32_t USBPRE : 1;
volatile uint32_t RESERVED1 : 1;6 a8 J2 t9 J8 k' V
volatile uint32_t MCO : 3;, o+ U$ L8 O" r" y) r
volatile uint32_t RESERVED2 : 5;' Y4 w4 H1 h4 D+ ^. E
}Bit;
}CFGR;! u- O- n$ K# l B6 O
volatile union! e* \6 F+ V# C" M. y" T
{
volatile uint32_t Value;, G, h! S# W9 E+ ]
volatile struct
{
volatile const uint32_t LSIRDYF : 1;- f3 T9 z' m! F$ b6 \1 X! T
volatile const uint32_t LSERDYF : 1;
volatile const uint32_t HSIRDYF : 1;
volatile const uint32_t HSERDYF : 1;
volatile const uint32_t PLLRDYF : 1;3 I3 g8 c6 n: R
volatile const uint32_t PLL2RDYF : 1;2 q0 G2 y" f5 t* q$ v V
volatile const uint32_t PLL3RDYF : 1;
volatile const uint32_t CSSF : 1;3 I2 P4 x5 b3 j
volatile uint32_t LSIRDYIE : 1;8 Y6 _5 z, |& n
volatile uint32_t LSERDYIE : 1;9 F# T- |. {' ?% H8 ^! l
volatile uint32_t HSIRDYIE : 1;
volatile uint32_t HSERDYIE : 1;9 H: k, y! h+ s s
volatile uint32_t PLLRDYIE : 1;
volatile const uint32_t PLL2RDYE : 1;
volatile const uint32_t PLL3RDYE : 1;
volatile uint32_t RESERVED1 : 1;# R& @/ W" e! \0 R( n3 g2 |" A
volatile uint32_t LSIRDYC : 1;. F; p( }% V7 Z3 @' ^/ I* x0 D
volatile uint32_t LSERDYC : 1;
volatile uint32_t HISRDYC : 1;
volatile uint32_t HSERDYC : 1;
volatile uint32_t PLLRDYC : 1;! Z# S6 @/ L' s8 o
volatile uint32_t PLL2RDYC : 1;, R8 R1 [0 y5 y/ G' A5 x
volatile uint32_t PLL3RDYC : 1;
volatile uint32_t CSSC : 1;
volatile uint32_t RESERVED2 : 8;
}Bit;2 F6 s' [& I! ~8 S8 J+ E1 E
}CIR;2 {$ l- q) X' Z) i* e, d! P
volatile union9 R2 j: U0 N/ S9 q# g" G
{
volatile uint32_t Value;
volatile struct) F" |# Q" K( @ V+ k1 c
{) S- ]4 m2 @- y" j! y, x5 j
volatile uint16_t AFIORST : 1;9 s6 N4 l: e& w6 i! B4 L0 K& _
volatile uint16_t RESERVED1 : 1;: [8 H2 ?5 e- r) z
volatile uint16_t IOPARST : 1;8 @: u' V/ h0 L/ \# r! \, g* D
volatile uint16_t IOPBRST : 1;, [( f( S3 P3 ^( k6 v7 A$ I# u
volatile uint16_t IOPCRST : 1;/ H: P( u4 V. J$ C" G9 f6 F
volatile uint16_t IOPDRST : 1;
volatile uint16_t IOPERST : 1;
volatile uint16_t IOPFRST : 1;
volatile uint16_t IOPGRST : 1;
volatile uint16_t ADC1RST : 1;
volatile uint16_t ADC2RST : 1;
volatile uint16_t TIM1RST : 1;9 K9 Q5 S W& a# \2 R- a4 G0 M: v- s
volatile uint16_t SPI1RST : 1;
volatile uint16_t TIM8RST : 1;3 H, d: l) c/ z% E) g( \
volatile uint16_t USART1RST : 1;
volatile uint16_t ADC3RST : 1;
volatile uint16_t RESERVED2 : 16;
}Bit;
}APB2RSTR;
# k. J: v4 z0 q ]5 \
volatile union
{
volatile uint32_t Value;8 d4 y# l" y' h/ B" U! B
volatile struct
{
volatile uint32_t TIM2RST : 1;7 [" P. _, Y. J- \) ^* n- S/ F
volatile uint32_t TIM3RST : 1;1 U) h" N7 f' g b, K5 O, M9 _# f
volatile uint32_t TIM4RST : 1;
volatile uint32_t TIM5RST : 1;6 I1 u. ?: y5 h+ s
volatile uint32_t TIM6RST : 1;
volatile uint32_t TIM7RST : 1;; w8 ^' f X) C0 s9 E; g
volatile uint32_t RESERVED1 : 2;, x9 F% e' c9 h; S
volatile uint32_t RESERVED2 : 3;2 ~* \ b4 g9 E- k+ ]7 W" X5 d0 k! A& K
volatile uint32_t WWDGRST : 1;; O! Q2 T0 Q" C- O i6 k9 N1 g- E
volatile uint32_t RESERVED3 : 2;
volatile uint32_t SPI2RST : 1;
volatile uint32_t SPI3RST : 1;
volatile uint32_t RESERVED4 : 1;' x" V( a! K- v4 k! Y6 S2 \4 a. g
volatile uint32_t USART2RST : 1;; K7 B: b0 x; u$ l% s7 \
volatile uint32_t USART3RST : 1;% r0 b {6 C: J( o9 f% [4 j
volatile uint32_t USART4RST : 1;! Z. d& H" Q5 v+ U; Q. E* Y
volatile uint32_t USART5RST : 1;/ Q" W- {, Z# n& u
volatile uint32_t I2C1RST : 1;
volatile uint32_t I2C2RST : 1;
volatile uint32_t USBRST : 1;
volatile uint32_t RESERVED5 : 1;6 v P& M1 ^0 O1 W+ b. Q$ R
volatile uint32_t CANRST : 1;
volatile uint32_t RESERVED6 : 1;9 R4 v2 I- B: h! n5 G
volatile uint32_t BKPRST : 1;
volatile uint32_t PWRRST : 1;$ P. [3 n! E$ ^
volatile uint32_t DACRST : 1;- K* u. D/ l. b- m3 v, q
volatile uint32_t RESERVED7 : 2;2 b& X* q6 a1 ]- S1 Y
}Bit;
}APB1RSTR;2 a* T( n8 H8 f+ T# w( q
volatile union
{% K, Y+ V8 v. q3 F' {5 V
volatile uint32_t Value;$ M# c# ~' X6 W. I/ [/ L: q* p
volatile struct
{- W7 F& s4 \# Q5 T# k8 |* u5 F
volatile uint16_t DMA1EN : 1;
volatile uint16_t DMA2EN : 1;
volatile uint16_t SRAMEN : 1;, M4 X, K2 C [ c0 b$ [
volatile uint16_t RESERVED1 : 1;
volatile uint16_t FLITFEN : 1;% r# L7 w3 b9 u, \' C, c, u6 L
volatile uint16_t RESERVED2 : 1;
volatile uint16_t CRCEN : 1;9 o! }, t* D# f" `5 t8 Y; T
volatile uint16_t RESERVED3 : 1;7 p. \# z( N2 p! O0 i0 S
volatile uint16_t FSMCEN : 1;' T" G2 T! V9 L3 p" U4 W
volatile uint16_t RESERVED4 : 1;
volatile uint16_t SDIOEN : 1;/ W* z) r3 k" |7 O% n0 T" x% A
volatile uint16_t RESERVED5 : 5;. @, e. C3 ?6 i2 M. L
volatile uint16_t RESERVED6 : 16;4 \9 T" d1 M7 u) F/ g' R0 O
}Bit;
}AHBENR;
volatile union
{
volatile uint32_t Value;# h6 r/ g7 K2 ^' h+ I
volatile struct4 k m5 [" N, f: H
{
volatile uint16_t AFIOEN : 1;% z& z2 a; R$ T1 S" `
volatile uint16_t RESERVED1 : 1;1 s: k: k# o2 o( w, ]4 s& {
volatile uint16_t IOPAEN : 1;
volatile uint16_t IOPBEN : 1;
volatile uint16_t IOPCEN : 1;; t" r3 U L/ D7 Z7 ~
volatile uint16_t IOPDEN : 1;& @; k/ p) p) @7 F0 C0 m5 ^
volatile uint16_t IOPEEN : 1;( u" h/ v7 X# \0 u' |) e8 R
volatile uint16_t IOPFEN : 1;
volatile uint16_t IOPGEN : 1;
volatile uint16_t ADC1EN : 1;5 O X0 `1 @( w& d- Q) n& \
volatile uint16_t ADC2EN : 1;
volatile uint16_t TIM1EN : 1;
volatile uint16_t SPI1EN : 1;
volatile uint16_t TIM8EN : 1;; Y& V5 ?1 A1 t5 h6 F* a
volatile uint16_t USART1EN : 1;
volatile uint16_t ADC3EN : 1;
volatile uint16_t RESERVED2 : 16;5 t! d$ `) J) W: H
}Bit;
}APB2ENR;
+ E @" T6 b/ j4 h) U8 Z+ V
volatile union
{& A: v$ n" F9 z$ l0 N) R
volatile uint32_t Value; v8 N! _1 C2 C3 A, X
volatile struct
{
volatile uint32_t TIM2EN : 1;
volatile uint32_t TIM3EN : 1;
volatile uint32_t TIM4EN : 1;
volatile uint32_t TIM5EN : 1;- ^: {6 O+ ?. s
volatile uint32_t TIM6EN : 1;# V3 {) p7 J! R+ B
volatile uint32_t TIM7EN : 1;
volatile uint32_t RESERVED1 : 2;3 `6 L5 X- _4 j d5 d0 K9 z: z
volatile uint32_t RESERVED2 : 3;, `# ^" T9 a& o/ b+ n7 C! z
volatile uint32_t WWDGEN : 1;8 C- {# d: e- \/ m
volatile uint32_t RESERVED3 : 2;/ j) l. s! D. D* m3 c
volatile uint32_t SPI2EN : 1;
volatile uint32_t SPI3EN : 1;
volatile uint32_t RESERVED4 : 1;7 K O Q$ q8 k* J
volatile uint32_t USART2EN : 1;) }( M2 R: v, M! [" _" _
volatile uint32_t USART3EN : 1;
volatile uint32_t USART4EN : 1;
volatile uint32_t USART5EN : 1;
volatile uint32_t I2C1EN : 1;
volatile uint32_t I2C2EN : 1;
volatile uint32_t USBEN : 1;
volatile uint32_t RESERVED5 : 1;2 z( e' ~6 F* D6 L: [0 y
volatile uint32_t CANEN : 1;$ f* c/ x: d$ G# @3 [& L
volatile uint32_t RESERVED6 : 1;
volatile uint32_t BKPEN : 1;
volatile uint32_t PWREN : 1;
volatile uint32_t DACEN : 1;
volatile uint32_t RESERVED7 : 2;- k" Q8 P5 B7 l u" {8 u* Y
}Bit;3 d3 T: r$ C7 W8 C/ t
}APB1ENR;
volatile union0 H. i: e/ a$ C
{/ _0 ?, U2 y X* P. c8 L
volatile uint32_t Value;
volatile struct9 `9 \; @) {, d0 Y
{
volatile uint32_t LSEON : 1;8 L5 ~0 I* s# t' G: n9 T2 e* s
volatile const uint32_t LSERDY : 1;/ S+ c' m/ l# Q2 X/ B
volatile uint32_t LSEBYP : 1;/ I! t5 o L; T/ X5 Z N5 Y
volatile uint32_t RESERVED1 : 5;
volatile uint32_t RTCSEL : 2;
volatile uint32_t RESERVED2 : 5;
volatile uint32_t RTCEN : 1;9 |# O1 R1 V# h5 X- J5 E1 i/ L0 K- M9 J
volatile uint32_t BDRST : 1;
volatile uint32_t RESERVED3 : 7;
volatile uint32_t RESERVED4 : 8;3 k9 K( e/ F* V& I( E
}Bit;
}BDCR;
2 J2 D3 H% u7 l5 p
volatile union
{2 P% n- G7 [' }& k* q& P
volatile uint32_t Value;
5 M4 k/ p9 G7 N# O0 H1 v
volatile struct9 ^( ?$ M4 I+ _2 K( N
{
volatile uint32_t LSION : 1;
volatile const uint32_t LSIRDY : 1;, Y% z$ X- J# a* O. Q5 L$ b* O
volatile uint32_t RESERVED1 : 6;" C/ t4 e% `3 Y8 _ \
volatile uint32_t RESERVED2 : 8;
volatile uint32_t RESERVED3 : 8;0 S5 ~$ K1 H+ b& ]0 S- [
volatile uint32_t RMVF : 1;/ I& I0 v/ `' h% q# s
volatile uint32_t RESERVED4 : 1;
volatile uint32_t PINRSTF : 1;
volatile uint32_t PORRSTF : 1;
volatile uint32_t SFTRSTF : 1;" L5 \' Y3 T* T7 {) ?
volatile uint32_t IWDGRSTF : 1;$ g7 u, ]" |- \5 ~6 X0 v7 Y
volatile uint32_t WWDGRSTF : 1;
volatile uint32_t LPWRRSTF : 1;
}Bit;
}CSR;8 Y0 \$ V' I! t9 i- O4 Q
};1 f8 f2 m v& z9 S4 J& H
T# l5 Q |3 R6 D5 O5 f
enum RCC_PLL_MUL
{+ i) u+ p! @# b6 e0 F4 W! }9 M5 h
RCC_PLL_MUL2 = 0b0000,3 o% w2 l9 m0 }$ z* H" _% p
RCC_PLL_MUL3 = 0b0001,, ?& F9 b U' H- J$ q3 ^0 Z0 H6 T
RCC_PLL_MUL4 = 0b0010,4 F3 S& k! x- }8 X, X
RCC_PLL_MUL5 = 0b0011,
RCC_PLL_MUL6 = 0b0100,
RCC_PLL_MUL7 = 0b0101,
RCC_PLL_MUL8 = 0b0110,8 H, K8 v" S5 b6 Y. r
RCC_PLL_MUL9 = 0b0111,$ q1 p: i- D+ G+ i
RCC_PLL_MUL10 = 0b1000,, g# L8 u5 n- h+ Y2 c) |
RCC_PLL_MUL11 = 0b1001,' R! [: H# o8 x& V* K) X, @
RCC_PLL_MUL12 = 0b1010,
RCC_PLL_MUL13 = 0b1011,0 s/ ^' t! o7 g8 _
RCC_PLL_MUL14 = 0b1100,
RCC_PLL_MUL15 = 0b1101,
RCC_PLL_MUL16 = 0b1110,
};. H& v# D8 S' E
& I' J; ^! j; u0 E9 H; \
enum RCC_PLL_XTPRE( q# _( M5 A# w. o) \" d
{8 F7 s% ? [6 x5 |$ ?/ n( {
RCC_PLL_XTPRE_DIV1 = 0b0,/ C1 M! `+ A, o2 H0 k/ C+ P
RCC_PLL_XTPRE_DIV2 = 0b1,
};
enum RCC_PLL_SRC
{" f3 w% u& X6 y
RCC_PLL_SRC_HSI_DIV2 = 0b0,
RCC_PLL_SRC_HSE = 0b1,7 V8 i% E$ l: V% Y) E
};0 m3 a; f+ Y% A! n; f, H# a7 r0 E
enum RCC_HPRE
{
RCC_HPRE_DIV1 = 0b0000,
RCC_HPRE_DIV2 = 0b1000,* F# g4 c# R' ?+ I$ q; Q" g
RCC_HPRE_DIV4 = 0b1001,
RCC_HPRE_DIV8 = 0b1010,- E. K/ h; F- S, K2 H+ G
RCC_HPRE_DIV16 = 0b1011,
RCC_HPRE_DIV64 = 0b1100,: J- d& d, z: {2 ~) I9 J5 W
RCC_HPRE_DIV128 = 0b1101,9 r% V4 N* `" \2 Z1 p% E! a1 g
RCC_HPRE_DIV256 = 0b1110,+ W O6 V2 O+ j# O* l
RCC_HPRE_DIV512 = 0b1111,
};3 J/ R# J9 d4 ]3 t, ?7 R6 U, q
enum RCC_PPRE/ N1 ~$ y2 A7 C$ S
{
RCC_PPRE_DIV1 = 0b000,* Y3 t9 Y/ q l, H. b- X/ l7 M
RCC_PPRE_DIV2 = 0b100,5 G0 Y! _' I- |$ o6 i2 b1 Y
RCC_PPRE_DIV4 = 0b101,
RCC_PPRE_DIV8 = 0b110,: C/ c9 ~5 h$ ~+ o
RCC_PPRE_DIV16 = 0b111,
};1 W" o/ d1 w- w. r" a7 `& g# A
enum RCC_SW
{6 X) L2 ~6 w; ^1 e( v2 x
RCC_SW_HSI = 0b00,( C+ J' ]& {# {" f. T
RCC_SW_HSE = 0b01,
RCC_SW_PLL = 0b10,
};; H0 t( w& B7 L+ X6 \: u6 o) ]
5 |5 ~& O# M5 s9 s
class CRCC
{
public:
//保存外部晶体频率 在EnableHSE方法中初始化 用于计算系统频率 b9 O4 m! @9 A6 O2 P
static uint32_t HSE;( }" ~) S; f# C9 Y5 }2 B
9 M/ I% n+ ^; y; _7 [' O ^' w( o
volatile struct RegisterRCC *pRegister = (volatile struct RegisterRCC *)(RCC_BASE);
% y T# u5 c& W, ?# [6 `
//开启外部高速时钟并等待时钟就绪,如果时钟不能就绪则一直等待. C" Y% R6 R S% h3 C7 _
//HSE:晶体频率 单位:Hz
void EnableHSE(uint32_t HSE)3 R0 r4 l: A! R$ O
{8 _8 h/ f( [ u! c) m; q) w$ t
CRCC::HSE = HSE;- o+ k8 y" m3 i, r; w: m. Y
4 M0 [; _( d/ T% }2 c0 n" U
this->pRegister->CR.Bit.HSEON = 1;" z6 X% w# r, @) W$ s: l
while(!this->pRegister->CR.Bit.HSERDY);
}: d @- V" X$ V" y' }- u1 h) C
5 [* \/ X* x6 ]; |
//设置PLL参数,必须在PLL关闭的状态下8 w, g6 w y7 B. i
void SetPLL(RCC_PLL_MUL MUL, RCC_PLL_XTPRE XTPRE, RCC_PLL_SRC SRC)( B6 V# i6 e6 ]+ d) _ X
{
this->pRegister->CFGR.Bit.PLLMUL = MUL; |3 O/ k# a1 k5 h! c
this->pRegister->CFGR.Bit.PLLXTPRE = XTPRE;
this->pRegister->CFGR.Bit.PLLSRC = SRC;# S2 ^+ E6 H) G- Y- n7 o+ @
}
//开启PLL时钟并等待时钟就绪,如果时钟不能就绪则一直等待' \4 \' E9 W" x2 q; T" l% x& i. P
void EnablePLL(void)" t( M% ~7 H: {$ ?. @
{
this->pRegister->CR.Bit.PLLON = 1;3 w% y9 A" g6 ^9 x0 ]0 f/ R
while(!this->pRegister->CR.Bit.PLLRDY);
}
* d/ g1 K; ?5 V4 O
//切换系统时钟源并等待切换就绪,如果切换不能就绪则一直等待
void SetSysClkSource(RCC_SW SW)
{
this->pRegister->CFGR.Bit.SW = SW;4 J6 Y' ~; [+ L4 ^1 T: R
while(this->pRegister->CFGR.Bit.SWS != SW);
}5 @3 G1 M0 C( ?; H
//获取系统时钟频率 单位:Hz" Q4 A: n# y( S$ y6 }
uint32_t GetSYSCLK(void)% T2 J+ [8 h) ]: @4 X% |, e
{5 e F0 H; V7 M) j' H$ O! X8 e; ?; P
if(this->pRegister->CFGR.Bit.SWS == RCC_SW_HSI)2 k ?2 z7 ?4 w& S9 _
{8 z; d+ G3 U4 Y
//系统时钟为HSI" M& [+ Y2 ]9 G5 L/ I
return 8000000;
}. x9 [* f5 {+ [) K1 T5 Q
else if(this->pRegister->CFGR.Bit.SWS == RCC_SW_HSE)
{# ^3 `( [3 r. g/ a& k) ?) W
//系统时钟为HSE( w1 v5 s# _1 O9 ~- |. {
return CRCC::HSE;4 [! N* i h. h9 M! \
}
else if(this->pRegister->CFGR.Bit.SWS == RCC_SW_PLL)3 [6 F! a% S0 v1 Y, t5 R6 N
{/ Q; P; \$ z) ~1 f* C
//系统时钟为PLL
uint32_t SRC, XTPRE, MUL;; p8 d0 ?6 i* u$ b6 A, j' u" C& l4 ^
/ G$ ~5 o7 v/ K" }
if(this->pRegister->CFGR.Bit.PLLSRC == RCC_PLL_SRC_HSI_DIV2)
{' i7 m6 C/ T; k* {. p
//PPL输入时钟源为HSI/2
SRC = 8000000;$ _8 Q& e4 G2 a) o7 n4 z
XTPRE = 2;
}
else3 X6 F1 B x$ Z
{
//PLL输入时钟源为HSE
SRC = CRCC::HSE;
if(this->pRegister->CFGR.Bit.PLLXTPRE == RCC_PLL_XTPRE_DIV1)
{
//PLL输入时钟源为HSE/2" e" i' f3 X& R; l# Z
XTPRE = 1;
}
else
{
//PLL输入时钟源为HSE/2
XTPRE = 2;. v3 {- c$ ^0 U" N9 Q8 ]; g! A
}
}
switch((uint32_t)this->pRegister->CFGR.Bit.PLLMUL)) w% n! j- P) b6 V
{
case RCC_PLL_MUL2: MUL = 2; break;+ U, k |+ N& O% Z' X) k3 r
case RCC_PLL_MUL3: MUL = 3; break;
case RCC_PLL_MUL4: MUL = 4; break;$ L* m6 F( [4 ]* z( v: ^
case RCC_PLL_MUL5: MUL = 5; break; [# ?1 f3 d( L4 m" I3 Q& p+ B8 {0 q# E
case RCC_PLL_MUL6: MUL = 6; break;
case RCC_PLL_MUL7: MUL = 7; break;9 @8 u- C8 e0 Y# F8 k
case RCC_PLL_MUL8: MUL = 8; break;
case RCC_PLL_MUL9: MUL = 9; break;5 P. Y1 i7 A+ t1 k
case RCC_PLL_MUL10: MUL = 10; break;5 z; o! j o+ d4 y8 ?: [! {' `
case RCC_PLL_MUL11: MUL = 11; break;2 }7 i7 o( Q9 f" J; ^
case RCC_PLL_MUL12: MUL = 12; break;
case RCC_PLL_MUL13: MUL = 13; break;. y4 q! E$ H$ T# @% w5 ]
case RCC_PLL_MUL14: MUL = 14; break;
case RCC_PLL_MUL15: MUL = 15; break;
case RCC_PLL_MUL16: MUL = 16; break;
default: MUL = 16; break;* [ t/ ^& I- j3 N
} U5 C7 _% B& F" R
' Y0 y& ?$ r" _/ n- q
return SRC / XTPRE * MUL;
}7 @* g( S5 T- i4 P
else
return 8000000;
}
//获取AHB总线频率 单位:Hz
uint32_t GetHCLK(void)7 I# U s8 _) C4 t7 T
{
uint32_t Prescaler;; U$ I+ E" c2 `- Z; |* R! d# `
6 d) z( @7 `9 l7 o
switch((uint32_t)this->pRegister->CFGR.Bit.HPRE)
{ p( M, \( y. G# B1 y* v
case RCC_HPRE_DIV1: Prescaler = 1; break;
case RCC_HPRE_DIV2: Prescaler = 2; break;
case RCC_HPRE_DIV4: Prescaler = 4; break;
case RCC_HPRE_DIV8: Prescaler = 8; break;' C' [/ o) B3 C& o( Z! d4 C; T- g
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;
default: Prescaler = 1; break;0 k" B7 T. t4 \$ U" a. G
}
. Y, \+ `3 I6 B3 _1 J' @
return this->GetSYSCLK() / Prescaler;
}
2 M( I' Q. `# j( j6 g; e% _
//获取APB1总线频率 单位:Hz& _' m2 V6 v5 ? e' Y) r- ~4 A6 L
uint32_t GetPCLK1(void)
{9 ~4 c4 f( ?5 i/ j7 @7 e
uint32_t Prescaler;
! \4 `) _! @5 t' ~. t, |$ J9 T2 ]
switch((uint32_t)this->pRegister->CFGR.Bit.PPRE1)
{
case RCC_PPRE_DIV1: Prescaler = 1; break;# B; ~! S- V4 z( n' |. x
case RCC_PPRE_DIV2: Prescaler = 2; break;4 S1 B2 E/ I3 Q6 `) B. g4 r) {
case RCC_PPRE_DIV4: Prescaler = 4; break;
case RCC_PPRE_DIV8: Prescaler = 8; break;
case RCC_PPRE_DIV16: Prescaler = 16; break;5 {, [, `# O4 [9 @: Q
default: Prescaler = 1; break; ]3 a7 I7 y( g& v' ?
}
return this->GetHCLK() / Prescaler;; N1 Y& p2 t* b% C0 C2 v% k) Y# d
}1 `1 ?& h& u6 G; G& _
//获取APB2总线频率 单位:Hz$ J# f% H% O7 `- }. c5 z U
uint32_t GetPCLK2(void)
{
uint32_t Prescaler;
& ^8 e ]/ r$ ?+ k" a0 b
switch((uint32_t)this->pRegister->CFGR.Bit.PPRE2)
{( p) V. e7 W6 T- q F Z
case RCC_PPRE_DIV1: Prescaler = 1; break;( b) m* I" _, ~# e0 b
case RCC_PPRE_DIV2: Prescaler = 2; break;
case RCC_PPRE_DIV4: Prescaler = 4; break;1 H$ R B6 l5 K6 i2 I0 B
case RCC_PPRE_DIV8: Prescaler = 8; break;: M: W& o& e1 l/ p1 V
case RCC_PPRE_DIV16: Prescaler = 16; break;: g3 {. m7 h% u/ {! {
default: Prescaler = 1; break;
}
2 f* p( h# H' H% P: M4 |2 x
return this->GetHCLK() / Prescaler;6 O C5 Q/ T! w7 i
}
};
: T8 Y7 t; C( P- U
uint32_t CRCC::HSE = 0;
}
}- m# l: l% m: b* b& s! W# ~
8 W8 A$ m5 O1 w# p3 r9 }, m' b5 ]7 _: j
#endif // !RCC_HPP_
1 F& f: e& Z0 H7 d( o. p
这是对RCC的封装,其实对寄存器的封装只是C++写单片机的小问题.还有几个问题要解决.& `6 W8 n! V+ V+ i. k
第一:如何把中断连接到任何对象的方法,这个问题本质就是如何利用指针调用对象方法,实现后可以更好的发挥面向对象的多态思路,实现一些高灵活度的编程.我用模板类实现了一个类似C#中的委托对象,然后利用这个对象还可以实现类似C#中的事件,完成消息的多播.当然这是需要付出代价的,这个模板类要用到虚函数表,经过实测这个消耗是可以接受的,多1us左右吧.这个模板类的实现具体可以用Bing国际版搜索FastDelegate.+ ]8 S/ ^4 d1 M0 p E5 V5 o& }% r
第二:如何new和delete,这个必须通过OS实现.我使用FreeRTOS,然后全局重载new和delete运算符到pvPortMalloc和pvPortFree.不过能定义声明的对象尽量定义声明,不要频繁new和delete因为FreeRTOS的内存管理还是很弱鸡的.实现的目的只是为了提高程序的灵活度.让一些代码可以更快地适配一些硬件.我通常都是只new不delete: A: _ d7 O; j
为了更好更快地更换MCU,应该定义一个HAL层,里面全是虚基类,抽象类似但又不同的硬件.然后顶层基于HAL开发,底层继承HAL的虚基类并做具体实现.不过虚基类又牵涉虚函数表,会有性能损失.在对性能要求高的地方还是针对不同的硬件进行直接控制.
k6 [$ @4 N5 X( U1 j, Y7 f3 M
为了更好的进行C++编程,我用gcc编译器.使用这个编译器时一定要注意,编译优化可能导致运行结果不正确,我通常是用O2或Os,一些特殊的函数还需要标记为O0.开发环境是Visual Studio 2019(个人版 免费) + VisualGDB(我买了正版).VisualGDB V5.5R2这个版本真是超级好用,自动完成,代码着色,代码格式化都是杠杠的.这个环境中在线仿真是ST-Link 2 + OpenOCD,可以设置端点,可以实时读取寄存器值,等等.( D( J8 \6 A, S# e; E
总之,C++配上这个开发环境生产力大大提高,代码的可复用性也大大提高.另外,我的项目已经投产了.# [, ?6 L: T, f) a9 \' N% P
4 M' @# _2 p( I
, {! |' h3 N4 \ a; B& G
我不是去开发一个IDE,而是用了别人很少用的IDE并且完全实现了我的想法。现在正在用这个IDE以及C++在做一个USB键盘(实际的产品)的项目。其实主要的动机来源于对USB的开发,真的是太复杂了不用C++很难解耦和重用。现在USB键盘已经接近尾声了,等我忙完我会继续写贴教大家怎么搭建开发环境,分享一些在C++过程中的心得。提前透露一下,我的键盘的USB枚举过程比海盗船K70快很多,K70是266ms完成,我键盘是176ms完成。海盗船单次获取设备描述耗时88us,我的键盘是61us,那些国产方案的键盘更是要230us左右。我购买一台USB协议分析仪测量的。虽然这个过程快一点对用户并没有什么卵用,但是说明使用C++程序效率并不会降低。
C#,C++面向对象被以前的那些所谓嵌入式大佬批得体无完肤!让他们得以在嵌入式上守住一片天地。
而C#,和C++也缺少实际的应用实例(嵌入式),而真正的即懂C#,C++又懂嵌入式的人,几乎没有!
老师望你在嵌入式C#,C++上开辟一片天地。
还有!老师要实现你的愿景!可能需要一个团队,一个人可能难以完成!也可以把它做成一个项目,才可能有持续下去的源源动力。* B! r7 ~" t; S
个人认为: 关键在于有足够多的实际应用(嵌入式的各个功能)实例服众。
不知道ST自己的IDE(STM32CubeIDE)也是使用GCC编译器,不知道对楼主改用C++开发是否有用。
然后单片机项目主要是UI花费时间重复代码多,应用逻辑的话其实还好。
我整过一个2W行的单片机项目,2/3的代码量花在UI上了,