[i=s] 本帖最后由 WildboarG 于 2025-2-20 17:30 编辑 [/i]
用Ai-Thinker-WB2做一个简易的编码器

引言
最近在搬运美化官方提供的API, 搬运的API已经完成,还有汉化中文版!方便我这种没过46级的小白快速参考!顺带又把我收藏吃灰的AI-WB2-12F拿出来,试着用它做一些简单可玩的项目,加深一下理解。
ps 一直在入门啊!
关于
最近刚好在自制简易的编码器,顺道分享一下我的思路,有更好的方案,欢迎评论区讨论分享。
既然是再一次入门,那就再搬一次园长和各位大佬的帖子,带大家再捋一下WB2的API,如有不足,还请各位指正。
GPIO
概念
通用型之输入输出(General-purpose input/output,通常称为GPIO),GPIO管理功能提供GPIO控制寄存器,实现软件对 GPIO 属性的配置,使用户能够方便地操作 GPIO。每个GPIO可以配置为输入、输出和可选功能三种模式。在每个模式下(除模拟可选功能),提供设置上拉,下拉,浮空三种端口状态,此外GPIO还提供中断功能,可以配置为上升沿触发,下降沿触发或者高电平/低电平触发。
Ai-WB2的 GPI0主要特点:
- 配置为普通输入输出功能,该模式下可以设定上拉,下拉或者浮空输入输出
- 配置为可选功能,搭配外设功能使用,可以设定上拉,下拉,在使用模拟功能时,必须设置为浮空
- 设置驱动能力,以提供更大的输出电流
- 设置施密特触发器功能,提供简单硬件防抖功能
每个GPIO可以通过软件配置为:
- 高阻输入
- 上拉输入
- 下拉输入
- 上拉中断输入
- 下拉中断输入
- 高阻中断输入
- 上拉输出
- 下拉输出
- 高阻输出
- 模拟输入可选功能
- 模拟输出可选功能
- 数字可选功能
GPIO的功能框图如下:

每个GPIO基本上都可以选择一些可选功能:

不同于STM32将复用功能放在指定引脚上,WB2的可选功能有些可以定义在任意引脚上
比如:stm32串口uart1 的发送接收功能一般就固定在GPIO PA9 PA10的复用功能上,但是WB2 是可以任意指定;假如GPIO0 复用功能选择UART,默认与之对应串口的RTS功能,加入我想强制他是UART发送的功能,只需要UART_SIGO_SEL 设置为6
功能
- 通用输入输出
- 可选与外设(uart,adc,dac,spi)搭配使用
- 中断
举个栗子:
如何将WB2的各个针脚设置为普通gpio功能:
- 通过设定func_sel 选定GPIO 为 SWGPIO
- 设置输入输出方向
- IE:INPUT enbale 输入使能 ,1有效 0无效 | OE:OUTPUT enable输出时能
- IE和OE只能选一个有效,不然就冲突了!!!
- 还可以通过SMT控制位来决定是否启用施密特触发器(这个玩意很有用,硬件过滤抖动)
- 通过PD PU决定是否开启上拉
- 最后如果是输入,那个就通过读取GPIO_I寄存器对应的位获取电平。如果是输出,引脚就会通过GPIO_O寄存器设置对应的电平
- 可选功能亦是如此
相关API
不知道函数怎么用就去看对应的GPIO API 有相应的注解
硬件准备
- AI-WB2-12F (多一些针脚的也行)
- RGB 板载自带
- 数据线
- 杜邦线
- 红外对射模块AEDS-9300
- 光栅板
- 比较器LM393
需求分析
编码器本质就是一个速度很快的计数器,所以我打算采用GPIO的外部中断实现计数,再配合光栅旋转不停的遮盖红外灯,实现一个简易的编码器。
可能会存在的问题: gpio中断抖动误触发导致计数值大的离谱
但是! WB2可是内置了施密特触发器,施密特触发器硬件防抖。搞!
原理分析

- 当光栅遮住红外对射模块,接收信号不导通,导致2IN+ 为高电平,假设电源VCC 是5V 那么2IN+此时就是5V
- 2IN-一直保持为2.5V (比例分压)
- LM393是比较器,工作原理就是正极大于负极时out端输出高电平,同理,负极大于正极out输出低电平。
- 那么,当遮挡光电开关2OUT输出高电平,led熄灭
- 相反,拿开2OUT输出低电平,LED点亮
用上图连接我的WB2 OUT1连接到我的中断触发引脚IO4 ,就可以通过中断触发来计数。
代码
#include <blog.h>
#include <hosal_gpio.h>
#include <stdbool.h>
#include <stdio.h>
#include <string.h>
#define LED_PIN 3 //用板子LED亮灭来观测是否触发了中断
#define TIGGER_PIN 4
// #define TAG "gpio_exti"
static bool led_state = false;
static hosal_gpio_dev_t led;
static hosal_gpio_dev_t tigger;
static int32_t counter = 0; // 计数值
static uint8_t value;
// 初始化灯
static void init_led(void) {
led.port = LED_PIN;
led.config = OUTPUT_PUSH_PULL;
hosal_gpio_init(&led);
printf("led inited\r\n");
}
// 中断回调
static void tigger_irq_callback(void *arg) {
uint16_t i = 1000;
while (i--)
;
hosal_gpio_input_get(&tigger, &value);// 软件延时防抖
if (value == 1) {
printf("tigger pressed:%d\r\n", counter++);
led_state = !led_state;
hosal_gpio_output_set(&led, led_state);
if (counter > 1024) { // 重新计数
counter = 0;
}
}
}
// 初始化触发引脚
static void init_pin() {
tigger.port = TIGGER_PIN;
tigger.config = INPUT_HIGH_IMPEDANCE;
hosal_gpio_init(&tigger); //初始化GPIO
hosal_gpio_irq_set(&tigger, HOSAL_IRQ_TRIG_POS_PULSE, tigger_irq_callback,
NULL); //开启GPIO中断以上升沿的方式触发中断
printf("tigger inited\r\n");
}
void main(void) {
init_led();
init_pin();
while (1) {
}
}
实物连接

- VCC接 3.3V
- GND接GND
- OUT1接IO4
不要问为什么这么简陋还飞线,问就是画反封装了
串口监控计数值

不停的遮挡光电模块就会通过中断触发计数
下一步制作光栅,把这玩意做成圆形的
后续思路
这个只是简单的计数编码器,不能分辨方向正反向,所以用两个错相位的光电模块,接入到D触发器来实现正反向的读取,在用一个光电模块对接光栅的定位孔来实现z脉冲。
