19.一节课讲明白旋转编码器计次
hello,小伙伴们,大家好,这里是左左右,这期视频我们要通过使用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++的操作,只有外部中断被触发了,才会由硬件自动跳转到对应的中断函数,好的,我们来运行试一下,报了一些错,我们查看原因并解决,写代码的过程就是边调试边改的一个过程,大家要有耐心,动起手来,不动手永远学不会,连接好我们的串口屏虚拟机和仿真就可以实现这样的功能,大家可以观察一下示波器里的波形和我讲的是否一致,同时我把最小系统板的接线图也放到这里供大家参考,好的,今天的视频就到这里了,您的关注,点赞和收藏是我持续更新下去的最大动力,我们下期见