至于上述代码中为什么用了一个“|”(或)呢?
这里我们解释一下这个C语言知识,对于一个8位的寄存器来说,这里我们简单起见,就举8位的寄存器作为例子,这里我们如果要实现修改其中一位,却不改变其他位的值,改如何实现的问题,这里我们具体看。
首先我们默认的8位寄存器的初始值假设我们设置为:1001_0111
那么现在我们比如说要改变第6位的值(这里我们指的是真实的第六位,为什么这么说,按照平常我们的说法寄存器是从第0位到第7位)。
我们的实现方法如下:
1001_0111 | 0010_0000 = 1011_0111
可以看出,通过或的方式我们可以实现只改变一位而不影响其他位的数据。
那么如果我们要让一位相反,从1变到零,我们可以通过与的方式来实现。比如我们这里改变第五位试下。
1001_0111 & 0001_0000 = 0001_0000
看到得到的结果并不是我们想要的,这里需要做一个小小的变通,对应后面要进行相与的数据要进行一个取反,之后才可以进行两者的相与。
1001_0111 & (~(0001_0000)) = 1001_0111 & 1110_1111 = 1000_0111
这样子我们就可以成功实现了不管是置零还是置一都可以不改变其他位的数据,之所以这样做的原因是,430没有办法进行位寻址,所以没有办法对位进行直接操作,只能通过寄存器的方式来进行操作,而对于51来说可以直接进行位寻址。而32实现的方式是通过位绑定的方式进行实现的,这里我们不细说这个位绑定的方式,我们下次再来写一个帖子说明这个问题,了解一下具体的实现方式。
好了,明白了这个我们在看下前面的SysTick->CTRL这个指的是用ARM提供的寄存器指令进行编写的,代表是指向我们SysTick这个模块的控制寄存器,在ARM-Cotex-M4中有四个寄存器来控制SysTick这个模块。我们可以找到他们的说明(这里我没有找到Cotex-M4的中文滴答定时器的说明,但是有Cotex-M3的中文说明,这两者是一样的在滴答定时器这个章节,所以我们引用的是M3的)。
在这里详细解释了我们滴答定时器的四个寄存器,包括相关的控制位功能,我们需要了解这些东西,但是没有必要去背他,我们只需要知道一个学习的方法,在以后需要用到的时候我们懂得找就可以了。毕竟以后当你出去到企业之后,公司做项目,不可能让你去背一个寄存器的每一位每一位是什么意思,不大现实,也毫无意义。所以,方法很重要,要掌握方法。
我们对比到432手册中给出的寄存器描述,同样有四个寄存器,后面内容有点多,这里就不在截图出来了,通过对比我们会发现两者其实是一模一样的(毕竟是同一家出的东西嘛,ARM)。
按照432中给出的配置步骤:
1.我们第一步需要配置STCVR这个寄存器,也就是我们的SysTick->LOAD,第一步要给出重装载的值,这里我们就要明白,这个重装载的值是24位数据,那么我们就可以计算出我们对应的最大的延时数。24位的寄存器最多可以数16_777_216个数据,那么我们根据我们的时钟频率就可以计算出我们的延时时间。如果是48MHz的话,我们最多可以数349_525这么多us,相应的就可以数34.9ms的时间。所以在这个频率下我们要控制我们的延时时间,不能过高。一般情况下这么长的延时时间我们也是够用的。可以看出48MHz的频率来数数,频率还是有点太高,要获得更长的延时,就要降低时钟频率。
2.接下来我们需要配置我们的STCVR这个寄存器,这个寄存器对应我们的SysTick->VAL,是我们当前寄存器的值,我们需要把它清零。
3.最后就是控制状态寄存器了,上面我们已经说明了,需要使能SysTick这个模块,同时把时钟源控制位置一。
这样我们完成了所有的配置工作,这里我们还需要明白的是SysTick是一个24位的自动重装载寄存器,我们使能了他,他就会从STCVR这个装载寄存器中载入要计数的值,然后一直向下数,而不是向上数,一直数到0,数到0之后会置位状态寄存器中的COUNTFLAG标志位,我们也是开启了滴答定时器之后一直查询该标志位,看看是否数完了。
temp&0x01这个查询的是SysTick模块时候使能,查询的是最低位,为什么用&呢?和我们上面说的置1和置0有点类似,实在不行自己写一两个数据验证一下也就明白了。
temp&(1<<16)这个查询的就是我们上面所说的COUNTFLAG这个标志位,那为什么这里1要左移16位呢?我们看寄存器的描述就可以知道,该位是该寄存器的第16位,所以要左移16位来进行相与。
需要同时满足temp&0x01&&!(temp&(1<<16)),这两个条件才可以,完成之后就会退出while循环,然后我们关闭SysTick这个模块,因为他是个自动重装载寄存器,不关闭的话,他会再次从重装载寄存器中取出数据继续数数。
后面的ms延时也是一个道理。就不在解释说明了。
补充说明一个问题,就是我们在配置SysTick->CRTL这个寄存器的时候使用了一个这样的宏定义,如下,前面出现了一个1UL,那么这个UL是什么意思呢?1UL这里指的是无符号长整型数字1,如果不写后缀名的话,系统默认为int型。那为什么这里需要用长整型,是因为这是一个32位的寄存器,整型没有办法表示这么大的需要一个长整型的才可以。后面的SysTick_CTRL_ENABLE_Pos,道理和我们上面的说的查询COUNTFLAG的道理是一样的,左移。
现在我们要验证一下时钟源到底是哪一个?看手册中的FCLK根本不知道是什么我们通过实际操作验证一下就知道了。
这里我们采用的是控制变量法,总共有ACLK、BCLK、HSMCLK、MCLK、SMCLK这几个时钟,把其中一个设置为高频率,其他的都设置为低频率,然后点一个灯,目测下闪烁频率就知道是哪个了。
我们之前已经推算出可能会是MCLK,所以一开始我就直接改了MCLK的频率,很明显的看出灯的闪烁频率发生了很大的改变,一开始我们都是以48MHz为基准,之后我修改为32KHz,中间的落差很大,所以确定SysTick的时钟来源是MCLK,那么现在我们就可以很快的确定我们改如何使用和配置SysTick的延时了。
中间的修改代码的过程和推理判断就不在说明了。大家只要记住结论就好了,SysTick的时钟是来自MCLK。
这里在分享一个调试的技巧,如果你不确定你的配置代码是否正确,需要从寄存器的级别来进行查看的话我们可以通过CCS的debug窗口中找到对应的寄存器,点开之后会有每一位的变化情况,暂停代码运行既可查看每一位的运行情况。
我们可以得到这样的结果。并且旁边还给出了相应的位进行解释,非常的方便。
最后我们的代码改成下面的,把我们的MCLK的频率选择为高速时钟,在进行16分频,这样就得到比较低的3MHz的频率,这样子就可以进行较长的延时。
同时我们对延时的初始函数进行了修改,这样方便大家理解和使用。