详见 【正点原子】 手把手教你学STM32 系列视频之 STM32F4-基于探索者F407
本文不局限于点灯,而重在分类总结一下三种方法对应的代码
法一:使用库函数
初始化
其实就是一个函数:GPIO_Init
理解的难点是我们需要做的是新建一个结构体,
GPIO_InitTypeDef GPIO_InitStructure;//定义GPIO_Init的第二个参数结构体
把设置信息存放到这个结构体里面,再把结构体指针作为参数传给GPIO_Init函数即可
//初始化PF9和PF10为输出口.并使能这两个口的时钟
//LED IO初始化
void LED_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;//定义GPIO_Init的第二个参数结构体
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOF, ENABLE);//使能GPIOF时钟
//GPIOF9,F10初始化设置
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9 | GPIO_Pin_10;//LED0和LED1对应IO口
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;//普通输出模式
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;//推挽输出,OD是开漏
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;//100MHz
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;//上拉
GPIO_Init(GPIOF, &GPIO_InitStructure);//初始化GPIO
GPIO_SetBits(GPIOF,GPIO_Pin_9 | GPIO_Pin_10);//GPIOF9,F10设置高,灯灭,初始化后默认灭
}
至于设置结构体的具体信息,视频中讲到了,意思是通过GPIO_Init中判断参数有效性的语句assert_param
,来反查询参数应该是啥,再从里面找出我们需要的设置即可。
void GPIO_Init(GPIO_TypeDef* GPIOx, GPIO_InitTypeDef* GPIO_InitStruct)
{
uint32_t pinpos = 0x00, pos = 0x00 , currentpin = 0x00;
/* Check the parameters */
assert_param(IS_GPIO_ALL_PERIPH(GPIOx));
assert_param(IS_GPIO_PIN(GPIO_InitStruct->GPIO_Pin));
assert_param(IS_GPIO_MODE(GPIO_InitStruct->GPIO_Mode));
assert_param(IS_GPIO_PUPD(GPIO_InitStruct->GPIO_PuPd));
比如对IS_GPIO_ALL_PERIPH
来go to def…就可以查询到第一个参数的有效值,从而知道我们设置PF9的话,第一个参数应该填GPIOF
main
虽然不一定要放在main,不过业务上对gpio主要就是三个函数:GPIO_SetBits\GPIO_ResetBits\GPIO_ReadInputDataBit
GPIO_SetBits(GPIOF,GPIO_Pin_10); //LED1对应引脚GPIOF.10拉高,灭 等同LED1=1;
GPIO_ResetBits(GPIOF,GPIO_Pin_9); //LED0对应引脚GPIOF.9拉低,亮 等同LED0=0;
#define KEY0 GPIO_ReadInputDataBit(GPIOE,GPIO_Pin_4) //PE4
int main(void)
{
delay_init(168); //初始化延时函数,系统时钟频率为168M
LED_Init(); //初始化LED端口
//下面是通过直接操作库函数的方式实现IO控制
while(1)
{
GPIO_ResetBits(GPIOF,GPIO_Pin_9); //LED0对应引脚GPIOF.9拉低,亮 等同LED0=0;
GPIO_SetBits(GPIOF,GPIO_Pin_10); //LED1对应引脚GPIOF.10拉高,灭 等同LED1=1;
delay_ms(500); //延时300ms
GPIO_SetBits(GPIOF,GPIO_Pin_9); //LED0对应引脚GPIOF.0拉高,灭 等同LED0=1;
GPIO_ResetBits(GPIOF,GPIO_Pin_10); //LED1对应引脚GPIOF.10拉低,亮 等同LED1=0;
delay_ms(500); //延时300ms
}
}
法二:配置寄存器
初始化
通过对7组,每组10个的寄存器进行直接配置来完成需要的设置
参考中文参考手册7.4
//初始化PF9和PF10为输出口.并使能这两个口的时钟
//LED IO初始化
void LED_Init(void)
{
RCC->AHB1ENR|=1<<5;//使能PORTF时钟 RCC实际上是一个结构体指针
//参考中文参考手册7.4
//PF9
GPIOF->MODER &= ~(3<<2*9);//配置GPIOx_MODER的PF9为00,先清零(11=3的取反)
GPIOF->MODER |= ~(1<<2*9);//配置GPIOx_MODER的PF9为01,01就是通用输出模式
GPIOF->OTYPER &=~(1<<9);
GPIOF->OTYPER |=(0<<9);//0是推挽输出
GPIOF->OSPEEDR &= ~(3<<2*9);//下面都与上同理
GPIOF->OSPEEDR |= ~(2<<2*9);//10=2==50mhz
GPIOF->PUPDR &= ~(3<<2*9);
GPIOF->PUPDR |= ~(1<<2*9);//01=1==上拉
//默认初始化完两个led灯亮
GPIOF->ODR |= 1<<9;
//GPIOF->ODR &=~(1<<9);
//pf10同pf9
GPIOF->MODER &= ~(3<<2*10);//配置GPIOx_MODER的PF10为00,先清零(11=3的取反)
GPIOF->MODER |= ~(1<<2*10);//配置GPIOx_MODER的PF10为01,01就是通用输出模式
GPIOF->OTYPER &=~(1<<10);
GPIOF->OTYPER |=(0<<10);//0是推挽输出
GPIOF->OSPEEDR &= ~(3<<2*10);//下面都与上同理
GPIOF->OSPEEDR |= ~(2<<2*10);//10=2==50mhz
GPIOF->PUPDR &= ~(3<<2*10);
GPIOF->PUPDR |= ~(1<<2*10);//01=1==上拉
//默认初始化完两个led灯亮
GPIOF->ODR |= 1<<10;
}
注意,这个配置实测有问题,就理解原理哈
main
寄存器输出输入主要是配置IDR\ODR\BSSR寄存器
int main(void)
{
// *******************下面注释掉的代码是通过 直接操作寄存器 方式实现IO口控制**************************************
//寄存器方法一:这种方法必须在库函数版本工程下有FWLIB文件夹才识别GPIO_Pin_9
/*
delay_init(168); //初始化延时函数
LED_Init(); //初始化LED端口
while(1)
{
GPIOF->BSRRH=GPIO_Pin_9;//LED0亮,高位是置为0
GPIOF->BSRRL=GPIO_Pin_10;//LED1灭,低位是置为1
delay_ms(500);
GPIOF->BSRRL=GPIO_Pin_9;//LED0灭
GPIOF->BSRRH=GPIO_Pin_10;//LED1亮
delay_ms(500);
}
*/
// **************************************************************************************************
//寄存器方法二:
delay_init(168); //初始化延时函数
LED_Init(); //初始化LED端口
while(1)
{
GPIOF->ODR &=~(1<<9);//LED0亮
GPIOF->ODR &=~(1<<10);//LED1亮
delay_ms(500);
GPIOF->ODR |=(1<<9);//LED0灭
GPIOF->ODR |=(1<<10);//LED1灭
delay_ms(500);//如果不加Stm32_Clock_Init500会变成5s,只有加上Stm32_Clock_Init(336,8,2,7);才会恢复正常
}
}
法三:位操作
本文不细讲其原理,详见《cortexM3权威指南》chpt05,虽然其原理比较复杂,但由于其实际代码实现上简化了代码量,所以经常被和库函数方法或寄存器方法混在一起用,当年学习的时候正点原子在寄存器版本的代码里面混杂着位带,就是这个把我搞得云里雾里……现在真香hh
另外正点原子自己编写的system文件夹下sys.h含有大量位带定义,可以直接用
初始化
利用GPIO_Set
led.c:
//LED IO初始化
void LED_Init(void)
{
//*******************下面是通过 位带 操作实现IO口控制**************************************
RCC->AHB1ENR|=1<<5;//使能PORTF时钟 RCC实际上是一个结构体指针
GPIO_Set(GPIOF,PIN9|PIN10,GPIO_MODE_OUT,GPIO_OTYPE_PP,GPIO_SPEED_100M,GPIO_PUPD_PU); //PF9,PF10设置
LED0=1;//LED0关闭
LED1=1;//LED1关闭
}
led.h:
#ifndef __LED_H
#define __LED_H
#include "sys.h"
//LED端口定义
#define LED0 PFout(9) // DS0
#define LED1 PFout(10) // DS1
void LED_Init(void);//初始化
#endif
main
int main(void)
{
// *******************下面是通过 位带 操作实现IO口控制**************************************
//位带法一:
Stm32_Clock_Init(336,8,2,7);//设置时钟,168Mhz
delay_init(168); //初始化延时函数
LED_Init(); //初始化LED时钟
while(1)
{
LED0=0; //DS0亮
LED1=1; //DS1灭
delay_ms(500);
LED0=1; //DS0灭
LED1=0; //DS1亮
delay_ms(500);
}
//位带法二:视频课程介绍方法,法一其实就是在法一基础上把PFout(9)在led.h中进行宏定义
/*
Stm32_Clock_Init(336,8,2,7);//设置时钟,168Mhz
delay_init(168); //初始化延时函数
LED_Init(); //初始化LED时钟
while(1)
{
PFout(9)=1; //DS0亮
PFout(10)=1; //DS1灭
delay_ms(500);
PFout(9)=0; //DS0灭
PFout(10)=0; //DS1亮
delay_ms(500);//如果不加Stm32_Clock_Init500会变成5s,只有加上Stm32_Clock_Init(336,8,2,7);才会恢复正常
}
*/
}
在led.h里面进行宏定义,即可在主函数实现类似51的对位直接操作,是不是代码非常简单hh