作者:机器谱
MCU-F4 Arduino |
一、支持平台
Win10及以上系统直接可用,Win7需要先安装.net框架组件。
二、电子硬件
三、操作步骤
上位机:Controller 1.0
下位机编程环境:keil5
① 使用MCU-F4主控板调试舵机之前,需要将STM32-controller下位机程序\servo_ctrl中的程序文件(MDK-ARM\project.uvprojx)下载到MCU-F4主控板中。
| 主控板 | MCU-F4 v2.0 |
| 扩展板 | SH-F4TT扩展板、SH-F4ST扩展板 |
| 硬件类型 | 型号 / 规格 |
| 主控板 | MCU-F4 v2.0 |
| 扩展板 | SH-F4TT 扩展板 |
| ① | STM32-controller下位机程序 |
| ② | Controller 1.0.exe |
| ③ | 例程-4自由度机械臂搬运 |
| ④ | 4自由度机械臂3D文件 |
| ⑤ | CH340芯片驱动 |
| ⑥ | 六自由度机械臂资料包(例程、3D文件) |
| /** * @file main.c * @brief 主程序文件 * * 此代码基于MCU-F4开发板、SH-F4TT扩展板和4自由度舵机机械臂(4个舵机+1个夹爪),使用了以下外设模块: * - 舵机:连接在SH-F4TT扩展板的舵机接口。 * * 程序功能: * 1. 初始化延时功能和舵机控制引脚PB14、PB15、PE5、PE6、PE9。 * 2. 先让舵机回归到初始位置,然后按照move_buf数组内运动。 * 3. 整个程序只完成一次完整动作。 * * 外设连接: * - 舵机连接在 SH-F4TT扩展板 的指定引脚上,控制舵机的运动。 * - 按照从末端夹爪到基座的顺序,舵机分别对应连接引脚PB14、PB15、PE5、PE6、PE9。 * - 舵机通过 PWM 信号控制,角度从0度到180度变化。 * * @author wjw * @date 2025/1/10 * @author Aaron.Liu * @date 2025/12/30 * @author lzl * @date 2026/2/21 */
#include "delay.h" #include "servo.h" #include "stm32f4xx.h" #include "uart.h" #include <stdlib.h> #include <string.h> //上位机获得的数组放这里 int move_buf[]={ 1486,1455,959,1193,1500, //初始姿态 1486,664,1822,1193,1500, //姿态1 1054,664,1822,1193,1500, //姿态2 1054,1599,1391,1193,1500, //姿态3 1054,1599,1391,1193,925, //姿态4 1054,664,1714,1193,925, //姿态5 1539,664,1714,1193,925, //姿态6 1539,1455,959,1193,925, //姿态7 1539,1455,959,1193,1500 //回到初始姿态 }; #define SERVO_MIN_PWM 500 #define SERVO_MAX_PWM 2500 #define SERVO_NUM5 //舵机的数量 #define SERVO_BUF_SIZE sizeof(move_buf)/sizeof(move_buf[0]) #define UPDATE_FREQUENCY 125 // 每秒更新的次数 GPIO_TypeDef* servo_gpio[SERVO_NUM]={GPIOB,GPIOB,GPIOE,GPIOE,GPIOE}; uint32_tservo_pin[SERVO_NUM]={GPIO_Pin_14,GPIO_Pin_15,GPIO_Pin_5,GPIO_Pin_6,GPIO_Pin_9}; void pwm_interpolation_sync(int pwm_start[], int pwm_end[], int steps,GPIO_TypeDef* GPIOs[], uint32_t Pins[], int num_servos) { float pwm_delta[5]; for (int i = 0; i < num_servos; i++) { pwm_delta[i] = (float)(pwm_end[i] - pwm_start[i]) / steps; } for (int i = 0; i < steps; i++) { int pwm_current[5]; for (int j = 0; j < num_servos; j++) { pwm_current[j] = (int)(pwm_start[j] + pwm_delta[j] * (i + 1));
if (pwm_current[j] >= SERVO_MAX_PWM) pwm_current[j] = SERVO_MAX_PWM; if (pwm_current[j] <= SERVO_MIN_PWM) pwm_current[j] = SERVO_MIN_PWM; } for (int j = 0; j < num_servos; j++) { Servo_SetAngle_Pulse(GPIOs[j], Pins[j], pwm_current[j]); }
Delay_ms(1000/UPDATE_FREQUENCY); // 保持原有延时,但所有舵机同步更新 } for (int j = 0; j < num_servos; j++) { Servo_SetAngle_Pulse(GPIOs[j], Pins[j], pwm_end[j]); } Delay_ms(50); //让舵机稳定稳定一段时间 } void back_home() { Servo_SetAngle_Pulse(GPIOB, GPIO_Pin_14, move_buf[0]); // 更新 PWM 值 Servo_SetAngle_Pulse(GPIOB, GPIO_Pin_15, move_buf[1]); // 更新 PWM 值 Servo_SetAngle_Pulse(GPIOE, GPIO_Pin_5, move_buf[2]); // 更新 PWM 值 Servo_SetAngle_Pulse(GPIOE, GPIO_Pin_6, move_buf[3]); // 更新 PWM 值 Servo_SetAngle_Pulse(GPIOE, GPIO_Pin_9, move_buf[4]); // 更新 PWM 值 } void servo_move() { int total_groups = SERVO_BUF_SIZE / SERVO_NUM; int steps_per_group = 60; // 每组间插值步数 for (int i = 0; i < total_groups - 1; i++) { int start_pwm[SERVO_NUM] = { move_buf[i*SERVO_NUM], move_buf[i*SERVO_NUM+1], move_buf[i*SERVO_NUM+2], move_buf[i*SERVO_NUM+3], move_buf[i*SERVO_NUM+4] }; int end_pwm[SERVO_NUM] = { move_buf[(i+1)*SERVO_NUM], move_buf[(i+1)*SERVO_NUM+1], move_buf[(i+1)*SERVO_NUM+2], move_buf[(i+1)*SERVO_NUM+3], move_buf[(i+1)*SERVO_NUM+4] }; pwm_interpolation_sync(start_pwm, end_pwm, steps_per_group, servo_gpio, servo_pin, SERVO_NUM); } } int main(void) { // 初始化外设 Delay_Init(); Servo_Init(GPIOB, GPIO_Pin_14); Servo_Init(GPIOB, GPIO_Pin_15); Servo_Init(GPIOE, GPIO_Pin_5); Servo_Init(GPIOE, GPIO_Pin_6); Servo_Init(GPIOE, GPIO_Pin_9); Delay_ms(1000); back_home(); Delay_ms(1000); servo_move(); while (1) { } } |
| 上位机舵机编号 | SH-F4TT扩展板舵机引脚 |
| P0 | PB14 |
| P1 | PB15 |
| P2 | PE5 |
| P3 | PE6 |
| P4 | PE9 |
| P5 | PE11 |
| P6 | PE13 |
| P7 | PE14 |
| 上位机舵机编号 | SH-F4ST扩展板舵机引脚 |
| P0 | PB14 |
| P1 | PB15 |
| P2 | PE5 |
| P3 | PE6 |
| P4 | PE9 |
| P5 | PE11 |
| P6 | PE13 |
| P7 | PE14 |
| P8 | PC6 |
| P9 | PC7 |
| P10 | PC8 |
| P11 | PC9 |
| P12 | PD12 |
| P13 | PD13 |
| P14 | PD14 |
| P15 | PD15 |
| P16 | PB8 |
| P17 | PB9 |
/** * @file main.c * @brief 主程序文件 * * 此代码基于MCU-F4开发板,配合SH-F4TT扩展板或者SH-F4ST扩展板使用。此外,还使用了以下外设模块: * - 舵机:连接在扩展板的舵机接口上。 * * 程序功能: * - 通过串口接收来自上位机的数据,并将数据进行分析,最后驱动对应的舵机。 * * 外设连接: * - 舵机连接在扩展板的指定引脚上,控制舵机的运动。 * - SH-F4TT扩展板有8个舵机接口,在本程序中表现为0~7,对应Controller 软件的P0~P7面板 * - SH-F4ST扩展板有18个舵机接口,在本程序中表现为0~17,对应Controller 软件的P0~P17面板 * - 舵机通过 PWM 信号控制,角度从0度到180度变化。 * * @author wjw * @date 2025/1/10 * @author Aaron.Liu * @date 2025/12/30 * @author lzl * @date 2026/1/20 */ #include "delay.h" #include "servo.h" #include "stm32f4xx.h" #include "uart.h" #include <stdlib.h> #include <string.h> unsigned char buf[256]= {0}; int rcnt= 0; intanalyse_flag= 0; int p_flag=0; int b_flag=0; int t_flag=0; int move_flag=0; typedef struct{ char pin[6]; char pulse[8]; GPIO_TypeDef* GPIOx; uint32_t GPIO_Pin; }Servo_info; Servo_info servo_info; void analyse_data(unsigned char *buf) { b_flag = p_flag = t_flag = -1; // 初始化为无效值 char len=strlen((char*)buf); for(int i=0;i<len;i++) { switch(buf[i]) { case '#' : { b_flag=i; break; } case 'P' : { p_flag=i; memset(servo_info.pin,0,sizeof(servo_info.pin)); memcpy(servo_info.pin,buf+b_flag+1,p_flag-b_flag-1); break; } case 'T' : { t_flag=i; memset(servo_info.pulse,0,sizeof(servo_info.pulse)); memcpy(servo_info.pulse,buf+p_flag+1,t_flag-p_flag-1); break; } case '\n': {break;} default : break; } } if(!strcmp(servo_info.pin,"0")){ //这是0 servo_info.GPIOx=GPIOB; servo_info.GPIO_Pin=GPIO_Pin_14; }else if(!strcmp(servo_info.pin,"1")){ //这是1 servo_info.GPIOx=GPIOB; servo_info.GPIO_Pin=GPIO_Pin_15; }else if(!strcmp(servo_info.pin,"2")){ //这是2 servo_info.GPIOx=GPIOE; servo_info.GPIO_Pin=GPIO_Pin_5; }else if(!strcmp(servo_info.pin,"3")){ //这是3 servo_info.GPIOx=GPIOE; servo_info.GPIO_Pin=GPIO_Pin_6; }else if(!strcmp(servo_info.pin,"4")){ //这是4 servo_info.GPIOx=GPIOE; servo_info.GPIO_Pin=GPIO_Pin_9; }else if(!strcmp(servo_info.pin,"5")){ //这是5 servo_info.GPIOx=GPIOE; servo_info.GPIO_Pin=GPIO_Pin_11; }else if(!strcmp(servo_info.pin,"6")){ //这是6 servo_info.GPIOx=GPIOE; servo_info.GPIO_Pin=GPIO_Pin_13; }else if(!strcmp(servo_info.pin,"7")){ //这是7 servo_info.GPIOx=GPIOE; servo_info.GPIO_Pin=GPIO_Pin_14; }else if(!strcmp(servo_info.pin,"8")){ //这是8 servo_info.GPIOx=GPIOC; servo_info.GPIO_Pin=GPIO_Pin_6; } else if(!strcmp(servo_info.pin,"9")){ //这是9 servo_info.GPIOx=GPIOC; servo_info.GPIO_Pin=GPIO_Pin_7; } else if(!strcmp(servo_info.pin,"10")){ //这是10 servo_info.GPIOx=GPIOC; servo_info.GPIO_Pin=GPIO_Pin_8; } else if(!strcmp(servo_info.pin,"11")){ //这是11 servo_info.GPIOx=GPIOC; servo_info.GPIO_Pin=GPIO_Pin_9; } else if(!strcmp(servo_info.pin,"12")){ //这是12 servo_info.GPIOx=GPIOD; servo_info.GPIO_Pin=GPIO_Pin_12; } else if(!strcmp(servo_info.pin,"13")){ //这是13 servo_info.GPIOx=GPIOD; servo_info.GPIO_Pin=GPIO_Pin_13; } else if(!strcmp(servo_info.pin,"14")){ //这是14 servo_info.GPIOx=GPIOD; servo_info.GPIO_Pin=GPIO_Pin_14; } else if(!strcmp(servo_info.pin,"15")){ //这是15 servo_info.GPIOx=GPIOD; servo_info.GPIO_Pin=GPIO_Pin_15; } else if(!strcmp(servo_info.pin,"16")){ //这是16 servo_info.GPIOx=GPIOB; servo_info.GPIO_Pin=GPIO_Pin_8; } else if(!strcmp(servo_info.pin,"17")){ //这是17 servo_info.GPIOx=GPIOB; servo_info.GPIO_Pin=GPIO_Pin_9; } } void Servo_move(Servo_info *s_info) { Servo_SetAngle_Pulse(s_info->GPIOx, s_info->GPIO_Pin, atoi(s_info->pulse)); } int main(void) { // 初始化外设 Delay_Init(); UART_Init(USART1,115200); Servo_Init(GPIOB, GPIO_Pin_14); Servo_Init(GPIOB, GPIO_Pin_15); Servo_Init(GPIOB, GPIO_Pin_8); Servo_Init(GPIOB, GPIO_Pin_9); Servo_Init(GPIOE, GPIO_Pin_5); Servo_Init(GPIOE, GPIO_Pin_6); Servo_Init(GPIOE, GPIO_Pin_9); Servo_Init(GPIOE, GPIO_Pin_11); Servo_Init(GPIOE, GPIO_Pin_13); Servo_Init(GPIOE, GPIO_Pin_14); Servo_Init(GPIOC, GPIO_Pin_6); Servo_Init(GPIOC, GPIO_Pin_7); Servo_Init(GPIOC, GPIO_Pin_8); Servo_Init(GPIOC, GPIO_Pin_9); Servo_Init(GPIOD, GPIO_Pin_12); Servo_Init(GPIOD, GPIO_Pin_13); Servo_Init(GPIOD, GPIO_Pin_14); Servo_Init(GPIOD, GPIO_Pin_15); Delay_ms(1000); while (1) { if(analyse_flag==1) { analyse_flag=0; analyse_data(buf); move_flag=1; } if(move_flag==1) { move_flag=0; Servo_move(&servo_info); } } } void USART1_IRQHandler() { unsigned char data; if(USART_GetITStatus(USART1,USART_IT_RXNE)== SET) { data=USART_ReceiveData(USART1); buf[rcnt++]=data; if(data=='\n') { buf[rcnt] = '\0'; analyse_flag=1; rcnt=0; } } } |
下载完成后,在电脑上安装CH340芯片驱动。然后将主控板通过Type-C线连接上电脑(要使用靠近开关的Type-C口,这个口是串口),并打开主控板电源,以便利用上位机进行调试。
② 双击打开Controller 1.0.exe:
③界面左上角选择:点击“设置→面板设置”,弹出需要显示的调试块,可通过勾选隐藏掉不需要的调试块,调试块里的数字是pwm值,范围500~2500,中值1500,分别对应舵机的0度~180度,以及中值90度。
④ 如果你用的是SH-F4TT扩展板,它有8个舵机接口。
因此最多只需要开启P0~P7(你的样机有几个舵机就开几个调试块),如图:
上位机舵机编号与SH-F4TT扩展板舵机引脚对应关系如下表所示:
⑤ 如果你用的是SH-F4ST扩展板,它有18个舵机接口。
因此最多只需要开启P0~P17(你的样机有几个舵机就开几个调试块),如图:
上位机舵机编号与SH-F4ST扩展板舵机引脚对应关系如下表所示:
⑥ 点击“联机”选择主控板对应端口号以及波特率(115200),并点击“连接”。
⑦ 用锂电池给主控板供电。拖动进度条,可以观察到对应舵机的摆动。通过拖动进度条或直接编辑舵机的PWM值,可调整样机达到所需的姿态,点击左下角的“添加”,即可获得一组舵机角度编码,再调整样机到达下一个姿态位,再点击“添加”,即可获得另一组舵机角度编码,以此类推,可以获得多组舵机编码,对应多个样机姿态位。最后点击“转化”,即可获得用于编程的舵机数组:
⑧ 该数组可直接复制到相应的MCU-F4程序中进行使用。
⑨ 示例项目:4自由度机械臂搬运
1.电子硬件:
2.参考代码如下:
main.c
五、资料清单
Controller1.0资料包-STM32.zip | 116.24MB | 下载40次 | 下载 |
1.支持平台
win10、win7
win10打开Controller 1.0.exe即可运行;win7需要先安装Controller1.0资料包\NetFarmwork文件夹中的.net框架组件。
2.电子硬件
我们用以下硬件为例来讲解Controller 1.0软件的使用:
| 主控板 | Basra主控板(兼容Arduino Uno) |
| 扩展板 | Bigfish2.1扩展板 |
SH-SR舵机扩展板 |
3.操作步骤
上位机:Controller 1.0
下位机编程环境:Arduino 1.8.19
① 使用 SH-SR舵机扩展板 调试舵机时,将Controller1.0资料包\Arduino\servo\servo.ino下载到主控板;
/*------------------------------------------------------------------------------------ 版权说明:Copyright 2023 Robottime(Beijing) Technology Co., Ltd. All Rights Reserved. Distributed under MIT license.See file LICENSE for detail or copy at https://opensource.org/licenses/MIT by 机器谱 2023-03-17 https://www.robotway.com/ ------------------------------*/ #include <Tlc5940.h> #include <tlc_servos.h> #define ANGLE_VALUE_MIN 0 #define ANGLE_VALUE_MAX 180 #define PWM_VALUE_MIN 500 #define PWM_VALUE_MAX 2500 int data_array[2] = {0,0}; //servo_pin: data_array[0], servo_value: data_array[1]; String data = ""; boolean dataComplete = false; void setup() { Serial.begin(9600); Tlc.init(0); tlc_initServos(); delay(1000); } void loop() {
while(Serial.available()) { int B_flag, P_flag, T_flag; data = Serial.readStringUntil('\n'); data.trim(); for(int i=0;i<data.length();i++) { //Serial.println(data[i]); switch(data[i]) { case '#': B_flag = i; break; case 'P': { String pin = ""; P_flag = i; for(int i=B_flag+1;i<P_flag;i++) { pin+=data[i]; } data_array[0] = pin.toInt(); } break; case 'T': { String angle = ""; T_flag = i; for(int i=P_flag+1;i<T_flag;i++) { angle += data[i]; } data_array[1] = angle.toInt(); } break; default: break; } }
/* Serial.println(B_flag); Serial.println(P_flag); Serial.println(T_flag);
for(int i=0;i<2;i++) { Serial.println(data_array[i]); } */
dataComplete = true; }
if(dataComplete) { if(data_array[1] >= ANGLE_VALUE_MIN && data_array[1] <= ANGLE_VALUE_MAX) { tlc_setServo(data_array[0], data_array[1]); } else if(data_array[1] >= PWM_VALUE_MIN && data_array[1] <= PWM_VALUE_MAX) { data_array[1] = map(data_array[1], 500, 2500, 0, 180); tlc_setServo(data_array[0], data_array[1]); } Tlc.update(); dataComplete = false; }
} |
/*------------------------------------------------------------------------------------ 版权说明:Copyright 2023 Robottime(Beijing) Technology Co., Ltd. All Rights Reserved. Distributed under MIT license.See file LICENSE for detail or copy at https://opensource.org/licenses/MIT by 机器谱 2023-03-17 https://www.robotway.com/ ------------------------------*/ /* * Bigfish扩展板舵机口; 4, 7, 11, 3, 8, 12, 14, 15, 16, 17, 18, 19 * 使用软件调节舵机时请拖拽对应序号的控制块 */ #include <Servo.h> #define ANGLE_VALUE_MIN 0 #define ANGLE_VALUE_MAX 180 #define PWM_VALUE_MIN 500 #define PWM_VALUE_MAX 2500 #define SERVO_NUM 12 Servo myServo[SERVO_NUM]; int data_array[2] = {0,0}; //servo_pin: data_array[0], servo_value: data_array[1]; int servo_port[SERVO_NUM] = {4, 7, 11, 3, 8, 12, 14, 15, 16, 17, 18, 19}; int servo_value[SERVO_NUM] = {}; String data = ""; boolean dataComplete = false; void setup() { Serial.begin(9600);
} void loop() {
while(Serial.available()) { int B_flag, P_flag, T_flag; data = Serial.readStringUntil('\n'); data.trim(); for(int i=0;i<data.length();i++) { //Serial.println(data[i]); switch(data[i]) { case '#': B_flag = i; break; case 'P': { String pin = ""; P_flag = i; for(int i=B_flag+1;i<P_flag;i++) { pin+=data[i]; } data_array[0] = pin.toInt(); } break; case 'T': { String angle = ""; T_flag = i; for(int i=P_flag+1;i<T_flag;i++) { angle += data[i]; } data_array[1] = angle.toInt(); servo_value[pin2index(data_array[0])] = data_array[1]; } break; default: break; } }
/* Serial.println(B_flag); Serial.println(P_flag); Serial.println(T_flag);
for(int i=0;i<2;i++) { Serial.println(data_array[i]); } */
dataComplete = true; }
if(dataComplete) { for(int i=0;i<SERVO_NUM;i++) { ServoGo(i, servo_value[i]); /*********************************串口查看输出***********************************/ // Serial.print(servo_value[i]); // Serial.print(" "); } // Serial.println(); /*********************************++++++++++++***********************************/ dataComplete = false; }
} void ServoStart(int which){ if(!myServo[which].attached() && (servo_value[which] != 0))myServo[which].attach(servo_port[which]); else return; pinMode(servo_port[which], OUTPUT); } void ServoStop(int which){ myServo[which].detach(); digitalWrite(servo_port[which],LOW); } void ServoGo(int which , int where){ ServoStart(which); if(where >= ANGLE_VALUE_MIN && where <= ANGLE_VALUE_MAX) { myServo[which].write(where); } else if(where >= PWM_VALUE_MIN && where <= PWM_VALUE_MAX) { myServo[which].writeMicroseconds(where); } } int pin2index(int _pin){ int index; switch(_pin) { case 4: index = 0; break; case 7: index = 1; break; case 11: index = 2; break; case 3: index = 3; break; case 8: index = 4; break; case 12: index = 5; break; case 14: index = 6; break; case 15: index = 7; break; case 16: index = 8; break; case 17: index = 9; break; case 18: index = 10; break; case 19: index = 11; break; } return index; } |
下载完成后,保持主控板和电脑的USB连接,并打开主控板电源,以便利用上位机进行调试。
② 双击打开Controller 1.0.exe:
③ 界面左上角选择:
设置-面板设置,弹出需要显示的调试块,可通过勾选隐藏不需要调试的舵机块:联机-选择主控板对应端口号以及波特率。
序号 | 内容 |
| 1 | Controller1.0资料包 |
【整体打包】-【U002】如何驱动模拟舵机-Controller 1.0-资料附件.zip | 66.94MB | 下载80次 | 下载 |
|
|