原创 【连载】【ALIENTEK MiniSTM32 开发板】STM32不完全手册--跑马灯实验(实验一)

2010-7-9 17:13 7291 11 21 分类: MCU/ 嵌入式

3.1 跑马灯实验
 
通过本节的学习,你将了解到STM32的IO口作为输出使用的方法。本节分为如下几个小节:
 3.1.1 STM32 IO口简介
 3.1.2 硬件设计
 3.1.3 软件设计
 3.1.4 仿真与下载


3.1.1 STM32 IO简介


作为所有开发板的经典入门实验,莫过于跑马灯了。ALIENTEK MiniSTM32开发板板载了2个LED,DS0和DS1,本实验将通过教你如何控制这两个灯实现交替闪烁的类跑马灯效果。
该实验的关键在于如何控制STM32的IO口输出。了解了STM32的IO口如何输出的,就可以实现跑马灯了。通过这一节的学习,你将初步掌握STM32基本IO口的使用,而这是迈向STM32的第一步。
STM32的IO口可以由软件配置成8种模式:
1、输入浮空
2、输入上拉
3、输入下拉
4、模拟输入
5、开漏输出
6、推挽输出
7、推挽式复用功能
8、开漏复用功能
每个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口的模式及输出速率。
STM32的IO口位配置表如表3.1.1.1所示:
 点击看大图
                      表3.1.1.1 STM32的IO口位配置表
STM32输出模式配置如表3.1.1.2所示:


点击看大图 
               表3.1.1.2 STM32输出模式配置表
 接下来我们看看端口低配置寄存器CRL的描述,如下图所示:
 点击看大图
                 图3.1.1.1 端口低配置寄存器CRL各位描述
该寄存器的复位值为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为推挽输出。
IDR是一个端口输入数据寄存器,只用了低16位。该寄存器为只读寄存器,并且只能以16位的形式读出。该寄存器各位的描述如下图所示:
 点击看大图
               图3.1.1.2 端口输入数据寄存器IDR各位描述
要想知道某个IO口的状态,你只要读这个寄存器,再看某个位的状态就可以了。使用起来是比较简单的。
ODR是一个端口输出数据寄存器,也只用了低16位。该寄存器虽然为可读写,但是从该寄存器读出来的数据都是0。只有写是有效的。其作用就是控制端口的输出。该寄存器的各位描述如下图所示:
 点击看大图
               图3.1.1.3 端口输出数据寄存器ODR各位描述
了解了这几个寄存器,我们就可以开始跑马灯实验的真正设计了。关于IO口更详细的介绍,请参考《STM32参考手册》第69页7.1节。


3.1.2 硬件设计


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


首先,找到之前新建的TEST工程,在该文件夹下面新建一个HARDWARE的文件夹,用来存储以后与硬件相关的代码。然后在HARDWARE文件夹下新建一个LED文件夹,用来存放与LED相关的代码。如下图所示:
 点击看大图
               图3.1.3.1 新建HARDWARE文件夹
然后我们打开USER文件夹下的TEST.Uv2工程,按 按钮新建一个文件,然后保存在HARDWARE->LED文件夹下面,保存为led.c。在该文件中输入如下代码:
#include <stm32f10x_lib.h>   
#include "led.h"
//Mini STM32开发板
//LED驱动代码   
//正点原子@ALIENTEK
//2010/5/27


// V1.0
//初始化PA8和PD2为输出口.并使能这两个口的时钟     
 
//LED IO初始化
void LED_Init(void)
{
 RCC->APB2ENR|=1<<2;    //使能PORTA时钟     
 RCC->APB2ENR|=1<<5;    //使能PORTD时钟      
 GPIOA->CRH&=0XFFFFFFF0;
 GPIOA->CRH|=0X00000003;//PA8 推挽输出    
    GPIOA->ODR|=1<<8;      //PA8 输出高           
 GPIOD->CRL&=0XFFFFF0FF;
 GPIOD->CRL|=0X00000300;//PD.2推挽输出
 GPIOD->ODR|=1<<2;      //PD.2输出高
}
该代码里面就包含了一个函数void LED_Init(void),该函数的功能就是用来实现配置PA8和PD2为推挽输出。在配置STM32外设的时候,任何时候都要先使能该外设的时钟!APB2ENR是APB2总线上的外设时钟使能寄存器,其各位的描述如下:
 点击看大图
               图3.1.3.2 寄存器APB2ENR各位描述
我们要使能的PORTA和PORTD的时钟使能位,分别在bit2和bit5,只要将这两位置1就可以使能PORTA和PORTD的时钟了。该寄存器还包括了很多其他外设的时钟使能。大家在以后会慢慢使用到的。关于这个寄存器的详细说明在《STM32参考手册》的第61页。
在设置完时钟之后就是配置完时钟之后,LED_Init配置了PA8和PD2的模式为推挽输出,并且默认输出1。这样就完成了对这两个IO口的初始化。
保存led.c代码,然后我们按同样的方法,新建一个led.h文件,也保存在LED文件夹下面。在led.h中输入如下代码:
#ifndef __LED_H
#define __LED_H 
#include "sys.h"
//Mini STM32开发板
//LED驱动代码   
//正点原子@ALIENTEK
//2010/5/27
//LED端口定义
#define LED0 PAout(8)// PA8
#define LED1 PDout(2)// PD2 
void LED_Init(void);//初始化          
#endif
这段代码里面最关键就是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口操作,我们都使用位带操作来实现,而不使用第二种方法。
将led.h也保存一下。接着,我们在Manage Components管理里面新建一个HARDWARE的组,并把led.c加入到这个组里面,如下图所示:
 点击看大图
                   图3.1.3.3 给工程新增HARDWARE组
单击OK,回到工程,然后你会发现在Project Workspace里面多了一个HARDWARE的组,在改组下面有一个led.c的文件。如下图所示:
                      560cdd1b-122b-4f58-b439-1485b136e8e5.JPG
                图3.1.3.4给工程新增HARDWARE组
然后用之前介绍的方法将led.h头文件的路径加入到工程里面。回到主界面,在main函数里面编写如下代码:
#include <stm32f10x_lib.h>
#include "sys.h"
#include "usart.h"  
#include "delay.h" 
#include "led.h"    
//Mini STM32开发板范例代码1
//跑马灯实验
//正点原子@ALIENTEK
//2010.5.27   
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"这句,使得LED0、LED1、LED_Init等能在main函数里被调用。接下来,main函数先配置系统时钟为72M,然后把延时函数初始化一下。接着就是调用LED_Init来初始化PA8和PD2为输出。最后在死循环里面实现LED0和LED1交替闪烁,间隔为300ms。
然后按编译,编译工程,得到结果如下图所示:
 点击看大图
                           图3.1.3.5编译结果
可以看到没有错误,也没有警告。接下来,我们就先进行软件仿真,验证一下是否有错误的地方,然后下载到Mini STM32看看实际运行的结果。


3.1.4 仿真与下载


我们可以先用软件仿真,看看结果对不对,根据软件仿真的结果,然后再下载到MINI STM32板子上面看运行是否正确。
点击看大图
                      图3.1.4.1逻辑分析设置
Display Type选择bit,然后单击Close关闭该对话框,可以看到逻辑分析窗口出来了2个信号,如下图所示:
 点击看大图
   点击看大图
                     图3.1.4.3 仿真波形
这里注意Gird要调节到0.25s左右比较合适,可以通过Zoom里面的In按钮来放大波形,通过Out按钮来缩小波形,或者按All显示全部波形。从上图中可以看到PORTD.2和PORTA.8交替输出,周期可以通过中间那根红线来测量。至此,我们的软件仿真已经顺利通过。
在软件仿真没有问题了之后,我们就可以把代码下载到Mini STM32观看运行结果是否与我们仿真的一致。运行结果如下图所示:
 4d92e017-25b1-4f2f-9986-11deb1776bcf.jpg
                     图3.1.4.4 执行结果
至此,我们的第一节学习就结束了,这一节,作为STM32的入门第一个例子,详细介绍了STM32的IO口操作,同时巩固了前面的学习,并进一步介绍了MDK的软件仿真功能。希望大家好好理解一下。


PDF档:


 https://static.assets-stash.eet-china.com/album/old-resources/2010/7/9/6fe4cd67-2bc5-47e7-9e1d-f2dda1b2a34d.rar
实例源代码:


https://static.assets-stash.eet-china.com/album/old-resources/2010/7/9/4449f71c-221a-457c-a79d-c9856522a80a.rar


 

PARTNER CONTENT

文章评论10条评论)

登录后参与讨论

用户1456026 2011-7-13 17:26

楼主再多给点资源呢?st库有吗?

用户1552953 2011-5-13 23:21

很纳闷,我用楼主的程序,学着写了段代码,主函数如下(和例程几乎没什么区别,是用的PB[15:8]口接的LED): int main(void) { Stm32_Clock_Init(9);//系统时钟设置 //RCC_Configuration(); delay_init(72); //延时初始化 LED_Init();//初始化与LED连接的接口 while(1) { LED0=1; LED1=1; LED2=1; delay_ms(300); LED0=0; LED1=0; LED2=0; delay_ms(300); } } 在KEIL里编译仿真都没有问题,但将程序下载到自己的实验板里面时,却没有任何现象,好像程序没有运行。我现在将Stm32_Clock_Init()函数用库函数 RCC_Configuration()替换后,下载到实验板运行又是正常的,求楼主指教

用户1549036 2011-2-3 14:10

很好很强大,拿过来用先了,谢谢了

用户1671803 2010-9-9 10:47

谢谢楼主、很强啊

liujun6037_345432000 2010-9-8 18:48

这应该是启动时间,没办法的。需要等待晶振稳定,才能执行其他操作。用内部晶振,可能会好点吧,你可以试试。

用户1671803 2010-9-8 11:14

对不起 应该是0.14毫秒

用户1671803 2010-9-8 10:34

楼主你好: 我在用STM32F101时发现从上电起要改变引脚的电平高低要用14毫秒,你这个例子程序我也试过了,请问有什么办法解决吗

liujun6037_345432000 2010-8-18 08:23

to:77554971 谢谢支持!

用户289279 2010-8-18 02:40

感谢楼主的无私奉献!可惜与你相见恨晚!我买了开发板才知道你的网站!实在很可惜!以后有朋友要买板,一定给你做介绍!

用户392262 2010-7-21 09:26

感谢楼主 这片文章对我很有帮助
相关推荐阅读
正点原子 2013-05-17 23:47
【连载】【ALIENTEK 战舰STM32开发板】STM32开发指南--第六十一章 战舰STM32开发板综合实验(标准例程终结篇)
   第六十一章 战舰STM32开发板综合实验        前面已经给大家讲了55个实例了,本章将设计一个综合实例,作为本指南的最后一个实验 ,该实验向大家展示了STM...
正点原子 2013-05-03 23:02
【连载】【ALIENTEK 战舰STM32开发板】STM32开发指南--第六十章 UCOSII实验3-消息队列、信号量集和软件定时器
   第六十章 UCOSII实验3-消息队列、信号量集和软件定时器   上一章,我们学习了UCOSII的信号量和邮箱的使用,本章,我们将学习消息队列、信号量集和软件定时器...
正点原子 2013-05-03 20:42
【连载】【ALIENTEK 战舰STM32开发板】STM32开发指南--第五十七章 ENC28J60网络实验
第五十七章 ENC28J60网络实验   本章,我们将向大家介绍ALIENTEK ENC28J60网络模块及其使用。本章,我们将使用ALIENTEK ENC28J60网络模块...
正点原子 2013-05-01 23:00
【连载】【ALIENTEK 战舰STM32开发板】STM32开发指南--第五十九章 UCOSII实验2-信号量和邮箱
第五十九章 UCOSII实验2-信号量和邮箱      上一章,我们学习了如何使用UCOSII,学习了UCOSII的任务调度,但是并没有用到任务间的同步与通信,本章我们将学习两个最基本的...
正点原子 2013-04-30 10:55
【连载】【ALIENTEK 战舰STM32开发板】STM32开发指南--第五十八章 UCOSII实验1-任务调度
  第五十八章 UCOSII实验1-任务调度      前面我们所有的例程都是跑的裸机程序(裸奔),从本章开始,我们将分3个章节向大家介绍UCOSII(实时多任务操作系...
正点原子 2013-04-26 23:16
【连载】【ALIENTEK 战舰STM32开发板】STM32开发指南--第五十七章 ENC28J60网络实验
 第五十七章 ENC28J60网络实验  本章,我们将向大家介绍ALIENTEK ENC28J60网络模块及其使用。本章,我们将使用ALIENTEK ENC28J60网络模块和uIP 1...
EE直播间
更多
我要评论
10
11
关闭 站长推荐上一条 /1 下一条