举个例子,比如回帖在3楼 第一步:那么点三楼内容进入编辑状态,然后在开头(或者任意位置)点击“插入/编辑锚点链接”→给锚点取个名称,比如3,然后发表;
第二步:在一楼进入编辑状态,选中你要链接到3楼的文字,点击“插入/编辑超链接”,弹出对话框,在“地址”下文本框中输入“#3”,确定,发表;
就OK了,不知说的明不明白。
STM32 的延时:
在51的时期,我们习惯用循环来做延时这个事情,比较才12M的晶振,指令周期再12分 也就1M,循环个数百次到千次。
但STM32F103 的工作频率是比较高的,建议最高频率达72MHz,当然最高肯定不只72MHz(后面开专题研究超频),而且有些单条指令只占2~3 个时钟周期,速度奇快。
当然我也用循环来做延时8调 delay(1000),里面的K值还贼大,这才出了个有点像1S 的延时。坑爹吧!
死磕的童鞋肯定磕不出什么好办法,作为一届网民,我们还是有利器的,谷歌、度娘。“STM32 延时” 然后新关键词就出现了Systick。多余的我不说了,我不是做教程的也没必要。作为一名工程师要学会的就是自己解决问题,问回来的答案永远不是自己的。
systick 寄存器作用如下
在CTRL寄存器中 有CLKSOURCE这么一控制位,控制的是使用内部时钟或是外部时钟,当初我以为的是使用片外晶振和片内晶振这么一个控制,后来迷惑了,翻看了数遍stm32的时钟关系,发现原来并非这么回事,M3 是STM32的一个内核,时钟控制外设,整片上系统的时钟好比树的主干,而外设是挂在主干上面的各个分支,同样M3 也是,M3使用的是系统主干的系统时钟。而systick 可以有两个时钟一个是和M3 同一个也就是所谓的内部时钟,另一个是系统主干时钟8分频后的外部输入时钟。这也好理解了网上众多教程中所说的要除8的原因。
——————————————————————————————————————
什么??你说你参考网上的教程例子 那个systick 库的程序编译好多错误?当然如果你有一份中文版的STM32 固件库使用手册对照了数百遍也还是没发现那里写错吧。我可以回答你,大哥你真的一句代码都没写错。原因在于你使用的是3.0后的固件库!在网上很多教程中都是写得一个死样的(这里我怀疑曾经有一个高手做出了好的教程,然后后面无数模仿的商家,真实一直被模仿从未被超越。),都是3.0之前的固件库。新的固件库对于systick 有了一个很大的改动。管网中有一份2.0 到3.0固件库升级的说明文档
2.0与3.0固件库对比.pdf
在新固件库下也有一个文档"stm32f10x_stdperiph_lib_um", 搜systick 也可以找到相关的用法,库中给出调用的函数是
if (SysTick_Config (SystemCoreClock / 1000)) ; { /* Setup SysTick for 1 msec interrupts */
while (1);
}
所以延时1ms 我们使用方法是
if (SysTick_Config (SystemCoreClock / 1000))
{ while (1); } //1ms 的延时计数
TimeCount=nCount; // 时间累加
while(TimeCount);
SysTick->CTRL=0x00; // 关闭计数器
SysTick->VAL =0X00; // 清除计数器
另外我们只要在中断函数中加入
void SysTick_Handler(void)
{ TimeCount--; }
延时1S 就是TimeCount=1000,1000个1ms就行了
1ns 的延时就写成 if (SysTick_Config (SystemCoreClock / 1000000))
让倒数值再减少3个0就行了
——————————————————————————————————————
纠结这个库是如何实现的呢,继续近一步分析库函数:
(先去打个水喝)
systick 固件库的函数使用已经从手册中找到了,那么其原理是如何实现的呢?
上面寄存器中,有个重载寄存器RELOAD 满值是0xFFFFFF, 即最大值是16777215, 因为是倒数的,所以倒数72000000 次就是1S 了,当然是在72MHz 系统时钟下使用M3核系统时钟,可是没那么大的框,所以装 72000000/1000=72000 这个数进去就好了,倒数完一次是1ms。
顺着库函数很清楚做的就是SysTick_Config (SystemCoreClock / 1000) 这函数运算。然后跳转导core_cm3.h 这个文件来了,然后看到下面这段代码
static __INLINE uint32_t SysTick_Config(uint32_t ticks)
{
if (ticks > SysTick_LOAD_RELOAD_Msk) return (1); /* Reload value impossible */
SysTick->LOAD = (ticks & SysTick_LOAD_RELOAD_Msk) - 1; /* set reload register */
NVIC_SetPriority (SysTick_IRQn, (1<<__NVIC_PRIO_BITS) - 1); /* set Priority for Cortex-M0 System Interrupts */
SysTick->VAL = 0; /* 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 (0); /* Function successful */
}
进入函数后第一件事是把输入的变量和SysTick_LOAD_RELOAD_Msk 做了一次比较,这是个什么数呢再跳
#define SysTick_LOAD_RELOAD_Pos 0 /*!< SysTick LOAD: RELOAD Position */
#define SysTick_LOAD_RELOAD_Msk (0xFFFFFFul << SysTick_LOAD_RELOAD_Pos) /*!< SysTick LOAD: RELOAD Mask */
看到0xFFFFFF 熟悉吧,正是的,RELOAD寄存器能装置的最大值,后面多了个ul? 怎么回事,查了好多资料,基本很少有讲,大致意思是定义的这个数是一个 unsigned long 类型.当我们输入的计数值大于积存能装载的最大值将会返回一个 值为1的错误返回。当然if (SysTick_Config (SystemCoreClock / 1000)) { while (1); } 直接死循环,调试的时候我们就会发现在这个函数跳不出去,因而找到错误。固件库中设置了好多这样的安全关口,保证我们自调试的时候能快速找到问题所在点。
再看载入值 (ticks & SysTick_LOAD_RELOAD_Msk) - 1; 与上一个全F当然出来的是我们填入的数例如72000,但疑问来了,为什么要-1啊~!! 为什么啊~!! 这其实算是一个C 语言的小毛病,Data[5]最大的那个数组是Data[4] 而不是Data[5]吧,是的还有个Data[0] 呢。
剩下的语句基本动作:中断设置、计数器清零、设定控制并且开始,相关寄存器控制位自己看表了。
回头再讲一个差点忘了的东西,我们除了那个除数从来可没给他输入过别的计数值啊。再跳SysTick_Config (SystemCoreClock / 1000) ,SystemCoreClock 跳转到systme_stm32f10x.h后看到uint32_t SystemCoreClock = SYSCLK_FREQ_72MHz; 噢,原来这里定义了它等于72MHz,还没到底呢,继续SYSCLK_FREQ_72MHz,再跳
#define SYSCLK_FREQ_72MHz 72000000。结束! 把这些最原始的值代入我们的程序就成了最原始的程序了,所有的计算方法以及寄存器控制灯
今天到此为止,下一章,另类玩转串口,是另类的哦~ 和你们的教程可都不一样的哦。请密切关注,谢谢。THINK YOU
回复
有奖活动 | |
---|---|
【有奖活动】分享技术经验,兑换京东卡 | |
话不多说,快进群! | |
请大声喊出:我要开发板! | |
【有奖活动】EEPW网站征稿正在进行时,欢迎踊跃投稿啦 | |
奖!发布技术笔记,技术评测贴换取您心仪的礼品 | |
打赏了!打赏了!打赏了! |