【STM32】开发板学习1 NUCLEO-L476RG:GPIO例程 点亮LED2灯

一、芯片以及开发板

1.芯片:STM32L476 

1.开发板:Nuleo-L476RG

官网查看详细信息以及下载各种用户手册、说明书等等......

https://www.st.com/content/st_com/en/products/evaluation-tools/product-evaluation-tools/mcu-mpu-eval-tools/stm32-mcu-mpu-eval-tools/stm32-nucleo-boards/nucleo-l476rg.html#resource

 

【STM32】开发板学习1 NUCLEO-L476RG:GPIO例程 点亮LED2灯_第1张图片

3、GPIO示例参考资料

https://www.yiboard.com/thread-430-1-1.html

https://www.yiboard.com/thread-433-1-1.html

二、点灯前准备工作:

0.到官网找相应板子的驱动程序,下载安装ST-Link V2-1

1.下载STM32CubeMX(STM32芯片图形化配置工具,允许用户使用图形化向导生成C初始化代码,可以大大减轻开发工作、时间和费用),创建工程,选择芯片型号等

【STM32】开发板学习1 NUCLEO-L476RG:GPIO例程 点亮LED2灯_第2张图片                                                                                                                                  2.Keil5 点击工具栏的Pack Installer图标,安装STM32L4xx_DFP设备支持包,导入工程  ,通电,灯亮。

三、工程示例分析                                                                       

1.STM32CubeMX下载的示例工程,已将除用户代码的功能全部写好,我们只需要在main.c中添加我们想让板子跑的程序就OK。这里我们要点亮一个LED2灯。

关于HAL库的介绍:https://blog.csdn.net/m0_37621078/article/details/100084448

查看开发板用户手册:有3个LED灯,LD1常亮,LD2是user LED,LD3亮红色说明开发板有电源5V。

【STM32】开发板学习1 NUCLEO-L476RG:GPIO例程 点亮LED2灯_第3张图片

2.查看开发板的原理图:

(1)user按键,B1,与PC13相连

【STM32】开发板学习1 NUCLEO-L476RG:GPIO例程 点亮LED2灯_第4张图片

(2)LD2 ,也就是LED2灯,与PA5相连

【STM32】开发板学习1 NUCLEO-L476RG:GPIO例程 点亮LED2灯_第5张图片

 

四、点亮LD2

1.使用GPIO的第一步是使能时钟:

该功能通过RCC(Reset and clock control)寄存器控制。所有的GPIO连接到AHB2总线。HAL库提供了专门的函数来启用GPIOA的时钟。

__HAL_RCC_GPIOA_CLK_ENABLE

该函数定义可在文件stm32l4xx_hal_rcc.h找到,其实现函数如下:

 【STM32】开发板学习1 NUCLEO-L476RG:GPIO例程 点亮LED2灯_第6张图片

 

通过实现函数可知,主要方式是通过RCC_AHB2ENR寄存器的第0位(GPIOA EN)置位来实现。

2.定义一个GPIO_InitTypeDef结构体来设置GPIO的参数

GPIO_InitTypeDef  GPIO_InitStruct;

结构体声明在:stm3214xx_hal_gpio.h

【STM32】开发板学习1 NUCLEO-L476RG:GPIO例程 点亮LED2灯_第7张图片

 

该结构体有5个参数:

1. Pin 选择引脚编号

2. Mode 设置GPIO的工作模式

3. Pull 设置引脚的上拉/下拉

4. Speed 设置GPIO输出的最大频率

5. Alternate 设置选择引脚的复用功能

其中每项都有自己的选项

■  Pin:指定需要配置的GPIO管脚,该选项可以是以下的任何值:

   ■  GPIO_PIN_0:  选择引脚0;

   ■  GPIO_PIN_1: 选择引脚1;

   ■  GPIO_PIN_2:  选择引脚2;

   ■  GPIO_PIN_3:  选择引脚3;

   ■  GPIO_PIN_4:  选择引脚4;

   ■  GPIO_PIN_5:  选择引脚5;

   ■  GPIO_PIN_6:  选择引脚6;

   ■  GPIO_PIN_7:  选择引脚7;

   ■  GPIO_PIN_8:  选择引脚8;

   ■  GPIO_PIN_9:  选择引脚9;

   ■  GPIO_PIN_10:  选择引脚10;

   ■  GPIO_PIN_11:  选择引脚11;

   ■  GPIO_PIN_12:  选择引脚12;

   ■  GPIO_PIN_13:  选择引脚13;

   ■  GPIO_PIN_14:  选择引脚14;

   ■  GPIO_PIN_15:  选择引脚15;

   ■  GPIO_PIN_All: 选择所有的引脚;

   ■  GPIO_PIN_MASK:引脚掩码;

■  Mode:指定选择引脚的工作模式

   ■  GPIO_MODE_INPUT:悬浮输入模式

   ■  GPIO_MODE_OUTPUT_PP:推挽输出模式

   ■  GPIO_MODE_OUTPUT_OD:漏极开路输出模式

   ■  GPIO_MODE_AF_PP:复用功能推挽模式

   ■  GPIO_MODE_AF_OD:复用功能漏极开路模式

   ■  GPIO_MODE_ANALOG:模拟模式

   ■  GPIO_MODE_ANALOG_ADC_CONTROL:模拟模式,用于ADC转换

   ■  GPIO_MODE_IT_RISING:上升沿触发检测的外部中断模式

   ■  GPIO_MODE_IT_FALLING:下降沿触发检测的外部中断模式

   ■  GPIO_MODE_IT_RISING_FALLING:上升/下降沿触发检测的外部中断模式

   ■  GPIO_MODE_EVT_RISING:上升沿触发检测的外部事件模式

   ■  GPIO_MODE_EVT_FALLING:下降沿触发检测的外部事件模式

   ■  GPIO_MODE_EVT_RISING_FALLING:上升/下降沿触发检测的外部事件模式

   ■  Pull:指定引脚的上拉/下拉

   ■  GPIO_NOPULL:无上拉/下拉电阻

   ■  GPIO_PULLUP:带有上拉电阻

   ■  GPIO_PULLDOWN:带有下拉电阻

■  Speed:指定引脚的输出频率:

    ■  GPIO_SPEED_FREQ_LOW:输出频率最大为5MHz

    ■  GPIO_SPEED_FREQ_MEDIUM:输出频率范围5MHz-25MHz   

    ■  GPIO_SPEED_FREQ_HIGH:输出频率范围25MHz-50MHz

    ■  GPIO_SPEED_FREQ_VERY_HIGH::输出频率范围50MHz-80MHz

3.HAL库提供了GPIO的初始化函数HAL_GPIO_Init()   ,函数在stm32l4xx_hal_gpio.h

void HAL_GPIO_Init(GPIO_TypeDef  *GPIOx, GPIO_InitTypeDef *GPIO_Init)

该头文件的对应的.c文件中,有函数实现....en  看不懂

/**
  * @brief  Initialize the GPIOx peripheral according to the specified parameters in the GPIO_Init.
  * @param  GPIOx: where x can be (A..H) to select the GPIO peripheral for STM32L4 family
  * @param  GPIO_Init: pointer to a GPIO_InitTypeDef structure that contains
  *         the configuration information for the specified GPIO peripheral.
  * @retval None
  */
void HAL_GPIO_Init(GPIO_TypeDef  *GPIOx, GPIO_InitTypeDef *GPIO_Init)
{
  uint32_t position = 0x00u;
  uint32_t iocurrent;
  uint32_t temp;

  /* Check the parameters */
  assert_param(IS_GPIO_ALL_INSTANCE(GPIOx));
  assert_param(IS_GPIO_PIN(GPIO_Init->Pin));
  assert_param(IS_GPIO_MODE(GPIO_Init->Mode));
  assert_param(IS_GPIO_PULL(GPIO_Init->Pull));

  /* Configure the port pins */
  while (((GPIO_Init->Pin) >> position) != 0x00u)
  {
    /* Get current io position */
    iocurrent = (GPIO_Init->Pin) & (1uL << position);

    if (iocurrent != 0x00u)
    {
      /*--------------------- GPIO Mode Configuration ------------------------*/
      /* In case of Alternate function mode selection */
      if((GPIO_Init->Mode == GPIO_MODE_AF_PP) || (GPIO_Init->Mode == GPIO_MODE_AF_OD))
      {
        /* Check the Alternate function parameters */
        assert_param(IS_GPIO_AF_INSTANCE(GPIOx));
        assert_param(IS_GPIO_AF(GPIO_Init->Alternate));

        /* Configure Alternate function mapped with the current IO */
        temp = GPIOx->AFR[position >> 3u];
        temp &= ~(0xFu << ((position & 0x07u) * 4u));
        temp |= ((GPIO_Init->Alternate) << ((position & 0x07u) * 4u));
        GPIOx->AFR[position >> 3u] = temp;
      }

      /* Configure IO Direction mode (Input, Output, Alternate or Analog) */
      temp = GPIOx->MODER;
      temp &= ~(GPIO_MODER_MODE0 << (position * 2u));
      temp |= ((GPIO_Init->Mode & GPIO_MODE) << (position * 2u));
      GPIOx->MODER = temp;

      /* In case of Output or Alternate function mode selection */
      if((GPIO_Init->Mode == GPIO_MODE_OUTPUT_PP) || (GPIO_Init->Mode == GPIO_MODE_AF_PP) ||
         (GPIO_Init->Mode == GPIO_MODE_OUTPUT_OD) || (GPIO_Init->Mode == GPIO_MODE_AF_OD))
      {
        /* Check the Speed parameter */
        assert_param(IS_GPIO_SPEED(GPIO_Init->Speed));
        /* Configure the IO Speed */
        temp = GPIOx->OSPEEDR;
        temp &= ~(GPIO_OSPEEDR_OSPEED0 << (position * 2u));
        temp |= (GPIO_Init->Speed << (position * 2u));
        GPIOx->OSPEEDR = temp;

        /* Configure the IO Output Type */
        temp = GPIOx->OTYPER;
        temp &= ~(GPIO_OTYPER_OT0 << position) ;
        temp |= (((GPIO_Init->Mode & GPIO_OUTPUT_TYPE) >> 4u) << position);
        GPIOx->OTYPER = temp;
      }

#if defined(STM32L471xx) || defined(STM32L475xx) || defined(STM32L476xx) || defined(STM32L485xx) || defined(STM32L486xx)

      /* In case of Analog mode, check if ADC control mode is selected */
      if((GPIO_Init->Mode & GPIO_MODE_ANALOG) == GPIO_MODE_ANALOG)
      {
        /* Configure the IO Output Type */
        temp = GPIOx->ASCR;
        temp &= ~(GPIO_ASCR_ASC0 << position) ;
        temp |= (((GPIO_Init->Mode & ANALOG_MODE) >> 3) << position);
        GPIOx->ASCR = temp;
      }

#endif /* STM32L471xx || STM32L475xx || STM32L476xx || STM32L485xx || STM32L486xx */

      /* Activate the Pull-up or Pull down resistor for the current IO */
      temp = GPIOx->PUPDR;
      temp &= ~(GPIO_PUPDR_PUPD0 << (position * 2u));
      temp |= ((GPIO_Init->Pull) << (position * 2u));
      GPIOx->PUPDR = temp;

      /*--------------------- EXTI Mode Configuration ------------------------*/
      /* Configure the External Interrupt or event for the current IO */
      if((GPIO_Init->Mode & EXTI_MODE) == EXTI_MODE)
      {
        /* Enable SYSCFG Clock */
        __HAL_RCC_SYSCFG_CLK_ENABLE();

        temp = SYSCFG->EXTICR[position >> 2u];
        temp &= ~(0x0FuL << (4u * (position & 0x03u)));
        temp |= (GPIO_GET_INDEX(GPIOx) << (4u * (position & 0x03u)));
        SYSCFG->EXTICR[position >> 2u] = temp;

        /* Clear EXTI line configuration */
        temp = EXTI->IMR1;
        temp &= ~(iocurrent);
        if((GPIO_Init->Mode & GPIO_MODE_IT) == GPIO_MODE_IT)
        {
          temp |= iocurrent;
        }
        EXTI->IMR1 = temp;

        temp = EXTI->EMR1;
        temp &= ~(iocurrent);
        if((GPIO_Init->Mode & GPIO_MODE_EVT) == GPIO_MODE_EVT)
        {
          temp |= iocurrent;
        }
        EXTI->EMR1 = temp;

        /* Clear Rising Falling edge configuration */
        temp = EXTI->RTSR1;
        temp &= ~(iocurrent);
        if((GPIO_Init->Mode & RISING_EDGE) == RISING_EDGE)
        {
          temp |= iocurrent;
        }
        EXTI->RTSR1 = temp;

        temp = EXTI->FTSR1;
        temp &= ~(iocurrent);
        if((GPIO_Init->Mode & FALLING_EDGE) == FALLING_EDGE)
        {
          temp |= iocurrent;
        }
        EXTI->FTSR1 = temp;
      }
    }

    position++;
  }
}

 

4.使用库函数,点亮LD2

int main(void)
{
  /* USER CODE BEGIN 1 */
				//定义一个GPIO结构体 
	      GPIO_InitTypeDef GPIO_InitDef;
        
        //启用GPIOA时钟
        __HAL_RCC_GPIOA_CLK_ENABLE();

        //定义LD2的每一个参数 init配置
        GPIO_InitDef.Pin = GPIO_PIN_5;
        GPIO_InitDef.Mode = GPIO_MODE_OUTPUT_PP;
        GPIO_InitDef.Pull = GPIO_NOPULL;
        GPIO_InitDef.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
        HAL_GPIO_Init(GPIOA, &GPIO_InitDef);

        //user按键的设置
        /*
        __HAL_RCC_GPIOC_CLK_ENABLE();
        GPIO_InitDef.Pin = GPIO_PIN_13;
        GPIO_InitDef.Mode = GPIO_MODE_INPUT;
        GPIO_InitDef.Pull = GPIO_PULLDOWN;
        GPIO_InitDef.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
        //Initialize pins
        HAL_GPIO_Init(GPIOC, &GPIO_InitDef);
        */

        while (1) 
        {
				  //点亮LD2
          HAL_GPIO_WritePin(GPIOA, GPIO_PIN_5, GPIO_PIN_SET);
        }

}

编译、无报错、load到开发板中,这样,开发板的LED2就点亮了。

回顾电路板的原理图:

【STM32】开发板学习1 NUCLEO-L476RG:GPIO例程 点亮LED2灯_第8张图片

我们要点亮LD2,所以要给PA5引脚接高电平。

如何给?HAL库中给出了很多生成的代码,代码的做法是:将GPIO端分组,分为ABCDEFG...等几个组,然后再用引脚1234567...确定。所以写的代码为:

 GPIO_InitDef.Pin = GPIO_PIN_5;
 HAL_GPIO_Init(GPIOA, &GPIO_InitDef);

先给结构体确定好引脚5,再规定这是A组的,就设置完一个结构体,也就是设置完一个LD2的参数。

五、关于GPIO结构体中的工作模式 GPIO_InitTypeDef ->Mode

1.这次点亮LED的工作模式为推挽输出:

推挽输出:意思就是输出具有驱动能力(比如:引脚上接一个LED,可以直接点亮,若是开漏输出,就不能点亮LED)。

推挽输出这个功能是比较常用的功能,我们一般输出控制某个信号,基本上都是配置为GPIO_Mode_Out_PP 推挽输出。

推挽输出电流大小也是比较关键的一个参数,根据芯片不同,其大小也不同,具体可以查看数据手册:

参考链接:https://blog.csdn.net/ybhuangfugui/article/details/52953533

 

1、上拉输入:上拉就是把电位拉高,比如拉到Vcc。上拉就是将不确定的信号通过一个电阻嵌位在高电平!电阻同时起限流作用!强弱只是上拉电阻的阻值不同,没有什么严格区分。

2、下拉输入:就是把电压拉低,拉到GND。与上拉原理相似。

3、浮空输入:浮空(floating)就是逻辑器件的输入引脚即不接高电平,也不接低电平。由于逻辑器件的内部结构,当它输入引脚悬空时,相当于该引脚接了高电平。一般实际运用时,引脚不建议悬空,易受干扰。 通俗讲就是让管脚什么都不接,浮空着。

4、模拟输入:模拟输入是指传统方式的输入。数字输入是输入PCM数字信号,即0,1的二进制数字信号,通过数模转换,转换成模拟信号,经前级放大进入功率放大器,功率放大器还是模拟的。

5、推挽输出:可以输出高,低电平,连接数字器件;推挽结构一般是指两个三极管分别受两互补信号的控制,总是在一个三极管导通的时候另一个截止。高低电平由IC的电源低定。

6、开漏输出:输出端相当于三极管的集电极。要得到高电平状态需要上拉电阻才行,适合于做电流型的驱动,其吸收电流的能力相对强(一般20mA以内)。

7、复用输出:可以理解为GPIO口被用作第二功能时的配置情况(即并非作为通用IO口使用)。端口必须配置成复用功能输出模式(推挽或开漏)。

好好理解一下,其实就能得出,这些GPIO模式能在下面几种情况应用,参考网上的资料后,总结出下面几点:
在STM32中选用IO模式,下面是参考网上的资料后总结出的结果。
(1)GPIO_Mode_AIN 模拟输入—应用ADC模拟输入,或者低功耗下省电

(2)GPIO_Mode_IN_FLOATING 浮空输入—可以做KEY识别

(3)GPIO_Mode_IPD 下拉输入— IO内部下拉电阻输入

(4)GPIO_Mode_IPU 上拉输入—IO内部上拉电阻输入

(5)GPIO_Mode_Out_OD 开漏输出—IO输出0接GND,IO输出1,悬空,需要外接上拉电阻,才能实现输出高电平。当输出为1时,IO口的状态由上拉电阻拉高电平,但由于是开漏输出模式,这样IO口也就可以由外部电路改变为低电平或不变。可以读IO输入电平变化,实现C51的IO双向功能。

(6)GPIO_Mode_Out_PP 推挽输出—IO输出0-接GND,IO输出1 -接VCC,读输入值是未知的。

(7)GPIO_Mode_AF_OD 复用开漏输出—片内外设功能(TX1,MOSI,MISO.SCK.SS)。

(8)GPIO_Mode_AF_PP 复用推挽输出—片内外设功能(I2C的SCL,SDA)。
————————————————

参考链接:https://blog.csdn.net/santa9527/article/details/78842832

 

你可能感兴趣的:(嵌入式)