STM32的GPIO

931 阅读15分钟

文章参考: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_ResetBitsGPIO_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的跑马灯实验

    LED跑马灯实现方法有很多,这里列举3种不同的方法来说明:

   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);
	} 
}