蓝桥杯嵌入式赛道备考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;例如“只认第…

AI编程:如何编写提示词

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

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;更是技术霸权对新兴挑战者的…

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

在数据湖中有效治理和管理“数据沼泽”问题&#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;它引入的创新性推理时缩放技术显著提升了推理能力…

大模型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;程序&…

origin调整图像的坐标轴,修改坐标轴起始点,增量

接上一篇帖子&#xff0c;如果再修改数据之后或者当前的数据之间差距较小&#xff0c;怎么通过调整坐标轴来使数据之间的差距更明显&#xff0c;举个例子&#xff0c; 像下面这个图的entropy指标&#xff0c;都是介于6到9之间&#xff0c;如果y轴坐标都从0开始&#xff0c;使用…

Redis_Redission的入门案例、多主案例搭建、分布式锁进行加锁、解锁底层源码解析

目录 ①. Redis为什么选择单线程&#xff1f; ②. 既然单线程这么好,为什么逐渐又加入了多线程特性&#xff1f; ③. redis6的多线程和IO多路复用入门篇 ④. Redis6.0默认是否开启了多线程&#xff1f; ⑤. REDIS多线程引入总结 ①. Redis为什么选择单线程&#xff1f; ①…

ARM嵌入式学习--第十一天(中断处理 , ADC)

--中断的概念 中断是指计算机运行过程中&#xff0c;出现某些意外情况需主机干预时&#xff0c;机器能自动停止正在运行的程序并转入处理新情况的程序&#xff0c;处理完毕后又返回被暂停的程序继续运行 --CPU处理事情的方式 -轮询方式 不断查询是否有事情需要处理&#xff0c…

vue2项目(一)

项目介绍 电商前台项目 技术架构&#xff1a;vuewebpackvuexvue-routeraxiosless.. 封装通用组件登录注册token购物车支付项目性能优化 一、项目初始化 使用vue create projrct_vue2在命令行窗口创建项目 1.1、脚手架目录介绍 ├── node_modules:放置项目的依赖 ├──…

[ACTF2020 新生赛]BackupFile1

题目 翻译&#xff0c;尝试找出源文件&#xff01; 扫目录使用参数-e * python dirsearch.py -u http://0c3b21c0-d360-4baa-8b97-aa244f4c4825.node5.buuoj.cn:81/ -e * 最终扫描到一个文件名为&#xff1a;/index.php.bak的文件&#xff0c;把备份文件下载下来 源码 <?…

OPENPPP2 —— VMUX_NET 多路复用原理剖析

在阅读本文之前&#xff0c;必先了解以下几个概念&#xff1a; 1、MUX&#xff08;Multiplexer&#xff09;&#xff1a;合并多个信号到单一通道。 2、DEMUX&#xff08;Demultiplexer&#xff09;&#xff1a;从单一通道分离出多个信号。 3、单一通道&#xff0c;可汇聚多个…

DeepSeek-R1大模型本地部署及简单测试

目录 DeepSeek-R1大模型本地部署及简单测试背景我的测试环境模型参数选择适用场景参数规模 本地部署安装 DeepSeek-R1大模型本地部署及简单测试 背景 最近deepseek非常火, 要说2025年震惊科技圈的事件要数DeepSeek这个国产AI的横空出世&#xff0c;这是一款免费、开源且隐私优…