1. 所选任务介绍
本项目旨在设计并实现一个基于ADMT4000角度传感器的精密升降台控制系统。该系统以RP2040微控制器为核心,通过采集ADMT4000角度传感器的实时数据,实现对FS90R舵机转速和转向的精确控制,从而驱动升降台完成精确的高度调节。
任务的核心难点在于将角度传感器的反馈数据与舵机控制相结合,形成一个闭环控制系统。通过ADMT4000传感器的绝对角度数据(ABS)和相对角度数据(REL),系统能够精确感知舵机转动的实际角度,并在达到目标角度时自动停止,确保每一次升降的高度一致性。
本次的模拟任务场景是可控的升降台控制。
2. 项目描述
2.1 项目概述
本项目设计了一个基于ADMT4000角度传感器的闭环升降控制系统。系统以RP2040微控制器为主控芯片,通过SPI接口同时驱动两个外设:ST7789显示屏用于人机交互界面,ADMT4000角度传感器用于实时监测舵机转动角度。控制信号通过PWM输出至FS90R 360°舵机,实现精确的角度控制。
系统的核心控制逻辑为:当用户按下UP按键时,舵机以设定速度正向转动,同时角度传感器实时采集ABS角度数据;当ABS角度变化达到预设阈值(约360°,对应一圈)时,系统自动控制舵机停止,从而确保每一次转动的角度精确可控。反转也是一样的控制逻辑。
2.2 主要功能
- 角度实时监测:通过ADMT4000传感器的SPI通信,实时采集相对角度(REL 0-360°)和绝对角度(ABS,包含圈数计数)数据。
- 舵机精确控制:采用50Hz PWM信号控制FS90R舵机,通过调整PWM脉宽(1.1ms-1.8ms)实现正转、停止、反转三档控制。
- 闭环反馈机制:利用角度传感器的ABS数据作为反馈信号,当转动角度达到目标值时自动停止,无需时间计时。
- 人机交互界面:通过ST7789 240×240显示屏显示实时数据,并通过UP、CENTER、DOWN、CONFIRM、RETURN五个按键进行操作控制。
- 传感器复位功能:通过COIL_PS、SHDN、RST三个控制引脚实现ADMT4000传感器的线圈复位操作。
3. 硬件介绍
3.1 主控平台 - 十二指神探(RP2040)
本项目采用基于树莓派RP2040芯片的开发平台作为主控制器。RP2040是树莓派基金会推出的一款高性能微控制器,搭载双核ARM Cortex-M0+处理器,主频可达133MHz,内置264KB SRAM和2MB Flash存储空间。
在本系统中,ST7789显示屏作为唯一的人机交互界面,实时显示传感器数据和系统状态。界面设计采用模块化布局,包括标题栏、数据区、状态栏三个部分。
在本项目中,RP2040的SPI0用于驱动ST7789显示屏,SPI1用于与ADMT4000角度传感器通信,PWM通道用于控制FS90R舵机,GPIO用于按键输入和复位控制。
3.2 角度传感器 - ADMT4000
ADMT4000传感器在本系统中的作用是实时监测与反馈。通过读取ABS_ANGLE寄存器的高10位(bit9-bit0)获得当前圈数内的角度值,通过bit15-bit8获得圈数计数(TurnCnt),两者结合可计算出在传感器整个生命周期内的累计转动脉冲数,实现精确的角度闭环控制。
ADMT4000传感器需要通过外部复位电路完成线圈充放电过程,以实现角度零位校准。该电路由三个控制引脚组成:
- COIL_PS (IO22):线圈电源控制,低电平关闭,高电平使能
- SHDN (IO23):关断控制,用于切断传感器电源
- RST (IO24):复位信号,控制复位时序
3.3 舵机 - FS90R
FS90R是一款360°连续旋转微型舵机,相比传统180°舵机,它可以实现连续正反转控制,非常适合需要精确转速和转向控制的场景。
PWM控制信号:
脉宽 | 舵机动作 |
1.1ms (3600) | 慢速正转(当前设置) |
1.5ms (4915) | 停止(中立位) |
1.8ms (5900) | 慢速反转(当前设置) |
FS90R舵机在本系统中作为执行元件,将电信号转化为机械转动。舵机输出轴通过联轴器与机械升降台连接,PWM信号的占空比决定了舵机的转速和转向。
4. 方案框图与设计思路
4.1 系统总体架构

4.2 闭环控制原理
本系统采用闭环反馈控制策略,以ADMT4000传感器的ABS角度数据作为反馈量,与目标角度进行比较,形成偏差信号,进而控制舵机的启停。
控制算法流程:
这种基于角度反馈的停止判据相比时间控制具有更高的精度和可靠性,不受舵机负载变化的影响。
5. 软件设计与实现
5.1 主程序流程图

5.2 关键代码说明
舵机控制模块
# 舵机脉宽对应的duty_u16值 (0-65535)
# 50Hz周期=20000µs, duty = 脉宽/20000 * 65535
DUTY_FORWARD = 3600 # 1.1ms - 慢速正转
DUTY_STOP = 4915 # 1.5ms - 停止
DUTY_REVERSE = 5900 # 1.8ms - 慢速反转
def set_servo(state):
"""设置舵机状态"""
global servo_state, servo_start_abs, servo_running
servo_state = state
if state == SERVO_FORWARD:
servo_pwm.duty_u16(DUTY_FORWARD)
servo_start_abs = sensor.read_abs_angle()[1] # 记录起始ABS
servo_running = True
elif state == SERVO_STOP:
servo_pwm.duty_u16(DUTY_STOP)
servo_start_abs = sensor.read_abs_angle()[1] # 更新基准位置
servo_running = False
else: # SERVO_REVERSE
servo_pwm.duty_u16(DUTY_REVERSE)
servo_start_abs = sensor.read_abs_angle()[1] # 记录起始ABS
servo_running = True
duty_u16()函数能够接收一个范围在0至65535之间的数值,该数值对应着PWM占空比从0%到100%的调整。而servo_start_abs则用于存储每次启动时的ABS值,以此作为角度计算的基准点。在停止运行时,会更新这一基准位置,从而确保下一次的转动能够以上一次停止的位置作为起点。
角度数据读取与闭环判断
# 主循环中的角度判断
current_angle = sensor.read_angle() # 读取相对角度 0-360°
current_turn, current_abs = sensor.read_abs_angle() # 读取绝对角度
# 检查舵机转动是否完成
if servo_running:
abs_delta = abs(current_abs - servo_start_abs)
if abs_delta >= TURN_ABS_DELTA: # TURN_ABS_DELTA = 355
set_servo(SERVO_STOP) # 停止舵机
调用`sensor.read_angle()`函数可获取0至360度范围内的相对角度值;而`sensor.read_abs_angle()`函数则返回一个元组`(TurnCnt, ABS)`,其中`ABS`的计算方式为`TurnCnt`乘以360度再加上相对角度`REL`。通过对比当前`ABS`值与起始`ABS`值的差值,能够判断是否已完成一整圈的旋转。在此过程中,将阈值设定为355度,以预留一定的余量,从而有效防止过冲现象的发生。
智能显示刷新
def format_num(value, digits):
"""格式化数字,保持固定位数,不足的补空格"""
s = str(int(value))
while len(s) < digits:
s = " " + s
return s
def update_display(angle, abs_angle, turn_cnt):
"""更新主页面数值 - 仅刷新变化的值"""
global last_angle, last_abs, last_turn
if angle != last_angle:
display.text(font2, format_num(angle, 3), 55, 52, st7789.WHITE)
last_angle = angle
if abs_angle != last_abs:
display.text(font2, format_num(abs_angle, 5), 55, 87, st7789.WHITE)
last_abs = abs_angle
if turn_cnt != last_turn:
display.text(font2, format_num(turn_cnt, 2), 55, 122, st7789.WHITE)
last_turn = turn_cnt
运用变长补空格的精妙策略,有效规避了数值变动时可能出现的字符残留问题。系统在首次展示数据后,会智能记录当前数值,后续仅在检测到数值发生实际变化时,才会触发刷新操作。其中,ANGLE字段设定为3位宽度、ABS字段为5位宽度、TURN字段为2位宽度,这样的固定宽度设计既贴合实际数据范围,又保证了界面展示的清晰与整洁。
按键去抖与响应
def check_buttons():
global current_page, last_return, last_confirm
# ... 按键引脚定义 ...
return_pressed = return_btn.value()
confirm_pressed = confirm_btn.value()
up_pressed = up_btn.value()
center_pressed = center_btn.value()
down_pressed = down_btn.value()
# 下降沿检测 - 按键按下瞬间触发
if return_pressed == 0 and last_return == 1:
if current_page == 0:
current_page = 1
draw_settings_page()
else:
current_page = 0
draw_main_page()
# 舵机控制 (仅在主页面)
if current_page == 0:
if up_pressed == 0 and last_up == 1:
set_servo(SERVO_FORWARD)
update_servo_status()
if center_pressed == 0 and last_center == 1:
set_servo(SERVO_STOP)
update_servo_status()
if down_pressed == 0 and last_down == 1:
set_servo(SERVO_REVERSE)
update_servo_status()
# 更新上次状态
last_return = return_pressed
last_confirm = confirm_pressed
运用状态机设计理念,通过对比当前数值与前一次数值来精准检测下降沿,确保仅在按键被按下的瞬间触发响应,有效规避长按导致的重复操作。同时,将页面切换逻辑与功能操作代码相分离,显著提升代码的可维护性。
6. 实物演示及说明
实物连接

硬件连接顺序:
1. 主控板:RP2040开发板经USB线连电脑,确保供电正常且能识别串口设备;
2. 角度传感器:ADMT4000传感器经SPI1接口连:GPIO26→SCK,GPIO27→MOSI,GPIO28→MISO,GPIO25→CS,单独供电;
3. 舵机:FS90R舵机控制线连GPIO20,电源正极接5V外部电源,负极共地。
4. 复位电路:COIL_PS、SHDN、RST依次连GPIO22、GPIO23、GPIO24。
角度实时监测
系统上电后,显示屏自动进入主页面,实时显示三个角度数据:
- REL:相对角度,0-360°连续变化
- ABS:绝对角度,包含圈数计数,显示累计转动角度
- TURN:圈数计数,记录完整的旋转圈数
舵机转动控制

7. 心得体会
通过本次项目,我们深入学习了嵌入式系统中闭环控制的实现方法。相比于简单的开环控制(给定PWM信号后依靠时间或经验判断停止),闭环控制通过传感器反馈实现精确的位置控制,具有更高的可靠性和抗干扰能力。在人机交互界面设计上,我们采用了实时数据显示+按键控制的基本框架,并通过定时刷新和变化检测机制优化了显示效果,提升了使用体验。