[应用相关] Alientek SMT32开发板 跑马灯实验

[复制链接]
1373|24
 楼主| decoding 发表于 2020-2-7 14:10 | 显示全部楼层 |阅读模式
原地址,附件下载:http://www.openedv.com/posts/list/18.htm
通过本节的学习,你将了解到STM32的IO口作为输出使用的方法。本节分为如下几个小节:
     3.1.1 STM32 IO口简介
     3.1.2 硬件设计
     3.1.3 软件设计
     3.1.4 仿真与下载

 楼主| decoding 发表于 2020-2-7 14:11 | 显示全部楼层
3.1.1 STM32 IO简介



作为所有开发板的经典入门实验,莫过于跑马灯了。ALIENTEKMiniSTM32开发板板载了2个LED,DS0和DS1,本实验将通过教你如何控制这两个灯实现交替闪烁的类跑马灯效果。

该实验的关键在于如何控制STM32的IO口输出。了解了STM32的IO口如何输出的,就可以实现跑马灯了。通过这一节的学习,你将初步掌握STM32基本IO口的使用,而这是迈向STM32的第一步。
 楼主| decoding 发表于 2020-2-7 14:11 | 显示全部楼层
STM32的IO口可以由软件配置成8种模式:

1、输入浮空

2、输入上拉

3、输入下拉

4、模拟输入

5、开漏输出

6、推挽输出

7、推挽式复用功能

8、开漏复用功能
 楼主| decoding 发表于 2020-2-7 14:12 | 显示全部楼层
每个IO口可以自由编程,单IO口寄存器必须要按32位字被访问。STM32的很多IO口都是5V兼容的,这些IO口在与5V电平的外设连接的时候很有优势,具体哪些IO口是5V兼容的,可以从该芯片的数据手册管脚描述章节查到(I/O Level标FT的就是5V电平兼容的)。

STM32的每个IO端口都有7个寄存器来控制。他们分别是:配置模式的2个32位的端口配置寄存器CRL和CRH;2个32位的数据寄存器IDR和ODR;1个32位的置位/复位寄存器BSRR;一个16位的复位寄存器BRR;1个32位的锁存寄存器LCKR;这里我们仅介绍常用 的几个寄存器,我们常用的IO端口寄存器只有4个:CRL、CRH、IDR、ODR。

CRL和CRH控制着每个IO口的模式及输出速率。
 楼主| decoding 发表于 2020-2-7 14:13 | 显示全部楼层
STM32的IO口位配置表如表3.1.1.1所示:

823785e3cffdedad1c.png
表3.1.1.1 STM32的IO口位配置表

STM32输出模式配置如表3.1.1.2所示:

740205e3cfff284f48.png
表3.1.1.2 STM32输出模式配置表
 楼主| decoding 发表于 2020-2-7 14:13 | 显示全部楼层
接下来我们看看端口低配置寄存器CRL的描述,如下图所示:

344505e3d001e99451.png
图3.1.1.1端口低配置寄存器CRL各位描述
 楼主| decoding 发表于 2020-2-7 14:14 | 显示全部楼层
该寄存器的复位值为0X4444 4444,从上图可以看到,复位值其实就是配置端口为浮空输入模式。从上图还可以得出:STM32的CRL控制着每个IO端口(A~G)的低8位的模式。每个IO端口的位占用CRL的4个位,高两位为CNF,低两位为MODE。这里我们可以记住几个常用的配置,比如0X4表示模拟输入模式(ADC用)、0X3表示推挽输出模式(做输出口用,50M速率)、0X8表示上/下拉输入模式(做输入口用)、0XB表示复用输出(使用IO口的第二功能,50M速率)。

CRH的作用和CRL完全一样,只是CRL控制的是低8位输出口,而CRH控制的是高8位输出口。这里我们对CRH就不做详细介绍了。

给个实例,比如我们要设置PORTC的11位为上拉输入,12位为推挽输出。代码如下:

GPIOC->CRH&=0XFFF00FFF;//清掉这2个位原来的设置,同时也不影响其他位的设置

GPIOC->CRH|=0X00038000;  //PC11输入,PC12输出

GPIOC->ODR=1<<11;//PC11上拉

通过这3句话的配置,我们就设置了PC11为上拉输入,PC12为推挽输出。
 楼主| decoding 发表于 2020-2-7 14:14 | 显示全部楼层
IDR是一个端口输入数据寄存器,只用了低16位。该寄存器为只读寄存器,并且只能以16位的形式读出。该寄存器各位的描述如下图所示:

814655e3d004c6896a.png

                   图3.1.1.2端口输入数据寄存器IDR各位描述

要想知道某个IO口的状态,你只要读这个寄存器,再看某个位的状态就可以了。使用起来是比较简单的。
 楼主| decoding 发表于 2020-2-7 14:15 | 显示全部楼层
ODR是一个端口输出数据寄存器,也只用了低16位。该寄存器虽然为可读写,但是从该寄存器读出来的数据都是0。只有写是有效的。其作用就是控制端口的输出。该寄存器的各位描述如下图所示:

163505e3d006355b10.png

                   图3.1.1.3端口输出数据寄存器ODR各位描述
 楼主| decoding 发表于 2020-2-7 14:15 | 显示全部楼层
了解了这几个寄存器,我们就可以开始跑马灯实验的真正设计了。关于IO口更详细的介绍,请参考《STM32参考手册》第69页7.1节。

在此,我们可以总结一下,对于学过AVR的人来说,我们都知道AVR的IO口由3个寄存器控制:DDR、 PORT、PIN。这里我们可以拿STM32的IO控制寄存器和AVR的来个类比:

1,            STM32的CRL和CRH就相当于AVR的DDR寄存器,用来控制IO口的方向,只不过STM32的CRL和CRH功能更强大一点罢了。

2,            STM32的ODR就相当于AVR的PORT,都是用来控制IO口的输出电平或者上下拉电阻的。

3,            STM32的IDR就相当于AVR的PIN,都是用来存储IO口当前的输入状态(高低电平)的。

除此之外,STM32还有BSRR、BRR、LCKR等几个寄存器用于控制IO口,这点是AVR所没有的。
 楼主| decoding 发表于 2020-2-7 14:15 | 显示全部楼层
3.1.2 硬件设计



该实验的硬件电路在ALIENTEM Mini STM32开发板上已经连接好了。DS0接PA8,DS1接PD2。所以在硬件上不需要动任何东西。其连接原理图如下:

974995e3d008b6f6f2.png

                   图3.1.2.1 LED与STM32连接原理图
 楼主| decoding 发表于 2020-2-7 14:16 | 显示全部楼层
3.1.3 软件设计



首先,找到之前新建的TEST工程,在该文件夹下面新建一个HARDWARE的文件夹,用来存储以后与硬件相关的代码。然后在HARDWARE文件夹下新建一个LED文件夹,用来存放与LED相关的代码。

然后我们打开USER文件夹下的TEST.Uv2工程,按按钮新建一个文件,然后保存在HARDWARE->LED文件夹下面,保存为led.c。在该文件中输入如下代码:

  1. #include <stm32f10x_lib.h>   

  2. #include "led.h"

  3. //Mini STM32开发板

  4. //LED驱动代码                             

  5. //正点原子@ALIENTEK

  6. //2010/5/27



  7. // V1.0

  8. //初始化PA8和PD2为输出口.并使能这两个口的时钟                       

  9.       

  10. //LED IO初始化

  11. void LED_Init(void)

  12. {

  13.      RCC->APB2ENR|=1<<2;    //使能PORTA时钟               

  14.      RCC->APB2ENR|=1<<5;    //使能PORTD时钟                           

  15.      GPIOA->CRH&=0XFFFFFFF0;

  16.      GPIOA->CRH|=0X00000003;//PA8推挽输出      

  17.    GPIOA->ODR|=1<<8;     //PA8 输出高                                                                                                               

  18.      GPIOD->CRL&=0XFFFFF0FF;

  19.      GPIOD->CRL|=0X00000300;//PD.2推挽输出

  20.      GPIOD->ODR|=1<<2;      //PD.2输出高

  21. }
 楼主| decoding 发表于 2020-2-7 14:16 | 显示全部楼层
该代码里面就包含了一个函数void LED_Init(void),该函数的功能就是用来实现配置PA8和PD2为推挽输出。在配置STM32外设的时候,任何时候都要先使能该外设的时钟!APB2ENR是APB2总线上的外设时钟使能寄存器,其各位的描述如下:

120595e3d00d527ef7.png
                   图3.1.3.2寄存器APB2ENR各位描述
 楼主| decoding 发表于 2020-2-7 14:17 | 显示全部楼层
我们要使能的PORTA和PORTD的时钟使能位,分别在bit2和bit5,只要将这两位置1就可以使能PORTA和PORTD的时钟了。该寄存器还包括了很多其他外设的时钟使能。大家在以后会慢慢使用到的。关于这个寄存器的详细说明在《STM32参考手册》的第61页。

在设置完时钟之后就是配置完时钟之后,LED_Init配置了PA8和PD2的模式为推挽输出,并且默认输出1。这样就完成了对这两个IO口的初始化。

保存led.c代码,然后我们按同样的方法,新建一个led.h文件,也保存在LED文件夹下面。在led.h中输入如下代码:
  1. #ifndef __LED_H
  2. #define __LED_H  
  3. #include "sys.h"
  4. //Mini STM32开发板
  5. //LED驱动代码                        
  6. //正点原子@ALIENTEK
  7. //2010/5/27

  8. //LED端口定义
  9. #define LED0 PAout(8)// PA8
  10. #define LED1 PDout(2)// PD2         
  11. void LED_Init(void);//初始化  
  12. #endif
 楼主| decoding 发表于 2020-2-7 14:18 | 显示全部楼层
这段代码里面最关键就是2个宏定义:

#define LED0 PAout(8)// PA8

#define LED1 PDout(2)// PD2

这里使用的是位带操作来实现操作某个IO口的1个位的,关于位带操作前面已经有介绍,这里不再多说。需要说明的是,这里可以使用另外一种操作方式实现。如下:

#define       LED0 (1<<8)  //led0 PA8

#define       LED1 (1<<2)  //led1       PD2

#define LED0_SET(x) GPIOA->ODR=(GPIOA->ODR&~LED0)|(x ? LED0:0)

#define LED1_SET(x) GPIOD->ODR=(GPIOD->ODR&~LED1)|(x ? LED1:0)           

后者通过LED0_SET(0)和LED0_SET(1)来控制PA8的输出0和1。而前者的类似操作为:LED0=0和LED0=1。显然前者简单很多,从而可以看出位带操作带来的好处。以后像这样的IO口操作,我们都使用位带操作来实现,而不使用第二种方法。
 楼主| decoding 发表于 2020-2-7 14:18 | 显示全部楼层
将led.h也保存一下。接着,我们在Manage Components管理里面新建一个HARDWARE的组,并把led.c加入到这个组里面,如下图所示:

726595e3d014017b0d.png
                       图3.1.3.3给工程新增HARDWARE组
 楼主| decoding 发表于 2020-2-7 14:19 | 显示全部楼层
单击OK,回到工程,然后你会发现在Project Workspace里面多了一个HARDWARE的组,在改组下面有一个led.c的文件。如下图所示:

333615e3d01569bd45.png
                    图3.1.3.4给工程新增HARDWARE组
 楼主| decoding 发表于 2020-2-7 14:20 | 显示全部楼层
然后用之前介绍的方法将led.h头文件的路径加入到工程里面。回到主界面,在main函数里面编写如下代码:
  1. #include <stm32f10x_lib.h>
  2. #include "sys.h"
  3. #include "usart.h"               
  4. #include "delay.h"  
  5. #include "led.h"                  
  6. //Mini STM32开发板范例代码1
  7. //跑马灯实验
  8. //正点原子@ALIENTEK
  9. //2010.5.27   
  10. int main(void)
  11. {                                       
  12.      Stm32_Clock_Init(9); //系统时钟设置
  13.      delay_init(72);        //延时初始化
  14.      LED_Init();                                //初始化与LED连接的硬件接口
  15.      while(1)
  16.      {
  17.                  LED0=0;
  18.                  LED1=1;
  19.                  delay_ms(300);
  20.                  LED0=1;
  21.                  LED1=0;
  22.                  delay_ms(300);
  23.      }         
  24. }
 楼主| decoding 发表于 2020-2-7 14:22 | 显示全部楼层
代码先包含了#include "led.h"这句,使得LED0、LED1、LED_Init等能在main函数里被调用。接下来,main函数先配置系统时钟为72M,然后把延时函数初始化一下。接着就是调用LED_Init来初始化PA8和PD2为输出。最后在死循环里面实现LED0和LED1交替闪烁,间隔为300ms。

然后按,编译工程,得到结果如下图所示:

9d2011458a2137e0837835113a5a7805_812.jpg
                               图3.1.3.5编译结果

可以看到没有错误,也没有警告。接下来,我们就先进行软件仿真,验证一下是否有错误的地方,然后下载到Mini STM32看看实际运行的结果。
 楼主| decoding 发表于 2020-2-7 14:23 | 显示全部楼层
资料如下:

ALIENTEK MINISTM32 实验1 跑马灯.rar (13.82 KB, 下载次数: 1)
跑马灯.pdf (786.94 KB, 下载次数: 0)
您需要登录后才可以回帖 登录 | 注册

本版积分规则

30

主题

469

帖子

0

粉丝
快速回复 在线客服 返回列表 返回顶部