OpenEdv-开源电子网

 找回密码
 立即注册
正点原子全套STM32/Linux/FPGA开发资料,上千讲STM32视频教程免费下载...
查看: 11941|回复: 11

【ALIENTEK 战舰STM32开发板例程系列连载+教学】第六章 跑马灯实验

[复制链接]

530

主题

11万

帖子

34

精华

管理员

Rank: 12Rank: 12Rank: 12

积分
165540
金钱
165540
注册时间
2010-12-1
在线时间
2117 小时
发表于 2013-1-9 16:40:26 | 显示全部楼层 |阅读模式

第六章 跑马灯实验  

STM32最简单的外设莫过于IO口的高低电平控制了,本章将通过一个经典的跑马灯程序,带大家开启STM32之旅,通过本章的学习,你将了解到STM32IO口作为输出使用的方法。在本章中,我们将通过代码控制ALIENTEK战舰STM32开发板上的两个LEDDS0DS1交替闪烁,实现类似跑马灯的效果。 本章分为如下四个小节:

    6.1 STM32 IO口简介

    6.2, 硬件设计

    6.3, 软件设计

    6.4, 仿真与下载

 

 

6.1 STM32 IO简介

 

本章将要实现的是控制ALIENTEK战舰STM32开发板上的两个LED实现一个类似跑马灯的效果,该实验的关键在于如何控制STM32IO口输出。了解了STM32IO口如何输出的,就可以实现跑马灯了。通过这一章的学习,你将初步掌握STM32基本IO口的使用,而这是迈向STM32的第一步。

STM32IO口可以由软件配置成如下8种模式:

1、输入浮空

2、输入上拉

3、输入下拉

4、模拟输入

5、开漏输出

6、推挽输出

7、推挽式复用功能

8、开漏复用功能

每个IO口可以自由编程,但IO口寄存器必须要按32位字被访问。STM32的很多IO口都是5V兼容的,这些IO口在与5V电平的外设连接的时候很有优势,具体哪些IO口是5V兼容的,可以从该芯片的数据手册管脚描述章节查到(I/O LevelFT的就是5V电平兼容的)。

STM32的每个IO端口都有7个寄存器来控制。他们分别是:配置模式的232位的端口配置寄存器CRLCRH232位的数据寄存器IDRODR132位的置位/复位寄存器BSRR;一个16位的复位寄存器BRR132位的锁存寄存器LCKR;这里我们仅介绍常用 的几个寄存器,我们常用的IO端口寄存器只有4个:CRLCRHIDRODR

CRLCRH控制着每个IO口的模式及输出速率。

STM32IO口位配置表如表6.1.1所示:


6.1.1 STM32IO口位配置表

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


6.1.2 STM32输出模式配置表

接下来我们看看端口低配置寄存器CRL的描述,如图6.1.1所示:


6.1.1 端口低配置寄存器CRL各位描述

该寄存器的复位值为0X4444 4444,从图6.1.1可以看到,复位值其实就是配置端口为浮空输入模式。从上图还可以得出:STM32CRL控制着每组IO端口(A~G)的低8位的模式。每个IO端口的位占用CRL4个位,高两位为CNF,低两位为MODE。这里我们可以记住几个常用的配置,比如0X0表示模拟输入模式(ADC用)、0X3表示推挽输出模式(做输出口用,50M速率)、0X8表示上/下拉输入模式(做输入口用)、0XB表示复用输出(使用IO口的第二功能,50M速率)。

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

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

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

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

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

通过这3句话的配置,我们就设置了PC11为上拉输入,PC12为推挽输出。

IDR是一个端口输入数据寄存器,只用了低16位。该寄存器为只读寄存器,并且只能以16位的形式读出。该寄存器各位的描述如图6.1.2所示:



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

要想知道某个IO口的状态,你只要读这个寄存器,再看某个位的状态就可以了。使用起来是比较简单的。

ODR是一个端口输出数据寄存器,也只用了低16位。该寄存器为可读写,从该寄存器读出来的数据可以用于判断当前IO口的输出状态。而向该寄存器写数据,则可以控制某个IO口的输出电平。该寄存器的各位描述如图6.1.3所示:


6.1.3 端口输出数据寄存器ODR各位描述

了解了这几个寄存器,我们就可以开始跑马灯实验的真正设计了。关于IO口更详细的介绍,请参考《STM32参考手册》第1058.1节。

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

1、             STM32CRLCRH就相当于AVRDDR寄存器,用来控制IO口的方向,只不过STM32CRLCRH功能更强大一点罢了。

2、             STM32ODR就相当于AVRPORT,都是用来控制IO口的输出电平或者上下拉电阻的。

3、             STM32IDR就相当于AVRPIN,都是用来存储IO口当前的输入状态(高低电平)的。

除此之外,STM32还有BSRRBRRLCKR等几个寄存器用于控制IO口,这点是AVR所没有的。

 

6.2 硬件设计

本章用到的硬件只有LEDDS0DS1)。其电路在ALIENTEK战舰STM32开发板上默认是已经连接好了的。DS0PB5DS1PE5。所以在硬件上不需要动任何东西。其连接原理图如图6.2.1下:



6.2.1 LEDSTM32连接原理图

6.3 软件设计

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

                    


6.3.1 新建HARDWARE文件夹

然后我们打开USER文件夹下的TEST.Uv2工程,按

按钮新建一个文件,然后保存在HARDWARE->LED文件夹下面,保存为led.c。在该文件中输入如下代码:

#include "led.h"

//初始化PB5PE5为输出口.并使能这两个口的时钟        

//LED IO初始化

void LED_Init(void)

{

    RCC->APB2ENR|=1<<3;    //使能PORTB时钟     

    RCC->APB2ENR|=1<<6;    //使能PORTE时钟         

    GPIOB->CRL&=0XFF0FFFFF;

    GPIOB->CRL|=0X00300000;//PB.5 推挽输出       

    GPIOB->ODR|=1<<5;      //PB.5 输出高                                     

    GPIOE->CRL&=0XFF0FFFFF;

    GPIOE->CRL|=0X00300000;//PE.5推挽输出

    GPIOE->ODR|=1<<5;      //PE.5输出高

}

该代码里面就包含了一个函数void LED_Init(void),该函数的功能就是用来实现配置PB5PE5为推挽输出。这里需要注意的是:在配置STM32外设的时候,任何时候都要先使能该外设的时钟!APB2ENRAPB2总线上的外设时钟使能寄存器,其各位的描述如图6.3.2所示:


6.3.2 寄存器APB2ENR各位描述

我们要使能的PORTBPORTE的时钟使能位,分别在bit3bit6,只要将这两位置1就可以使能PORTAPORTD的时钟了。该寄存器还包括了很多其他外设的时钟使能。大家在以后会慢慢使用到的。关于这个寄存器的详细说明在《STM32参考手册》的第70页。

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

保存led.c代码,然后我们按同样的方法,新建一个led.h文件,也保存在LED文件夹下面。在led.h中输入如下代码:

#ifndef __LED_H

#define __LED_H

#include "sys.h"

//LED端口定义

#define LED0 PBout(5)// DS0

#define LED1 PEout(5)// DS1

void LED_Init(void);//初始化                           

#endif

这段代码里面最关键就是2个宏定义:

#define LED0 PBout(5)// DS0

#define LED1 PEout(5)// DS1

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

#define LED0 (1<<5)  //led0   PB5

#define LED1 (1<<5)  //led1   PE5

#define LED0_SET(x) GPIOB->ODR=(GPIOB->ODR&~LED0)|(x ? LED00)

#define LED1_SET(x) GPIOE->ODR=(GPIOE->ODR&~LED1)|(x ? LED10)

后者通过LED0_SET(0)LED0_SET(1)来控制PB5的输出01。而前者的类似操作为:LED0=0LED0=1。显然前者简单很多,从而可以看出位带操作带来的好处。以后像这样的IO口操作,我们都使用位带操作来实现,而不使用第二种方法。

led.h也保存一下。接着,我们在Manage Components管理里面新建一个HARDWARE的组,并把led.c加入到这个组里面,如图6.3.3所示:


6.3.3 给工程新增HARDWARE

单击OK,回到工程,然后你会发现在Project Workspace里面多了一个HARDWARE的组,在改组下面有一个led.c的文件。如图6.3.4所示:


6.3.4新增HARDWARE

然后用之前介绍的方法(在3.2节介绍的)将led.h头文件的路径加入到工程里面。回到主界面,在main函数里面编写如下代码:

#include "sys.h"

#include "usart.h"     

#include "delay.h" 

#include "led.h"

//ALIENTEK战舰STM32开发板实验1

//跑马灯实验 

int main(void)

{                

    Stm32_Clock_Init(9);//系统时钟设置

    delay_init(72);     //延时初始化

    LED_Init();         //初始化与LED连接的硬件接口

    while(1)

    {

        LED0=0;

        LED1=1;

        delay_ms(300);

        LED0=1;

        LED1=0;

        delay_ms(300);

    }  

}

代码包含了#include "led.h"这句,使得LED0LED1LED_Init等能在main函数里被调用。接下来,main函数先配置系统时钟为72M,然后把延时函数初始化一下。接着就是调用LED_Init来初始化PB5PE5为输出。最后在死循环里面实现LED0LED1交替闪烁,间隔为300ms

然后按

,编译工程,得到结果如图6.3.5所示:

                             

6.3.5 编译结果

可以看到没有错误,也没有警告。接下来,我们就先进行软件仿真,验证一下是否有错误的地方,然后下载到Mini STM32看看实际运行的结果。

 

6.4 仿真与下载

 

此代码,我们先进行软件仿真,看看结果对不对,根据软件仿真的结果,然后再下载到ALIENTEK战舰STM32板子上面看运行是否正确。

首先,我们进行软件仿真(请先确保Options for Targetà Debug选项卡里面已经设置为Use Simulator)。先按

开始仿真,接着按

,显示逻辑分析窗口,点击Setup,新建两个信号PORTB.5PORTE.5,如图6.4.1所示:

                        


6.4.1 逻辑分析设置

Display Type选择bit,然后单击Close关闭该对话框,可以看到逻辑分析窗口出来了两个信号,如图6.4.2所示:


6.4.2 设置后的逻辑分析窗口

接着,点击 ,开始运行。运行一段时间之后,按 按钮,暂停仿真回到逻辑分析窗口,可以看到如图6.4.3所示的波形:

         

             

6.4.3 仿真波形

这里注意Gird要调节到0.25s左右比较合适,可以通过Zoom里面的In按钮来放大波形,通过Out按钮来缩小波形,或者按All显示全部波形。从上图中可以看到PORTB.5PORTE.5交替输出,周期可以通过中间那根红线来测量。至此,我们的软件仿真已经顺利通过。

在软件仿真没有问题了之后,我们就可以把代码下载到开发板上,看看运行结果是否与我们仿真的一致。运行结果如图6.4.4所示:

                          


6.4.4 执行结果

至此,我们的第一章的学习就结束了,本章作为STM32的入门第一个例子,详细介绍了STM32IO口操作,同时巩固了前面的学习,并进一步介绍了MDK的软件仿真功能。希望大家好好理解一下。

 

 

《STM32开发指南》第六章 跑马灯实验.pdf

870.35 KB, 下载次数: 1100

实验1 跑马灯实验.rar

24.11 KB, 下载次数: 1155

我是开源电子网www.openedv.com站长,有关站务问题请与我联系。
正点原子STM32开发板购买店铺http://openedv.taobao.com
正点原子官方微信公众平台,点击这里关注“正点原子”
正点原子逻辑分析仪DL16劲爆上市
回复

使用道具 举报

530

主题

11万

帖子

34

精华

管理员

Rank: 12Rank: 12Rank: 12

积分
165540
金钱
165540
注册时间
2010-12-1
在线时间
2117 小时
 楼主| 发表于 2013-1-9 16:55:21 | 显示全部楼层
论坛的朋友,对本例程有任何疑问的,请直接跟帖,我将尽快回复。
我是开源电子网www.openedv.com站长,有关站务问题请与我联系。
正点原子STM32开发板购买店铺http://openedv.taobao.com
正点原子官方微信公众平台,点击这里关注“正点原子”
回复 支持 反对

使用道具 举报

0

主题

16

帖子

0

精华

新手上路

积分
37
金钱
37
注册时间
2013-1-29
在线时间
0 小时
发表于 2013-3-25 01:28:19 | 显示全部楼层
回复【2楼】正点原子:
---------------------------------
原子哥,想请教 #define LED0_SET(x) GPIOB->ODR=(GPIOB->ODR&~LED0)|(x ? LED0:0) 这句话,如果他给LED0位清零之后,又与后半部分(x ? LED0:0)的结果按位或。可是这个结果直接赋给 GPIOB->ODR是不是会影响ODR寄存器的其他位?如果不想影响的话是不是用个 “|=”符号就可以解决了?

谢谢
回复 支持 反对

使用道具 举报

530

主题

11万

帖子

34

精华

管理员

Rank: 12Rank: 12Rank: 12

积分
165540
金钱
165540
注册时间
2010-12-1
在线时间
2117 小时
 楼主| 发表于 2013-3-25 10:05:50 | 显示全部楼层
不需要。
你可以实际自己测试下。
我是开源电子网www.openedv.com站长,有关站务问题请与我联系。
正点原子STM32开发板购买店铺http://openedv.taobao.com
正点原子官方微信公众平台,点击这里关注“正点原子”
回复 支持 反对

使用道具 举报

0

主题

16

帖子

0

精华

新手上路

积分
37
金钱
37
注册时间
2013-1-29
在线时间
0 小时
发表于 2013-3-27 22:34:38 | 显示全部楼层
回复【4楼】正点原子:
---------------------------------
我那天晚上测了,然后发现是我没计算明白,呵呵!谢谢你!!
回复 支持 反对

使用道具 举报

3

主题

13

帖子

0

精华

初级会员

Rank: 2

积分
58
金钱
58
注册时间
2012-4-22
在线时间
4 小时
发表于 2013-7-25 17:18:08 | 显示全部楼层
原子哥的版子是STM32ZET6的芯片么,我下程序到自己的板子上有点问题。
回复 支持 反对

使用道具 举报

530

主题

11万

帖子

34

精华

管理员

Rank: 12Rank: 12Rank: 12

积分
165540
金钱
165540
注册时间
2010-12-1
在线时间
2117 小时
 楼主| 发表于 2013-7-25 20:52:57 | 显示全部楼层
回复【6楼】zjy123go:
---------------------------------
是的.
我是开源电子网www.openedv.com站长,有关站务问题请与我联系。
正点原子STM32开发板购买店铺http://openedv.taobao.com
正点原子官方微信公众平台,点击这里关注“正点原子”
回复 支持 反对

使用道具 举报

11

主题

71

帖子

0

精华

初级会员

Rank: 2

积分
135
金钱
135
注册时间
2014-1-3
在线时间
0 小时
发表于 2014-10-4 20:42:23 | 显示全部楼层
原子哥,请问下,你的系统时钟是在哪里设置的呢?只在delay.c里看到了fac_us=SystemCoreClock/8000000; //为系统时钟的1/8,没找到你在哪里定义了SystemCoreClock
回复 支持 反对

使用道具 举报

530

主题

11万

帖子

34

精华

管理员

Rank: 12Rank: 12Rank: 12

积分
165540
金钱
165540
注册时间
2010-12-1
在线时间
2117 小时
 楼主| 发表于 2014-10-4 22:50:09 | 显示全部楼层
回复【8楼】jidian0177:
---------------------------------
你看下时钟树图,就知道了了。
我是开源电子网www.openedv.com站长,有关站务问题请与我联系。
正点原子STM32开发板购买店铺http://openedv.taobao.com
正点原子官方微信公众平台,点击这里关注“正点原子”
回复 支持 反对

使用道具 举报

2

主题

8

帖子

0

精华

新手上路

积分
43
金钱
43
注册时间
2011-11-12
在线时间
1 小时
发表于 2015-7-23 19:50:46 | 显示全部楼层
( 然后用之前介绍的方法(在3.2节介绍的)将led.h头文件的路径加入到工程里面。回到主界面,在main函数里面编写如下代码: )

這個步驟意思是在test.c內的int main(void) 加代碼 ?
還是開一個新的main.c 檔案,在內加上代碼啊?
回复 支持 反对

使用道具 举报

530

主题

11万

帖子

34

精华

管理员

Rank: 12Rank: 12Rank: 12

积分
165540
金钱
165540
注册时间
2010-12-1
在线时间
2117 小时
 楼主| 发表于 2015-7-23 22:51:10 | 显示全部楼层
回复【10楼】53861:
---------------------------------
在之前的代码基础上改
我是开源电子网www.openedv.com站长,有关站务问题请与我联系。
正点原子STM32开发板购买店铺http://openedv.taobao.com
正点原子官方微信公众平台,点击这里关注“正点原子”
回复 支持 反对

使用道具 举报

2

主题

8

帖子

0

精华

新手上路

积分
43
金钱
43
注册时间
2011-11-12
在线时间
1 小时
发表于 2015-7-23 23:22:29 | 显示全部楼层
謝謝!!! 
原來是!!!
在test.c 內的 int main(void)加入代碼成功了!!! 
注意仿真是用PORTA.8及PORTD.2!!!
OK了!!!!
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则



关闭

原子哥极力推荐上一条 /2 下一条

正点原子公众号

QQ|手机版|OpenEdv-开源电子网 ( 粤ICP备12000418号-1 )

GMT+8, 2025-6-20 06:57

Powered by OpenEdv-开源电子网

© 2001-2030 OpenEdv-开源电子网

快速回复 返回顶部 返回列表