你的浏览器版本过低,可能导致网站不能正常访问!
为了你能正常使用网站功能,请使用这些浏览器。

NUCLEO_L552ZE_Q 『人生中的首块STM32L552开发板』 ┅ GPIO

[复制链接]
三界狗 发布时间:2020-3-13 17:21
这两天家里有些事情需要忙,家里人刚去上班了,因此得接管家里的一些事情,也抽不出空来发帖子,让大家久等了。
  H+ v2 h$ I( g' C0 _这次是针对NUCLEO_L552ZE_Q开发板的第三次评测贴,上次的帖子介绍了如何在Windows平台上搭建开发环境,不知道大家有没有不明白的地方,如果有,欢迎在评论区向我提出;既然环境已经准备好了,接下来我们就要开始动手写程序测试了(写程序是不会写的,这辈子都不会写程序的,因为有STM32CubeMX),是的,STM32CubeMX把基本的程序框架都写好了,对于基本的测试,只需敲几行字母就行了,真是懒人的必备,哈哈哈。
7 }5 L) E6 q& f, F5 g' x  K废话不多说,相信许多玩过单片机的朋友都知道,拿到一款单片机,写程序的第一步无非就是GPIO口的输出输入功能,输出功能就是点亮一盏LED,输入功能就是按键扫描,那接下来我们就遵守这一不成文的规定,先从GPIO口的输出输入功能入手。+ s4 f5 a! X$ S" t# Y$ v$ N/ Z. {

( F1 f- X% S* m- \5 B评测内容:5 X2 q7 q+ M/ G, R% p, W6 c
1、新建MDK-ARM工程,使用STM32CubeMX新建一个工程;1 l2 P) P4 c* Y# d+ Q# e' G8 q
2、GPIO口电平输出功能,控制NUCLEO_L552ZE_Q开发板上的红色LED灯闪烁;
0 }4 W1 h+ N. l1 t3 C/ ]3、GPIO口电平输入功能,使用NUCLEO_L552ZE_Q开发板上的USER BUTTON按键控制红色LED灯闪烁的频率大小。& T3 y/ b) Z, E- z; ^; t
! C; A* x& s3 Y9 E% o! C$ U
新建MDK-ARM工程:
, E& j2 i) V( d! v' @5 S3 t在开始之前,我们还需要了解一些如何使用STM32CubeMX新建一个工程的知识。
2 n- p: @: j  k第一步:新建目录- Y! U- j, ]# L' G
在电脑的磁盘上新建一个文件夹,这个文件夹并不是工程目录,至于工程目录是哪个,稍后会说。注意,文件件的名称,上级名称,上上级名称等,就是整个路径,一定不能含有中文!半个中文都不能有!不信你可以试试。为了演示,我就随便建个名为“Demo”的文件夹,如下图。
/ H8 B1 H# B& a
新建目录.png
9 n, b6 m2 f% V4 m6 z
第二步:新建STM32CubeMX工程
- M0 @9 [* C' e7 F1、打开STM32CubeMX,鼠标左键单击“New Project”中的“ACCESS TO MCU SELECTOR”,6 K% ~0 e% [/ m. ]4 _6 t0 J
2-1.png
) w" y! _6 }' a9 X: q: P* u
2、在弹出的“New Project from a MCU/MPU”窗口中的左侧的搜索框中输入你需要的单片机型号,我这里输入“STM32L552ZE”,
/ c( {" d7 g( I9 ]$ q8 f4 a! _1 w3、然后在左下角的“MCUs/MPUs List”中选择所需要的单片机型号,我这里选择STM32L552ZET6Q,双击单片机型号,% d' i: X, K* O, @
2-2.png

. O5 v; J- u8 t7 ]. F4、弹出窗口问你是否使用TrustZone,我选择No,然后就创建了一个STM32CubeMX工程。+ [4 z$ U; F/ C6 {+ Q
2-3.png
: O8 f% _3 F3 e" I$ {0 \& n) g
( n. o8 x* P5 [2 W- [+ ^
第三步:配置STM32CubeMX工程
! j8 j3 `2 a! G- }5 Y0 a, U- p1、设置工程属性,点击STM32CubeMX工程窗口上方的“Project Manager”选项,在最左边选项切换到“Project”,/ @- \8 I- s: W  K* T
2、在“Project Location”中选择在第一步的时候创建的文件夹,
: R4 {0 y9 o# w4 Z3、在“Project Name”中填入工程名(不能是中文),填入的工程名就是这个工程的根目录,路径如蓝色框所示,4 }+ D- |+ {4 P  O. `! _
4、在“Toolchain / IDE”中选择“MDK-ARM”,其他保持默认即可,具体如下图所示,
# |3 p+ z* K! Q0 e9 O8 t# i
3-1.png
/ ]0 p* i2 L7 U  _$ S1 r* M
5、最左边选项切换到“Code Generator”,在右边的“STM32Cube MCU packages an embedded software packs”中有三个选项,意思分别为拷贝全部的库文件到工程目录中、仅拷贝需要的库文件到工程目录中、仅在工程文件中引用需要的库文件(库文件放在STM32CubeMX的安装目录中),我选择第二项,

! l4 b) @! `$ N* P
3-2.png

5 Z2 i5 V+ o8 g5 I1 }6、设置时钟属性,点击STM32CubeMX工程窗口上方的“Project Manager”选项,在“HCLK(MHz)”中输入需要的时钟频率,按回车键,在弹出的提示中选择Ok便可,我输入STM32L552ZET6Q的最大时钟频率:110MHz,其他的保持默认:内部高速时钟16MHz和内部低速时钟32KHz,8 a' R5 O& Y: ?! @* J( P
3-3.png
) d( D' t/ p% c# p& a
7、配置点亮LED灯的GPIO,NUCLEO_L552ZE_Q开发板上共有三盏LED灯,分别为红绿蓝三色,根据原理图提供的信息,红绿蓝三盏等分别接在了单片机的PA9、PC7和PB7引脚,我选择了最经典的红色LED,在“Pinout & Configuration”中使用鼠标左键点击单片机模型上的PA9引脚(右侧从上往下数第8个引脚),在小窗口中选择“GPIO_Output”,至此,STM32CubeMX工程就配置好了。, R3 N, F: I6 |! D
3-4.png 3-5.png

7 r& _& k8 v8 c+ A
第四步:保存STM32CubeMX工程。
点击上方菜单中的“File”,选择“Save Project”即可。
第五步:生成MDK-ARM工程。
鼠标左键单击右上方的“GENERATE CODE”,软件便可根据之前的配置自动生成一个代码工程,如果在第一步中创建的目录中有中文,在这一部生成代码工程的时候就会出现错误提示,导致工程创建失败,因此需要特别注意。
6 u! B' M8 h4 a/ a7 {9 X$ K
5-1.png
) a3 B* f0 b2 x- ~0 U+ P6 b
5-2.png
GPIO口电平输出功能:点亮一盏LED灯
第一步:了解一下工程的目录结构。
' c4 f, p. R- W7 O6 q$ {: l$ I5 N
6-1.png
Dirvers:存放STM32的固件库以及ARM公司提供的CMSIS库,通常情况下不需要修改里面的文件内容;
inc:存放用户编写的.h文件,文件内容可修改;
MDK-ARM:存放MDK的工程文件,以及STM32的启动文件,文件内容通常情况下不需要修改;
src:存放用户编写的.c文件,文件内容可修改。

; s% M. h* z2 W& D* g8 j. ?
第二步:在MDK-ARM文件夹内,双击打开.uvprojx后缀的工程文件,不需要改动,直接链接一遍,看是否有问题,结果0错误、0警告。

; t/ }1 V% X* r3 l, I
7-1.png
9 n& p1 J) q. y+ S* _7 D4 Y/ u
第三步:打开main.c文件,找到主函数main(),在主函数的while循环内添加如下代码,意思为每隔500ms对PA9引脚进行电平取反操作,让红色LED灯闪烁。
HAL_GPIO_TogglePin(GPIOA, GPIO_PIN_9);
HAL_Delay(500);

. \) q9 @1 R! w/ A  P4 ?+ f
第四步:配置目标属性。
1、电机窗口上方的魔术棒,弹出配置窗口;
2、切换到“Debug”选项;
3、选择调试器,我使用ST-Link Debugger;
4、电机左侧的“Settings”

0 p: ?' W8 ]* V- s
7-3.png
; d& X. P/ `! {5 H
5、在弹出的窗口中切换到“Flash Download”选项,勾选“Reset and Run”,使得将程序烧录到单片机后就会自动复位并运行程序。

9 D2 \. d9 u( s( `+ p, L
7-4.png

9 z) o7 e8 `; u5 N. H
6、点击“确定”、“OK”确认修改后,再次编译链接工程,0错误0警告。
第五步:烧录程序。
1、使用一根Micro USB数据线链接电脑与开发板上的ST-Link USB-A口,如果还没安装驱动,可查看我上一期的帖子,末尾有附件,如果开发板上的LD4亮红色,LD6亮绿色,说明连接正常。

: c8 _* Q: @6 W& k* v
8-1.jpg

) w7 q/ B9 b' R
2、电机MDK软件左上角的下载按钮 LOAD.png ,开始将程序下载到单片机上运行。
3、观察运行情况,红色LED灯开始闪烁,说明GPIO口能够正常输出高低电平。

: Q! o4 ], l: Y7 C
LED测试结果.gif
GPIO口电平输入功能:按键扫描
第一步:重新生成MDK-ARM工程。将MDK-ARM工程关闭,根据原理图知道开发板上的USER按键连接在单片机的PC13引脚,回到STM32CubeMX工程,在“Pinout & Configuration”中使用鼠标左键点击单片机模型上的PC13引脚(芯片模型的左侧从上往下数第7个引脚),在小窗口中选择“GPIO_Input”,保存STM32CubeMX工程并且点击“GENERATE CODE”重新生成代码。

8 J# G& B+ l' q; C% Y9 N
9-1.png

+ \# h6 X0 E9 W
9-2.png

" g# a& g1 U. B9 U
第二步:实现按键扫描代码
" U# |, ^0 W% I0 T
1、打开MDK-ARM工程,在main.c源文件的主函数实现代码的开头添加如下代码,声明两个局部变量。
uint8_t u_cnt=0, u_cycle=10; //计数值:u_cnt ; 闪烁周期:2 * u_cycle * 10ms
& i! z, z  P: e$ K8 E
2、在主函数的while循环内添加如下代码,实现按键扫描和LED闪烁功能。
/*GPIO口输入功能*/
//按键检测程序
if(HAL_GPIO_ReadPin(GPIOC, GPIO_PIN_13) == GPIO_PIN_SET)
{
        HAL_Delay(20);//消抖
        if(HAL_GPIO_ReadPin(GPIOC, GPIO_PIN_13) == GPIO_PIN_SET)
        {
                u_cycle += 10;
                if(u_cycle>=50)
                {
                        u_cycle = 10;
                }
$ F7 k; E% S4 J
                while(HAL_GPIO_ReadPin(GPIOC, GPIO_PIN_13) == GPIO_PIN_SET);
        }
}
//LED灯闪烁程序
u_cnt++;
if(u_cnt >= u_cycle)
{
        u_cnt = 0;
        HAL_GPIO_TogglePin(GPIOA, GPIO_PIN_9);
}
HAL_Delay(10);

1 |. o( x) p4 h( D3 S
% d' b) q$ [. e* M. o: ?! w
3、修改完之后,编译链接一遍代码,将代码烧录到开发板上,用手按开发板左下方的USER按键,观察红色LED的闪烁频率变化情况,实际情况如下:
8 }* ^5 }1 J) R1 G# X2 z. s
按键测试结果.gif

4 g2 N0 |) T* t' {* l: ?
仿位带操作:实现与51单片机一样的IO口操作方法
使用过正点原子Cortex_M3或者Cortex_M4系列单片机开发板的朋友都应该接触过位带操作功能,在Cortex_M3权威指南(中文)中的第5.5节有如下介绍
在CM3中,有两个区中实现了位带。其中一个是SRAM区的最低1MB范围,第二个则是片内外设区的最低1MB范围。这两个位带中的地址除了可以像普通的RAM一样使用外,它们还都有自己的“位带别名区”,位带别名区把每个比特膨胀成一个32位的字。当你通过位带别名区访问这些字时,就可以达到访问原始比特的目的。
: L2 _' U. T' W$ m
文中所述的位带区与位带别名区有如下对应关系
位带区与位带别名区的膨胀对应关系.png
( o0 ]8 u+ q# u2 G
STM32单片机的GPIO口地址就在片内外设区的最低1MB范围内,意思就是能够通过位带别名区中的每一个地址,操作到对应的片内外设区的外设寄存器中的一个bit,往位带别名区中的地址写1,则对应的片内外设区的外设寄存器中的相应位就会置1,读取位带别名区中的地址的值,就会返回对应的片内外设区的外设寄存器中的相应位的值,GPIO口的每一个Pin的电平就是由ODR寄存器中的每一个位来控制,以及通过IDR寄存器中的一个位来获取一个Pin的电平。但是,我查找过Cortex_M33的手册,并没有发现这个位带别名区,也就是Cortex_M33没有位带操作功能,但我可以通过自定义一个位域结构体来实现类似的操作,实现代码如下:
typedef struct3 f8 I( w. G# t
{
2 l# o4 c8 A& z- r    uint16_t OD0 : 1;  i7 g  R( H1 x7 P- p- Q
    uint16_t OD1 : 1;
- ?: u( k) d/ v& `2 k$ X    uint16_t OD2 : 1;
" I2 y8 c0 p% A- }2 }8 s    uint16_t OD3 : 1;3 f9 H7 U. B! [6 r2 c' s- u( ]
    uint16_t OD4 : 1;
( {5 `% c& Y& d6 t& A- v    uint16_t OD5 : 1;5 @% P  }: i: x- [* p, f0 ]" z) M
    uint16_t OD6 : 1;
2 N& |  [' k( o9 L; f$ w  w    uint16_t OD7 : 1;
) s9 `: [9 X& e    uint16_t OD8 : 1;1 {+ f+ j3 Z2 |
    uint16_t OD9 : 1;7 F- V8 W0 d9 G  s
    uint16_t OD10 : 1;2 a; O5 k( X2 E6 n
    uint16_t OD11 : 1;( b) x/ Y% z7 R$ b4 y1 d- n. Q2 `
    uint16_t OD12 : 1;
' E- P+ n9 K0 W    uint16_t OD13 : 1;2 j( U# o% D. e0 b) H
    uint16_t OD14 : 1;
) o7 W1 m+ J; d9 A9 Y2 C    uint16_t OD15 : 1;
7 J( W/ u- R6 c4 B- a, W. J} ODR_TypeDef;. n6 Y2 F; i( b$ ], F- W
9 Z- H6 Q9 @, E3 x
#define PAin(n) ( ( GPIOA->IDR&(1 << (n)) )>>n )5 N8 w, n* }- P7 A0 s5 k" }
#define PBin(n) ( ( GPIOB->IDR&(1 << (n)) )>>n )& W: Z5 U9 G7 v0 Z' s
#define PCin(n) ( ( GPIOC->IDR&(1 << (n)) )>>n )
( {' d+ Y8 w- T#define PDin(n) ( ( GPIOD->IDR&(1 << (n)) )>>n )7 o+ L* s8 [1 T0 \+ V0 w7 X1 F
#define PEin(n) ( ( GPIOE->IDR&(1 << (n)) )>>n )
: B+ D: i1 f5 m#define PFin(n) ( ( GPIOF->IDR&(1 << (n)) )>>n )
# L3 x4 d6 u) B5 K$ L/ {% x#define PGin(n) ( ( GPIOG->IDR&(1 << (n)) )>>n )* c( n" ^: y/ E  H. j# U

: g+ i# g5 J1 @4 n#define PAout(n)  ( ((ODR_TypeDef *)(&(GPIOA->ODR)))->OD##n )0 j6 w( p" j" z  r
#define PBout(n)  ( ((ODR_TypeDef *)(&(GPIOB->ODR)))->OD##n )' [9 V3 G* |. {2 |" ?$ L
#define PCout(n)  ( ((ODR_TypeDef *)(&(GPIOC->ODR)))->OD##n )
$ D" ^& i" _% a7 m- b! b#define PDout(n)  ( ((ODR_TypeDef *)(&(GPIOD->ODR)))->OD##n )
$ x1 f0 H0 a3 n, a#define PEout(n)  ( ((ODR_TypeDef *)(&(GPIOE->ODR)))->OD##n )4 `5 G$ H& Z. _1 `9 }
#define PFout(n)  ( ((ODR_TypeDef *)(&(GPIOF->ODR)))->OD##n )
9 l+ Z: M5 w% i9 E7 D2 A1 ?#define PGout(n)  ( ((ODR_TypeDef *)(&(GPIOG->ODR)))->OD##n )

6 b( A6 x, x( o" f5 H可将此代码封装到一个头文件内供调用,使用方法同正点原子的源码一样。
#define LED_R PAout(9)4 [# r7 F' D' w- L, O$ S( v; n
#define USER_BUTTON PCin(13)
/*仿位带操作*/, m. A$ c' w4 ]! w* K
//按键检测程序
- M) ~6 V0 m+ H# o9 i3 yif(USER_BUTTON == 1)
  G, d& p/ u! f5 [4 a- ~2 A{+ i( K% w: V1 H' Z' x6 O
        HAL_Delay(20);//消抖
# k( V/ H6 K( p- t# n        if(USER_BUTTON == 1)
" Q% I# u. d7 n0 E/ d        {7 l* ]6 X* }% c) A( W! C
                u_cycle += 10;
7 i8 T5 [. z1 h& R1 W( K& s/ S% c* X                if(u_cycle>=50). F- B; F# O' `0 ^, U
                {7 D2 W+ s; ~" B7 O9 E
                        u_cycle = 10;( y6 q. ]+ S5 A: S. r+ W
                }
0 v! j& X  e* Q' |6 @& X4 {               
- y$ Y: ~* T5 x; ?. ~5 a                while(USER_BUTTON == 1);
$ R) j9 U4 M, ]# A9 S+ \, {* G( _2 ~' l        }% O' P5 X0 V( Q# X! z
}  m8 v. \6 K/ v- ?
//LED灯闪烁程序) ?( Y3 L/ [  x' V# N$ d5 B9 ^4 c5 z
u_cnt++;
! C: g% T( g' T  x4 Lif(u_cnt >= u_cycle)
/ u) {4 Y' H; Z, T# K' }$ @{
" ^6 b. i- o. `- I! x        u_cnt = 0;! q; Q% f2 _3 E  ~& v8 @7 h, U) ^
        LED_R = !LED_R;
/ Z, N: t: l! |/ I) k5 P}- ]9 T7 d/ P4 _! |# A5 ~, `
HAL_Delay(10);
9 X# q9 C9 Q3 M' V: t! h% g
总结:
+ @9 e8 f0 L) @: }. U1 ^可见,在STM32CubeMX的帮助下,实现一些基础的功能代码还是比较方便的,加之有HAL固件库的支持,无论单片机的底层寄存器如何变化,HAL固件库都已经统一封装成一致的函数名,使用起来也非常方便。本次测试的GPIO口操作,主要使用到了两个函数:HAL_GPIO_TogglePin和HAL_GPIO_ReadPin,两个函数的实现也比较简单,都是直接操作寄存器:
+ Q( \7 \( \& K. j8 D( T( t
void HAL_GPIO_TogglePin(GPIO_TypeDef *GPIOx, uint16_t GPIO_Pin)
* o/ D: n6 o: R{' i& J3 f# |& f' P
  /* Check the parameters */! \; u3 ~+ U* u9 z  q
  assert_param(IS_GPIO_PIN(GPIO_Pin));, P* I1 f3 X* A3 e+ k" ~) h) g

  q$ _' P! X1 o1 {: m3 l  G  if ((GPIOx->ODR & GPIO_Pin) != 0U)
; @0 O1 I2 c5 ]3 ?2 P8 [. b" Z  {
# ^5 Y5 g" V0 u1 z    GPIOx->BRR = (uint32_t)GPIO_Pin;
8 @# @: o7 c( k* @0 c3 y" s  }
3 }- q/ p3 s& y  e5 z: Q. F) W  else
5 t7 g9 v2 v: y  {4 i# n8 s: W. B5 \
    GPIOx->BSRR = (uint32_t)GPIO_Pin;$ q) B8 m, h7 u' i# y: V! k7 G# c
  }
+ h4 g$ a- I8 ]: W4 u. I6 Q7 }}
GPIO_PinState HAL_GPIO_ReadPin(GPIO_TypeDef *GPIOx, uint16_t GPIO_Pin)& m* ^8 [! f3 y+ D& D# V8 r
{1 T$ a2 W6 O6 }* o7 s" M
  GPIO_PinState bitstatus;
6 t8 h3 k# a5 Q6 ]( m8 G# |$ V: K9 N! P) `" ]: v# q% L, C8 _+ P
  /* Check the parameters */3 l. V" f% J+ f8 F( F$ E  f
  assert_param(IS_GPIO_PIN(GPIO_Pin));
% m% q: z; W# A- _4 k- b  I7 z4 o4 o' w3 g
  if((GPIOx->IDR & GPIO_Pin) != 0U)
6 r' e( ~7 O/ L- j/ s  {7 Y' M/ M$ D; j( C# l. @
    bitstatus = GPIO_PIN_SET;- U. i7 @+ [. w2 U7 O% [
  }
* K; v$ G/ V: X* U1 H  else0 H- {. ~! T$ d* [# f0 S! a
  {
3 u! w% d6 O+ t% c1 A    bitstatus = GPIO_PIN_RESET;% S& X& [( p3 |# u+ Z
  }/ P/ \/ x5 w5 \! {$ ]- C6 N
  return bitstatus;! _! I: ^3 s8 J
}
- g9 D2 T) y* s8 k! {5 e4 ]- X: M
本次我们介绍了GPIO口的使用,由于篇幅已经太多了,花了一天时间才写完,等下次有空再介绍其他一些基本的功能。还是那句话,如果朋友们觉得有任何疑问,欢迎在评论区向我提出。感谢您的关注,谢谢。
: {7 M3 ^$ Y" u1 s) b
8 U' g: M0 |2 n8 S GPIO_Demo.zip (990.25 KB, 下载次数: 9)
7-2.png
赞 收藏 2 评论0 发布时间:2020-3-13 17:21

举报

0个回答

所属标签

相似技术帖

官网相关资源

关于
我们是谁
投资者关系
意法半导体可持续发展举措
创新与技术
意法半导体官网
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
官方最新发布
人形机器人运动控制、感知与智能配电
半导体创新技术与应用方向
EE架构与软件定义汽车
12V/48V 汽车智能配电(SPD)
区域控制单元(ZCU)与分区架构
关注我们
st-img 微信公众号
st-img 手机版