
基于地正星和drv8833的简易PID项目
简介
电赛TI训练营项目,使用地正星作为主控,电机驱动模块为DRV8833,支持驱动两路直流电机
简介:电赛TI训练营项目,使用地正星作为主控,电机驱动模块为DRV8833,支持驱动两路直流电机开源协议
:GPL 3.0
描述
视频链接:
项目简介
本项目是基于立创MSPM0L1306地正星开发板的简易PID入门项目,可通过PID算法控制电机转速和角度,支持实时调参并在屏幕上显示当前参数和电机状态
项目参数
- 电源输入:5V⎓2A
- 主控:立创MSPM0L1306地正星开发板,板载USB转串口电路:商品链接
- 电机驱动模块:DRV8833电机驱动模块,支持同时驱动两路直流电机或一路步进电机
- 编码器电机:13线AB相编码器电机,减速比1:48:商品链接
- 屏幕:ST7789驱动芯片240x240分辨率1.2英寸IPS液晶屏
- 开关按键:共有7个物理开关,其中5个集成在多向开关里,另外两个为独立按键
项目功能介绍
本项目一共有三个功能页面,分别是主页、定速功能页和定距功能页
1.新增功能:
-
屏幕背光亮度调整: 短按KEY1/KEY2独立按键可提高/降低屏幕亮度
-
电机切换: 短按中键可以循环切换当前使用的电机,左上角LED不亮表示当前电机为0号电机(左边的那个),LED点亮表示当前电机为1号电机(右边的那个)
2.主页:
-
主页状态: 显示项目简介和功能按钮,短按上下方向键会切换当前选择的模式,短按右键会进入所选模式
-
主页流程图:

3.定速功能页:
-
电机控制: 进入定速/定距状态后长按右键可以启动电机,电机启动时再次长按右键会关闭电机
-
回到首页: 在定速/定距状态短按左键可返回首页状态
-
定速流程图:

-
设置状态: 在定速/定距状态短按右键可以进入设置状态,进入设置状态后当前选中参数会出现不闭合方框。在设置状态按上/下键可以切换上一个/下一个要设置的参数,短按右键可进入调参状态,短按左键可退出设置状态
-
设置流程图
-
调参状态: 进入调参状态后已选中的参数会出现闭合方框,在调参状态短按左键会退出调参状态,短按上下键可以慢速调整选中参数,长按上下键可以快速调整所选参数
-
调参流程图
3.定距功能页:
- 在定距模式下同样可以进入设置和调参状态,功能与定速模式基本相同。主要区别为定速模式调整的是目标速度,定距模式调整的是目标角度
原理解析(硬件说明)
- 本项目硬件由以下几部分组成,分别是电源输入滤波电路、主控板、开关按键、LCD屏幕、电机驱动模块、编码器电机
- 本项目通过按键接收用户输入,主控接收到数据后切换到指定状态,进行对应的电机控制和屏幕显示操作
graph TD
电源输入滤波 -->电源软开关 -->主控板
主控板 --> LCD显示屏
主控板 --> 开关按键
主控板 --> 电机驱动模组
电机驱动模组 --> 编码器电机
电源软开关 --> 电机驱动模组
电源电路:
-
采用TYPE-C-6P接口作为供电接口,在CC1和CC2引脚处加入了5.1K下拉电阻,便于不同主机识别和配置。
-
电源回路上放置了两个47uf电解电容和一个22uf MLCC用于滤波,可减少电机启动和换向时产生的电源波动,同时放置了一个防反接二极管用来隔离主控电源与动力电源
-
将原项目的电源开关更换为了自锁按键+NMOS,理论上可以提高开关寿命。由于使用的是NMOS,开关关闭时断开的是GND,如果此时再将开发板连到电脑可能会导致5V串入信号引脚烧毁主板,所以请不要在开关关闭时将开发板连接到电脑
主控电路:
- 主控采用的是立创MSPM0L1306地正星开发板,我手上这块开发板是之前参加立创泰山派训练营二等奖送的内测版本,内部串口电路和正式版有些许不同。正式版板载ch340连接的是PA22/PA23,而我这块连接的是PA10/PA11
- 主控GPIO利用率基本达到100%,除了串口和SWD调试接口之外全部引出
物理按键:
- 板载7个物理按键,其中5个集成在多向开关里,另外两个为独立按键
- 多向开关主要用来控制电机相关功能,独立按键用来控制屏幕背光亮度
- 7个按键全部预留有上拉电阻位置,可选择外接10kΩ上拉电阻或开启内部上拉
屏幕模组:
- 本项目使用的是1.2寸LCD显示屏,屏幕驱动芯片与立创官方项目相同,都是ST7789:屏幕项目链接
- 屏幕接口用的10Pin,淘宝签到正好有红包就买了,实际只需要8P
- 屏幕的时钟和数据信号由硬件SPI外设驱动,对应引脚PA17-18。其余信号由GPIO模拟,背光控制信号由独立的定时器PWM驱动
电机驱动:
- 本项目采用DRV8833电机驱动模块:商品链接
- 模块标称输入电压2.7-10.8V,实际使用电压5V
- 单H桥输出电流:1.5A,可驱动2个直流减速电机
- 内置过流保护,短路保护,欠压闭锁和过热保护。带低功耗睡眠模式
- 模块输入信号IN1-4分别连接PA0-1/3-4引脚,SLEEP和FAULT信号通过0Ω电阻(不出料)分别预留到PA2/PA8,这两个引脚的相关功能目前暂未实现
- 模块输出信号连接对应电机,对应关系为IN1-2→电机0(左边电机接口),IN3-4→电机1(右边电机接口)
软件代码
单片机引脚和资源配置
- 配置好项目模板后打开sysconfig文件,然后在Tools菜单中打开sysconfig工具。项目模板教程参考;环境搭建
- 工具打开后进行相关配置,Project configuration无需修改
- Board无需修改,SWD引脚保持开启
- 一共需要配置6组GPIO,分别是指示灯,屏幕引脚,按键引脚,电机0编码器,电机1编码器,电机驱动控制信号
- 其中电机驱动控制信号为预留引脚,可以挪作他用或者不进行配置
- 指示灯引脚的配置最为简单,按下面图片修改引脚名称,将PA12设置为输出模式,设置默认电平为高电平即可
- 屏幕引脚稍微复杂一点,首先要配置硬件SPI,使用自定义模式,频率16000000hz(L1306最高支持到这个频率),3wire模式,SPI0外设,引脚选择PA17-18
- 然后配置剩余控制信号,RST,DC,CS分别配置为PA13,14,15
- 最后配置背光信号,BUSCLK 8分频,预分频器1,周期计数值100,启动定时器,设置为边沿对齐向下计数,配置通道0开启,初始占空比20%,配置输出引脚为PA16
- 接下来是按键引脚,中键,左键,右键,上键,下键,按键1,按键2对应PA21-27,按键全部设置为上拉输入
- 0号电机编码器,A相引脚为PA8,B相引脚为PA9,统一配置为输入模式,开启中断,配置优先级0,上升沿触发
- 1号电机编码器,A相引脚为PA6,B相引脚为PA7,统一配置为输入模式,开启中断,配置优先级0,上升沿触发
- 预留DRV8833的控制信号,SLEEP配置为输出,FAULT配置为输入
- 接下来配置串口,首先在SYSCTL页面勾选使用时钟树
- 然后按下图配置时钟树
- 然后再配置串口,设置时钟来源BUSCLK,分频1,目标波特率9600,开启接收中断,优先级3,配置引脚为PA10/11
- 最后是电机驱动,电机0定时器设置时钟来源BUSCLK,,分频1,预分频1,周期计数4000,启用定时器
- 配置PWM模式边沿对齐向下计数,开启通道0和通道1(对应电机正反转),设置周期值立即生效
- 配置定时器外设为TIMG1,通道0对应PA0,通道1对应PA1
- 电机1配置定时器外设为TIMG2,通道0对应PA3,通道1对应PA4,其余配置与电机0一致
- 单片机资源和引脚配置完成,使用了全部28个GPIO和全部4个定时器,利用率达到了惊人的100%(
底层组件
- 为了控制篇幅,下面只对改动过的程序进行介绍
- 底层组件包括屏幕驱动,按键驱动,电机驱动还有编码器驱动,跟硬件直接打交道的就属于底层程序
屏幕驱动
- 在hw_lcd.c中添加了函数set_BL、BL_up和BL_down来调整背光亮度
- set_BL:输入参数范围1-99,传入函数后进行翻转,然后更新背光定时器的捕获-比较值以控制背光占空比
void set_BL(uint32_t dat)
{
dat = 99 - dat;
DL_TimerG_setCaptureCompareValue(PWM_BL_INST, dat, DL_TIMER_CC_0_INDEX);
}
- BL_up:运行BL_up时背光变量BL_level自增,随后对背光亮度最大值进行限制,最后再运行set_BL设置背光
- BL_down:运行BL_down时背光变量BL_level自减,随后对背光亮度最小值进行限制,最后再运行set_BL设置背光
uint32_t BL_level = 15;
void BL_up(void)
{
BL_level += 10;
if (BL_level > 99)
{
BL_level = 99;
}
set_BL(BL_level);
}
void BL_down(void)
{
BL_level -= 10;
if (BL_level > 99 ^ BL_level == 0)
{
BL_level = 1;
}
set_BL(BL_level);
}
- 在hw_lcd.c中修改了LCD_Address_Set、lcd_init函数以适配240*240分辨率的LCD屏幕
- LCD_Address_Set:注释了Y坐标偏移语句,这里我使用的是正方形屏幕,不需要偏移
void LCD_Address_Set(unsigned int x1, unsigned int y1, unsigned int x2, unsigned int y2)
{
LCD_WR_REG(0x2a); // 列地址设置
LCD_WR_DATA(x1);
LCD_WR_DATA(x2);
LCD_WR_REG(0x2b); // 行地址设置
// LCD_WR_DATA(y1+35);
// LCD_WR_DATA(y2+35);
LCD_WR_DATA(y1);
LCD_WR_DATA(y2);
LCD_WR_REG(0x2c); // 储存器写
}
- lcd_init:根据deepseek的指示重新设置了初始化显存映射,取消显示方向旋转
void lcd_init(void)
{
/* GPIO已在图形化工具中初始化 */
LCD_RES_Clr(); // 复位
delay_ms(30);
LCD_RES_Set();
delay_ms(100);
LCD_WR_REG(0x11);
LCD_WR_REG(0x36);
// LCD_WR_DATA8(0x70);//显示方向顺时针旋转90°
LCD_WR_DATA8(0x00);//不旋转
LCD_WR_REG(0x3A);
LCD_WR_DATA8(0x05);
LCD_WR_REG(0xB2);
LCD_WR_DATA8(0x0C);
LCD_WR_DATA8(0x0C);
LCD_WR_DATA8(0x00);
LCD_WR_DATA8(0x33);
LCD_WR_DATA8(0x33);
LCD_WR_REG(0xB7);
LCD_WR_DATA8(0x35);
LCD_WR_REG(0xBB);
LCD_WR_DATA8(0x1A);
LCD_WR_REG(0xC0);
LCD_WR_DATA8(0x2C);
LCD_WR_REG(0xC2);
LCD_WR_DATA8(0x01);
LCD_WR_REG(0xC3);
LCD_WR_DATA8(0x0B);
LCD_WR_REG(0xC4);
LCD_WR_DATA8(0x20);
LCD_WR_REG(0xC6);
LCD_WR_DATA8(0x0F);
LCD_WR_REG(0xD0);
LCD_WR_DATA8(0xA4);
LCD_WR_DATA8(0xA1);
LCD_WR_REG(0x21);
LCD_WR_REG(0xE0);
LCD_WR_DATA8(0xF0);
LCD_WR_DATA8(0x00);
LCD_WR_DATA8(0x04);
LCD_WR_DATA8(0x04);
LCD_WR_DATA8(0x04);
LCD_WR_DATA8(0x05);
LCD_WR_DATA8(0x29);
LCD_WR_DATA8(0x33);
LCD_WR_DATA8(0x3E);
LCD_WR_DATA8(0x38);
LCD_WR_DATA8(0x12);
LCD_WR_DATA8(0x12);
LCD_WR_DATA8(0x28);
LCD_WR_DATA8(0x30);
LCD_WR_REG(0xE1);
LCD_WR_DATA8(0xF0);
LCD_WR_DATA8(0x07);
LCD_WR_DATA8(0x0A);
LCD_WR_DATA8(0x0D);
LCD_WR_DATA8(0x0B);
LCD_WR_DATA8(0x07);
LCD_WR_DATA8(0x28);
LCD_WR_DATA8(0x33);
LCD_WR_DATA8(0x3E);
LCD_WR_DATA8(0x36);
LCD_WR_DATA8(0x14);
LCD_WR_DATA8(0x14);
LCD_WR_DATA8(0x29);
LCD_WR_DATA8(0x32);
LCD_WR_REG(0x11);
delay_ms(120);
LCD_WR_REG(0x29);
}
- hw_lcd.h:修改屏幕分辨率宏定义以适配240x240分辨率的LCD屏幕
#define LCD_W 240
#define LCD_H 240
按键驱动
- 修改按键扫描函数,增加了三个按键
typedef struct {
unsigned int up : 1; // 使用位字段来节省空间
unsigned int left : 1;
unsigned int right : 1;
unsigned int down : 1;
unsigned int enter : 1;
unsigned int key1 : 1;
unsigned int key2 : 1;
} KEY_STATUS;
KEY_STATUS key_scan(void)
{
KEY_STATUS states;
// 读取每个按键的状态
states.up = DL_GPIO_readPins(GPIO_KEY_PORT, GPIO_KEY_PIN_UP_PIN) ? 1 : 0;
states.left = DL_GPIO_readPins(GPIO_KEY_PORT, GPIO_KEY_PIN_LEFT_PIN) ? 1 : 0;
states.right = DL_GPIO_readPins(GPIO_KEY_PORT, GPIO_KEY_PIN_RIGHT_PIN) ? 1 : 0;
states.down = DL_GPIO_readPins(GPIO_KEY_PORT, GPIO_KEY_PIN_DOWN_PIN) ? 1 : 0;
states.enter = DL_GPIO_readPins(GPIO_KEY_PORT, GPIO_KEY_PIN_ENTER_PIN) ? 1 : 0;
states.key1 = DL_GPIO_readPins(GPIO_KEY_PORT, GPIO_KEY_PIN_KEY1_PIN) ? 1 : 0;
states.key2 = DL_GPIO_readPins(GPIO_KEY_PORT, GPIO_KEY_PIN_KEY2_PIN) ? 1 : 0;
return states;
}
电机驱动
- 修改了电机驱动函数,通过额外的参数判断需要控制的电机编号。由于单片机主频较低,将PWM比较值限制在3999
// 设置fi引脚的PWM比较值
static void set_fi(uint16_t dat, MotorNum motor_num)
{
if (motor_num == MOTOR_0)
{
DL_TimerG_setCaptureCompareValue(PWM_MOTOR0_INST, dat, GPIO_PWM_MOTOR0_C1_IDX);
}
else
{
DL_TimerG_setCaptureCompareValue(PWM_MOTOR1_INST, dat, GPIO_PWM_MOTOR1_C1_IDX);
}
}
// 设置bi引脚的PWM比较值
static void set_bi(uint16_t dat, MotorNum motor_num)
{
if (motor_num == MOTOR_0)
{
DL_TimerG_setCaptureCompareValue(PWM_MOTOR0_INST, dat, GPIO_PWM_MOTOR0_C0_IDX);
}
else
{
DL_TimerG_setCaptureCompareValue(PWM_MOTOR1_INST, dat, GPIO_PWM_MOTOR1_C0_IDX);
}
}
- 修改了电机停止函数,将低电平修改为高电平,因为DRV8833驱动真值表与原项目驱动芯片有点不一样,两个输入都置高电平时才是停止电机
// 电机停止
void stop_motor(MotorNum motor_num)
{
set_motor(MOTOR_PWM_MAX, MOTOR_PWM_MAX, motor_num);
}
编码器驱动
- 与电机驱动类似,同样增加了参数以判断编码器编号,同时多创建了一个编码器数据结构体用于存储编码器1的数据
static ENCODER_RES motor_encoder;
static ENCODER_RES motor_encoder1;
// 获取编码器的值
int get_encoder_count(uint8_t encoder_num)
{
if (!encoder_num)
{
return motor_encoder.count;
}
else
{
return motor_encoder1.count;
}
}
// 获取编码器的方向
ENCODER_DIR get_encoder_dir(uint8_t encoder_num)
{
if (!encoder_num)
{
return motor_encoder.dir;
}
else
{
return motor_encoder1.dir;
}
}
/******************************************************************
* 函 数 说 明:获取实时编码器的值
* 函 数 形 参:
* 函 数 返 回:返回对应的编码器值
* 作 者:LC
* 备 注:
******************************************************************/
long long get_temp_encoder(uint8_t encoder_num)
{
if (!encoder_num)
{
return motor_encoder.temp_count;
}
else
{
return motor_encoder1.temp_count;
}
}
// 编码器数据更新
// 请间隔一定时间更新
void encoder_update(void)
{
motor_encoder.count = motor_encoder.temp_count;
// 确定方向
motor_encoder.dir = (motor_encoder.count >= 0) ? FORWARD : REVERSAL;
motor_encoder.temp_count = 0; // 编码器计数值清零
motor_encoder1.count = motor_encoder1.temp_count;
// 确定方向
motor_encoder1.dir = (motor_encoder1.count >= 0) ? FORWARD : REVERSAL;
motor_encoder1.temp_count = 0; // 编码器计数值清零
}
// 外部中断处理函数
void GROUP1_IRQHandler(void)
{
// 获取中断信号情况
if (DL_GPIO_getEnabledInterruptStatus(GPIO_ENCODER0_PORT, GPIO_ENCODER0_PIN_A_PIN) == GPIO_ENCODER0_PIN_A_PIN)
{
// 如果在A相上升沿下,B相为低电平
if (!DL_GPIO_readPins(GPIO_ENCODER0_PORT, GPIO_ENCODER0_PIN_B_PIN))
{
motor_encoder.temp_count--;
}
else
{
motor_encoder.temp_count++;
}
}
else if (DL_GPIO_getEnabledInterruptStatus(GPIO_ENCODER0_PORT, GPIO_ENCODER0_PIN_B_PIN) == GPIO_ENCODER0_PIN_B_PIN)
{
// 如果在B相上升沿下,A相为低电平
if (DL_GPIO_readPins(GPIO_ENCODER0_PORT, GPIO_ENCODER0_PIN_A_PIN))
{
motor_encoder.temp_count--;
}
else
{
motor_encoder.temp_count++;
}
}
// 清除状态
DL_GPIO_clearInterruptStatus(GPIO_ENCODER0_PORT, GPIO_ENCODER0_PIN_A_PIN | GPIO_ENCODER0_PIN_B_PIN);
// 获取中断信号情况
if (DL_GPIO_getEnabledInterruptStatus(GPIO_ENCODER1_PORT, GPIO_ENCODER1_PIN_1A_PIN) == GPIO_ENCODER1_PIN_1A_PIN)
{
// 如果在A相上升沿下,B相为低电平
if (!DL_GPIO_readPins(GPIO_ENCODER1_PORT, GPIO_ENCODER1_PIN_1B_PIN))
{
motor_encoder1.temp_count--;
}
else
{
motor_encoder1.temp_count++;
}
}
else if (DL_GPIO_getEnabledInterruptStatus(GPIO_ENCODER1_PORT, GPIO_ENCODER1_PIN_1B_PIN) == GPIO_ENCODER1_PIN_1B_PIN)
{
// 如果在B相上升沿下,A相为低电平
if (DL_GPIO_readPins(GPIO_ENCODER1_PORT, GPIO_ENCODER1_PIN_1A_PIN))
{
motor_encoder1.temp_count--;
}
else
{
motor_encoder1.temp_count++;
}
}
// 清除状态
DL_GPIO_clearInterruptStatus(GPIO_ENCODER1_PORT, GPIO_ENCODER1_PIN_1A_PIN | GPIO_ENCODER1_PIN_1B_PIN);
}
中间层组件
- 中间层组件包括指示灯,串口调试,多功能按键库,定时器以及PID函数库,主要是一些经过抽象的功能,负责底层和应用层的衔接
开源按键库
- 参照教程对新加入的按键进行适配,定义了按键返回值和按键事件回调函数,使得7个按键都可以被上层应用正常调用
typedef enum
{
BUTTON_UP = 0,
BUTTON_LEFT,
BUTTON_RIGHT,
BUTTON_DOWN,
BUTTON_ENTER,
BUTTON_KEY1,
BUTTON_KEY2,
USER_BUTTON_MAX
} user_button_t;
// 配置你按键的返回值
static uint8_t button_enter_read(void) { return key_scan().enter; }
static uint8_t button_key1_read(void) { return key_scan().key1; }
static uint8_t button_key2_read(void) { return key_scan().key2; }
// 配置按键初始化
// 用户自行根据案例配置
void user_button_init(void)
{
int i;
/* 初始化按键数据结构 */
memset(&user_button[0], 0x0, sizeof(user_button));
user_button[BUTTON_ENTER].usr_button_read = button_enter_read; // 按键读值回调函数
user_button[BUTTON_ENTER].cb = (flex_button_response_callback)btn_enter_cb; // 按键事件回调函数
user_button[BUTTON_KEY1].usr_button_read = button_key1_read; // 按键读值回调函数
user_button[BUTTON_KEY1].cb = (flex_button_response_callback)btn_key1_cb; // 按键事件回调函数
user_button[BUTTON_KEY2].usr_button_read = button_key2_read; // 按键读值回调函数
user_button[BUTTON_KEY2].cb = (flex_button_response_callback)btn_key2_cb; // 按键事件回调函数
}
系统定时器
- 我的电机经测试比官方的转得慢,所以在系统定时器中断内设置每两次中断更新一次编码器,这样子最终获得的速度就跟官方差不多,方便屏幕进行显示
// 电机编码器脉冲计数
void TIMER_TICK_INST_IRQHandler(void)
{
// 20ms归零中断触发
if (DL_Timer_getPendingInterrupt(TIMER_TICK_INST) == DL_TIMER_IIDX_ZERO)
{
static bool i;
i = !i;
if (i && get_functional_mode() != DISTANCE_FUNCTION)
{
// 编码器更新40ms一次
encoder_update();
}
if (get_task_status() == TASK_ENABLE)
{
// 按键扫描+事件管理
flex_button_scan();
}
}
}
应用层组件
- 应用层组件包括按键任务回调,系统模式转换,用户界面,PID定速应用以及PID定距应用
按键任务回调
- 修改了左键回调,设置在调参状态按左键会清除方框,这样看起来更舒服
// 如果当前是调参页
if (get_show_state() == PARAMETER_PAGE)
{
// 触发退出事件
event_manager(&system_status, QUIT_EVENT);
// 擦除全部选择框
ui_parameter_select_box_bold(4);
}
- 修改了中键回调,单击中键触发电机切换事件,然后按照当前电机状态设置指示灯亮灭
void btn_enter_cb(flex_button_t *btn)
{
switch (btn->event)
{
case FLEX_BTN_PRESS_CLICK: // 单击事件
event_manager(&system_status, MOTOR_SWITCH_EVENT);
if (system_status.Current_Motor == MOTOR_0)
{
set_debug_led_off();
}
else
{
set_debug_led_on();
}
break;
default:
break;
}
}
- 修改了按键1和按键2的回调函数,单击按键1/2分别触发背光调亮和调暗
void btn_key1_cb(flex_button_t *btn)
{
switch (btn->event)
{
case FLEX_BTN_PRESS_CLICK: // 单击事件
BL_up();
break;
default:
break;
}
}
void btn_key2_cb(flex_button_t *btn)
{
switch (btn->event)
{
case FLEX_BTN_PRESS_CLICK: // 单击事件
BL_down();
break;
default:
break;
}
}
系统模式转换
- 修改了事件管理器,增加电机切换事件和相关宏定义以实现两个电机的控制切换
case MOTOR_SWITCH_EVENT:
if (status->Current_Motor == MOTOR_1)
{
status->Current_Motor = MOTOR_0;
}
else
{
status->Current_Motor = MOTOR_1;
}
// 定义触发事件
typedef enum
{
IDLE_EVENT = 0, // 空闲
ENTER_EVENT, // 进入事件
QUIT_EVENT, // 退出事件
MOTOR_EVENT, // 电机事件
MOTOR_SWITCH_EVENT, // 切换电机事件
LONG_PRESS_ADD_START_EVENT, // 长按加开始事件
LONG_PRESS_SUBTRACT_START_EVENT, // 长按减开始事件
LONG_PRESS_END_EVENT, // 长按结束事件
} SystemEvent;
// 定义电机状态
typedef enum
{
MOTOR_STATUS_OFF = 0, // 电机关
MOTOR_STATUS_ON, // 电机开
} MotorStatus;
// 定义电机编号
typedef enum
{
MOTOR_0,
MOTOR_1,
} MotorNum;
// 定义系统相关信息
typedef struct
{
SystemPageShow show_state; // 当前界面显示页
LongPressStatus long_press_state; // 当前按键长按状态
int default_page_flag; // 当前首页(默认页)选择的内容
int set_page_flag; // 当前设置页选择的内容
MotorStatus motor_flag; // 当前电机状态
Function function_state; // 当前功能
MotorNum Current_Motor; // 当前电机编号
} SystemStatus;
用户界面
- 修改了所有元素的大小和位置来适配240*240的分辨率(代码太太太长,不放,看附件
组装流程
实物图
注意事项
- 使用左上角C口供电且电源开关关闭时请勿将开发板连接电脑,操作不当有概率串电烧毁电脑主板
- 我用的地正星是内测版,串口引脚配置和官方正式版不同
- 按钮键帽和其他3D打印件没制造条件可以不装,基本不影响功能
- 电机驱动模块和开发板请严格按照教程示例和板上丝印组装
设计图
未生成预览图,请在编辑器重新保存一次BOM
暂无BOM
克隆工程工程成员
知识产权声明&复刻说明
本项目为开源硬件项目,其相关的知识产权归创作者所有。创作者在本平台上传该硬件项目仅供平台用户用于学习交流及研究,不包括任何商业性使用,请勿用于商业售卖或其他盈利性的用途;如您认为本项目涉嫌侵犯了您的相关权益,请点击上方“侵权投诉”按钮,我们将按照嘉立创《侵权投诉与申诉规则》进行处理。
请在进行项目复刻时自行验证电路的可行性,并自行辨别该项目是否对您适用。您对复刻项目的任何后果负责,无论何种情况,本平台将不对您在复刻项目时,遇到的任何因开源项目电路设计问题所导致的直接、间接等损害负责。










