运行环境: stm32h743iit6; 主频480MHz; APB1; 240MHz; TIM5 240MHz; 预分频系数为1; 定时器计数频率240MHz;
应用需求:实现软件模拟IIC,延迟精度2个微秒;
量变引起质变,当延迟粒度太小时,需要考虑延迟的实现策略是否适合。
目前的延迟策略:
-
传统的基于定时器中断策略;
-
polling轮询等待策略;
对于强迫症和完美主义的人来讲,可能首选考虑的是中断策略。但是当下形势变化了,由于延迟时间过于短(2个微秒)所以对延迟精度要求较高。
根据当前判断,采取的是执行polling轮询等待策略实现。
实现正确延迟的前提是,必须保证各个节点的时钟频率是计算正确的符合实际的!
polling轮询等待策略的实现
-
根据外部为 timer 提供的时钟源速率、内部预分频系数等计算得到定时器的计数器的运行时频率,也就得到了1秒内计数器累加多少次的节拍; 当然可以计算获得1微秒需要走多少个节拍,这个可能更加实用。
-
程序内延迟开始时,直接摘取定时器计数器的当前值,作为开始的测量点;延迟结束后,再次直接读取定时器的计数器的当前值作为结束的测量点; 由此得到,延迟过程中定时器的计数器走过多少个节拍,也就是累加的增量是多少个。
-
根据前两步得到的,延迟总节拍数除以1微秒的节拍数就可以知道此过程延迟了多少个微秒。
注意:延迟过程内的代码应该尽量简洁,就是不要执行过多的不处于测量范围内的CPU指令,否则这部分也会被算进去导致测量有误差。
测量时代码实现上:
-
计算延迟 n 微秒需要的总步长,n*STEP,也就是计数器应该累加的增量值N;
-
将当前定时器的计数器的数值设置为零,从零开始计数;
-
polling轮询 计数器的值 与 N 作比较,使其在不少于N的范围内继续等待,否则延迟时间达到,跳出延迟代码;
-
可以将受保护的代码使⽤禁⽤中断保护起来,运⾏结束再开启中断;
void delay_us (uint32_t us)
{//可以禁用中断,保证不可打断volatile uint32_t ticks = us * STEP_OF_TIM5_CNT;TIM5->CNT = 0;while (TIM5->CNT < ticks);//可以禁用中断,保证不可打断,且需要覆盖下面延迟的目的代码:比如IIC的电平转换,以保证效果。}
如何测量延迟是否准确
测量延迟节拍数是否符合预期
原理前面讲了,可以通过调试软件观察得到节拍数,进行验证。
使用keil仿真调试
可以使用keil仿真观察直接显示的时间结果数据。在此之前,需要正确设置好keil,且确保MCU内有ARM debug IP core 的DWT(debug watch point)IP core支持,因为keil就是通过 jlink、stlink 间接获取DWT的计时数据。那么如何设置好keil呢?这里参考了鱼鹰的微博写的很好,做简明描述:
实际测试数据
案例2
案例3
案例4
这次延迟5000个微秒,误差只有1.30个微秒,属于正常刚好跟上⾯的误差延迟对应。说明⽐较准确了。可能上次有进出中断导致误差太⼤了!。