
跑马灯实验 本章将通过一个经典的跑马灯程序,带大家开启STM32H750之旅。通过本章的学习,我们将了解到STM32H750的IO口作为输出使用的方法。我们将通过代码控制开发板上的RGB灯:LED0、LED1和LED2交替闪烁,实现类似跑马灯的效果。 13.1 STM32H7 GPIO简介& W( M6 l5 G/ H( Y c GPIO是控制或者采集外部器件的信息的外设,既负责输入输出。它按组分配,每组16个IO口,组数视芯片而定。STM32H750VBT6芯片是100脚的芯片,它的IO口有82个,IO分为5组,分别是GPIOA-GPIOE。这里GPIOA-GPIOE,16*5=80个IO口,少了两个,还有两个就是PH0和PH1。PH0和PH1用于连接外部高速晶振。- A6 d; ?; }( M z/ T9 l2 E 这里重点说一下STM32H750的IO电平兼容性问题,STM32H750的绝大部分IO口,都兼容5V,至于到底哪些是兼容5V的,请看STM32H750VBT6.pdf的数据手册(注意是数据手册,不是中文参考手册),见表:Table 7 STM32H750xB pin/ball definition,凡是有FT标志的,都是兼容5V电平的IO口,可以直接接5V的外设(注意:如果引脚设置的是模拟输入模式,则不能接5V!),凡是不是FT标志的,大家就不要接5V了,可能烧坏MCU。对 Table 7符号的描述如下:9 G9 e5 x1 f1 a3 l4 l9 F- L+ k( r 2 i0 s R( |* A+ E0 [% p3 t ![]() 9 G- N$ \- s# ]! [' F+ u& t9 D. G 表13.1.1 IO口属性缩写符号表2 F7 a* r, r$ V$ Y/ V7 ?9 i 13.1.1 GPIO功能模式) @7 ?' G( r4 Z2 X# ? GPIO有八种工作模式,分别是: 1、输入浮空 2、输入上拉* r8 C- X" n& N! k: X, ` 3、输入下拉$ q) Q# ?# \& A- X 4、模拟 5、开漏输出* u, K7 X: j3 u8 q 6、推挽输出, }7 x% b r$ Q) j 7、开漏式复用功能 8、推挽式复用功能 9 \" R2 j( R/ _+ D 13.1.2 GPIO基本结构分析4 S4 I/ z& A9 d8 t# u8 r$ W 我们知道了GPIO有八种工作模式,具体这些模式是怎么实现的?下面我们通过GPIO的基本结构图来分别进行详细分析,先看看总的框图,如图13.1.2.1 所示。 ![]() ' w( Z+ K; O5 C/ v" P4 k% }( w; m 图13.1.2.1 GPIO的基本结构图9 N& ^+ j4 D3 i6 R 如上图所示,可以看到右边只有I/O引脚,这个I/O引脚就是我们可以看到的芯片实物的引脚,其他部分都是GPIO的内部结构。 ① 保护二极管 保护二极管共有两个,用于保护引脚外部过高或过低的电压输入。当引脚输入电压高于VDD时,上面的二极管导通,当引脚输入电压低于VSS时,下面的二极管导通,从而使输入芯片内部的电压处于比较稳定的值。虽然有二极管的保护,但这样的保护却很有限,大电压大电流的接入很容易烧坏芯片。所以在实际的设计中我们要考虑设计引脚的保护电路。 ② 上拉、下拉电阻1 a. Z8 W. c: y1 k% H0 L9 r 它们阻值大概在30~50K欧之间,可以通过上、下两个对应的开关控制,这两个开关由寄存器控制。当引脚外部的器件没有干扰引脚的电压时,即没有外部的上、下拉电压,引脚的电平由引脚内部上、下拉决定,开启内部上拉电阻工作,引脚电平为高,开启内部下拉电阻工作,则引脚电平为低。同样,如果内部上、下拉电阻都不开启,这种情况就是我们所说的浮空模式。浮空模式下,引脚的电平是不可确定的。引脚的电平可以由外部的上、下拉电平决定。需要注意的是,STM32的内部上拉是一种“弱上拉”,这样的上拉电流很弱,如果有要求大电流还是得外部上拉。8 K, \$ _! Z, ]2 @3 O; g ③ 施密特触发器 对于标准施密特触发器,当输入电压高于正向阈值电压,输出为高;当输入电压低于负向阈值电压,输出为低;当输入在正负向阈值电压之间,输出不改变,也就是说输出由高电准位翻转为低电准位,或是由低电准位翻转为高电准位对应的阈值电压是不同的。只有当输入电压发生足够的变化时,输出才会变化,因此将这种元件命名为触发器。这种双阈值动作被称为迟滞现象,表明施密特触发器有记忆性。从本质上来说,施密特触发器是一种双稳态多谐振荡器。9 {/ J" f" j- a& W" U 施密特触发器可作为波形整形电路,能将模拟信号波形整形为数字电路能够处理的方波波形,而且由于施密特触发器具有滞回特性,所以可用于抗干扰,其应用包括在开回路配置中用于抗扰,以及在闭回路正回授/负回授配置中用于实现多谐振荡器。" {, X* C- z6 g9 {; L) ]% k 下面看看比较器跟施密特触发器的作用的比较,就清楚的知道施密特触发器对外部输入信号具有一定抗干扰能力,如图13.1.2.2所示。: e4 C/ J7 o+ w! t x6 }6 a, o1 L4 y ![]() ! B! E7 Y) r5 G 图13.1.2.2 比较器的(A)和施密特触发器(B)作用比较( M9 f- G# l+ a. C( H ④ P-MOS管和N-MOS管 这个结构控制GPIO的开漏输出和推挽输出两种模式。开漏输出:输出端相当于三极管的集电极,要得到高电平状态需要上拉电阻才行。推挽输出:这两只对称的MOS管每次只有一只导通,所以导通损耗小、效率高。输出既可以向负载灌电流,也可以从负载拉电流。推拉式输出既能提高电路的负载能力,又能提高开关速度。4 D0 I. Z; N1 `4 g( |, m9 w. i 上面我们对GPIO的基本结构图中的关键器件做了介绍,下面分别介绍GPIO八种工作模式对应结构图的工作情况。1 M4 Z+ G; \( m+ T6 F# g 1、输入浮空3 x$ D0 l/ d. b* P# R2 q 输入浮空模式:上拉/下拉电阻为断开状态,施密特触发器打开,输出被禁止。输入浮空模式下,IO口的电平完全是由外部电路决定。如果IO引脚没有连接其他的设备,那么检测其输入电平是不确定的。该模式可以用于按键检测,RX1等。 ![]() 图13.1.2.3 输入浮空模式 2、输入上拉 输入上拉模式:上拉电阻导通,施密特触发器打开,输出被禁止。在需要外部上拉电阻的时候,可以使用内部上拉电阻,这样可以节省一个外部电阻,但是内部上拉电阻的阻值较大,所以只是“弱上拉”,不适合做电流型驱动。4 S2 Y9 {$ U |1 v ![]() 图13.1.2.4 输入上拉模式 3、输入下拉 输入下拉模式:下拉电阻导通,施密特触发器打开,输出被禁止。在需要外部下拉电阻的时候,可以使用内部下拉电阻,这样可以节省一个外部电阻,但是内部下拉电阻的阻值较大,所以不适合做电流型驱动。 ) T2 [# V: f: B4 F6 x, A ![]() 图13.1.2.5 输入下拉模式" E4 [/ a2 @1 R" p t/ o 4、模拟功能' S* e0 q4 A- K) W7 }& H5 u 模拟功能:上下拉电阻断开,施密特触发器关闭,双MOS管也关闭。该模式用于ADC采集或者DAC输出,或者低功耗下省电。( S1 v9 x/ ]" e1 h: P! M/ _: e; B - r+ G1 u( O v4 b% X) X' Z ![]() 图13.1.2.6 模拟功能0 ^9 m6 l4 u+ u 5、开漏输出* p( R! D* ]! G- ~& m6 ]" S: g 开漏输出模式:STM32的开漏输出模式是数字电路输出的一种,从结果上看它只能输出低电平Vss或者高阻态。根据《STM32H7xx参考手册_V3(中文版).pdf》第451页关于“GPIO输出配置”的描述,我们可以推知开漏模式下输出电路大致是这样工作的:1 |- h9 }1 H+ p5 E8 }- g9 r# h P-MOS被“输出控制”控制在截止状态,因此IO的状态取决于N-MOS的导通状况。; I0 t- f+ a/ Q6 J 只有N-MOS还受控制于输出寄存器,“输出控制器”对输入信号进行了逻辑非的操作。- C. ?5 h- {% j: S% {$ Y$ W IO到输入电路的采样电路仍被打开,且可以选择是否使用上下拉电阻。 根据参考手册的描述,我们替换了“输出控制”部分,作出了如图13.1.2.7的开漏模式下的简化等效图,图中①的输入对应的②的输出是我们最关心的开漏输出的结果。简化后的图13.1.2.7能更好地表示开漏输出模式的输出关系。- n& ^. f* s% R! T+ g3 Q 开漏输出的具体的理解描述如下: 开漏模式下,P-MOS管是一直截止的,所以P-MOS管的栅极一直接VSS。如果输出数据寄存器设置为0时,经过“输出控制”的逻辑非操作后,输出逻辑1到N-MOS管的栅极,这时N-MOS管就会导通,使得I/O引脚接到VSS,即输出低电平。4 M3 q3 N" N/ A$ s 如果输出数据寄存器设置为1时,经过“输出控制器”的逻辑非操作后,输出逻辑0到N-MOS管的栅极,这时N-MOS管就会截止。因为P-MOS管是一直截止的,使得I/O引脚呈现高阻态,即不输出低电平,也不输出高电平。因此要I/O引脚输出高电平就必须接上拉电阻。这时可以接内部上拉电阻,或者接一个外部上拉电阻。由于内部上拉电阻的阻值较大,所以只是“弱上拉”。需要大电流驱动,请接外部的上拉电阻。此外,上拉电阻具有线与特性,即如果有很多开漏模式的引脚连在一起的时候,只有当所有引脚都输出高阻态,电平才为1,只要有其中一个为低电平时,就等于接地,使得整条线路都为低电平0。我们的IIC通信(IIC_SDA)就用到这个原理。 另外在开漏输出模式下,施密特触发器是打开的,所以IO口引脚的电平状态会被采集到输入数据寄存器中,如果对输入数据寄存器进行读访问可以得到IO口的状态。也就是说开漏输出模式下,我们可以对IO口进行读数据。8 a& y% I+ T" { 0 L8 P1 \' K7 J: d f0 n ![]() 2 `* V) ^( x7 g" h9 Y9 ^, b 图13.1.2.7 开漏输出模式 6、推挽输出 推挽输出模式:STM32的推挽输出模式,从结果上看它会输出低电平VSS或者高电平VDD。推挽输出跟开漏输出不同的是,推挽输出模式P-MOS管和N-MOS管都用上。同样地,我们根据参考手册推挽模式下的输出描述,列出等效原理如图13.1.2.8所示,根据手册描述可以把“输出控制”简单地等效为一个非门。! B3 ~/ E. \( q* b 推挽输出的具体的理解描述如下: 如果输出数据寄存器设置为0时,经过“输出控制”的逻辑非操作后,输出逻辑1到P-MOS管的栅极,这时P-MOS管就会截止,同时也会输出逻辑1到N-MOS管的栅极,这时N-MOS管就会导通,使得I/O引脚接到VSS,即输出低电平。 如果输出数据寄存器设置为1时,经过“输出控制”的逻辑非操作后,输出逻辑0到N-MOS管的栅极,这时N-MOS管就会截止,同时也会输出逻辑0到P-MOS管的栅极,这时P-MOS管就会导通,使得I/O引脚接到VDD,即输出高电平。8 d; N1 j& M" ]0 o% U 上面的描述可以知道,推挽输出模式下,P-MOS管和N-MOS管同一时间只能有一个MOS管是导通的。当引脚高低电平切换时,两个管子轮流导通,一个负责灌电流,一个负责拉电流,使其负载能力和开关速度都有很大的提高。 另外在推挽输出模式下,施密特触发器也是打开的,我们可以读取IO口的电平状态。( J1 _, ?2 k; {- F 由于推挽输出模式输出高电平时,是直接连接VDD ,所以驱动能力较强,可以做电流型驱动,驱动电流最大可达25mA。该模式也是最常用的输出模式。 ![]() 4 @' B0 w9 J, \. n. I$ v6 G1 t 图13.1.2.8 推挽输出模式* I4 h5 \4 ~& L, V+ s7 O 7、开漏式复用功能: w1 ^3 a0 w G, |4 T# s 开漏式复用功能:一个IO口可以是通用的IO口功能,还可以是其他外设的特殊功能引脚,这就是IO口的复用功能。一个IO口可以是多个外设的功能引脚,我们需要选择作为其中一个外设的功能引脚。当选择复用功能时,引脚的状态是由对应的外设控制,而不是输出数据寄存器。除了复用功能外,其他的结构分析请参考开漏输出模式。1 t7 L* `' B+ x) R; b; j' Q s 另外在开漏式复用功能模式下,施密特触发器也是打开的,我们可以读取IO口的电平状态,同时外设可以读取IO口的信息。 ![]() 图13.1.2.9 开漏式复用功能: c/ x6 s8 D9 Z: \! R" h" n 8、推挽式复用功能& L* \0 h3 U B7 ?8 I, I5 y0 k 推挽式复用功能:复用功能介绍请查看开漏式复用功能,结构分析请参考推挽输出模式,这里不再赘述。 ![]() 图13.1.2.10 推挽式复用功能% v0 c0 W2 l* r% s 13.1.3 GPIO寄存器介绍4 |! x0 y/ [" J% Z: \" j STM32H7每组(这里是A~E)通用GPIO口有10个32位寄存器控制,包括 :/ a4 Q4 G9 i% ~+ x& ` 4 个 32 位配置寄存器(MODER、OTYPER、OSPEEDR 和 PUPDR)# J9 [8 m7 S& B. F9 B8 l o+ i- o; m 2 个 32 位数据寄存器(IDR 和 ODR) 1 个 32 位置位/复位寄存器 (BSRR) 1 个 32 位锁定寄存器 (LCKR) 2 个 32 位复用功能选择寄存器(AFRH 和 AFRL) 下面我们将带大家理解本章用到的寄存器,没有介绍到的寄存器后面用到会继续介绍。这里主要是带大家学会怎么理解这些寄存器的方法,其他寄存器理解方法是一样的。因为寄存器太多不可能一个个列出来讲,以后基本就是只会把重要的寄存器拿出来讲述,希望大家尽快培养自己学会看手册的能力。下面先看GPIO的4个32位配置寄存器: GPIO端口模式寄存器 (GPIOx_MODER) (x =A…K)- I! `$ X$ l# B! X W 该寄存器是GPIO口模式控制寄存器,用于控制GPIOx(STM32H7最多有11组IO,用大写字母表示,即x=A/B/C/D/E/F/G/H/I/J/K,下同)的工作模式,寄存器描述如图13.1.3.1所示。 ![]() 图13.1.3.1 MODER寄存器描述1 g, k6 o* `- l" f) Y 每组GPIO下有16个IO口,该寄存器共32位,每2个位控制1个IO。我们看看这个寄存器的复位值,然后用复位值举例说明一下这样的配置值代表什么意思。比如GPIOA的复位值是0xABFF FFFF,低16位都是1,也就是PA0PA7默认都是模拟模式。高16位的值是0xABFF,也就是PA8PA12默认是模拟模式,PA13\PA14\PA15则默认是复用功能模式。而GPIOB的复位值是0xFFFF FEBF,只有PB3默认是复用功能模式,其他默认都是模拟模式。这四个默认是复用功能模式的IO口都是JTAG功能对应的IO口。 GPIO端口输出类型寄存器 (GPIOx_OTYPER) (x = A…K), [3 ]. }! d, d1 J! s B0 `( N5 N 该寄存器用于控制GPIOx的输出类型,寄存器描述如图13.1.3.2所示。 ![]() * o% z5 z8 c# ? 图13.1.3.2 GPIOx OTYPER寄存器描述 该寄存器仅用于输出模式,在输入模式(MODER[1:0]=00/11时)下不起作用。该寄存器低16位有效,每一个位控制一个IO口,复位后,该寄存器值均为0,也就是在输出模式下IO口默认为推挽输出。 GPIO端口输出速度寄存器 (GPIOx_OSPEEDR) (x = A…K)) l9 g$ S' w' w" ? 该寄存器用于控制GPIOx的输出速度,寄存器描述如图13.1.3.3所示。3 ~) Y2 b0 w# J1 R% x6 Z9 @ t! z0 e, r. Q4 j ![]() 图13.1.3.3 GPIOx OSPEEDR寄存器描述; Z* k" v* K* n3 i 该寄存器仅用于输出模式,在输入模式(MODER[1:0]=00/11时)下不起作用。该寄存器低16位有效,每两个位控制一个IO口。 GPIO端口上拉/下拉寄存器 (GPIOx_PUPDR) (x = A…K) 该寄存器用于控制GPIOx的上拉/下拉,寄存器描述如图13.1.3.4所示。% T- L' h* g2 B ^, o ![]() 图13.1.3.4 GPIOx PUPDR寄存器描述( f% s1 O+ q5 U0 k& u- c 该寄存器每两个位控制一个IO口,用于设置上下拉,复位后,该寄存器值一般为0,即无上拉或下拉。 上面这4个配置寄存器就是用来配置GPIO的相关模式和状态,它们通过不同的配置组合方法,就决定我们所说的8种工作模式。下面,我们来列表阐述,如表13.1.3.1所示。 ![]() 表13.1.3.1 4个配置寄存器组合下的8种工作模式& a. {& `7 W2 s( r* e 因为本章需要GPIO作为输出口使用,所以我们再来看看端口输出数据寄存器。6 t6 f' J, z! r2 U: B 端口输出数据寄存器(ODR)$ d! ~) N6 D/ ] 该寄存器用于控制GPIOx的输出高电平或者低电平,寄存器描述如图13.1.3.5所示。7 n0 U! ^$ v# V# ]% ^) m" Z' l . X) b! H2 r; A! O2 ~3 f M& i& I ![]() % u0 a/ L$ p6 M7 y: i6 o 图13.1.3.5 GPIOx ODR寄存器描述. i% l- c2 O; y+ Y" v* e 该寄存器低16位有效,分别对应每一组GPIO的16个引脚。当CPU写访问该寄存器,如果对应的某位写0(ODRy=0),则表示设置该IO口输出的是低电平,如果写1(ODRy=1),则表示设置该IO口输出的是高电平,y=0~15。* W3 ?/ R6 t+ E# z! T" \8 a 除了ODR寄存器,还有一个寄存器也是用于控制GPIO输出的,它就是BSRR寄存器。 Q" N, M" o# o 端口置位/复位寄存器(BSRR) 该寄存器也用于控制GPIOx的输出高电平或者低电平,寄存器描述如图13.1.3.6所示。 ![]() : E' U: V/ I5 M0 `% o8 l0 H 图13.1.3.6 GPIOx BSRR寄存器描述 为什么有了ODR寄存器,还要这个BDRR寄存器呢?我们先看看BSRR的寄存器描述,首先BSRR是只写权限,而ODR是可读可写权限。BSRR寄存器32位有效,对于低16位(0-15),我们往相应的位写1(BSy=1),那么对应的IO口会输出高电平,往相应的位写0(BSy=0),对IO口没有任何影响,高16位(16-31)作用刚好相反,对相应的位写1(BRy=1)会输出低电平,写0(BRy=0)没有任何影响,y=0~15。 也就是说,对于BSRR寄存器,你写0的话,对IO口电平是没有任何影响的。我们要设置某个IO口电平,只需要相关位设置为1即可。而ODR寄存器,我们要设置某个IO口电平,我们首先需要读出来ODR寄存器的值,然后对整个ODR寄存器重新赋值来达到设置某个或者某些IO口的目的,而BSRR寄存器,我们就不需要先读,而是直接设置即可,这在多任务实时操作系统中作用很大。BSRR寄存器还有一个好处,就是BSRR寄存器改变引脚状态的时候,不会被中断打断,而ODR寄存器有被中断打断的风险。+ W! B+ N% f- v6 v8 K% f$ u 7 k8 I$ D4 D( @, w 13.2 硬件设计6 L7 {+ \& E5 B3 Z+ u2 R7 c) n 1.例程功能- D# n1 I: }" C RGB灯:LED0(红)、LED1(绿)和LED2(蓝)每过500ms一次交替闪烁,实现类似跑马灯的效果。( j+ _. e' g( g- m2 h 2.硬件资源: \% k$ h- |" g/ } 1)RGB灯" W) Y ~9 ~4 v% B LEDR : LED0 - PB4(红) LEDG : LED1 - PE6(绿). m4 V! i2 @5 _( n$ \4 E; j LEDB : LED2 - PE5(蓝) 3.原理图& c7 E! H" c8 A$ _+ ^1 E F& V3 z 本章用到的硬件用到RGB灯:LEDR、LEDG和LEDB。电路在开发板上已经连接好,所以在硬件上不需要动任何东西,直接下载代码就可以测试使用。其连接原理图如图13.2.1所示:) {, O# A! f. v5 g5 z ; X8 E5 ^1 Q3 v5 l; r$ B' @- o ![]() 图13.2.1 LED与STM32H750连接原理图 下面,给大家介绍一下LED灯的压降和额定电流,供大家设计硬件参考。 直插超亮发光二极管压降,主要有三种颜色,然而三种发光二极管的压降都不相同,具体压降参考值如下:$ m. z7 ^$ U! m9 M; B8 p4 a 红色发光二极管的压降为2.0V-2.2V。$ L" O6 E3 K% V* u 黄色发光二极管的压降为1.8V-2.0V。8 T/ u) L" K( @+ b; ` 绿色发光二极管的压降为3.0V-3.2V。 正常发光时的额定电流约为20mA。7 h6 J1 w" H: }, b2 n5 \( R 贴片LED压降:- h4 D* ` Z5 ` 红色的压降为1.82-1.88V,电流5-8mA。: T! ]: {7 l/ P9 Z# Y( L" T3 \ 绿色的压降为1.75-1.82V,电流3-5mA。1 B. t8 h4 \& z# b/ h( h4 ~ 橙色的压降为1.7-1.8V,电流3-5mA。& Y" V& Z- p& h( h! t. ~4 l4 Q 蓝色的压降为3.1-3.3V,电流8-10mA。 白色的压降为3-3.2V,电流10-15mA。 LED发光颜色的不同,需要的驱动电压也有差异。我们使用的LED0和LED1是红绿两个颜色的贴片灯珠,它的正常工作驱动电流典型值约为25mA/1.8V。而STM32的GPIO的输出电流也大概是25mA/3.3V,所以可以直接用STM32的IO驱动这种LED。但STM32对总的输出电流有限制,为了经过LED的电流更稳定,我们一般不用STM32的IO直接输出电流驱动LED,而是通过外接电阻的方式限制灌入STM32的电流。如我们使用红灯时灌到IO口的最大电流约为(3.3-1.8)V/510R≈2.94mA。 ![]() * Q5 ^0 P+ n, Z0 p1 b( w 图13.2.2 STM32 IO的电气参数 13.3 程序设计 了解了GPIO的结构原理和寄存器,还有我们的实验功能,下面开始设计程序。4 K1 N4 m; D5 @ 13.3.1 GPIO的HAL库驱动分析 HAL库中关于GPIO的驱动程序在stm32h7xx_hal_gpio.c文件以及其对应的头文件。: _) w5 l) Q/ }$ e1 F, w9 m4 q0 o 1.HAL_GPIO_Init函数 要使用一个外设我们首先要对它进行初始化,所以我们先看外设GPIO的初始化函数。其声明如下: void HAL_GPIO_Init(GPIO_TypeDef *GPIOx, GPIO_InitTypeDef *GPIO_Init);8 `2 l/ Y3 j6 b. Q7 H 函数描述: 用于配置GPIO功能模式,还可以设置EXTI功能。1 S; [9 {" I+ q1 E) X7 m 函数形参: 形参1是端口号,可以有以下的选择:
这是库里面的选择项,实际上我们的芯片只能从GPIOA~GPIOE,因为我们只有5组IO口。. D! d* }% y: O4 l$ j) L% ~8 R 形参2是GPIO_InitTypeDef类型的结构体指针变量,其定义如下:# s/ O# e% n# Y$ Z( E$ e3 E4 I
该结构体很重要,下面对每个成员介绍一下。9 r$ i5 T; P: {6 t0 h8 m 成员Pin表示引脚号,范围:GPIO_PIN_0到 GPIO_PIN_15,另外还有GPIO_PIN_All和GPIO_PIN_MASK可选。: j9 v! H; H. T M# {$ ]8 _ 成员Mode是GPIO的模式选择,有以下选择项:/ o2 \9 @" ^! r8 d& E* S / d+ E8 |2 [& y7 i5 {
成员Alternate用于配置具体的复用功能,不同的GPIO口可以复用的功能不同,具体可参考数据手册《STM32H750VBT6.pdf》。复用功能的选择在stm32h7xx_hal_gpio_ex.h文件里进行了定义,后面具体用到了,我们在进行讲解。: a5 J# i6 B( M y: {+ C6 z* ] 函数返回值: 无, i+ j3 k& S# w- i/ e! t" u 注意事项: HAL库的EXTI外部中断的设置功能整合到此函数里面,而不是单独独立一个文件。这个我们到外部中断实验再细讲。 2. HAL_GPIO_WritePin函数, X5 v" ]0 s" e0 x! _6 \3 S5 l HAL_GPIO_WritePin函数是GPIO口的写引脚函数。其声明如下: void HAL_GPIO_WritePin(GPIO_TypeDef *GPIOx,) y {3 A" e2 D4 S uint16_t GPIO_Pin, GPIO_PinState PinState);" h: p0 d! o: l2 A* I A6 z 函数描述: 用于设置引脚输出高电平或者低电平,通过BSRR寄存器复位或者置位操作。8 y, R5 X9 D% c2 y% r+ \ 函数形参: 形参1是端口号,可以选择范围:GPIOA~GPIOK。 形参2是引脚号,可以选择范围:GPIO_PIN_0到 GPIO_PIN_15。 形参3是要设置输出的状态,是枚举型有两个选择:GPIO_PIN_SET 表示高电平,GPIO_PIN_RESET表示低电平。 函数返回值: 无& x% ~$ i% ?1 C' Q' C2 i7 P L2 ` 3. HAL_GPIO_TogglePin函数* _$ J; W9 V9 T/ o& ] HAL_GPIO_TogglePin函数是GPIO口的电平翻转函数。其声明如下: void HAL_GPIO_TogglePin(GPIO_TypeDef *GPIOx, uint16_t GPIO_Pin); 函数描述: 用于设置引脚的电平翻转,也是通过BSRR寄存器复位或者置位操作。" q; @% f- y3 W/ N _ 函数形参: 形参1是端口号,可以选择范围:GPIOA~GPIOK。- j# [5 A# x# n; ^1 l, U! m1 K 形参2是引脚号,可以选择范围:GPIO_PIN_0到 GPIO_PIN_15。3 x: F) n; O: C% }8 j 函数返回值: 无% v6 B6 w. `0 I! ^6 E" j3 j 本实验我们用到上面三个函数,其他的API函数后面用到再进行讲解。 |" S" U+ Z2 B9 h9 O GPIO输出配置步骤 1)使能对应GPIO时钟2 Q( x' x4 n5 n$ E STM32在使用任何外设之前,我们都要先使能其时钟(下同)。本实验用到PB5和PE5两个IO口,因此需要先使能GPIOB和GPIOE的时钟,代码如下: __HAL_RCC_GPIOB_CLK_ENABLE(); __HAL_RCC_GPIOE_CLK_ENABLE();7 ^4 p' D2 y/ ~: Y# x 2)设置对应GPIO工作模式(推挽输出) 本实验GPIO使用推挽输出模式,控制LED亮灭,通过函数HAL_GPIO_Init设置实现。/ p7 j: V3 c; L4 V" b 3)控制GPIO引脚输出高低电平% V9 `- W0 G+ i+ c 在配置好GPIO工作模式后,我们就可以通过HAL_GPIO_WritePin函数控制GPIO引脚输出高低电平,从而控制LED的亮灭了。 13.3.2 程序流程图 程序流程图能帮助我们更好的理解一个工程的功能和实现的过程,对学习和设计工程有很好的主导作用。 下面看看本实验的程序流程图:4 R3 {! d- B7 i2 j- @1 s* ` $ v: l1 W1 v; s ![]() 图13.3.2.1 跑马灯实验程序流程图 13.3.3 程序解析 1.led驱动代码 这里我们只讲解核心代码,详细的源码请大家参考光盘本实验对应源码。LED驱动源码包括两个文件:led.c和led.h。 下面我们先解析led.h的程序,我们把它分两部分功能进行讲解。 由硬件设计小节,我们知道RGB灯在硬件上分别连接到PB4、PE5、PE6,再结合HAL库,我们做了下面的引脚定义。" l$ P& \* \- ?. h7 d
这样的好处是进一步隔离底层函数操作,移植更加方便,函数命名更亲近实际的开发板。比如:当我们看到LED0_GPIO_PORT这个宏定义,我们就知道这是灯LED0的端口号;看到LED0_GPIO_PIN这个宏定义,就知道这是灯LED0的引脚号;看到LED0_GPIO_CLK_ENABLE这个宏定义,就知道这是灯LED0的时钟使能函数。大家后面学习时间长了就会慢慢熟悉这样的命名方式。 特别注意:这里的时钟使能函数宏定义,使用了do{ }while(0)结构,是为了避免在某些使用场景出错的问题(下同),详见《嵌入式单片机 C代码规范与风格》第六章第2点。4 J! N3 W7 _! }& X' F __HAL_RCC_GPIOx_CLK_ENABLE函数是HAL库的IO口时钟使能函数,x=A到K。 H- L/ M2 h ^1 O 为了后续对RGB灯进行便捷的操作,我们为RGB灯操作函数做了下面的定义。 t N5 Z8 v; e1 ]$ W
LED0、LED1和LED2这三个宏定义,分别是控制LED0、LED1和LED2的亮灭。例如:对于宏定义标识符LED0(x),它的值是通过条件运算符来确定:当x=0时,宏定义的值为HAL_GPIO_WritePin(LED0_GPIO_PORT, LED0_GPIO_PIN, GPIO_PIN_RESET),也就是设置PB4输出低电平,当n!=0时,宏定义的值为HAL_GPIO_WritePin (LED0_GPIO_PORT, LED0_GPIO_PIN, GPIO_PIN_SET),也就是设置PB4输出高电平。所以如果要设置LED0输出低电平,那么调用宏定义LED0(0)即可,如果要设置LED0输出高电平,调用宏定义LED0(1)即可。宏定义LED1(x)和LED0(x)同理。% P3 E! n# x+ t9 {! [ 因为STM32H7不支持位带操作,所以这里我们并没有像F1/F4一样通过位带操作来实现IO口输出输入电平控制。 LED0_TOGGLE、LED1_TOGGLE和LED2_TOGGLE这三个宏定义,分别是控制LED0、LED1和LED2的翻转。这里利用HAL_GPIO_TogglePin函数实现IO口输出电平取反操作。 下面我们再解析led.c的程序,这里只有一个函数led_init,这是RGB灯的初始化函数,其定义如下:9 n+ O5 y- a7 g. ~$ U B G4 l( h; x0 K% i0 W
对RGB灯的三个引脚都设置为中速上拉的推挽输出。最后关闭RGB的三个LED灯,防止没有操作就亮了。 2. main.c代码 在main.c里面编写如下代码:7 T3 O9 l+ |: o9 u , ]6 J& L; A% u' `' a
首先是调用系统级别的初始化:sys_cache_enable函数使能I-Cache和D-Cache,然后初始化 HAL库、系统时钟和延时函数。接下来,调用led_init来初始化RGB灯。最后在无限循环里面实现LED0、LED1和LED2间隔500ms交替闪烁一次。 13.4 下载验证 w: y, F% v* R( F 我们先来看看编译结果,如图13.4.1所示。 ![]() 图13.4.1 编译结果, E$ k+ l: y' E% Q E& g 可以看到没有0错误,0警告。从编译信息可以看出,我们的代码占用FLASH大小为:13364字节(12874+714),所用的SRAM大小为:2424个字节(32+2392)。这里我们解释一下,编译结果里面的几个数据的意义: Code:表示程序所占用FLASH的大小(FLASH)。* Z) r. @1 R- ]+ _ RO-data:即Read Only-data,表示程序定义的常量(FLASH)。 RW-data:即Read Write-data,表示已被初始化的变量(SRAM)# M1 w+ ?6 [ H" V4 M! H/ H3 c ZI-data:即Zero Init-data,表示未被初始化的变量(SRAM)# ^! n6 @& @) W' I* D) N9 f 有了这个就可以知道你当前使用的flash和sram大小了,所以,一定要注意的是程序的大小不是.hex文件的大小,而是编译后的Code和RO-data之和。 接下来,大家就可以下载验证了。这里我们使用DAP仿真器下载(也可以通过其他仿真器下载,如果是JLINK,必须是V9或者以上版本,才可以支持STM32H750!! 下同)。5 b; y+ C# Y& u0 ~ 下载完之后,可以看到RGB灯的LED0(红)、LED1(绿)和LED2(蓝)轮流亮。 至此,我们的跑马灯实验的学习就结束了,本章介绍了STM32H750的IO口的使用及注意事项,是后面学习的基础,希望大家好好理解。 ————————————————5 ]4 S% r7 a# G7 N, S 版权声明:正点原子 |
拷打cubemx【003】——找不到的芯片包
【2025·STM32峰会】GUI解决方案实训分享5-调通板载的NRF24L01 SPI接口并使用模块进行无线通信(发送和接收)
【2025·STM32峰会】GUI解决方案实训分享4-使用MVP架构从硬件外设读取数据并显示到图形界面、从图形界面发送指令控制硬件外设
【2025·STM32峰会】GUI解决方案实训分享3-搭建空白TouchGFX例程并实现简单的功能(含硬件部分的串口打印)
【2025·STM32峰会】GUI解决方案实训分享2-编译运行TouchGFX咖啡机例程(含桌面仿真)
【2025·STM32峰会】+TouchGFX实现动态进度显示以及界面切换
【2025·STM32峰会】+使用TouchGFX快速创建GUI
【2025·STM32峰会】GUI解决方案实训分享1-对LVGL咖啡机例程的牛刀小试以及问题排查
实战经验 | 关于STM32H7使用LL库生成ADC代码工作异常问题说明
实战经验 | 关于STM32H745的MC SDK电机控制工程问题的解决办法