梁山派入门指南2——滴答定时器位带操作按键输入

news2024/11/6 21:28:18

梁山派入门指南2——滴答定时器&位带操作&按键输入

  • 1. 滴答定时器
    • 1.1 滴答定时器简介
    • 1.2 相关寄存器
    • 1.3 固件库函数
  • 2. 位带操作
    • 2.1 位带操作介绍
    • 2.2 位带操作的优势
    • 2.3 支持位带操作的内存地址
    • 2.4 位带别名区地址的计算方式
    • 2.5 位带操作使用示例
  • 3 按键输入
    • 3.1 独立按键原理图
    • 3.2 独立按键配置流程
    • 3.3 独立按键初始化
    • 3.4 按键扫描&bsp_key文件
    • 3.5 按键输入demo

1. 滴答定时器

1.1 滴答定时器简介

SysTick 定时器可用作标准的下行计数器,是一个 24 位向下计数器,有自动重新装载能力,可屏蔽系统中断发生器。Cortex-M4 处理器内部包含了一个简单的定时器,所有基于 M4 内核的控制器都带有 SysTick 定时器,这样就方便了程序在不同的器件之间的移植。SysTick 定时器可用于操作系统,提供系统必要的时钟节拍,为操作系统的任务调度提供一个有节奏的“心跳”。正因为所有的 M4内核的芯片都有 Systick 定时器,这在移植的时候不像普通定时器那样难以移植。

GD32中的RCU 通过 AHB 时钟(HCLK)8 分频后作为 Cortex 系统定时器(SysTick)的外部时钟。通过对
SysTick 控制和状态寄存器的设置,可选择上述时钟或 AHB(HCLK)时钟作为 SysTick 时钟。

GD32F4系列的时钟概览:
在这里插入图片描述
我们来看一下滴答定时器相关的部分:
在这里插入图片描述
SysTick 定时器设定初值并使能之后,每经过 1 个系统时钟周期,计数值就减 1,减到 0 时,SysTick计数器自动重新装载初值并继续计数,同时内部的 COUNTFLAG 标志位被置位,触发中断(前提开启中断)。

  • 滴答定时器的时钟频率为30Mhz

1.2 相关寄存器

在这里插入图片描述

1.3 固件库函数

SysTick 定 时 器 的 使 用 主 要 有 SysTick_Config() 函 数 和 systick_clksource_set(uint32_t systick_clksource)函数,SysTick_Config()函数主要用来设置定时时间,systick_clksource_set()函数用来选择 SysTick 时钟源。

  1. systick_config()
/*!
    \brief    配置sysTick的时钟和中断周期
    \param[in]  none
    \param[out] none
    \retval     none
*/
void systick_config(void)
{
    /* 设置sysTick中断的频率为1000Hz,即1ms触发1次 */
    if(SysTick_Config(SystemCoreClock / 1000U)) {
        /* capture error */
        while(1) {
        }
    }
    /* 设置sysTick的优先级为0 */
    NVIC_SetPriority(SysTick_IRQn, 0x00U);
}
  1. SysTick_Handler()
/*!
    \brief    滴答定时器中断服务函数
    \param[in]  none
    \param[out] none
    \retval     none
*/
void SysTick_Handler(void)
{
	/* delay--,delay是需要延时的时间 */
    delay_decrement();
}

/*!
    \brief    delay decrement
    \param[in]  none
    \param[out] none
    \retval     none
*/
void delay_decrement(void)
{
    if(0U != delay) {
        delay--;
    }
}
  1. delay_1ms()
/*!
    \brief    延时一段时间,
    \param[in]  count: count in milliseconds
    \param[out] none
    \retval     none
*/
void delay_1ms(uint32_t count)
{
    delay = count;

    while(0U != delay) {
    }
}
  1. systick_clksource_set()

在这里插入图片描述

以上就是滴答定时器涉及到的几个函数,最主要使用的应该就是,delay_1ms()这个函数,其他的只需要了解即可。。。。

2. 位带操作

2.1 位带操作介绍

为了减少“读-改-写”操作的次数,Cortex ® -M4 处理器提供了一个可以执行单原子比特操作的位带功能。存储器映射包含了两个支持位带操作的区域。其中一个是 SRAM 区的最低 1MB 范围,第二个是片内外设区的最低 1MB 范围。这两个区域中的地址除了普通应用外,还有自己的“位带别名区”。位带别名区把每个比特扩展成一个 32 位的字。当用户访问位带别名区时,就可以达到访问原始比特的目的。总结就是 CPU 不能直接对位带区中的单个数据位位寻址,只能通过对位带别名区的访问(或读/写)实现对位带区单个数据位的访问(或读/写),这种操作被称为位带操作

2.2 位带操作的优势

  • 更高效:避免了 ” 读-改-写 “ 的繁琐步骤
  • 读取更简单:只需要直接读取位带别名区的内容,就可以直接对某个bit位寻址
  • 访问速度更快:位带操作由于是属于硬件完成的,只需要CPU直接对位带别名区进行读写即可。
  • 相对安全:在带有操作系统的开发中,多任务并发运行的时候就有可能在任务切换的过程中发生不可预料的问题,而位带操作由于是属于硬件完成的不可被异常打断的操作(原子操作),相对于读-写-改的操作模式会更安全。
  • 提高运行效率和节省代码空间。简单的程序直接使用库函数或者寄存器操作,对于比较复杂的程序建议尽量使用位带操作来实现

2.3 支持位带操作的内存地址

并不是所有的区域都支持位带操作,下面是两个支持位带操作的区域:

  1. SRAM 区的最低 1MB: 0x2000_0000-0x200F_FFFF(对应位带别名区的地址:0x2200 0000)
    在这里插入图片描述
  2. 片上外设区的最低 1MB:0x4000_0000-0x400F_FFFF(对应位带别名区的地址:0x4200 0000)
    在这里插入图片描述
    在这里插入图片描述](https://i-blog.csdnimg.cn/direct/c58d512bdca64756a4b3efce6c274e7f.png)
![在这里插入图片描述

2.4 位带别名区地址的计算方式

上面介绍了位带操作的地址和优势,那怎么去查找目标比特对应的位带别名区的地址呢?

下面的公式表明了位带别名区中的每个字如何对应位带区的相应比特或目标比特:

				     	 bit_word_addr =bit_band_base +(byte_offset×32)+(bit_number×4)

其中:

  • bit_word_addr指的是位带区目标比特对应在位带别名区的地址
  • bit_band_base指的是位带别名区的起始地址
  • byte_offset指的是位带区目标比特所在的字节的字节地址偏移量
  • bit_number指的是目标比特在对应字节中的位置(0-7)

在这里插入图片描述

2.5 位带操作使用示例

#include "gd32f4xx.h"
#include "systick.h"
#include <stdio.h>
#include "main.h"
#include "bsp_led.h"
#include "sys.h"

#define BIT_ADDR(byte_offset,bit_number) (volatile unsigned long*)(0x42000000 +(byte_offset << 5)+(bit_number << 2))   // 位带别名区的地址
	
#define GPIOD_OCTL_OFFSET ((GPIOD + 0x14) - 0x40000000) 	// 偏移量

#define PDout(n)  *(BIT_ADDR(GPIOD_OCTL_OFFSET,n))   		  // PD口输出

/************************************************
函数名称 : main
功    能 : 主函数
参    数 : 无
返 回 值 : 无
作    者 : LC
*************************************************/
int main(void)
{

    systick_config();    // 滴答定时器初始化
	led_gpio_config();   // led初始化

	// gpio_bit_set(GPIOD,GPIO_PIN_7);     // PD7输出高电平
	// gpio_bit_reset(GPIOD,GPIO_PIN_7);   // PD7输出低电平
 
    while(1) {
			//gpio_bit_write(PORT_LED2,PIN_LED2,SET);     // LED2输出高电平
			PDout(7) = 1;                                 // PD7输出高电平
			delay_1ms(1000);                           		// 延时1s
			//gpio_bit_write(PORT_LED2,PIN_LED2,RESET);   // LED2输出低电平
			PDout(7) = 0;                                 // PD7输出低电平
			delay_1ms(1000);                              // 延时1s
    }
}

3 按键输入

3.1 独立按键原理图

我们打开梁山派提供的原理图,可以看到有三个按键,但是我们能使用的只有唤醒按键:
在这里插入图片描述

在这里插入图片描述

3.2 独立按键配置流程

打开梁山派开发指南,查看GPIO的输入配置流程如下:
在这里插入图片描述

3.3 独立按键初始化

根据配置流程,结合固件库手册中提供的库函数使用说明,开始编写初始化代码:
【注】:GPIO相关的库函数上一章有提到,可以查看:梁山派入门指南1——初窥门径梁山派

/************************************************
函数名称 : key_gpio_config
功    能 : 按键引脚初始化,默认配置为输入模式,下拉
参    数 : 需要初始化的引脚
返 回 值 : 无
作    者 : 不想写代码的我
*************************************************/
void key_gpio_config(void)
{
	/* 开启时钟 */
	rcu_periph_clock_enable(BSP_KEYUP_RCU);
	/* 配置为输入模式,下拉 */
	gpio_mode_set(BSP_KEYUP_PORT,GPIO_MODE_INPUT,GPIO_PUPD_PULLDOWN,BSP_KEYUP_PIN);
}

3.4 按键扫描&bsp_key文件

为了节省篇幅,在这里我直接展示我写好的bsp_key文件,不介绍具体原理

  1. bsp_key.c
#include "bsp_key.h"

/************************************************
函数名称 : key_gpio_config
功    能 : 按键引脚初始化,默认配置为输入模式,下拉
参    数 : 需要初始化的引脚
返 回 值 : 无
作    者 : 不想写代码的我
*************************************************/
void key_gpio_config(void)
{
	/* 开启时钟 */
	rcu_periph_clock_enable(BSP_KEYUP_RCU);
	/* 配置为输入模式,下拉 */
	gpio_mode_set(BSP_KEYUP_PORT,GPIO_MODE_INPUT,GPIO_PUPD_PULLDOWN,BSP_KEYUP_PIN);
}


/************************************************
函数名称 : WKUP_Down_Task
功    能 : KEY_UP按键按下时调用的函数
参    数 : 需要初始化的引脚
返 回 值 : 无
作    者 : 不想写代码的我
*************************************************/
void WKUP_Down_Task(void)
{
   gpio_bit_toggle(PORT_LED2,PIN_LED2);
}

/************************************************
函数名称 : WWKUP_Up_Task
功    能 : KEY_UP按键弹起时调用的函数
参    数 : 需要初始化的引脚
返 回 值 : 无
作    者 : 不想写代码的我
*************************************************/
void WWKUP_Up_Task(void)
{
   
}

/************************************************
函数名称 : Key_One_Scan
功    能 : 按键扫描函数,。这里只能扫描KEY_UP按键
参    数 : 需要初始化的引脚
返 回 值 : 无
作    者 : 不想写代码的我
*************************************************/
void Key_One_Scan(uint8_t KeyName ,void(*OnKeyOneUp)(void), void(*OnKeyOneDown)(void))
{
   static uint8_t Key_Val[Key_Name_Max];    //按键值的存放位置
   static uint8_t Key_Flag[Key_Name_Max];   //KEY0~2为0时表示按下,为1表示松开,WKUP反之
    
   Key_Val[KeyName] = Key_Val[KeyName] <<1;  //每次扫描完,将上一次扫描的结果左移保存
   
    switch(KeyName)
    {
        case Key_Name_WKUP:  Key_Val[KeyName] = Key_Val[KeyName] | (gpio_input_bit_get(BSP_KEYUP_PORT,BSP_KEYUP_PIN));   //读取WKUP按键值
            break; 
        default:
            break;
    }
    if(KeyName == Key_Name_WKUP)     //WKUP的电路图与其他按键不同,所以需要特殊处理
    {
        //WKUP特殊情况
        //当按键标志为1(松开)是,判断是否按下,WKUP按下时为0xff
        if(Key_Val[KeyName] == 0xff && Key_Flag[KeyName] == 1)
        {
            (*OnKeyOneDown)();
           Key_Flag[KeyName] = 0;
        }
        //当按键标志位为0(按下),判断按键是否松开,WKUP松开时为0x00
        if(Key_Val[KeyName] == 0x00 && Key_Flag[KeyName] == 0)
        {
            (*OnKeyOneUp)();
           Key_Flag[KeyName] = 1;
        } 
    }
   
}
  1. bsp_key.h
#ifndef _BSP_KEY_H
#define _BSP_KEY_H

#include "gd32f4xx.h"
#include "systick.h"
#include "bsp_led.h"

#define BSP_KEYUP_RCU   RCU_GPIOA   // 按键端口时钟
#define BSP_KEYUP_PORT  GPIOA       // 按键端口
#define BSP_KEYUP_PIN   GPIO_PIN_0  // 按键引脚

typedef enum
{
 Key_Name_WKUP = 0,
 Key_Name_Max
}EnumKeyOneName;

/* 按键引脚初始化,默认配置为输入模式,下拉 */
void key_gpio_config(void); 	  

 

/*  KEY_UP按键按下按下和弹起时调用的函数*/
void WKUP_Down_Task(void);

void WWKUP_Up_Task(void);

/* 按键扫描函数 */
void Key_One_Scan(uint8_t KeyName ,void(*OnKeyOneUp)(void), void(*OnKeyOneDown)(void)); 

#endif /* _BSP_KEY_H */

3.5 按键输入demo

结合上面的bsp_key文件,就可以编写出一个按键扫描的demo,代码如下:

#include "gd32f4xx.h"
#include "systick.h"
#include <stdio.h>
#include "main.h"

#include "bsp_led.h"
#include "bsp_key.h"

/*!
    \brief    main function
    \param[in]  none
    \param[out] none
    \retval     none
*/
int main(void)
{
	uint16_t i=0;
	
	/* 配置sysTick的时钟和中断周期*/
    systick_config();
	
	/* led初始化 */
	led_gpio_config(Led_Name_Led1);
	led_gpio_config(Led_Name_Led2);
	key_gpio_config();

    while(1) 
	{
		if(++i >= 50)
		{
			/* 500ms翻转一次led的状态 */
			gpio_bit_toggle(PORT_LED1,PIN_LED1);
			i = 0;
		}
		
		Key_One_Scan(Key_Name_WKUP,WWKUP_Up_Task,WKUP_Down_Task);
		delay_1ms(10);

    }
}

【注】:这里的按键输入是采用GPIO输入的方式实现的,后续中断中还会对bsp_key文件进行进一步封装。
关于按键扫描的原理,可以参照:夜深人静学32系列9——GPIO驱动数码管/蜂鸣器/按键/LED 和 STM32框架之按键扫描新思路

以上就是本期的所有内容,创造不易,点个关注再走呗。

在这里插入图片描述

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

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

相关文章

RHCE--4

第四章 web服务器 web服务器简介 www可以结合文字、图形、影像以及声音等多媒体&#xff0c;并通过可以让鼠标单击超链接的方式将信息以Internet传递到世界各处去。 www所用的协议&#xff1a;Hyper Text Transport Protocol&#xff0c;HTTP&#xff0c;超文本传输协议。 ww…

C++ | Leetcode C++题解之第515题在每个树行中找最大值

题目&#xff1a; 题解&#xff1a; class Solution { public:vector<int> largestValues(TreeNode* root) {if (!root) {return {};}vector<int> res;queue<TreeNode*> q;q.push(root);while (!q.empty()) {int len q.size();int maxVal INT_MIN;while (…

如何安装和使用PowerDesigner

教程目录 一、安装二、使用 一、安装 1、启动安装程序。 2、Trial&#xff0c;然后Next。 3、选PRC&#xff0c;同意协议&#xff0c;Next。 4、设置安装路径&#xff0c;Next。 5、Next。 6、全选&#xff0c;Next。 7、Next。 8、Next。 9、等待安装。 10、…

人工智能生产力悖论:为什么大多数人没用上AI工具?

真正的障碍不是技术&#xff0c;而是思考的时间: 虽然像ChatGPT这样的工具确实有潜力带来变革&#xff0c;但我接触的大多数人几乎不用它。就算用的&#xff0c;也只是用来做些总结之类的小活儿。而在这些用户里&#xff0c;只有大约5%的人订阅了高级版&#xff0c;这意味着真…

JS中DOM和BOM

DOM DOM&#xff08;文档对象模型&#xff09;是一个跨平台和语言独立的接口&#xff0c;它允许程序和脚本动态地访问和更新文档的内容、结构和样式。在网页浏览器中&#xff0c;DOM 通常表示 HTML 或 XML 文档的对象模型。DOM 将网页内容视为节点树&#xff0c;其中每个节点都…

C语言初阶:十一.代码调试技巧

❤欢迎各位大佬访问&#xff1a;折枝寄北-CSDN博客折枝寄北擅长C语言初阶,等方面的知识,折枝寄北关注python,c,java,qt,c语言领域.https://blog.csdn.net/2303_80170533?typeblog❤文章所属专栏https://blog.csdn.net/2303_80170533/category_12794764.html?spm1001.2014.300…

Linux265资源分享网——Linux命令大全在线网址

Linux命令大全在线网址 https://linux265.com/

Chrome浏览器控制台替换接口返回内容

1.打开Chrome DevTools&#xff1a; 使用快捷键F12或右键点击页面并选择“检查”来打开Chrome DevTools。 2.导航到Network面板&#xff1a; 在DevTools的顶部菜单中&#xff0c;选择“Network”&#xff08;网络&#xff09;面板以监视网络请求。 3.发起目标请求&#xff1a; …

HarmonyOS开发5.0 net 启动界面设置

第一步、创建我们界面 第二步&#xff0c; 在EntryAbility中配置启动页面&#xff0c;在entry/src/main/ets/entryability/EntryAbility.ets中配置启动页面 配置如下 至此大功告成

ssm012医院住院管理系统(论文+源码)_kaic

摘 要 随着时代的发展&#xff0c;医疗设备愈来愈完善&#xff0c;医院也变成人们生活中必不可少的场所。如今&#xff0c;已经2021年了&#xff0c;虽然医院的数量和设备愈加完善&#xff0c;但是老龄人口也越来越多。在如此大的人口压力下&#xff0c;医院住院就变成了一个问…

UML图之对象图详解

~犬&#x1f4f0;余~ “我欲贱而贵&#xff0c;愚而智&#xff0c;贫而富&#xff0c;可乎&#xff1f; 曰&#xff1a;其唯学乎” 零、什么是对象图 对象图&#xff08;Object Diagram&#xff09;是UML中一种重要的静态结构图&#xff0c;它用于表示在特定时间点上系统中的对…

STM32之外部中断(实验对射式传感器计次实验)

外部中断配置 #include "stm32f10x.h" // Device headeruint16_t CountSensor_Count;void CountSensor_Init(void) {//RCC--> GPIO--> AFIO--> EXTI--> NVIC五步RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE); //开启GPIOB时…

MFC的SendMessage与PostMessage的区别

一、SendMessage 同步操作&#xff1a; SendMessage 是一个同步函数&#xff0c;它会将消息发送到指定的窗口&#xff0c;并等待该窗口的消息处理过程完成&#xff0c;然后返回。这意味着它会阻塞当前线程&#xff0c;直到消息处理完成。 直接调用&#xff1a; SendMessage 会…

update-alternatives(选择工具)

0 Preface/foreword 1 update-alternatives介绍 1.1 选项和用法 1.2 install用法 update-alternatives --install <link> <name> <path> <priority> [--slave <link> <name> <path>] link&#xff1a;符号链接&#xff08;软链…

PostgreSQL(WINDOWS)下载、安装、简单使用

下载 PostgreSQL: Downloads PostgreSQL: Windows installers EDB: Open-Source, Enterprise Postgres Database Management 安装 注意密码要方便自己使用&#xff0c;不能忘记。 打开pgAdmin&#xff0c;输入密码 新建数据库 打开命令工具 新建表

疯狂讲义———第3章 数据类型与运算符

目录 一.注释的重要性 1.注释 2.Java语言的注释类型 3.文档注释 二.javadoc命令的用法 三.数据类型的两大类 四.8种基本类型及各自的注意点 1.整型 2.字符型 3.浮点型 4.布尔型 五.自动类型转换和强制类型转换 六.表达式类型的自动提升 七.Java提供的基本运算符 八.运算符的结…

【算法】递归系列:206.反转链表(两种递归实现)

目录 1、题目链接 2、题目介绍 3、解法 递归法&#xff08;从前往后递归&#xff09; 从后往前递归 4、代码 递归法&#xff08;从前往后递归&#xff09; 从后往前递归 1、题目链接 206.反转链表 2、题目介绍 3、解法 递归法&#xff08;从前往后递归&#xff09; 递归…

编程之路:蓝桥杯备赛指南

文章目录 一、蓝桥杯的起源与发展二、比赛的目的与意义三、比赛内容与形式四、比赛前的准备五、获奖与激励六、蓝桥杯的影响力七、蓝桥杯比赛注意事项详解使用Dev-C的注意事项 一、蓝桥杯的起源与发展 蓝桥杯全国软件和信息技术专业人才大赛&#xff0c;简称蓝桥杯&#xff0c…

uni-app 运行HarmonyOS项目

1. uni-app 运行HarmonyOS项目 文档中心 1.1. HarmonyOS端 1.1.1. 准备工作 &#xff08;1&#xff09;下载DevEco Studio开发工具。   &#xff08;2&#xff09;在 DevEco Studio 中打开任意一个项目&#xff08;也可以新建一个空项目&#xff09;。   &#xff08;3&…

camera和lidar外参标定

雷达和相机的外参标定&#xff08;外部参数标定&#xff09;指的是确定两者之间的旋转和平移关系&#xff0c;使得它们的坐标系可以对齐。 文章目录 无目标标定livox_camera_calibdirect_visual_lidar_calibration 有目标标定velo2cam_calibration 无目标标定 livox_camera_ca…