一、项目介绍
使用ADMT4000模块和MCU进行旋转方向与转速检测,要求手动旋转磁铁,通过串口实时识别旋转方向(正转/反转)并计算转速。要求如下:
1. 读取 ADMT4000 角度数据,判断当前旋转方向(正转/反转),串口实时显示
2. 根据角度变化量和时间计算转速(转/秒或度/秒),串口实时显示
3. 静止时显示转速为 0,旋转时实时更新
4. 在 46 圈范围内验证方向和转速检测的正确性
二、硬件介绍
本项目主要由三个硬件模块:
1、MCU是ESP32S3,一款主打AIoT的高性能、低功耗双模无线 SoC 芯片,专为边缘计算、语音 / 图像识别和智能交互场景设计。具有多个GPIO端口和SPI通讯接口
2、ADMT4000模块:是一种磁转数传感器,即使在设备断电时也能够记录磁系统的旋转次数。通电时可以查询该套件,以报告系统的绝对位置。绝对位置通过串行外设接口 (SPI) 报告,ADMT4000 最多可计数 46 圈外部磁场。
3、SSD OLED显示屏:用于实时显示检测结果,是最常用、最经典的小尺寸单色 128*64的双色OLED 模块。
三、项目设计
项目主要分为下面几步,首先是配置GPIO引脚,然后进行硬件的初始化,在main中进行循环查询对应的圈数和角度值,接着进行方向和速度的计算,最后在串口及OLED中进行实时输出结果。

四、代码介绍
首次使用ADMT4000模块需要进行复位操作,通过BOOT键注册回调,当系统检测到BOOT被按下,执行线圈复位操作。
// 初始化复位功能
static void reset_init()
{
gpio_config_t io_conf = {
.pin_bit_mask = (1ULL << COIL_RESET_PIN),
.mode = GPIO_MODE_OUTPUT,
.pull_up_en = GPIO_PULLUP_DISABLE,
.pull_down_en = GPIO_PULLDOWN_DISABLE,
.intr_type = GPIO_INTR_DISABLE};
gpio_config(&io_conf);
gpio_set_level(COIL_RESET_PIN, 0);
button_config_t btn_cfg = {0};
button_gpio_config_t gpio_cfg = {
.gpio_num = BOOT0_BUTTON_PIN,
.active_level = 0,
.enable_power_save = false,
};
button_handle_t btn_handle;
esp_err_t ret = iot_button_new_gpio_device(&btn_cfg, &gpio_cfg, &btn_handle);
if (ret == ESP_OK)
{
iot_button_register_cb(btn_handle, BUTTON_SINGLE_CLICK, NULL, boot0_button_pressed, NULL);
printf("复位功能初始化完成 - 按BOOT0键复位线圈\n");
}
else
{
printf("按键初始化失败: %s\n", esp_err_to_name(ret));
}
}
下面是OLED的初始化,在IDF中可以使用menuconfig进行OLED的配置选择,包括SCL、SDA端口及分辨率等。
void ssd1306_init(SSD1306_t* dev) {
#if CONFIG_I2C_INTERFACE
i2c_master_init(dev,CONFIG_SDA_GPIO,CONFIG_SCL_GPIO,CONFIG_RESET_GPIO);
#endif // CONFIG_I2C_INTERFACE
#if CONFIG_SPI_INTERFACE
spi_clock_speed(CONFIG_CLOCK_SPEED_HZ);
spi_master_init(dev,CONFIG_MOSI_GPIO,CONFIG_SCLK_GPIO,CONFIG_CS_GPIO,CONFIG_DC_GPIO,CONFIG_RESET_GPIO);
#endif // CONFIG_SPI_INTERFACE
#if CONFIG_FLIP
dev._flip=true;
#endif
#if CONFIG_SSD1306_128x64
_ssd1306_init(dev,128,64);
#endif // CONFIG_SSD1306_128x64
#if CONFIG_SSD1306_128x32
_ssd1306_init(dev,128,32);
#endif // CONFIG_SSD1306_128x32
}
int ssd1306_get_width(SSD1306_t* dev) {
return dev->_width;
}
其次是每秒钟进行检测选择的圈数和角度值,根据当前的圈数值和上次的值进行比较,从而确定旋转方向dir是正向(+)还是反向(-),同时根据圈数差值乘以360度,转换为度数的差值,由于每次计算的时间差为1秒,因此得出了旋转速度单位为度/秒。最后的代码是清空OLED的显示,把最新结果显示在屏幕上面。
const char *dir ="+";
float speed = 0;
if(!isFirst){
speed = (turns - lastTruns)*360/1.0;
if(speed>0){
dir = "+";
}
if(speed<0){
dir = "-";
}
if(abs(speed)<1){
dir = "STOP";
speed = 0;
}
printf("单圈角度: %.2f°, 方向: %s, 速度:%0.1f度/秒\n",
single_turn_angle, dir, speed);
}
isFirst = false;
lastTruns = turns;
// 清除显示
ssd1306_clear_buffer(&dev, false);
char text[32];
snprintf(text, sizeof(text), "turns: %.2f", turns);
ssd1306_draw_ASCII_string(&dev, 0, 2, text, &afont16x8, false);
snprintf(text, sizeof(text), "direction: %s", dir);
ssd1306_draw_ASCII_string(&dev, 0, 22, text, &afont16x8, false);
snprintf(text, sizeof(text), "speed: %.1f", speed);
ssd1306_draw_ASCII_string(&dev, 0, 42, text, &afont16x8, false);
ssd1306_show_buffer(&dev);
五、实物演示
ADMT4000通过SPI和ESP32S3进行连接通讯,OLED采用I2C与主控进行交互,当主控ESP32S3检测到数据后,进行简单的计算把结果进行输出到OLED。


六、难点及体会
由于是手动旋转磁铁进行项目检测,因此磁铁旋转位置不太好控制,磁场会出现偏移导致结果有时候会不太正确,后期应该把磁铁固定在一个可旋转的物件上进行实践。第二个就是OLED显示只显示了英文,后续可以增加中文的显示,效果会更好一些。