专家
公告
财富商城
电子网
旗下网站
首页
问题库
专栏
标签库
话题
专家
NEW
门户
发布
提问题
发文章
STM32
STM32 HAL库创建滴答定时器(systick定时器)
2019-07-20 16:09
发布
×
打开微信“扫一扫”,打开网页后点击屏幕右上角分享按钮
站内问答
/
STM32/STM8
5143
1
1406
本帖最后由 急速的蜗牛 于 2017-2-21 11:15 编辑
以前使用老版固件库进行STM32的开发,现在使用新版HAL库进行
例程是正点原子的STM32F429例程,发现滴答定时器的写法和以前老版本的完全不同
对其代码分析,发现,在初始化定时器时和定时函数中并没有赋予装载值load
后来发现其实在main函数的最开始进行了全局初始化,函数为:
HAL_Init();
复制代码
函数定义:
HAL_StatusTypeDef HAL_Init(void)
{
/* Configure Flash prefetch, Instruction cache, Data cache */
#if (INSTRUCTION_CACHE_ENABLE != 0)
__HAL_FLASH_INSTRUCTION_CACHE_ENABLE();
#endif /* INSTRUCTION_CACHE_ENABLE */
#if (DATA_CACHE_ENABLE != 0)
__HAL_FLASH_DATA_CACHE_ENABLE();
#endif /* DATA_CACHE_ENABLE */
#if (PREFETCH_ENABLE != 0)
__HAL_FLASH_PREFETCH_BUFFER_ENABLE();
#endif /* PREFETCH_ENABLE */
/* Set Interrupt Group Priority */
HAL_NVIC_SetPriorityGrouping(NVIC_PRIORITYGROUP_2);//ÖD¶ÏóÅÏ輶·Ö×é2
/* Use systick as time base source and configure 1ms tick (default clock after Reset is HSI) */
HAL_InitTick(TICK_INT_PRIORITY);
/* Init the low level hardware */
HAL_MspInit();
/* Return function status */
return HAL_OK;
}
复制代码
说明有对滴答定时器的初始化操作,打开这个函数来分析
__weak HAL_StatusTypeDef HAL_InitTick(uint32_t TickPriority)
{
/*Configure the SysTick to have interrupt in 1ms time basis*/
HAL_SYSTICK_Config(HAL_RCC_GetHCLKFreq()/1000);
/*Configure the SysTick IRQ priority */
HAL_NVIC_SetPriority(SysTick_IRQn, TickPriority ,0);
/* Return function status */
return HAL_OK;
}
复制代码
继续下一步查找重点函数:
uint32_t HAL_SYSTICK_Config(uint32_t TicksNumb)
{
SysTick_Config(TicksNumb);</font>
}
复制代码
继续:
__STATIC_INLINE uint32_t SysTick_Config(uint32_t ticks)
{
if ((ticks - 1UL) > SysTick_LOAD_RELOAD_Msk)
{
return (1UL); /* Reload value impossible */
}
SysTick->LOAD = (uint32_t)(ticks - 1UL); /* set reload register */
NVIC_SetPriority (SysTick_IRQn, (1UL << __NVIC_PRIO_BITS) - 1UL); /* set Priority for Systick Interrupt */
SysTick->VAL = 0UL; /* Load the SysTick Counter Value */
SysTick->CTRL = SysTick_CTRL_CLKSOURCE_Msk |
SysTick_CTRL_TICKINT_Msk |
SysTick_CTRL_ENABLE_Msk; /* Enable SysTick IRQ and SysTick Timer */
return (0UL); /* Function successful */
}
复制代码
至此到达重点,在前面提到这个函数,如下:
HAL_SYSTICK_Config(HAL_RCC_GetHCLKFreq()/1000);
复制代码
这里可以看出函数参数是通过
HAL_RCC_GetHCLKFreq()
函数来获取当前时钟频率的
uint32_t HAL_RCC_GetHCLKFreq(void)
{
SystemCoreClock = HAL_RCC_GetSysClockFreq() >> APBAHBPrescTable[(RCC->CFGR & RCC_CFGR_HPRE)>> POSITION_VAL(RCC_CFGR_HPRE)];
return SystemCoreClock;
}
复制代码
在这里, HAL_RCC_GetSysClockFreq() 函数是用来获取当前系统的时钟频率里面的实现方法暂不介绍,这里只提一个重点
PSITION_VAL(RCC_CFGR_HPRE)
复制代码
这个函数的意义我研究了好久,最后通过模拟来确认的,网上资料确实不多
首先这个函数原型是一个宏定义
#define POSITION_VAL(VAL) (__CLZ(__RBIT(VAL)))
复制代码
这里重点的就是__CLZ(__RBIT(VAL)) 这个函数
__RBIT(VAL)这个函数的含义是,将val按照32bit格式翻转180° 比如:
原本二进制为:0000 0000 0000 0000 0000 1111 1100 0000
通过这个函数返回的值为:0000 0011 1111 0000 0000 0000 0000 0000
函数实现如下:
uint32_t __RBIT(uint32_t value)
{
uint32_t result;
int32_t s = 4 /*sizeof(v)*/ * 8 - 1; /* extra shift needed at end */
result = value; /* r will be reversed bits of v; first get LSB of v */
for (value >>= 1U; value; value >>= 1U)
{
result <<= 1U;
result |= value & 1U;
s--;
}
result <<= s; /* shift when v's highest bits are zero */
return(result);
}
复制代码
重点:
前面的__CLZ()看网上的介绍,说是前导零计算个数,就是二进制数第一个1之前到最高位0的个数 比如说
第31位为1 那么计算结果为0 第30位为1,31位为0
计算结果
为1。这里听的不明代的建议在网上再搜索一下。
这样两个函数加起来的意思就明显了,前导零的个数是VAL指的180°翻转后前面零的个数,这样,我们就可以
知道原来的值在32bit中的具体位置.
比如之前的例子二进制为:0000 0000 0000 0000 0000 1111 1100 0000,我们可以得出这个32bit型的整数
右移6位就得到111111 这个值。即十进制63 ,有什么具体作用呢?
HAL库中,很多寄存器的值读出来的都是32bit的,里面包含了很多寄存器,如果我们知道所在寄存器的位置
比如说第7-11位为一个寄存器,那么将这个32bit的值&上0000 0000 0000 0000 0000 1111 1000 0000就可以得到
当前寄存器中第7-11实际的值。然后通过
__CLZ(__RBIT(VAL))
换算知道后面有7个零 这样再将&后的值右移7位,然后就得到
当前寄存器的实际值了,在HAL库中非常实用。
实际上
到这里
我们就可以计算出当前系统的时钟频率,回到这个函数:
__STATIC_INLINE uint32_t SysTick_Config(uint32_t ticks)
复制代码
SysTick->LOAD = (uint32_t)(ticks - 1UL);
复制代码
可以知道如果频率不大于24bit最大值得情况下
加载值就是当前频率的1/1000。即1ms需要这么多个时钟周期
(比如时钟频率为180MHZ 这个意思是1S种可以跑180M个时钟周期,除以1000即
1ms可以跑180000个周期)
写入重装载值中
如果控制位不设为0便永不停息
所以,滴答定时器函数中无需再重复设置加载值,便于HAL_Delay也能正常运行
下面便是新版的延时函数(
没有复制OS支持的代码部分
)
[mw_shl_code=c,true]void delay_init(u8 SYSCLK)
{
HAL_SYSTICK_CLKSourceConfig(SYSTICK_CLKSOURCE_HCLK);
fac_us=SYSCLK;
} [/mw_shl_code]实际上可以直接宏定义fac_us 180 选择时钟来源已经在HAL_Init()中选择了。
#define fac_us 180
复制代码
这样的话,就可以直接写延时函数就可以了,不用在main中写初始化了。
void delay_us(u32 nus)
{
u32 ticks;
u32 told,tnow,tcnt=0;
u32 reload=SysTick->LOAD;
ticks=nus*fac_us;
told=SysTick->VAL;
while(1)
{
tnow=SysTick->VAL;
if(tnow!=told)
{
if(tnow<told)tcnt+=told-tnow;
else tcnt+=reload-tnow+told;
told=tnow;
if(tcnt>=ticks)break;
}
}
}
复制代码
void delay_ms(u16 nms)
{
u32 i;
for(i=0;i<nms;i++) delay_us(1000);
}
复制代码
亦可以改为
void delay_ms(u16 nms)
{
HAL_Delay(nms);
}
复制代码
或者不用这个函数就好直接用HAL_Delay();
友情提示:
此问题已得到解决,问题已经关闭,关闭后问题禁止继续编辑,回答。
该问题目前已经被作者或者管理员关闭, 无法添加新回复
1条回答
dop101
1楼-- · 2019-07-20 19:15
顶。。。。。。。。。。。
加载中...
一周热门
更多
>
相关问题
STM32F4上I2C(在PROTEUS中模拟)调试不通的问题
6 个回答
芯片供应紧张,准备换个MCU,MM32L系列替换STM32L系列的怎么样?
7 个回答
STM32同时使用两个串口进行数据收发时数据丢包的问题
5 个回答
STM32F103串口通信死机问题
4 个回答
STM32WLE5CC连接SX1268在LoRa模式下能与 SX1278互通吗?
2 个回答
STM32开发板免费用活动
7 个回答
stm32 处理 DHT11占用太多时间,大家程序是怎么设计的
8 个回答
分享一个STM32单片机做的离线编程器代码
9 个回答
相关文章
ST公司第一款无线低功耗单片机模块有效提高物联网设计生产效率
0个评论
如何实现对单片机寄存器的访问
0个评论
通过USB用STM32片内自带Bootloader下载程序及注意事项
0个评论
欲练此功必先自宫之STM32汇编启动,放慢是为了更好的前行
0个评论
×
关闭
采纳回答
向帮助了您的网友说句感谢的话吧!
非常感谢!
确 认
×
关闭
编辑标签
最多设置5个标签!
STM32
保存
关闭
×
关闭
举报内容
检举类型
检举内容
检举用户
检举原因
广告推广
恶意灌水
回答内容与提问无关
抄袭答案
其他
检举说明(必填)
提交
关闭
×
关闭
您已邀请
15
人回答
查看邀请
擅长该话题的人
回答过该话题的人
我关注的人
一周热门 更多>