|
用C++写单片机程序 STM32F103 + 自建固件库 + FreeRTOS 用gcc编译器和newlib(1篇) % b- W6 b' ?! ] 作为论坛的新人,先自我介绍一下。本人有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 Z# F# D: f/ z# @' G0 ~ \ " b2 I' t% u# m- q 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可以吐槽的地方还有很多很多,我可以给你说一整天,我并不想开吐槽大会,只是想告诉大家其实可以有更好的选择。. {+ w$ e# a" 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语言一样,该干嘛干嘛。( i4 }4 i5 Z) R0 ]: } ; z9 f/ J, j q* ~6 V2 {9 |: c 互联网上可以找到的资料实在是少之又少,断断续续搞了2年,现在已经完整构建了整个环境,打算写一个连载。这是第一篇,主要是阐述这种开发环境的优势。后面将陆续给大家分享开发环境的搭建,为什么要自建固件库,如何自建固件库,FreeRTOS如何与C++联合使用,newlib中如何实现printf重定向到串口,以及在实践过程中遇到的各种零碎的问题。随着芯片科技的不断进步,MCU性能的进一步加强,我相信这种环境将是未来的主流。也希望一些技术大牛不要再保守秘密了,把真正的技术都分享出来,彻底提升MCU编程。目前我已在用这个环境做实际的项目了,下面给一张开发环境的截图。能回复的回复一下啊,让我有写完整篇的动力。. |$ P, _$ Y2 H: [8 C' B* c: o
0 G0 a: t- @; A |
DMA 原理从 0 到 1:为什么它能解放 CPU?一文讲透 DMA 的工作流程
【福利三:雨露均沾·逢7狂欢】之五:STM32系统时钟PLL故障排除1例
【福利三:雨露均沾·逢7狂欢】之三:AI帮我查BUG
福利三:雨露均沾·逢7狂欢】之二:STM32F407VG串口通信乱码故障的排除
基于STM32F103的I2C主从机通信
OpenBLT移植到STM32F103战舰开发板上适用于所有STM32F103系列的Bootloader
TFT LCD 与 FSMC 的硬件连接大容量 STM32F10xxx FSMC 接口
单片机:初学者该了解的STM32F103基础知识
2025软件工具兔哥知道
STM32之继电器模块
微信公众号
手机版
分享一个# E( p" ^8 n: f, M2 L
#ifndef RCC_HPP_
#define RCC_HPP_) `' o' u5 |5 C4 }% ~5 @& R
: E7 ^. E7 K5 R% a. N! ?8 `, a
#include "STM32F103.h"
namespace MCU
{/ K' ?& \% t: X( |/ q2 `/ t0 V$ r- X
namespace STM32F1034 @6 t2 V3 W/ |, ^8 p
{+ w p$ a$ a/ c/ W
struct RegisterRCC
{
volatile union6 b% |+ m5 s, f5 v9 b' Z+ P
{
volatile uint32_t Value;) E: M$ e* y+ h9 N
2 ~0 L9 O9 I9 Z {! x9 C
volatile struct4 ^; u2 F* y, c
{, x) D3 y+ Y. x: r
volatile uint32_t HSION : 1;
volatile const uint32_t HSIRDY : 1;9 t* u% r2 i9 T* L
volatile uint32_t RESERVED1 : 1;
volatile uint32_t HSITRIM : 5;% u: q5 f; U) [9 ?( ]+ D# \; o
volatile const uint32_t HSICAL : 8;
volatile uint32_t HSEON : 1;
volatile const uint32_t HSERDY : 1;
volatile uint32_t HSEBYP : 1;0 e! J# L# n# }- C" M* {7 ~; A
volatile uint32_t CSSON : 1;" S0 ]# F- i+ [; ?% |
volatile uint32_t RESERVDE2 : 4;1 H. \) T) s7 _8 D! w
volatile uint32_t PLLON : 1;
volatile const uint32_t PLLRDY : 1;
volatile uint32_t RESERVDE3 : 6;3 k) A5 X* [2 @) O: A. r
}Bit;
}CR;! s. g& Q% ], m0 H; m/ E1 R; _, Y
volatile union
{2 Q* ?/ h, g: \- r
volatile uint32_t Value;
volatile struct( q, y8 h4 H |' S& L0 i
{- Q; ~: |% A7 i. V/ ?
volatile uint32_t SW : 2;; B2 V# H) P3 L
volatile const uint32_t SWS : 2;/ d0 m" r) s2 U7 K2 d
volatile uint32_t HPRE : 4;5 L8 q* P. X$ Q/ g* S
volatile uint32_t PPRE1 : 3;
volatile uint32_t PPRE2 : 3;
volatile uint32_t ADCPRE : 2;5 v4 ~5 ]6 H8 {
volatile uint32_t PLLSRC : 1;! p& k0 ^( p5 r0 e0 q
volatile uint32_t PLLXTPRE : 1;. |& Q: L5 G: |; o1 Z" i
volatile uint32_t PLLMUL : 4;1 i8 P7 H/ |$ g6 j) e" M
volatile uint32_t USBPRE : 1;
volatile uint32_t RESERVED1 : 1;: a( P) q+ ]+ W Y
volatile uint32_t MCO : 3;
volatile uint32_t RESERVED2 : 5;8 K" f O6 I5 L& B- s- V0 H
}Bit;
}CFGR;
3 R- E8 `% h e
volatile union3 u) x! H: [( o" `! E$ O+ E* M
{
volatile uint32_t Value;4 C ?: ~5 b. e9 O# J; x
$ t+ \- F( d9 E* b
volatile struct/ q5 ^4 \# A3 V+ y1 t" S
{
volatile const uint32_t LSIRDYF : 1;
volatile const uint32_t LSERDYF : 1;# I6 z7 F. c+ @
volatile const uint32_t HSIRDYF : 1;
volatile const uint32_t HSERDYF : 1;
volatile const uint32_t PLLRDYF : 1;
volatile const uint32_t PLL2RDYF : 1;2 G* Z/ o# g: i! I4 ~. g% d; U! u3 i
volatile const uint32_t PLL3RDYF : 1;
volatile const uint32_t CSSF : 1;' y5 Y1 s8 R. n! }, h
volatile uint32_t LSIRDYIE : 1;
volatile uint32_t LSERDYIE : 1;
volatile uint32_t HSIRDYIE : 1;
volatile uint32_t HSERDYIE : 1;
volatile uint32_t PLLRDYIE : 1;
volatile const uint32_t PLL2RDYE : 1;) j0 H q9 m6 O |
volatile const uint32_t PLL3RDYE : 1;
volatile uint32_t RESERVED1 : 1;
volatile uint32_t LSIRDYC : 1;
volatile uint32_t LSERDYC : 1;
volatile uint32_t HISRDYC : 1;2 D0 F" S$ ]: w; A( y
volatile uint32_t HSERDYC : 1;. A% w1 h+ t4 F6 X! e
volatile uint32_t PLLRDYC : 1;
volatile uint32_t PLL2RDYC : 1;( Y* V$ ~8 P: s, O I6 t0 Y
volatile uint32_t PLL3RDYC : 1;
volatile uint32_t CSSC : 1;+ }5 [' J( P v( |9 Z2 C
volatile uint32_t RESERVED2 : 8;! n( W+ G4 t9 E, U
}Bit;! C6 _: M% k" n$ \
}CIR;' W& K* |* d6 G I9 a
: e+ I9 ?" A; o( D |# g
volatile union
{
volatile uint32_t Value;
volatile struct+ X0 w/ z9 C3 b4 V' d5 q
{
volatile uint16_t AFIORST : 1;# o1 ~$ `% R2 f9 }
volatile uint16_t RESERVED1 : 1;
volatile uint16_t IOPARST : 1;( _' G3 m( Q$ U7 a( a1 B
volatile uint16_t IOPBRST : 1;
volatile uint16_t IOPCRST : 1;
volatile uint16_t IOPDRST : 1;
volatile uint16_t IOPERST : 1;
volatile uint16_t IOPFRST : 1;* y f+ m8 `# \
volatile uint16_t IOPGRST : 1;4 y$ D( k M2 a9 O
volatile uint16_t ADC1RST : 1;
volatile uint16_t ADC2RST : 1;
volatile uint16_t TIM1RST : 1;
volatile uint16_t SPI1RST : 1;' j* T- @9 s9 i: w1 x: M
volatile uint16_t TIM8RST : 1;6 {: L" |% F% X* {4 H9 G
volatile uint16_t USART1RST : 1;' H1 |% m4 D. U+ h7 ?: i- D7 X
volatile uint16_t ADC3RST : 1;. r7 M8 J( y% X7 U+ ?
volatile uint16_t RESERVED2 : 16;! U4 w+ o5 s( r& m$ M. `, I: v/ p
}Bit;! j3 L5 h! W! O" E
}APB2RSTR;) ^$ t" t& n& O8 x4 L# d
volatile union! _# Z, d0 m7 i( R' P6 D% G
{
volatile uint32_t Value;
volatile struct) {# V- y, W8 V. X3 R$ B
{
volatile uint32_t TIM2RST : 1;; [% _4 J: _4 I6 K/ g6 X% Y9 B) A
volatile uint32_t TIM3RST : 1;' H) W: |% I' |" E' `
volatile uint32_t TIM4RST : 1;
volatile uint32_t TIM5RST : 1;
volatile uint32_t TIM6RST : 1;# i- F$ }, ^: L
volatile uint32_t TIM7RST : 1;
volatile uint32_t RESERVED1 : 2;" r. `% R1 F' E" J
volatile uint32_t RESERVED2 : 3;! N% y$ S% m# M
volatile uint32_t WWDGRST : 1;
volatile uint32_t RESERVED3 : 2;5 c" j# |* P0 j8 y1 H
volatile uint32_t SPI2RST : 1;1 v/ x2 p9 k( z
volatile uint32_t SPI3RST : 1; P) u' S" R, C! y* R% F
volatile uint32_t RESERVED4 : 1;' Y, _2 h$ ?8 O2 w1 c4 B
volatile uint32_t USART2RST : 1;3 G3 c% N2 R5 O) ?) B! f
volatile uint32_t USART3RST : 1;/ ?. Q2 L1 r# \7 j0 q5 i M
volatile uint32_t USART4RST : 1;# U9 P8 \" P. t5 u" `# |, u. P
volatile uint32_t USART5RST : 1;
volatile uint32_t I2C1RST : 1;9 H8 P6 W( A) O
volatile uint32_t I2C2RST : 1;+ [1 N- E; y8 O
volatile uint32_t USBRST : 1;
volatile uint32_t RESERVED5 : 1;. D6 s% J% V' K+ A
volatile uint32_t CANRST : 1;! Y2 c+ i2 `, k# Y' S2 `
volatile uint32_t RESERVED6 : 1;$ p* \: E/ q: H! k( ]0 A2 `
volatile uint32_t BKPRST : 1;& T+ e$ W0 a* H
volatile uint32_t PWRRST : 1;1 A7 |2 c1 H$ k* q6 A" m
volatile uint32_t DACRST : 1;
volatile uint32_t RESERVED7 : 2;
}Bit;9 r8 p4 W1 r2 w& Q0 V+ j
}APB1RSTR;
& L" L/ B4 e/ \( ]
volatile union9 a' @3 d% y! [8 D) k
{
volatile uint32_t Value;: M8 r5 |* x+ r) }5 |2 ~! _
* g0 j' l: D9 c8 Z# ~2 x" }
volatile struct
{- t2 [7 i/ I/ ]5 N
volatile uint16_t DMA1EN : 1;0 J1 |; O, ?1 ], P4 Y2 u
volatile uint16_t DMA2EN : 1;
volatile uint16_t SRAMEN : 1;
volatile uint16_t RESERVED1 : 1;- t; Q- U5 f' Q# Y: F' l
volatile uint16_t FLITFEN : 1;
volatile uint16_t RESERVED2 : 1;1 S$ H' D7 G; I- Z; h
volatile uint16_t CRCEN : 1;4 B6 n1 p& H% G* {" i& M6 m
volatile uint16_t RESERVED3 : 1;
volatile uint16_t FSMCEN : 1;- u A2 _4 x# V
volatile uint16_t RESERVED4 : 1;
volatile uint16_t SDIOEN : 1;
volatile uint16_t RESERVED5 : 5;! A+ l+ I1 q+ B& L& g( T
volatile uint16_t RESERVED6 : 16;
}Bit;% l( s8 u. ]1 r1 `0 i
}AHBENR;, U# K& w( n% ^& |
volatile union Y, _* P$ h. `% D0 F1 }" j
{
volatile uint32_t Value;0 b3 `3 W7 w2 O4 }
- }/ a' \& @: s; k
volatile struct W$ Y+ j$ W. n# F
{
volatile uint16_t AFIOEN : 1;
volatile uint16_t RESERVED1 : 1;
volatile uint16_t IOPAEN : 1;
volatile uint16_t IOPBEN : 1;7 D5 u* @* G* V* w( v* Y
volatile uint16_t IOPCEN : 1;
volatile uint16_t IOPDEN : 1;
volatile uint16_t IOPEEN : 1;
volatile uint16_t IOPFEN : 1;: S' ?) H) R0 S0 @& v6 Q y4 }
volatile uint16_t IOPGEN : 1;
volatile uint16_t ADC1EN : 1;' s" R2 N( W# c% ?
volatile uint16_t ADC2EN : 1;
volatile uint16_t TIM1EN : 1;
volatile uint16_t SPI1EN : 1;
volatile uint16_t TIM8EN : 1;
volatile uint16_t USART1EN : 1;
volatile uint16_t ADC3EN : 1;9 ]2 a& W4 J3 _& o- D0 F, Y0 C7 `
volatile uint16_t RESERVED2 : 16; t4 t! D0 e! m' s6 Q3 k
}Bit;8 q7 W! d0 R: M% S" q
}APB2ENR;5 {; D. l1 `# T; k
volatile union: m/ ^5 Q9 h/ [4 Q& o
{- j4 I* n% u3 n
volatile uint32_t Value;) U" ^" {* t& l; T4 }; p, `, P( ?
volatile struct( ]7 t0 E, q3 n( D
{
volatile uint32_t TIM2EN : 1;
volatile uint32_t TIM3EN : 1;
volatile uint32_t TIM4EN : 1;
volatile uint32_t TIM5EN : 1;
volatile uint32_t TIM6EN : 1;
volatile uint32_t TIM7EN : 1;
volatile uint32_t RESERVED1 : 2;; U% |8 S$ Y9 z, H- @
volatile uint32_t RESERVED2 : 3;
volatile uint32_t WWDGEN : 1;
volatile uint32_t RESERVED3 : 2;) I/ |# n& ?1 I5 M
volatile uint32_t SPI2EN : 1;- Y; J$ ]: a4 t
volatile uint32_t SPI3EN : 1;5 U' N$ S/ C- q$ ~
volatile uint32_t RESERVED4 : 1;7 g1 @' Q% O# y& {. \& w! J
volatile uint32_t USART2EN : 1;
volatile uint32_t USART3EN : 1;
volatile uint32_t USART4EN : 1;# B; a) e" F9 L1 Y
volatile uint32_t USART5EN : 1;
volatile uint32_t I2C1EN : 1;
volatile uint32_t I2C2EN : 1;8 H ~! X( c- Z+ c C# W: d2 V
volatile uint32_t USBEN : 1;
volatile uint32_t RESERVED5 : 1;
volatile uint32_t CANEN : 1;# T0 }/ M' H" u9 |& n z
volatile uint32_t RESERVED6 : 1;
volatile uint32_t BKPEN : 1;- G& Y9 b; J# x, t
volatile uint32_t PWREN : 1;3 N( L, ~0 G% Y7 i9 ]. A( c
volatile uint32_t DACEN : 1;
volatile uint32_t RESERVED7 : 2;
}Bit; c3 j& L) |- i8 t
}APB1ENR;; K3 m* j+ H$ R# ]) g
volatile union5 n E$ k8 t/ |0 U3 j4 l
{: o7 k, L9 K& x) c
volatile uint32_t Value;* c: `7 k: y' I$ [: d& ~1 O z
volatile struct
{5 F v8 U6 L0 k7 ?4 x$ f8 w9 q: O
volatile uint32_t LSEON : 1;& o$ ^ o4 d; ]- R$ |, y+ h
volatile const uint32_t LSERDY : 1;
volatile uint32_t LSEBYP : 1;
volatile uint32_t RESERVED1 : 5;* o% \# y K% R# t- L6 i. I
volatile uint32_t RTCSEL : 2;
volatile uint32_t RESERVED2 : 5;) d- e9 B: \" V5 A% s0 F) t
volatile uint32_t RTCEN : 1;& V6 x4 ]! S+ U0 P
volatile uint32_t BDRST : 1;1 N; W& `4 S: q) D1 ~; X& i
volatile uint32_t RESERVED3 : 7;
volatile uint32_t RESERVED4 : 8;
}Bit;
}BDCR;
+ Y5 ?$ r* a2 T" \
volatile union 9 t: ?7 B$ r1 e
{
volatile uint32_t Value;9 c H! T4 L9 L: q
) Q& h' M7 s( S0 R1 j4 d; @; D
volatile struct* Z9 h( w" _/ R7 N
{6 A+ G; Q7 Z$ Y7 W Q
volatile uint32_t LSION : 1;$ E. G# N$ I0 r4 ^+ b
volatile const uint32_t LSIRDY : 1;
volatile uint32_t RESERVED1 : 6;
volatile uint32_t RESERVED2 : 8;/ m9 |' y2 v! s
volatile uint32_t RESERVED3 : 8;" K) p Q8 w; H( Z
volatile uint32_t RMVF : 1;* n7 j; |- Z! G# _% R+ O, f
volatile uint32_t RESERVED4 : 1;
volatile uint32_t PINRSTF : 1;
volatile uint32_t PORRSTF : 1; K X- `! A- G: @
volatile uint32_t SFTRSTF : 1;! _$ W/ i: Q2 H* x6 P: [- |
volatile uint32_t IWDGRSTF : 1;
volatile uint32_t WWDGRSTF : 1;
volatile uint32_t LPWRRSTF : 1;
}Bit;' `7 h O. c) E8 i1 q4 Q5 o% F
}CSR;. X: I$ B8 f+ r4 b2 Y9 v% ~" s
};
4 @7 j9 h* a( a0 H
enum RCC_PLL_MUL
{
RCC_PLL_MUL2 = 0b0000,
RCC_PLL_MUL3 = 0b0001,& Q) C+ T! k5 Z. c
RCC_PLL_MUL4 = 0b0010,6 x+ J$ \ V- r4 Q' Z0 H" o* R4 S M
RCC_PLL_MUL5 = 0b0011,
RCC_PLL_MUL6 = 0b0100,1 w' ]) E, C8 d" w B
RCC_PLL_MUL7 = 0b0101,: H% U3 G' D7 l+ |; [5 v4 R
RCC_PLL_MUL8 = 0b0110,7 K% D9 g! a8 p0 v! I7 ?' Q s* X
RCC_PLL_MUL9 = 0b0111,
RCC_PLL_MUL10 = 0b1000,0 h+ Y+ P( u) L N- }& m& `% I5 `
RCC_PLL_MUL11 = 0b1001,2 a) d7 [. F8 z7 X7 q/ C
RCC_PLL_MUL12 = 0b1010,
RCC_PLL_MUL13 = 0b1011,
RCC_PLL_MUL14 = 0b1100,0 w" Y! C7 m$ |
RCC_PLL_MUL15 = 0b1101,$ ~) }+ a$ {0 _/ D8 \+ E; z0 Y
RCC_PLL_MUL16 = 0b1110,0 r5 Q. p. D, ~* i: ~: y
};
& i3 a7 K: m5 c, U& Q i+ j4 h: H; j
enum RCC_PLL_XTPRE& s1 Q( P( `7 @" ]3 v3 \
{( c4 G7 O$ E/ U" S3 G0 m) M
RCC_PLL_XTPRE_DIV1 = 0b0,
RCC_PLL_XTPRE_DIV2 = 0b1,
};
1 d" A8 n" v" @
enum RCC_PLL_SRC
{3 U# v% Y& D$ A
RCC_PLL_SRC_HSI_DIV2 = 0b0,5 _9 m- t6 _ l3 t% V. r7 o
RCC_PLL_SRC_HSE = 0b1,5 _( {2 T& H: h) o5 m8 q7 h% P
};; U4 d2 a9 v9 x* I
: K% q3 G/ E3 d& M
enum RCC_HPRE; n' `. }. I1 P3 M; Y3 l
{
RCC_HPRE_DIV1 = 0b0000,: y4 ]( S) \, m) s1 | u1 F b
RCC_HPRE_DIV2 = 0b1000,
RCC_HPRE_DIV4 = 0b1001, p9 L* i& c2 O+ E; e4 K! ~% A
RCC_HPRE_DIV8 = 0b1010,
RCC_HPRE_DIV16 = 0b1011,
RCC_HPRE_DIV64 = 0b1100,1 V0 X! Q& O+ Q
RCC_HPRE_DIV128 = 0b1101,0 m- o) |: _" s8 g5 _
RCC_HPRE_DIV256 = 0b1110,
RCC_HPRE_DIV512 = 0b1111,$ x, R/ N% C, H* H3 p
};
+ k) A7 d7 c$ I/ Y" N
enum RCC_PPRE3 R0 P* F) I5 J3 e- `
{
RCC_PPRE_DIV1 = 0b000,
RCC_PPRE_DIV2 = 0b100,7 `9 n! t( L1 C3 A; n @: q; A
RCC_PPRE_DIV4 = 0b101,1 h& |0 t9 A: s' ~# d% C( J
RCC_PPRE_DIV8 = 0b110,- b4 y' w( ^$ E) r
RCC_PPRE_DIV16 = 0b111,
};
enum RCC_SW* R4 g) f; g+ s: G
{ U; H2 a4 c* `
RCC_SW_HSI = 0b00,4 C5 s6 [2 \- ^1 V
RCC_SW_HSE = 0b01,
RCC_SW_PLL = 0b10,
};/ b3 V' J1 |( K' P6 n
class CRCC
{$ f/ U* l8 D, `& I
public:+ _7 W: |' G! U! G# f1 [; q
//保存外部晶体频率 在EnableHSE方法中初始化 用于计算系统频率
static uint32_t HSE;
) C; m B3 e8 N4 w. K+ S
volatile struct RegisterRCC *pRegister = (volatile struct RegisterRCC *)(RCC_BASE);
" M) f- A; u2 X' o8 A, K
//开启外部高速时钟并等待时钟就绪,如果时钟不能就绪则一直等待5 r# l0 o5 w7 ?9 k* m) o5 v
//HSE:晶体频率 单位:Hz2 S: o& y+ x; h S! C z
void EnableHSE(uint32_t HSE); f$ D; o& E: `) `! D
{: R, ]+ \6 }4 U5 L3 \ f) d6 F) M
CRCC::HSE = HSE;
: b, c4 _ G2 i; u0 m8 z
this->pRegister->CR.Bit.HSEON = 1;1 g4 F! C6 u' M$ [3 s/ \
while(!this->pRegister->CR.Bit.HSERDY);
}
3 K5 U* K9 W- w5 ?! A: u
//设置PLL参数,必须在PLL关闭的状态下; ^+ P+ f& p( Q" M
void SetPLL(RCC_PLL_MUL MUL, RCC_PLL_XTPRE XTPRE, RCC_PLL_SRC SRC)
{# ~+ ]9 K2 d* Q4 t; r9 N1 z5 @( x
this->pRegister->CFGR.Bit.PLLMUL = MUL;
this->pRegister->CFGR.Bit.PLLXTPRE = XTPRE;
this->pRegister->CFGR.Bit.PLLSRC = SRC;& f* K* G% K& @$ X6 F
}/ T# P* ?7 g! _9 g& W
//开启PLL时钟并等待时钟就绪,如果时钟不能就绪则一直等待1 e- `% h- e3 T1 U" P
void EnablePLL(void)
{
this->pRegister->CR.Bit.PLLON = 1;, c4 i( B3 C" b" b
while(!this->pRegister->CR.Bit.PLLRDY);
}" \- @1 ?9 V3 _; S+ w6 Y
//切换系统时钟源并等待切换就绪,如果切换不能就绪则一直等待1 V! G4 y9 p4 ~3 E2 @
void SetSysClkSource(RCC_SW SW)
{
this->pRegister->CFGR.Bit.SW = SW;
while(this->pRegister->CFGR.Bit.SWS != SW);
}
//获取系统时钟频率 单位:Hz
uint32_t GetSYSCLK(void)
{. y( ~3 x9 r, J
if(this->pRegister->CFGR.Bit.SWS == RCC_SW_HSI)) o5 n4 v9 |! T9 ]6 T$ S: O& N
{$ `5 F' {' @& Z' b- b) p
//系统时钟为HSI
return 8000000;
}
else if(this->pRegister->CFGR.Bit.SWS == RCC_SW_HSE)
{; W: K2 L- C! M- J/ I
//系统时钟为HSE
return CRCC::HSE;
}- x7 t1 f8 \3 K
else if(this->pRegister->CFGR.Bit.SWS == RCC_SW_PLL)) d. ~/ {8 l8 f; G1 E p8 P' [
{
//系统时钟为PLL
uint32_t SRC, XTPRE, MUL;- o" J- C9 g; a; O: I4 X
if(this->pRegister->CFGR.Bit.PLLSRC == RCC_PLL_SRC_HSI_DIV2)% [ i; j* M( Z6 _
{
//PPL输入时钟源为HSI/2: H: z1 x& X5 Y1 S
SRC = 8000000;
XTPRE = 2;; I; `; |, g) C, y* U
}- m3 Y5 H& t7 B: u
else
{/ I1 I2 M7 ^7 ?' C: B
//PLL输入时钟源为HSE
SRC = CRCC::HSE;. E; m, b! w3 @) u) L& ~
if(this->pRegister->CFGR.Bit.PLLXTPRE == RCC_PLL_XTPRE_DIV1)9 n6 S% c5 z/ ] O
{& y1 K, c! F/ d, Z7 n/ {+ s
//PLL输入时钟源为HSE/23 h) t7 V! t: m8 z& `* _
XTPRE = 1;8 Q) ]4 @- D: U7 \7 f
}
else, f8 X. w$ ?2 M4 P$ F+ o
{
//PLL输入时钟源为HSE/2, s- L9 b0 u$ b% m! M# q/ ~' G$ W
XTPRE = 2;
}' R. u* y2 j) X
}) K4 G5 X' z& k
3 Y- y! _& ?+ \5 g: O5 j. X
switch((uint32_t)this->pRegister->CFGR.Bit.PLLMUL)1 Q$ J% J, f- q
{
case RCC_PLL_MUL2: MUL = 2; break; u2 o2 C7 {* s% W) H
case RCC_PLL_MUL3: MUL = 3; break;& A& @; u: o; O' O3 {6 v3 A
case RCC_PLL_MUL4: MUL = 4; break;$ F7 N/ r4 d( J% P
case RCC_PLL_MUL5: MUL = 5; break;
case RCC_PLL_MUL6: MUL = 6; break;
case RCC_PLL_MUL7: MUL = 7; break;6 h5 S% R" j8 h% M. s7 U
case RCC_PLL_MUL8: MUL = 8; break;+ E+ ~0 z- U" N5 T' Q1 ~$ ]5 J
case RCC_PLL_MUL9: MUL = 9; break;
case RCC_PLL_MUL10: MUL = 10; break;# m/ N: ~, r4 R) q, _" [4 u, }
case RCC_PLL_MUL11: MUL = 11; break; s( a1 k2 O* ]/ E" |, S
case RCC_PLL_MUL12: MUL = 12; break;
case RCC_PLL_MUL13: MUL = 13; break;
case RCC_PLL_MUL14: MUL = 14; break;6 k1 m; r5 r3 ]! m. a
case RCC_PLL_MUL15: MUL = 15; break;
case RCC_PLL_MUL16: MUL = 16; break;
default: MUL = 16; break;" U5 ?9 |. C$ f/ e: `0 K6 y
}' J! z5 j5 C; t- V
) Y- w$ h! g" |! Y/ |4 U" G/ \
return SRC / XTPRE * MUL;8 z- p& ^" L, I G- u( p
}! x' B. G. h' E
else' u- z6 x$ |$ E6 P& j% e, l
return 8000000;* {# ]& |/ n; s) B9 Q
}$ Z5 T( c2 K" R( w+ a7 e) M
//获取AHB总线频率 单位:Hz
uint32_t GetHCLK(void)
{
uint32_t Prescaler;; F5 }7 G, n; D2 b) j2 I" w
switch((uint32_t)this->pRegister->CFGR.Bit.HPRE)$ p+ ^4 A2 Z: B5 X
{- E$ q: k! a0 _. Z; o
case RCC_HPRE_DIV1: Prescaler = 1; break;( @1 `0 P% v% `' D
case RCC_HPRE_DIV2: Prescaler = 2; break;
case RCC_HPRE_DIV4: Prescaler = 4; break;
case RCC_HPRE_DIV8: Prescaler = 8; break;. h8 V4 B9 R( m# e, h
case RCC_HPRE_DIV16: Prescaler = 16; break;
case RCC_HPRE_DIV64: Prescaler = 64; break;
case RCC_HPRE_DIV128: Prescaler = 128; break;& B P: L9 M1 h/ \
case RCC_HPRE_DIV256: Prescaler = 256; break;
case RCC_HPRE_DIV512: Prescaler = 512; break;
default: Prescaler = 1; break;
}# b( }; `& L" g+ ]6 P2 l _2 `
return this->GetSYSCLK() / Prescaler;
}
& X ~ |$ m9 m3 }0 V/ Z
//获取APB1总线频率 单位:Hz5 Q) j8 }) d1 {7 d. R; u1 [* z
uint32_t GetPCLK1(void)% g( B# U; N0 p' ]% |3 O
{9 N/ \) ?# w, R1 l9 h
uint32_t Prescaler;
switch((uint32_t)this->pRegister->CFGR.Bit.PPRE1)6 a. G2 M$ H; [, a( o
{
case RCC_PPRE_DIV1: Prescaler = 1; break;7 Y, g/ {+ p: s* I
case RCC_PPRE_DIV2: Prescaler = 2; break;
case RCC_PPRE_DIV4: Prescaler = 4; break;, k% [3 p5 [8 N3 a* K t8 D/ A
case RCC_PPRE_DIV8: Prescaler = 8; break;/ |' `9 Y: T, |& Z1 {( e
case RCC_PPRE_DIV16: Prescaler = 16; break;
default: Prescaler = 1; break;+ L* ]1 K; V2 p$ _: {
}
return this->GetHCLK() / Prescaler;
}) x; U3 _5 L- q3 ?1 w
6 m( l- H3 h4 S0 h/ ^2 _- z
//获取APB2总线频率 单位:Hz ^9 A, ] T& ~% C; Z; s
uint32_t GetPCLK2(void) U) O, P" b$ `, X) |4 v" [3 c
{
uint32_t Prescaler;
; [; W! G6 X" R9 d5 y: ?
switch((uint32_t)this->pRegister->CFGR.Bit.PPRE2)
{
case RCC_PPRE_DIV1: Prescaler = 1; break;
case RCC_PPRE_DIV2: Prescaler = 2; break;
case RCC_PPRE_DIV4: Prescaler = 4; break;2 n/ u G" M& u
case RCC_PPRE_DIV8: Prescaler = 8; break;9 t$ {8 K. M) D8 \; J; q
case RCC_PPRE_DIV16: Prescaler = 16; break;/ D& |& p$ J0 }( O( X/ `! a
default: Prescaler = 1; break;' k U6 I( y0 _6 |8 j/ ?/ S, @
}
return this->GetHCLK() / Prescaler;
}
};
( J7 l5 |- ?5 B4 Q
uint32_t CRCC::HSE = 0;2 O+ \0 L% B+ W6 P; a; h, [( O, ~
}
}
2 K5 D3 I) b/ [9 q; L; b+ L4 b5 l
#endif // !RCC_HPP_7 \0 \1 G9 w2 w2 H
% m% r5 G1 ^2 X6 w7 b. ^
这是对RCC的封装,其实对寄存器的封装只是C++写单片机的小问题.还有几个问题要解决.) w5 N6 ]! y6 z2 k8 R6 M
第一:如何把中断连接到任何对象的方法,这个问题本质就是如何利用指针调用对象方法,实现后可以更好的发挥面向对象的多态思路,实现一些高灵活度的编程.我用模板类实现了一个类似C#中的委托对象,然后利用这个对象还可以实现类似C#中的事件,完成消息的多播.当然这是需要付出代价的,这个模板类要用到虚函数表,经过实测这个消耗是可以接受的,多1us左右吧.这个模板类的实现具体可以用Bing国际版搜索FastDelegate.- ]# `4 z. X8 r1 s% t
第二:如何new和delete,这个必须通过OS实现.我使用FreeRTOS,然后全局重载new和delete运算符到pvPortMalloc和pvPortFree.不过能定义声明的对象尽量定义声明,不要频繁new和delete因为FreeRTOS的内存管理还是很弱鸡的.实现的目的只是为了提高程序的灵活度.让一些代码可以更快地适配一些硬件.我通常都是只new不delete
+ N- M( g" _6 L: C% u5 _' E' |7 {; |
为了更好更快地更换MCU,应该定义一个HAL层,里面全是虚基类,抽象类似但又不同的硬件.然后顶层基于HAL开发,底层继承HAL的虚基类并做具体实现.不过虚基类又牵涉虚函数表,会有性能损失.在对性能要求高的地方还是针对不同的硬件进行直接控制.5 u0 _% p1 ~- N) L) M7 y
/ v$ \$ Q* B1 ~) {9 H l" N7 o; K
为了更好的进行C++编程,我用gcc编译器.使用这个编译器时一定要注意,编译优化可能导致运行结果不正确,我通常是用O2或Os,一些特殊的函数还需要标记为O0.开发环境是Visual Studio 2019(个人版 免费) + VisualGDB(我买了正版).VisualGDB V5.5R2这个版本真是超级好用,自动完成,代码着色,代码格式化都是杠杠的.这个环境中在线仿真是ST-Link 2 + OpenOCD,可以设置端点,可以实时读取寄存器值,等等.
7 z9 \$ X" L9 N' v4 h- u
总之,C++配上这个开发环境生产力大大提高,代码的可复用性也大大提高.另外,我的项目已经投产了.
* x2 `/ q& f* C% I
我不是去开发一个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++又懂嵌入式的人,几乎没有!' Y9 U- W1 P2 B2 r: q
老师望你在嵌入式C#,C++上开辟一片天地。% U7 R, `: u, C
还有!老师要实现你的愿景!可能需要一个团队,一个人可能难以完成!也可以把它做成一个项目,才可能有持续下去的源源动力。
个人认为: 关键在于有足够多的实际应用(嵌入式的各个功能)实例服众。, n% f* C; J$ Q7 u
不知道ST自己的IDE(STM32CubeIDE)也是使用GCC编译器,不知道对楼主改用C++开发是否有用。
然后单片机项目主要是UI花费时间重复代码多,应用逻辑的话其实还好。) v# v W6 f( c, S' }( X
我整过一个2W行的单片机项目,2/3的代码量花在UI上了,