
简易PID项目复刻
简介
本项目是简易PID项目复刻,学习项目使用的MSPM0G3507开发板,复刻完成后对项目进行了移植,移植使用的STM32F103C8T6开发板,移植验证成功。
简介:本项目是简易PID项目复刻,学习项目使用的MSPM0G3507开发板,复刻完成后对项目进行了移植,移植使用的STM32F103C8T6开发板,移植验证成功。开源协议
:GPL 3.0
描述
视频链接:
项目简介
本项目学习使用MSPM0G3507开发板学习简易PID项目,借助嘉立创平台学习MSPM0G3507开发板的程序设计以及PID算法使用,通过PID算法实现电机的定距转动以及定速运行(电赛TI:简易PID入门项目)。
学习完成后对官方开源项目进行了简单的二次开发,对页面进行了简单修改,增加了中间按键,修改调整了按键按键功能,增加了系统运行灯。
最后对整体项目进行了移植,移植使用立创地阔星STM32F103C8T6开发板,移植使用HAL库。
项目功能
本设计是基于立创天猛星MSPM0G3507开发板/立创地阔星STM32F103C8T6开发板设计简易PID控制;设置有五个独立按键(上、下、左、右、OK),1.9寸SPI屏幕用作人机交互显示,通过“轮询+中断+状态机”结构实现PID的定速与定距控制。
首页状态机

定速状态机

定距状态机

设置状态机

调参状态机

前台任务
在 main 中的 while 中执行。

后台任务
在定时器中断中执行。

硬件设计
电源

电源通过TYPE-C接口输入5V电压经过开关输入到MOTOR_VCC和+5V。
MOTOR_VCC直接将TYPE-C接口输入5V电压输入电机正负极。
+5V是TYPE-C接口输入的5V电压通过MOTOR_VCC后经过一个防倒灌二极管然后经过一个22μF电容与100nF电容滤波后的电源,该电源接入到开发板的5V上,为开发板供电。
电机与编码器

电机驱动芯片使用BDR6126D,它是一款大功率直流电机驱动芯片,最高输入18V,最大持续输出电流4.5A。
电机驱动芯片的BO统一接到电机一极,FO统一接到电机另一极。通过BI和FI控制 BO与FO的输出,为了防止电机启动的瞬时电流抢走开发板电流增加蓄能电容保持电流稳定。
按键

开发板的GPIO通过按键接到GND。当引脚检测到是低电平时,说明按键被按下。
天猛星MSPMOG3507

开发板的输入电源是+5V,因为开发板本身自带了5V降压3.3V的功能,所以直接接出3V3到编码器的电源输入。
电机控制信号BI\FI必须接入支持PWM功能的引脚,原理图中选择了GPIOA26和GPIOA27。GPIOA26选择通过TIMG7-C0输出PWM,GPIOA27选择通过TMG7-C1输出PWM。
编码器的AB相任意GPIO引脚,这里选择接到了GPIOB0,和GPIOB1。
按键信号也是任意引脚,这里选择接到GPIOA8、GPIOA9、GPIOA28、GPIOA31、GPIOB4。

地阔星STM32F103C8T6(移植)

开发板的输入电源是+5V,5V降压3.3V后接出3V3到编码器的电源输入。
SPI屏幕使用硬件SPI的SCK与MOSI引脚,原理图中选择PB13与PB15,PB13选择SPI2_SCK,PB15选择SPI2_MOSI,剩余引脚选择任意引脚GPIO功能,原理图中选择RES->PA2、DC->PA0、CS->PB12、BLK->PA1。
电机控制信号BI\FI必须接入支持PWM功能的引脚,原理图中选择了GPIOA3和GPIOA6。GPIOA3选择通过TIM2_CH4输出PWM,GPIOA6选择通过TIM3_CH1输出PWM。
编码器的AB相任意GPIO引脚,这里选择接到了GPIOB0,和GPIOB1。
按键信号也是任意引脚,这里选择接到GPIOB3、GPIOB4、GPIOB5、GPIOB6、GPIOB7。
USART串口使用的USART1,PA9->USART1_TX,PA10->USART1_RX。

软件设计
系统架构
-
应用层 (app/)
- app_sys_mode.c/h: 系统模式管理,包括页面状态、功能模式、事件处理
- app_speed_pid.c/h: 速度PID控制实现
- app_distance_pid.c/h: 距离PID控制实现
- app_key_task.c/h: 按键任务处理和事件响应
- app_ui.c/h: 用户界面显示和交互
-
中间层 (middle/)
- mid_pid.c/h: PID算法核心实现
- mid_timer.c/h: 定时器管理和任务调度
- mid_button.c/h: 按键扫描和状态管理
- mid_debug_led.c/h: 调试LED控制
- mid_debug_uart.c/h: 调试串口通信
-
硬件层 (hardware/)
- hw_motor.c/h: 电机PWM驱动控制
- hw_encoder.c/h: 编码器信号采集和处理
- hw_lcd.c/h: LCD显示屏驱动
- hw_key.c/h: 按键硬件接口
- lcdfont.h, pic.h: 显示资源文件
主要功能模块
- PID控制算法 (mid_pid.c)
实现标准PID控制算法:输出 = Kp×误差 + Ki×误差积分 + Kd×误差微分
核心算法实现:float pid_calc(PID *pid, float target, float current) { pid->last_error = pid->error; pid->error = target - current; float pout = pid->error; // 比例项 pid->change_i += pid->error; // 积分项累积 float dout = pid->error - pid->last_error; // 微分项 // 积分限幅 if(pid->change_i > pid->max_change_i) pid->change_i = pid->max_change_i; else if(pid->change_i < -pid->max_change_i) pid->change_i = -pid->max_change_i; // PID输出计算 pid->output = (pid->kp * pout) + (pid->ki * pid->change_i) + (pid->kd * dout); // 输出限幅 if(pid->output > pid->max_output) pid->output = pid->max_output; else if(pid->output < -pid->max_output) pid->output = -pid->max_output; return pid->output; } - 速度控制模式 (app_speed_pid.c)
- 目标:控制电机转速达到设定值
- 反馈:编码器测量的实际转速
- 输出:PWM占空比控制电机功率
速度计算方法:
// 20ms定时器中断中更新速度 void encoder_update_speed(void) { static uint32_t last_count = 0; uint32_t current_count = encoder_get_count(); // 计算20ms内的脉冲增量 int32_t delta_count = current_count - last_count; // 转换为RPM (脉冲/20ms -> RPM) // 假设编码器分辨率为1000脉冲/转 float speed_rpm = (delta_count * 50.0 * 60.0) / 1000.0; encoder_data.speed = speed_rpm; last_count = current_count; } - 距离控制模式 (app_distance_pid.c)
- 目标:控制电机转动特定角度或距离
- 反馈:编码器累计脉冲数
- 输出:PWM占空比控制电机功率
位置控制实现:
void app_distance_pid_control(void) { if(g_distance_pid_data.enable == 1) { // 获取当前位置(编码器脉冲数) float current_pos = hw_encoder_get_count(); // PID计算 float output = mid_pid_calculate(&g_distance_pid_data.pid, g_distance_pid_data.target, current_pos); // 输出到电机 hw_motor_set((int16_t)output); // 到达目标位置时停止 if(fabs(g_distance_pid_data.target - current_pos) < 5.0) { hw_motor_stop(); g_distance_pid_data.enable = 0; } } } - 编码器系统 (hw_encoder.c)
- 双相正交编码器接口
- 外部中断驱动,实时响应
- 自动方向检测(正转/反转)
编码器中断处理:
// 编码器A相中断处理函数 void GROUP1_IRQHandler(void) { switch (DL_Interrupt_getPendingGroup(DL_INTERRUPT_GROUP_1)) { case DL_INTERRUPT_GROUP1_IIDX_GPIOA: switch (DL_GPIO_getPendingInterrupt(GPIOA)) { case DL_GPIO_IIDX_DIO21: // 编码器A相 // 读取B相状态判断方向 if(DL_GPIO_readPins(GPIOA, DL_GPIO_PIN_22)) g_encoder_temp_count++; // 正转 else g_encoder_temp_count--; // 反转 break; } break; } } // 定时器中断中更新编码器数据 void hw_encoder_update(void) { g_encoder_data.count = g_encoder_temp_count; g_encoder_data.direction = (g_encoder_temp_count > g_encoder_data.last_count) ? 1 : -1; g_encoder_data.last_count = g_encoder_data.count; } - 电机驱动 (hw_motor.c)
- 双路PWM输出控制
- 支持正转、反转、停止
- 输出限幅保护
电机控制实现:
void hw_motor_set(int16_t pwm_value) { if(pwm_value > 0) // 正转 { DL_TimerG_setCaptureCompareValue(PWM_0_INST, pwm_value, DL_TIMER_CC_0_INDEX); DL_TimerG_setCaptureCompareValue(PWM_0_INST, 0, DL_TIMER_CC_1_INDEX); } else if(pwm_value < 0) // 反转 { DL_TimerG_setCaptureCompareValue(PWM_0_INST, 0, DL_TIMER_CC_0_INDEX); DL_TimerG_setCaptureCompareValue(PWM_0_INST, -pwm_value, DL_TIMER_CC_1_INDEX); } else // 停止 { DL_TimerG_setCaptureCompareValue(PWM_0_INST, 0, DL_TIMER_CC_0_INDEX); DL_TimerG_setCaptureCompareValue(PWM_0_INST, 0, DL_TIMER_CC_1_INDEX); } }
二次开发
按键功能调整
代码位置:app_key_task.c(文件全部内容,太多了就不放了)
添加OK键位选择,OK键功能为原项目右键功能。
除OK键:
首页状态下修改:上键单击->嘉立创logo页面,下键单击->嘉立创常用网址,左、右键单击->定速、定距状态选择。
设置状态下修改:右键单击->速度/角度参数调节(根据当前是定速状态还是定距状态选择),上下键单击->P、I、D参数选择。
设置ui_flag、target_flag两个标志位,ui_flag用于在首页状态下在从logo页面与网址页面进入模式选择时恢复上次选择,target_flag用于在设置状态下从速度/角度切换到P、I、D选择时恢复上次选择。
UI界面
设置ui_flag标志位,用于在首页状态下在从logo页面与网址页面进入模式选择时重新刷新页面。
添加两个页面选择:logo页面(添加pic.h文件,用于存放彩色图片数字)与网址页面。
//app_ui.c
//根据按键选择绘制首页两个选项的选择框
void ui_home_page_select(int mode)
{
char select_box_seze = 5;
switch(mode)
{
case 0: //选择PID定速模式
if(ui_flag == 1)
{
ui_home_page();
ui_flag = 0;
disp_select_box(40,80,65,80,10,select_box_seze,WHITE);
}
else
{
disp_select_box(40,80,65,80,10,select_box_seze,WHITE);
disp_select_box(200,80,65,80,10,select_box_seze,BLACK);
}
break;
case 1: //选择PID定距模式
if(ui_flag == 1)
{
ui_home_page();
ui_flag = 0;
disp_select_box(200,80,65,80,10,select_box_seze,WHITE);
}
else
{
disp_select_box(40,80,65,80,10,select_box_seze,BLACK);
disp_select_box(200,80,65,80,10,select_box_seze,WHITE);
}
break;
case 2: //嘉立创logo
LCD_Fill(0,0,LCD_W,LCD_H,WHITE);
LCD_ShowPicture(0,10,305,143,gImage_1);
ui_flag = 1;
break;
case 3: //嘉立创网址
LCD_Fill(0,0,LCD_W,LCD_H,WHITE);
LCD_ShowChinese(8,5,"立创EDA:",RED,WHITE,16,0);
LCD_ShowChinese(8,25,"https://lceda.cn/",RED,WHITE,16,0);
LCD_ShowChinese(8,45,"立创开源硬件平台:",RED,WHITE,16,0);
LCD_ShowChinese(8,65,"https://oshwhub.com/",RED,WHITE,16,0);
LCD_ShowChinese(8,85,"立创商城:",RED,WHITE,16,0);
LCD_ShowChinese(8,105,"https://www.szlcsc.com/",RED,WHITE,16,0);
LCD_ShowChinese(8,125,"立创文档中心:",RED,WHITE,16,0);
LCD_ShowChinese(8,145,"https://wiki.lckfb.com/",RED,WHITE,16,0);
LCD_ShowChinese(225,2,"若",RED,WHITE,24,0);
LCD_ShowChinese(225,28,"你",RED,WHITE,24,0);
LCD_ShowChinese(225,50,"决",RED,WHITE,24,0);
LCD_ShowChinese(225,74,"丁",RED,WHITE,24,0); //“定”不是“丁”,因为与“定距”的定字体不同会相互影响所有做出区分
LCD_ShowChinese(225,98,"灿",RED,WHITE,24,0);
LCD_ShowChinese(225,122,"烂",RED,WHITE,24,0);
LCD_ShowChinese(265,28,"山",RED,WHITE,24,0);
LCD_ShowChinese(265,50,"无",RED,WHITE,24,0);
LCD_ShowChinese(265,74,"遮",RED,WHITE,24,0);
LCD_ShowChinese(265,98,"海",RED,WHITE,24,0);
LCD_ShowChinese(265,122,"无",RED,WHITE,24,0);
LCD_ShowChinese(265,146,"拦",RED,WHITE,24,0);
ui_flag = 1;
break;
}
}
void ui_speed_page_select_box(int mode)函数中修改选择框的清除。
//app_ui.c
void ui_speed_page_select_box(int mode)
{
char select_box_interval = 3;
switch(mode)
{
case 0: //P
disp_select_box(34,104-34,104,128-104,10,select_box_interval,WHITE);
disp_select_box(118,188-118,104,128-104,10,select_box_interval,BLACK);
disp_select_box(202,272-202,104,128-104,10,select_box_interval,BLACK);
disp_select_box(320-150,316-(320-150),170-34,166-(170-34),10,select_box_interval,BLACK);
break;
case 1: //I
disp_select_box(118,188-118,104,128-104,10,select_box_interval,WHITE);
disp_select_box(202,272-202,104,128-104,10,select_box_interval,BLACK);
disp_select_box(34,104-34,104,128-104,10,select_box_interval,BLACK);
disp_select_box(320-150,316-(320-150),170-34,166-(170-34),10,select_box_interval,BLACK);
break;
case 2: //D
disp_select_box(202,272-202,104,128-104,10,select_box_interval,WHITE);
disp_select_box(34,104-34,104,128-104,10,select_box_interval,BLACK);
disp_select_box(320-150,316-(320-150),170-34,166-(170-34),10,select_box_interval,BLACK);
disp_select_box(118,188-118,104,128-104,10,select_box_interval,BLACK);
break;
case 3: //target
disp_select_box(320-150,316-(320-150),170-34,166-(170-34),10,select_box_interval,WHITE);
disp_select_box(34,104-34,104,128-104,10,select_box_interval,BLACK);
disp_select_box(118,188-118,104,128-104,10,select_box_interval,BLACK);
disp_select_box(202,272-202,104,128-104,10,select_box_interval,BLACK);
break;
case 4: //all clean
disp_select_box(34,104-34,104,128-104,10,select_box_interval,BLACK);
disp_select_box(118,188-118,104,128-104,10,select_box_interval,BLACK);
disp_select_box(202,272-202,104,128-104,10,select_box_interval,BLACK);
disp_select_box(320-150,316-(320-150),170-34,166-(170-34),10,select_box_interval,BLACK);
break;
}
}
系统运行指示灯
每秒进行一次反转
//mid_timer.c
//系统运行指示灯
void TIMER_LED_INST_IRQHandler(void)
{
//如果产生了定时器中断
switch( DL_TimerG_getPendingInterrupt(TIMER_LED_INST) )
{
case DL_TIMER_IIDX_ZERO://如果是0溢出中断
//将LED灯的状态翻转
DL_GPIO_togglePins(DEBUG_LED_PORT, DEBUG_LED_PIN_22_PIN);
break;
default://其他的定时器中断
break;
}
}
移植
1.引脚配置

2.时钟配置

3.中断配置

4.SPI屏幕
- HAL_GPIO_WritePin(GPIO_TypeDef *GPIOx, uint16_t GPIO_Pin, GPIO_PinState PinState):
写入引脚状态,*GPIOx:GPIO端口外设,GPIO_Pin:引脚端口号,PinState:写入值(GPIO_PIN_RESET:0,GPIO_PIN_SET:1) - HAL_SPI_Transmit(SPI_HandleTypeDef *hspi, const uint8_t *pData, uint16_t Size, uint32_t Timeout):
阻塞模式下传输数据,*hspi:SPI句柄的指针,*pData:发送数据的指针,Size:数据大小,Timeout:超时时间
//hw_lcd.h
#define LCD_SCLK_Clr() HAL_GPIO_WritePin(LCD_SCL_GPIO_Port,LCD_SCL_Pin,GPIO_PIN_RESET)//SCL=SCLK
#define LCD_SCLK_Set() HAL_GPIO_WritePin(LCD_SCL_GPIO_Port,LCD_SCL_Pin,GPIO_PIN_SET)
#define LCD_MOSI_Clr() HAL_GPIO_WritePin(LCD_MOSI_GPIO_Port,LCD_MOSI_Pin,GPIO_PIN_RESET)//SDA=MOSI
#define LCD_MOSI_Set() HAL_GPIO_WritePin(LCD_MOSI_GPIO_Port,LCD_MOSI_Pin,GPIO_PIN_SET)
#define LCD_RES_Clr() HAL_GPIO_WritePin(LCD_RES_GPIO_Port,LCD_RES_Pin,GPIO_PIN_RESET)//RES
#define LCD_RES_Set() HAL_GPIO_WritePin(LCD_RES_GPIO_Port,LCD_RES_Pin,GPIO_PIN_SET)
#define LCD_DC_Clr() HAL_GPIO_WritePin(LCD_DC_GPIO_Port,LCD_DC_Pin,GPIO_PIN_RESET)//DC
#define LCD_DC_Set() HAL_GPIO_WritePin(LCD_DC_GPIO_Port,LCD_DC_Pin,GPIO_PIN_SET)
#define LCD_CS_Clr() HAL_GPIO_WritePin(LCD_CS_GPIO_Port,LCD_CS_Pin,GPIO_PIN_RESET)//CS
#define LCD_CS_Set() HAL_GPIO_WritePin(LCD_CS_GPIO_Port,LCD_CS_Pin,GPIO_PIN_SET)
#define LCD_BLK_Clr() HAL_GPIO_WritePin(LCD_BLK_GPIO_Port,LCD_BLK_Pin,GPIO_PIN_RESET)//BLK
#define LCD_BLK_Set() HAL_GPIO_WritePin(LCD_BLK_GPIO_Port,LCD_BLK_Pin,GPIO_PIN_SET)
//hw_lcd.c
void spi_write_bus(unsigned char dat)
{
//发送数据
HAL_SPI_Transmit(&hspi2, &dat, 1, HAL_MAX_DELAY);
}
5.按键
- HAL_GPIO_ReadPin(GPIO_TypeDef *GPIOx, uint16_t GPIO_Pin):
读取引脚状态,*GPIOx:GPIO端口外设,GPIO_Pin:引脚端口号
//hw_key.c
KEY_STATUS key_scan(void)
{
KEY_STATUS states;
// 读取每个按键的状态
states.up = HAL_GPIO_ReadPin(GPIOB, KEY_UP_Pin) ? 1 : 0;
states.left = HAL_GPIO_ReadPin(GPIOB, KEY_LEFT_Pin) ? 1 : 0;
states.right = HAL_GPIO_ReadPin(GPIOB, KEY_RIGHT_Pin) ? 1 : 0;
states.down = HAL_GPIO_ReadPin(GPIOB, KEY_DOWN_Pin) ? 1 : 0;
states.ok = HAL_GPIO_ReadPin(GPIOB, KEY_OK_Pin) ? 1 : 0;
return states;
}
6.PWM输出
- __HAL_TIM_SetCompare(TIM_HandleTypeDef *htim, uint32_t Channel, uint32_t CompareValue)
调节PWM占空比,*htim:定时器句柄的指针,Channel:定时器通道,CompareValue:占空比
//hw_encoder.c
//PWM输出引脚使能,这里写在了编码器初始化中了!
void encoder_init(void)
{
//使能PWM输出引脚
HAL_TIM_PWM_Start(&htim2, TIM_CHANNEL_4);
HAL_TIM_PWM_Start(&htim3, TIM_CHANNEL_1);
}
//hw_motor.c
// 设置fi引脚的PWM比较值
static void set_fi(uint16_t dat)
{
__HAL_TIM_SetCompare(&htim2, TIM_CHANNEL_4,dat);
}
// 设置bi引脚的PWM比较值
static void set_bi(uint16_t dat)
{
__HAL_TIM_SetCompare(&htim3, TIM_CHANNEL_1,dat);
}
7.编码器驱动
- void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin):
处理外部中断(EXTI)事件的用户回调函数,GPIO_Pin:触发中断的 GPIO 引脚号
//hw_encoder.c
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
if(GPIO_Pin == ENCODER_A_Pin)
{
//如果在A相上升沿下,B相为低电平
if(!HAL_GPIO_ReadPin(ENCODER_B_GPIO_Port,ENCODER_B_Pin))
{
motor_encoder.temp_count++;
}
else
{
motor_encoder.temp_count--;
}
}//编码器B相上升沿触发
else if(GPIO_Pin == ENCODER_B_Pin)
{
//如果在B相上升沿下,A相为低电平
if(!HAL_GPIO_ReadPin(ENCODER_A_GPIO_Port,ENCODER_A_Pin))
{
motor_encoder.temp_count--;
}
else
{
motor_encoder.temp_count++;
}
}
}
8.定时器
- HAL_TIM_Base_Start_IT(TIM_HandleTypeDef *htim):
启动定时器基础计数功能并开启定时器更新中断,*htim:定时器句柄的指针 - void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
定时器更新中断事件的用户回调函数,*htim:定时器句柄的指针
//mid_timer.c
void timer_init(void)
{
//使能定时器1
HAL_TIM_Base_Start_IT(&htim1);
}
//电机编码器脉冲计数
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
//20ms归零中断触发
if(htim == (&htim1))
{
//编码器更新
if( get_functional_mode() != DISTANCE_FUNCTION )
{
encoder_update();
}
if( get_task_status() == TASK_ENABLE )
{
//按键扫描+事件管理
flex_button_scan();
}
}
}
5. 中间层
中间层代码基本保持不变,主要包括:
- PID算法实现 (mid_pid.c)
- 按键处理 (mid_button.c)
- 调试功能 (mid_debug_*.c)
- 定时器 (mid_timer.c)
6. 应用层
应用层代码完全相同,无需修改:
- app_speed_pid.c - PID速度控制逻辑
- app_distance_pid.c - PID距离控制逻辑
- app_sys_mode.c - 系统状态管理
- app_ui.c - 用户界面逻辑
- app_key_task.c - 按键任务处理
实物图
立创·天猛星MSPM0G3507
天猛星开发板实物:

天猛星开发板PCB:

立创·地阔星STM32F103C8T6
地阔星开发板实物:

地阔星PCB:

注意问题
STM32F103C8T6使用AC5编译(KeilMDK默认安装的是AC6),需要自行安装AC5(安装包:https://pan.baidu.com/s/1m1eD18CDMSN_f6YXWDWLSg?pwd=cgrq 提取码: cgrq)。
AC5的安装地址一定要在KeilMDK软件的ARM文件夹里面才行,安装设置完成之后,重新注册KeilMDK的激活码。
AC5安装后的配置:

..........................................................................................................................................................................................................
..........................................................................................................................................................................................................

附件及EDA工程说明
MSPM0G3507压缩文件为MSPM0G3507开发板代码。
STM32F103C8T6压缩文件为STM32F103C8T6开发板代码。
物料清单文件为实物购买清单。
EDA工程中MSPM0G3507为MSPM0G3507开发板的工程,STM32F103C8T6为STM32F103C8T6开发板的工程。
设计图
未生成预览图,请在编辑器重新保存一次BOM
暂无BOM
克隆工程知识产权声明&复刻说明
本项目为开源硬件项目,其相关的知识产权归创作者所有。创作者在本平台上传该硬件项目仅供平台用户用于学习交流及研究,不包括任何商业性使用,请勿用于商业售卖或其他盈利性的用途;如您认为本项目涉嫌侵犯了您的相关权益,请点击上方“侵权投诉”按钮,我们将按照嘉立创《侵权投诉与申诉规则》进行处理。
请在进行项目复刻时自行验证电路的可行性,并自行辨别该项目是否对您适用。您对复刻项目的任何后果负责,无论何种情况,本平台将不对您在复刻项目时,遇到的任何因开源项目电路设计问题所导致的直接、间接等损害负责。










