PRismino嵌入式平台:面向教育与原型开发的机器人控制套件

发布时间:2026/7/31 4:12:09

PRismino嵌入式平台:面向教育与原型开发的机器人控制套件 1. 项目概述Robopoly PRismino 是一款面向教育与原型开发的嵌入式控制平台由 Robopoly 团队设计并开源。它并非通用微控制器开发板而是一个高度集成的“功能即插即用”型硬件系统其核心价值在于将常见机器人与自动化场景中所需的底层驱动、电源管理、通信接口及运动控制逻辑全部固化于硬件与配套固件中使开发者可跳过繁琐的外设初始化、时序调试与驱动适配环节直接聚焦于上层控制策略与应用逻辑。该平台由两大部分构成PRismino 主控模块PRismino Core与Robopoly Shield 扩展板Robopoly Shield。二者通过标准排针物理堆叠连接电气上形成统一供电域与共享总线拓扑。PRismino Core 基于 STM32F401RE 微控制器ARM Cortex-M4F84 MHz512 KB Flash96 KB RAM搭载 USB-C 接口支持 CDC ACM 虚拟串口与 DFU 升级、板载 RGB LED、用户按键与复位电路Robopoly Shield 则是其功能扩展中枢集成了双路 H 桥电机驱动TB6612FNG、4 路独立 PWM 输出用于舵机或调光、2 路编码器输入接口、1 路 I²C 总线带电平转换兼容 3.3V/5V 传感器、1 路 UARTTTL 电平可配置为蓝牙/ESP32 透传通道、1 路 SPI预留用于 OLED 或 SD 卡、以及 12 路可编程 GPIO含 6 路模拟输入 ADC 引脚。Robopoly PRismino库以下简称 PRismino 库是官方提供的 C/C 软件抽象层专为该硬件组合定制。它不依赖 HAL 库的完整封装而是采用混合策略对时间敏感、需精确时序的模块如编码器捕获、PWM 同步输出使用 LLLow Layer寄存器级操作以保障实时性对通用外设如 UART、I²C则基于 STM32 HAL 库构建兼顾开发效率与稳定性。库的设计哲学是“最小侵入、最大可用”所有 API 均为静态链接无动态内存分配无阻塞式等待除明确标注的*_Blocking函数外完全适配裸机环境与 FreeRTOS 实时操作系统。2. 硬件架构与引脚映射理解 PRismino 的硬件布局是正确使用库的前提。下表列出了 Robopoly Shield 上所有对外功能接口及其在 STM32F401RE 上的物理引脚映射、默认复用功能与电气特性功能模块物理接口描述MCU 引脚 (AF)默认复用功能电气特性备注电机驱动 AOUTA1, OUTA2, PWMA, AIN1, AIN2PA0 (TIM2_CH1), PA1 (TIM2_CH2), PA8 (TIM1_CH1), PB0 (TIM3_CH3), PB1 (TIM3_CH4)TIMx_CHy, GPIO逻辑电平3.3V驱动能力1.2A/通道支持正反转、制动、待机PWMA 为 PWM 输入电机驱动 BOUTB1, OUTB2, PWMB, BIN1, BIN2PA2 (TIM2_CH3), PA3 (TIM2_CH4), PA9 (TIM1_CH2), PB10 (TIM2_CH3), PB11 (TIM2_CH4)TIMx_CHy, GPIO同上舵机/PWM 输出SERVO1–SERVO4PC6 (TIM3_CH1), PC7 (TIM3_CH2), PC8 (TIM3_CH3), PC9 (TIM3_CH4)TIM3_CHx5V 兼容 OC 输出频率可调默认 50 Hz专用 5V 供电轨隔离主控电源编码器输入 AENCA_A, ENCA_BPA6 (TIM3_CH1), PA7 (TIM3_CH2)TIM3_CH1/CH25V 兼容施密特触发输入支持四倍频计数需外部上拉编码器输入 BENCB_A, ENCB_BPB6 (TIM4_CH1), PB7 (TIM4_CH2)TIM4_CH1/CH2同上I²C 总线SCL, SDAPB6 (I2C1_SCL), PB7 (I2C1_SDA)I2C1开漏3.3V/5V 双模电平转换内置 4.7kΩ 上拉电阻UART 透传TX, RXPA2 (USART2_TX), PA3 (USART2_RX)USART2TTL 电平3.3V默认波特率 1152008N1SPI 总线SCK, MISO, MOSI, NSSPA5 (SPI1_SCK), PA6 (SPI1_MISO), PA7 (SPI1_MOSI), PA4 (SPI1_NSS)SPI13.3V 电平NSS 为软件控制ADC 输入A0–A5PA0 (ADC1_IN0), PA1 (ADC1_IN1), PA2 (ADC1_IN2), PA3 (ADC1_IN3), PC0 (ADC1_IN10), PC1 (ADC1_IN11)ADC112-bit参考电压 VREF 3.3V采样速率最高 2.4 MSPS关键设计说明电源隔离PRismino Core 与 Robopoly Shield 共享 5V 输入USB 或外部 DC但内部通过磁珠与 LDO 实现数字域3.3V与电机/舵机域5V的电源分割有效抑制大电流切换引入的噪声。编码器同步ENCA/ENCB 两组编码器均采用定时器输入捕获模式并启用ICPSCInput Capture Prescaler分频确保在电机高速旋转时仍能可靠计数。库内已预设TIM3和TIM4为编码器专用定时器避免与其他 PWM 资源冲突。PWM 分频策略4 路舵机 PWM 由TIM3驱动其时钟源为APB142 MHz经PSC4199、ARR19999配置后基准频率为 50 Hz周期 20 msCCR值范围500–2500对应 1–2 ms 脉宽符合标准舵机协议。3. 核心 API 接口详解PRismino 库提供三类核心 API初始化函数、控制函数与状态获取函数。所有函数声明位于prismino.h实现位于prismino.c无外部依赖除stm32f4xx_hal.h与stm32f4xx_ll_tim.h。3.1 初始化 API初始化是使用库的第一步必须在main()中HAL_Init()与SystemClock_Config()之后调用。/** * brief 初始化 PRismino 硬件平台 * param None * retval PRISMINO_OK: 初始化成功PRISMINO_ERROR: 初始化失败如时钟未就绪 * note 此函数执行以下操作 * 1. 使能所有相关外设时钟GPIOA/B/C, TIM1/2/3/4, USART2, I2C1, ADC1 * 2. 配置所有 GPIO 为复用推挽输出或浮空输入按功能需求 * 3. 初始化 TIM1/TIM2电机 PWM、TIM3舵机 PWM ENCA、TIM4ENCB * 4. 初始化 USART2虚拟串口、I2C1传感器总线、ADC1模拟输入 */ PRISMINO_StatusTypeDef PRISMINO_Init(void);3.2 电机控制 API电机控制采用双缓冲机制SetMotorSpeed()设置目标速度UpdateMotor()在后台定时器中断中执行实际 PWM 更新确保运动平滑无抖动。/** * brief 设置电机 A/B 的目标转速-100 ~ 100百分比 * param motor: MOTOR_A 或 MOTOR_B * param speed: 目标速度正数为正转负数为反转0 为停止 * retval None * note 速度值经内部查表线性映射为 PWM 占空比0~100%非直接写入 CCR */ void PRISMINO_SetMotorSpeed(PRISMINO_Motor_TypeDef motor, int8_t speed); /** * brief 执行电机 PWM 更新需在 1ms 定时器中断中周期调用 * param None * retval None * note 此函数不可在主循环中调用必须由 SysTick 或 TIMx 更新中断触发。 * 库已预配置 TIM6 为 1kHz 更新源用户只需在 TIM6_IRQHandler 中调用此函数。 */ void PRISMINO_UpdateMotor(void); /** * brief 立即设置电机 PWM 占空比0~100%Blocking * param motor: MOTOR_A 或 MOTOR_B * param duty: 占空比百分比0~100 * retval None * note 此函数绕过速度缓冲区直接写入 TIMx_CCR 寄存器适用于调试或紧急制动。 */ void PRISMINO_SetMotorDutyBlocking(PRISMINO_Motor_TypeDef motor, uint8_t duty);3.3 舵机控制 API舵机控制采用绝对位置模式输入为角度值0°~180°库内部完成角度到脉宽的转换。/** * brief 设置指定舵机的目标角度0 ~ 180 度 * param servo: SERVO1 ~ SERVO4 * param angle: 目标角度整数 * retval None * note 角度值被钳位至 [0, 180]内部映射为 500~2500 us 脉宽。 * 调用后立即生效无需额外更新函数。 */ void PRISMINO_SetServoAngle(PRISMINO_Servo_TypeDef servo, uint8_t angle); /** * brief 设置指定舵机的 PWM 脉宽us0 ~ 2500 * param servo: SERVO1 ~ SERVO4 * param pulse_us: 脉宽微秒值 * retval None * note 此为底层接口直接写入 TIM3_CCRx适用于非标准舵机或自定义波形。 */ void PRISMINO_SetServoPulse(PRISMINO_Servo_TypeDef servo, uint16_t pulse_us);3.4 编码器与传感器 API编码器提供累计计数值与方向信息I²C 接口提供标准读写封装。/** * brief 获取编码器 A 的当前累计计数值有符号 32-bit * param None * retval int32_t: 计数值正正转负反转 * note 计数值在 TIM3 中断中自动累加调用此函数为原子读取。 */ int32_t PRISMINO_GetEncoderA(void); /** * brief 获取编码器 B 的当前累计计数值有符号 32-bit * param None * retval int32_t: 计数值 */ int32_t PRISMINO_GetEncoderB(void); /** * brief I²C 写入数据带重试机制 * param dev_addr: 7-bit 设备地址左移一位LSB0 * param reg_addr: 寄存器地址1 字节 * param data: 待写入数据指针 * param size: 数据长度字节 * retval HAL_StatusTypeDef: HAL_OK 或 HAL_ERROR */ HAL_StatusTypeDef PRISMINO_I2C_Write(uint8_t dev_addr, uint8_t reg_addr, uint8_t *data, uint16_t size); /** * brief I²C 读取数据带重试机制 * param dev_addr: 7-bit 设备地址左移一位LSB0 * param reg_addr: 寄存器地址1 字节 * param data: 接收缓冲区指针 * param size: 读取长度字节 * retval HAL_StatusTypeDef: HAL_OK 或 HAL_ERROR */ HAL_StatusTypeDef PRISMINO_I2C_Read(uint8_t dev_addr, uint8_t reg_addr, uint8_t *data, uint16_t size);3.5 ADC 与通用 IO API提供简单易用的模拟量与数字量读取接口。/** * brief 读取指定 ADC 通道的 12-bit 数值0~4095 * param channel: ADC_CHANNEL_0 ~ ADC_CHANNEL_5对应 A0~A5 * retval uint16_t: ADC 原始值 */ uint16_t PRISMINO_ReadADC(PRISMINO_ADC_Channel_TypeDef channel); /** * brief 设置指定 GPIO 引脚为高/低电平 * param pin: GPIO_PIN_0 ~ GPIO_PIN_15仅限 PA0~PA15, PB0~PB15, PC0~PC15 * param state: GPIO_PIN_SET 或 GPIO_PIN_RESET * retval None * note 此函数直接操作 BSRR/BSRR 寄存器无 HAL 开销。 */ void PRISMINO_GPIO_Write(PRISMINO_GPIO_Pin_TypeDef pin, GPIO_PinState state); /** * brief 读取指定 GPIO 引脚电平 * param pin: GPIO_PIN_x * retval GPIO_PinState: GPIO_PIN_SET 或 GPIO_PIN_RESET */ GPIO_PinState PRISMINO_GPIO_Read(PRISMINO_GPIO_Pin_TypeDef pin);4. 典型应用示例解析4.1 双轮差速机器人闭环控制FreeRTOS 集成以下代码片段展示如何在 FreeRTOS 下实现基于编码器反馈的 PID 速度闭环。任务结构清晰分离encoder_task以 10ms 周期读取编码器并计算速度motor_task以 20ms 周期执行 PID 运算并下发控制量。#include prismino.h #include cmsis_os.h #define ENCODER_UPDATE_MS 10 #define MOTOR_UPDATE_MS 20 // 全局变量需加互斥锁或使用队列传递 static int32_t enc_a_last 0; static int32_t enc_b_last 0; static float target_speed_a 0.0f; // RPM static float target_speed_b 0.0f; // PID 参数示例 typedef struct { float Kp, Ki, Kd; float integral, last_error; } PID_Controller_t; PID_Controller_t pid_a {1.2f, 0.05f, 0.1f}; PID_Controller_t pid_b {1.2f, 0.05f, 0.1f}; // 编码器读取任务 void encoder_task(void const * argument) { osDelay(100); // 等待系统稳定 while(1) { int32_t enc_a_now PRISMINO_GetEncoderA(); int32_t enc_b_now PRISMINO_GetEncoderB(); // 计算 10ms 内的增量假设每转 1000 线 float rpm_a (enc_a_now - enc_a_last) * 60.0f / (1000.0f * ENCODER_UPDATE_MS / 1000.0f); float rpm_b (enc_b_now - enc_b_last) * 60.0f / (1000.0f * ENCODER_UPDATE_MS / 1000.0f); enc_a_last enc_a_now; enc_b_last enc_b_now; // 发送至 motor_task此处简化为全局变量生产环境建议用队列 // xQueueSend(encoder_queue, rpm_data, 0); osDelay(ENCODER_UPDATE_MS); } } // 电机控制任务 void motor_task(void const * argument) { osDelay(100); while(1) { // 读取当前速度简化版实际从队列获取 float current_rpm_a /* ... */; float current_rpm_b /* ... */; // PID 计算位置式 float error_a target_speed_a - current_rpm_a; pid_a.integral error_a * (MOTOR_UPDATE_MS / 1000.0f); float output_a pid_a.Kp * error_a pid_a.Ki * pid_a.integral pid_a.Kd * (error_a - pid_a.last_error); pid_a.last_error error_a; // 钳位输出至 [-100, 100] int8_t speed_cmd_a (int8_t)CLAMP(output_a, -100, 100); PRISMINO_SetMotorSpeed(MOTOR_A, speed_cmd_a); PRISMINO_UpdateMotor(); // 必须调用 osDelay(MOTOR_UPDATE_MS); } }4.2 I²C 传感器集成MPU6050 陀螺仪利用库的PRISMINO_I2C_Read快速读取 MPU6050 的原始加速度与角速度数据#include prismino.h #define MPU6050_ADDR 0x681 // 7-bit 地址 0x68左移后为 8-bit void read_mpu6050(void) { uint8_t data[14]; int16_t ax, ay, az, gx, gy, gz; // 连续读取 14 字节ACCEL_XOUT_H ~ GYRO_ZOUT_L if (PRISMINO_I2C_Read(MPU6050_ADDR, 0x3B, data, 14) HAL_OK) { ax (int16_t)((data[0] 8) | data[1]); ay (int16_t)((data[2] 8) | data[3]); az (int16_t)((data[4] 8) | data[5]); gx (int16_t)((data[8] 8) | data[9]); gy (int16_t)((data[10] 8) | data[11]); gz (int16_t)((data[12] 8) | data[13]); // 转换为物理量需根据量程配置 // 例如±2g 量程下1g 16384 LSB float acc_x_g (float)ax / 16384.0f; } }5. 高级配置与调试技巧5.1 自定义定时器中断源PRismino 库默认使用TIM6作为PRISMINO_UpdateMotor()的触发源。若需更高精度或与其他定时器冲突可重定向至TIM7// 在 PRISMINO_Init() 后添加 __HAL_RCC_TIM7_CLK_ENABLE(); htim7.Instance TIM7; htim7.Init.Prescaler 83; // 84MHz / (831) 1MHz htim7.Init.CounterMode TIM_COUNTERMODE_UP; htim7.Init.Period 999; // 1MHz / 1000 1kHz HAL_TIM_Base_Init(htim7); HAL_TIM_Base_Start_IT(htim7); // 修改 TIM7 中断服务函数 void TIM7_IRQHandler(void) { HAL_TIM_IRQHandler(htim7); } // 在 HAL_TIM_PeriodElapsedCallback 中调用 void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim) { if (htim-Instance TIM7) { PRISMINO_UpdateMotor(); } }5.2 低功耗模式适配在电池供电场景下可结合PRISMINO_GPIO_Write关闭非必要外设电源// 关闭舵机 5V 电源假设由 PC12 控制 PRISMINO_GPIO_Write(GPIO_PIN_12, GPIO_PIN_RESET); // 进入 Stop 模式需先关闭所有时钟 __HAL_RCC_PWR_CLK_ENABLE(); HAL_PWR_EnterSTOPMode(PWR_LOWPOWERREGULATOR_ON, PWR_STOPENTRY_WFI);5.3 故障诊断库内置错误检测机制可通过以下方式定位问题电机无响应检查PRISMINO_Init()返回值用万用表测量PWMA/PWMB引脚是否有 PWM 波形确认AIN1/AIN2电平是否符合预期方向。编码器计数异常用示波器观察ENCA_A/ENCA_B是否有方波检查PRISMINO_GetEncoderA()返回值是否随手动转动线性变化确认TIM3时钟使能与输入捕获配置。I²C 通信失败用逻辑分析仪抓取SCL/SDA波形检查设备地址是否正确注意 7-bit 与 8-bit 区别确认上拉电阻是否安装板载已焊接 4.7kΩ。6. 与主流生态的集成路径PRismino 库设计时已充分考虑与主流嵌入式生态的兼容性PlatformIO在platformio.ini中添加lib_deps https://github.com/robopoly/prismino.gitSTM32CubeMX生成初始化代码后在main.c中/* USER CODE BEGIN Includes */区域添加#include prismino.h并在main()中调用PRISMINO_Init()。Arduino IDE需将库文件复制至Arduino/libraries/并修改prismino.h中的#include stm32f4xx_hal.h为#include Arduino.h同时重映射HAL_Delay为delay。该平台已在多所高校机器人课程中验证典型项目包括基于 PID 的平衡小车、视觉循迹 AGV、六足机器人步态控制器、工业机械臂简易示教器。其价值不在于性能参数的极致而在于将嵌入式开发的“硬门槛”降至最低让工程师能以天为单位完成从想法到可运行原型的全过程。

相关新闻