[江科大编程技巧] 第1期 定时器实现非阻塞式程序 按键控制LED闪烁模式——笔记

news2024/12/29 8:49:30

提前声明——我只是写的详细其实非常简单,不要看着多就放弃学习!

阻塞:执行某段程序时,CPU因为需要等待延时或者等待某个信号而被迫处于暂停状态一段时间,程序执行时间较长或者时间不定

非阻塞:执行某段程序时,CPU不会等待,程序很快执行结束

常规delay方法按键控制LED

uint8_t Key_GetNum(void)
{
	uint8_t KeyNum = 0;
	if (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_1) == 0)
	{
		Delay_ms(20);
		while (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_1) == 0);
		Delay_ms(20);
		KeyNum = 1;
	}
	if (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_11) == 0)
	{
		Delay_ms(20);
		while (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_11) == 0);
		Delay_ms(20);
		KeyNum = 2;
	}
	
	return KeyNum;
}

这种方法通过外部中断来实现。会响应阻塞
mian()里面本想通过按键按下控制LED的慢闪和熄灭。但是因为Delay_ms(500);所以熄灭时会很不灵敏,得长按才能熄灭。并且在LED亮时i这个变量要1ms才增加1

#include "stm32f10x.h"                  // Device header
#include "Delay.h"
#include "OLED.h"
#include "kEY.h"
#include "LED.h"

uint8_t key1_flag = 0 ;
uint8_t keyNum ;
uint8_t i;
int main(void)
{
	OLED_Init();
	Key_Init();
	LED_Init();
	while (1)
	{
		
		keyNum = Key_GetNum();
		if(keyNum == 1)
		{
			key1_flag = !key1_flag;
		}
		if(key1_flag)
		{
			LED1_ON();
			Delay_ms(500);
			LED1_OFF();
			Delay_ms(500);			
		}
		else
		{
			LED1_OFF();			
		}
		
		OLED_ShowNum(2,2,i++,5);
	}
}

这种程序不仅仅效果非常差,而且很占CPU

为了让主程序不被阻塞,也就是主程序可以快速刷新,但是按键消抖和LED闪烁是很常见的,就必须要让我们的程序有类似于多线程的操作了,单片机最长用的多线程是定时器定时中断

我们有两个Delay 1:按键消抖Delay_ms(20);LED闪烁Delay_ms(500);

一.定时器中断解决按键消抖Delay的问题,

解决办法就是定时器扫描按键,不要用外部中断检测

先归纳:

1.在Key.c写获取按键状态函数:PB1按下返回1,PB11按下返回2,没有按键按返回0(目的获取状态)

2.key.C建立一个key_Tick(),然后在主函数void TIM2_IRQHandler(void)调用:每隔20ms读取一次本次键码值和上次键码值,判断,如果本次是0,上次非0,则表示按键按下且当前处于刚松手的状态 置键码标志位,

3写按键返回函数()向主程序报告此事件

4.主函数根据什么按键按下对应执行操作

根据上述思路

我们第一步:在Key.c写获取按键状态函数

PB1按下返回1,PB11按下返回2,没有按键按返回0

/**
  * @brief  获取按键状态
  * @param  无
  * @retval 状态返回值
  */
uint8_t Key_Getstate(void)
{
	if (GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_1) == 0)
	{
		return 1;
	}
	if (GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_1) == 0)
	{
		return 2;
	}
	return 0;
}

第二步:写定时中断函数()

定时中断函数如果写在主函数里,不利于外设模块化编程,如果写在key.C里其他模块不好用

key.C建立一个key_Tick(),然后在主函数调用即可

1.每隔20ms读取一次本次键码值和上次键码值

2.判断,如果本次是0,上次非0,则表示按键按下且当前处于刚松手的状态 置键码标志位,

/**
  * @brief  定时器按键检测
  * @param  key_count:记20ms;;
  * @param  prevstate:上次状态;
  * @param  currstate:本次状态;
  * @retval 状态返回值
  */
void Key_Tick(void)
{
	static uint8_t key_count =0;
	static uint8_t prevstate ,currstate;
	key_count++;
	/*20ms检测*/
	if (key_count >= 20)
	{
		key_count = 0;
		/*上一次的本次就是上一次*/
		prevstate = currstate;			//上次
		currstate = Key_Getstate();		//本次
		
		/*得到本次和上一次的状态后判断*/
		if (currstate == 0 && prevstate !=0) //满足就代表按下了 prevstate按键
		{
			key_num = prevstate;			//那么返回按键
		}
	}
}

为了返回Key_num

第三步:写按键返回函数()向主程序报告此事件

/**
  * @brief  按键返回
  * @param  无
  * @retval KeyNum:按键值
  */
uint8_t Key_GetNum(void)
{
	uint8_t temp;
	if(key_num)
	{
		temp= key_num;
		key_num = 0;
		return temp;
	}	
	return 0;
}

于是我们在主函数里调用

其实就添加了void TIM2_IRQHandler(void),其他都是一样的,我们只是把按键检测这个事情从外部中断检测改为了定时器定时检测

#include "stm32f10x.h"                  // Device header
#include "Delay.h"
#include "OLED.h"
#include "kEY.h"
#include "LED.h"
#include "timer.h"

uint8_t key1_flag = 0 ;
uint8_t keyNum ;
uint8_t i;
int main(void)
{
	OLED_Init();
	Key_Init();
	LED_Init();
	Timer_Init();
	
	while (1)
	{
		
		keyNum = Key_GetNum();
		if(keyNum == 1)
		{
			key1_flag = !key1_flag;
		}
		if(key1_flag)
		{
			LED1_ON();
			Delay_ms(500);
			LED1_OFF();
			Delay_ms(500);			
		}
		else
		{
			LED1_OFF();			
		}
		
		OLED_ShowNum(2,2,i++,5);
	}
}


void TIM2_IRQHandler(void)
{
	if (TIM_GetITStatus(TIM2, TIM_IT_Update) == SET)
	{
		key_Tick();
		TIM_ClearITPendingBit(TIM2, TIM_IT_Update);
	}
}

到这里我们已经解决了,按键阻塞问题,实现定时器扫描按键的任务就完成了。

二.定时器中断解决LED闪烁Delay_ms(500)的问题

第一步:写定时中断函数()

这里也是一样的定时中断函数如果写在主函数里,不利于外设模块化编程,如果写在led.c里其他模块不好用

故我们在led.C建立一个led_Tick(),然后在主函数调用即可

控制灯光模式函数,通过主函数输入参数决定

/**
  * @brief  控制灯光模式
  * @param  Mode:0->灭,1->亮;
  * @retval 无
  */
void led_SetMOde(uint8_t mode)
{
	led1_Mode = mode;
}	

 LED定时器中断函数led_Tick()

/**
  * @brief  定时器按键检测
  * @param  led1_Mode:0->灭,1->亮;
  * @retval 状态返回值
  */
void led_Tick(void)
{
	if(led1_Mode == 0)		//控制模式0,熄灭
	{
		LED1_OFF();
	}
	else if (led1_Mode == 1) //控制模式1,慢闪
	{
		led1_count++;
		led1_count %= 1000; //1000ms周期
		
		if(led1_count<500)  //亮500ms
		{
			LED1_ON();
		}
		else				//灭500ms
		{
			LED1_OFF();
		}	
	}

}

主函数

#include "stm32f10x.h"                  // Device header
#include "Delay.h"
#include "OLED.h"
#include "kEY.h"
#include "LED.h"
#include "timer.h"

uint8_t key1_flag = 0 ;
uint8_t keyNum ;
uint8_t i;
int main(void)
{
	OLED_Init();
	Key_Init();
	LED_Init();
	Timer_Init();
	
	while (1)
	{
		
		keyNum = Key_GetNum();
		if(keyNum == 1)
		{
			key1_flag = !key1_flag;
		}
		if(key1_flag)
		{
			led_SetMOde(1);
		}
		else
		{
			led_SetMOde(0);			
		}
		
		OLED_ShowNum(2,2,i++,5);
	}
}


void TIM2_IRQHandler(void)
{
	if (TIM_GetITStatus(TIM2, TIM_IT_Update) == SET)
	{
		key_Tick();
		led_Tick();
		TIM_ClearITPendingBit(TIM2, TIM_IT_Update);
	}
}

到这里两个阻塞我们都已经解决了,按键已经可以非常完美的控制灯光了,我们再给他增加一点功能吧

void LED1_SetMode(uint8_t Mode)
{
	if (Mode != LED1_Mode)
	{
		LED1_Mode = Mode;
		LED1_Count = 0;
	}
}

void LED2_SetMode(uint8_t Mode)
{
	if (Mode != LED2_Mode)
	{
		LED2_Mode = Mode;
		LED2_Count = 0;
	}
}

 

void LED_Tick(void)
{
	if (LED1_Mode == 0)
	{
		LED1_OFF();
	}
	else if (LED1_Mode == 1)
	{
		LED1_ON();
	}
	else if (LED1_Mode == 2)
	{
		LED1_Count ++;
		LED1_Count %= 1000;
		
		if (LED1_Count < 500)
		{
			LED1_ON();
		}
		else
		{
			LED1_OFF();
		}
	}
	else if (LED1_Mode == 3)
	{
		LED1_Count ++;
		LED1_Count %= 100;
		
		if (LED1_Count < 50)
		{
			LED1_ON();
		}
		else
		{
			LED1_OFF();
		}
	}
	else if (LED1_Mode == 4)
	{
		LED1_Count ++;
		LED1_Count %= 1000;
		
		if (LED1_Count < 100)
		{
			LED1_ON();
		}
		else
		{
			LED1_OFF();
		}
	}
	
	
	if (LED2_Mode == 0)
	{
		LED2_OFF();
	}
	else if (LED2_Mode == 1)
	{
		LED2_ON();
	}
	else if (LED2_Mode == 2)
	{
		LED2_Count ++;
		LED2_Count %= 1000;
		
		if (LED2_Count < 500)
		{
			LED2_ON();
		}
		else
		{
			LED2_OFF();
		}
	}
	else if (LED2_Mode == 3)
	{
		LED2_Count ++;
		LED2_Count %= 100;
		
		if (LED2_Count < 50)
		{
			LED2_ON();
		}
		else
		{
			LED2_OFF();
		}
	}
	else if (LED2_Mode == 4)
	{
		LED2_Count ++;
		LED2_Count %= 1000;
		
		if (LED2_Count < 100)
		{
			LED2_ON();
		}
		else
		{
			LED2_OFF();
		}
	}
}

因为按键有五种状态了用flag不太好,我们修改为mode

#include "stm32f10x.h"                  // Device header
#include "Delay.h"
#include "OLED.h"
#include "LED.h"
#include "Key.h"
#include "Timer.h"

uint8_t KeyNum;
uint8_t LED1Mode;
uint8_t LED2Mode;

uint16_t i;

int main(void)
{
	OLED_Init();
	LED_Init();
	Key_Init();
	Timer_Init();
	
	OLED_ShowString(1, 1, "i:");
	OLED_ShowString(2, 1, "LED1Mode:");
	OLED_ShowString(3, 1, "LED2Mode:");
	
	while (1)
	{
		KeyNum = Key_GetNum();
		
		if (KeyNum == 1)
		{
			LED1Mode ++;
			LED1Mode %= 5;
			LED1_SetMode(LED1Mode);
		}
		if (KeyNum == 2)
		{
			LED2Mode ++;
			LED2Mode %= 5;
			LED2_SetMode(LED2Mode);
		}
		
		OLED_ShowNum(1, 3, i ++, 5);
		OLED_ShowNum(2, 10, LED1Mode, 1);
		OLED_ShowNum(3, 10, LED2Mode, 1);
	}
}

void TIM2_IRQHandler(void)
{
	if (TIM_GetITStatus(TIM2, TIM_IT_Update) == SET)
	{
		Key_Tick();
		LED_Tick();
		TIM_ClearITPendingBit(TIM2, TIM_IT_Update);
	}
}

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

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

相关文章

如何理解:产品线经营管理的战略、组织、业务、项目、流程、绩效之间的逻辑关系?-中小企实战运营和营销工作室博客

如何理解&#xff1a;产品线经营管理的战略、组织、业务、项目、流程、绩效之间的逻辑关系&#xff1f;-中小企实战运营和营销工作室博客 产品线经营管理中&#xff0c;战略、组织、业务、项目、流程、绩效之间存在着紧密的逻辑关系&#xff0c;它们相互影响、相互作用&#xf…

【CSS in Depth 2 精译_096】16.4:CSS 中的三维变换 + 16.5:本章小结

当前内容所在位置&#xff08;可进入专栏查看其他译好的章节内容&#xff09; 第五部分 添加动效 ✔️【第 16 章 变换】 ✔️ 16.1 旋转、平移、缩放与倾斜 16.1.1 变换原点的更改16.1.2 多重变换的设置16.1.3 单个变换属性的设置 16.2 变换在动效中的应用 16.2.1 放大图标&am…

Oracle 11G还有新BUG?ORACLE 表空间迷案!

前段时间遇到一个奇葩的问题&#xff0c;在开了SR和oracle support追踪两周以后才算是有了不算完美的结果&#xff0c;在这里整理出来给大家分享。 1.问题描述 12/13我司某基地MES全厂停线&#xff0c;系统卡死不可用&#xff0c;通知到我排查&#xff0c;查看alert log看到是…

三只脚的电感是什么东西?

最近在做加湿器&#xff0c;把水雾化的陶瓷片需要有专门的驱动电路。 我参考了某宝卖家的驱动板以及网上的开源项目&#xff0c;发现了驱动电路的核心就是一个三脚电感。 在此之前我都没注意过这玩意&#xff0c;三脚电感不也还是电感嘛&#xff1f; 今天我们就来看看三脚电…

pyQT + OpenCV相关练习

一、设计思路 1、思路分析与设计 本段代码是一个使用 PyQt6 和 OpenCV 创建的图像处理应用程序。其主要功能是通过一个图形界面让用户对图片进行基本的图像处理操作&#xff0c;如灰度化、翻转、旋转、亮度与对比度调整&#xff0c;以及一些滤镜效果&#xff08;模糊、锐化、边…

【Git_bugs】remote error GH013 Repository rule violations found for.md

背景 1 在一个分支上的提交顺序如下&#xff1a;-> 代表新的提交 在提交 E 中&#xff0c;文件包含了 GitHub 生成的 token提交 F 是一次普通的提交&#xff0c;不包含 token A -> ... -> E -> F (敏感信息在 E 中)附&#xff1a;给提交起名是为了方便说明问题。…

Day1 微服务 单体架构、微服务架构、微服务拆分、服务远程调用、服务注册和发现Nacos、OpenFeign

目录 1.导入单体架构项目 1.1 安装mysql 1.2 后端 1.3 前端 2.微服务 2.1 单体架构 2.2 微服务 2.3 SpringCloud 3.微服务拆分 3.1 服务拆分原则 3.1.1 什么时候拆 3.1.2 怎么拆 3.2 拆分购物车、商品服务 3.2.1 商品服务 3.2.2 购物车服务 3.3 服务调用 3.3.1 RestTemplate 3.…

安卓执法仪Android接入国标GB28181平台实现实时监控、对讲、报警、定位等管理方案

最近协助不少企业完成了4G无线设备国标接入的需求&#xff0c;尤其是国产芯片的接入&#xff0c;国标发展了十年的时间&#xff0c;目前协议从完成度、性能、AI等各个方面&#xff0c;都已经非常完美地满足各种各样的场景需求&#xff0c;尤其是GB28181-2022的推出&#xff0c;…

SpringMVC学习(二)——RESTful API、拦截器、异常处理、数据类型转换

一、RESTful (一)RESTful概述 RESTful是一种软件架构风格&#xff0c;用于设计网络应用程序。REST是“Representational State Transfer”的缩写&#xff0c;中文意思是“表现层状态转移”。它基于客户端-服务器模型和无状态操作&#xff0c;以及使用HTTP请求来处理数据。RES…

国内独立开发者案例及免费送独立开发蓝图书

独立开发者在国内越来越受到关注&#xff0c;他们追求的是一种自由且自给自足的工作状态。 送这个&#xff1a; 少楠light&#xff08;Flomo、小报童、如果相机&#xff09;&#xff1a;他们是独立开发者的典范&#xff0c;不仅开发了多款产品&#xff0c;还坚信“剩者为王”…

【JavaEE进阶】@RequestMapping注解

目录 &#x1f4d5;前言 &#x1f334;项目准备 &#x1f332;建立连接 &#x1f6a9;RequestMapping注解 &#x1f6a9;RequestMapping 注解介绍 &#x1f384;RequestMapping是GET还是POST请求&#xff1f; &#x1f6a9;通过Fiddler查看 &#x1f6a9;Postman查看 …

一文详解MacOS+CLion——构建libtorch机器学习开发环境

对于希望在本地环境中进行深度学习开发的开发者来说&#xff0c;配置合适的工具链是至关重要的一步。本文旨在帮助您在 macOS 操作系统上&#xff0c;利用 CLion IDE 和 PyTorch 的 C依赖库——libtorch&#xff0c;快速搭建起一个高效的开发环境。这里我们将一步步地讲解如何下…

Bert中文文本分类

这是一个经典的文本分类问题&#xff0c;使用google的预训练模型BERT中文版bert-base-chinese来做中文文本分类。可以先在Huggingface上下载预训练模型备用。https://huggingface.co/google-bert/bert-base-chinese/tree/main 我使用的训练环境是 pip install torch2.0.0; pi…

shardingsphere分库分表项目实践5-自己用java写一个sql解析器+完整项目源码

前1节我们介绍了 shardingsphere 分表分库的sql解析与重写&#xff1a; shardingsphere分库分表项目实践4-sql解析&重写-CSDN博客 那么shardingsphere sql 解析底层究竟是怎么实现的呢&#xff0c;其实它直接用了著名的开源软件 antlr . antlr 介绍&#xff1a; ANTLR&a…

10分钟掌握项目管理核心工具:WBS、甘特图、关键路径法全解析

一、引言 在项目管理的广阔天地里&#xff0c;犹如一场精心编排的交响乐演奏&#xff0c;每个乐器、每个音符都需精准配合才能奏响美妙乐章。而 WBS&#xff08;工作分解结构&#xff09;、甘特图、关键路径法无疑是这场交响乐中的关键乐章&#xff0c;它们从不同维度为项目管…

【LLM】OpenAI 的DAY12汇总和o3介绍

note o3 体现出的编程和数学能力&#xff0c;不仅达到了 AGI 的门槛&#xff0c;甚至摸到了 ASI&#xff08;超级人工智能&#xff09;的边。 Day 1&#xff1a;o1完全版&#xff0c;开场即巅峰 12天发布会的开场即是“炸场级”更新——o1完全版。相比此前的预览版本&#x…

使用Kubernetes部署MySQL+WordPress

目录 前提条件 部署MySQL和WordPress 编写yaml文件 应用yaml文件 存在问题及解决方案 创建PV(持久化卷) 创建一个PVC(持久化卷声明) 部署添加PVC 查看PV对应的主机存储 删除资源 查看资源 删除deployment和service 查看主机数据 删除PVC和PV 删除主机数据 前提条…

RabbitMQ中的异步Confirm模式:提升消息可靠性的利器

在现代分布式系统中&#xff0c;消息队列&#xff08;Message Queue&#xff09;扮演着至关重要的角色&#xff0c;它能够解耦系统组件、提高系统的可扩展性和可靠性。RabbitMQ作为一款广泛使用的消息队列中间件&#xff0c;提供了多种机制来确保消息的可靠传递。其中&#xff…

sentinel限流+其他

quick-start | Sentinel sentinel 作用 限流 熔断降级 1&#xff0c;限制什么 QPS 并发线程数 2&#xff0c;限制什么 资源&#xff0c;什么资源 服务&#xff0c;方法&#xff0c;接口&#xff0c;或者一段代码 3&#xff0c;实现方式 配置规则 注解 其他 Java常见5种限流…

Ubuntu 中安装 RabbitMQ 教程

简介 RabbitMq作为一款消息队列产品&#xff0c;它由Erlang语言开发&#xff0c;实现AMQP&#xff08;高级消息队列协议&#xff09;的开源消息中间件。 应用场景 异步处理 场景说明&#xff1a;用户注册后&#xff0c;注册信息写入数据库&#xff0c;再发邮件、短信通知。 …