1.注意我们的教材讲解是基于寄存器操作,方便初学者理解透彻,
2.我们另外还提供了该实例的库函数源码,
下载链接:http://bbs.eeworld.com.cn/icview-210815-1-1.html
3.此实验的教程在《Alientek STM32不完全手册》的 3.1节:
下载地址: 《Alientek STM32不完全手册》
4.源码:
ALIENTEK MINISTM32 实验1 跑马灯.rar (13.82 KB)
Alientek STM32开发板实验1 跑马灯实验
通过本节的学习,你将了解到STM32
的IO
口作为输出使用的方法。本节分为如下几个小节:
3.1.1 STM32 IO
口简介
3.1.2
硬件设计
3.1.3
软件设计
3.1.4
仿真与下载
3.1.1 STM32 IO简介
作为所有开发板的经典入门实验,莫过于跑马灯了。ALIENTEKMiniSTM32
开发板板载了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/OLevel
标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.1STM32
的IO
口位配置表
STM32
输出模式配置如表3.1.1.2
所示:
表3.1.1.2STM32
输出模式配置表
接下来我们看看端口低配置寄存器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
节。
在此,我们可以总结一下,对于学过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
所没有的。
3.1.2 硬件设计
该实验的硬件电路在ALIENTEM Mini STM32
开发板上已经连接好了。DS0
接PA8
,DS1
接PD2
。所以在硬件上不需要动任何东西。其连接原理图如下:
图3.1.2.1LED
与STM32
连接原理图
3.1.3
软件设计
首先,找到之前新建的TEST
工程,在该文件夹下面新建一个HARDWARE
的文件夹,用来存储以后与硬件相关的代码。然后在HARDWARE
文件夹下新建一个LED
文件夹,用来存放与LED
相关的代码。
然后我们打开USER
文件夹下的TEST.Uv2
工程,按按钮新建一个文件,然后保存在HARDWARE->LED
文件夹下面,保存为led.c
。在该文件中输入如下代码:
#include <stm32f10x_lib.h>
#include "led.h"
//Mini STM32
开发板
//LED
驱动代码
//
正点原子@ALIENTEK
www.openedv.com
//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
输出高
}
该代码里面就包含了一个函数voidLED_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
www.openedv.com
//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
也保存一下。接着,我们在ManageComponents
管理里面新建一个HARDWARE
的组,并把led.c
加入到这个组里面,如下图所示:
图3.1.3.3
给工程新增HARDWARE
组
单击OK
,回到工程,然后你会发现在ProjectWorkspace
里面多了一个HARDWARE
的组,在改组下面有一个led.c
的文件。如下图所示:
图3.1.3.4
给工程新增HARDWARE
组
然后用之前介绍的方法将led.h
头文件的路径加入到工程里面(参照本文第36
页)。回到主界面,在main
函数里面编写如下代码:
#include <stm32f10x_lib.h>
#include "sys.h"
#include "usart.h"
#include "delay.h"
#include "led.h"
//Mini STM32
开发板范例代码1
//
跑马灯实验
//
正点原子@ALIENTEK
www.openedv.com
//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看看实际运行的结果。
店铺:http://shop62057469.taobao.com/
QQ讨论群:95288038(验证:21ic)
QQ:389063473/ 497610476
技术支持论坛:www.openedv.com ;
www.eeworld.com.cn