嵌入式|蓝桥杯STM32G431(HAL库开发)——CT117E学习笔记04:从零开始创建工程模板并开始点灯

系列文章目录

嵌入式|蓝桥杯STM32G431(HAL库开发)——CT117E学习笔记01:赛事介绍与硬件平台

嵌入式|蓝桥杯STM32G431(HAL库开发)——CT117E学习笔记02:开发环境安装

嵌入式|蓝桥杯STM32G431(HAL库开发)——CT117E学习笔记03:G4时钟结构

嵌入式|蓝桥杯STM32G431(HAL库开发)——CT117E学习笔记04:从零开始创建工程模板并开始点灯

嵌入式|蓝桥杯STM32G431(HAL库开发)——CT117E学习笔记05:Systick滴答定时器

嵌入式|蓝桥杯STM32G431(HAL库开发)——CT117E学习笔记06:按键输入

嵌入式|蓝桥杯STM32G431(HAL库开发)——CT117E学习笔记07:ADC模数转换

嵌入式|蓝桥杯STM32G431(HAL库开发)——CT117E学习笔记08:LCD液晶屏

嵌入式|蓝桥杯STM32G431(HAL库开发)——CT117E学习笔记09:EEPROM

嵌入式|蓝桥杯STM32G431(HAL库开发)——CT117E学习笔记10:USART串口通讯


目录

系列文章目录

前言

一、GPIO输出模式

二、电路原理

三、程序设计

1.复制工程

2.设置GPIO

3.项目管理器

4.配置下载算法

5.编写LED代码

5.1 新建文件

5.2 常用函数

5.3 C语言基础

5.4 代码编写

5.5 函数声明

6.调用函数

6.1 在main.c中添加led.h

6.2 调用LED_Control函数,完成对LED的控制。

总结


前言

上一节讲了时钟的配置并且生成了一个没有用的工程文件(里面可以看时钟配置的代码),这一节我们将从零开始新建一个带有GPIO的文件,能够实现具体的功能——LED灯的控制。并且在其中可以学习GPIO的相关知识,以及需要用到的函数,并自己进行流水灯的编程。

再将LED模块的原理之前,我们必须先了解GPIO的相关知识,因为LED灯模块本质上就是GPIO的输出。


一、GPIO输出模式

打开参考手册351页可以看到GPIO的定义,我们可知GPIO输出模式可以配置成推挽模式或者开漏模式。输出的数据可以通过输出寄存器或其他外设进行控制,也可以进行输入输出的速度选择,即配置快速慢速模式。并且可以看到两个I/O框图,一个是基础的I/O框图,一个是5V容忍的I/O框图,两张图的主要区别就是一个Vdd。我们就以五容忍的图为例。

如图,主要关注输出的部分。Write即写入的数据,经过两个寄存器到达复选框,复选框我们今天就只用第一个,然后到达Output control,它主要是两个mos管的驱动器加上一个取反。举例来说,假设写入的数据是0,即write是0,那么经过Output control之后就会变成1,然后到达两个MOS管,因为N-MOS是高电平导通,P-MOS是低电平导通,那么这里两个MOS管都是高电平,所以P-MOS截止,N-MOS导通,所以右侧的I/O pin会流向N-MOS下侧的Vss,而Vss一般是接地的,所以输出就是0。这样看来就是输入为0,就可以输出0。那么换过来看,如果输入是1,则P-MOS导通,N-MOS截止,那么I/O会流向Vddiox,他的值是3.3V,即输出3.3V,也就是输入1,输出1。这就是输出驱动器的工作原理。

图上也写了一行字,说可以配置成推挽模式(push-pull)或者开漏模式(open-drain)或disable。这里我们解释一下推挽模式和开漏模式。什么是推挽,简而言之就是,两个管子,一个导通另一个就会截止,就像刚刚举例说的那样,两个MOS管对接就可以组成推挽结构。所以推挽模式可以输出高电平或者低电平,且有一定的驱动能力(尽量在10mA以内,不然芯片会发烫)。开漏输出就是只用MOS管的漏极进行输出,所以要把上面的P-MOS直接断掉,只用N-MOS,但是这样的话就只能输出强低电平,如果要输出高电平就不行,所以我们就要用别的方法来输出高电平,这就需要一个上拉的电阻来确定电平。因为输入1时两个MOS管都关断,所以I/O pin会直接通过上拉电阻接到Vdd上来实现输出高电平。当输入低电平时,N-MOS导通,I/O pin会直接接地,由于电阻的阻值很大,一般是几千欧姆,而MOS管的导通电阻只有几欧姆或者几十欧姆,所以不会影响到输出的电平。这样就能实现输出低电平。

所以我们可知推挽模式和开漏模式的区别了。


二、电路原理

我们首先看一下板子上LED模块的电路原理图,打开产品手册就可以看到(之前的第一章里也有)。

如图,所有LED灯的阳极都接在Vdd上,即共阳接法。那么如果要点亮LED,就要在负极接低电平,同理接高电平就会关断。但是LED的阴极是接在一个锁存器上的,并不是直接接在芯片的接口上。这是因为在我们的板子上,PC8-15不止用于LED灯模块,而且还要用在LCD屏幕上,所以要用锁存器来控制LED。

那么我们就要研究一下锁存器了。锁存器相当于一个门锁,而PD2是这个门锁的钥匙,PD2如果为1,相当于”开锁“,锁存器D端的电平会进入,并传输到Q端上,也就是让Q端的电平与D端保持相同。PD2如果为0,就相当于”关锁“,锁存器的D端无论如何变化,Q端都保持不变,与D端无关。这样先开锁,用PC接口控制LED灯,然后关锁,实现电平的保持,PC接口就可以去做别的事情。

简单来说就是:PC端低电平点亮,PD2控制锁存器,高电平写入,低电平保存。


三、程序设计

程序设计的步骤:

1、从“赛场资源”里复制一份程序作为“模板”,一份作为“编程工程”。

(模板作为CubeMX生成代码的工程,编程工程作为自己编写代码的工程,并从模板里移植代码)

2、设置LED相关的GPIO为推挽输出模式。(PD2、PC8-15)

3、CubeMX里设置将每个外设单独生成.c和.h文件,并生成代码。

4、下载程序时,设置Debug-Flash Download-添加STM32G4的下载算法。

(下载程序,查看LCD是否正常)

5、编写led.c和led.h文件,并添加到工程中,编写LED_Control函数用于控制LED灯模块。

6、在main.c中添加led.h,并调用LED_Control函数,完成对LED的控制。

说明:比赛的时候会提供一份赛场资源包,资源包里的内容几乎每届都是一样的。资源包里会有一个液晶初始化的例程,我们要拷贝两份,一份作为模板一份作为编程工程。原因是CubeMX在生成代码的时候会覆盖自己写的一些程序,所以如果只在一个工程里进行编程的话,如果再生成一些外设的初始化,会覆盖自己之前的代码,所以要复制两份工程。复制的时候不推荐放在桌面上,找个文件夹专门装着。


1.复制工程

在“赛场资源”里复制“HAL_06_LCD”到新建文件夹中,改名为“HAL_LED”,点击CubeMX的工程。会弹出一个是否要更新固件包的弹窗(因为比赛用的是1.2版本的固件包,所以软件会提醒你更新,现在实际上已经更新到1.5了,但是比赛的时候是不联网的,所以也更新不了,所以就不更新了),选择continue进入初始化配置界面。

这里PC8-15已经设置好了,因为LCD模块本身就需要用到这些输出。但是PD2是要我们自己设置的,这里设置成GPIO_Output。

2.设置GPIO

我们点击System core中的GPIO窗口,可以看到所有LCD中需要用到的接口,这里PC和PD我们都设置low低电平,不进行改变(因为没必要,在编程的时候可以用软件控制上电后的状态,而且我们也不希望一上电锁存器是打开的状态)。

pull up和pull down也不需要设置,因为推挽输出不需要设置上下拉。maximum output即最大输出速度我们也不需要改,low就可以了,因为不需要led输出的特别快,就默认配置就行了。

RCC选择外部晶振,不需要变动。

时钟树也配置好了。

3.项目管理器

项目管理器中更改一下MDK-ARM版本为v5,并设置生成.c和.h文件,方便后续移植程序。

生成代码,编译一下看有没有错误。

4.配置下载算法

点击魔术棒,设置Debug-Flash Download-Add-G4 128 FLASH

5.编写LED代码

5.1 新建文件

新建一个text文件并保存为led.c放在src里面,再新建一个led.h文件放在inc里面。

复制gpio.h的格式到led.h里面,并稍作更改,即#ifndef#define,原因是防止一些变量的重复定义。

在led.c中添加#include“led.h”

在led.h中添加#include"main.h"(和gpio.h中一样)。

然后双击Application把led.c放进来,底下就在led.c里面写led的控制程序了。

5.2 常用函数

在编写程序之前我们要先了解一下GPIO的引脚控制函数:

  • 设置引脚状态值HAL_GPIO_WritePin()

格式:HAL_GPIO_WritePin(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin, GPIO_PinState PinState)
第一个参数:GPIOA,B,C,D…GPIO组
第二个参数:可以取GPIO_PIN_0~GPIO_PIN_15,GPIO编号
第三个参数:GPIO_PIN_SET(置高电平),GPIO_PIN_RESET(置低电平)
如:HAL_GPIO_WritePin(GPIOC,GPIO_PIN_0 | GPIO_PIN_1,GPIO_PIN_SET); //PC0、PC1置1

  • 读取引脚电平值,HAL_GPIO_ReadPin

HAL_GPIO_ReadPin(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin)
返回值:返回"0",对应引脚为低电平,返回"1",对应引脚为高电平。
如:变量pinstate = HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_0); //读取PA0的电平值

  • GPIO引脚翻转,HAL_GPIO_TogglePin

HAL_GPIO_TogglePin(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin)
HAL_Delay(DELAY_TIME); 时间单位ms,由SYS系统时钟提供。

  • 延时函数,HAL_Delay()

HAL_Delay();括号里面是延时的长短,单位是ms,如HAL_Delay(1000)就是延时一秒。

5.3 C语言基础

其实这里根据PIN的定义我们已经可以看出,PIN是由一个16位整数定义的。即uint16_t,这个意思是一个无符号的16位整数变量。也就是说,不同的引脚(1到16)我们可以用16位整数代替,如0x0000

我们打出GPIO_PIN_8 ,然后右击,可以看到不同引脚的定义。

所以我们可以用16位整数0x0100来代替GPIO_PIN_8这个参数,那么写入引脚状态我们就可以写成:HAL_GPIO_WritePin(GPIOC, 0x0100, GPIO_PIN_SET),就代表关闭PC8。

后面的9是0x0200,10是0x0400,11是0x0800,12是0x1000......

除此之外,我们还可以用“与”的方法一次性表示多个引脚,比如,要同时表示PC10和PC11,就可以表示为GPIO_PIN_10|GPIO_PIN_11,等同于0x0400|0x0800,也就等同于0x0C00,这样通过0x0C00这一个数就可以控制LED3和LED4两个灯,同理,0xF000就可以控制后四个灯,0xFF00就可以控制所有灯,这就是为什么图中GPIO_PIN_All定义为0xFFFF,实际上是所有16进制数相与的结果。所以说,HAL_GPIO_WritePin(GPIOC,GPIO_PIN_8|GPIO_PIN_9|GPIO_PIN_10|GPIO_PIN11|GPIO_PIN_12|GPIO_PIN_13|GPIO_PIN_14|GPIO_PIN_15,GPIO_PIN_SET) 

以及

HAL_GPIO_WritePin(GPIOC,GPIO_PIN_All,GPIO_PIN_SET) 

以及

HAL_GPIO_WritePin(GPIOC,0xFF00,GPIO_PIN_SET) 

这三者在这里的作用是一样的,都是关闭所有LED灯。

但是实际上,由于我们只有8个灯,只用到了8-15这8个数字,每一位只用1、2、4、8四个数字来定义,所以我们只用到了uint16_t的前两个数字,后面两个都是0,根本没有用到,所以理论上我们只需要用8位就可以表示所有灯了,我们可以输入一个8位数,然后左移8位,以此来表示16位数,比如0x01,左移8位,就可以表示0x0100,这个过程我们可以写成:0x01<<8。同理,PC14可以表示成:0x40<<8。“与”运算也不受影响:0xF0<<8可以表示为PC12|PC13|PC14|PC15(因为F=15=1+2+4+8)。这样我们就可以用一个8位整数来控制所有的LED灯了。

我们最好用一个变量来表示这个8位整数,这样就可以定义一个函数,输入一个8位数到这个函数里面,就可以控制哪些灯亮。比如说定义一个8位整数变量叫abc,就可以写成:uint8_t abc,这样以后abc<<8就可以表示16位数,把abc定义在函数里面,void LED_Control(uint8_t abc),那么以后通过这个函数的输入值就可以实现LED灯的开关。如LED_Control(0x06),就可以控制第2、3个灯的开关。

除此之外,我们还要了解一下,在正常的编程中,uint8_t也可以写成unsigned char或者u8,都表示为定义一个无符号的8位整数。

5.4 代码编写

#include"led.h"


void LED_Control(u8 led_ctrl) //构造一个无返回值的函数,名为LED_Control。括号内是输入参数,定义一个8位整数变量led_ctrl
{
    //先关闭所有的灯,注意led灯是低电平点亮
    HAL_GPIO_WritePin(GPIOC,0xFF00,GPIO_PIN_SET)
    //HAL_GPIO_WritePin(GPIOC,GPIO_PIN_8|GPIO_PIN_9|GPIO_PIN_10|GPIO_PIN11|GPIO_PIN_12|GPIO_PIN_13|GPIO_PIN_14|GPIO_PIN_15,GPIO_PIN_SET);
    HAL_GPIO_WritePin(GPIOD,GPIO_PIN_2,GPIO_PIN_SET);//打开锁存器
    HAL_GPIO_WritePin(GPIOD,GPIO_PIN_2,GPIO_PIN_RESET);//关闭锁存器
    
    //根据led_ctrl的值来点亮对应的LED灯
    HAL_GPIO_WritePin(GPIOC,led_ctrl<<8,GPIO_PIN_RESET);//左移8位,变成16位数一一对应PC8-15
    HAL_GPIO_WritePin(GPIOD,GPIO_PIN_2,GPIO_PIN_SET);
    HAL_GPIO_WritePin(GPIOD,GPIO_PIN_2,GPIO_PIN_RESET);
}
//调用的时候直接输入八位整数,如0x04,就打开3号灯

这个函数的功能是打开一部分LED灯(传入的8位数据控制8个灯),可以同时控制多个灯的打开,如:

LED_Control(0x06);    //打开2、3号灯(6=2+4)

LED_Control(0xF0);    //打开5、6、7、8号灯(F=15=1+2+4+8)

LED_Control(0xAB);   //打开1、2、4、6、8号灯(B=11=1+2+8,A=10=2+8)

5.5 函数声明

在led.h中声明一下这个函数:void LED_Control(u8 led_ctrl);

6.调用函数

6.1 在main.c中添加led.h

在main.c中的Includes后面添加#include "led.h"

6.2 调用LED_Control函数,完成对LED的控制。

在while(1)的主循环里调用这个函数,LED_Control(0x80);,即可打开8号灯。

如图,LED8被点亮了。

我们也可以用HAL_Delay()函数做一些LED的闪烁程序等,如:

  while (1)
  {
		LED_Control(0xFF);//点亮所有灯
		HAL_Delay(500);//延时0.5s
		LED_Control(0x00);//关闭所有灯
		HAL_Delay(500);//延时0.5s
  }

就能实现所有灯的0.5秒的闪烁。


总结

本节的重点是要熟悉程序设计的流程,那6个步骤要滚瓜烂熟。

然后就是要熟悉GPIO的相关函数:

设置引脚状态值HAL_GPIO_WritePin();

读取引脚电平值,HAL_GPIO_ReadPin;

GPIO引脚翻转,HAL_GPIO_TogglePin;

延时函数,HAL_Delay();

下节课我们会学习定时器的相关内容。

  • 21
    点赞
  • 25
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
STM32G431HAL库中,可以使用外部中断来处理外部信号的触发。下面是使用HAL库进行外部中断的步骤: 1. 配置IO口为外部中断输入源:在GPIO初始化函数中,使用`GPIO_InitStruct.Mode = GPIO_MODE_IT_RISING/FALLING/RISING_FALLING`来设置IO口的触发方式,可以选择上升沿触发、下降沿触发或者上升下降沿都触发。 2. 在NVIC中使能外部中断,并分配优先级:使用`HAL_NVIC_SetPriority(EXTI_IRQn, PreemptPriority, SubPriority)`函数设置外部中断的优先级,其中`EXTI_IRQn`是外部中断的中断号,`PreemptPriority`是抢占优先级,`SubPriority`是子优先级。 3. 实际的中断执行函数:在中断执行函数中,可以编写具体的中断处理代码。在HAL库中,中断执行函数的命名规则为`void EXTIx_IRQHandler(void)`,其中`x`是对应的外部中断线号。 4. 使能中断时:使用`HAL_NVIC_EnableIRQ(EXTI_IRQn)`函数使能外部中断。 下面是一个使用HAL库进行外部中断的示例代码: ```c // 配置IO口为外部中断输入源 GPIO_InitTypeDef GPIO_InitStruct; GPIO_InitStruct.Pin = GPIO_PIN_0; // 设置为对应的IO口 GPIO_InitStruct.Mode = GPIO_MODE_IT_RISING; // 上升沿触发 GPIO_InitStruct.Pull = GPIO_PULLDOWN; // 默认拉低 HAL_GPIO_Init(GPIOA, &GPIO_InitStruct); // 在NVIC中使能外部中断,并分配优先级 HAL_NVIC_SetPriority(EXTI0_IRQn, 0, 0); // 实际的中断执行函数 void EXTI0_IRQHandler(void) { HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_0); } // 使能中断 HAL_NVIC_EnableIRQ(EXTI0_IRQn); ``` 请注意,以上代码仅为示例,具体的配置和使用方法可能会根据实际情况有所不同。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值