那么,每次都要将2进制的数转换为16进制的数。是不是很容易出错?那么,有什么更简单的方法呢?其实,我们只要将16进制的0x1向左移动15位就可以了。接下来我们要将PA15这一位单独置0。我们都知道0与任意值做与运算都等于0,而1与任意值做与运算都等于这个值本身。这时将GPIO A ODR这个指针指向的值与这个数做与运算。
接着,我们往上翻来找一下每个寄存器的位含义。首先,来看这里的STK_CTRL寄存器。它的的全称是 SysTick Control and Status Register。通常译为SysTick 控制与状态寄存器。STK_CTRL寄存器别看它32位。只有4位起作用,第0位是计数的使能位,也就是CTRL这个寄存器的总开关,所以这一位是一定要置1的。
中文译为SysTick 当前值寄存器。它实时记录被倒计时的当前值。STK_CALIB全称SysTick calibration value register。通常译为SysTick校准值寄存器。这里我们可以看到前面三个寄存器这里都是rw,也就是可读可写,而这个寄存器只有一个r,也就是这个寄存器是只读的,它是芯片出厂时就写好的,告诉你一些硬件特性。
计数完成标志位。这是一个状态标志位。如果定时器已经完成了一轮计数,减到零了,这一位就会被硬件自动置1。你可以通过读它来查询定时是否完成?接着,来看 S T K L O A D这个寄存器。它的全称是 SysTick reload value register。通常译为SysTick 崇装载值寄存器。
它是一个24位的值。RELOAD值可以是0x00000001至0x00FFFFFF范围内的任意值。这里放的就是要倒数到0的那个数。STK_VAL全称 SysTick Current Value Register。中文译为SysTick 当前值寄存器。它实时记录被倒计时的当前值,STK_CALIB全称SysTick calibration value register。
通常译为SysTick校准值寄存器。这里我们可以看到前面三个寄存器这里都是r w,也就是可读可写,而这个寄存器只有一个r,也就是这个寄存器是只读的,它是芯片出厂时就写好的,告诉你一些硬件特性。比如,在标准参考时钟下,实现1毫秒定时需要多少计数值。位31称作无参考时钟标志位(N O R E F)。
当读取到N O R E F = 0 时,代表SysTick可以选择使用处理器时钟 (A H B) 也可以使用。外部参考时钟 (H C L K除以8),而当读取到N O R E F = 1 时。代表SysTick只能使用处理器时钟 (A H B)。由于我们这里的时钟源是N O R E F = 0 的这种情况,所以,当我们读取这一位时肯定是0。
位30被称作校准偏差标志位。它是用来指示TENMS值的精确度的,S K E W = 0时,T E N M S 值精确。S K E W = 1时T E N M S 值不精确。S t m 3 2 f 1 0 3 系列在出厂校准时。SysTick校准值是基于内部 R C 振荡器测量的,也就是H S I,H S I本身的精度并不高。
所以,位0到位23存储的是1毫秒校准值。它这里显示的是在外部参考时钟H C L K除以8下。产生1毫秒定时所需的计数值。以HCLK最高72兆赫兹为例。TENMS等于9000。STK_CALIB这个寄存器使用频率并不高。所以,我们只做了解就可以了。光说不练假把式。咱们来实战配置一下寄存器,让SysTick精确地每1毫秒完成一次倒数。
这里我们默认H C L K为72兆赫兹的最大频率。前面我们说过72兆赫兹相当于72分之一微秒就记一次数。那么1毫秒需要记72000个数。但是,这里的零也算一个数。比如,我们要记10个数,记0到9就可以了。所以,这里的72000要减去一个1。这时的计数值就是1毫秒。所以,重载值寄存器这里就存入71999这个数。
紧接着有一个 if 判断:如果延时值 wait 小于一个延时最大值。则会将 wait 自增一个数,这个数就是1,这稍后解释。最关键的是while循环,其条件正是。当前时间减去开始时间小于需要等待的时间,只要条件成立,程序就在这里循环。模拟“等待”的过程,这也就是我们前面说的“看表”动作。
循环中调用了 HAL GetTick() 来获取“当前时间”。它返回的是一个名为 uwTick 的全局变量。到这里貌似到了一个死胡同了。但是,靠前面知识的积累。直觉告诉我们这个uwTick肯定是一个每隔1毫秒递增一个数。这样才能起到计时作用。带着这个疑问。我们从程序启动的地方开始寻找答案。在 main 函数之前。
系统会调用 HAL_ Init() 进行初始化。在这里,我们一眼就能看到带有“Tick”字样的函数——HAL_InitTick。点进去看看。它内部调用了 HAL SYSTICK_Config,这里传入的值是 8000。然而,在跟踪代码时我们发现。这里不就是上期视频讲到的滴答定时器的相关寄存器吗?
给config赋值。然后,就跳出switch选择语句了。这里的 ? :是 C 语言中的条件运算符,也叫三元运算符。这个也很简单,当问号前的条件满足就返回冒号后的第一个值。条件不满足就返回冒号后的第二个值。由于这里的iocurrent中的值(0x8000)大于GPIO_PIN_8(0x0100),所以,条件不成立。
hello,小伙伴们,大家好,这里是左左右,在前面的第8期视频中,我们用STM32CubeIDE通过对I O口的简单配置,和几行简单的代码,实现了按钮控制L E D灯亮灭的一个小案例,这期视频我们就使用Clion来实现相同的功能,与STM32CubeIDE开发的不同之处在于,我们要用自己写代码来配置I O口,通过原理图不难发现,按钮一端与PC13相连,另一端接地接地,当按钮被按下时,PC 13对应 I D R寄存器被置0,当按钮抬起时,PC13引脚受外部干扰,输入电平不确定,这时我们要将 PC 13引脚设置为上拉输入模式,提供一个相反的电平信号,接着我们就通过可以读取 PC 13对应 I D R寄存器中的值来判断,按钮是抬起还是按下状态,接着来看原理图,LED1一端与PA 15相连接,另一端接3.3V,当我们把PA 15对应 O D R寄存器被置0时,PA15向外输出0V的低电平,小灯两端形成电压差,小灯被点亮,当我们把PA 15对应ODR寄存器被置1时,PA 15向外输出3.3V的高电平,小灯两端无电压差,小灯熄灭,这时我们通过判断 PC 13对应 I D R寄存器中值的变化,来操作PA15对应 ODR寄存器中的值,就实现了按键控制小灯亮灭的功能了,好多小伙伴们会问,为什么这里是PC 13和 PA 15这两个引脚,其他引脚不可以吗?
这是因为我们的电路板就是这样设计的,在设计电路板前要先绘制原理图,规划s t m 3 2芯片各个引脚要与什么样的元器件连接,实现什么样的功能,接下来才是绘制电路板,当电路板生产完成后,原理图就成了这块电路板的说明书了,因为直接看电路板是看不出来具体引脚的连接方式的。
接着打开我们上期视频建好的CLion项目,将它复制一份出来,打开项目文件删掉文件名为.idea的文件夹,这个文件夹包含了旧项目的所有CLion专属配置,在新项目中使用会导致冲突。紧接着打开Clion,点击新建项目,位置这里输入我们刚才的路径,点击继续,接着将之前写好的代码全部删掉,这时这个项目就是个模板项目了,同样我们打开这个模板项目的所在位置,删除.idea的文件夹,之后每次我们在创建新项目时,把它复制一份出来就可以了,接着打开复制好的项目,首先采用寄存器的写法来实现按键控制 L E D小灯亮灭的案例,这里为了方便大家的理解,我就直接就用二进制数了,首先将 PC 13引脚设置为上拉输入模式,这里我们定义指针指向 GPIOC_IDR寄存器地址,这是因为待会我们要读取这个寄存器中值来判断按键是否被按下,接着将PA15设置为推挽输出模式,然后在whlie循环中判断按键是否被按下,当 PC13对应 IDR寄存器值为0时,说明按键被按下了,由于按键刚被按下时,会有发生抖动,这时我们加一个10毫秒的延时将这段抖动延时过去,接着继续判断按键状态如果被按下,就翻转 PA15电平信号,也就是翻转PA15对应ODR寄存器中的值,这里我们要增加一个新的知识点,前面我们讲过位的“或”运算和“与”运算,这里我们讲一个新的位运算符,那就是位的“异或”运算,符号是这样,也很简单,两个值相同就为零,不相同为1,当 PA15对应 ODR中对应值为1时,与这个值做异或运算,结果为0,当 PA15对应 ODR中对应值为0时,与这个值做“异或”运算,结果为1,而且对其他位没有任何影响,是不是很巧妙的实现了电平翻转效果,接着等待按键被抬起,就跳出while循环,虽然我们在按下按键到抬起按键的时间很短暂,但程序的执行速度远大于这个时间,所以这里加个while循环做等待,最后就是用Hell库实现相同的功能,这个就很简单了。
首先,定义变量。接着,检测传入参数的合法性。接下来就是一个while循环了。在循环里藏着两个 if 判断,当条件成立时进入判断语句,条件不成立时。执行position++的操作。观察完整体结构。咱们就从头开始。一行一行地把这个函数读懂。这里一共定义了5个变量。分别是position,iocurrent,tmp,registeroffset。