helllo,小伙伴们,大家好。这里是左左右。这期视频我们要通过使用exti外部中断来实现编码器的计次功能。首先,打开串口屏的上位机来快速的做一下屏幕相关功能。记一下这里的字符编码是GB2312,后面会用到。现阶段我们不追求美观,只为实现功能。老样子,先创建一个字库。点击工具箱中的按钮,将按钮命名为本期视频的序号。
在按钮的按下事件中,输入 page page1,代表跳转到page1。接着,新建页面并打开。点击工具箱中的文本,将其命名为计数器。点击工具箱中的按钮,将其命名为返回。在按钮的按下事件中,输入 page page0,代表跳转到page0。点击调试来看一下,就是要实现这样一个功能。
接着,来讲一下旋转编码器的工作原理。首先,打开我们原理图中的旋转编码器。编码器在旋转过程中会通过AB引脚向外输出方波信号。通过原理图不难发现,旋转编码器的AB引脚分别和stm32的PA0和PA1相连。旋转编码器旋转一下,不管是A引脚还是B引脚都会形成一个周期的方波信号,也就是一高一低两个电平。

这时我们只需选择PA0和PA1中任意一个引脚作为EXTI外部中断。选择上升沿或者下降沿来触发中断。每次触发中断,计数器加1,这样就能实现计数的功能了。那么,如何判断正反转呢,其实AB引脚产生的方波信号是有相位差的?当旋转编码器正转或者反转时,B产生的方波信号会比A提前1/4个周期,或延迟1/4个周期。
这里的正转和反转其实是相对的,我们将其中一种命名正转,另外一种就命名为反转。这时我们将PA0和PA1中两个引脚作为EXTI外部中断,选择下降沿来触发中断,当EXTI0进入中断时,PA1输入高电平,计数器加 1,代表旋转编码器正转。当EXTI1进入中断时,PA0输入高电平,计数器减 1,代表旋转编码器反转。
原理这部分弄明白了,就打开我们的cubeide开始写代码吧。老规矩,把之前串口屏那期视频的项目复制一份出来。打开ioc文件,将PA0和PA1分别设置为EXTI0和EXTI1。接着,打开GPIO,在GPIO mode下拉菜单中可以看到中断响应和事件响应。这就是我们中断那期视频中讲的EXTI外部中断的响应方式。

这里的上升沿、下降沿、上升/下降沿。就是我们中断那期视频中讲的EXTI外部中断的触发方式。我们选择响应方式为中断响应,触发方式为下降沿。接着,点击NVIC,点击中断分组的下拉菜单。是不是和我们中断那期视频也是一模一样的?这里选择4位抢占优先级和0位响应优先级这个分组。接着,来设置一下他们的抢占优先级的值。
点击保存。打开main.c这个文件。随着项目越来越复杂,main.c中的函数越来越多了,听过上期视频的小伙伴们应该一下子就想到解决办法了。这里.c和.h文件在cubeide中只需要设置一下就可以自动生成了。接着,顺便把字符编码也设置成GB2312。打开自动生成的.c和.h文件。
是不是和我们上期视频自己写的头文件一模一样的?打开这个it.c文件。在里面找到EXTI0_IRQHandler和EXTI1_IRQHandler这两个函数,前面视频我们讲过,中断是一种硬件机制,也就是说EXTI0中断被触发后,硬件会让程序自动跳转到EXTI0_IRQHandler这个函数中。


所以,里面写我们想要实现的功能。这个函数是检查外部中断(EXTI)标志位的状态。这里判断如果EXTI0的外部中断标志位为真就执行大括号里的内容。接着,在这里写入我们刚才判断旋转方向的逻辑。
首先,判断PA1是不是高电平?如果是高电平,就是正转。我们执行count++。由于count这个变量还没有定义,我们在前面定义一下,大家看到int16_t是不是傻眼了?之前讲c语言数据类型可没讲过。可是在讲数据类型那一期视频我们讲过。定义数据类型的过程就是和内存要空间的过程。
这样可以有效提高内存空间的利用率。在c语言中int占32个bit,而stm32的内存是很小的,有时候要定义的数很小,要32个bit有点太浪费空间了。于是在就有了int8_t,int16_t,int32_t等等。其中int8_t占用8个比特的空间。可以代表2的8次方数,也就是0-255。
由于int8_t中也有负数,所以,通常用于存储范围在-128到127之间的整数。同理可以推出int16_t,int32_t。这里用到sprintf这个函数。所以,我们要包含stdio.h这个标准库。这里的sprintf的意思是将双引号里的内容赋值给str,而%d就代表count,和我们之前常用的printf类似。
由于%d代表的count是整数型,我们给他加个双引号转换成文本型。如果这里直接使用双引号,编译器就会误以为前面这个双引号是一部分。后面这个双引号是另一部分,就会报错。所以,我们要在引号前加个反斜杠,\"也是一个转义字符,他还是代表双引号。但是,不会被编译器误判。这里的\x后面代表十六进制数,这里是ff我们可以直接判断这是是16进制数。
如果是10呢,编译器也不知道你写的是二进制数,还是十进制数。又或者是16进制数。这里的\x也是一个转义字符。3个\xff是串口屏的结束符。它与uart那期视频讲的帧数据中结束符作用是一样的。假设这里的count是整数1,sprintf这个函数相当于完成了这样一行代码。
接下来写我们熟悉的发送数据到串口屏的函数。这个字符串对于串口屏来说。就是设置page1下的控件t0显示为变量count中的值。这里的strlen返回的是str这个字符串中字符的个数。想要用这个strlen就要包含string.h这个标准库。接着,往下看他这里自动生成的这个库函数代表什么意思?
这个的库函数就代表清除中断标志位,如果没有这行代码程序会一直卡在中断里,返回到刚才的位置。照猫画虎,把EXTI1_IRQHandler里的内容也补全了。这里的huart1在这个文件中并没有定义。为什么可以直接用呢?这是因为它在usart.c这个文件中定义过了。但是,就算是在这里定义过了也不可以跨文件使用。
要想跨文件使用就要在usart.h文件中将定义的huart1前面加个extern的关键字。这里cubeide已经帮我们写好了。这里的判断其实是一个双保险的写法。既然能进入EXTI1_IRQHandler这个函数。就说明产生了EXTI1的外部中断。
那么EXTI1的外部中断标志位就肯定为真。所以,这个判断就永远为真。这样写还有另外一个原因。假如我们用到的是PA11和PA12这两个引脚为外部中断引脚,点击保存自动生成代码。这时我们会发现EXTI10-15写在一个函数中了。刚才那个判断就有他的用武之地了。我们可以通过这个判断得知是哪个引脚触发了中断。

好的,我们把刚才设置的引脚恢复原状态。接着,我们回到主程序,这里也不能让主程序闲着,让它每秒钟执行一次i++的操作,也就是说程序正常状态下会在主程序中循环执行i++的操作。只有外部中断被触发了,才会由硬件自动跳转到对应的中断函数。
好的,我们来运行试一下。报了一些错,我们查看原因并解决。写代码的过程就是边调试边改的一个过程,大家要有耐心。动起手来,不动手永远学不会。连接好我们的串口屏虚拟机和仿真就可以实现这样的功能。大家可以观察一下示波器里的波形和我讲的是否一致?
同时,我把最小系统板的接线图也放到这里供大家参考。好的,今天的视频就到这里了。您的关注,点赞和收藏是我持续更新下去的最大动力。我们下期见。