使用HAL库开发STM32:系统时间基础及进阶使用

2023-10-30

目的

HAL库默认提供了系统时间,系统时间默认情况下由SysTick定时器计数产生。系统时间一方面用于HAL库自身调用,另一方面用户也可以使用,为开发带来便利。(本文提到的相关使用主要应用于未使用OS(操作系统)的情况下。)

基础使用

一般的系统时间使用方面常用到两个函数:

  • __weak uint32_t HAL_GetTick(void)
    返回从系统运行开始经过的时间,默认情况下单位为ms;
  • __weak void HAL_Delay(uint32_t Delay)
    延时,该延时是阻塞的,默认情况下延时单位为ms,该函数不能在等于或高于系统时钟源优先级(默认情况下为0)的中断程序中使用,不然程序就阻塞在这里不动了;

对于上面两个函数本身来说没什么特别可以多说的,需要注意的点也在上面说明了。上面的延时函数是阻塞型的,当然我们也有方式实现非阻塞的延时。

int main(void)
{
    HAL_Init();
    SystemClock_Config();
    MX_GPIO_Init();

    while (1)
    {
        HAL_GPIO_TogglePin(GPIOA, GPIO_PIN_0);
        HAL_Delay(1000);
    }
}

上面方式使用HAL_Delay()进行延时,可以实现GPIOA0口每秒反转一次电平。如果只有一个任务这样就没问题,但是如果有多个对延时时间有不同需求的任务这样就不太合适了,这时候可以使用下面方式:

uint32_t previousMillisA0 = 0;
uint32_t previousMillisA1 = 0;

int main(void)
{
    HAL_Init();
    SystemClock_Config();
    MX_GPIO_Init();

    while (1)
    {
        uint32_t currentMillis = HAL_GetTick(); //获取当前系统时间
        if (currentMillis - previousMillisA0 >= 1000) //当前时间刻减去前次执行的时间刻
        {
            previousMillisA0 = currentMillis; //更新执行时间刻
            HAL_GPIO_TogglePin(GPIOA, GPIO_PIN_0);
        }
        if (currentMillis - previousMillisA1 >= 500) //当前时间刻减去前次执行的时间刻
        {
            previousMillisA1 = currentMillis; //更新执行时间刻
            HAL_GPIO_TogglePin(GPIOA, GPIO_PIN_1);
        }
    }
}

上面的代码实现了GPIOA0口每秒反转一次电平,同时GPIOA1口每500毫秒反转一次电平。可以使用这种方式处理更多的任务。

进阶使用

HAL的系统时间由定时器在中断中累加计数:

/**
  * @brief This function is called to increment  a global variable "uwTick"
  *        used as application time base.
  * @note In the default implementation, this variable is incremented each 1ms
  *       in SysTick ISR.
 * @note This function is declared as __weak to be overwritten in case of other 
  *      implementations in user file.
  * @retval None
  */
__weak void HAL_IncTick(void)
{
  uwTick += uwTickFreq;
}

因为这个回调函数是由 __weak 符号修饰的,所以可以自己重新写同名函数来实现更复杂的功能,比如可以用来实现调度器功能。先看下面演示:
在这里插入图片描述
上图例子中声明了两个任务,分别设置运行参数并运行。通过图中可以看到两个任务都按期望的方式执行了。

以下是Ticker部分代码:

#ifndef LIB_TICKER_H_
#define LIB_TICKER_H_

#include "main.h"

#define LIB_TICKER_MAX_SIZE 16 // 最大Ticker可绑定数

class LibTicker {
public:
	LibTicker(void);
	~LibTicker(void);

	typedef void (*callback_t)(void);
	typedef void (*callback_with_arg_t)(void*);

	// 设置Ticker定期执行,输入参数分别是时间(毫秒)、回调函数
	bool attach(size_t milliseconds, callback_t callback) {
		if (!milliseconds) {
			return false;
		}
		return _attach(milliseconds, milliseconds, reinterpret_cast<callback_with_arg_t>(callback), 0);
	}

	// 设置Ticker定期执行,输入参数分别是时间(毫秒)、回调函数、不大于32位变量
	template<typename T>
	bool attach(size_t milliseconds, void (*callback)(T), T arg) {
		if (!milliseconds) {
			return false;
		}
		if (sizeof(T) > sizeof(size_t)) {
			return false;
		}
		uint32_t arg32 = (uint32_t) arg;
		return _attach(milliseconds, milliseconds, reinterpret_cast<callback_with_arg_t>(callback), arg32);
	}

	// 设置Ticker执行一次,输入参数分别是时间(毫秒)、回调函数
	bool once(size_t milliseconds, callback_t callback) {
		return _attach(0, milliseconds, reinterpret_cast<callback_with_arg_t>(callback), 0);
	}

	// 设置Ticker执行一次,输入参数分别是时间(毫秒)、回调函数、不大于32位变量
	template<typename T>
	bool once(size_t milliseconds, void (*callback)(T), T arg) {
		if (sizeof(T) > sizeof(size_t)) {
			return false;
		}
		uint32_t arg32 = (uint32_t) arg;
		return _attach(0, milliseconds, reinterpret_cast<callback_with_arg_t>(callback), arg32);
	}
	void detach(void); // 停止已绑定运行的Ticker
	bool active(void); // 返回Ticker当前是否已绑定
	static void handle(void); // 执行已就绪的任务,该函数可以设置成main(){while(1){LibTicker::handle();}}
	static void schedule(void);

private:
	bool _active;
	size_t _index;
	bool _ready;
	size_t _period;
	size_t _count;
	callback_with_arg_t _callback;
	size_t _arg;
	bool _attach(size_t period, size_t count, callback_with_arg_t callback, size_t arg);
	static LibTicker *_ticker[LIB_TICKER_MAX_SIZE];
};

#endif /* LIB_TICKER_H_ */
#include "lib_ticker.h"

typedef void (*LibTickerCallBack)(size_t arg);

LibTicker::LibTicker(void) :
		_active(false), _ready(false), _period(0), _count(0), _callback(nullptr), _arg(0) {
}

LibTicker::~LibTicker(void) {
	detach();
}

void LibTicker::detach(void) {
	if (_active) { // 如果Ticker当前已绑定运行
		__disable_irq();
		_active = false;
		_ready = false;
		_period = 0;
		_callback = nullptr;
		_arg = 0;
		_ticker[_index] = nullptr;
		__enable_irq();
	}
}

bool LibTicker::active(void) {
	return _active;
}

bool LibTicker::_attach(size_t period, size_t count, callback_with_arg_t callback, size_t arg) {
	if (callback == nullptr) {
		return false;
	}
	if (_active) { // 如果Ticker当前已绑定运行
		_ready = false;
		_period = period;
		_count = count;
		_callback = callback;
		_arg = arg;
		return true;
	} else {
		for (size_t i = 0; i < LIB_TICKER_MAX_SIZE; i++) {
			if (_ticker[i] == nullptr) {
				__disable_irq();
				_active = true;
				_index = i;
				_ready = false;
				_period = period;
				_count = count;
				_callback = callback;
				_arg = arg;
				_ticker[i] = this;
				if (_count == 0) {
					_ready = true;
				}
				__enable_irq();
				return true;
			}
		}
	}
	return false;
}

void LibTicker::handle(void) {
	for (size_t i = 0; i < LIB_TICKER_MAX_SIZE; i++) {
		if (_ticker[i] == nullptr) {
			continue;
		}
		if (!_ticker[i]->_active) {
			continue;
		}
		if (_ticker[i]->_ready) { // 当前Ticker已就绪
			_ticker[i]->_ready = false;
			_ticker[i]->_callback(reinterpret_cast<void*>(_ticker[i]->_arg));
			if ((_ticker[i]->_period == 0) && (_ticker[i]->_count == 0)) { // 该任务只运行一次
				_ticker[i]->detach();
			}
		}
	}
}

void LibTicker::schedule(void) {
	for (size_t i = 0; i < LIB_TICKER_MAX_SIZE; i++) {
		if (_ticker[i] == nullptr) {
			continue;
		}
		if (!_ticker[i]->_active) {
			continue;
		}
		if (_ticker[i]->_count) {
			_ticker[i]->_count--;
		}
		if (_ticker[i]->_count == 0) {
			_ticker[i]->_ready = true;
			_ticker[i]->_count = _ticker[i]->_period;
		}
	}
}

LibTicker * LibTicker::_ticker[LIB_TICKER_MAX_SIZE] = { nullptr };

extern __IO uint32_t uwTick;
extern HAL_TickFreqTypeDef uwTickFreq;
void HAL_IncTick(void) { // 重写系统时间计数函数
	uwTick += uwTickFreq; // 保留系统时间计数功能
	LibTicker::schedule(); // 进行Ticker调度处理
}

上面的例子打包下载:
《基于STM32 HAL库的定时任务调度器例程 stm32f405ticker.zip》
https://download.csdn.net/download/naisu_kun/11913140
另外也可以参考GitHub上项目,命名稍有调整,功能并没有改动:
https://github.com/NaisuXu/STM32-tool-library-based-on-HAL-and-LL

总结

系统时间在开发过程中还是比较有用的,上面只是列举了部分常见用法。如果对时间有更精密的需求的话推荐使用定时器。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

使用HAL库开发STM32:系统时间基础及进阶使用 的相关文章

  • HCIP笔记

    HCIA复习 抽象语言 编码编码 二进制 二进制 电信号处理电信号 OSI参考模型 OSI RM 应用层 表示层 会话层 传输层 端口号 0 65535 1 1023是注明端口网络层 IP地址 数据链路层 物理层 ARP协议 正向ARP 通
  • 手把手教你写垃圾分类系统

    垃圾分类是目前社会的一个热点 制作垃圾分类只要找到合适的数据集 垃圾分类的模型构建并不难 这里收集了一份关于垃圾分类的数据集 一共有四个大类和245个小类 大类分别是厨余垃圾 可回收物 其他垃圾和有害垃圾 小类主要是垃圾的具体类别 果皮 纸
  • 2021-10-24

    2021 10 24

随机推荐

  • Linux系统常用命令

    文章目录 一 Linux目录介绍 二 Linux命令 三 Linux常用系统工作命令 1 输出字符串或者环境变量取值后的值 echo 2 显示或者设置系统时间与日期 date 3 设置系统时间 timedatectl 4 系统重启 rebo
  • C++模板-泛型函数与泛型类

    泛型 在调用函数或使用该类时才指定特定的类型 可以避免重复写类似功能代码 那C 语言如何定义泛型呢 Author W 泛型 模板 只有在调用或使用该函数或类时 才确定类型 1 泛型函数 2 泛型类 引入标准输入输出流 include
  • C++异常详细介绍

    目录 前言 一 C 异常概念 二 异常的抛出和匹配规则 三 异常的重新抛出 四 异常安全问题 五 异常规范说明 六 自定义异常体系 七 C 标准库的异常体系 八 异常优缺点 前言 在C语言中处理错误的方式和缺陷有 返回错误码 缺陷 1 错误
  • 硬件系统工程师宝典(22)-----电容、电感的特性,你都知道吗?

    各位同学大家好 欢迎继续做客电子工程学习圈 今天我们继续来讲这本书 硬件系统工程师宝典 上篇我们说到做电阻选型时要考虑阻值 功率 精度和封装大小 上下拉电阻除了给引脚一个稳定的电平 可以提高电压准位 增加输出功率以及满足阻抗匹配的要求 今天
  • buuctf(入门) Have Fun

    首先我们打开题目链接 我们看到一只猫 什么也没有 直接F12查看原代码 发现中间绿色代码的意思大概是定义一个数cat赋予它值cat值cat使用get函数请求的 如果数cat dog的话 则输出syc cat cat cat 所以我们只需要在
  • 867. 转置矩阵

    class Solution public vector
  • 安装postgresql以及乱码问题

    一 进入官网 按照自己的电脑型号选择合适的安装包 点下载那个图标的时候没有反应就多点几次 就可以弹出下载窗口了 二 打开下载好的安装包进行安装 可能出现乱码问题 一般出现这个问题大部分是因为你的系统用户名有中文字符 你需要把它改掉才行 之前
  • 区块链数据结构概述(MT、MPT)

    区块链数据结构概述 MT MPT Ethereum Foundation Blog MT Hash List Merkle Tree Merkle Tree的主要特点 Merkle Tree的相关操作 MPT Merkle Patricia
  • 请教Ado.Net按文本读取CSV/Txt文件时,如何禁止将内容转换成数字

    例如现有文件内容如下 文件内容开始 Column1 Column200001234 00005678 文件内容结束 读得的结果是 lt 1234 5678 gt 即它 智能 地认为我里面的内容为数字 而我希望它把内容当文本来处理 期望的结果
  • 重构总结

    之前就听说 重构 改善既有代码的设计 这本书很经典 一直没有机会拜读 书中讲的都是很实用的重构小技术 很多人肯定都用过 看完之后还需要在工作中多多使用 下面总结了一下这本书的知识点 方便日后查看
  • linux进程间信号量通信IPC(sem)

    一 信号量 信号量是一种用于提供不同进程间或一个从给定进程的不同线程间同步手段的原语 1 1 Posix信号量的选择 1 单个进程的各个线程共享 可以使用基于内存的信号量 2 彼此无亲缘关系的不同进程需使用信号量时 通常使用有名信号量 1
  • STM32无线网络监控传感器数据

    介绍 在此项目中 我们将首先创建一个无线传感器节点 传感器节点由四个基本组件组成 例如传感单元 处理单元 收发器单元和电源单元 传感单元可以由任何传感器组成 我正在使用BME280气压传感器 处理单元是STM32F103C微控制器 收发器单
  • Python之格式化字符串小练

    格式化字符串 a 3 b 5 print str a str b str a b 对于字符串的拼接显示 称为格式化字符串 有两种方案 的方式 print s s s a b a b s表示字符串 d表示整数类型 f表示浮点型的整数 info
  • python列表的三种遍历方法(for循环,索引,下标)

    列表是python中使用频率非常高的数据类型 用方括号 定义 接下来介绍遍历列表常用的三种方法 1 直接遍历 list1 1 24 34 44 533 5 219 for item in list1 直接遍历 print item 2 按索
  • Linux内核之ICMPv6详解

    要知道什么是ICMPv6协议 我们首先要知道什么是ICMP ICMP是一种面向无连接的协议 负责传递可能需要注意的差错和控制报文 差错指示通信网络是否存在错误 如目的主机无法到达 IP路由器无法正常传输数据包等 注意 路由器缓冲区溢出导致的
  • 执行git status命令时出现了“fatal: detected dubious ownership in repository“

    这个错误提示表示发现了版本库中存在可疑的所有权问题 即指定的目录 E take Class Rust MyRust 的所有者与当前用户不匹配 为了解决这个问题 Git提供了一个添加目录异常规则的方法 你可以按照下面的步骤进行操作 1 打开命
  • 前端基础知识之SVG&Canvas之间的区别与简单应用

    tip canvas 常用API fillRect x y width height 实心矩形 strokeRect x y width height 空心矩形 fillText Hello world 200 200 实心文字 strok
  • 8. R语言绘图系统介绍、高级绘图与低级绘图、【绘图参数】、绘图函数包

    b站课程视频链接 https www bilibili com video BV19x411X7C6 p 1 腾讯课堂 最新 但是要花钱 我花99 元买了 感觉讲的没问题 就是知识点结构有点乱 有点废话 https ke qq com co
  • HTML 个人简历源码

  • 使用HAL库开发STM32:系统时间基础及进阶使用

    文章目录 目的 基础使用 进阶使用 总结 目的 HAL库默认提供了系统时间 系统时间默认情况下由SysTick定时器计数产生 系统时间一方面用于HAL库自身调用 另一方面用户也可以使用 为开发带来便利 本文提到的相关使用主要应用于未使用OS