蓝桥杯嵌入式赛道备考1 —— 基础GPIO实战

1. 点亮一个LED

蓝桥杯的板子资料的URL,笔者是从GitHub - JoyRiderJie/LanQiaoBei-QianRuShi拉去下来的。这个是Github仓库地址。

从应用层去玩一个开发板子,首先需要的是去尝试是点亮一个LED。让我们切换到手册《CT117E——产品手册》的第11页,笔者截取了一张图。

我们分析一下这个图。首先,从左侧看起,VDD给LED提供了高电平,也就是使用了上拉电阻上拉到了VCC,想要点亮这个LED,只需要给对应的电平一个低电平。所以事情变得非常的简单。只需要使能对应的GPIO拉低就好了。

问题和难点就在这个SN74HC753ADWR,这是什么呢?这是一个锁存器。锁存器芯片74HC573芯片的用法,及其在实际电路中的应用_74hc573真值表-CSDN博客,这一博客介绍了锁存器的用法。

笔者建议使用MX偷懒,给一个推挽输出的GPIO,然后HAL_GPIO_WritePin就完事。

我们需要注意的是,我们对锁存器编程只需要关心他的输入输出即可,对于这个锁存器,给一个高电平的时候。锁存器释放状态,允许将状态通到右侧。当给一个低电平的时候则是将状态锁住。所以,对LED编程,我们首先需要给寄存器释放状态,给一个高电平,然后写入LED电平后给一个低电平。

void led_set_status(uint8_t states)
{HAL_GPIO_WritePin(LOCKER_GPIO_Port, LOCKER_Pin, GPIO_PIN_SET);// 这个compose_all_led_gpio稍后说明,理解为生成所有LED的Pin即可HAL_GPIO_WritePin(GPIOC, __compose_all_led_gpio(), !states);HAL_GPIO_WritePin(LOCKER_GPIO_Port, LOCKER_Pin, GPIO_PIN_RESET);
}

现在麻烦之处在于,我们如何方便的操作8个LED呢?我们难道必须给具体的Pin吗?其实不然,下面就来揭晓笔者写的__compose_all_led_gpio

uint16_t __compose_all_led_gpio(void)
{uint16_t gpio_type = 0;for(uint8_t i = 0; i < 8; i++){gpio_type |= (GPIO_PIN_8 << (i) );}return gpio_type;
}

关心的是中间的(GPIO_PIN_8 << (i)),这个操作让我们到hal_gpio.h文件中仔细瞧瞧。

#define GPIO_PIN_0                 ((uint16_t)0x0001)  /* Pin 0 selected    */
#define GPIO_PIN_1                 ((uint16_t)0x0002)  /* Pin 1 selected    */
#define GPIO_PIN_2                 ((uint16_t)0x0004)  /* Pin 2 selected    */
#define GPIO_PIN_3                 ((uint16_t)0x0008)  /* Pin 3 selected    */
#define GPIO_PIN_4                 ((uint16_t)0x0010)  /* Pin 4 selected    */
#define GPIO_PIN_5                 ((uint16_t)0x0020)  /* Pin 5 selected    */
#define GPIO_PIN_6                 ((uint16_t)0x0040)  /* Pin 6 selected    */
#define GPIO_PIN_7                 ((uint16_t)0x0080)  /* Pin 7 selected    */
#define GPIO_PIN_8                 ((uint16_t)0x0100)  /* Pin 8 selected    */
#define GPIO_PIN_9                 ((uint16_t)0x0200)  /* Pin 9 selected    */
#define GPIO_PIN_10                ((uint16_t)0x0400)  /* Pin 10 selected   */
#define GPIO_PIN_11                ((uint16_t)0x0800)  /* Pin 11 selected   */
#define GPIO_PIN_12                ((uint16_t)0x1000)  /* Pin 12 selected   */
#define GPIO_PIN_13                ((uint16_t)0x2000)  /* Pin 13 selected   */
#define GPIO_PIN_14                ((uint16_t)0x4000)  /* Pin 14 selected   */
#define GPIO_PIN_15                ((uint16_t)0x8000)  /* Pin 15 selected   */
#define GPIO_PIN_All               ((uint16_t)0xFFFF)  /* All pins selected */

眼尖的朋友已经一眼看到了要点。那就是我们完全可以通过移位得到一个GPIO的Pin。Pin9就是Pin8左移移位得到的。基于这个技巧,我们可以迅速的写出这样的方便的代码

/* index is from 0 for the gpio8 */
void led_set_single_status(uint8_t states, uint8_t index)
{HAL_GPIO_WritePin(LOCKER_GPIO_Port, LOCKER_Pin, GPIO_PIN_SET);HAL_GPIO_WritePin(GPIOC, GPIO_PIN_8 << (index), !states);HAL_GPIO_WritePin(LOCKER_GPIO_Port, LOCKER_Pin, GPIO_PIN_RESET);
}

所以,现在我们来看看流水线灯的代码吧:

/* USER CODE BEGIN Header */
/********************************************************************************* @file           : main.c* @brief          : Main program body******************************************************************************* @attention** <h2><center>&copy; Copyright (c) 2025 STMicroelectronics.* All rights reserved.</center></h2>** This software component is licensed by ST under BSD 3-Clause license,* the "License"; You may not use this file except in compliance with the* License. You may obtain a copy of the License at:*                        opensource.org/licenses/BSD-3-Clause********************************************************************************/
/* USER CODE END Header */
/* Includes ------------------------------------------------------------------*/
#include "main.h"
#include "gpio.h"
​
/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include "led.h"
/* USER CODE END Includes */
​
...
int main(void)
{/* USER CODE BEGIN 1 */
​/* USER CODE END 1 */
​/* MCU Configuration--------------------------------------------------------*/
​/* Reset of all peripherals, Initializes the Flash interface and the Systick. */HAL_Init();
​/* USER CODE BEGIN Init */
​/* USER CODE END Init */
​/* Configure the system clock */SystemClock_Config();
​/* USER CODE BEGIN SysInit */
​/* USER CODE END SysInit */
​/* Initialize all configured peripherals */MX_GPIO_Init();/* USER CODE BEGIN 2 */// 对LED进行初始化LED_GPIO_Init();/* USER CODE END 2 */
​/* Infinite loop *//* USER CODE BEGIN WHILE */// states是LED的点亮点灭状态,i表达的是第几个GPIOuint8_t states = 1;uint8_t i = 0;while (1){led_set_single_status(states, i);i++;if(i == 8){states = !states;i = 0;}HAL_Delay(500);/* USER CODE END WHILE *//* USER CODE BEGIN 3 */}/* USER CODE END 3 */
}
...

按键Key

GPIO除了编程为对外输出,还可以编程为输入。让我们看看产品手册的指导

很好,我们可以看到,板子已经有了上拉和下拉,所以事情变得非常的简单,让我们简单的配置一下...

void key_init(void)
{GPIO_InitTypeDef GPIO_InitStruct = {0};// enables the led clocks__HAL_RCC_GPIOB_CLK_ENABLE();__HAL_RCC_GPIOA_CLK_ENABLE();uint16_t key_pins = GPIO_PIN_0 | GPIO_PIN_1 | GPIO_PIN_2 ;GPIO_InitStruct.Pin = key_pins;GPIO_InitStruct.Mode = GPIO_MODE_INPUT;GPIO_InitStruct.Pull = GPIO_NOPULL; // 无所谓上下,因为已经有外置的上下拉电阻了GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);key_pins = GPIO_PIN_0;GPIO_InitStruct.Pin = key_pins;HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
}

笔者比较喜欢自己配置,比较自由,但是代价就是不熟练就会发生低级错误。所以建议是少的就直接MX梭哈。

状态机编程

我们的PBX和PAX读取我们的按钮,无非就是确定的高低电平。在按钮没有嗯下的时候,则是高电平,嗯下了就是低电平。为此,抽象一个状态机变得非常有必要

static struct {GPIO_TypeDef*   key_port;uint16_t        pin_state;uint8_t         state;uint8_t         prev_state;
}states[4] = {{GPIOB, GPIO_PIN_0, 1, 1},{GPIOB, GPIO_PIN_1, 1, 1},{GPIOB, GPIO_PIN_2, 1, 1},{GPIOA, GPIO_PIN_0, 1, 1}
};

解释一下笔者抽象的结构体,首先是对应Key的GPIO表达,其次是两个state作为状态。我们检测的是——摁扭嗯下和摁扭抬起的操作。摁扭嗯下,此时此刻,我们使用GPIO_ReadPin来读取我们的GPIO状态

uint8_t key_scanner(uint8_t index)
{uint8_t result = 0;states[index].state = HAL_GPIO_ReadPin(states[index].key_port, states[index].pin_state);

此时,我们应该得到的是低电平(若我们嗯下的话),这样就跟我们的先前的高电平的状态反过来了,说明摁扭被嗯下。

    if(!states[index].state && states[index].prev_state){result = 1;}

很好象,反过来当摁扭被抬起来的时候,原先是低电平,现在换成了高电平。但是笔者这里就不写检测了。我们判断好了结果后快速的薄记转移状态。并把检测的结果返回回去。

    states[index].prev_state = states[index].state;return result;
}

补充一个使用TogglePin来完成电平反转的LED操作函数

void led_flip_status(uint8_t index)
{HAL_GPIO_WritePin(LOCKER_GPIO_Port, LOCKER_Pin, GPIO_PIN_SET);HAL_GPIO_TogglePin(GPIOC, GPIO_PIN_8 << (index));HAL_GPIO_WritePin(LOCKER_GPIO_Port, LOCKER_Pin, GPIO_PIN_RESET);   
}

我们来看看我们的主代码

key_init();
...while (1){if(key_scanner(0)){led_flip_status(0); }if(key_scanner(1)){led_flip_status(1); }if(key_scanner(2)){led_flip_status(2); }if(key_scanner(3)){led_flip_status(3); }/* USER CODE END WHILE */}
...

完事,这也就意味着检测到摁扭摁下就反转对应LED的电平。

所有代码的地址都在:MCU_Libs/Competitions at main · Charliechen114514/MCU_Libs (github.com)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/bicheng/69796.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

浅析DNS污染及防范

DNS污染&#xff08;DNS Cache Poisoning&#xff09;是一种网络攻击手段&#xff0c;通过篡改DNS服务器的缓存数据&#xff0c;将域名解析结果指向错误的IP地址&#xff0c;从而误导用户访问恶意网站或无法访问目标网站。这种攻击利用了DNS协议的特性&#xff0c;例如“只认第…

前端学习-事件解绑,mouseover和mouseenter的区别(二十九)

目录 前言 解绑事件 语法 鼠标经过事件的区别 鼠标经过事件 示例代码 两种注册事件的区别 总结 前言 人道洛阳花似锦&#xff0c;偏我来时不逢春 解绑事件 on事件方式&#xff0c;直接使用null覆盖就可以实现事件的解绑 语法 btn.onclick function(){alert(点击了…

AI编程:如何编写提示词

这是小卷对AI编程工具学习的第2篇文章&#xff0c;今天讲讲如何编写AI编程的提示词&#xff0c;并结合实际功能需求案例来进行开发 1.编写提示词的技巧 好的提示词应该是&#xff1a;目标清晰明确&#xff0c;具有针对性&#xff0c;能引导模型理解问题 下面是两条提示词的对…

R 字符串:深入理解与高效应用

R 字符串:深入理解与高效应用 引言 在R语言中,字符串是数据处理和编程中不可或缺的一部分。无论是数据清洗、数据转换还是数据分析,字符串的处理都是基础技能。本文将深入探讨R语言中的字符串概念,包括其基本操作、常见函数以及高效应用方法。 字符串基本概念 字符串定…

处理 .gitignore 未忽略文件夹问题

本地删除缓存 例如 .idea 文件夹被其他同事误提交&#xff0c;那么他本地执行以下代码 git rm -r --cached .idea对应本地再提交即可

linux asio网络编程理论及实现

最近在B站看了恋恋风辰大佬的asio网络编程&#xff0c;质量非常高。在本章中将对ASIO异步网络编程的整体及一些实现细节进行完整的梳理&#xff0c;用于复习与分享。大佬的博客&#xff1a;恋恋风辰官方博客 Preactor/Reactor模式 在网络编程中&#xff0c;通常根据事件处理的触…

【思维导图】并发编程

学习计划&#xff1a;将目前已经学的知识点串成一个思维导图。在往后的学习过程中&#xff0c;不断往思维导图里补充&#xff0c;形成自己整个知识体系。对于思维导图里的每个技术知识&#xff0c;自己用简洁的话概括出来&#xff0c; 训练自己的表达能力。 并发和并行的区别 并…

【B站保姆级视频教程:Jetson配置YOLOv11环境(四)cuda cudnn tensorrt配置】

Jetson配置YOLOv11环境&#xff08;4&#xff09;cuda cudnn tensorrt配置 文章目录 0. 简介1. cuda配置&#xff1a;添加cuda环境变量2. cudnn配置3. TensorRT Python环境配置3.1 系统自带Python环境中的TensorRT配置3.2 Conda 虚拟Python环境中的TensorRT配置 0. 简介 官方镜…

【深度分析】DeepSeek 遭暴力破解,攻击 IP 均来自美国,造成影响有多大?有哪些好的防御措施?

技术铁幕下的暗战&#xff1a;当算力博弈演变为代码战争 一场针对中国AI独角兽的全球首例国家级密码爆破&#xff0c;揭开了数字时代技术博弈的残酷真相。DeepSeek服务器日志中持续跳动的美国IP地址&#xff0c;不仅是网络攻击的地理坐标&#xff0c;更是技术霸权对新兴挑战者的…

DeepSeek R1功能设计涉及的几个关键词

DeepSeek R1作为人工智能助手&#xff0c;其功能设计主要基于以下步骤&#xff1a; 字典过滤与词汇选择 使用蒸馏技术对候选词汇进行筛选和优化&#xff0c;确保选择的词汇与上下文语境相关且准确。候选词汇通过多源数据&#xff08;如公开文档、专家分析等&#xff09;进行训练…

如何在数据湖中有效治理和管理“数据沼泽”问题,提高数据的可发现性和利用率?

在数据湖中有效治理和管理“数据沼泽”问题&#xff0c;提高数据的可发现性和利用率&#xff0c;需要从多个方面入手&#xff0c;包括数据治理、元数据管理、数据质量控制、安全性保障以及生命周期管理等。以下是具体的策略和方法&#xff1a; 1. 构建强大的数据治理框架 数据…

【4Day创客实践入门教程】Day3 实战演练——桌面迷你番茄钟

Day3 实战演练——桌面迷你番茄钟 目录 Day3 实战演练——桌面迷你番茄钟1. 选择、准备元件、收集资料2. 硬件搭建3.编写代码 Day0 创想启程——课程与项目预览Day1 工具箱构建——开发环境的构建Day2 探秘微控制器——单片机与MicroPython初步Day3 实战演练——桌面迷你番茄钟…

Oracle Primavera P6自动进行进度计算

前言 在P6 Professional 有一个自动计划计算的选项&#xff0c;很多人不了解该设置如何使用&#xff0c;以及什么时候该启动这项配置。 详情 P6 Professional 默认为非自动进度计算。启用自动选项后&#xff0c;可以快速查看调度更改的效果。 ​ ​ 如图所示&#xff0c;当你…

DeepSeek-R1 论文解读 —— 强化学习大语言模型新时代来临?

近年来&#xff0c;人工智能&#xff08;AI&#xff09;领域发展迅猛&#xff0c;大语言模型&#xff08;LLMs&#xff09;为通用人工智能&#xff08;AGI&#xff09;的发展开辟了道路。OpenAI 的 o1 模型表现非凡&#xff0c;它引入的创新性推理时缩放技术显著提升了推理能力…

舵机型号与识别

舵机型号繁多&#xff0c;不同品牌和制造商有不同的命名规则。常见的舵机品牌包括 Futaba、Hitec、Tower Pro、Savox、JX Servo 等。以下是舵机型号的常见识别方法以及一些典型的型号示例&#xff1a; 一、舵机型号的识别方法 型号命名规则&#xff1a; 舵机型号通常由字母和数…

6.二分算法

二分 二分算法&#xff0c;也称为二分查找或折半查找&#xff0c;是一种在有序数组中查找特定元素的高效算法。以下是 C 中二分算法的相关内容&#xff1a; 算法原理 二分算法的基本思想是将有序数组分成两部分&#xff0c;然后将目标值与中间元素进行比较。如果目标值等于中…

大模型GUI系列论文阅读 DAY4续:《Large Language Model Agent for Fake News Detection》

摘要 在当前的数字时代&#xff0c;在线平台上虚假信息的迅速传播对社会福祉、公众信任和民主进程构成了重大挑战&#xff0c;并影响着关键决策和公众舆论。为应对这些挑战&#xff0c;自动化假新闻检测机制的需求日益增长。 预训练的大型语言模型&#xff08;LLMs&#xff0…

LevelDB 源码阅读:写入键值的工程实现和优化细节

读、写键值是 KV 数据库中最重要的两个操作&#xff0c;LevelDB 中提供了一个 Put 接口&#xff0c;用于写入键值对。使用方法很简单&#xff1a; leveldb::Status status leveldb::DB::Open(options, "./db", &db); status db->Put(leveldb::WriteOptions…

【Proteus仿真】【51单片机】多功能计算器系统设计

目录 一、主要功能 二、使用步骤 三、硬件资源 四、软件设计 五、实验现象 联系作者 一、主要功能 1、LCD1602液晶显示 2、矩阵按键​ 3、加减乘除&#xff0c;开方运算 4、带符号运算 5、最大 999*999 二、使用步骤 基于51单片机多功能计算器 包含&#xff1a;程序&…

利用Manim库结合`matplotlib`、`numpy`和`scipy`来制作工作流程动画

以下是一个利用Manim库结合matplotlib、numpy和scipy来制作工作流程动画&#xff0c;展示流场速度分布计算模型&#xff0c;以及三流喷嘴的速度场和主要参数分布的可视化图形与动画的示例代码。这个示例将模拟一个简化的三流喷嘴速度场&#xff0c;通过计算速度分布并将其可视化…