一、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]; };
1.
声明结构体: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_Pin,GPIO_Speed,GPIO_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);
三、代码示例
相关程序代码放在这里了,程序移植性较高: