本帖最后由 6552918 于 2023-7-30 22:25 编辑
#申请原创# #技术资源#[url=home.php?mod=space&uid=760190]@21小跑堂 [/url]
厂家提供的库例程里使用了延时函数,但延时函数使用了指令空跑的形式,此方式会受各种原因的影响,在不同编译器和优化等级下都会有不同的执行效果,在实际使用中意义不大。
现在为大家分享一种依赖systick但又不影响systick,还能实现各种延时的库---perf_counter(Cortex-M 系统专用的性能计数器)。它与用户原有的SysTick功能共享SysTick,且不干扰用户原有的SysTick功能。该库将带来新的功能,例如time.h中定义的性能计数器、delay_us和clock()服务
源码地址如下:
https://github.com/GorgonMeducer/perf_counter
但因为github在国内访问,基本就没稳定的时候,所有我擦用gitee导入的方式下载源码
导入成功后就可在gitee上看到perf_counter的内容了
然后再进行源码下载就不会有问题了
解压后的源码包内容如下
今天为大家介绍的使用方法是CMSIS-Pack方式,可以在MDK环境中实现傻瓜式的部署
相关文件路径和安装方法如下:
为什么要使用CMSIS-Pack方式呢,因为源码方式会涉及到很对编译器相关的设置,使用起来稍微麻烦点,而且还可能影响到现有代码,所有使用CMSIS-Pack方式是比较方便可靠的。
使用方式如下:
打开RTE配置窗口:
在列表的末端出现了一个Utilities条目,依次展开后勾选perf_counter—Core—Variant项选择Library,系统会选择以库的形式来实现模块的部署
单击OK按钮后,我们会发现Utilities已经被加入到了工程管理器中,而perf_counter.lib也已经成功的部署到了目标工程中:
因为厂家例程默认使用的是AC5编译器
应该同时勾选C99 Mode和GNU extensions两个选项
如果是用AC6,需要打开对GNU扩展和C99(极其以上)语言标准的支持,具体方法如下图所示:在Language C标准下拉列表中选择带有gnu前缀的选项——如果没有什么特别的顾虑,推荐直接拉满——使用gnu11即可。
在使用perf_counter的C语言源文件中,我们需要首先加入对头文件的引用:
需要注意的是,通过RTE方式部署的perf_counter并不会在工程管理器中引入 perf_counter.h 以供用户查看。想要查看perf_counter.h查找可用API的小伙伴,可以“简单的”在上述代码行上单击右键,从弹出菜单中选择“Open document 'perf_counter'” 来实现对头文件的访问。
perf_counter.h 内展示了所有perf_counter内实现的功能函数
在使用前需要添加perf_counter初始化函数,这里需要注意传递的参数,如果自己的程序已经使用systick,如果已经使用需要传参为ture,如果没有使用需要传参fales
编译后
用户自己的应用里完全没有使用SysTick。此时,在编译时,我们多半会看到类似如下的错误提示:
对于这种情况,我们需要在任意的C文件中添加一个SysTick中断处理程序即可,由于官方例程库里有void SysTick_Handler(void)函数,所有一般不会遇到这个问题,
但会遇到这问题
是因为按照ARM的规范需要再有个system_xxxxxx.h的文件定义内核相关的信息,里面有个SystemCoreClock的全局定义需要被用到,但凌欧的库里没有按照ARM的传统做法去做,所有需要我们自己定义一个SystemCoreClock区局变量(也可以用文件有有的sys_clk = 96000000;),为perf_counter 库所使用。
添加完,错误报告就会消失。
需要特别注意的是:由于用户并没有自己初始化 SysTick,因此我们需要将这一情况告知 perf_counter 库——由它来完成对 SysTick 的初始化——这里传递 false 给函数 init_cycle_counter() 就是这个功能。如果由perf_counter 库自己来初始化SysTick,它会为了自己功能更可靠将SysTick的溢出值(LOAD寄存器)设置为最大值(0x00FFFFFF)。
2、用户自己的应用里使用了SysTick,拥有自己的初始化过程。对于这种情况,我们需要确保一件事情:即,SysTick的CTRL寄存器的 BIT2(SysTick_CTRL_CLKSOURCE_Msk)是否被置位了——如果其值是1,说明SysTick使用了跟CPU一样的工作频率,那么SysTick的测量结果就是CPU的周期数;如果其值是0,说明SysTick使用了来自于别处的时钟源,这个时钟源具体频率是多少就只能看芯片手册了(比如STM32就喜欢将系统频率做 1/8 分频后提供给SysTick作为时钟源),此时SysTick测量出来的结果就不是CPU的周期数。
在确保了 CTRL 寄存器的 BIT2 被正确置位,并且SysTick中断被使能(置位 BIT1,SysTick_CTRL_TICKINT_Msk )后,我们可以简单的通过 init_cycle_counter() 函数告诉perf_counter模块:SysTick 被用户占用了——这里传递 true 就实现这一功能。
关于Library的匹配问题
perf_counter.lib 库在编译的时候,开启了 Short enums/wchar(分别对应命令行的 -fshort-enums -fshort-wchar)。这么做其实没什么特别的原因,但如果你的工程使用了不同的配置,例如:
下图的工程配置中,没有勾选 "Short enums/wchar"
解决方发是,在工程配置中勾选上这一选项
至此,perf_counter的部署工作就完成了,可以使用相关的API进行操作了。
微秒级阻塞延时
perf_counter提供了一个us级阻塞延时函数 delay_us(),它的函数原型如下:
externvoiddelay_us(int32_tiUs);
实际上,由于函数调用的开销,delay_us在时间判断上会存在一个“不积累”的误差——根据优化等级的不同,其具体CPU周期数存在差异,如果我们以Library方式进行部署时,这一误差大约在+/-25个CPU周期左右——这一信息实际上告诉我们:
在使用Library的情况下,当你的CPU频率超过50MHz时,delay_us() 可以提供最小<1us的延时误差;
当你的系统频率不满足上述条件时,以系统频率 12MHz为参考,则可以认为delay_us误差为不积累的 +/- 2us。
接下来我们看一下延时效果如何,
先看看us延时500us
us延时10us
再看看ms延时10ms
总体来看,延时精度还是非常高的,而且这个延时不会收到编译设置和环境改变的影响,因为是通过记录内核运行时间来实现的,所以非常准确。
这些延时函数都是perf_counter的最基本函数,perf_counter还有能多高级**,比如 查看程序运行的多少个机器周期等,感兴趣的可以自己去研究一下。
本帖只是个入门贴,还有很多高级功能需要大家自己去研究和使用。
相关源码如下:
Demo28_Systick_delay.zip
(1.29 MB)
|