1. 项目概述
在嵌入式Linux开发中,控制步进电机是一个常见但颇具挑战性的任务。本文将详细介绍如何在RK3568平台上移植ULN2003驱动板来驱动28BYJ-48步进电机的完整过程。这个项目不仅涉及Linux设备驱动的开发,还需要深入理解硬件接口和电机控制原理。
28BYJ-48是一种常见的低成本步进电机,广泛应用于各种小型自动化项目中。它采用四相五线结构,配合ULN2003驱动板使用,可以方便地通过GPIO控制。RK3568作为一款性能强大的ARM64处理器,运行Linux 5.10内核,为这类控制应用提供了良好的平台。
2. 硬件环境搭建
2.1 硬件组件介绍
主控芯片:RK3568采用ARM64架构,四核Cortex-A55设计,主频可达2GHz。它内置丰富的GPIO资源,非常适合嵌入式控制应用。我们使用的是Linux 5.10内核版本,这个内核版本对RK3568的支持已经相当完善。
驱动芯片:ULN2003A是一款高压大电流达林顿晶体管阵列,内含7个NPN达林顿管。每个通道可提供500mA的驱动电流,并内置续流二极管,特别适合驱动感性负载如继电器、直流电机和步进电机。
步进电机:28BYJ-48是一款5V四相五线步进电机,减速比为1:64。它的步进角为5.625°/64(经减速后输出轴步进角),这意味着电机轴每转一圈需要64×64=4096个脉冲。
2.2 硬件连接方案
以下是详细的接线方案:
| ULN2003引脚 | RK3568引脚 | GPIO号 | 功能说明 |
|---|---|---|---|
| IN1 | GPIO3_B0 | gpio-88 | 电机A相控制 |
| IN2 | GPIO3_C5 | gpio-101 | 电机B相控制 |
| IN3 | GPIO0_C0 | gpio-8 | 电机C相控制 |
| IN4 | GPIO3_B6 | gpio-94 | 电机D相控制 |
| VCC | 5V电源 | - | 电机驱动电源 |
| GND | GND | - | 共地连接 |
重要提示:ULN2003的COM端必须接5V电源,这是内部续流二极管的公共端。同时确保电源能够提供至少500mA电流,信号线可串联100Ω电阻限流保护GPIO。
3. 设备树配置
3.1 设备树文件修改
设备树是Linux内核中描述硬件配置的重要机制。我们需要在RK3568的设备树中添加步进电机的相关配置。
首先定位到设备树文件:
bash复制cd /home/alientek/rk3568_linux5.10_sdk/kernel
vim arch/arm64/boot/dts/rockchip/rk3568-atk-evb1-ddr4-v10.dtsi
3.2 添加步进电机节点
在根节点下添加以下内容:
dts复制stepper_motor: stepper-motor {
compatible = "atk,stepper-motor-uln2003";
pinctrl-names = "default";
pinctrl-0 = <&stepper_motor_pins>;
// 定义四相控制GPIO,顺序对应 IN1~IN4
gpios = <&gpio3 RK_PB0 GPIO_ACTIVE_HIGH>, // IN1: GPIO3_B0
<&gpio3 RK_PC5 GPIO_ACTIVE_HIGH>, // IN2: GPIO3_C5
<&gpio0 RK_PC0 GPIO_ACTIVE_HIGH>, // IN3: GPIO0_C0
<&gpio3 RK_PB6 GPIO_ACTIVE_HIGH>; // IN4: GPIO3_B6
// 电机参数(可选)
steps-per-revolution = <4096>; // 完整一圈所需步数
status = "okay";
};
3.3 配置pinctrl引脚复用
在pinctrl节点内添加引脚复用配置:
dts复制&pinctrl {
stepper_motor {
stepper_motor_pins: stepper-motor-pins {
rockchip,pins =
<3 RK_PB0 RK_FUNC_GPIO &pcfg_pull_none>, // GPIO3_B0
<3 RK_PC5 RK_FUNC_GPIO &pcfg_pull_none>, // GPIO3_C5
<0 RK_PC0 RK_FUNC_GPIO &pcfg_pull_none>, // GPIO0_C0
<3 RK_PB6 RK_FUNC_GPIO &pcfg_pull_none>; // GPIO3_B6
};
};
};
3.4 启用GPIO控制器
确保相关的GPIO控制器已启用:
dts复制&gpio0 {
status = "okay";
};
&gpio3 {
status = "okay";
};
4. 驱动开发详解
4.1 驱动框架设计
步进电机驱动采用标准的Linux字符设备驱动框架,主要包括以下组件:
- 平台驱动结构体注册
- 设备树匹配机制
- 字符设备接口实现
- 高精度定时器控制
- GPIO操作接口
4.2 关键数据结构
定义电机设备结构体,包含所有必要的控制参数:
c复制struct motor_device {
struct platform_device *pdev;
struct cdev cdev;
struct device *device;
dev_t devno;
/* GPIO资源 */
int gpios[4];
/* 运动控制参数 */
int direction; // 当前方向: 0正转 1反转
int step_index; // 当前步序索引 (0-7)
int target_steps; // 目标步数 (0表示连续旋转)
int current_steps; // 已走步数
int step_interval; // 步脉冲间隔(us)
/* 定时器和同步 */
struct hrtimer timer; // 高精度定时器
spinlock_t lock; // 自旋锁保护共享数据
bool running; // 是否在运行
bool stop_flag; // 停止标志
};
4.3 励磁时序表
28BYJ-48采用四相八拍驱动模式,定义如下励磁时序:
c复制static const int step_sequence[8][4] = {
{1, 0, 0, 0}, // A
{1, 1, 0, 0}, // AB
{0, 1, 0, 0}, // B
{0, 1, 1, 0}, // BC
{0, 0, 1, 0}, // C
{0, 0, 1, 1}, // CD
{0, 0, 0, 1}, // D
{1, 0, 0, 1} // DA
};
这种1-2相励磁方式能提供更好的扭矩和平稳性,是28BYJ-48电机的推荐驱动方式。
4.4 控制接口设计
通过ioctl提供电机控制接口:
c复制#define MOTOR_IOC_MAGIC 'M'
#define MOTOR_SET_DIRECTION _IOW(MOTOR_IOC_MAGIC, 1, int) // 设置方向
#define MOTOR_SET_SPEED _IOW(MOTOR_IOC_MAGIC, 2, int) // 设置速度
#define MOTOR_SET_STEPS _IOW(MOTOR_IOC_MAGIC, 3, int) // 设置步数
#define MOTOR_START _IO(MOTOR_IOC_MAGIC, 4) // 启动运动
#define MOTOR_STOP _IO(MOTOR_IOC_MAGIC, 5) // 停止运动
#define MOTOR_GET_STATUS _IOR(MOTOR_IOC_MAGIC, 6, int) // 获取状态
4.5 定时器控制
使用高精度定时器(hrtimer)精确控制步进间隔:
c复制static enum hrtimer_restart motor_timer_callback(struct hrtimer *timer)
{
struct motor_device *motor = container_of(timer, struct motor_device, timer);
unsigned long flags;
bool need_restart = false;
spin_lock_irqsave(&motor->lock, flags);
if (!motor->running || motor->stop_flag) {
spin_unlock_irqrestore(&motor->lock, flags);
return HRTIMER_NORESTART;
}
/* 更新步序索引 */
if (motor->direction == 0) {
motor->step_index = (motor->step_index + 1) % 8;
} else {
motor->step_index = (motor->step_index + 7) % 8; // 反向
}
/* 设置输出相位 */
motor_set_phase(motor);
/* 更新已走步数 */
if (motor->target_steps > 0) {
motor->current_steps++;
/* 检查是否达到目标步数 */
if (motor->current_steps >= motor->target_steps) {
motor->running = false;
dev_dbg(&motor->pdev->dev, "Target steps reached: %d\n",
motor->target_steps);
spin_unlock_irqrestore(&motor->lock, flags);
return HRTIMER_NORESTART;
}
need_restart = true;
} else {
/* 连续旋转模式 */
need_restart = true;
}
spin_unlock_irqrestore(&motor->lock, flags);
/* 重启定时器 */
if (need_restart) {
hrtimer_forward_now(timer, ns_to_ktime(motor->step_interval * 1000));
return HRTIMER_RESTART;
}
return HRTIMER_NORESTART;
}
5. 驱动编译与测试
5.1 驱动编译配置
将驱动文件放入内核源码树:
bash复制cd /home/alientek/rk3568_linux5.10_sdk/kernel/drivers/misc
cp /path/to/stepper_motor_driver.c .
修改Kconfig添加配置选项:
config复制config STEPPER_MOTOR
tristate "ULN2003 Stepper Motor Driver for 28BYJ-48"
depends on OF_GPIO
help
This driver supports 28BYJ-48 stepper motor with ULN2003 driver board.
It provides ioctl interface for direction, speed and step control.
修改Makefile添加编译项:
makefile复制obj-$(CONFIG_STEPPER_MOTOR) += stepper_motor_driver.o
5.2 内核配置与编译
配置内核启用驱动:
bash复制cd /home/alientek/rk3568_linux5.10_sdk/kernel
export ARCH=arm64
export CROSS_COMPILE=/home/alientek/rk3568_linux5.10_sdk/buildroot/output/rockchip_atk_dlrk3568/host/bin/aarch64-buildroot-linux-gnu-
make menuconfig
在menuconfig中按路径:Device Drivers → Misc devices → ULN2003 Stepper Motor Driver for 28BYJ-48,选择编译进内核(*)。
或者直接修改.config文件:
bash复制echo "CONFIG_STEPPER_MOTOR=y" >> .config
编译内核并烧写:
bash复制# 将配置同步到默认配置文件
cp .config arch/arm64/configs/rockchip_linux_defconfig
# 编译内核
cd /home/alientek/rk3568_linux5.10_sdk
./build.sh kernel
# 烧写boot镜像
sudo ./rkflash.sh boot
6. 测试与验证
6.1 驱动加载验证
检查驱动加载日志:
bash复制dmesg | grep stepper
期望输出类似:
code复制[ 5.123456] stepper-motor stepper-motor: Stepper motor probe start
[ 5.123789] stepper-motor stepper-motor: GPIO 88 requested
[ 5.124012] stepper-motor stepper-motor: GPIO 101 requested
[ 5.124234] stepper-motor stepper-motor: GPIO 8 requested
[ 5.124456] stepper-motor stepper-motor: GPIO 94 requested
[ 5.125678] stepper-motor stepper-motor: Stepper motor driver probed successfully
[ 5.125901] stepper-motor stepper-motor: Device node: /dev/stepper
6.2 设备节点检查
确认设备节点已创建:
bash复制ls -l /dev/stepper
应显示类似:
code复制crw------- 1 root root 242, 0 Mar 9 10:00 /dev/stepper
6.3 GPIO状态检查
查看GPIO调试信息:
bash复制mount -t debugfs none /sys/kernel/debug
cat /sys/kernel/debug/gpio | grep motor
应看到四个motor-phaseX的GPIO条目,初始状态为lo(低电平)。
6.4 测试程序运行
编译测试程序:
bash复制cd /home/alientek/rk3568_linux5.10_sdk
source buildroot/output/rockchip_atk_dlrk3568/host/environment-setup
aarch64-buildroot-linux-gnu-gcc motor_test.c -o motor_test
测试电机运行:
bash复制# 正转测试 (2000us/step ≈ 73rpm)
./motor_test /dev/stepper fwd 2000
sleep 10
./motor_test /dev/stepper stop
# 反转测试
./motor_test /dev/stepper rev 2000
sleep 10
./motor_test /dev/stepper stop
# 定步长测试 (旋转1圈 = 4096步)
./motor_test /dev/stepper step 4096 2000
7. 常见问题排查
7.1 电机不转动
可能原因及解决方案:
- GPIO未正确输出:检查
dmesg日志,确认GPIO申请成功 - 电源供电不足:确保5V电源能提供至少500mA电流
- ULN2003连接错误:核对IN1~IN4与GPIO对应关系
7.2 电机抖动或失步
可能原因及解决方案:
- 速度过快:增大step_interval值降低转速
- 励磁时序错误:检查step_sequence数组定义
- 负载过大:减小负载或增大驱动电流
7.3 GPIO冲突处理
如果出现GPIO冲突(如与其他驱动如4G模块冲突),可以:
- 修改冲突驱动的GPIO配置
- 禁用不需要的驱动模块
- 选择其他未使用的GPIO引脚
查找冲突节点:
bash复制grep -r "rm500u-modem" /home/alientek/rk3568_linux5.10_sdk/kernel/arch/arm64/boot/dts/rockchip/
修改设备树禁用冲突模块:
dts复制&rm500u_modem {
status = "disabled";
};
8. 性能优化与扩展
8.1 添加PWM调速
可以结合RK3568的PWM子系统实现更精确的调速控制。在设备树中添加PWM节点配置:
dts复制pwm15: pwm@fe700000 {
compatible = "rockchip,rk3568-pwm", "rockchip,rk3328-pwm";
reg = <0x0 0xfe700000 0x0 0x10>;
#pwm-cells = <3>;
pinctrl-names = "default";
pinctrl-0 = <&pwm15m0_pins>;
clocks = <&cru CLK_PWM1>, <&cru PCLK_PWM1>;
clock-names = "pwm", "pclk";
status = "disabled";
};
8.2 添加位置反馈
配合编码器或限位开关可以实现闭环控制,提高定位精度。需要在驱动中添加:
- 编码器接口(如正交编码器或霍尔传感器)
- 位置反馈处理逻辑
- PID控制算法实现
8.3 多电机同步控制
扩展驱动支持多个步进电机协同运动,需要:
- 为每个电机创建独立的设备实例
- 实现同步运动算法
- 添加多线程或中断处理机制
9. 关键技术与经验总结
-
四相八拍励磁时序:精确实现1-2相励磁时序是保证电机平稳运行的关键。我们定义的8步序列提供了良好的扭矩和平滑性。
-
高精度定时器:使用hrtimer而非传统的定时器,可以获得微秒级的步进间隔精度,这对于高速运行时的稳定性至关重要。
-
并发保护:在多任务环境中,使用自旋锁保护共享数据,防止竞态条件导致电机控制异常。
-
设备树配置:将硬件配置与驱动代码分离,提高了驱动的可移植性。通过设备树可以方便地修改GPIO分配而不需要重新编译驱动。
-
电源管理:实际测试中发现,当电机启动瞬间电流较大,可能导致电源电压跌落。建议在电源端并联大容量电容(如1000μF)以稳定电压。
-
热管理:长时间运行时ULN2003会发热,建议添加散热片或限制最大工作电流。实测在400mA以下工作温度较为安全。
-
调试技巧:在开发过程中,先通过LED或逻辑分析仪验证GPIO输出时序正确,再连接电机,可以避免因软件错误损坏硬件。
