目录

一、led.h头文件分析

问:ifndef,endif有什么用?

二、Led_Init()函数分析

typedef:在C语言中,用户可以用typedef这个关键字,来自定义自己常用的数据类型名称。

三、代码示例


一、led.h头文件分析

#ifndef __LED_H
#define __LED_H
#include "sys.h"
#define LED0 PAout(8) // PA8
#define LED1 PDout(2) // PD2
void LED_Init(void);//初始化
#endif

问:ifndef,endif有什么用?

在这一段代码中,假设同时有A.h和B.h同时包含了这个头文件,在编译器编译A时,会先判断LED.H有没有被定义(这里肯定是未被定义的),那么就定义(define __LED_H),然后再(endif),在编译器编译B时,同样会判断LED.H有没有被定义(这里经过A的编译,肯定是被定义的),那么就直接(endif)。这样,就可以防止重复编译。

二、Led_Init()函数分析

APB2 外设时钟使能寄存器(RCC_APB2ENR)

由图易知,外部时钟寄存器的2~8位是对应相关的GPIO,当我们想要使用该GPIO时,必须打开时钟(clock),这样,各种寄存器,上、下升沿触发器,锁存器才会工作。

void RCC_APB2PeriphClockCmd(uint32_t RCC_APB2Periph, FunctionalState NewState)

/**作用:开启APB2外部时钟使能

  *1st参数:外部时钟的寄存器

  *@arg RCC_APB2Periph_AFIO, RCC_APB2Periph_GPIOA, RCC_APB2Periph_GPIOB,

  *          RCC_APB2Periph_GPIOC, RCC_APB2Periph_GPIOD, RCC_APB2Periph_GPIOE,

  *          RCC_APB2Periph_GPIOF, RCC_APB2Periph_GPIOG, RCC_APB2Periph_ADC1,

  *          RCC_APB2Periph_ADC2, RCC_APB2Periph_TIM1, RCC_APB2Periph_SPI1,

  *          RCC_APB2Periph_TIM8, RCC_APB2Periph_USART1, RCC_APB2Periph_ADC3,

  *          RCC_APB2Periph_TIM15, RCC_APB2Periph_TIM16, RCC_APB2Periph_TIM17,

  *          RCC_APB2Periph_TIM9, RCC_APB2Periph_TIM10, RCC_APB2Periph_TIM11

  *2st:ENABLE or DISABLE (是否使能)

  */

然后,我们要定义一个结构体,因为我们需要配置GPIO端口的各种参数。而这个结构体,在库函数中可以找到

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;

我们来分析一下这个结构体

typedef

在C语言中,用户可以用typedef这个关键字,来自定义自己常用的数据类型名称

我们可以看一下有无typedef时定义结构体有什么不同

(1)没有typedef,传统定义一个结构

struct Student
{
   int no;
   char name[12];
};

声明结构体:struct Student stu1;

(2)用typedef定义一个结构体

typedef struct Student
{
   int no;
   char name[12];
}Stu,student;

声明结构体:Stu stu1; | student stu2;

3)如果这里用了typedef,也可以不需要Student

typedef struct
{
   int no;
   char name[12];
}Stu;

声明结构体:Stu student1

在上面中,先定义一个结构体,里面包含(GPIO_PinGPIO_SpeedGPIO_Mode)三个参数,然后再把这个结构体typedef为GPIO_InitTypeDef

然后,就可以配置这三个结构体变量

GPIO_InitStruct.GPIO_Pin=GPIO_Pin_8;

GPIO_Pin_8是宏定义了一个寄存器

  GPIO_InitStruct.GPIO_Mode=GPIO_Mode_Out_PP;//设置推挽输出

设置电平输出方式

端口模式的配置有上拉,下拉,模拟,浮空输入;推挽,开漏,复用输出

输入

1.上拉输入:把不确定的信号电位强制拉高到VCC

2.下拉输入:把电压拉低到GND

3.浮空输入:逻辑器件,不接任何电压,例:按键,RX

4.模拟输入:输入0/1的数字信号,然后转化为模拟信号,例:ADC,低功耗下省电

输出

  1. 推挽输出:可以输出0/1
  2. 开漏输出:可输出0,若需要输出1需要接一个上拉电阻
  3. 复用输出:当IO口不作为通用IO口时使用,常配合其它输出使用例如:各种通信方式,PWM

     4.复用+推挽输出:I2C的SCL,SDA

     5.复用+开漏输出:TX

  GPIO_InitStruct.GPIO_Speed=GPIO_Speed_10MHz;//速度越快,功耗越高

设置输出速度,如果是输入的话可以直接忽略。速度越快,功耗越高

最后,再调用外设初始化函数,把配置好的结构体写到相应的寄存器中

GPIO_Init(GPIOA,&GPIO_InitStruct);

//void GPIO_Init(GPIO_TypeDef* GPIOx, GPIO_InitTypeDef* GPIO_InitStruct)

/**作用:将结构体成员写入寄存器当中

  *1st参数:GPIO端口,即GPIOx x=A ...G)

  *2st参数:结构体的指针

  */

这样,我们的GPIO配置就完成了

在main函数当中,可以调用Reset来点亮LED

GPIO_ResetBits(GPIOA,GPIO_Pin_8);

可以调用Set灭灯

GPIO_SetBits(GPIOA,GPIO_Pin_8);

三、代码示例

相关程序代码放在这里了,程序移植性较高:

STM32代码句句解析:点亮一个LED

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐