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

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

[复制链接]
三界狗 发布时间:2020-3-13 17:21
这两天家里有些事情需要忙,家里人刚去上班了,因此得接管家里的一些事情,也抽不出空来发帖子,让大家久等了。1 I! H7 l3 F8 A7 |. S6 w. r
这次是针对NUCLEO_L552ZE_Q开发板的第三次评测贴,上次的帖子介绍了如何在Windows平台上搭建开发环境,不知道大家有没有不明白的地方,如果有,欢迎在评论区向我提出;既然环境已经准备好了,接下来我们就要开始动手写程序测试了(写程序是不会写的,这辈子都不会写程序的,因为有STM32CubeMX),是的,STM32CubeMX把基本的程序框架都写好了,对于基本的测试,只需敲几行字母就行了,真是懒人的必备,哈哈哈。2 [: S5 V3 I: q9 l: |* U" A* K
废话不多说,相信许多玩过单片机的朋友都知道,拿到一款单片机,写程序的第一步无非就是GPIO口的输出输入功能,输出功能就是点亮一盏LED,输入功能就是按键扫描,那接下来我们就遵守这一不成文的规定,先从GPIO口的输出输入功能入手。* t* N2 B( e1 P) O

) q/ c; N% v! ^" X' T评测内容:' D2 f! O% x1 B& w( ?
1、新建MDK-ARM工程,使用STM32CubeMX新建一个工程;% O! U& h0 @: X! o4 X4 i( R
2、GPIO口电平输出功能,控制NUCLEO_L552ZE_Q开发板上的红色LED灯闪烁;4 t. H# \) ]! q
3、GPIO口电平输入功能,使用NUCLEO_L552ZE_Q开发板上的USER BUTTON按键控制红色LED灯闪烁的频率大小。! b& @. M/ N8 ~% V# t% t
0 D, Q2 c( K7 s3 y' D
新建MDK-ARM工程:
; a/ s, C) f- @& o) X$ c/ M在开始之前,我们还需要了解一些如何使用STM32CubeMX新建一个工程的知识。
- p# k( S! H1 y. s9 t. M6 z第一步:新建目录
3 [; @  E  G& B& g  z' c& b3 ^在电脑的磁盘上新建一个文件夹,这个文件夹并不是工程目录,至于工程目录是哪个,稍后会说。注意,文件件的名称,上级名称,上上级名称等,就是整个路径,一定不能含有中文!半个中文都不能有!不信你可以试试。为了演示,我就随便建个名为“Demo”的文件夹,如下图。
' F6 r* {/ u7 {- U  M9 }
新建目录.png

' R& Y2 q( y( k" k第二步:新建STM32CubeMX工程; q# M! I% {) N
1、打开STM32CubeMX,鼠标左键单击“New Project”中的“ACCESS TO MCU SELECTOR”,
) ^' }0 c+ V+ J. L8 {, e9 s
2-1.png

+ W% u% F7 U1 L+ a( u2、在弹出的“New Project from a MCU/MPU”窗口中的左侧的搜索框中输入你需要的单片机型号,我这里输入“STM32L552ZE”,
/ g' }, r5 |2 L4 c6 T/ v3、然后在左下角的“MCUs/MPUs List”中选择所需要的单片机型号,我这里选择STM32L552ZET6Q,双击单片机型号,
9 q& [& {2 H0 J. B. m' [
2-2.png
) ~' h5 c. ~/ [- S3 O+ T+ P
4、弹出窗口问你是否使用TrustZone,我选择No,然后就创建了一个STM32CubeMX工程( n. G. i: d& b: F# ?; h
2-3.png
" h( ]0 D( L+ \! x! g; c; u
3 h+ X0 I- _! @
第三步:配置STM32CubeMX工程
+ y" z/ B$ o3 l1 j1、设置工程属性,点击STM32CubeMX工程窗口上方的“Project Manager”选项,在最左边选项切换到“Project”,8 @6 X1 j* [8 y5 r! @- Z1 C
2、在“Project Location”中选择在第一步的时候创建的文件夹,
# v  F" J+ _6 }' r+ A" J+ X: g3、在“Project Name”中填入工程名(不能是中文),填入的工程名就是这个工程的根目录,路径如蓝色框所示,
5 ^1 ]6 f: ]) h) O$ O7 `  F$ s4、在“Toolchain / IDE”中选择“MDK-ARM”,其他保持默认即可,具体如下图所示,
4 ~% Q( B: X, w) w/ V
3-1.png

- W" O6 W0 g3 b( H/ U9 p
5、最左边选项切换到“Code Generator”,在右边的“STM32Cube MCU packages an embedded software packs”中有三个选项,意思分别为拷贝全部的库文件到工程目录中、仅拷贝需要的库文件到工程目录中、仅在工程文件中引用需要的库文件(库文件放在STM32CubeMX的安装目录中),我选择第二项,

/ ?$ `+ Z$ h) K% I2 _/ C2 q; q
3-2.png

0 o: B& d% H( o- K6、设置时钟属性,点击STM32CubeMX工程窗口上方的“Project Manager”选项,在“HCLK(MHz)”中输入需要的时钟频率,按回车键,在弹出的提示中选择Ok便可,我输入STM32L552ZET6Q的最大时钟频率:110MHz,其他的保持默认:内部高速时钟16MHz和内部低速时钟32KHz,
# A1 Z6 y' v% g5 L
3-3.png
& G6 C7 _5 k2 D% q
7、配置点亮LED灯的GPIO,NUCLEO_L552ZE_Q开发板上共有三盏LED灯,分别为绿三色,根据原理图提供的信息,绿三盏等分别接在了单片机的PA9PC7PB7引脚,我选择了最经典的红色LED,在“Pinout & Configuration”中使用鼠标左键点击单片机模型上的PA9引脚(右侧从上往下数第8个引脚),在小窗口中选择“GPIO_Output”,至此,STM32CubeMX工程就配置好了。  h* {$ H* i, E  A1 S
3-4.png 3-5.png
$ @: o& h0 c4 g
第四步:保存STM32CubeMX工程。
点击上方菜单中的“File”,选择“Save Project”即可。
第五步:生成MDK-ARM工程。
鼠标左键单击右上方的“GENERATE CODE”,软件便可根据之前的配置自动生成一个代码工程,如果在第一步中创建的目录中有中文,在这一部生成代码工程的时候就会出现错误提示,导致工程创建失败,因此需要特别注意。

# }1 ~8 ]& f1 l. m* P& e$ `
5-1.png
8 l$ A! L, z5 w9 `  ^
5-2.png
GPIO口电平输出功能:点亮一盏LED灯
第一步:了解一下工程的目录结构。

- a+ v! S9 `4 \. k; t( D
6-1.png
Dirvers:存放STM32的固件库以及ARM公司提供的CMSIS库,通常情况下不需要修改里面的文件内容;
inc:存放用户编写的.h文件,文件内容可修改;
MDK-ARM:存放MDK的工程文件,以及STM32的启动文件,文件内容通常情况下不需要修改;
src:存放用户编写的.c文件,文件内容可修改。

7 `) H. b. m4 t0 v  k5 M  p
第二步:在MDK-ARM文件夹内,双击打开.uvprojx后缀的工程文件,不需要改动,直接链接一遍,看是否有问题,结果0错误、0警告。
- H! \! r6 l& z
7-1.png
, N& {2 Y, {; _
第三步:打开main.c文件,找到主函数main(),在主函数的while循环内添加如下代码,意思为每隔500ms对PA9引脚进行电平取反操作,让红色LED灯闪烁。
HAL_GPIO_TogglePin(GPIOA, GPIO_PIN_9);
HAL_Delay(500);

$ O6 H' r3 ~# A
第四步:配置目标属性。
1、电机窗口上方的魔术棒,弹出配置窗口;
2、切换到“Debug”选项;
3、选择调试器,我使用ST-Link Debugger;
4、电机左侧的“Settings”

: n. o# Q( J- I
7-3.png
! e5 i3 ]/ t4 ~; n( D: L; I8 z
5、在弹出的窗口中切换到“Flash Download”选项,勾选“Reset and Run”,使得将程序烧录到单片机后就会自动复位并运行程序。

* u6 f+ {. A( i% b1 T8 B0 t
7-4.png

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

3 n: p- a) S5 }
8-1.jpg

/ e. ^  U" \& W9 Q" ^
2、电机MDK软件左上角的下载按钮 LOAD.png ,开始将程序下载到单片机上运行。
3、观察运行情况,红色LED灯开始闪烁,说明GPIO口能够正常输出高低电平。
# Y$ R4 j- ^2 U8 X
LED测试结果.gif
GPIO口电平输入功能:按键扫描
第一步:重新生成MDK-ARM工程。将MDK-ARM工程关闭,根据原理图知道开发板上的USER按键连接在单片机的PC13引脚,回到STM32CubeMX工程,在“Pinout & Configuration”中使用鼠标左键点击单片机模型上的PC13引脚(芯片模型的左侧从上往下数第7个引脚),在小窗口中选择“GPIO_Input”,保存STM32CubeMX工程并且点击“GENERATE CODE”重新生成代码。
$ ~1 p" ?$ {7 ?6 e
9-1.png

7 A( Q: L5 j7 {+ m4 V- \& m$ _; `
9-2.png
- K' F: K$ X4 S2 e: f& R* [
第二步:实现按键扫描代码
, T  C* S8 E4 r' ?3 l: L
1、打开MDK-ARM工程,在main.c源文件的主函数实现代码的开头添加如下代码,声明两个局部变量。
uint8_t u_cnt=0, u_cycle=10; //计数值:u_cnt ; 闪烁周期:2 * u_cycle * 10ms

# K# N% y% i1 c5 W4 R
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;
                }

" ^8 b) @3 G: u
                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);
" t0 h' F' \9 \6 d/ e$ v( {
4 y. Y3 u( |5 J2 P- [! ], J
3、修改完之后,编译链接一遍代码,将代码烧录到开发板上,用手按开发板左下方的USER按键,观察红色LED的闪烁频率变化情况,实际情况如下:

: |+ U) a! |2 C8 v$ e 按键测试结果.gif
/ a5 b- A% `# {$ R
仿位带操作:实现与51单片机一样的IO口操作方法
使用过正点原子Cortex_M3或者Cortex_M4系列单片机开发板的朋友都应该接触过位带操作功能,在Cortex_M3权威指南(中文)中的第5.5节有如下介绍
在CM3中,有两个区中实现了位带。其中一个是SRAM区的最低1MB范围,第二个则是片内外设区的最低1MB范围。这两个位带中的地址除了可以像普通的RAM一样使用外,它们还都有自己的“位带别名区”,位带别名区把每个比特膨胀成一个32位的字。当你通过位带别名区访问这些字时,就可以达到访问原始比特的目的。

4 J" [; x$ I, w8 O文中所述的位带区与位带别名区有如下对应关系
位带区与位带别名区的膨胀对应关系.png

( N2 A" S% V" t7 y5 w6 d7 aSTM32单片机的GPIO口地址就在片内外设区的最低1MB范围内,意思就是能够通过位带别名区中的每一个地址,操作到对应的片内外设区的外设寄存器中的一个bit,往位带别名区中的地址写1,则对应的片内外设区的外设寄存器中的相应位就会置1,读取位带别名区中的地址的值,就会返回对应的片内外设区的外设寄存器中的相应位的值,GPIO口的每一个Pin的电平就是由ODR寄存器中的每一个位来控制,以及通过IDR寄存器中的一个位来获取一个Pin的电平。但是,我查找过Cortex_M33的手册,并没有发现这个位带别名区,也就是Cortex_M33没有位带操作功能,但我可以通过自定义一个位域结构体来实现类似的操作,实现代码如下:
typedef struct: y7 t" h' L0 \4 {1 ^, w, I
{" U' p( b  H5 \2 Z& U3 o2 K
    uint16_t OD0 : 1;/ f0 n9 B2 _/ z" _  Y: z
    uint16_t OD1 : 1;0 F# U: h. [9 {& K$ x& m& Y6 G
    uint16_t OD2 : 1;6 C! r9 J, W- b  D$ Y) b2 g
    uint16_t OD3 : 1;+ H* f/ Z7 Y8 y8 `6 ]* u& m# [
    uint16_t OD4 : 1;
" ?6 Q) E- v' C! Z  p    uint16_t OD5 : 1;: n' z$ P3 f) n! |
    uint16_t OD6 : 1;
7 g% |* t. ?( k( c6 a% V    uint16_t OD7 : 1;  b) q6 d9 d7 |
    uint16_t OD8 : 1;
( U) b8 h' U6 G' Y    uint16_t OD9 : 1;/ y$ D; a6 y8 M; Y1 B3 @
    uint16_t OD10 : 1;4 Z0 H; I- B& W# h
    uint16_t OD11 : 1;+ }7 v3 I' d$ U* Q
    uint16_t OD12 : 1;
/ l! r1 A  O5 c* ]& i    uint16_t OD13 : 1;% l5 G- U5 U0 B1 ^
    uint16_t OD14 : 1;( r+ [1 H) f, C5 X8 t  n- Z) }
    uint16_t OD15 : 1;
& k+ q/ q& o$ P3 v  M4 n} ODR_TypeDef;' g4 O; e8 X2 ]" V# M/ _& [

/ N7 @, b& o# D$ m9 i, V#define PAin(n) ( ( GPIOA->IDR&(1 << (n)) )>>n )
+ C0 v& I$ u# W2 }#define PBin(n) ( ( GPIOB->IDR&(1 << (n)) )>>n ). b. l6 F6 n1 {: T# Z# h. ]9 y
#define PCin(n) ( ( GPIOC->IDR&(1 << (n)) )>>n )
1 {7 C9 N% p# E#define PDin(n) ( ( GPIOD->IDR&(1 << (n)) )>>n )+ F# u! S$ Z5 G. F
#define PEin(n) ( ( GPIOE->IDR&(1 << (n)) )>>n )
+ n/ K9 ^  _$ ~& V% ?: L$ i#define PFin(n) ( ( GPIOF->IDR&(1 << (n)) )>>n )
" j9 a  {! S" H8 ~; a' {/ I, q" _#define PGin(n) ( ( GPIOG->IDR&(1 << (n)) )>>n )
9 M3 s6 D: u, Z; a' E7 G  g; _8 |
#define PAout(n)  ( ((ODR_TypeDef *)(&(GPIOA->ODR)))->OD##n )7 n4 ^; q$ S, q6 A
#define PBout(n)  ( ((ODR_TypeDef *)(&(GPIOB->ODR)))->OD##n )
. j: i  Y. W/ z' e- u! n# U  K. O#define PCout(n)  ( ((ODR_TypeDef *)(&(GPIOC->ODR)))->OD##n )
8 _$ O6 z7 h( z4 T' O$ }#define PDout(n)  ( ((ODR_TypeDef *)(&(GPIOD->ODR)))->OD##n )
( s. m/ h: U+ {7 u" Y- O#define PEout(n)  ( ((ODR_TypeDef *)(&(GPIOE->ODR)))->OD##n )+ x% l& S0 h" v
#define PFout(n)  ( ((ODR_TypeDef *)(&(GPIOF->ODR)))->OD##n )+ m) `% i. E, g. g( d4 j6 ]
#define PGout(n)  ( ((ODR_TypeDef *)(&(GPIOG->ODR)))->OD##n )

* V5 b( r0 }% g! u1 s* R& p0 k9 v可将此代码封装到一个头文件内供调用,使用方法同正点原子的源码一样。
#define LED_R PAout(9)0 `; k+ m1 f; m2 \/ ]' D$ M* P
#define USER_BUTTON PCin(13)
/*仿位带操作*/! E* y7 x2 b  H1 w0 M5 \1 `
//按键检测程序
8 A/ K, u* ~# ]$ ~, }4 lif(USER_BUTTON == 1)3 n. [1 t/ A/ u6 @
{
5 F* e5 i& W9 m; {2 |$ B4 h        HAL_Delay(20);//消抖
* N' U% |9 R( _7 ~$ C6 s        if(USER_BUTTON == 1)2 h$ `9 n& n: j1 y( I5 a% }
        {
* }# a. U2 I- q+ [4 {, [( }/ ?                u_cycle += 10;6 c6 _3 t  n7 i. ^
                if(u_cycle>=50)1 z& |% N% i3 o+ _, s
                {
" G# ]9 f- ~8 J' ?* d% n9 D                        u_cycle = 10;( H+ ?4 z" p( E- |
                }4 L. L4 m* ~$ u  U- x' }, d
               
  E2 g+ K  ?4 P. D                while(USER_BUTTON == 1);& F" P% M- c4 a) M% D( [: ]; m
        }- S' I# A3 I' ~, i9 S
}
, [+ K  T& `: [" D//LED灯闪烁程序2 i0 Q( f- K* ?
u_cnt++;
$ ]' \5 ?- W" _8 n# d! jif(u_cnt >= u_cycle)
' a- @+ M* c% c5 V1 o{, p- E$ C* K$ |
        u_cnt = 0;
  }4 g7 m8 H, s2 T) V# ]' K        LED_R = !LED_R;
! N9 \# N: A. q' w: n; C3 V! ^6 D4 A' U}: F4 B/ k; B7 r) G& E: o
HAL_Delay(10);
$ S( u7 t% |# Z, S  q
总结:
0 l1 y; ^8 G7 F6 t; V* i可见,在STM32CubeMX的帮助下,实现一些基础的功能代码还是比较方便的,加之有HAL固件库的支持,无论单片机的底层寄存器如何变化,HAL固件库都已经统一封装成一致的函数名,使用起来也非常方便。本次测试的GPIO口操作,主要使用到了两个函数:HAL_GPIO_TogglePin和HAL_GPIO_ReadPin,两个函数的实现也比较简单,都是直接操作寄存器:
9 u3 K2 R6 k2 B& b: j
void HAL_GPIO_TogglePin(GPIO_TypeDef *GPIOx, uint16_t GPIO_Pin)( w- K; \/ x& a3 m
{6 Q) \$ n6 }* \! T' d, T) D* v
  /* Check the parameters */
, B, x, V$ @, Y- i2 P' C  assert_param(IS_GPIO_PIN(GPIO_Pin));( P; k* b9 p' v3 ^3 I

7 V- I# W" t% @8 x  if ((GPIOx->ODR & GPIO_Pin) != 0U)* x0 A2 D6 Z7 O7 i, `
  {
6 K0 `5 P8 W5 N8 p7 w" b; g: E    GPIOx->BRR = (uint32_t)GPIO_Pin;
- B' ?( b- a( S4 o8 j  }
! `- I, ]/ G. P& V& @3 Z2 r' [  else
' P) m6 @. y( |. J* J& D. E# J  {
/ b4 z% V# h; w0 F1 b. J8 y" c' y    GPIOx->BSRR = (uint32_t)GPIO_Pin;
$ r3 E# p" t1 L4 L4 N$ A1 w% Y  }" ]9 ^5 R; l' J0 b. v
}
GPIO_PinState HAL_GPIO_ReadPin(GPIO_TypeDef *GPIOx, uint16_t GPIO_Pin)# y1 A0 P/ c4 l, Z; J
{3 T! c! i( X6 R: x' M7 `
  GPIO_PinState bitstatus;
4 i* O  C: J% m
3 F9 m1 V0 v1 n- v  /* Check the parameters */, D1 g* k; c6 W5 c9 W- l+ [
  assert_param(IS_GPIO_PIN(GPIO_Pin));* S3 }  G/ M4 g0 i% `% O+ Q

6 e. X+ {1 f" @6 \5 x9 v2 @  if((GPIOx->IDR & GPIO_Pin) != 0U)  u7 k$ a7 t, Q4 e3 [7 J
  {
4 ~5 `# S, Z' w& K7 ~$ v* |( ?    bitstatus = GPIO_PIN_SET;6 G7 `, c$ u* u& ~
  }
0 ?/ G3 [  j5 @% m6 E, m3 C  else& {9 `& v/ i5 G. B$ `. I
  {5 P9 _5 E+ d0 ]# H
    bitstatus = GPIO_PIN_RESET;+ J+ n3 a& r6 }
  }. N1 K( r: z+ ?
  return bitstatus;: A* ?. d( e- R4 o; {
}
/ F$ D1 }( l7 G5 ], q6 ]+ l
本次我们介绍了GPIO口的使用,由于篇幅已经太多了,花了一天时间才写完,等下次有空再介绍其他一些基本的功能。还是那句话,如果朋友们觉得有任何疑问,欢迎在评论区向我提出。感谢您的关注,谢谢。
4 u9 X- \" o. Y4 I- W: N1 B2 u$ r2 m# t" A* n- ?# [
GPIO_Demo.zip (990.25 KB, 下载次数: 8)
7-2.png
收藏 2 评论0 发布时间:2020-3-13 17:21

举报

0个回答
关于意法半导体
我们是谁
投资者关系
意法半导体可持续发展举措
创新和工艺
招聘信息
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
关注我们
st-img 微信公众号
st-img 手机版