文章参考:6.1 GPIO功能概述、GPIO 配置之ODR, BSRR, BRR 详解、关于GPIO的BSRR和BRR寄存器、STM32____GPIO几个寄存器的理解、STM32的8种GPIO输入输出模式深入详解、STM32 GPIO管脚模式的设置及使用方法、STM32学习:STM32 GPIO介绍、reference manual.pdf
硬件:STM32F107VC( Cortex-M3)
软件:Keil μVision4,JLINK
一、基本概念
1. 定义
GPIO是通用输入/输出(General Purpose Input/Output)的简称,可以通过软件来控制其输入和输出。STM32芯片的GPIO引脚与外部设备连接起来,从而实现与外部通讯、控制以及数据采集的功能。
有2个重要概念:端口和引脚。
端口(PORT):独立的外设子模块,包括多个引脚,通过多个硬件寄存器控制引脚。
引脚(PIN):对应微控制器 的一个管脚,归属于端口,由端口寄存器的对应位控制。
一个端口默认包含16个引脚,但是不同型号的STM32微控制器所包含的端口数量及各端口包含的引脚数量各不相同,具体信息可查询芯片的数据手册。
引脚占用情况:PA0配置wakeup唤醒;PC14和PC15接入外部低速时钟LSE;PC13的TAMPER_RTC是入侵检测功能,当 TAMPER引脚上的信号从 0变成1或者从 1变成 0(取决于备份控制寄存器BKP_CR的 TPAL位),会产生一个侵入检测事件,侵入检测事件将所有数据备份寄存器内容清除。
我使用的硬件是STM32F107VC( Cortex-M3),它的GPIO模块由端口GPIOA、GPIOB、GPIOC、GPIOD、GPIOE这5个独立的子模块构成,每个GPIO端口包括16个引脚,例如GPIOA包括PA0-PA15这16个引脚,通过7个硬件寄存器控制引脚工作。
LED1对应的引脚 PD7对应STM32F107VC芯片的88脚,属于端口GPIOD,输出电平由GPIOD的输出数据寄存器GPIOD_ODR的第7位决定。
2. 基本特性和用途
- 多达80个标志I/O端口,包括16个模拟输入
- 所有(AD输入即模拟输入除外) I/O端口均为5V兼容,20mA驱动能力
- 高达18MHz翻转速度
- 复用功能管脚(包括USART、I2C、SPIx、CAN、USB…)
- 所有I/O端口可作为外部中断输入(同时可有16个输入)
- PA.00可作为从待机模式唤醒的管脚
- PC.13可作为侵入检测管脚
- 通过BSRR和BRR寄存器可实现管脚的原子设置/清除
- 具有上锁功能,避免意外修改I/O寄存器配置
- 某些复用管脚可以重映射到其它管脚,方便PCB的设计
- 所有调试管脚均可用普通I/O管脚
主要用于工业现场需要用到数字量输入/输出的场合,例如:
3. 电路
按电路的功能划分三个部分
第一部分是端口控制寄存器组,它给出了3个寄存器
第二部分是输入/输出驱动器,输出驱动器主要由门电路和一对MOS管构成,输入驱动器主要由施密特触发器和上拉下拉电阻构成
第三部分是引脚电路,由保护二极管构成
输入模式:
(1)输出驱动器关闭;
(2)施密特触发器打开,可以获取引脚状态,利用门阀电压将引脚电压转换为高/低电平两种状态,进而转化为1/0信号存入输入数据寄存器;
(3)通过寄存器使能上拉/下拉输入电阻,当上拉下拉都不使能时引脚工作于浮空输入状态,当上拉电阻使能时,引脚工作于上拉输入状态,当下拉电阻使能时,引脚工作于下拉输入状态;工作模式下的上拉下拉电阻在设计外围电路时非常有用,比如设计按键电路时可以省去外接的电阻;
(4)引脚电平状态将存入输入数据寄存器。
输入模式可分为下面几种:
(1)浮空输入:浮空就是逻辑器件与引脚即不接高电平,也不接低电平。由于逻辑器件的内部结构,当它输入引脚悬空时,相当于该引脚接了高电平。一般实际运用时,引脚不建议悬空,易受干扰。通俗讲就是浮空就是浮在空中,就相当于此端口在默认情况下什么都不接,呈高阻态,这种设置在数据传输时用的比较多。浮空最大的特点就是电压的不确定性,它可能是0V,页可能是VCC,还可能是介于两者之间的某个值(最有可能)。 浮空一般用来做ADC输入用,这样可以减少上下拉电阻对结果的影响
(2)上拉输入模式:上拉就是把点位拉高,比如拉到Vcc。上拉就是将不确定的信号通过一个电阻嵌位在高电平。电阻同时起到限流的作用。弱强只是上拉电阻的阻值不同,没有什么严格区分。
(3)下拉输入模式:就是把电压拉低,拉到GND。与上拉原理相似
输出模式:
(1)输出驱动器打开;
(2)施密特触发器打开;
(3)通过寄存器使能上拉/下拉输入电阻,输出模式下一般不使能上拉/下拉输入电阻;
(4)通过输入数据寄存器获取引脚电平状态。这里注意下,在输出模式下也可以读取引脚状态。
输出模式可分为下面几种:
(1)推挽式输出:可以输出高,低电平,连接数字器件;推挽结构一般是指两个三级管分别受到互补信号的控制,总是在一个三极管导通的时候另一个截止。高低电平由IC的电源低定。推挽电路是两个参数相同的三极管或MOSFET,以推挽方式存在于电路中,各负责正负半周的波形方法任务,电路工作时,两只对称的功率开关管每次只有一个导通,所以导通损耗小,效率高。输出即可以向负载灌电流。推拉式输出级即提高电路的负载能力,又提高开关速度。
(2)开漏输出:输出端相当于三极管的集电极,要得到高电平状态需要上拉电阻才行,适合于做电流型的驱动,其吸收电流的能力相对强(一般20mA以内)
模拟模式:单纯的引脚模拟状态或者引脚作为片内模拟外设的复用脚。
模拟输入:模拟输入是指传统方式的输入,数字输入是输入PCM数字信号,即0,1的二进制数字信号,通过数模转换,转换成模拟信号,经前级放大进入功率放大器,功率放大器还是模拟的
复用模式:
(1)输出可配置为推挽或者开漏模式;
(2)施密特触发器打开;
(3)通过寄存器使能上拉/下拉输入电阻;
(4)通过输入数据寄存器获取引脚电平状态。
复用模式可分为下面几种:
(1)开漏复用功能:可以理解为GPIO口被用作第二功能时的配置情况(即并非作为通用IO口使用)。端口必须配置成复用功能输出模式(推挽或开漏)
(2)推挽式复用功能:可以理解为GPIO口被用作第二功能时的配置情况(并非作为通用IO口使用)
二、GPIO的基于固件库法开发
1. GPIO的引脚初始化
下面从库函数的层面来说明如何初始化IO口。在stm32f10x_gpio.h里定义了GPIO_InitTypeDef结构体变量。
typedef struct
{
uint16_t GPIO_Pin; /*!< Specifies the GPIO pins to be configured.
This parameter can be any value of @ref GPIO_pins_define */
GPIOSpeed_TypeDef GPIO_Speed; /*!< Specifies the speed for the selected pins.
This parameter can be a value of @ref GPIOSpeed_TypeDef */
GPIOMode_TypeDef GPIO_Mode; /*!< Specifies the operating mode for the selected pins.
This parameter can be a value of @ref GPIOMode_TypeDef */
}GPIO_InitTypeDef;
GPIO_InitTypeDef这个结构体第一个变量类型为一种无符号的整形,
typedef unsigned short int uint16_t;
变量名为GPIO_Pin,即为确定是哪一个IO口(引脚)。每个端口都有16个引脚,编号0-15,所以GPIO_Pin有
#define GPIO_Pin_0 ((uint16_t)0x0001) /*!< Pin 0 selected */
#define GPIO_Pin_1 ((uint16_t)0x0002) /*!< Pin 1 selected */
#define GPIO_Pin_2 ((uint16_t)0x0004) /*!< Pin 2 selected */
#define GPIO_Pin_3 ((uint16_t)0x0008) /*!< Pin 3 selected */
#define GPIO_Pin_4 ((uint16_t)0x0010) /*!< Pin 4 selected */
#define GPIO_Pin_5 ((uint16_t)0x0020) /*!< Pin 5 selected */
#define GPIO_Pin_6 ((uint16_t)0x0040) /*!< Pin 6 selected */
#define GPIO_Pin_7 ((uint16_t)0x0080) /*!< Pin 7 selected */
#define GPIO_Pin_8 ((uint16_t)0x0100) /*!< Pin 8 selected */
#define GPIO_Pin_9 ((uint16_t)0x0200) /*!< Pin 9 selected */
#define GPIO_Pin_10 ((uint16_t)0x0400) /*!< Pin 10 selected */
#define GPIO_Pin_11 ((uint16_t)0x0800) /*!< Pin 11 selected */
#define GPIO_Pin_12 ((uint16_t)0x1000) /*!< Pin 12 selected */
#define GPIO_Pin_13 ((uint16_t)0x2000) /*!< Pin 13 selected */
#define GPIO_Pin_14 ((uint16_t)0x4000) /*!< Pin 14 selected */
#define GPIO_Pin_15 ((uint16_t)0x8000) /*!< Pin 15 selected */
#define GPIO_Pin_All ((uint16_t)0xFFFF) /*!< All pins selected */
第二个变量类型是用枚举定义的,根据变量名很容易知道它是确定IO口的输入或输出的速度的,有10MHz、2MHz、50MHz。通常使用50MHz。
typedef enum
{
GPIO_Speed_10MHz = 1,
GPIO_Speed_2MHz,
GPIO_Speed_50MHz
}GPIOSpeed_TypeDef;
第三变量同样是枚举定义的,是确定IO口的工作模式。这八种模式中,分别是模拟输入、浮空输入、下拉输入、上拉输入、开漏输出、推挽输出、复用开漏输出、复用推挽输出。最常用的也是应该掌握的有三种:推挽输出、开漏输出、上拉输入。
typedef enum
{ GPIO_Mode_AIN = 0x0,
GPIO_Mode_IN_FLOATING = 0x04,
GPIO_Mode_IPD = 0x28,
GPIO_Mode_IPU = 0x48,
GPIO_Mode_Out_OD = 0x14,
GPIO_Mode_Out_PP = 0x10,
GPIO_Mode_AF_OD = 0x1C,
GPIO_Mode_AF_PP = 0x18
}GPIOMode_TypeDef;
2. GPIO的引脚电平状态
引脚电平状态有2个,分别是Bit_RESET
的低电平状态和Bit_SET
的高电平状态。
typedef enum
{ Bit_RESET = 0,
Bit_SET
}BitAction;
3. GPIO的引脚所属端口
虽然stm32f10x_gpio.h里定义了7个端口,但是所用的硬件STM32F107VC( Cortex-M3),它的GPIO的引脚所属端口只有5个,分别是GPIOA-GPIOE。
#define GPIO_PortSourceGPIOA ((uint8_t)0x00)
#define GPIO_PortSourceGPIOB ((uint8_t)0x01)
#define GPIO_PortSourceGPIOC ((uint8_t)0x02)
#define GPIO_PortSourceGPIOD ((uint8_t)0x03)
#define GPIO_PortSourceGPIOE ((uint8_t)0x04)
#define GPIO_PortSourceGPIOF ((uint8_t)0x05)
#define GPIO_PortSourceGPIOG ((uint8_t)0x06)
4. 初始化步骤
初始化步骤如下图:
具体使用步骤如下图:
例如LED跑马灯设置中,4个LED灯对应的引脚分别是PD7、PD13、PD3、PD4。将这4个IO口设置为推挽输出,输入速度为50MHz。引脚PD7、PD13、PD3、PD4属于同一端口GPIOD,且配置参数相同,可以采用按位或的方式同时配置,以简化程序。
GPIO_InitTypeDef GPIO_InitStructure; /* 定义1个结构体变量 */
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOD, ENABLE);/* 使能外设时钟 */
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_7 | GPIO_Pin_13| GPIO_Pin_3 | GPIO_Pin_4;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; /* 推挽输出模式 */
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; /* 速度50MHz */
GPIO_Init(GPIOD, &GPIO_InitStructure); /* 调用库函数初始化GPIO */
5. 对IO口进行相关操作
GPIO的相关函数如下:
void GPIO_DeInit(GPIO_TypeDef* GPIOx);
void GPIO_AFIODeInit(void);
void GPIO_Init(GPIO_TypeDef* GPIOx, GPIO_InitTypeDef* GPIO_InitStruct);
void GPIO_StructInit(GPIO_InitTypeDef* GPIO_InitStruct);
uint8_t GPIO_ReadInputDataBit(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin);
uint16_t GPIO_ReadInputData(GPIO_TypeDef* GPIOx);
uint8_t GPIO_ReadOutputDataBit(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin);
uint16_t GPIO_ReadOutputData(GPIO_TypeDef* GPIOx);
void GPIO_SetBits(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin);
void GPIO_ResetBits(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin);
void GPIO_WriteBit(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin, BitAction BitVal);
void GPIO_Write(GPIO_TypeDef* GPIOx, uint16_t PortVal);
void GPIO_PinLockConfig(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin);
void GPIO_EventOutputConfig(uint8_t GPIO_PortSource, uint8_t GPIO_PinSource);
void GPIO_EventOutputCmd(FunctionalState NewState);
void GPIO_PinRemapConfig(uint32_t GPIO_Remap, FunctionalState NewState);
void GPIO_EXTILineConfig(uint8_t GPIO_PortSource, uint8_t GPIO_PinSource);
void GPIO_ETH_MediaInterfaceConfig(uint32_t GPIO_ETH_MediaInterface);
主要介绍2个函数,GPIO_ResetBits
和GPIO_SetBits
用于设置IO口的电平的高低。GPIO_SetBits
用于设置IO口电平为高,GPIO_ResetBits
用于设置IO口电平为低。例如设置LED1灯灭,则对PD7(引脚Pin_7,端口GPIOD)设置低电平状态,如下:
GPIO_ResetBits(GPIOD , GPIO_Pin_7);
设置LED2灯亮,则对PD13(引脚Pin_13,端口GPIOD)设置高电平状态,如下:
GPIO_SetBits(GPIOD , GPIO_Pin_13);
三、GPIO的基于板级函数法开发
基于板级函数法开发,就是直接调用EVAL文件里的现成的板级函数。板级函数是更高级的固件封装。Eval文件是ST公司提供的几种测试评估样板的硬件定义文件,简单点说就是把板子上的每个硬件接口都起个名字,编程的时候用的是硬件的名字而不是本来的端口名。这个文件必须对应相应的硬件主板,在STM32F107VC( Cortex-M3)硬件主板上有STM32_EVAL和STM3210C_EVAL2种评估板的专用驱动。
在stm3210c_eval.c中STM3210C_EVAL文件里的现成的板级函数如下:
void STM_EVAL_LEDInit(Led_TypeDef Led);
void STM_EVAL_LEDOn(Led_TypeDef Led);
void STM_EVAL_LEDOff(Led_TypeDef Led);
void STM_EVAL_LEDToggle(Led_TypeDef Led);
void STM_EVAL_PBInit(Button_TypeDef Button, ButtonMode_TypeDef Button_Mode);
uint32_t STM_EVAL_PBGetState(Button_TypeDef Button);
void STM_EVAL_COMInit(COM_TypeDef COM, USART_InitTypeDef* USART_InitStruct);
void SD_LowLevel_DeInit(void);
void SD_LowLevel_Init(void);
void sEE_LowLevel_DeInit(void);
void sEE_LowLevel_Init(void);
void sEE_LowLevel_DMAConfig(uint32_t pBuffer, uint32_t BufferSize, uint32_t Direction);
这里对LED1的GPIO引脚的初始化,直接使用STM_EVAL_LEDInit
函数,
STM_EVAL_LEDInit(LED1);
在stm3210c_eval.c中STM_EVAL_LEDInit
函数定义如下:
void STM_EVAL_LEDInit(Led_TypeDef Led)
{
GPIO_InitTypeDef GPIO_InitStructure;
/* Enable the GPIO_LED Clock */
RCC_APB2PeriphClockCmd(GPIO_CLK[Led], ENABLE);
/* Configure the GPIO_LED pin */
GPIO_InitStructure.GPIO_Pin = GPIO_PIN[Led];
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIO_PORT[Led], &GPIO_InitStructure);
}
其中涉及的引脚名称,所属端口,使能时钟如下:
#define LEDn 4
#define LED1_PIN GPIO_Pin_7
#define LED1_GPIO_PORT GPIOD
#define LED1_GPIO_CLK RCC_APB2Periph_GPIOD
#define LED2_PIN GPIO_Pin_13
#define LED2_GPIO_PORT GPIOD
#define LED2_GPIO_CLK RCC_APB2Periph_GPIOD
#define LED3_PIN GPIO_Pin_3
#define LED3_GPIO_PORT GPIOD
#define LED3_GPIO_CLK RCC_APB2Periph_GPIOD
#define LED4_PIN GPIO_Pin_4
#define LED4_GPIO_PORT GPIOD
#define LED4_GPIO_CLK RCC_APB2Periph_GPIOD
四、GPIO的基于寄存器法开发
GPIO的寄存器分别是GPIO配置寄存器(GPIOx_CRL,GPIOx_CRH)、GPIO数据寄存器(GPIOx_IDR和GPIOx_ODR)、复位寄存器(GPIOx_BRR)、置位/复位寄存器(GPIOx_BSRR)、锁定寄存器(GPIOx_LCKR)。 其中x表示端口号,取值A~G。 在stm32f10x.h中定义了GPIO的相关寄存器,如下:
typedef struct
{
__IO uint32_t CRL;
__IO uint32_t CRH;
__IO uint32_t IDR;
__IO uint32_t ODR;
__IO uint32_t BSRR;
__IO uint32_t BRR;
__IO uint32_t LCKR;
} GPIO_TypeDef;
1. GPIO配置寄存器(GPIOx_CRL,GPIOx_CRH)
CRH和CRL的使用基本相同,CRH用于控制GPIOX(X表示A-G)的高8位(Pin15-Pin8),而CRL用于控制GPIOX(X表示A-G)的低8位(Pin7-Pin0)。
其中MODE对应GPIO的最大输出速度,CNF对应GPIO的工作(配置)模式。具体如下:
例子:GPIOA->CRH|=0x00000003
,对照CRH的表格,可知:
MODE8[1:0]=11,指的是输出模式,最大速度是50MHz;
CNF8[1:0]=00,指的是输出模式中的general purpose output push_pull,即推挽模式;
可以看出对应的引脚所属端口是GPIOA,引脚是Pin_8。
所以这句代码GPIOA->CRH|=0x00000003
是PA8 推挽输出,工作速度50MHz。
2. GPIO数据寄存器(GPIOx_IDR和GPIOx_ODR)
(1) 输入数据寄存器GPIOx_IDR
32位寄存器,其中高16位保留,低16位的每1位用于存放对应引脚的电平状态
0:对应引脚输入低电平, 1:对应引脚输入高电平
** (2)输出数据寄存器GPIOx_ODR**
32位寄存器,其中高16位保留,低16位的每1位用于控制对应引脚输出高/低电平
0:控制对应引脚输出低电平, 1:控制对应引脚输出高电平
3. 置位/复位寄存器(GPIOx_BSRR)
高16位控制对应引脚输出低电平:写入_1_对应引脚输出低电平;写入0,没有任何作用
低16位控制对应引脚输出高电平:写入_1对应引脚输出高电平_;写入0,没有任何作用
例如:
GPIOA->BSRR | = 1<<5; //设置bits5=1,PA5输出高电平
GPIOA->BSRR | = 1<<(5+16); //设置bits21=1,PA5输出低电平
4. 复位寄存器(GPIOx_BRR)
32位寄存器,其中高16位保留,低16位的每1位写入_1_对应引脚输出低电平,写入0,没有任何作用。
2-4. BSRR、BRR和ODR的对比
区别:(1)设置多个引脚输出高/低电平时,使用置位/复位寄存器BSRR更加简单。例如:
(2)BRR、BSRR都可以做到只改变位0的值,不影响其他位的值,用一个等号可以完成。 而ODR 改变则时全部改变。例如:
16寄存器为1010101010101010,经过GPIO->BSRR=0x01
后,变为1010101010101011
经过GPIO->ODR=0x01
后,变为0000000000000001
(3)对ODR赋值是一次操作16位的,也就是同时设置了16个引脚的输出电平;
GPIO_PIN是指某个端口的具体某一个引脚,是位操作,可以通过设置BSRR或BRR寄存器来设置某一特定引脚的输出电平,而保持其他引脚输出不变,速度快,效率高。
例如 GPIO_PORT[Led]->BSRR = GPIO_PIN[Led];
就是把GPIOx中的某个pin置高电平
GPIO_PORT[Led]->BRR = GPIO_PIN[Led];
就是把GPIOx中的某个pin置低电平
(4)ODR 和BSRR、BRR对控制管脚高低电平应用的优劣对比
用BSRR和BRR去改变管脚状态的时候,没有被中断打断的风险,也就不需要关闭中断。关闭中断明显会延迟或丢失一事件的捕获,所以控制GPIO的状态最好还是用SBRR和BRR。
例子:GPIOE的16个IO都被设置成输出,而每次操作仅需要改变低8位的数据而保持高8位不变,假设新的8位数据在变量Newdata中。
可以直接操作这两个寄存器:
GPIOE->BSRR = Newdata & 0xff;GPIOE->BRR = ~Newdata & 0xff;
当然还可以一次完成对8位的操作:
GPIOE->BSRR = (Newdata & 0xff) | ( (~Newdata & 0xff)<<16 );
当然还可以一次完成对16位的操作:
GPIOE->BSRR = (Newdata & 0xffff) | ( (~Newdata )<<16 );
(5)使用BSRR寄存器,可以实现8个端口位的同时修改操作,且Set比Reset的级别高,就是说同一个bit又做Set又做Reset,最后结果是Set。要同步变化只要简单的 GPIOx->BSRR = 0xFFFF0000 | PATTEN
;即可,不用考虑哪些需要置1,哪些需要清零。
5. 锁定寄存器(GPIOx_LCKR)
32位寄存器,其中高15位保留,
第16位是Lock key,写入0表示端口配置锁键位没被激活;写入1:端口配置锁键位被激活,下次系统复位前GPIOx_LCKR寄存器被锁住。 锁键的写入序列: 写1 -> 写0 -> 写1 -> 读0 -> 读1 最后一个读可省略,但可以用来确认锁键已被激活。 注:在操作锁键的写入序列时,不能改变LCK[15:0]的值。 操作锁键写入序列中的任何错误将不能激活锁键。
低16位的每1位写入1表示端口设置没被锁,写入0,端口设置被锁。
五、基于GPIO的跑马灯实验
1. 通过使用固件库配置GPIO引脚的固件库函数法
表示4个LED灯轮流亮,代码如下:
#include "stm32f10x.h"
//这是一个延迟函数
void Delay(__IO uint32_t nCount)
{
/* __IO 就是volatile, 加上这个后可以避免延迟函数被编译器优化掉 */
for(; nCount != 0; nCount--);
}
// 主函数
int main(void)
{
SystemInit();
GPIO_InitTypeDef GPIO_InitStructure; /* 定义1个结构体变量 */
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOD, ENABLE);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_7 | GPIO_Pin_13| GPIO_Pin_3 | GPIO_Pin_4;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; /* 推挽输出模式 */
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOD, &GPIO_InitStructure); /* 调用库函数初始化GPIO */
while (1)
{
GPIO_SetBits(GPIOD, GPIO_Pin_7); //LED1亮
Delay(0xFAF080);
GPIO_ResetBits(GPIOD, GPIO_Pin_7); //LED1灭
GPIO_SetBits(GPIOD, GPIO_Pin_13); //LED2亮
Delay(0xFAF080);
GPIO_ResetBits(GPIOD, GPIO_Pin_13); //LED2灭
GPIO_SetBits(GPIOD, GPIO_Pin_3); //LED3亮
Delay(0xFAF080);
GPIO_ResetBits(GPIOD, GPIO_Pin_3); //LED3灭
GPIO_SetBits(GPIOD, GPIO_Pin_4); //LED4亮
Delay(0xFAF080);
GPIO_ResetBits(GPIOD, GPIO_Pin_4); //LED4灭
Delay(0xFAF080);
}
}
2. 直接调用板级函数已有的LED亮、灭函数法
表示4个LED灯轮流亮,代码如下:
#include "stm32f10x.h"
#include "stm32_eval.h"
//这是一个延迟函数
void Delay(__IO uint32_t nCount)
{
/* __IO 就是volatile, 加上这个后可以避免延迟函数被编译器优化掉 */
for(; nCount != 0; nCount--);
}
int main(void)
{
SystemInit();
/* Initialize Leds mounted on STM3210X-EVAL board */
STM_EVAL_LEDInit(LED1);
STM_EVAL_LEDInit(LED2);
STM_EVAL_LEDInit(LED3);
STM_EVAL_LEDInit(LED4);
while (1)
{
STM_EVAL_LEDOn(LED1); //LED1亮
Delay(0xFAF080);
STM_EVAL_LEDOff(LED1); //LED1灭
STM_EVAL_LEDOn(LED2); //LED2亮
Delay(0xFAF080);
STM_EVAL_LEDOff(LED2); //LED2灭
STM_EVAL_LEDOn(LED3); //LED3亮
Delay(0xFAF080);
STM_EVAL_LEDOff(LED3); //LED3灭
STM_EVAL_LEDOn(LED4); //LED4亮
Delay(0xFAF080);
STM_EVAL_LEDOff(LED4); //LED4灭
Delay(0xFAF080);
}
}
3. 配置寄存器法
表示4个LED灯全亮,全灭,代码如下:
#include "stm32f10x.h"
//这是一个延迟函数
void Delay(__IO uint32_t nCount)
{
/* __IO 就是volatile, 加上这个后可以避免延迟函数被编译器优化掉 */
for(; nCount != 0; nCount--);
}
int main(void)
{
SystemInit();
RCC_APB2ENR |=(1<<5); // 使能PORTD口的时钟
GPIOD_CRL &= 0x00000000; // 配置寄存器,将GPIOD_CRL低16位清0,这样CNF为00
GPIOD_CRL |= 0x33333333; // 配置寄存器,MODE选择为11,即50MHz输出模式,,选择为推 挽输出模式
GPIOD_CRH &= 0x00000000; // 配置寄存器,将GPIOD_CRL高16位清0,这样CNF为00,
GPIOD_CRH |= 0x33333333; //选择为推挽输出模式配置寄存器,MODE选择为11,即50MHz输出模式
while(1)
{
GPIOD_ODR=0xffff; //LED全亮
Delay(FAF080);
GPIOD_ODR=0x0000; //LED全灭
Delay(FAF080);
}
}