stm32学习随笔(四)——花式点灯师的三种方法(库函数、寄存器、位操作)

文章目录

    • 法一:使用库函数
      • 初始化
      • main
    • 法二:配置寄存器
      • 初始化
      • main
    • 法三:位操作
      • 初始化
      • main

详见 【正点原子】 手把手教你学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

你可能感兴趣的:(stm32)