使用HAL库开发STM32:系统时间基础及进阶使用
目的
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();
<span class="token keyword">while</span> <span class="token punctuation">(</span><span class="token number">1</span><span class="token punctuation">)</span>
<span class="token punctuation">{<!-- --></span>
<span class="token function">HAL_GPIO_TogglePin</span><span class="token punctuation">(</span>GPIOA<span class="token punctuation">,</span> GPIO_PIN_0<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token function">HAL_Delay</span><span class="token punctuation">(</span><span class="token number">1000</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
上面方式使用HAL_Delay()进行延时,可以实现GPIOA0口每秒反转一次电平。如果只有一个任务这样就没问题,但是如果有多个对延时时间有不同需求的任务这样就不太合适了,这时候可以使用下面方式:
uint32_t previousMillisA0 = 0;
uint32_t previousMillisA1 = 0;
int main(void)
{
HAL_Init();
SystemClock_Config();
MX_GPIO_Init();
<span class="token keyword">while</span> <span class="token punctuation">(</span><span class="token number">1</span><span class="token punctuation">)</span>
<span class="token punctuation">{<!-- --></span>
uint32_t currentMillis <span class="token operator">=</span> <span class="token function">HAL_GetTick</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">//获取当前系统时间</span>
<span class="token keyword">if</span> <span class="token punctuation">(</span>currentMillis <span class="token operator">-</span> previousMillisA0 <span class="token operator">>=</span> <span class="token number">1000</span><span class="token punctuation">)</span> <span class="token comment">//当前时间刻减去前次执行的时间刻</span>
<span class="token punctuation">{<!-- --></span>
previousMillisA0 <span class="token operator">=</span> currentMillis<span class="token punctuation">;</span> <span class="token comment">//更新执行时间刻</span>
<span class="token function">HAL_GPIO_TogglePin</span><span class="token punctuation">(</span>GPIOA<span class="token punctuation">,</span> GPIO_PIN_0<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token keyword">if</span> <span class="token punctuation">(</span>currentMillis <span class="token operator">-</span> previousMillisA1 <span class="token operator">>=</span> <span class="token number">500</span><span class="token punctuation">)</span> <span class="token comment">//当前时间刻减去前次执行的时间刻</span>
<span class="token punctuation">{<!-- --></span>
previousMillisA1 <span class="token operator">=</span> currentMillis<span class="token punctuation">;</span> <span class="token comment">//更新执行时间刻</span>
<span class="token function">HAL_GPIO_TogglePin</span><span class="token punctuation">(</span>GPIOA<span class="token punctuation">,</span> GPIO_PIN_1<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span>
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
上面的代码实现了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;
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
因为这个回调函数是由 __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);
<span class="token keyword">typedef</span> <span class="token keyword">void</span> <span class="token punctuation">(</span><span class="token operator">*</span>callback_t<span class="token punctuation">)</span><span class="token punctuation">(</span><span class="token keyword">void</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token keyword">typedef</span> <span class="token keyword">void</span> <span class="token punctuation">(</span><span class="token operator">*</span>callback_with_arg_t<span class="token punctuation">)</span><span class="token punctuation">(</span><span class="token keyword">void</span><span class="token operator">*</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token comment">// 设置Ticker定期执行,输入参数分别是时间(毫秒)、回调函数</span>
bool <span class="token function">attach</span><span class="token punctuation">(</span>size_t milliseconds<span class="token punctuation">,</span> callback_t callback<span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span>
<span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token operator">!</span>milliseconds<span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span>
<span class="token keyword">return</span> false<span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token keyword">return</span> <span class="token function">_attach</span><span class="token punctuation">(</span>milliseconds<span class="token punctuation">,</span> milliseconds<span class="token punctuation">,</span> reinterpret_cast<span class="token operator"><</span>callback_with_arg_t<span class="token operator">></span><span class="token punctuation">(</span>callback<span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token number">0</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token comment">// 设置Ticker定期执行,输入参数分别是时间(毫秒)、回调函数、不大于32位变量</span>
template<span class="token operator"><</span>typename T<span class="token operator">></span>
bool <span class="token function">attach</span><span class="token punctuation">(</span>size_t milliseconds<span class="token punctuation">,</span> <span class="token keyword">void</span> <span class="token punctuation">(</span><span class="token operator">*</span>callback<span class="token punctuation">)</span><span class="token punctuation">(</span>T<span class="token punctuation">)</span><span class="token punctuation">,</span> T arg<span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span>
<span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token operator">!</span>milliseconds<span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span>
<span class="token keyword">return</span> false<span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token keyword">sizeof</span><span class="token punctuation">(</span>T<span class="token punctuation">)</span> <span class="token operator">></span> <span class="token keyword">sizeof</span><span class="token punctuation">(</span>size_t<span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span>
<span class="token keyword">return</span> false<span class="token punctuation">;</span>
<span class="token punctuation">}</span>
uint32_t arg32 <span class="token operator">=</span> <span class="token punctuation">(</span>uint32_t<span class="token punctuation">)</span> arg<span class="token punctuation">;</span>
<span class="token keyword">return</span> <span class="token function">_attach</span><span class="token punctuation">(</span>milliseconds<span class="token punctuation">,</span> milliseconds<span class="token punctuation">,</span> reinterpret_cast<span class="token operator"><</span>callback_with_arg_t<span class="token operator">></span><span class="token punctuation">(</span>callback<span class="token punctuation">)</span><span class="token punctuation">,</span> arg32<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token comment">// 设置Ticker执行一次,输入参数分别是时间(毫秒)、回调函数</span>
bool <span class="token function">once</span><span class="token punctuation">(</span>size_t milliseconds<span class="token punctuation">,</span> callback_t callback<span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span>
<span class="token keyword">return</span> <span class="token function">_attach</span><span class="token punctuation">(</span><span class="token number">0</span><span class="token punctuation">,</span> milliseconds<span class="token punctuation">,</span> reinterpret_cast<span class="token operator"><</span>callback_with_arg_t<span class="token operator">></span><span class="token punctuation">(</span>callback<span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token number">0</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token comment">// 设置Ticker执行一次,输入参数分别是时间(毫秒)、回调函数、不大于32位变量</span>
template<span class="token operator"><</span>typename T<span class="token operator">></span>
bool <span class="token function">once</span><span class="token punctuation">(</span>size_t milliseconds<span class="token punctuation">,</span> <span class="token keyword">void</span> <span class="token punctuation">(</span><span class="token operator">*</span>callback<span class="token punctuation">)</span><span class="token punctuation">(</span>T<span class="token punctuation">)</span><span class="token punctuation">,</span> T arg<span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span>
<span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token keyword">sizeof</span><span class="token punctuation">(</span>T<span class="token punctuation">)</span> <span class="token operator">></span> <span class="token keyword">sizeof</span><span class="token punctuation">(</span>size_t<span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span>
<span class="token keyword">return</span> false<span class="token punctuation">;</span>
<span class="token punctuation">}</span>
uint32_t arg32 <span class="token operator">=</span> <span class="token punctuation">(</span>uint32_t<span class="token punctuation">)</span> arg<span class="token punctuation">;</span>
<span class="token keyword">return</span> <span class="token function">_attach</span><span class="token punctuation">(</span><span class="token number">0</span><span class="token punctuation">,</span> milliseconds<span class="token punctuation">,</span> reinterpret_cast<span class="token operator"><</span>callback_with_arg_t<span class="token operator">></span><span class="token punctuation">(</span>callback<span class="token punctuation">)</span><span class="token punctuation">,</span> arg32<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token keyword">void</span> <span class="token function">detach</span><span class="token punctuation">(</span><span class="token keyword">void</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// 停止已绑定运行的Ticker</span>
bool <span class="token function">active</span><span class="token punctuation">(</span><span class="token keyword">void</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// 返回Ticker当前是否已绑定</span>
<span class="token keyword">static</span> <span class="token keyword">void</span> <span class="token function">handle</span><span class="token punctuation">(</span><span class="token keyword">void</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// 执行已就绪的任务,该函数可以设置成main(){while(1){LibTicker::handle();}}</span>
<span class="token keyword">static</span> <span class="token keyword">void</span> <span class="token function">schedule</span><span class="token punctuation">(</span><span class="token keyword">void</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
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_ */
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
#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调度处理
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
- 84
- 85
- 86
- 87
- 88
- 89
- 90
- 91
- 92
- 93
- 94
- 95
- 96
- 97
- 98
- 99
- 100
- 101
- 102
- 103
- 104
- 105
- 106
- 107
上面的例子打包下载:
《基于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
总结
系统时间在开发过程中还是比较有用的,上面只是列举了部分常见用法。如果对时间有更精密的需求的话推荐使用定时器。