This commit is contained in:
2025-11-25 21:25:41 +08:00
parent 09c8ef7be8
commit e2e275b6e4
117 changed files with 12755 additions and 0 deletions

BIN
assets/User_code/device/.DS_Store vendored Normal file

Binary file not shown.

View File

View File

@@ -0,0 +1,142 @@
/*
AI
*/
/* Includes ----------------------------------------------------------------- */
#include "ai.h"
#include <stdbool.h>
#include <string.h>
#include "bsp/time.h"
#include "bsp/uart.h"
#include "component/ahrs.h"
#include "component/crc16.h"
#include "component/crc8.h"
#include "component/user_math.h"
#include "component/filter.h"
/* Private define ----------------------------------------------------------- */
#define AI_LEN_RX_BUFF (sizeof(AI_DownPackage_t))
/* Private macro ------------------------------------------------------------ */
/* Private typedef ---------------------------------------------------------- */
/* Private variables -------------------------------------------------------- */
static uint8_t rxbuf[AI_LEN_RX_BUFF];
static bool inited = false;
static osThreadId_t thread_alert;
static uint32_t drop_message = 0;
// uint16_t crc16;
/* Private function -------------------------------------------------------- */
static void Ai_RxCpltCallback(void) {
osThreadFlagsSet(thread_alert, SIGNAL_AI_RAW_REDY);
}
static void Ai_IdleLineCallback(void) {
osThreadFlagsSet(thread_alert, SIGNAL_AI_RAW_REDY);
}
/* Exported functions ------------------------------------------------------- */
int8_t AI_Init(AI_t *ai) {
UNUSED(ai);
if (inited) return DEVICE_ERR_INITED;
thread_alert = osThreadGetId();
BSP_UART_RegisterCallback(BSP_UART_AI, BSP_UART_RX_CPLT_CB,
Ai_RxCpltCallback);
BSP_UART_RegisterCallback(BSP_UART_AI, BSP_UART_IDLE_LINE_CB,
Ai_IdleLineCallback);
inited = true;
return 0;
}
int8_t AI_Restart(AI_t *ai) {
UNUSED(ai);
__HAL_UART_DISABLE(BSP_UART_GetHandle(BSP_UART_AI));
__HAL_UART_ENABLE(BSP_UART_GetHandle(BSP_UART_AI));
return DEVICE_OK;
}
int8_t AI_StartReceiving(AI_t *ai) {
UNUSED(ai);
// if (HAL_UART_Receive_DMA(BSP_UART_GetHandle(BSP_UART_AI), rxbuf,
// AI_LEN_RX_BUFF) == HAL_OK)
if (BSP_UART_Receive(BSP_UART_AI, rxbuf,
AI_LEN_RX_BUFF, true) == HAL_OK)
return DEVICE_OK;
return DEVICE_ERR;
}
bool AI_WaitDmaCplt(void) {
return (osThreadFlagsWait(SIGNAL_AI_RAW_REDY, osFlagsWaitAll,0) ==
SIGNAL_AI_RAW_REDY);
}
int8_t AI_ParseHost(AI_t *ai) {
// crc16 = CRC16_Calc((const uint8_t *)&(rxbuf), sizeof(ai->from_host) - 2, CRC16_INIT);
if (!CRC16_Verify((const uint8_t *)&(rxbuf), sizeof(ai->from_host)))
goto error;
ai->header.online = true;
ai->header.last_online_time = BSP_TIME_Get();
memcpy(&(ai->from_host), rxbuf, sizeof(ai->from_host));
memset(rxbuf, 0, AI_LEN_RX_BUFF);
return DEVICE_OK;
error:
drop_message++;
return DEVICE_ERR;
}
int8_t AI_PackMCU(AI_t *ai, const AHRS_Quaternion_t *data){
if (ai == NULL || data == NULL) return DEVICE_ERR_NULL;
ai->to_host.mcu.id = AI_ID_MCU;
ai->to_host.mcu.package.quat=*data;
ai->to_host.mcu.package.notice = ai->status;
ai->to_host.mcu.crc16 = CRC16_Calc((const uint8_t *)&(ai->to_host.mcu), sizeof(AI_UpPackageMCU_t) - 2, CRC16_INIT);
return DEVICE_OK;
}
int8_t AI_PackRef(AI_t *ai, const AI_UpPackageReferee_t *data) {
if (ai == NULL || data == NULL) return DEVICE_ERR_NULL;
ai->to_host.ref = *data;
return DEVICE_OK;
}
int8_t AI_HandleOffline(AI_t *ai) {
if (ai == NULL) return DEVICE_ERR_NULL;
if (BSP_TIME_Get() - ai->header.last_online_time >
100000) {
ai->header.online = false;
}
return DEVICE_OK;
}
int8_t AI_StartSend(AI_t *ai, bool ref_online){
if (ai == NULL) return DEVICE_ERR_NULL;
if (ref_online) {
// 发送裁判系统数据和MCU数据
if (BSP_UART_Transmit(BSP_UART_AI, (uint8_t *)&(ai->to_host),
sizeof(ai->to_host.ref) + sizeof(ai->to_host.mcu), true) == HAL_OK)
return DEVICE_OK;
else
return DEVICE_ERR;
} else {
// 只发送MCU数据
if (BSP_UART_Transmit(BSP_UART_AI, (uint8_t *)&(ai->to_host.mcu),
sizeof(ai->to_host.mcu), true) == HAL_OK)
return DEVICE_OK;
else
return DEVICE_ERR;
}
}

View File

@@ -0,0 +1,131 @@
/*
AI
*/
#pragma once
#include <sys/cdefs.h>
#ifdef __cplusplus
extern "C" {
#endif
/* Includes ----------------------------------------------------------------- */
#include "component/ahrs.h"
#include "component/filter.h"
#include "component/user_math.h"
#include "device/device.h"
#include <cmsis_os2.h>
#include <stdbool.h>
#include <stdint.h>
/* Exported constants ------------------------------------------------------- */
/* Exported macro ----------------------------------------------------------- */
#define AI_ID_MCU (0xC4)
#define AI_ID_REF (0xA8)
#define AI_ID_AI (0xA1)
/* Exported types ----------------------------------------------------------- */
typedef enum {
AI_ARMOR_HERO = 0, /*英雄机器人*/
AI_ARMOR_INFANTRY, /*步兵机器人*/
AI_ARMOR_SENTRY, /*哨兵机器人*/
AI_ARMOR_ENGINEER, /*工程机器人*/
AI_ARMOR_OUTPOST, /*前哨占*/
AI_ARMOR_BASE, /*基地*/
AI_ARMOR_NORMAL, /*由AI自动选择*/
} AI_ArmorsType_t;
typedef enum {
AI_STATUS_OFF = 0, /* 关闭 */
AI_STATUS_AUTOAIM, /* 自瞄 */
AI_STATUS_AUTOPICK, /* 自动取矿 */
AI_STATUS_AUTOPUT, /* 自动兑矿 */
AI_STATUS_AUTOHITBUFF, /* 自动打符 */
AI_STATUS_AUTONAV,
} AI_Status_t;
typedef enum {
AI_NOTICE_NONE = 0,
AI_NOTICE_SEARCH,
AI_NOTICE_FIRE,
}AI_Notice_t;
/* 电控 -> 视觉 MCU数据结构体*/
typedef struct __packed {
AHRS_Quaternion_t quat; /* 四元数 */
// struct {
// AI_ArmorsType_t armor_type;
// AI_Status_t status;
// }notice; /* 控制命令 */
uint8_t notice;
} AI_Protucol_UpDataMCU_t;
/* 电控 -> 视觉 裁判系统数据结构体*/
typedef struct __packed {
/* USER REFEREE BEGIN */
uint16_t team; /* 本身队伍 */
uint16_t time; /* 比赛开始时间 */
/* USER REFEREE END */
} AI_Protocol_UpDataReferee_t;
/* 视觉 -> 电控 数据包结构体*/
typedef struct __packed {
AHRS_Eulr_t eulr; /* 欧拉角 */
MoveVector_t move_vec; /* 运动向量 */
uint8_t notice; /* 控制命令 */
} AI_Protocol_DownData_t;
/* 电控 -> 视觉 裁判系统数据包 */
typedef struct __packed {
uint8_t id; /* 包ID */
AI_Protocol_UpDataReferee_t package; /* 数据包 */
uint16_t crc16; /* CRC16校验 */
} AI_UpPackageReferee_t;
/* 电控 -> 视觉 MUC数据包 */
typedef struct __packed {
uint8_t id;
AI_Protucol_UpDataMCU_t package;
uint16_t crc16;
} AI_UpPackageMCU_t;
/* 视觉 -> 电控 数据包 */
typedef struct __packed {
uint8_t id; /* 包ID */
AI_Protocol_DownData_t package; /* 数据包 */
uint16_t crc16; /* CRC16校验 */
} AI_DownPackage_t;
typedef struct __packed {
DEVICE_Header_t header; /* 设备通用头部 */
AI_DownPackage_t from_host;
AI_Status_t status;
struct {
AI_UpPackageReferee_t ref;
AI_UpPackageMCU_t mcu;
} to_host;
} AI_t;
/* Exported functions prototypes -------------------------------------------- */
int8_t AI_Init(AI_t *ai);
int8_t AI_Restart(AI_t *ai);
int8_t AI_StartReceiving(AI_t *ai);
bool AI_WaitDmaCplt(void);
int8_t AI_ParseHost(AI_t *ai);
int8_t AI_PackMCU(AI_t *ai, const AHRS_Quaternion_t *quat);
int8_t AI_PackRef(AI_t *ai, const AI_UpPackageReferee_t *data);
int8_t AI_HandleOffline(AI_t *ai);
int8_t AI_StartSend(AI_t *ai, bool ref_online);
#ifdef __cplusplus
}
#endif

View File

@@ -0,0 +1,381 @@
/*
BMI088 陀螺仪+加速度计传感器。
*/
/* Includes ----------------------------------------------------------------- */
#include "bmi088.h"
#include <cmsis_os2.h>
#include <gpio.h>
#include <stdbool.h>
#include <string.h>
#include "bsp/time.h"
#include "bsp/gpio.h"
#include "bsp/spi.h"
#include "component/user_math.h"
/* USER INCLUDE BEGIN */
/* USER INCLUDE END */
/* Private define ----------------------------------------------------------- */
/* Private define ----------------------------------------------------------- */
#define BMI088_REG_ACCL_CHIP_ID (0x00)
#define BMI088_REG_ACCL_ERR (0x02)
#define BMI088_REG_ACCL_STATUS (0x03)
#define BMI088_REG_ACCL_X_LSB (0x12)
#define BMI088_REG_ACCL_X_MSB (0x13)
#define BMI088_REG_ACCL_Y_LSB (0x14)
#define BMI088_REG_ACCL_Y_MSB (0x15)
#define BMI088_REG_ACCL_Z_LSB (0x16)
#define BMI088_REG_ACCL_Z_MSB (0x17)
#define BMI088_REG_ACCL_SENSORTIME_0 (0x18)
#define BMI088_REG_ACCL_SENSORTIME_1 (0x19)
#define BMI088_REG_ACCL_SENSORTIME_2 (0x1A)
#define BMI088_REG_ACCL_INT_STAT_1 (0x1D)
#define BMI088_REG_ACCL_TEMP_MSB (0x22)
#define BMI088_REG_ACCL_TEMP_LSB (0x23)
#define BMI088_REG_ACCL_CONF (0x40)
#define BMI088_REG_ACCL_RANGE (0x41)
#define BMI088_REG_ACCL_INT1_IO_CONF (0x53)
#define BMI088_REG_ACCL_INT2_IO_CONF (0x54)
#define BMI088_REG_ACCL_INT1_INT2_MAP_DATA (0x58)
#define BMI088_REG_ACCL_SELF_TEST (0x6D)
#define BMI088_REG_ACCL_PWR_CONF (0x7C)
#define BMI088_REG_ACCL_PWR_CTRL (0x7D)
#define BMI088_REG_ACCL_SOFTRESET (0x7E)
#define BMI088_REG_GYRO_CHIP_ID (0x00)
#define BMI088_REG_GYRO_X_LSB (0x02)
#define BMI088_REG_GYRO_X_MSB (0x03)
#define BMI088_REG_GYRO_Y_LSB (0x04)
#define BMI088_REG_GYRO_Y_MSB (0x05)
#define BMI088_REG_GYRO_Z_LSB (0x06)
#define BMI088_REG_GYRO_Z_MSB (0x07)
#define BMI088_REG_GYRO_INT_STAT_1 (0x0A)
#define BMI088_REG_GYRO_RANGE (0x0F)
#define BMI088_REG_GYRO_BANDWIDTH (0x10)
#define BMI088_REG_GYRO_LPM1 (0x11)
#define BMI088_REG_GYRO_SOFTRESET (0x14)
#define BMI088_REG_GYRO_INT_CTRL (0x15)
#define BMI088_REG_GYRO_INT3_INT4_IO_CONF (0x16)
#define BMI088_REG_GYRO_INT3_INT4_IO_MAP (0x18)
#define BMI088_REG_GYRO_SELF_TEST (0x3C)
#define BMI088_CHIP_ID_ACCL (0x1E)
#define BMI088_CHIP_ID_GYRO (0x0F)
#define BMI088_LEN_RX_BUFF (19)
/* Private macro ------------------------------------------------------------ */
#define BMI088_ACCL_NSS_SET() \
BSP_GPIO_WritePin(BSP_GPIO_ACCL_CS, GPIO_PIN_SET)
#define BMI088_ACCL_NSS_RESET() \
BSP_GPIO_WritePin(BSP_GPIO_ACCL_CS, GPIO_PIN_RESET)
#define BMI088_GYRO_NSS_SET() \
BSP_GPIO_WritePin(BSP_GPIO_GYRO_CS, GPIO_PIN_SET)
#define BMI088_GYRO_NSS_RESET() \
BSP_GPIO_WritePin(BSP_GPIO_GYRO_CS, GPIO_PIN_RESET)
/* Private typedef ---------------------------------------------------------- */
typedef enum {
BMI_ACCL,
BMI_GYRO,
} BMI_Device_t;
/* USER STRUCT BEGIN */
/* USER STRUCT END */
/* Private variables -------------------------------------------------------- */
static uint8_t buffer[2];
static uint8_t bmi088_rxbuf[BMI088_LEN_RX_BUFF];
static osThreadId_t thread_alert;
static bool inited = false;
/* Private function -------------------------------------------------------- */
/* USER FUNCTION BEGIN */
/* USER FUNCTION END */
static void BMI_WriteSingle(BMI_Device_t dv, uint8_t reg, uint8_t data) {
buffer[0] = (reg & 0x7f);
buffer[1] = data;
BSP_TIME_Delay(1);
switch (dv) {
case BMI_ACCL:
BMI088_ACCL_NSS_RESET();
break;
case BMI_GYRO:
BMI088_GYRO_NSS_RESET();
break;
}
BSP_SPI_Transmit(BSP_SPI_BMI088, buffer, 2u, false);
switch (dv) {
case BMI_ACCL:
BMI088_ACCL_NSS_SET();
break;
case BMI_GYRO:
BMI088_GYRO_NSS_SET();
break;
}
}
static uint8_t BMI_ReadSingle(BMI_Device_t dv, uint8_t reg) {
BSP_TIME_Delay(1);
switch (dv) {
case BMI_ACCL:
BMI088_ACCL_NSS_RESET();
break;
case BMI_GYRO:
BMI088_GYRO_NSS_RESET();
break;
}
buffer[0] = (uint8_t)(reg | 0x80);
BSP_SPI_Transmit(BSP_SPI_BMI088, buffer, 1u, false);
BSP_SPI_Receive(BSP_SPI_BMI088, buffer, 2u, false);
switch (dv) {
case BMI_ACCL:
BMI088_ACCL_NSS_SET();
return buffer[1];
case BMI_GYRO:
BMI088_GYRO_NSS_SET();
return buffer[0];
}
}
static void BMI_Read(BMI_Device_t dv, uint8_t reg, uint8_t *data, uint8_t len) {
if (data == NULL) return;
switch (dv) {
case BMI_ACCL:
BMI088_ACCL_NSS_RESET();
break;
case BMI_GYRO:
BMI088_GYRO_NSS_RESET();
break;
}
buffer[0] = (uint8_t)(reg | 0x80);
BSP_SPI_Transmit(BSP_SPI_BMI088, buffer, 1u, false);
BSP_SPI_Receive(BSP_SPI_BMI088, data, len, true);
}
static void BMI088_RxCpltCallback(void) {
if (BSP_GPIO_ReadPin(BSP_GPIO_ACCL_CS) == GPIO_PIN_RESET) {
BMI088_ACCL_NSS_SET();
osThreadFlagsSet(thread_alert, SIGNAL_BMI088_ACCL_RAW_REDY);
}
if (BSP_GPIO_ReadPin(BSP_GPIO_GYRO_CS) == GPIO_PIN_RESET) {
BMI088_GYRO_NSS_SET();
osThreadFlagsSet(thread_alert, SIGNAL_BMI088_GYRO_RAW_REDY);
}
}
static void BMI088_AcclIntCallback(void) {
osThreadFlagsSet(thread_alert, SIGNAL_BMI088_ACCL_NEW_DATA);
}
static void BMI088_GyroIntCallback(void) {
osThreadFlagsSet(thread_alert, SIGNAL_BMI088_GYRO_NEW_DATA);
}
/* Exported functions ------------------------------------------------------- */
int8_t BMI088_Init(BMI088_t *bmi088, const BMI088_Cali_t *cali) {
if (bmi088 == NULL) return DEVICE_ERR_NULL;
if (cali == NULL) return DEVICE_ERR_NULL;
if (inited) return DEVICE_ERR_INITED;
if ((thread_alert = osThreadGetId()) == NULL) return DEVICE_ERR_NULL;
bmi088->cali = cali;
BMI_WriteSingle(BMI_ACCL, BMI088_REG_ACCL_SOFTRESET, 0xB6);
BMI_WriteSingle(BMI_GYRO, BMI088_REG_GYRO_SOFTRESET, 0xB6);
BSP_TIME_Delay(30);
/* Switch accl to SPI mode. */
BMI_ReadSingle(BMI_ACCL, BMI088_CHIP_ID_ACCL);
if (BMI_ReadSingle(BMI_ACCL, BMI088_REG_ACCL_CHIP_ID) != BMI088_CHIP_ID_ACCL)
return DEVICE_ERR_NO_DEV;
if (BMI_ReadSingle(BMI_GYRO, BMI088_REG_GYRO_CHIP_ID) != BMI088_CHIP_ID_GYRO)
return DEVICE_ERR_NO_DEV;
BSP_GPIO_DisableIRQ(BSP_GPIO_ACCL_INT);
BSP_GPIO_DisableIRQ(BSP_GPIO_GYRO_INT);
BSP_SPI_RegisterCallback(BSP_SPI_BMI088, BSP_SPI_RX_CPLT_CB,
BMI088_RxCpltCallback);
BSP_GPIO_RegisterCallback(BSP_GPIO_ACCL_INT, BMI088_AcclIntCallback);
BSP_GPIO_RegisterCallback(BSP_GPIO_GYRO_INT, BMI088_GyroIntCallback);
/* Accl init. */
/* Filter setting: Normal. */
/* ODR: 0xAB: 800Hz. 0xAA: 400Hz. 0xA9: 200Hz. 0xA8: 100Hz. 0xA6: 25Hz. */
BMI_WriteSingle(BMI_ACCL, BMI088_REG_ACCL_CONF, 0xAA);
/* 0x00: +-3G. 0x01: +-6G. 0x02: +-12G. 0x03: +-24G. */
BMI_WriteSingle(BMI_ACCL, BMI088_REG_ACCL_RANGE, 0x01);
/* INT1 as output. Push-pull. Active low. Output. */
BMI_WriteSingle(BMI_ACCL, BMI088_REG_ACCL_INT1_IO_CONF, 0x08);
/* Map data ready interrupt to INT1. */
BMI_WriteSingle(BMI_ACCL, BMI088_REG_ACCL_INT1_INT2_MAP_DATA, 0x04);
/* Turn on accl. Now we can read data. */
BMI_WriteSingle(BMI_ACCL, BMI088_REG_ACCL_PWR_CTRL, 0x04);
BSP_TIME_Delay(50);
/* Gyro init. */
/* 0x00: +-2000. 0x01: +-1000. 0x02: +-500. 0x03: +-250. 0x04: +-125. */
BMI_WriteSingle(BMI_GYRO, BMI088_REG_GYRO_RANGE, 0x01);
/* Filter bw: 47Hz. */
/* ODR: 0x02: 1000Hz. 0x03: 400Hz. 0x06: 200Hz. 0x07: 100Hz. */
BMI_WriteSingle(BMI_GYRO, BMI088_REG_GYRO_BANDWIDTH, 0x03);
/* INT3 and INT4 as output. Push-pull. Active low. */
BMI_WriteSingle(BMI_GYRO, BMI088_REG_GYRO_INT3_INT4_IO_CONF, 0x00);
/* Map data ready interrupt to INT3. */
BMI_WriteSingle(BMI_GYRO, BMI088_REG_GYRO_INT3_INT4_IO_MAP, 0x01);
/* Enable new data interrupt. */
BMI_WriteSingle(BMI_GYRO, BMI088_REG_GYRO_INT_CTRL, 0x80);
BSP_TIME_Delay(10);
inited = true;
BSP_GPIO_EnableIRQ(BSP_GPIO_ACCL_INT);
BSP_GPIO_EnableIRQ(BSP_GPIO_GYRO_INT);
return DEVICE_OK;
}
bool BMI088_GyroStable(AHRS_Gyro_t *gyro) {
return ((gyro->x < 0.03f) && (gyro->y < 0.03f) && (gyro->z < 0.03f));
}
uint32_t BMI088_WaitNew() {
return osThreadFlagsWait(
SIGNAL_BMI088_ACCL_NEW_DATA | SIGNAL_BMI088_GYRO_NEW_DATA, osFlagsWaitAll,
osWaitForever);
}
int8_t BMI088_AcclStartDmaRecv() {
BMI_Read(BMI_ACCL, BMI088_REG_ACCL_X_LSB, bmi088_rxbuf, BMI088_LEN_RX_BUFF);
return DEVICE_OK;
}
uint32_t BMI088_AcclWaitDmaCplt() {
return osThreadFlagsWait(SIGNAL_BMI088_ACCL_RAW_REDY, osFlagsWaitAll,
osWaitForever);
}
int8_t BMI088_GyroStartDmaRecv() {
BMI_Read(BMI_GYRO, BMI088_REG_GYRO_X_LSB, bmi088_rxbuf + 7, 6u);
return DEVICE_OK;
}
uint32_t BMI088_GyroWaitDmaCplt() {
return osThreadFlagsWait(SIGNAL_BMI088_GYRO_RAW_REDY, osFlagsWaitAll,
osWaitForever);
}
int8_t BMI088_ParseAccl(BMI088_t *bmi088) {
if (bmi088 == NULL) return DEVICE_ERR_NULL;
#if 1
int16_t raw_x, raw_y, raw_z;
memcpy(&raw_x, bmi088_rxbuf + 1, sizeof(raw_x));
memcpy(&raw_y, bmi088_rxbuf + 3, sizeof(raw_y));
memcpy(&raw_z, bmi088_rxbuf + 5, sizeof(raw_z));
bmi088->accl.x = (float)raw_x;
bmi088->accl.y = (float)raw_y;
bmi088->accl.z = (float)raw_z;
#else
const int16_t *praw_x = (int16_t *)(bmi088_rxbuf + 1);
const int16_t *praw_y = (int16_t *)(bmi088_rxbuf + 3);
const int16_t *praw_z = (int16_t *)(bmi088_rxbuf + 5);
bmi088->accl.x = (float)*praw_x;
bmi088->accl.y = (float)*praw_y;
bmi088->accl.z = (float)*praw_z;
#endif
/* 3G: 10920. 6G: 5460. 12G: 2730. 24G: 1365. */
bmi088->accl.x /= 5460.0f;
bmi088->accl.y /= 5460.0f;
bmi088->accl.z /= 5460.0f;
int16_t raw_temp =
(uint16_t)((bmi088_rxbuf[17] << 3) | (bmi088_rxbuf[18] >> 5));
if (raw_temp > 1023) raw_temp -= 2048;
bmi088->temp = (float)raw_temp * 0.125f + 23.0f;
return DEVICE_OK;
}
int8_t BMI088_ParseGyro(BMI088_t *bmi088) {
if (bmi088 == NULL) return DEVICE_ERR_NULL;
#if 1
/* Gyroscope imu_raw -> degrees/sec -> radians/sec */
int16_t raw_x, raw_y, raw_z;
memcpy(&raw_x, bmi088_rxbuf + 7, sizeof(raw_x));
memcpy(&raw_y, bmi088_rxbuf + 9, sizeof(raw_y));
memcpy(&raw_z, bmi088_rxbuf + 11, sizeof(raw_z));
bmi088->gyro.x = (float)raw_x;
bmi088->gyro.y = (float)raw_y;
bmi088->gyro.z = (float)raw_z;
#else
/* Gyroscope imu_raw -> degrees/sec -> radians/sec */
const int16_t *raw_x = (int16_t *)(bmi088_rxbuf + 7);
const int16_t *raw_y = (int16_t *)(bmi088_rxbuf + 9);
const int16_t *raw_z = (int16_t *)(bmi088_rxbuf + 11);
bmi088->gyro.x = (float)*raw_x;
bmi088->gyro.y = (float)*raw_y;
bmi088->gyro.z = (float)*raw_z;
#endif
/* FS125: 262.144. FS250: 131.072. FS500: 65.536. FS1000: 32.768.
* FS2000: 16.384.*/
bmi088->gyro.x /= 32.768f;
bmi088->gyro.y /= 32.768f;
bmi088->gyro.z /= 32.768f;
bmi088->gyro.x *= M_DEG2RAD_MULT;
bmi088->gyro.y *= M_DEG2RAD_MULT;
bmi088->gyro.z *= M_DEG2RAD_MULT;
bmi088->gyro.x -= bmi088->cali->gyro_offset.x;
bmi088->gyro.y -= bmi088->cali->gyro_offset.y;
bmi088->gyro.z -= bmi088->cali->gyro_offset.z;
return DEVICE_ERR_NULL;
}
float BMI088_GetUpdateFreq(BMI088_t *bmi088) {
(void)bmi088;
return 400.0f;
}

View File

@@ -0,0 +1,81 @@
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
/* Includes ----------------------------------------------------------------- */
#include <stdbool.h>
#include <stdint.h>
#include "component/ahrs.h"
#include "device/device.h"
/* USER INCLUDE BEGIN */
/* USER INCLUDE END */
/* USER DEFINE BEGIN */
/* USER DEFINE END */
/* Exported constants ------------------------------------------------------- */
/* Exported macro ----------------------------------------------------------- */
/* Exported types ----------------------------------------------------------- */
typedef struct {
struct {
float x;
float y;
float z;
} gyro_offset; /* 陀螺仪偏置 */
} BMI088_Cali_t; /* BMI088校准数据 */
typedef struct {
DEVICE_Header_t header;
AHRS_Accl_t accl;
AHRS_Gyro_t gyro;
float temp; /* 温度 */
const BMI088_Cali_t *cali;
} BMI088_t;
/* USER STRUCT BEGIN */
/* USER STRUCT END */
/* Exported functions prototypes -------------------------------------------- */
int8_t BMI088_Init(BMI088_t *bmi088, const BMI088_Cali_t *cali);
int8_t BMI088_Restart(void);
bool BMI088_GyroStable(AHRS_Gyro_t *gyro);
/* Sensor use right-handed coordinate system. */
/*
x < R(logo)
y
UP is z
All implementation should follow this rule.
*/
uint32_t BMI088_WaitNew();
/*
BMI088的Accl和Gyro共用同一个DMA通道所以一次只能读一个传感器。
即BMI088_AcclStartDmaRecv() 和 BMI088_AcclWaitDmaCplt() 中间不能
出现 BMI088_GyroStartDmaRecv()。
*/
int8_t BMI088_AcclStartDmaRecv();
uint32_t BMI088_AcclWaitDmaCplt();
int8_t BMI088_GyroStartDmaRecv();
uint32_t BMI088_GyroWaitDmaCplt();
int8_t BMI088_ParseAccl(BMI088_t *bmi088);
int8_t BMI088_ParseGyro(BMI088_t *bmi088);
float BMI088_GetUpdateFreq(BMI088_t *bmi088);
/* USER FUNCTION BEGIN */
/* USER FUNCTION END */
#ifdef __cplusplus
}
#endif

View File

@@ -0,0 +1,171 @@
#include "device/buzzer.h"
#include "bsp/time.h"
#include <math.h>
/* USER INCLUDE BEGIN */
/* USER INCLUDE END */
/* USER DEFINE BEGIN */
/* USER DEFINE END */
#define MUSIC_DEFAULT_VOLUME 0.5f
#define MUSIC_A4_FREQ 440.0f // A4音符频率
/* USER MUSIC MENU BEGIN */
// RM音乐
const Tone_t RM[] = {
{NOTE_B, 5, 200},
{NOTE_G, 4, 200},
{NOTE_B, 5, 400},
{NOTE_G, 4, 200},
{NOTE_B, 5, 400},
{NOTE_G, 4, 200},
{NOTE_D, 5, 400},
{NOTE_G, 4, 200},
{NOTE_C, 5, 200},
{NOTE_C, 5, 200},
{NOTE_G, 4, 200},
{NOTE_B, 5, 200},
{NOTE_C, 5, 200}
};
// Nokia 经典铃声音符
const Tone_t NOKIA[] = {
{NOTE_E, 5, 125}, {NOTE_D, 5, 125}, {NOTE_FS, 4, 250}, {NOTE_GS, 4, 250},
{NOTE_CS, 5, 125}, {NOTE_B, 4, 125}, {NOTE_D, 4, 250}, {NOTE_E, 4, 250},
{NOTE_B, 4, 125}, {NOTE_A, 4, 125}, {NOTE_CS, 4, 250}, {NOTE_E, 4, 250},
{NOTE_A, 4, 500}
};
/* USER MUSIC MENU END */
static void BUZZER_Update(BUZZER_t *buzzer){
buzzer->header.online = true;
buzzer->header.last_online_time = BSP_TIME_Get_ms();
}
// 根据音符和八度计算频率的辅助函数
static float BUZZER_CalcFreq(NOTE_t note, uint8_t octave) {
if (note == NOTE_REST) {
return 0.0f; // 休止符返回0频率
}
// 将音符和八度转换为MIDI音符编号
int midi_num = (int)note + (int)((octave + 1) * 12);
// 使用A4 (440Hz) 作为参考,计算频率
// 公式: freq = 440 * 2^((midi_num - 69)/12)
float freq = 440.0f * powf(2.0f, ((float)midi_num - 69.0f) / 12.0f);
return freq;
}
// 播放单个音符
static int8_t BUZZER_PlayTone(BUZZER_t *buzzer, NOTE_t note, uint8_t octave, uint16_t duration_ms) {
if (buzzer == NULL || !buzzer->header.online)
return DEVICE_ERR;
float freq = BUZZER_CalcFreq(note, octave);
if (freq > 0.0f) {
// 播放音符
if (BUZZER_Set(buzzer, freq, MUSIC_DEFAULT_VOLUME) != DEVICE_OK)
return DEVICE_ERR;
if (BUZZER_Start(buzzer) != DEVICE_OK)
return DEVICE_ERR;
} else {
// 休止符,停止播放
BUZZER_Stop(buzzer);
}
// 等待指定时间
BSP_TIME_Delay_ms(duration_ms);
// 停止当前音符,为下一个音符做准备
BUZZER_Stop(buzzer);
BSP_TIME_Delay_ms(20); // 短暂间隔
return DEVICE_OK;
}
int8_t BUZZER_Init(BUZZER_t *buzzer, BSP_PWM_Channel_t channel) {
if (buzzer == NULL) return DEVICE_ERR;
buzzer->channel = channel;
buzzer->header.online = true;
BUZZER_Stop(buzzer);
return DEVICE_OK ;
}
int8_t BUZZER_Start(BUZZER_t *buzzer) {
if (buzzer == NULL || !buzzer->header.online)
return DEVICE_ERR;
BUZZER_Update(buzzer);
return (BSP_PWM_Start(buzzer->channel) == BSP_OK) ?
DEVICE_OK : DEVICE_ERR;
}
int8_t BUZZER_Stop(BUZZER_t *buzzer) {
if (buzzer == NULL || !buzzer->header.online)
return DEVICE_ERR;
BUZZER_Update(buzzer);
return (BSP_PWM_Stop(buzzer->channel) == BSP_OK) ?
DEVICE_OK : DEVICE_ERR;
}
int8_t BUZZER_Set(BUZZER_t *buzzer, float freq, float duty_cycle) {
if (buzzer == NULL || !buzzer->header.online)
return DEVICE_ERR;
int result = DEVICE_OK ;
BUZZER_Update(buzzer);
if (BSP_PWM_SetFreq(buzzer->channel, freq) != BSP_OK)
result = DEVICE_ERR;
if (BSP_PWM_SetComp(buzzer->channel, duty_cycle) != BSP_OK)
result = DEVICE_ERR;
return result;
}
int8_t BUZZER_PlayMusic(BUZZER_t *buzzer, MUSIC_t music) {
if (buzzer == NULL || !buzzer->header.online)
return DEVICE_ERR;
const Tone_t *melody = NULL;
size_t melody_length = 0;
// 根据音乐类型选择对应的音符数组
switch (music) {
case MUSIC_RM:
melody = RM;
melody_length = sizeof(RM) / sizeof(Tone_t);
break;
case MUSIC_NOKIA:
melody = NOKIA;
melody_length = sizeof(NOKIA) / sizeof(Tone_t);
break;
default:
return DEVICE_ERR;
}
// 播放整首音乐
for (size_t i = 0; i < melody_length; i++) {
if (BUZZER_PlayTone(buzzer, melody[i].note, melody[i].octave, melody[i].duration_ms) != DEVICE_OK) {
BUZZER_Stop(buzzer); // 出错时停止播放
return DEVICE_ERR;
}
}
// 音乐播放完成后停止
BUZZER_Stop(buzzer);
return DEVICE_OK;
}
/* USER FUNCTION BEGIN */
/* USER FUNCTION END */

View File

@@ -0,0 +1,138 @@
/**
* @file buzzer.h
* @brief 蜂鸣器设备驱动头文件
* @details 提供蜂鸣器音频播放功能,支持单音符播放和预设音乐播放
* @author Generated by STM32CubeMX
* @date 2025年10月23日
*/
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
/* Includes ----------------------------------------------------------------- */
#include "bsp/pwm.h" // PWM底层硬件抽象层
#include "device.h" // 设备通用头文件
#include <stddef.h> // 标准定义
#include <stdint.h> // 标准整型定义
/* USER INCLUDE BEGIN */
/* USER INCLUDE END */
/* Exported constants ------------------------------------------------------- */
/* USER DEFINE BEGIN */
/* USER DEFINE END */
/* Exported types ----------------------------------------------------------- */
/**
* @brief 音符枚举类型
* @details 定义标准十二平均律音符,用于音乐播放
*/
typedef enum {
NOTE_C = 0, ///< Do音符
NOTE_CS = 1, ///< Do#音符 (升Do)
NOTE_D = 2, ///< Re音符
NOTE_DS = 3, ///< Re#音符 (升Re)
NOTE_E = 4, ///< Mi音符
NOTE_F = 5, ///< Fa音符
NOTE_FS = 6, ///< Fa#音符 (升Fa)
NOTE_G = 7, ///< Sol音符
NOTE_GS = 8, ///< Sol#音符 (升Sol)
NOTE_A = 9, ///< La音符
NOTE_AS = 10, ///< La#音符 (升La)
NOTE_B = 11, ///< Si音符
NOTE_REST = 255 ///< 休止符 (无声音)
} NOTE_t;
/**
* @brief 音调结构体
* @details 定义一个完整的音调信息,包括音符、八度和持续时间
*/
typedef struct {
NOTE_t note; ///< 音符名称 (使用NOTE_t枚举)
uint8_t octave; ///< 八度 (0-8通常使用3-7)
uint16_t duration_ms; ///< 持续时间,单位毫秒
} Tone_t;
/**
* @brief 预设音乐枚举类型
* @details 定义可播放的预设音乐类型
*/
typedef enum {
/* USER MUSIC MENU BEGIN */
MUSIC_RM, ///< RM战队音乐
MUSIC_NOKIA, ///< 诺基亚经典铃声
/* USER MUSIC MENU END */
} MUSIC_t;
/**
* @brief 蜂鸣器设备结构体
* @details 蜂鸣器设备的完整描述包含设备头信息和PWM通道
*/
typedef struct {
DEVICE_Header_t header; ///< 设备通用头信息 (在线状态、时间戳等)
BSP_PWM_Channel_t channel; ///< PWM输出通道
} BUZZER_t;
/* USER STRUCT BEGIN */
/* USER STRUCT END */
/* Exported functions prototypes -------------------------------------------- */
/**
* @brief 初始化蜂鸣器设备
* @param buzzer 蜂鸣器设备结构体指针
* @param channel PWM输出通道
* @return int8_t 返回值DEVICE_OK(0) 成功DEVICE_ERR(-1) 失败
* @note 初始化后蜂鸣器处于停止状态
*/
int8_t BUZZER_Init(BUZZER_t *buzzer, BSP_PWM_Channel_t channel);
/**
* @brief 启动蜂鸣器播放
* @param buzzer 蜂鸣器设备结构体指针
* @return int8_t 返回值DEVICE_OK(0) 成功DEVICE_ERR(-1) 失败
* @note 需要先调用BUZZER_Set设置频率和占空比
*/
int8_t BUZZER_Start(BUZZER_t *buzzer);
/**
* @brief 停止蜂鸣器播放
* @param buzzer 蜂鸣器设备结构体指针
* @return int8_t 返回值DEVICE_OK(0) 成功DEVICE_ERR(-1) 失败
*/
int8_t BUZZER_Stop(BUZZER_t *buzzer);
/**
* @brief 设置蜂鸣器频率和占空比
* @param buzzer 蜂鸣器设备结构体指针
* @param freq 频率 (Hz)通常范围20Hz-20kHz
* @param duty_cycle 占空比 (0.0-1.0),影响音量大小
* @return int8_t 返回值DEVICE_OK(0) 成功DEVICE_ERR(-1) 失败
* @note 设置后需要调用BUZZER_Start才能听到声音
*/
int8_t BUZZER_Set(BUZZER_t *buzzer, float freq, float duty_cycle);
/**
* @brief 播放预设音乐
* @param buzzer 蜂鸣器设备结构体指针
* @param music 音乐类型 (使用MUSIC_t枚举)
* @return int8_t 返回值DEVICE_OK(0) 成功DEVICE_ERR(-1) 失败
* @note 这是一个阻塞函数,会播放完整首音乐后返回
*/
int8_t BUZZER_PlayMusic(BUZZER_t *buzzer, MUSIC_t music);
/* USER FUNCTION BEGIN */
/* USER FUNCTION END */
#ifdef __cplusplus
}
#endif

View File

@@ -0,0 +1,227 @@
devices:
dr16:
name: "DR16"
description: "大疆遥控器接收机"
dependencies:
bsp: ["uart"]
component: ["user_math"]
bsp_requirements:
- type: "uart"
var_name: "BSP_UART_DR16" # 需要替换的变量名
description: "用于接收遥控器数据"
thread_signals:
- name: "SIGNAL_DR16_RAW_REDY"
files:
header: "dr16.h"
source: "dr16.c"
ops9:
name: "OPS9"
description: "ACTION OPS9 码盘"
dependencies:
bsp: ["uart"]
component: ["user_math"]
bsp_requirements:
- type: "uart"
var_name: "BSP_UART_OPS9" # 需要替换的变量名
description: "用于接收码盘"
thread_signals:
- name: "SIGNAL_OPS9_RAW_REDY"
files:
header: "ops9.h"
source: "ops9.c"
bmi088:
name: "BMI088"
description: "BMI088 陀螺仪+加速度计传感器"
dependencies:
bsp: ["spi", "gpio"]
component: ["user_math"]
bsp_requirements:
- type: "spi"
var_name: "BSP_SPI_BMI088"
description: "用于与 BMI088 通信的 SPI 总线"
- type: "gpio"
var_name: "BSP_GPIO_ACCL_CS"
description: "加速度计片选输出引脚"
gpio_type: "output"
- type: "gpio"
var_name: "BSP_GPIO_GYRO_CS"
description: "陀螺仪片选输出引脚"
gpio_type: "output"
- type: "gpio"
var_name: "BSP_GPIO_ACCL_INT"
description: "加速度计中断输入引脚"
gpio_type: "EXTI"
- type: "gpio"
var_name: "BSP_GPIO_GYRO_INT"
description: "陀螺仪中断输入引脚"
gpio_type: "EXTI"
thread_signals:
- name: "SIGNAL_BMI088_ACCL_RAW_REDY"
- name: "SIGNAL_BMI088_GYRO_RAW_REDY"
- name: "SIGNAL_BMI088_ACCL_NEW_DATA"
- name: "SIGNAL_BMI088_GYRO_NEW_DATA"
files:
header: "bmi088.h"
source: "bmi088.c"
ist8310:
name: "IST8310"
description: "IST8310 地磁传感器"
dependencies:
bsp: ["i2c", "gpio"]
component: []
bsp_requirements:
- type: "i2c"
var_name: "BSP_I2C_COMP"
description: "用于与 IST8310 通信的 I2C 总线"
- type: "gpio"
var_name: "CMPS_RST_Pin"
description: "IST8310 复位引脚"
gpio_type: "output"
- type: "gpio"
var_name: "CMPS_INT_Pin"
description: "IST8310 数据中断引脚"
gpio_type: "EXTI"
thread_signals:
- name: "SIGNAL_IST8310_MAGN_RAW_REDY"
- name: "SIGNAL_IST8310_MAGN_NEW_DATA"
files:
header: "ist8310.h"
source: "ist8310.c"
motor_vesc:
name: "VESC 电调"
description: "VESC 电调驱动"
dependencies:
bsp: ["can", "time", "mm"]
component: ["user_math"]
files:
header: "motor_vesc.h"
source: "motor_vesc.c"
motor_odrive:
name: "ODrive 电机"
description: "ODrive 电机驱动"
dependencies:
bsp: ["can", "time", "mm"]
component: ["user_math"]
files:
header: "motor_odrive.h"
source: "motor_odrive.c"
motor_rm:
name: "RM 电机"
description: "RM 电机驱动"
dependencies:
bsp: ["can", "time", "mm"]
component: ["user_math"]
files:
header: "motor_rm.h"
source: "motor_rm.c"
motor:
name: "通用电机"
description: "通用电机驱动"
dependencies:
bsp: []
component: []
bsp_requirements: []
thread_signals: []
files:
header: "motor.h"
source: "motor.c"
ws2812:
name: "WS2812 LED 灯"
description: "WS2812 RGB LED 灯驱动"
dependencies:
bsp: ["pwm", "time"]
component: []
thread_signals: []
files:
header: "ws2812.h"
source: "ws2812.c"
buzzer:
name: "蜂鸣器"
description: "蜂鸣器驱动"
dependencies:
bsp: ["pwm"]
component: []
bsp_requirements:
- type: "pwm"
var_name: "BSP_PWM_BUZZER"
description: "用于蜂鸣器的PWM通道"
thread_signals: []
files:
header: "buzzer.h"
source: "buzzer.c"
dm_imu:
name: "DM IMU"
description: "DM IMU 传感器"
dependencies:
bsp: ["can", "time"]
component: ["user_math"]
files:
header: "dm_imu.h"
source: "dm_imu.c"
led:
name: "LED 灯"
description: "LED 灯驱动"
dependencies:
bsp: ["gpio", "pwm"]
component: []
thread_signals: []
files:
header: "led.h"
source: "led.c"
motor_lk:
name: "LK 电机"
description: "LK 电机驱动"
dependencies:
bsp: ["can", "time", "mm"]
component: ["user_math"]
files:
header: "motor_lk.h"
source: "motor_lk.c"
motor_lz:
name: "LZ 电机"
description: "LZ 电机驱动"
dependencies:
bsp: ["can", "time", "mm"]
component: ["user_math"]
files:
header: "motor_lz.h"
source: "motor_lz.c"
servo:
name: "舵机"
description: "舵机驱动"
dependencies:
bsp: ["pwm"]
component: []
thread_signals: []
files:
header: "servo.h"
source: "servo.c"
vofa:
name: "VOFA"
description: "VOFA 数据传输协议"
dependencies:
bsp: ["uart"]
component: []
bsp_requirements:
- type: "uart"
var_name: "BSP_UART_VOFA" # 需要替换的变量名
description: "用于VOFA数据传输"
thread_signals: []
files:
header: "vofa.h"
source: "vofa.c"

View File

@@ -0,0 +1,47 @@
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
#include <stdbool.h>
#include <stdint.h>
/* USER INCLUDE BEGIN */
/* USER INCLUDE END */
/* USER DEFINE BEGIN */
/* USER DEFINE END */
#define DEVICE_OK (0)
#define DEVICE_ERR (-1)
#define DEVICE_ERR_NULL (-2)
#define DEVICE_ERR_INITED (-3)
#define DEVICE_ERR_NO_DEV (-4)
/* AUTO GENERATED SIGNALS BEGIN */
/* AUTO GENERATED SIGNALS END */
/* USER SIGNALS BEGIN */
/* USER SIGNALS END */
/*设备层通用Header*/
typedef struct {
bool online;
uint64_t last_online_time;
} DEVICE_Header_t;
/* USER STRUCT BEGIN */
/* USER STRUCT END */
/* USER FUNCTION BEGIN */
/* USER FUNCTION END */
#ifdef __cplusplus
}
#endif

View File

@@ -0,0 +1,271 @@
/*
DM_IMU数据获取CAN
*/
/* Includes ----------------------------------------------------------------- */
#include "dm_imu.h"
#include "bsp/can.h"
#include "bsp/time.h"
#include "component/user_math.h"
#include <string.h>
/* Private define ----------------------------------------------------------- */
#define DM_IMU_OFFLINE_TIMEOUT 1000 // 设备离线判定时间1000ms
#define ACCEL_CAN_MAX (58.8f)
#define ACCEL_CAN_MIN (-58.8f)
#define GYRO_CAN_MAX (34.88f)
#define GYRO_CAN_MIN (-34.88f)
#define PITCH_CAN_MAX (90.0f)
#define PITCH_CAN_MIN (-90.0f)
#define ROLL_CAN_MAX (180.0f)
#define ROLL_CAN_MIN (-180.0f)
#define YAW_CAN_MAX (180.0f)
#define YAW_CAN_MIN (-180.0f)
#define TEMP_MIN (0.0f)
#define TEMP_MAX (60.0f)
#define Quaternion_MIN (-1.0f)
#define Quaternion_MAX (1.0f)
/* Private macro ------------------------------------------------------------ */
/* Private typedef ---------------------------------------------------------- */
/* Private variables -------------------------------------------------------- */
/* Private function --------------------------------------------------------- */
static uint8_t count = 0; // 计数器,用于判断设备是否离线
/**
* @brief: 无符号整数转换为浮点数函数
*/
static float uint_to_float(int x_int, float x_min, float x_max, int bits)
{
float span = x_max - x_min;
float offset = x_min;
return ((float)x_int)*span/((float)((1<<bits)-1)) + offset;
}
/**
* @brief 解析加速度计数据
*/
static int8_t DM_IMU_ParseAccelData(DM_IMU_t *imu, uint8_t *data, uint8_t len) {
if (imu == NULL || data == NULL || len < 8) {
return DEVICE_ERR;
}
int8_t temp = data[1];
uint16_t acc_x_raw = (data[3] << 8) | data[2];
uint16_t acc_y_raw = (data[5] << 8) | data[4];
uint16_t acc_z_raw = (data[7] << 8) | data[6];
imu->data.temp = (float)temp;
imu->data.accl.x = uint_to_float(acc_x_raw, ACCEL_CAN_MIN, ACCEL_CAN_MAX, 16);
imu->data.accl.y = uint_to_float(acc_y_raw, ACCEL_CAN_MIN, ACCEL_CAN_MAX, 16);
imu->data.accl.z = uint_to_float(acc_z_raw, ACCEL_CAN_MIN, ACCEL_CAN_MAX, 16);
return DEVICE_OK;
}
/**
* @brief 解析陀螺仪数据
*/
static int8_t DM_IMU_ParseGyroData(DM_IMU_t *imu, uint8_t *data, uint8_t len) {
if (imu == NULL || data == NULL || len < 8) {
return DEVICE_ERR;
}
uint16_t gyro_x_raw = (data[3] << 8) | data[2];
uint16_t gyro_y_raw = (data[5] << 8) | data[4];
uint16_t gyro_z_raw = (data[7] << 8) | data[6];
imu->data.gyro.x = uint_to_float(gyro_x_raw, GYRO_CAN_MIN, GYRO_CAN_MAX, 16);
imu->data.gyro.y = uint_to_float(gyro_y_raw, GYRO_CAN_MIN, GYRO_CAN_MAX, 16);
imu->data.gyro.z = uint_to_float(gyro_z_raw, GYRO_CAN_MIN, GYRO_CAN_MAX, 16);
return DEVICE_OK;
}
/**
* @brief 解析欧拉角数据
*/
static int8_t DM_IMU_ParseEulerData(DM_IMU_t *imu, uint8_t *data, uint8_t len) {
if (imu == NULL || data == NULL || len < 8) {
return DEVICE_ERR;
}
uint16_t pit_raw = (data[3] << 8) | data[2];
uint16_t yaw_raw = (data[5] << 8) | data[4];
uint16_t rol_raw = (data[7] << 8) | data[6];
imu->data.euler.pit = uint_to_float(pit_raw, PITCH_CAN_MIN, PITCH_CAN_MAX, 16) * M_DEG2RAD_MULT;
imu->data.euler.yaw = uint_to_float(yaw_raw, YAW_CAN_MIN, YAW_CAN_MAX, 16) * M_DEG2RAD_MULT;
imu->data.euler.rol = uint_to_float(rol_raw, ROLL_CAN_MIN, ROLL_CAN_MAX, 16) * M_DEG2RAD_MULT;
return DEVICE_OK;
}
/**
* @brief 解析四元数数据
*/
static int8_t DM_IMU_ParseQuaternionData(DM_IMU_t *imu, uint8_t *data, uint8_t len) {
if (imu == NULL || data == NULL || len < 8) {
return DEVICE_ERR;
}
int w = (data[1] << 6) | ((data[2] & 0xF8) >> 2);
int x = ((data[2] & 0x03) << 12) | (data[3] << 4) | ((data[4] & 0xF0) >> 4);
int y = ((data[4] & 0x0F) << 10) | (data[5] << 2) | ((data[6] & 0xC0) >> 6);
int z = ((data[6] & 0x3F) << 8) | data[7];
imu->data.quat.q0 = uint_to_float(w, Quaternion_MIN, Quaternion_MAX, 14);
imu->data.quat.q1 = uint_to_float(x, Quaternion_MIN, Quaternion_MAX, 14);
imu->data.quat.q2 = uint_to_float(y, Quaternion_MIN, Quaternion_MAX, 14);
imu->data.quat.q3 = uint_to_float(z, Quaternion_MIN, Quaternion_MAX, 14);
return DEVICE_OK;
}
/* Exported functions ------------------------------------------------------- */
/**
* @brief 初始化DM IMU设备
*/
int8_t DM_IMU_Init(DM_IMU_t *imu, DM_IMU_Param_t *param) {
if (imu == NULL || param == NULL) {
return DEVICE_ERR_NULL;
}
// 初始化设备头部
imu->header.online = false;
imu->header.last_online_time = 0;
// 配置参数
imu->param.can = param->can;
imu->param.can_id = param->can_id;
imu->param.device_id = param->device_id;
imu->param.master_id = param->master_id;
// 清零数据
memset(&imu->data, 0, sizeof(DM_IMU_Data_t));
// 注册CAN接收队列用于接收回复报文
int8_t result = BSP_CAN_RegisterId(imu->param.can, imu->param.master_id, 10);
if (result != BSP_OK) {
return DEVICE_ERR;
}
return DEVICE_OK;
}
/**
* @brief 请求IMU数据
*/
int8_t DM_IMU_Request(DM_IMU_t *imu, DM_IMU_RID_t rid) {
if (imu == NULL) {
return DEVICE_ERR_NULL;
}
// 构造发送数据id_L, id_H(DM_IMU_ID), RID, 0xcc
uint8_t tx_data[4] = {
imu->param.device_id & 0xFF, // id_L
(imu->param.device_id >> 8) & 0xFF, // id_H
(uint8_t)rid, // RID
0xCC // 固定值
};
// 发送标准数据帧
BSP_CAN_StdDataFrame_t frame = {
.id = imu->param.can_id,
.dlc = 4,
};
memcpy(frame.data, tx_data, 4);
int8_t result = BSP_CAN_TransmitStdDataFrame(imu->param.can, &frame);
return (result == BSP_OK) ? DEVICE_OK : DEVICE_ERR;
}
/**
* @brief 更新IMU数据从CAN中获取所有数据并解析
*/
int8_t DM_IMU_Update(DM_IMU_t *imu) {
if (imu == NULL) {
return DEVICE_ERR_NULL;
}
BSP_CAN_Message_t msg;
int8_t result;
bool data_received = false;
// 持续接收所有可用消息
while ((result = BSP_CAN_GetMessage(imu->param.can, imu->param.master_id, &msg, BSP_CAN_TIMEOUT_IMMEDIATE)) == BSP_OK) {
// 验证回复数据格式(至少检查数据长度)
if (msg.dlc < 3) {
continue; // 跳过无效消息
}
// 根据数据位的第0位确定反馈报文类型
uint8_t rid = msg.data[0] & 0x0F; // 取第0位的低4位作为RID
// 根据RID类型解析数据
int8_t parse_result = DEVICE_ERR;
switch (rid) {
case 0x01: // RID_ACCL
parse_result = DM_IMU_ParseAccelData(imu, msg.data, msg.dlc);
break;
case 0x02: // RID_GYRO
parse_result = DM_IMU_ParseGyroData(imu, msg.data, msg.dlc);
break;
case 0x03: // RID_EULER
parse_result = DM_IMU_ParseEulerData(imu, msg.data, msg.dlc);
break;
case 0x04: // RID_QUATERNION
parse_result = DM_IMU_ParseQuaternionData(imu, msg.data, msg.dlc);
break;
default:
continue; // 跳过未知类型的消息
}
// 如果解析成功,标记为收到数据
if (parse_result == DEVICE_OK) {
data_received = true;
}
}
// 如果收到任何有效数据,更新设备状态
if (data_received) {
imu->header.online = true;
imu->header.last_online_time = BSP_TIME_Get_ms();
return DEVICE_OK;
}
return DEVICE_ERR;
}
/**
* @brief 自动更新IMU所有数据包括加速度计、陀螺仪、欧拉角和四元数,最高1khz
*/
int8_t DM_IMU_AutoUpdateAll(DM_IMU_t *imu){
if (imu == NULL) {
return DEVICE_ERR_NULL;
}
switch (count) {
case 0:
DM_IMU_Request(imu, RID_ACCL);
break;
case 1:
DM_IMU_Request(imu, RID_GYRO);
break;
case 2:
DM_IMU_Request(imu, RID_EULER);
break;
case 3:
DM_IMU_Request(imu, RID_QUATERNION);
DM_IMU_Update(imu); // 更新所有数据
break;
}
count++;
if (count >= 4) {
count = 0; // 重置计数器
return DEVICE_OK;
}
return DEVICE_ERR;
}
/**
* @brief 检查设备是否在线
*/
bool DM_IMU_IsOnline(DM_IMU_t *imu) {
if (imu == NULL) {
return false;
}
uint32_t current_time = BSP_TIME_Get_ms();
return imu->header.online &&
(current_time - imu->header.last_online_time < DM_IMU_OFFLINE_TIMEOUT);
}

View File

@@ -0,0 +1,90 @@
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
/* Includes ----------------------------------------------------------------- */
#include "device/device.h"
#include "component/ahrs.h"
#include "bsp/can.h"
/* Exported constants ------------------------------------------------------- */
#define DM_IMU_CAN_ID_DEFAULT 0x6FF
#define DM_IMU_ID_DEFAULT 0x42
#define DM_IMU_MST_ID_DEFAULT 0x43
/* Exported macro ----------------------------------------------------------- */
/* Exported types ----------------------------------------------------------- */
typedef struct {
BSP_CAN_t can; // CAN总线句柄
uint16_t can_id; // CAN通信ID
uint8_t device_id; // 设备ID
uint8_t master_id; // 主机ID
} DM_IMU_Param_t;
typedef enum {
RID_ACCL = 0x01, // 加速度计
RID_GYRO = 0x02, // 陀螺仪
RID_EULER = 0x03, // 欧拉角
RID_QUATERNION = 0x04, // 四元数
} DM_IMU_RID_t;
typedef struct {
AHRS_Accl_t accl; // 加速度计
AHRS_Gyro_t gyro; // 陀螺仪
AHRS_Eulr_t euler; // 欧拉角
AHRS_Quaternion_t quat; // 四元数
float temp; // 温度
} DM_IMU_Data_t;
typedef struct {
DEVICE_Header_t header;
DM_IMU_Param_t param; // IMU参数配置
DM_IMU_Data_t data; // IMU数据
} DM_IMU_t;
/* Exported functions prototypes -------------------------------------------- */
/**
* @brief 初始化DM IMU设备
* @param imu DM IMU设备结构体指针
* @param param IMU参数配置指针
* @return DEVICE_OK 成功,其他值失败
*/
int8_t DM_IMU_Init(DM_IMU_t *imu, DM_IMU_Param_t *param);
/**
* @brief 请求IMU数据
* @param imu DM IMU设备结构体指针
* @param rid 请求的数据类型
* @return DEVICE_OK 成功,其他值失败
*/
int8_t DM_IMU_Request(DM_IMU_t *imu, DM_IMU_RID_t rid);
/**
* @brief 更新IMU数据从CAN中获取所有数据并解析
* @param imu DM IMU设备结构体指针
* @return DEVICE_OK 成功,其他值失败
*/
int8_t DM_IMU_Update(DM_IMU_t *imu);
/**
* @brief 自动更新IMU所有数据包括加速度计、陀螺仪、欧拉角和四元数,最高1khz运行4次才有完整数据
* @param imu DM IMU设备结构体指针
* @return DEVICE_OK 成功,其他值失败
*/
int8_t DM_IMU_AutoUpdateAll(DM_IMU_t *imu);
/**
* @brief 检查设备是否在线
* @param imu DM IMU设备结构体指针
* @return true 在线false 离线
*/
bool DM_IMU_IsOnline(DM_IMU_t *imu);
#ifdef __cplusplus
}
#endif

View File

@@ -0,0 +1,169 @@
/*
DR16接收机
Example
DR16_Init(&dr16);
while (1) {
DR16_StartDmaRecv(&dr16);
if (DR16_WaitDmaCplt(20)) {
DR16_ParseData(&dr16);
} else {
DR16_Offline(&dr16);
}
}
*/
/* Includes ----------------------------------------------------------------- */
#include "dr16.h"
#include "bsp/uart.h"
#include "bsp/time.h"
#include "device.h"
#include <string.h>
#include <stdbool.h>
/* USER INCLUDE BEGIN */
/* USER INCLUDE END */
/* Private define ----------------------------------------------------------- */
#define DR16_CH_VALUE_MIN (364u)
#define DR16_CH_VALUE_MID (1024u)
#define DR16_CH_VALUE_MAX (1684u)
/* USER DEFINE BEGIN */
/* USER DEFINE END */
/* Private macro ------------------------------------------------------------ */
/* Private typedef ---------------------------------------------------------- */
/* Private variables -------------------------------------------------------- */
static osThreadId_t thread_alert;
static bool inited = false;
/* Private function -------------------------------------------------------- */
static void DR16_RxCpltCallback(void) {
osThreadFlagsSet(thread_alert, SIGNAL_DR16_RAW_REDY);
}
static bool DR16_DataCorrupted(const DR16_t *dr16) {
if (dr16 == NULL) return DEVICE_ERR_NULL;
if ((dr16->raw_data.ch_r_x < DR16_CH_VALUE_MIN) ||
(dr16->raw_data.ch_r_x > DR16_CH_VALUE_MAX))
return DEVICE_ERR;
if ((dr16->raw_data.ch_r_y < DR16_CH_VALUE_MIN) ||
(dr16->raw_data.ch_r_y > DR16_CH_VALUE_MAX))
return DEVICE_ERR;
if ((dr16->raw_data.ch_l_x < DR16_CH_VALUE_MIN) ||
(dr16->raw_data.ch_l_x > DR16_CH_VALUE_MAX))
return DEVICE_ERR;
if ((dr16->raw_data.ch_l_y < DR16_CH_VALUE_MIN) ||
(dr16->raw_data.ch_l_y > DR16_CH_VALUE_MAX))
return DEVICE_ERR;
if (dr16->raw_data.sw_l == 0) return DEVICE_ERR;
if (dr16->raw_data.sw_r == 0) return DEVICE_ERR;
return DEVICE_OK;
}
/* Exported functions ------------------------------------------------------- */
int8_t DR16_Init(DR16_t *dr16) {
if (dr16 == NULL) return DEVICE_ERR_NULL;
if (inited) return DEVICE_ERR_INITED;
if ((thread_alert = osThreadGetId()) == NULL) return DEVICE_ERR_NULL;
BSP_UART_RegisterCallback(BSP_UART_DR16, BSP_UART_RX_CPLT_CB,
DR16_RxCpltCallback);
inited = true;
return DEVICE_OK;
}
int8_t DR16_Restart(void) {
__HAL_UART_DISABLE(BSP_UART_GetHandle(BSP_UART_DR16));
__HAL_UART_ENABLE(BSP_UART_GetHandle(BSP_UART_DR16));
return DEVICE_OK;
}
int8_t DR16_StartDmaRecv(DR16_t *dr16) {
if (HAL_UART_Receive_DMA(BSP_UART_GetHandle(BSP_UART_DR16),
(uint8_t *)&(dr16->raw_data),
sizeof(dr16->raw_data)) == HAL_OK)
return DEVICE_OK;
return DEVICE_ERR;
}
bool DR16_WaitDmaCplt(uint32_t timeout) {
return (osThreadFlagsWait(SIGNAL_DR16_RAW_REDY, osFlagsWaitAll, timeout) ==
SIGNAL_DR16_RAW_REDY);
}
int8_t DR16_ParseData(DR16_t *dr16){
if (dr16 == NULL) return DEVICE_ERR_NULL;
if (DR16_DataCorrupted(dr16)) {
return DEVICE_ERR;
}
dr16->header.online = true;
dr16->header.last_online_time = BSP_TIME_Get_us();
memset(&(dr16->data), 0, sizeof(dr16->data));
float full_range = (float)(DR16_CH_VALUE_MAX - DR16_CH_VALUE_MIN);
// 解析摇杆数据
dr16->data.ch_r_x = 2.0f * ((float)dr16->raw_data.ch_r_x - DR16_CH_VALUE_MID) / full_range;
dr16->data.ch_r_y = 2.0f * ((float)dr16->raw_data.ch_r_y - DR16_CH_VALUE_MID) / full_range;
dr16->data.ch_l_x = 2.0f * ((float)dr16->raw_data.ch_l_x - DR16_CH_VALUE_MID) / full_range;
dr16->data.ch_l_y = 2.0f * ((float)dr16->raw_data.ch_l_y - DR16_CH_VALUE_MID) / full_range;
// 解析拨杆位置
dr16->data.sw_l = (DR16_SwitchPos_t)dr16->raw_data.sw_l;
dr16->data.sw_r = (DR16_SwitchPos_t)dr16->raw_data.sw_r;
// 解析鼠标数据
dr16->data.mouse.x = dr16->raw_data.x;
dr16->data.mouse.y = dr16->raw_data.y;
dr16->data.mouse.z = dr16->raw_data.z;
dr16->data.mouse.l_click = dr16->raw_data.press_l;
dr16->data.mouse.r_click = dr16->raw_data.press_r;
// 解析键盘按键 - 使用union简化代码
uint16_t key_value = dr16->raw_data.key;
// 解析键盘位映射W-B键位0-15
for (int i = DR16_KEY_W; i <= DR16_KEY_B; i++) {
dr16->data.keyboard.key[i] = (key_value & (1 << i)) != 0;
}
// 解析鼠标点击
dr16->data.keyboard.key[DR16_L_CLICK] = dr16->data.mouse.l_click;
dr16->data.keyboard.key[DR16_R_CLICK] = dr16->data.mouse.r_click;
// 解析第五通道
dr16->data.ch_res = 2.0f * ((float)dr16->raw_data.res - DR16_CH_VALUE_MID) / full_range;
return DEVICE_OK;
}
int8_t DR16_Offline(DR16_t *dr16){
if (dr16 == NULL) return DEVICE_ERR_NULL;
dr16->header.online = false;
memset(&(dr16->data), 0, sizeof(dr16->data));
return DEVICE_OK;
}
/* USER FUNCTION BEGIN */
/* USER FUNCTION END */

View File

@@ -0,0 +1,117 @@
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
/* Includes ----------------------------------------------------------------- */
#include <cmsis_os2.h>
#include "component/user_math.h"
#include "device.h"
/* USER INCLUDE BEGIN */
/* USER INCLUDE END */
/* USER DEFINE BEGIN */
/* USER DEFINE END */
/* Exported constants ------------------------------------------------------- */
/* Exported macro ----------------------------------------------------------- */
/* Exported types ----------------------------------------------------------- */
typedef struct __packed {
uint16_t ch_r_x : 11;
uint16_t ch_r_y : 11;
uint16_t ch_l_x : 11;
uint16_t ch_l_y : 11;
uint8_t sw_r : 2;
uint8_t sw_l : 2;
int16_t x;
int16_t y;
int16_t z;
uint8_t press_l;
uint8_t press_r;
uint16_t key;
uint16_t res;
} DR16_RawData_t;
typedef enum {
DR16_SW_ERR = 0,
DR16_SW_UP = 1,
DR16_SW_MID = 3,
DR16_SW_DOWN = 2,
} DR16_SwitchPos_t;
/* 键盘按键值 */
typedef enum {
DR16_KEY_W = 0,
DR16_KEY_S,
DR16_KEY_A,
DR16_KEY_D,
DR16_KEY_SHIFT,
DR16_KEY_CTRL,
DR16_KEY_Q,
DR16_KEY_E,
DR16_KEY_R,
DR16_KEY_F,
DR16_KEY_G,
DR16_KEY_Z,
DR16_KEY_X,
DR16_KEY_C,
DR16_KEY_V,
DR16_KEY_B,
DR16_L_CLICK,
DR16_R_CLICK,
DR16_KEY_NUM,
} DR16_Key_t;
typedef struct {
float ch_l_x; /* 遥控器左侧摇杆横轴值,上为正 */
float ch_l_y; /* 遥控器左侧摇杆纵轴值,右为正 */
float ch_r_x; /* 遥控器右侧摇杆横轴值,上为正 */
float ch_r_y; /* 遥控器右侧摇杆纵轴值,右为正 */
float ch_res; /* 第五通道值 */
DR16_SwitchPos_t sw_r; /* 右侧拨杆位置 */
DR16_SwitchPos_t sw_l; /* 左侧拨杆位置 */
struct {
int16_t x;
int16_t y;
int16_t z;
bool l_click; /* 左键 */
bool r_click; /* 右键 */
} mouse; /* 鼠标值 */
union {
bool key[DR16_KEY_NUM]; /* 键盘按键值 */
uint16_t value; /* 键盘按键值的位映射 */
} keyboard;
uint16_t res; /* 保留,未启用 */
} DR16_Data_t;
typedef struct {
DEVICE_Header_t header;
DR16_RawData_t raw_data;
DR16_Data_t data;
} DR16_t;
/* Exported functions prototypes -------------------------------------------- */
int8_t DR16_Init(DR16_t *dr16);
int8_t DR16_Restart(void);
int8_t DR16_StartDmaRecv(DR16_t *dr16);
bool DR16_WaitDmaCplt(uint32_t timeout);
int8_t DR16_ParseData(DR16_t *dr16);
int8_t DR16_Offline(DR16_t *dr16);
/* USER FUNCTION BEGIN */
/* USER FUNCTION END */
#ifdef __cplusplus
}
#endif

View File

@@ -0,0 +1,178 @@
/*
IST8310 地磁传感器。
*/
/* Includes ----------------------------------------------------------------- */
#include "ist8310.h"
#include <gpio.h>
#include <stdbool.h>
#include <string.h>
#include "bsp/time.h"
#include "bsp/gpio.h"
#include "bsp/i2c.h"
/* USER INCLUDE BEGIN */
/* USER INCLUDE END */
/* Private define ----------------------------------------------------------- */
#define IST8310_WAI (0x00)
#define IST8310_STAT1 (0x02)
#define IST8310_DATAXL (0x03)
#define IST8310_STAT2 (0x09)
#define IST8310_CNTL1 (0x0A)
#define IST8310_CNTL2 (0x0B)
#define IST8310_STR (0x0C)
#define IST8310_TEMPL (0x1C)
#define IST8310_TEMPH (0x1D)
#define IST8310_AVGCNTL (0x41)
#define IST8310_PDCNTL (0x42)
#define IST8310_CHIP_ID (0x10)
#define IST8310_IIC_ADDRESS (0x0E << 1)
#define IST8310_LEN_RX_BUFF (6)
/* USER DEFINE BEGIN */
/* USER DEFINE END */
/* Private macro ------------------------------------------------------------ */
#define IST8310_SET() \
BSP_GPIO_WritePin(CMPS_RST_Pin, GPIO_PIN_SET)
#define IST8310_RESET() \
BSP_GPIO_WritePin(CMPS_RST_Pin, GPIO_PIN_RESET)
/* Private typedef ---------------------------------------------------------- */
/* USER STRUCT BEGIN */
/* USER STRUCT END */
/* Private variables -------------------------------------------------------- */
uint8_t ist8310_rxbuf[IST8310_LEN_RX_BUFF];
static osThreadId_t thread_alert;
static bool inited = false;
/* Private function -------------------------------------------------------- */
/* USER FUNCTION BEGIN */
/* USER FUNCTION END */
static void IST8310_WriteSingle(uint8_t reg, uint8_t data) {
BSP_I2C_MemWriteByte(BSP_I2C_COMP, IST8310_IIC_ADDRESS, reg, data);
}
static uint8_t IST8310_ReadSingle(uint8_t reg) {
return BSP_I2C_MemReadByte(BSP_I2C_COMP, IST8310_IIC_ADDRESS, reg);
}
static void IST8310_Read(uint8_t reg, uint8_t *data, uint8_t len) {
if (data == NULL) return;
BSP_I2C_MemRead(BSP_I2C_COMP, IST8310_IIC_ADDRESS, reg, data, len, true);
}
static void IST8310_MemRxCpltCallback(void) {
osThreadFlagsSet(thread_alert, SIGNAL_IST8310_MAGN_RAW_REDY);
}
static void IST8310_IntCallback(void) {
osThreadFlagsSet(thread_alert, SIGNAL_IST8310_MAGN_NEW_DATA);
}
/* Exported functions ------------------------------------------------------- */
int8_t IST8310_Init(IST8310_t *ist8310, const IST8310_Cali_t *cali) {
if (ist8310 == NULL) return DEVICE_ERR_NULL;
if (cali == NULL) return DEVICE_ERR_NULL;
if (inited) return DEVICE_ERR_INITED;
if ((thread_alert = osThreadGetId()) == NULL) return DEVICE_ERR_NULL;
ist8310->cali = cali;
IST8310_RESET();
BSP_TIME_Delay(50);
IST8310_SET();
BSP_TIME_Delay(50);
if (IST8310_ReadSingle(IST8310_WAI) != IST8310_CHIP_ID)
return DEVICE_ERR_NO_DEV;
BSP_GPIO_DisableIRQ(CMPS_INT_Pin);
BSP_I2C_RegisterCallback(BSP_I2C_COMP, HAL_I2C_MEM_RX_CPLT_CB,
IST8310_MemRxCpltCallback);
BSP_GPIO_RegisterCallback(CMPS_INT_Pin, IST8310_IntCallback);
/* Init. */
/* 0x00: Stand-By mode. 0x01: Single measurement mode. */
/* 0x08: Data ready function enable. DRDY signal active low*/
IST8310_WriteSingle(IST8310_CNTL2, 0x08);
IST8310_WriteSingle(IST8310_AVGCNTL, 0x09);
IST8310_WriteSingle(IST8310_PDCNTL, 0xC0);
IST8310_WriteSingle(IST8310_CNTL1, 0x0B);
BSP_TIME_Delay(10);
inited = true;
BSP_GPIO_EnableIRQ(CMPS_INT_Pin);
return DEVICE_OK;
}
bool IST8310_WaitNew(uint32_t timeout) {
return (osThreadFlagsWait(SIGNAL_IST8310_MAGN_NEW_DATA, osFlagsWaitAll,
timeout) == SIGNAL_IST8310_MAGN_NEW_DATA);
}
int8_t IST8310_StartDmaRecv() {
IST8310_Read(IST8310_DATAXL, ist8310_rxbuf, IST8310_LEN_RX_BUFF);
return DEVICE_OK;
}
uint32_t IST8310_WaitDmaCplt() {
return osThreadFlagsWait(SIGNAL_IST8310_MAGN_RAW_REDY, osFlagsWaitAll,
osWaitForever);
}
int8_t IST8310_Parse(IST8310_t *ist8310) {
if (ist8310 == NULL) return DEVICE_ERR_NULL;
#if 1
/* Magn -> T */
int16_t raw_x, raw_y, raw_z;
memcpy(&raw_x, ist8310_rxbuf + 0, sizeof(raw_x));
memcpy(&raw_y, ist8310_rxbuf + 2, sizeof(raw_y));
memcpy(&raw_z, ist8310_rxbuf + 4, sizeof(raw_z));
ist8310->magn.x = (float)raw_x;
ist8310->magn.y = (float)raw_y;
ist8310->magn.z = (float)-raw_z;
#else
const int16_t *raw_x = (int16_t *)(ist8310_rxbuf + 0);
const int16_t *raw_y = (int16_t *)(ist8310_rxbuf + 2);
const int16_t *raw_z = (int16_t *)(ist8310_rxbuf + 4);
ist8310->magn.x = (float)*raw_x;
ist8310->magn.y = (float)*raw_y;
ist8310->magn.z = -(float)*raw_z;
#endif
ist8310->magn.x *= 3.0f / 20.0f;
ist8310->magn.y *= 3.0f / 20.0f;
ist8310->magn.z *= 3.0f / 20.0f;
ist8310->magn.x = (ist8310->magn.x - ist8310->cali->magn_offset.x) *
ist8310->cali->magn_scale.x;
ist8310->magn.y = (ist8310->magn.y - ist8310->cali->magn_offset.y) *
ist8310->cali->magn_scale.y;
ist8310->magn.z = (ist8310->magn.z - ist8310->cali->magn_offset.y) *
ist8310->cali->magn_scale.z;
return DEVICE_OK;
}

View File

@@ -0,0 +1,49 @@
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
/* Includes ----------------------------------------------------------------- */
#include <cmsis_os2.h>
#include <stdbool.h>
#include <stdint.h>
#include "component/ahrs.h"
#include "device/device.h"
/* Exported constants ------------------------------------------------------- */
/* Exported macro ----------------------------------------------------------- */
/* Exported types ----------------------------------------------------------- */
typedef struct {
struct {
float x;
float y;
float z;
} magn_offset; /* 磁力计偏置 */
struct {
float x;
float y;
float z;
} magn_scale; /* 磁力计缩放 */
} IST8310_Cali_t; /* IST8310校准数据 */
typedef struct {
DEVICE_Header_t header;
AHRS_Magn_t magn;
const IST8310_Cali_t *cali;
} IST8310_t;
/* Exported functions prototypes -------------------------------------------- */
int8_t IST8310_Init(IST8310_t *ist8310, const IST8310_Cali_t *cali);
int8_t IST8310_Restart(void);
bool IST8310_WaitNew(uint32_t timeout);
int8_t IST8310_StartDmaRecv();
uint32_t IST8310_WaitDmaCplt();
int8_t IST8310_Parse(IST8310_t *ist8310);
#ifdef __cplusplus
}
#endif

View File

@@ -0,0 +1,37 @@
/*
led控制
*/
/*Includes -----------------------------------------*/
#include "device/led.h"
#include "device.h"
/* Private define ----------------------------------------------------------- */
/* Private macro ------------------------------------------------------------ */
/* Private typedef ---------------------------------------------------------- */
#ifdef LED_PWM
int8_t DEVICE_LED_PWM_Set(BSP_PWM_Channel_t channel, float duty_cycle)
{
if (duty_cycle < 0.0f || duty_cycle > 1.0f) {
return DEVICE_ERR_NULL; // 错误:占空比超出范围
}
uint16_t pulse = (uint16_t)(duty_cycle * (float)UINT16_MAX);
BSP_PWM_Start(channel);
BSP_PWM_SetComp(channel, pulse);
return DEVICE_OK;
}
#endif
#ifdef LED_GPIO
int8_t DEVICE_LED_GPIO_Set(BSP_GPIO_t gpio, bool value)
{
if (value) {
BSP_GPIO_WritePin(gpio, true);
} else {
BSP_GPIO_WritePin(gpio, false);
}
return DEVICE_OK;
}
#endif

View File

@@ -0,0 +1,63 @@
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
/* Includes ----------------------------------------------------------------- */
/* USER DEFIN BEGIN */
/* USER DEFIN END */
#include <stdint.h>
#ifdef LED_GPIO
#include "bsp/gpio.h"
#endif
#ifdef LED_PWM
#include "bsp/pwm.h"
#endif
#include "bsp/bsp.h"
/* Exported constants ------------------------------------------------------- */
/* Exported macro ----------------------------------------------------------- */
/* Exported types ----------------------------------------------------------- */
typedef struct {
#ifdef LED_GPIO
BSP_GPIO_t gpio;
#endif
#ifdef LED_PWM
BSP_PWM_Channel_t channel;
#endif
} DEVICE_LED_t;
extern DEVICE_LED_t LED_Map;
/* Exported functions prototypes -------------------------------------------- */
#ifdef LED_PWM
int8_t DEVICE_LED_PWM_Set(BSP_PWM_Channel_t channel, float duty_cycle)
{
if (duty_cycle < 0.0f || duty_cycle > 1.0f) {
return DEVICE_ERR_NULL; // 错误:占空比超出范围
}
uint16_t pulse = (uint16_t)(duty_cycle * (float)UINT16_MAX);
BSP_PWM_Start(channel);
BSP_PWM_SetComp(channel, pulse);
return DEVICE_OK;
}
#endif
#ifdef LED_GPIO
int8_t DEVICE_LED_GPIO_Set(BSP_GPIO_t gpio, bool value)
{
if (value) {
BSP_GPIO_WritePin(gpio, true);
} else {
BSP_GPIO_WritePin(gpio, false);
}
return DEVICE_OK;
}
#endif
#ifdef __cplusplus
}
#endif

View File

@@ -0,0 +1,52 @@
/*
电机通用函数
*/
/* Includes ----------------------------------------------------------------- */
#include "motor.h"
#include <string.h>
/* USER INCLUDE BEGIN */
/* USER INCLUDE END */
/* Private define ----------------------------------------------------------- */
/* USER DEFINE BEGIN */
/* USER DEFINE END */
/* Private macro ------------------------------------------------------------ */
/* Private typedef ---------------------------------------------------------- */
/* USER STRUCT BEGIN */
/* USER STRUCT END */
/* Private variables -------------------------------------------------------- */
/* Private function -------------------------------------------------------- */
/* USER FUNCTION BEGIN */
/* USER FUNCTION END */
/* Exported functions ------------------------------------------------------- */
float MOTOR_GetRotorAbsAngle(const MOTOR_t *motor) {
if (motor == NULL) return DEVICE_ERR_NULL;
return motor->feedback.rotor_abs_angle;
}
float MOTOR_GetRotorSpeed(const MOTOR_t *motor) {
if (motor == NULL) return DEVICE_ERR_NULL;
return motor->feedback.rotor_speed;
}
float MOTOR_GetTorqueCurrent(const MOTOR_t *motor) {
if (motor == NULL) return DEVICE_ERR_NULL;
return motor->feedback.torque_current;
}
float MOTOR_GetTemp(const MOTOR_t *motor) {
if (motor == NULL) return DEVICE_ERR_NULL;
return motor->feedback.temp;
}

View File

@@ -0,0 +1,68 @@
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
/* Includes ----------------------------------------------------------------- */
#include "device/device.h"
/* USER INCLUDE BEGIN */
/* USER INCLUDE END */
/* USER DEFINE BEGIN */
/* USER DEFINE END */
/* Exported constants ------------------------------------------------------- */
/* Exported macro ----------------------------------------------------------- */
/* Exported types ----------------------------------------------------------- */
typedef struct {
float rotor_abs_angle; /* 转子绝对角度 */
float rotor_speed; /* 实际转子转速 */
float torque_current; /* 转矩电流 */
float temp; /* 温度 */
} MOTOR_Feedback_t;
/**
* @brief mit电机输出参数结构体
*/
typedef struct {
float torque; /* 目标力矩 */
float velocity; /* 目标速度 */
float angle; /* 目标位置 */
float kp; /* 位置环增益 */
float kd; /* 速度环增益 */
} MOTOR_MIT_Output_t;
/**
* @brief 转矩电流控制模式参数结构体
*/
typedef struct {
float current; /* 目标电流 */
} MOTOR_Current_Output_t;
typedef struct {
DEVICE_Header_t header;
bool reverse; /* 是否反装 true表示反装 */
MOTOR_Feedback_t feedback;
} MOTOR_t;
/* USER STRUCT BEGIN */
/* USER STRUCT END */
/* Exported functions prototypes -------------------------------------------- */
float MOTOR_GetRotorAbsAngle(const MOTOR_t *motor);
float MOTOR_GetRotorSpeed(const MOTOR_t *motor);
float MOTOR_GetTorqueCurrent(const MOTOR_t *motor);
float MOTOR_GetTemp(const MOTOR_t *motor);
/* USER FUNCTION BEGIN */
/* USER FUNCTION END */
#ifdef __cplusplus
}
#endif

View File

@@ -0,0 +1,496 @@
#define MOTOR_DM_FLOAT_TO_INT_SIGNED(x, x_min, x_max, bits) \
((int32_t)roundf(((x) / ((x_max) - (x_min))) * (1 << (bits)) + (1 << ((bits) - 1))))
#define MOTOR_DM_INT_TO_FLOAT_SIGNED(x_int, x_min, x_max, bits) \
(((float)((int32_t)(x_int) - (1 << ((bits) - 1))) * ((x_max) - (x_min)) / (float)(1 << (bits))))
/* Includes ----------------------------------------------------------------- */
#include "device/motor_dm.h"
#include "bsp/mm.h"
#include "bsp/time.h"
#include "component/user_math.h"
#include "string.h"
#include "math.h"
/* Private constants -------------------------------------------------------- */
/* DM电机数据映射范围 */
#define DM_P_MIN (-12.56637f)
#define DM_P_MAX (12.56637f)
#define DM_V_MIN (-30.0f)
#define DM_V_MAX (30.0f)
#define DM_T_MIN (-12.0f)
#define DM_T_MAX (12.0f)
#define DM_KP_MIN (0.0f)
#define DM_KP_MAX (500.0f)
#define DM_KD_MIN (0.0f)
#define DM_KD_MAX (5.0f)
/* CAN ID偏移量 */
#define DM_CAN_ID_OFFSET_POS_VEL 0x100
#define DM_CAN_ID_OFFSET_VEL 0x200
/* Private macro ------------------------------------------------------------ */
#define FLOAT_TO_UINT(x, x_min, x_max, bits) \
(uint32_t)((x - x_min) * ((1 << bits) - 1) / (x_max - x_min))
#define UINT_TO_FLOAT(x_int, x_min, x_max, bits) \
((float)(x_int) * (x_max - x_min) / ((1 << bits) - 1) + x_min)
/* Private variables -------------------------------------------------------- */
static MOTOR_DM_CANManager_t *can_managers[BSP_CAN_NUM] = {NULL};
static int float_to_uint(float x_float, float x_min, float x_max, int bits)
{
/* Converts a float to an unsigned int, given range and number of bits */
float span = x_max - x_min;
float offset = x_min;
return (int) ((x_float-offset)*((float)((1<<bits)-1))/span);
}
static float uint_to_float(int x_int, float x_min, float x_max, int bits)
{
/* converts unsigned int to float, given range and number of bits */
float span = x_max - x_min;
float offset = x_min;
return ((float)x_int)*span/((float)((1<<bits)-1)) + offset;
}
/* Private function prototypes ---------------------------------------------- */
static int8_t MOTOR_DM_ParseFeedbackFrame(MOTOR_DM_t *motor, const uint8_t *data);
static int8_t MOTOR_DM_SendMITCmd(MOTOR_DM_t *motor, MOTOR_MIT_Output_t *output);
static int8_t MOTOR_DM_SendPosVelCmd(MOTOR_DM_t *motor, float pos, float vel);
static int8_t MOTOR_DM_SendVelCmd(MOTOR_DM_t *motor, float vel);
static MOTOR_DM_CANManager_t* MOTOR_DM_GetCANManager(BSP_CAN_t can);
/* Private functions -------------------------------------------------------- */
/**
* @brief 解析DM电机反馈帧
* @param motor 电机实例
* @param data CAN数据
* @return 解析结果
*/
static int8_t MOTOR_DM_ParseFeedbackFrame(MOTOR_DM_t *motor, const uint8_t *data) {
if (motor == NULL || data == NULL) {
return DEVICE_ERR_NULL;
}
uint16_t p_int=(data[1]<<8)|data[2];
motor->motor.feedback.rotor_abs_angle = uint_to_float(p_int, DM_P_MIN, DM_P_MAX, 16); // (-12.5,12.5)
uint16_t v_int=(data[3]<<4)|(data[4]>>4);
motor->motor.feedback.rotor_speed = uint_to_float(v_int, DM_V_MIN, DM_V_MAX, 12); // (-30.0,30.0)
uint16_t t_int=((data[4]&0xF)<<8)|data[5];
motor->motor.feedback.torque_current = uint_to_float(t_int, DM_T_MIN, DM_T_MAX, 12); // (-12.0,12.0)
motor->motor.feedback.temp = (float)(data[6]);
while (motor->motor.feedback.rotor_abs_angle < 0) {
motor->motor.feedback.rotor_abs_angle += M_2PI;
}
while (motor->motor.feedback.rotor_abs_angle >= M_2PI) {
motor->motor.feedback.rotor_abs_angle -= M_2PI;
}
if (motor->param.reverse) {
motor->motor.feedback.rotor_abs_angle = M_2PI - motor->motor.feedback.rotor_abs_angle;
motor->motor.feedback.rotor_speed = -motor->motor.feedback.rotor_speed;
motor->motor.feedback.torque_current = -motor->motor.feedback.torque_current;
}
return DEVICE_OK;
}
/**
* @brief 发送MIT模式控制命令
* @param motor 电机实例
* @param output MIT控制参数
* @return 发送结果
*/
static int8_t MOTOR_DM_SendMITCmd(MOTOR_DM_t *motor, MOTOR_MIT_Output_t *output) {
if (motor == NULL || output == NULL) {
return DEVICE_ERR_NULL;
}
uint8_t data[8];
uint16_t pos_tmp,vel_tmp,kp_tmp,kd_tmp,tor_tmp;
uint16_t id = motor->param.can_id;
pos_tmp = float_to_uint(output->angle, DM_P_MIN , DM_P_MAX, 16);
vel_tmp = float_to_uint(output->velocity, DM_V_MIN , DM_V_MAX, 12);
kp_tmp = float_to_uint(output->kp, DM_KP_MIN, DM_KP_MAX, 12);
kd_tmp = float_to_uint(output->kd, DM_KD_MIN, DM_KD_MAX, 12);
tor_tmp = float_to_uint(output->torque, DM_T_MIN , DM_T_MAX, 12);
/* 打包数据 */
data[0] = (pos_tmp >> 8);
data[1] = pos_tmp;
data[2] = (vel_tmp >> 4);
data[3] = ((vel_tmp&0xF)<<4)|(kp_tmp>>8);
data[4] = kp_tmp;
data[5] = (kd_tmp >> 4);
data[6] = ((kd_tmp&0xF)<<4)|(tor_tmp>>8);
data[7] = tor_tmp;
/* 发送CAN消息 */
BSP_CAN_StdDataFrame_t frame;
frame.id = motor->param.can_id;
frame.dlc = 8;
memcpy(frame.data, data, 8);
return BSP_CAN_TransmitStdDataFrame(motor->param.can, &frame) == BSP_OK ? DEVICE_OK : DEVICE_ERR;
}
/**
* @brief 发送位置速度模式控制命令
* @param motor 电机实例
* @param pos 目标位置
* @param vel 目标速度
* @return 发送结果
*/
static int8_t MOTOR_DM_SendPosVelCmd(MOTOR_DM_t *motor, float pos, float vel) {
if (motor == NULL) {
return DEVICE_ERR_NULL;
}
uint8_t data[8] = {0};
/* 直接发送浮点数数据 */
memcpy(&data[0], &pos, 4); // 位置,低位在前
memcpy(&data[4], &vel, 4); // 速度,低位在前
/* 发送CAN消息ID为原ID+0x100 */
uint32_t can_id = DM_CAN_ID_OFFSET_POS_VEL + motor->param.can_id;
BSP_CAN_StdDataFrame_t frame;
frame.id = can_id;
frame.dlc = 8;
memcpy(frame.data, data, 8);
return BSP_CAN_TransmitStdDataFrame(motor->param.can, &frame) == BSP_OK ? DEVICE_OK : DEVICE_ERR;
}
/**
* @brief 发送速度模式控制命令
* @param motor 电机实例
* @param vel 目标速度
* @return 发送结果
*/
static int8_t MOTOR_DM_SendVelCmd(MOTOR_DM_t *motor, float vel) {
if (motor == NULL) {
return DEVICE_ERR_NULL;
}
uint8_t data[4] = {0};
/* 直接发送浮点数数据 */
memcpy(&data[0], &vel, 4); // 速度,低位在前
/* 发送CAN消息ID为原ID+0x200 */
uint32_t can_id = DM_CAN_ID_OFFSET_VEL + motor->param.can_id;
BSP_CAN_StdDataFrame_t frame;
frame.id = can_id;
frame.dlc = 4;
memcpy(frame.data, data, 4);
return BSP_CAN_TransmitStdDataFrame(motor->param.can, &frame) == BSP_OK ? DEVICE_OK : DEVICE_ERR;
}
/**
* @brief 获取指定CAN总线的管理器
* @param can CAN总线
* @return CAN管理器指针
*/
static MOTOR_DM_CANManager_t* MOTOR_DM_GetCANManager(BSP_CAN_t can) {
if (can >= BSP_CAN_NUM) {
return NULL;
}
return can_managers[can];
}
/**
* @brief 创建CAN管理器
* @param can CAN总线
* @return 创建结果
*/
static int8_t MOTOR_DM_CreateCANManager(BSP_CAN_t can) {
if (can >= BSP_CAN_NUM) return DEVICE_ERR;
if (can_managers[can] != NULL) return DEVICE_OK;
can_managers[can] = (MOTOR_DM_CANManager_t*)BSP_Malloc(sizeof(MOTOR_DM_CANManager_t));
if (can_managers[can] == NULL) return DEVICE_ERR;
memset(can_managers[can], 0, sizeof(MOTOR_DM_CANManager_t));
can_managers[can]->can = can;
return DEVICE_OK;
}
/* Exported functions ------------------------------------------------------- */
/**
* @brief 注册一个DM电机
* @param param 电机参数
* @return 注册结果
*/
int8_t MOTOR_DM_Register(MOTOR_DM_Param_t *param) {
if (param == NULL) {
return DEVICE_ERR_NULL;
}
/* 创建CAN管理器 */
if (MOTOR_DM_CreateCANManager(param->can) != DEVICE_OK) {
return DEVICE_ERR;
}
/* 获取CAN管理器 */
MOTOR_DM_CANManager_t *manager = MOTOR_DM_GetCANManager(param->can);
if (manager == NULL) {
return DEVICE_ERR;
}
/* 检查是否已注册 */
for (int i = 0; i < manager->motor_count; i++) {
if (manager->motors[i] && manager->motors[i]->param.master_id == param->master_id) {
return DEVICE_ERR_INITED;
}
}
/* 检查是否已达到最大数量 */
if (manager->motor_count >= MOTOR_DM_MAX_MOTORS) {
return DEVICE_ERR;
}
/* 分配内存 */
MOTOR_DM_t *motor = (MOTOR_DM_t *)BSP_Malloc(sizeof(MOTOR_DM_t));
if (motor == NULL) {
return DEVICE_ERR;
}
/* 初始化电机 */
memset(motor, 0, sizeof(MOTOR_DM_t));
memcpy(&motor->param, param, sizeof(MOTOR_DM_Param_t));
motor->motor.header.online = false;
motor->motor.reverse = param->reverse;
/* 注册CAN接收ID - DM电机使用Master ID接收反馈 */
uint16_t feedback_id = param->master_id;
if (BSP_CAN_RegisterId(param->can, feedback_id, 3) != BSP_OK) {
BSP_Free(motor);
return DEVICE_ERR;
}
/* 添加到管理器 */
manager->motors[manager->motor_count] = motor;
manager->motor_count++;
return DEVICE_OK;
}
/**
* @brief 更新指定电机数据
* @param param 电机参数
* @return 更新结果
*/
int8_t MOTOR_DM_Update(MOTOR_DM_Param_t *param) {
if (param == NULL) {
return DEVICE_ERR_NULL;
}
MOTOR_DM_CANManager_t *manager = MOTOR_DM_GetCANManager(param->can);
if (manager == NULL) {
return DEVICE_ERR_NO_DEV;
}
/* 查找电机 */
MOTOR_DM_t *motor = NULL;
for (int i = 0; i < manager->motor_count; i++) {
if (manager->motors[i] && manager->motors[i]->param.master_id == param->master_id) {
motor = manager->motors[i];
break;
}
}
if (motor == NULL) {
return DEVICE_ERR_NO_DEV;
}
/* 主动接收CAN消息 */
uint16_t feedback_id = param->master_id;
BSP_CAN_Message_t rx_msg;
if (BSP_CAN_GetMessage(param->can, feedback_id, &rx_msg, BSP_CAN_TIMEOUT_IMMEDIATE) != BSP_OK) {
uint64_t now_time = BSP_TIME_Get();
if (now_time - motor->motor.header.last_online_time > 100000) { // 100ms超时单位微秒
motor->motor.header.online = false;
}
return DEVICE_ERR;
}
motor->motor.header.online = true;
motor->motor.header.last_online_time = BSP_TIME_Get();
MOTOR_DM_ParseFeedbackFrame(motor, rx_msg.data);
return DEVICE_OK;
}
/**
* @brief 更新所有电机数据
* @return 更新结果
*/
int8_t MOTOR_DM_UpdateAll(void) {
int8_t ret = DEVICE_OK;
for (int can = 0; can < BSP_CAN_NUM; can++) {
MOTOR_DM_CANManager_t *manager = MOTOR_DM_GetCANManager((BSP_CAN_t)can);
if (manager == NULL) continue;
for (int i = 0; i < manager->motor_count; i++) {
MOTOR_DM_t *motor = manager->motors[i];
if (motor != NULL) {
if (MOTOR_DM_Update(&motor->param) != DEVICE_OK) {
ret = DEVICE_ERR;
}
}
}
}
return ret;
}
/**
* @brief MIT模式控制
* @param param 电机参数
* @param output MIT控制参数
* @return 控制结果
*/
int8_t MOTOR_DM_MITCtrl(MOTOR_DM_Param_t *param, MOTOR_MIT_Output_t *output) {
if (param == NULL || output == NULL) {
return DEVICE_ERR_NULL;
}
MOTOR_DM_t *motor = MOTOR_DM_GetMotor(param);
if (motor == NULL) {
return DEVICE_ERR_NO_DEV;
}
return MOTOR_DM_SendMITCmd(motor, output);
}
/**
* @brief 位置速度模式控制
* @param param 电机参数
* @param target_pos 目标位置
* @param target_vel 目标速度
* @return 控制结果
*/
int8_t MOTOR_DM_PosVelCtrl(MOTOR_DM_Param_t *param, float target_pos, float target_vel) {
if (param == NULL) {
return DEVICE_ERR_NULL;
}
MOTOR_DM_t *motor = MOTOR_DM_GetMotor(param);
if (motor == NULL) {
return DEVICE_ERR_NO_DEV;
}
return MOTOR_DM_SendPosVelCmd(motor, target_pos, target_vel);
}
/**
* @brief 速度模式控制
* @param param 电机参数
* @param target_vel 目标速度
* @return 控制结果
*/
int8_t MOTOR_DM_VelCtrl(MOTOR_DM_Param_t *param, float target_vel) {
if (param == NULL) {
return DEVICE_ERR_NULL;
}
MOTOR_DM_t *motor = MOTOR_DM_GetMotor(param);
if (motor == NULL) {
return DEVICE_ERR_NO_DEV;
}
return MOTOR_DM_SendVelCmd(motor, target_vel);
}
/**
* @brief 获取指定电机的实例指针
* @param param 电机参数
* @return 电机实例指针
*/
MOTOR_DM_t* MOTOR_DM_GetMotor(MOTOR_DM_Param_t *param) {
if (param == NULL) {
return NULL;
}
MOTOR_DM_CANManager_t *manager = MOTOR_DM_GetCANManager(param->can);
if (manager == NULL) {
return NULL;
}
/* 查找对应的电机 */
for (int i = 0; i < manager->motor_count; i++) {
MOTOR_DM_t *motor = manager->motors[i];
if (motor && motor->param.can == param->can &&
motor->param.master_id == param->master_id) {
return motor;
}
}
return NULL;
}
int8_t MOTOR_DM_Enable(MOTOR_DM_Param_t *param){
if (param == NULL) {
return DEVICE_ERR_NULL;
}
MOTOR_DM_t *motor = MOTOR_DM_GetMotor(param);
if (motor == NULL) {
return DEVICE_ERR_NO_DEV;
}
BSP_CAN_StdDataFrame_t frame;
frame.id = motor->param.can_id;
frame.dlc = 8;
frame.data[0] = 0XFF;
frame.data[1] = 0xFF;
frame.data[2] = 0xFF;
frame.data[3] = 0xFF;
frame.data[4] = 0xFF;
frame.data[5] = 0xFF;
frame.data[6] = 0xFF;
frame.data[7] = 0xFC;
return BSP_CAN_TransmitStdDataFrame(motor->param.can, &frame) == BSP_OK ? DEVICE_OK : DEVICE_ERR;
}
/**
* @brief 使电机松弛设置输出为0
* @param param 电机参数
* @return 操作结果
*/
int8_t MOTOR_DM_Relax(MOTOR_DM_Param_t *param) {
if (param == NULL) {
return DEVICE_ERR_NULL;
}
MOTOR_MIT_Output_t output = {0};
return MOTOR_DM_MITCtrl(param, &output);
}
/**
* @brief 使电机离线设置在线状态为false
* @param param 电机参数
* @return 操作结果
*/
int8_t MOTOR_DM_Offine(MOTOR_DM_Param_t *param) {
if (param == NULL) {
return DEVICE_ERR_NULL;
}
MOTOR_DM_t *motor = MOTOR_DM_GetMotor(param);
if (motor == NULL) {
return DEVICE_ERR_NO_DEV;
}
motor->motor.header.online = false;
return DEVICE_OK;
}

View File

@@ -0,0 +1,98 @@
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
/* Includes ----------------------------------------------------------------- */
#include "device/device.h"
#include "device/motor.h"
#include "bsp/can.h"
/* Exported constants ------------------------------------------------------- */
#define MOTOR_DM_MAX_MOTORS 32
/* Exported macro ----------------------------------------------------------- */
/* Exported types ----------------------------------------------------------- */
typedef enum {
MOTOR_DM_J4310,
} MOTOR_DM_Module_t;
/*每个电机需要的参数*/
typedef struct {
BSP_CAN_t can;
uint16_t master_id; /* 主站ID用于发送控制命令 */
uint16_t can_id; /* 反馈ID用于接收电机反馈 */
MOTOR_DM_Module_t module;
bool reverse;
} MOTOR_DM_Param_t;
/*电机实例*/
typedef struct{
MOTOR_DM_Param_t param;
MOTOR_t motor;
} MOTOR_DM_t;
/*CAN管理器管理一个CAN总线上所有的电机*/
typedef struct {
BSP_CAN_t can;
MOTOR_DM_t *motors[MOTOR_DM_MAX_MOTORS];
uint8_t motor_count;
} MOTOR_DM_CANManager_t;
/* Exported functions prototypes -------------------------------------------- */
/**
* @brief 注册一个LK电机
* @param param 电机参数
* @return
*/
int8_t MOTOR_DM_Register(MOTOR_DM_Param_t *param);
/**
* @brief 更新指定电机数据
* @param param 电机参数
* @return
*/
int8_t MOTOR_DM_Update(MOTOR_DM_Param_t *param);
/**
* @brief 更新所有电机数据
* @return
*/
int8_t MOTOR_DM_UpdateAll(void);
int8_t MOTOR_DM_MITCtrl(MOTOR_DM_Param_t *param, MOTOR_MIT_Output_t *output);
int8_t MOTOR_DM_PosVelCtrl(MOTOR_DM_Param_t *param, float target_pos, float target_vel);
int8_t MOTOR_DM_VelCtrl(MOTOR_DM_Param_t *param, float target_vel);
/**
* @brief 获取指定电机的实例指针
* @param param 电机参数
* @return
*/
MOTOR_DM_t* MOTOR_DM_GetMotor(MOTOR_DM_Param_t *param);
int8_t MOTOR_DM_Enable(MOTOR_DM_Param_t *param);
/**
* @brief 使电机松弛设置输出为0
* @param param
* @return
*/
int8_t MOTOR_DM_Relax(MOTOR_DM_Param_t *param);
/**
* @brief 使电机离线设置在线状态为false
* @param param
* @return
*/
int8_t MOTOR_DM_Offine(MOTOR_DM_Param_t *param);
#ifdef __cplusplus
}
#endif

View File

@@ -0,0 +1,329 @@
/*
LK电机驱动
*/
/* Includes ----------------------------------------------------------------- */
#include "motor_lk.h"
#include <stdbool.h>
#include <string.h>
#include "bsp/can.h"
#include "bsp/mm.h"
#include "bsp/time.h"
#include "component/user_math.h"
/* USER INCLUDE BEGIN */
/* USER INCLUDE END */
/* Private define ----------------------------------------------------------- */
#define LK_CTRL_ID_BASE (0x140)
#define LK_FB_ID_BASE (0x240)
// LK电机命令字节定义
#define LK_CMD_FEEDBACK (0x9C) // 反馈命令字节
#define LK_CMD_MOTOR_OFF (0x80) // 电机关闭命令
#define LK_CMD_MOTOR_ON (0x88) // 电机运行命令
#define LK_CMD_TORQUE_CTRL (0xA1) // 转矩闭环控制命令
// LK电机参数定义
#define LK_CURR_LSB_MF (33.0f / 4096.0f) // MF电机转矩电流分辨率 A/LSB
#define LK_CURR_LSB_MG (66.0f / 4096.0f) // MG电机转矩电流分辨率 A/LSB
#define LK_POWER_RANGE_MS (1000) // MS电机功率范围 ±1000
#define LK_TORQUE_RANGE (2048) // 转矩控制值范围 ±2048
#define LK_TORQUE_CURRENT_MF (16.5f) // MF电机最大转矩电流 A
#define LK_TORQUE_CURRENT_MG (33.0f) // MG电机最大转矩电流 A
#define MOTOR_TX_BUF_SIZE (8)
#define MOTOR_RX_BUF_SIZE (8)
// 编码器分辨率定义
#define LK_ENC_14BIT_MAX (16383) // 14位编码器最大值
#define LK_ENC_15BIT_MAX (32767) // 15位编码器最大值
#define LK_ENC_16BIT_MAX (65535) // 16位编码器最大值
/* USER DEFINE BEGIN */
/* USER DEFINE END */
/* Private macro ------------------------------------------------------------ */
/* Private typedef ---------------------------------------------------------- */
/* USER STRUCT BEGIN */
/* USER STRUCT END */
/* Private variables -------------------------------------------------------- */
static MOTOR_LK_CANManager_t *can_managers[BSP_CAN_NUM] = {NULL};
/* Private functions -------------------------------------------------------- */
/* USER FUNCTION BEGIN */
/* USER FUNCTION END */
static float MOTOR_LK_GetCurrentLSB(MOTOR_LK_Module_t module) {
switch (module) {
case MOTOR_LK_MF9025:
case MOTOR_LK_MF9035:
return LK_CURR_LSB_MF;
default:
return LK_CURR_LSB_MG; // 默认使用MG的分辨率
}
}
static uint16_t MOTOR_LK_GetEncoderMax(MOTOR_LK_Module_t module) {
// 根据电机型号返回编码器最大值这里假设都使用16位编码器
// 实际使用时需要根据具体电机型号配置
return LK_ENC_16BIT_MAX;
}
static MOTOR_LK_CANManager_t* MOTOR_LK_GetCANManager(BSP_CAN_t can) {
if (can >= BSP_CAN_NUM) return NULL;
return can_managers[can];
}
static int8_t MOTOR_LK_CreateCANManager(BSP_CAN_t can) {
if (can >= BSP_CAN_NUM) return DEVICE_ERR;
if (can_managers[can] != NULL) return DEVICE_OK;
can_managers[can] = (MOTOR_LK_CANManager_t*)BSP_Malloc(sizeof(MOTOR_LK_CANManager_t));
if (can_managers[can] == NULL) return DEVICE_ERR;
memset(can_managers[can], 0, sizeof(MOTOR_LK_CANManager_t));
can_managers[can]->can = can;
return DEVICE_OK;
}
static void MOTOR_LK_Decode(MOTOR_LK_t *motor, BSP_CAN_Message_t *msg) {
// 检查命令字节是否为反馈命令
if (msg->data[0] != LK_CMD_FEEDBACK) {
// 如果不是标准反馈命令,可能是其他格式的数据
// 临时跳过命令字节检查,直接解析数据
// return;
}
// 解析温度 (DATA[1])
motor->motor.feedback.temp = (int8_t)msg->data[1];
// 解析转矩电流值或功率值 (DATA[2], DATA[3])
int16_t raw_current_or_power = (int16_t)((msg->data[3] << 8) | msg->data[2]);
// 根据电机类型解析电流或功率
switch (motor->param.module) {
case MOTOR_LK_MF9025:
case MOTOR_LK_MF9035:
motor->motor.feedback.torque_current = raw_current_or_power * MOTOR_LK_GetCurrentLSB(motor->param.module);
break;
default:
motor->motor.feedback.torque_current = (float)raw_current_or_power;
break;
}
// 解析转速 (DATA[4], DATA[5]) - 单位1dps/LSB
int16_t raw_speed = (int16_t)((msg->data[5] << 8) | msg->data[4]);
motor->motor.feedback.rotor_speed = motor->param.reverse ? -raw_speed : raw_speed;
// 解析编码器值 (DATA[6], DATA[7])
uint16_t raw_encoder = (uint16_t)((msg->data[7] << 8) | msg->data[6]);
uint16_t encoder_max = MOTOR_LK_GetEncoderMax(motor->param.module);
// 将编码器值转换为弧度 (0 ~ 2π)
float angle = (float)raw_encoder / (float)encoder_max * M_2PI;
motor->motor.feedback.rotor_abs_angle = motor->param.reverse ? (M_2PI - angle) : angle;
}
/* Exported functions ------------------------------------------------------- */
int8_t MOTOR_LK_Register(MOTOR_LK_Param_t *param) {
if (param == NULL) return DEVICE_ERR_NULL;
if (MOTOR_LK_CreateCANManager(param->can) != DEVICE_OK) return DEVICE_ERR;
MOTOR_LK_CANManager_t *manager = MOTOR_LK_GetCANManager(param->can);
if (manager == NULL) return DEVICE_ERR;
// 检查是否已注册
for (int i = 0; i < manager->motor_count; i++) {
if (manager->motors[i] && manager->motors[i]->param.id == param->id) {
return DEVICE_ERR_INITED;
}
}
// 检查数量
if (manager->motor_count >= MOTOR_LK_MAX_MOTORS) return DEVICE_ERR;
// 创建新电机实例
MOTOR_LK_t *new_motor = (MOTOR_LK_t*)BSP_Malloc(sizeof(MOTOR_LK_t));
if (new_motor == NULL) return DEVICE_ERR;
memcpy(&new_motor->param, param, sizeof(MOTOR_LK_Param_t));
memset(&new_motor->motor, 0, sizeof(MOTOR_t));
new_motor->motor.reverse = param->reverse;
// 对于某些LK电机反馈数据可能通过命令ID发送
// 根据实际测试使用命令ID接收反馈数据
uint16_t feedback_id = param->id; // 使用命令ID作为反馈ID
// 注册CAN接收ID
if (BSP_CAN_RegisterId(param->can, feedback_id, 3) != BSP_OK) {
BSP_Free(new_motor);
return DEVICE_ERR;
}
manager->motors[manager->motor_count] = new_motor;
manager->motor_count++;
return DEVICE_OK;
}
int8_t MOTOR_LK_Update(MOTOR_LK_Param_t *param) {
if (param == NULL) return DEVICE_ERR_NULL;
MOTOR_LK_CANManager_t *manager = MOTOR_LK_GetCANManager(param->can);
if (manager == NULL) return DEVICE_ERR_NO_DEV;
for (int i = 0; i < manager->motor_count; i++) {
MOTOR_LK_t *motor = manager->motors[i];
if (motor && motor->param.id == param->id) {
// 对于某些LK电机反馈数据通过命令ID发送
uint16_t feedback_id = param->id;
BSP_CAN_Message_t rx_msg;
if (BSP_CAN_GetMessage(param->can, feedback_id, &rx_msg, BSP_CAN_TIMEOUT_IMMEDIATE) != BSP_OK) {
uint64_t now_time = BSP_TIME_Get();
if (now_time - motor->motor.header.last_online_time > 1000) {
motor->motor.header.online = false;
return DEVICE_ERR_NO_DEV;
}
return DEVICE_ERR;
}
motor->motor.header.online = true;
motor->motor.header.last_online_time = BSP_TIME_Get();
MOTOR_LK_Decode(motor, &rx_msg);
return DEVICE_OK;
}
}
return DEVICE_ERR_NO_DEV;
}
int8_t MOTOR_LK_UpdateAll(void) {
int8_t ret = DEVICE_OK;
for (int can = 0; can < BSP_CAN_NUM; can++) {
MOTOR_LK_CANManager_t *manager = MOTOR_LK_GetCANManager((BSP_CAN_t)can);
if (manager == NULL) continue;
for (int i = 0; i < manager->motor_count; i++) {
MOTOR_LK_t *motor = manager->motors[i];
if (motor != NULL) {
if (MOTOR_LK_Update(&motor->param) != DEVICE_OK) {
ret = DEVICE_ERR;
}
}
}
}
return ret;
}
int8_t MOTOR_LK_SetOutput(MOTOR_LK_Param_t *param, float value) {
if (param == NULL) return DEVICE_ERR_NULL;
MOTOR_LK_CANManager_t *manager = MOTOR_LK_GetCANManager(param->can);
if (manager == NULL) return DEVICE_ERR_NO_DEV;
// 限制输出值范围
if (value > 1.0f) value = 1.0f;
if (value < -1.0f) value = -1.0f;
MOTOR_LK_t *motor = MOTOR_LK_GetMotor(param);
if (motor == NULL) return DEVICE_ERR_NO_DEV;
// 根据反转参数调整输出
float output = param->reverse ? -value : value;
// 转矩闭环控制命令 - 将输出值转换为转矩控制值
int16_t torque_control = (int16_t)(output * (float)LK_TORQUE_RANGE);
// 构建CAN帧
BSP_CAN_StdDataFrame_t tx_frame;
tx_frame.id = param->id;
tx_frame.dlc = MOTOR_TX_BUF_SIZE;
tx_frame.data[0] = LK_CMD_TORQUE_CTRL;
tx_frame.data[1] = 0x00;
tx_frame.data[2] = 0x00;
tx_frame.data[3] = 0x00;
tx_frame.data[4] = (uint8_t)(torque_control & 0xFF);
tx_frame.data[5] = (uint8_t)((torque_control >> 8) & 0xFF);
tx_frame.data[6] = 0x00;
tx_frame.data[7] = 0x00;
return BSP_CAN_TransmitStdDataFrame(param->can, &tx_frame) == BSP_OK ? DEVICE_OK : DEVICE_ERR;
}
int8_t MOTOR_LK_Ctrl(MOTOR_LK_Param_t *param) {
// 对于LK电机每次设置输出时就直接发送控制命令
// 这个函数可以用于发送其他控制命令,如电机开启/关闭
return DEVICE_OK;
}
int8_t MOTOR_LK_MotorOn(MOTOR_LK_Param_t *param) {
if (param == NULL) return DEVICE_ERR_NULL;
BSP_CAN_StdDataFrame_t tx_frame;
tx_frame.id = param->id;
tx_frame.dlc = MOTOR_TX_BUF_SIZE;
// 电机运行命令
tx_frame.data[0] = LK_CMD_MOTOR_ON; // 命令字节
tx_frame.data[1] = 0x00;
tx_frame.data[2] = 0x00;
tx_frame.data[3] = 0x00;
tx_frame.data[4] = 0x00;
tx_frame.data[5] = 0x00;
tx_frame.data[6] = 0x00;
tx_frame.data[7] = 0x00;
return BSP_CAN_TransmitStdDataFrame(param->can, &tx_frame) == BSP_OK ? DEVICE_OK : DEVICE_ERR;
}
int8_t MOTOR_LK_MotorOff(MOTOR_LK_Param_t *param) {
if (param == NULL) return DEVICE_ERR_NULL;
BSP_CAN_StdDataFrame_t tx_frame;
tx_frame.id = param->id;
tx_frame.dlc = MOTOR_TX_BUF_SIZE;
// 电机关闭命令
tx_frame.data[0] = LK_CMD_MOTOR_OFF; // 命令字节
tx_frame.data[1] = 0x00;
tx_frame.data[2] = 0x00;
tx_frame.data[3] = 0x00;
tx_frame.data[4] = 0x00;
tx_frame.data[5] = 0x00;
tx_frame.data[6] = 0x00;
tx_frame.data[7] = 0x00;
return BSP_CAN_TransmitStdDataFrame(param->can, &tx_frame) == BSP_OK ? DEVICE_OK : DEVICE_ERR;
}
MOTOR_LK_t* MOTOR_LK_GetMotor(MOTOR_LK_Param_t *param) {
if (param == NULL) return NULL;
MOTOR_LK_CANManager_t *manager = MOTOR_LK_GetCANManager(param->can);
if (manager == NULL) return NULL;
for (int i = 0; i < manager->motor_count; i++) {
MOTOR_LK_t *motor = manager->motors[i];
if (motor && motor->param.id == param->id) {
return motor;
}
}
return NULL;
}
int8_t MOTOR_LK_Relax(MOTOR_LK_Param_t *param) {
return MOTOR_LK_SetOutput(param, 0.0f);
}
int8_t MOTOR_LK_Offine(MOTOR_LK_Param_t *param) {
MOTOR_LK_t *motor = MOTOR_LK_GetMotor(param);
if (motor) {
motor->motor.header.online = false;
return DEVICE_OK;
}
return DEVICE_ERR_NO_DEV;
}

View File

@@ -0,0 +1,119 @@
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
/* Includes ----------------------------------------------------------------- */
#include "device/device.h"
#include "device/motor.h"
#include "bsp/can.h"
/* Exported constants ------------------------------------------------------- */
#define MOTOR_LK_MAX_MOTORS 32
/* Exported macro ----------------------------------------------------------- */
/* Exported types ----------------------------------------------------------- */
typedef enum {
MOTOR_LK_MF9025,
MOTOR_LK_MF9035,
} MOTOR_LK_Module_t;
/*每个电机需要的参数*/
typedef struct {
BSP_CAN_t can;
uint16_t id;
MOTOR_LK_Module_t module;
bool reverse;
} MOTOR_LK_Param_t;
/*电机实例*/
typedef struct{
MOTOR_LK_Param_t param;
MOTOR_t motor;
} MOTOR_LK_t;
/*CAN管理器管理一个CAN总线上所有的电机*/
typedef struct {
BSP_CAN_t can;
MOTOR_LK_t *motors[MOTOR_LK_MAX_MOTORS];
uint8_t motor_count;
} MOTOR_LK_CANManager_t;
/* Exported functions prototypes -------------------------------------------- */
/**
* @brief 注册一个LK电机
* @param param 电机参数
* @return
*/
int8_t MOTOR_LK_Register(MOTOR_LK_Param_t *param);
/**
* @brief 更新指定电机数据
* @param param 电机参数
* @return
*/
int8_t MOTOR_LK_Update(MOTOR_LK_Param_t *param);
/**
* @brief 设置一个电机的输出
* @param param 电机参数
* @param value 输出值,范围[-1.0, 1.0]
* @return
*/
int8_t MOTOR_LK_SetOutput(MOTOR_LK_Param_t *param, float value);
/**
* @brief 发送控制命令到电机注意一个CAN可以控制多个电机所以只需要发送一次即可
* @param param 电机参数
* @return
*/
int8_t MOTOR_LK_Ctrl(MOTOR_LK_Param_t *param);
/**
* @brief 发送电机开启命令
* @param param 电机参数
* @return
*/
int8_t MOTOR_LK_MotorOn(MOTOR_LK_Param_t *param);
/**
* @brief 发送电机关闭命令
* @param param 电机参数
* @return
*/
int8_t MOTOR_LK_MotorOff(MOTOR_LK_Param_t *param);
/**
* @brief 获取指定电机的实例指针
* @param param 电机参数
* @return
*/
MOTOR_LK_t* MOTOR_LK_GetMotor(MOTOR_LK_Param_t *param);
/**
* @brief 使电机松弛设置输出为0
* @param param
* @return
*/
int8_t MOTOR_LK_Relax(MOTOR_LK_Param_t *param);
/**
* @brief 使电机离线设置在线状态为false
* @param param
* @return
*/
int8_t MOTOR_LK_Offine(MOTOR_LK_Param_t *param);
/**
* @brief
* @param
* @return
*/
int8_t MOTOR_LK_UpdateAll(void);
#ifdef __cplusplus
}
#endif

View File

@@ -0,0 +1,440 @@
/*
灵足电机驱动
灵足电机通信协议:
- CAN 2.0通信接口波特率1Mbps
- 采用扩展帧格式(29位ID)
- ID格式Bit28~24(通信类型) + Bit23~8(数据区2) + Bit7~0(目标地址)
*/
/* Includes ----------------------------------------------------------------- */
#include "motor_lz.h"
#include <stdint.h>
#include <stdbool.h>
#include <string.h>
#include "bsp/can.h"
#include "bsp/mm.h"
#include "bsp/time.h"
#include "component/user_math.h"
/* Private define ----------------------------------------------------------- */
// 灵足电机协议参数
#define LZ_ANGLE_RANGE_RAD (12.57f) /* 角度范围 ±12.57 rad */
#define LZ_VELOCITY_RANGE_RAD_S (20.0f) /* 角速度范围 ±20 rad/s */
#define LZ_TORQUE_RANGE_NM (60.0f) /* 力矩范围 ±60 Nm */
#define LZ_KP_MAX (5000.0f) /* Kp最大值 */
#define LZ_KD_MAX (100.0f) /* Kd最大值 */
#define LZ_RAW_VALUE_MAX (65535) /* 16位原始值最大值 */
#define LZ_TEMP_SCALE (10.0f) /* 温度缩放因子 */
#define LZ_MAX_RECOVER_DIFF_RAD (0.28f)
#define MOTOR_TX_BUF_SIZE (8)
#define MOTOR_RX_BUF_SIZE (8)
/* Private macro ------------------------------------------------------------ */
MOTOR_LZ_MotionParam_t lz_relax_param = {
.target_angle = 0.0f,
.target_velocity = 0.0f,
.kp = 0.0f,
.kd = 0.0f,
.torque = 0.0f,
};
/* Private typedef ---------------------------------------------------------- */
/* Private variables -------------------------------------------------------- */
static MOTOR_LZ_CANManager_t *can_managers[BSP_CAN_NUM] = {NULL};
/* Private function prototypes ---------------------------------------------- */
static MOTOR_LZ_CANManager_t* MOTOR_LZ_GetCANManager(BSP_CAN_t can);
static int8_t MOTOR_LZ_CreateCANManager(BSP_CAN_t can);
static void MOTOR_LZ_Decode(MOTOR_LZ_t *motor, BSP_CAN_Message_t *msg);
static uint32_t MOTOR_LZ_BuildExtID(MOTOR_LZ_CmdType_t cmd_type, uint16_t data2, uint8_t target_id);
static uint16_t MOTOR_LZ_FloatToRaw(float value, float max_value);
static float MOTOR_LZ_RawToFloat(uint16_t raw_value, float max_value);
static int8_t MOTOR_LZ_SendExtFrame(BSP_CAN_t can, uint32_t ext_id, uint8_t *data, uint8_t dlc);
static uint32_t MOTOR_LZ_IdParser(uint32_t original_id, BSP_CAN_FrameType_t frame_type);
/* Private functions -------------------------------------------------------- */
/**
* @brief 获取CAN管理器
*/
static MOTOR_LZ_CANManager_t* MOTOR_LZ_GetCANManager(BSP_CAN_t can) {
if (can >= BSP_CAN_NUM) return NULL;
return can_managers[can];
}
/**
* @brief 创建CAN管理器
*/
static int8_t MOTOR_LZ_CreateCANManager(BSP_CAN_t can) {
if (can >= BSP_CAN_NUM) return DEVICE_ERR;
if (can_managers[can] != NULL) return DEVICE_OK;
can_managers[can] = (MOTOR_LZ_CANManager_t*)BSP_Malloc(sizeof(MOTOR_LZ_CANManager_t));
if (can_managers[can] == NULL) return DEVICE_ERR;
memset(can_managers[can], 0, sizeof(MOTOR_LZ_CANManager_t));
can_managers[can]->can = can;
return DEVICE_OK;
}
/**
* @brief 构建扩展ID
*/
static uint32_t MOTOR_LZ_BuildExtID(MOTOR_LZ_CmdType_t cmd_type, uint16_t data2, uint8_t target_id) {
uint32_t ext_id = 0;
ext_id |= ((uint32_t)cmd_type & 0x1F) << 24; // Bit28~24: 通信类型
ext_id |= ((uint32_t)data2 & 0xFFFF) << 8; // Bit23~8: 数据区2
ext_id |= ((uint32_t)target_id & 0xFF); // Bit7~0: 目标地址
return ext_id;
}
/**
* @brief 浮点值转换为原始值(对称范围:-max_value ~ +max_value
*/
static uint16_t MOTOR_LZ_FloatToRaw(float value, float max_value) {
// 限制范围
if (value > max_value) value = max_value;
if (value < -max_value) value = -max_value;
// 转换为0~65535范围对应-max_value~max_value
return (uint16_t)((value + max_value) / (2.0f * max_value) * (float)LZ_RAW_VALUE_MAX);
}
/**
* @brief 浮点值转换为原始值单向范围0 ~ +max_value
*/
static uint16_t MOTOR_LZ_FloatToRawPositive(float value, float max_value) {
// 限制范围
if (value > max_value) value = max_value;
if (value < 0.0f) value = 0.0f;
// 转换为0~65535范围对应0~max_value
return (uint16_t)(value / max_value * (float)LZ_RAW_VALUE_MAX);
}
/**
* @brief 原始值转换为浮点值
*/
static float MOTOR_LZ_RawToFloat(uint16_t raw_value, float max_value) {
// 将0~65535范围转换为-max_value~max_value
return ((float)raw_value / (float)LZ_RAW_VALUE_MAX) * (2.0f * max_value) - max_value;
}
/**
* @brief 发送扩展帧
*/
static int8_t MOTOR_LZ_SendExtFrame(BSP_CAN_t can, uint32_t ext_id, uint8_t *data, uint8_t dlc) {
BSP_CAN_ExtDataFrame_t tx_frame;
tx_frame.id = ext_id;
tx_frame.dlc = dlc;
if (data != NULL) {
memcpy(tx_frame.data, data, dlc);
} else {
memset(tx_frame.data, 0, dlc);
}
return BSP_CAN_TransmitExtDataFrame(can, &tx_frame) == BSP_OK ? DEVICE_OK : DEVICE_ERR;
}
/**
* @brief 灵足电机ID解析器
* @param original_id 原始CAN ID29位扩展帧
* @param frame_type 帧类型
* @return 解析后的ID用于队列匹配
*
* 灵足电机扩展ID格式
* Bit28~24: 通信类型 (0x1=运控控制, 0x2=反馈数据, 0x3=使能, 0x4=停止, 0x6=设零位)
* Bit23~8: 数据区2 (根据通信类型而定)
* Bit7~0: 目标地址 (目标电机CAN ID)
*/
static uint32_t MOTOR_LZ_IdParser(uint32_t original_id, BSP_CAN_FrameType_t frame_type) {
// 只处理扩展数据帧
if (frame_type != BSP_CAN_FRAME_EXT_DATA) {
return original_id; // 非扩展帧直接返回原始ID
}
// 解析扩展ID各个字段
uint8_t cmd_type = (original_id >> 24) & 0x1F; // Bit28~24: 通信类型
uint16_t data2 = (original_id >> 8) & 0xFFFF; // Bit23~8: 数据区2
uint8_t host_id = (uint8_t)(original_id & 0xFF); // Bit7~0: 主机CAN ID
// 对于反馈数据帧,我们使用特殊的解析规则
if (cmd_type == MOTOR_LZ_CMD_FEEDBACK) {
// 反馈数据的data2字段包含
// Bit8~15: 当前电机CAN ID
// Bit16~21: 故障信息
// Bit22~23: 模式状态
uint8_t motor_can_id = data2 & 0xFF; // bit8~15: 当前电机CAN ID
// 返回格式化的ID便于匹配
// 格式0x02HHMMTT (02=反馈命令, HH=主机ID, MM=电机ID, TT=主机ID)
return (0x02000000) | (host_id << 16) | (motor_can_id << 8) | host_id;
}
// 对于其他命令类型直接返回原始ID
return original_id;
}
/**
* @brief 解码灵足电机反馈数据
*/
static void MOTOR_LZ_Decode(MOTOR_LZ_t *motor, BSP_CAN_Message_t *msg) {
if (motor == NULL || msg == NULL) return;
uint8_t cmd_type = (msg->original_id >> 24) & 0x1F;
if (cmd_type != MOTOR_LZ_CMD_FEEDBACK) return;
uint16_t id_data2 = (msg->original_id >> 8) & 0xFFFF;
uint8_t motor_can_id = id_data2 & 0xFF;
uint8_t fault_info = (id_data2 >> 8) & 0x3F;
uint8_t mode_state = (id_data2 >> 14) & 0x03;
motor->lz_feedback.motor_can_id = motor_can_id;
motor->lz_feedback.fault.under_voltage = (fault_info & 0x01) != 0;
motor->lz_feedback.fault.driver_fault = (fault_info & 0x02) != 0;
motor->lz_feedback.fault.over_temp = (fault_info & 0x04) != 0;
motor->lz_feedback.fault.encoder_fault = (fault_info & 0x08) != 0;
motor->lz_feedback.fault.stall_overload = (fault_info & 0x10) != 0;
motor->lz_feedback.fault.uncalibrated = (fault_info & 0x20) != 0;
motor->lz_feedback.state = (MOTOR_LZ_State_t)mode_state;
// 反馈解码并自动反向
uint16_t raw_angle = (uint16_t)((msg->data[0] << 8) | msg->data[1]);
float angle = MOTOR_LZ_RawToFloat(raw_angle, LZ_ANGLE_RANGE_RAD);
uint16_t raw_velocity = (uint16_t)((msg->data[2] << 8) | msg->data[3]);
float velocity = MOTOR_LZ_RawToFloat(raw_velocity, LZ_VELOCITY_RANGE_RAD_S);
uint16_t raw_torque = (uint16_t)((msg->data[4] << 8) | msg->data[5]);
float torque = MOTOR_LZ_RawToFloat(raw_torque, LZ_TORQUE_RANGE_NM);
while (angle <0){
angle += M_2PI;
}
while (angle > M_2PI){
angle -= M_2PI;
}
// 自动反向
if (motor->param.reverse) {
angle = M_2PI - angle;
velocity = -velocity;
torque = -torque;
}
motor->lz_feedback.current_angle = angle;
motor->lz_feedback.current_velocity = velocity;
motor->lz_feedback.current_torque = torque;
uint16_t raw_temp = (uint16_t)((msg->data[6] << 8) | msg->data[7]);
motor->lz_feedback.temperature = (float)raw_temp / LZ_TEMP_SCALE;
motor->motor.feedback.rotor_abs_angle = angle;
motor->motor.feedback.rotor_speed = velocity;
motor->motor.feedback.torque_current = torque;
motor->motor.feedback.temp = (int8_t)motor->lz_feedback.temperature;
motor->motor.header.online = true;
motor->motor.header.last_online_time = BSP_TIME_Get();
}
/* Exported functions ------------------------------------------------------- */
/**
* @brief 初始化灵足电机驱动系统
* @return 设备状态码
*/
int8_t MOTOR_LZ_Init(void) {
// 注册灵足电机专用的ID解析器
return BSP_CAN_RegisterIdParser(MOTOR_LZ_IdParser) == BSP_OK ? DEVICE_OK : DEVICE_ERR;
}
int8_t MOTOR_LZ_Register(MOTOR_LZ_Param_t *param) {
if (param == NULL) return DEVICE_ERR_NULL;
if (MOTOR_LZ_CreateCANManager(param->can) != DEVICE_OK) return DEVICE_ERR;
MOTOR_LZ_CANManager_t *manager = MOTOR_LZ_GetCANManager(param->can);
if (manager == NULL) return DEVICE_ERR;
// 检查是否已注册
for (int i = 0; i < manager->motor_count; i++) {
if (manager->motors[i] && manager->motors[i]->param.motor_id == param->motor_id) {
return DEVICE_ERR; // 已注册
}
}
// 检查数量
if (manager->motor_count >= MOTOR_LZ_MAX_MOTORS) return DEVICE_ERR;
// 创建新电机实例
MOTOR_LZ_t *new_motor = (MOTOR_LZ_t*)BSP_Malloc(sizeof(MOTOR_LZ_t));
if (new_motor == NULL) return DEVICE_ERR;
memcpy(&new_motor->param, param, sizeof(MOTOR_LZ_Param_t));
memset(&new_motor->motor, 0, sizeof(MOTOR_t));
memset(&new_motor->lz_feedback, 0, sizeof(MOTOR_LZ_Feedback_t));
memset(&new_motor->motion_param, 0, sizeof(MOTOR_LZ_MotionParam_t));
new_motor->motor.reverse = param->reverse;
// 注册CAN接收ID - 使用解析后的反馈数据ID
// 构建反馈数据的原始扩展ID
// 反馈数据data2包含电机ID(bit8~15)target_id是主机ID
uint32_t original_feedback_id = MOTOR_LZ_BuildExtID(MOTOR_LZ_CMD_FEEDBACK, param->motor_id, param->host_id);
// 通过ID解析器得到解析后的ID
uint32_t parsed_feedback_id = MOTOR_LZ_IdParser(original_feedback_id, BSP_CAN_FRAME_EXT_DATA);
if (BSP_CAN_RegisterId(param->can, parsed_feedback_id, 3) != BSP_OK) {
BSP_Free(new_motor);
return DEVICE_ERR;
}
manager->motors[manager->motor_count] = new_motor;
manager->motor_count++;
return DEVICE_OK;
}
int8_t MOTOR_LZ_Update(MOTOR_LZ_Param_t *param) {
if (param == NULL) return DEVICE_ERR_NULL;
MOTOR_LZ_CANManager_t *manager = MOTOR_LZ_GetCANManager(param->can);
if (manager == NULL) return DEVICE_ERR_NO_DEV;
for (int i = 0; i < manager->motor_count; i++) {
MOTOR_LZ_t *motor = manager->motors[i];
if (motor && motor->param.motor_id == param->motor_id) {
// 获取反馈数据 - 使用解析后的ID
uint32_t original_feedback_id = MOTOR_LZ_BuildExtID(MOTOR_LZ_CMD_FEEDBACK, param->motor_id, param->host_id);
uint32_t parsed_feedback_id = MOTOR_LZ_IdParser(original_feedback_id, BSP_CAN_FRAME_EXT_DATA);
BSP_CAN_Message_t msg;
while (BSP_CAN_GetMessage(param->can, parsed_feedback_id, &msg, 0) == BSP_OK) {
MOTOR_LZ_Decode(motor, &msg);
}
return DEVICE_OK;
}
}
return DEVICE_ERR_NO_DEV;
}
int8_t MOTOR_LZ_UpdateAll(void) {
int8_t ret = DEVICE_OK;
for (int can = 0; can < BSP_CAN_NUM; can++) {
MOTOR_LZ_CANManager_t *manager = MOTOR_LZ_GetCANManager((BSP_CAN_t)can);
if (manager == NULL) continue;
for (int i = 0; i < manager->motor_count; i++) {
MOTOR_LZ_t *motor = manager->motors[i];
if (motor) {
if (MOTOR_LZ_Update(&motor->param) != DEVICE_OK) {
ret = DEVICE_ERR;
}
}
}
}
return ret;
}
int8_t MOTOR_LZ_MotionControl(MOTOR_LZ_Param_t *param, MOTOR_LZ_MotionParam_t *motion_param) {
if (param == NULL || motion_param == NULL) return DEVICE_ERR_NULL;
MOTOR_LZ_t *motor = MOTOR_LZ_GetMotor(param);
if (motor == NULL) return DEVICE_ERR_NO_DEV;
// 自动反向控制
MOTOR_LZ_MotionParam_t send_param = *motion_param;
if (param->reverse) {
send_param.target_angle = -send_param.target_angle;
send_param.target_velocity = -send_param.target_velocity;
send_param.torque = -send_param.torque;
}
memcpy(&motor->motion_param, motion_param, sizeof(MOTOR_LZ_MotionParam_t));
uint16_t raw_torque = MOTOR_LZ_FloatToRaw(send_param.torque, LZ_TORQUE_RANGE_NM);
uint32_t ext_id = MOTOR_LZ_BuildExtID(MOTOR_LZ_CMD_MOTION, raw_torque, param->motor_id);
uint8_t data[8];
uint16_t raw_angle = MOTOR_LZ_FloatToRaw(send_param.target_angle, LZ_ANGLE_RANGE_RAD);
data[0] = (raw_angle >> 8) & 0xFF;
data[1] = raw_angle & 0xFF;
uint16_t raw_velocity = MOTOR_LZ_FloatToRaw(send_param.target_velocity, LZ_VELOCITY_RANGE_RAD_S);
data[2] = (raw_velocity >> 8) & 0xFF;
data[3] = raw_velocity & 0xFF;
uint16_t raw_kp = MOTOR_LZ_FloatToRawPositive(send_param.kp, LZ_KP_MAX);
data[4] = (raw_kp >> 8) & 0xFF;
data[5] = raw_kp & 0xFF;
uint16_t raw_kd = MOTOR_LZ_FloatToRawPositive(send_param.kd, LZ_KD_MAX);
data[6] = (raw_kd >> 8) & 0xFF;
data[7] = raw_kd & 0xFF;
return MOTOR_LZ_SendExtFrame(param->can, ext_id, data, 8);
}
int8_t MOTOR_LZ_Enable(MOTOR_LZ_Param_t *param) {
if (param == NULL) return DEVICE_ERR_NULL;
// 构建扩展ID - 使能命令
uint32_t ext_id = MOTOR_LZ_BuildExtID(MOTOR_LZ_CMD_ENABLE, param->host_id, param->motor_id);
// 数据区清零
uint8_t data[8] = {0};
return MOTOR_LZ_SendExtFrame(param->can, ext_id, data, 8);
}
int8_t MOTOR_LZ_Disable(MOTOR_LZ_Param_t *param, bool clear_fault) {
if (param == NULL) return DEVICE_ERR_NULL;
// 构建扩展ID - 停止命令
uint32_t ext_id = MOTOR_LZ_BuildExtID(MOTOR_LZ_CMD_DISABLE, param->host_id, param->motor_id);
// 数据区
uint8_t data[8] = {0};
if (clear_fault) {
data[0] = 1; // Byte[0]=1时清故障
}
return MOTOR_LZ_SendExtFrame(param->can, ext_id, data, 8);
}
int8_t MOTOR_LZ_SetZero(MOTOR_LZ_Param_t *param) {
if (param == NULL) return DEVICE_ERR_NULL;
// 构建扩展ID - 设置零位命令
uint32_t ext_id = MOTOR_LZ_BuildExtID(MOTOR_LZ_CMD_SET_ZERO, param->host_id, param->motor_id);
// 数据区 - Byte[0]=1
uint8_t data[8] = {1, 0, 0, 0, 0, 0, 0, 0};
return MOTOR_LZ_SendExtFrame(param->can, ext_id, data, 8);
}
MOTOR_LZ_t* MOTOR_LZ_GetMotor(MOTOR_LZ_Param_t *param) {
if (param == NULL) return NULL;
MOTOR_LZ_CANManager_t *manager = MOTOR_LZ_GetCANManager(param->can);
if (manager == NULL) return NULL;
for (int i = 0; i < manager->motor_count; i++) {
MOTOR_LZ_t *motor = manager->motors[i];
if (motor && motor->param.motor_id == param->motor_id) {
return motor;
}
}
return NULL;
}
int8_t MOTOR_LZ_Relax(MOTOR_LZ_Param_t *param) {
return MOTOR_LZ_MotionControl(param, &lz_relax_param);
}
int8_t MOTOR_LZ_Offline(MOTOR_LZ_Param_t *param) {
MOTOR_LZ_t *motor = MOTOR_LZ_GetMotor(param);
if (motor) {
motor->motor.header.online = false;
return DEVICE_OK;
}
return DEVICE_ERR_NO_DEV;
}
static MOTOR_LZ_Feedback_t* MOTOR_LZ_GetFeedback(MOTOR_LZ_Param_t *param) {
MOTOR_LZ_t *motor = MOTOR_LZ_GetMotor(param);
if (motor && motor->motor.header.online) {
return &motor->lz_feedback;
}
return NULL;
}

View File

@@ -0,0 +1,212 @@
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
/* Includes ----------------------------------------------------------------- */
#include "device/device.h"
#include "device/motor.h"
#include "bsp/can.h"
/* Exported constants ------------------------------------------------------- */
#define MOTOR_LZ_MAX_MOTORS 32
/* Exported macro ----------------------------------------------------------- */
/* Exported types ----------------------------------------------------------- */
typedef enum {
MOTOR_LZ_RSO0,
MOTOR_LZ_RSO1,
MOTOR_LZ_RSO2,
MOTOR_LZ_RSO3,
MOTOR_LZ_RSO4,
MOTOR_LZ_RSO5,
MOTOR_LZ_RSO6,
} MOTOR_LZ_Module_t;
/* 灵足电机控制模式 */
typedef enum {
MOTOR_LZ_MODE_MOTION = 0x1, /* 运控模式 */
MOTOR_LZ_MODE_CURRENT = 0x2, /* 电流模式 */
MOTOR_LZ_MODE_VELOCITY = 0x3, /* 速度模式 */
MOTOR_LZ_MODE_POSITION = 0x4, /* 位置模式 */
} MOTOR_LZ_ControlMode_t;
/* 灵足电机通信类型 */
typedef enum {
MOTOR_LZ_CMD_MOTION = 0x1, /* 运控模式控制 */
MOTOR_LZ_CMD_FEEDBACK = 0x2, /* 电机反馈数据 */
MOTOR_LZ_CMD_ENABLE = 0x3, /* 电机使能运行 */
MOTOR_LZ_CMD_DISABLE = 0x4, /* 电机停止运行 */
MOTOR_LZ_CMD_SET_ZERO = 0x6, /* 设置电机机械零位 */
} MOTOR_LZ_CmdType_t;
/* 灵足电机运行状态 */
typedef enum {
MOTOR_LZ_STATE_RESET = 0, /* Reset模式[复位] */
MOTOR_LZ_STATE_CALI = 1, /* Cali模式[标定] */
MOTOR_LZ_STATE_MOTOR = 2, /* Motor模式[运行] */
} MOTOR_LZ_State_t;
/* 灵足电机故障信息 */
typedef struct {
bool uncalibrated; /* bit21: 未标定 */
bool stall_overload; /* bit20: 堵转过载故障 */
bool encoder_fault; /* bit19: 磁编码故障 */
bool over_temp; /* bit18: 过温 */
bool driver_fault; /* bit17: 驱动故障 */
bool under_voltage; /* bit16: 欠压故障 */
} MOTOR_LZ_Fault_t;
/* 灵足电机运控参数 */
typedef struct {
float target_angle; /* 目标角度 (-12.57f~12.57f rad) */
float target_velocity; /* 目标角速度 (-20~20 rad/s) */
float kp; /* 位置增益 (0.0~5000.0) */
float kd; /* 微分增益 (0.0~100.0) */
float torque; /* 力矩 (-60~60 Nm) */
} MOTOR_LZ_MotionParam_t;
/*每个电机需要的参数*/
typedef struct {
BSP_CAN_t can; /* CAN总线 */
uint8_t motor_id; /* 电机CAN ID */
uint8_t host_id; /* 主机CAN ID */
MOTOR_LZ_Module_t module; /* 电机型号 */
bool reverse; /* 是否反向 */
MOTOR_LZ_ControlMode_t mode; /* 控制模式 */
} MOTOR_LZ_Param_t;
/*电机反馈信息扩展*/
typedef struct {
float current_angle; /* 当前角度 (-12.57f~12.57f rad) */
float current_velocity; /* 当前角速度 (-20~20 rad/s) */
float current_torque; /* 当前力矩 (-60~60 Nm) */
float temperature; /* 当前温度 (摄氏度) */
MOTOR_LZ_State_t state; /* 运行状态 */
MOTOR_LZ_Fault_t fault; /* 故障信息 */
uint8_t motor_can_id; /* 当前电机CAN ID */
} MOTOR_LZ_Feedback_t;
/*电机实例*/
typedef struct {
MOTOR_LZ_Param_t param;
MOTOR_t motor;
MOTOR_LZ_Feedback_t lz_feedback; /* 灵足电机特有反馈信息 */
MOTOR_LZ_MotionParam_t motion_param; /* 运控模式参数 */
} MOTOR_LZ_t;
/*CAN管理器管理一个CAN总线上所有的电机*/
typedef struct {
BSP_CAN_t can;
MOTOR_LZ_t *motors[MOTOR_LZ_MAX_MOTORS];
uint8_t motor_count;
} MOTOR_LZ_CANManager_t;
/* Exported functions prototypes -------------------------------------------- */
/**
* @brief 初始化灵足电机驱动系统
* @return 设备状态码
*/
int8_t MOTOR_LZ_Init(void);
/**
* @brief 注册一个灵足电机
* @param param 电机参数
* @return 设备状态码
*/
int8_t MOTOR_LZ_Register(MOTOR_LZ_Param_t *param);
/**
* @brief 更新指定电机数据
* @param param 电机参数
* @return 设备状态码
*/
int8_t MOTOR_LZ_Update(MOTOR_LZ_Param_t *param);
/**
* @brief 更新所有电机数据
* @return 设备状态码
*/
int8_t MOTOR_LZ_UpdateAll(void);
/**
* @brief 运控模式控制电机
* @param param 电机参数
* @param motion_param 运控参数
* @return 设备状态码
*/
int8_t MOTOR_LZ_MotionControl(MOTOR_LZ_Param_t *param, MOTOR_LZ_MotionParam_t *motion_param);
/**
* @brief 电流(力矩)模式控制电机
* @param param 电机参数
* @param torque 目标力矩 (-60~60 Nm)
* @return 设备状态码
*/
int8_t MOTOR_LZ_TorqueControl(MOTOR_LZ_Param_t *param, float torque);
/**
* @brief 位置模式控制电机
* @param param 电机参数
* @param target_angle 目标角度 (-12.57~12.57 rad)
* @param max_velocity 最大速度 (0~20 rad/s)
* @return 设备状态码
*/
int8_t MOTOR_LZ_PositionControl(MOTOR_LZ_Param_t *param, float target_angle, float max_velocity);
/**
* @brief 速度模式控制电机
* @param param 电机参数
* @param target_velocity 目标速度 (-20~20 rad/s)
* @return 设备状态码
*/
int8_t MOTOR_LZ_VelocityControl(MOTOR_LZ_Param_t *param, float target_velocity);
/**
* @brief 电机使能运行
* @param param 电机参数
* @return 设备状态码
*/
int8_t MOTOR_LZ_Enable(MOTOR_LZ_Param_t *param);
/**
* @brief 电机停止运行
* @param param 电机参数
* @param clear_fault 是否清除故障
* @return 设备状态码
*/
int8_t MOTOR_LZ_Disable(MOTOR_LZ_Param_t *param, bool clear_fault);
/**
* @brief 设置电机机械零位
* @param param 电机参数
* @return 设备状态码
*/
int8_t MOTOR_LZ_SetZero(MOTOR_LZ_Param_t *param);
/**
* @brief 获取指定电机的实例指针
* @param param 电机参数
* @return 电机实例指针失败返回NULL
*/
MOTOR_LZ_t* MOTOR_LZ_GetMotor(MOTOR_LZ_Param_t *param);
/**
* @brief 使电机松弛(发送停止命令)
* @param param 电机参数
* @return 设备状态码
*/
int8_t MOTOR_LZ_Relax(MOTOR_LZ_Param_t *param);
/**
* @brief 使电机离线设置在线状态为false
* @param param 电机参数
* @return 设备状态码
*/
int8_t MOTOR_LZ_Offline(MOTOR_LZ_Param_t *param);
#ifdef __cplusplus
}
#endif

View File

@@ -0,0 +1,327 @@
/*
Odrive电机驱动
*/
/* Includes ----------------------------------------------------------------- */
#include "motor_odrive.h"
#include <stdbool.h>
#include <string.h>
#include "bsp/can.h"
#include "bsp/mm.h"
#include "bsp/time.h"
#include "component/user_math.h"
/* Private define ----------------------------------------------------------- */
/* Private macro ------------------------------------------------------------ */
/* Private typedef ---------------------------------------------------------- */
/* Private variables -------------------------------------------------------- */
static ODrive_CANManager_t *can_managers[BSP_CAN_NUM] = {NULL};
// 获取指定CAN总线的电机管理器指针
static ODrive_CANManager_t* MOTOR_GetCANManager(BSP_CAN_t can) {
if (can >= BSP_CAN_NUM) return NULL;
return can_managers[can];
}
// 为指定CAN总线创建电机管理器
static int8_t MOTOR_CreateCANManager(BSP_CAN_t can) {
if (can >= BSP_CAN_NUM) return DEVICE_ERR;
if (can_managers[can] != NULL) return DEVICE_OK;
can_managers[can] = (ODrive_CANManager_t*)BSP_Malloc(sizeof(ODrive_CANManager_t));
if (can_managers[can] == NULL) return DEVICE_ERR;
memset(can_managers[can], 0, sizeof(ODrive_CANManager_t));
can_managers[can]->can = can;
return DEVICE_OK;
}
// 解析CAN报文更新电机反馈信息
static void Motor_Decode(ODrive_t *motor, BSP_CAN_Message_t *msg)
{
uint8_t axis_id = (msg->original_id >> 5) & 0x3F; // 提取电机号0~63
uint8_t cmd_id = msg->original_id & 0x1F; // 提取命令 ID低 5 位)
motor->param.id = axis_id; // 保存电机 ID
// 解析帧类型(数据帧或远程帧)
if (msg->frame_type == BSP_CAN_FRAME_STD_DATA) {
// 数据帧处理
switch (cmd_id)
{
case ODRIVE_HEARTBEAT_MESSAGE: // 0x001 ODrive心跳消息
// motor->motor.feedback.axis_error = (msg->data[0] | msg->data[1]<<8 | msg->data[2]<<16 | msg->data[3]<<24);
// motor->motor.feedback.axis_state = msg->data[4];
// motor->motor.feedback.controller_status = msg->data[5];
break;
case ENCODER_ESTIMATES: // 0x009
{
uint32_t raw_pos = (msg->data[0] | msg->data[1]<<8 | msg->data[2]<<16 | msg->data[3]<<24);
uint32_t raw_vel = (msg->data[4] | msg->data[5]<<8 | msg->data[6]<<16 | msg->data[7]<<24);
memcpy(&motor->motor.feedback.rotor_abs_angle, &raw_pos, sizeof(float));
memcpy(&motor->motor.feedback.rotor_speed, &raw_vel, sizeof(float));
}
break;
case GET_ENCODER_COUNT: // 0x014
// motor->motor.feedback.encoder_shadow = (msg->data[0] | msg->data[1]<<8 | msg->data[2]<<16 | msg->data[3]<<24);
// motor->motor.feedback.encoder_cpr = (msg->data[4] | msg->data[5]<<8 | msg->data[6]<<16 | msg->data[7]<<24);
break;
case GET_BUS_VOLTAGE_CURRENT: // 0x017
{
uint32_t raw_vbus, raw_ibus;
raw_vbus = (msg->data[0] | msg->data[1]<<8 | msg->data[2]<<16 | msg->data[3]<<24);
raw_ibus = (msg->data[4] | msg->data[5]<<8 | msg->data[6]<<16 | msg->data[7]<<24);
// memcpy(&motor->motor.feedback.bus_voltage, &raw_vbus, sizeof(float));
memcpy(&motor->motor.feedback.torque_current, &raw_ibus, sizeof(float));
}
break;
case GET_IQ: // 0x018
{
uint32_t raw_iq_set, raw_iq_meas;
raw_iq_set = (msg->data[0] | msg->data[1]<<8 | msg->data[2]<<16 | msg->data[3]<<24);
raw_iq_meas = (msg->data[4] | msg->data[5]<<8 | msg->data[6]<<16 | msg->data[7]<<24);
// memcpy(&motor->motor.feedback.iq_setpoint, &raw_iq_set, sizeof(float));
// memcpy(&motor->motor.feedback.iq_measured, &raw_iq_meas, sizeof(float));
}
break;
default:
break;
}
}
}
/* Exported functions ------------------------------------------------------- */
// 注册一个新的电机实例到管理器
int8_t ODrive_Register(ODrive_Param_t *param) {
if (param == NULL) return DEVICE_ERR_NULL;
if (MOTOR_CreateCANManager(param->can) != DEVICE_OK) return DEVICE_ERR;
ODrive_CANManager_t *manager = MOTOR_GetCANManager(param->can);
if (manager == NULL) return DEVICE_ERR;
// 检查是否已注册
for (int i = 0; i < manager->motor_count; i++) {
if (manager->motors[i] && manager->motors[i]->param.id == param->id) {
return DEVICE_ERR_INITED;
}
}
// 检查数量
if (manager->motor_count >= ODRIVE_MAX_MOTORS) return DEVICE_ERR;
// 创建新电机实例
ODrive_t *new_motor = (ODrive_t*)BSP_Malloc(sizeof(ODrive_t));
if (new_motor == NULL) return DEVICE_ERR;
memcpy(&new_motor->param, param, sizeof(ODrive_Param_t));
memset(&new_motor->motor, 0, sizeof(MOTOR_t));
new_motor->motor.reverse = param->reverse;
// 注册CAN接收ID
if (BSP_CAN_RegisterId(param->can, param->id, 3) != BSP_OK) {
BSP_Free(new_motor);
return DEVICE_ERR;
}
manager->motors[manager->motor_count] = new_motor;
manager->motor_count++;
return DEVICE_OK;
}
// 更新指定电机的反馈数据
int8_t ODrive_Update(ODrive_Param_t *param) {
if (param == NULL) return DEVICE_ERR_NULL;
ODrive_CANManager_t *manager = MOTOR_GetCANManager(param->can);
if (manager == NULL) return DEVICE_ERR_NO_DEV;
for (int i = 0; i < manager->motor_count; i++) {
ODrive_t *motor = manager->motors[i];
if (motor && motor->param.id == param->id) {
BSP_CAN_Message_t rx_msg;
if (BSP_CAN_GetMessage(param->can, param->id, &rx_msg, BSP_CAN_TIMEOUT_IMMEDIATE) != BSP_OK) {
uint64_t now_time = BSP_TIME_Get();
if (now_time - motor->motor.header.last_online_time > 1000) {
motor->motor.header.online = false;
return DEVICE_ERR_NO_DEV;
}
return DEVICE_ERR;
}
motor->motor.header.online = true;
motor->motor.header.last_online_time = BSP_TIME_Get();
Motor_Decode(motor, &rx_msg);
return DEVICE_OK;
}
}
return DEVICE_ERR_NO_DEV;
}
// 更新所有CAN总线下所有电机的反馈数据
int8_t ODrive_UpdateAll(void) {
int8_t ret = DEVICE_OK;
for (int can = 0; can < BSP_CAN_NUM; can++) {
ODrive_CANManager_t *manager = MOTOR_GetCANManager((BSP_CAN_t)can);
if (manager == NULL) continue;
for (int i = 0; i < manager->motor_count; i++) {
ODrive_t *motor = manager->motors[i];
if (motor != NULL) {
if (ODrive_Update(&motor->param) != DEVICE_OK) {
ret = DEVICE_ERR;
}
}
}
}
return ret;
}
// 获取指定参数对应的电机实例指针
ODrive_t* ODrive_GetMotor(ODrive_Param_t *param) {
if (param == NULL) return NULL;
ODrive_CANManager_t *manager = MOTOR_GetCANManager(param->can);
if (manager == NULL) return NULL;
for (int i = 0; i < manager->motor_count; i++) {
ODrive_t *motor = manager->motors[i];
if (motor && motor->param.id == param->id) {
return motor;
}
}
return NULL;
}
// 设置指定电机的输出值
int8_t ODrive_SetOutput(ODrive_Param_t *param, float value) {
if (param == NULL) return DEVICE_ERR_NULL;
// 如果电机反转标志为 true则反向值
if (param->reverse) {
value = -value;
}
BSP_CAN_StdDataFrame_t tx_frame;
uint16_t command_id;
uint8_t *pVal = (uint8_t *)&value;
// 选择命令 ID 和数据打包方式
switch (param->mode) {
case POSITION_CONTROL: {
command_id = SET_INPUT_POS;
float pos = value;
int16_t vel_ff = 0; // 可扩展为参数传入 0就行
int16_t torque_ff = 0; // 可扩展为参数传入 0就行
uint8_t *pPos = (uint8_t *)&pos;
uint8_t *pVel = (uint8_t *)&vel_ff;
uint8_t *pTor = (uint8_t *)&torque_ff;
memcpy(&tx_frame.data[0], pPos, 4);
memcpy(&tx_frame.data[4], pVel, 2);
memcpy(&tx_frame.data[6], pTor, 2);
tx_frame.dlc = 8;
break;
}
case VELOCITY_CONTROL: {
command_id = SET_INPUT_VEL;
float vel = value;
float torque_ff = 0.0f; // 可扩展为参数传入
uint8_t *pVel = (uint8_t *)&vel;
uint8_t *pTor = (uint8_t *)&torque_ff;
memcpy(&tx_frame.data[0], pVel, 4);
memcpy(&tx_frame.data[4], pTor, 4);
tx_frame.dlc = 8;
break;
}
case TORQUE_CONTROL: {
command_id = SET_INPUT_TORQUE;
memcpy(&tx_frame.data[0], pVal, 4);
tx_frame.dlc = 4;
break;
}
case VOLTAGE_CONTROL:
default:
return DEVICE_ERR; // 暂不支持电压模式
}
// 组装 CAN ID标准帧
tx_frame.id = (param->id << 5) | command_id;
// 标准数据帧
return BSP_CAN_TransmitStdDataFrame(param->can, &tx_frame) == BSP_OK ? DEVICE_OK : DEVICE_ERR;
}
// 设置加速度和减速度
int8_t ODrive_SetAccel(ODrive_Param_t *param, float accel, float decel) {
if (param == NULL) return DEVICE_ERR_NULL;
BSP_CAN_StdDataFrame_t tx_frame;
uint16_t command_id = SET_TRAJ_ACCEL_LIMITS;
uint8_t *pAccel = (uint8_t *)&accel;
uint8_t *pDecel = (uint8_t *)&decel;
memcpy(&tx_frame.data[0], pAccel, 4);
memcpy(&tx_frame.data[4], pDecel, 4);
tx_frame.dlc = 8;
tx_frame.id = (param->id << 5) | command_id;
return BSP_CAN_TransmitStdDataFrame(param->can, &tx_frame) == BSP_OK ? DEVICE_OK : DEVICE_ERR;
}
// 获取位置和速度反馈
int8_t ODrive_RequestEncoderEstimates(ODrive_Param_t *param) {
if (param == NULL) return DEVICE_ERR_NULL;
BSP_CAN_StdDataFrame_t tx_frame;
uint16_t command_id = ENCODER_ESTIMATES; // 请求编码器估计值命令
uint8_t zero_data[8] = {0}; // 发送全 0 数据ODrive 协议要求)
memcpy(tx_frame.data, zero_data, 8);
tx_frame.dlc = 8;
tx_frame.id = (param->id << 5) | command_id;
return BSP_CAN_TransmitStdDataFrame(param->can, &tx_frame) == BSP_OK ? DEVICE_OK : DEVICE_ERR;
}
// 设置轴请求状态(一般用来重启 ODrive 的某个轴)
// ODrive_SetAxisRequestedState(odrive_axis[0], CLOSED_LOOP_CONTROL);
int8_t ODrive_SetAxisRequestedState(ODrive_Param_t *param, Axis_State state) {
if (param == NULL) return DEVICE_ERR_NULL;
BSP_CAN_StdDataFrame_t tx_frame;
uint16_t command_id = SET_AXIS_REQUESTED_STATE;
// 将 state 转为 4 字节
memcpy(tx_frame.data, &state, 4);
memset(&tx_frame.data[4], 0, 4);
tx_frame.dlc = 4;
// 组装 CAN ID
tx_frame.id = (param->id << 5) | command_id;
return BSP_CAN_TransmitStdDataFrame(param->can, &tx_frame) == BSP_OK ? DEVICE_OK : DEVICE_ERR;
}
// 清除错误
int8_t ODrive_ClearErrors(ODrive_Param_t *param) {
if (param == NULL) return DEVICE_ERR_NULL;
BSP_CAN_StdDataFrame_t tx_frame;
uint16_t command_id = CLEAR_ERRORS;
memset(tx_frame.data, 0, 8);
tx_frame.dlc = 0;
tx_frame.id = (param->id << 5) | command_id;
return BSP_CAN_TransmitStdDataFrame(param->can, &tx_frame) == BSP_OK ? DEVICE_OK : DEVICE_ERR;
}
// 重启 ODrive
int8_t ODrive_Reboot(ODrive_Param_t *param) {
if (param == NULL) return DEVICE_ERR_NULL;
BSP_CAN_StdDataFrame_t tx_frame;
uint16_t command_id = REBOOT_ODRIVE;
memset(tx_frame.data, 0, 8);
tx_frame.dlc = 0;
tx_frame.id = (param->id << 5) | command_id;
return BSP_CAN_TransmitStdDataFrame(param->can, &tx_frame) == BSP_OK ? DEVICE_OK : DEVICE_ERR;
}

View File

@@ -0,0 +1,162 @@
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
/* Includes ----------------------------------------------------------------- */
#include "device/device.h"
#include "device/motor.h"
#include "bsp/can.h"
/* Private define ----------------------------------------------------------- */
//ODrive型号根据实际情况调整
#define ODRIVE_MAX_MOTORS 2
//COMMAND ID
#define ODRIVE_HEARTBEAT_MESSAGE 0x001 // ODrive心跳消息
#define SET_AXIS_NODE_ID 0x006 // 设置电机节点ID
#define GET_ENCODER_ESTIMATES 0x008 // 获取编码器估计值
#define GET_ENCODER_COUNT 0x00A // 获取编码器计数
#define SET_AXIS_REQUESTED_STATE 0x007 // 设置电机请求状态
#define ENCODER_ESTIMATES 0x009 // 编码器估计值
#define GET_ENCODER_COUNT 0x00A // 获取编码器计数
#define SET_CONTROLLER_MODES 0x00B // 设置控制器模式
#define SET_INPUT_POS 0x00C // 设置输入位置
#define SET_INPUT_VEL 0x00D // 设置输入速度
#define SET_INPUT_TORQUE 0x00E // 设置输入转矩
#define SET_LIMITS 0x00F // 设置限制
#define GET_IQ 0x014 // 获取电流
#define REBOOT_ODRIVE 0x016 // 重启ODrive
#define GET_BUS_VOLTAGE_CURRENT 0x017 // 获取总线电压和电流
#define CLEAR_ERRORS 0x018 // 清除错误
#define SET_POSITION_GAIN 0x01A // 设置位置增益
#define SET_VEL_GAINS 0x01B // 设置速度增益
#define SET_TRAJ_ACCEL_LIMITS 0x012 // 设置轨迹加速度限制
/* Exported constants ------------------------------------------------------- */
/* Exported macro ----------------------------------------------------------- */
/* Exported types ----------------------------------------------------------- */
//Axis States
typedef enum {
UNDEFINED = 0x0,
IDLE = 0x1,
STARTUP_SEQUENCE = 0x2,
FULL_CALIBRATION_SEQUENCE = 0x3,
MOTOR_CALIBRATION = 0x4,
ENCODER_INDEX_SEARCH = 0x6,
ENCODER_OFFSET_CALIBRATION = 0x7,
CLOSED_LOOP_CONTROL = 0x8,
LOCKIN_SPIN = 0x9,
ENCODER_DIR_FIND = 0xA,
HOMING = 0xB,
ENCODER_HALL_POLARITY_CALIBRATION = 0xC,
ENCODER_HALL_PHASE_CALIBRATION = 0xD
} Axis_State;
//Control Modes
typedef enum{
VOLTAGE_CONTROL = 0x0,
TORQUE_CONTROL = 0x1,
VELOCITY_CONTROL = 0x2,
POSITION_CONTROL = 0x3
} Control_Mode;
/*每个电机需要的参数*/
typedef struct {
BSP_CAN_t can;
uint16_t id;
uint16_t mode;
bool reverse;
} ODrive_Param_t;
/*电机实例*/
typedef struct ODrive_t {
ODrive_Param_t param;
MOTOR_t motor;
} ODrive_t;
/*CAN管理器管理一个CAN总线上所有的电机*/
typedef struct {
BSP_CAN_t can;
ODrive_t *motors[ODRIVE_MAX_MOTORS];
uint8_t motor_count;
} ODrive_CANManager_t;
/* Exported functions prototypes -------------------------------------------- */
/**
* @brief 注册一个odrive电机
* @param param 电机参数
* @return
*/
int8_t ODrive_Register(ODrive_Param_t *param);
/**
* @brief 更新指定电机数据
* @param param 电机参数
* @return
*/
int8_t ODrive_Update(ODrive_Param_t *param);
/** * @brief 更新所有ODrive电机状态
* @return
*/
int8_t ODrive_UpdateAll(void);
/**
* @brief 设置一个电机的输出
* @param param 电机参数
* @param value 输出值
* @return
*/
int8_t ODrive_SetOutput(ODrive_Param_t *param, float value);
/** * @brief 设置电机加速度和减速度限制
* @param param 电机参数
* @param accel 加速度
* @param decel 减速度
* @return
*/
int8_t ODrive_SetAccel(ODrive_Param_t *param, float accel, float decel);
/**
* @brief 获取指定电机的实例指针
* @param param 电机参数
* @return
*/
ODrive_t* ODrive_GetMotor(ODrive_Param_t *param);
/** * @brief 获取指定电机的编码器估计值
* @param param 电机参数
* @return
*/
int8_t ODrive_RequestEncoderEstimates(ODrive_Param_t *param);
/** * @brief 设置轴请求状态(一般用来重启 ODrive 的某个轴)
* @param param 电机参数
* @return
*/
int8_t ODrive_SetAxisRequestedState(ODrive_Param_t *param, Axis_State state);
/** * @brief 清除错误
* @param param 电机参数
* @return
*/
int8_t ODrive_ClearErrors(ODrive_Param_t *param);
/** * @brief 重启 ODrive
* @param param 电机参数
* @return
*/
int8_t ODrive_Reboot(ODrive_Param_t *param);
#ifdef __cplusplus
}
#endif

View File

@@ -0,0 +1,321 @@
/*
RM电机驱动
*/
/* Includes ----------------------------------------------------------------- */
#include "motor_rm.h"
#include <stdbool.h>
#include <string.h>
#include "bsp/can.h"
#include "bsp/mm.h"
#include "bsp/time.h"
#include "component/user_math.h"
/* USER INCLUDE BEGIN */
/* USER INCLUDE END */
/* Private define ----------------------------------------------------------- */
#define GM6020_FB_ID_BASE (0x205)
#define GM6020_CTRL_ID_BASE (0x1ff)
#define GM6020_CTRL_ID_EXTAND (0x2ff)
#define M3508_M2006_FB_ID_BASE (0x201)
#define M3508_M2006_CTRL_ID_BASE (0x200)
#define M3508_M2006_CTRL_ID_EXTAND (0x1ff)
#define M3508_M2006_ID_SETTING_ID (0x700)
#define GM6020_MAX_ABS_LSB (30000)
#define M3508_MAX_ABS_LSB (16384)
#define M2006_MAX_ABS_LSB (10000)
#define MOTOR_TX_BUF_SIZE (8)
#define MOTOR_RX_BUF_SIZE (8)
#define MOTOR_ENC_RES (8192) /* 电机编码器分辨率 */
#define MOTOR_CUR_RES (16384) /* 电机转矩电流分辨率 */
/* USER DEFINE BEGIN */
/* USER DEFINE END */
/* Private macro ------------------------------------------------------------ */
/* Private typedef ---------------------------------------------------------- */
/* USER STRUCT BEGIN */
/* USER STRUCT END */
/* Private variables -------------------------------------------------------- */
static MOTOR_RM_CANManager_t *can_managers[BSP_CAN_NUM] = {NULL};
/* Private function -------------------------------------------------------- */
/* USER FUNCTION BEGIN */
/* USER FUNCTION END */
static int8_t MOTOR_RM_GetLogicalIndex(uint16_t can_id, MOTOR_RM_Module_t module) {
switch (module) {
case MOTOR_M2006:
case MOTOR_M3508:
if (can_id >= M3508_M2006_FB_ID_BASE && can_id < M3508_M2006_FB_ID_BASE + 7) {
return can_id - M3508_M2006_FB_ID_BASE;
}
break;
case MOTOR_GM6020:
if (can_id >= GM6020_FB_ID_BASE && can_id < GM6020_FB_ID_BASE + 6) {
return can_id - GM6020_FB_ID_BASE + 4;
}
break;
default:
break;
}
return DEVICE_ERR;
}
static float MOTOR_RM_GetRatio(MOTOR_RM_Module_t module) {
switch (module) {
case MOTOR_M2006: return 36.0f;
case MOTOR_M3508: return 3591.0f / 187.0f;
case MOTOR_GM6020: return 1.0f;
default: return 1.0f;
}
}
static int16_t MOTOR_RM_GetLSB(MOTOR_RM_Module_t module) {
switch (module) {
case MOTOR_M2006: return M2006_MAX_ABS_LSB;
case MOTOR_M3508: return M3508_MAX_ABS_LSB;
case MOTOR_GM6020: return GM6020_MAX_ABS_LSB;
default: return DEVICE_ERR;
}
}
static MOTOR_RM_CANManager_t* MOTOR_RM_GetCANManager(BSP_CAN_t can) {
if (can >= BSP_CAN_NUM) return NULL;
return can_managers[can];
}
static int8_t MOTOR_RM_CreateCANManager(BSP_CAN_t can) {
if (can >= BSP_CAN_NUM) return DEVICE_ERR;
if (can_managers[can] != NULL) return DEVICE_OK;
can_managers[can] = (MOTOR_RM_CANManager_t*)BSP_Malloc(sizeof(MOTOR_RM_CANManager_t));
if (can_managers[can] == NULL) return DEVICE_ERR;
memset(can_managers[can], 0, sizeof(MOTOR_RM_CANManager_t));
can_managers[can]->can = can;
return DEVICE_OK;
}
static void Motor_RM_Decode(MOTOR_RM_t *motor, BSP_CAN_Message_t *msg) {
uint16_t raw_angle = (uint16_t)((msg->data[0] << 8) | msg->data[1]);
int16_t raw_speed = (int16_t)((msg->data[2] << 8) | msg->data[3]);
int16_t raw_current = (int16_t)((msg->data[4] << 8) | msg->data[5]);
int16_t lsb = MOTOR_RM_GetLSB(motor->param.module);
float ratio = MOTOR_RM_GetRatio(motor->param.module);
float rotor_angle = raw_angle / (float)MOTOR_ENC_RES * M_2PI;
float rotor_speed = raw_speed;
float torque_current = raw_current * lsb / (float)MOTOR_CUR_RES;
if (motor->param.gear) {
// 多圈累加
int32_t delta = (int32_t)raw_angle - (int32_t)motor->last_raw_angle;
if (delta > (MOTOR_ENC_RES / 2)) {
motor->gearbox_round_count--;
} else if (delta < -(MOTOR_ENC_RES / 2)) {
motor->gearbox_round_count++;
}
motor->last_raw_angle = raw_angle;
float single_turn = rotor_angle;
motor->gearbox_total_angle = (motor->gearbox_round_count * M_2PI + single_turn) / ratio;
// 输出轴多圈绝对值
motor->feedback.rotor_abs_angle = motor->gearbox_total_angle;
motor->feedback.rotor_speed = rotor_speed / ratio;
motor->feedback.torque_current = torque_current * ratio;
} else {
// 非gear模式直接用转子单圈
motor->gearbox_round_count = 0;
motor->last_raw_angle = raw_angle;
motor->gearbox_total_angle = 0.0f;
motor->feedback.rotor_abs_angle = rotor_angle;
motor->feedback.rotor_speed = rotor_speed;
motor->feedback.torque_current = torque_current;
}
while (motor->feedback.rotor_abs_angle < 0) {
motor->feedback.rotor_abs_angle += M_2PI;
}
while (motor->feedback.rotor_abs_angle >= M_2PI) {
motor->feedback.rotor_abs_angle -= M_2PI;
}
if (motor->motor.reverse) {
motor->feedback.rotor_abs_angle = M_2PI - motor->feedback.rotor_abs_angle;
motor->feedback.rotor_speed = -motor->feedback.rotor_speed;
motor->feedback.torque_current = -motor->feedback.torque_current;
}
motor->feedback.temp = msg->data[6];
}
/* Exported functions ------------------------------------------------------- */
int8_t MOTOR_RM_Register(MOTOR_RM_Param_t *param) {
if (param == NULL) return DEVICE_ERR_NULL;
if (MOTOR_RM_CreateCANManager(param->can) != DEVICE_OK) return DEVICE_ERR;
MOTOR_RM_CANManager_t *manager = MOTOR_RM_GetCANManager(param->can);
if (manager == NULL) return DEVICE_ERR;
// 检查是否已注册
for (int i = 0; i < manager->motor_count; i++) {
if (manager->motors[i] && manager->motors[i]->param.id == param->id) {
return DEVICE_ERR_INITED;
}
}
// 检查数量
if (manager->motor_count >= MOTOR_RM_MAX_MOTORS) return DEVICE_ERR;
// 创建新电机实例
MOTOR_RM_t *new_motor = (MOTOR_RM_t*)BSP_Malloc(sizeof(MOTOR_RM_t));
if (new_motor == NULL) return DEVICE_ERR;
memcpy(&new_motor->param, param, sizeof(MOTOR_RM_Param_t));
memset(&new_motor->motor, 0, sizeof(MOTOR_t));
new_motor->motor.reverse = param->reverse;
// 注册CAN接收ID
if (BSP_CAN_RegisterId(param->can, param->id, 3) != BSP_OK) {
BSP_Free(new_motor);
return DEVICE_ERR;
}
manager->motors[manager->motor_count] = new_motor;
manager->motor_count++;
return DEVICE_OK;
}
int8_t MOTOR_RM_Update(MOTOR_RM_Param_t *param) {
if (param == NULL) return DEVICE_ERR_NULL;
MOTOR_RM_CANManager_t *manager = MOTOR_RM_GetCANManager(param->can);
if (manager == NULL) return DEVICE_ERR_NO_DEV;
for (int i = 0; i < manager->motor_count; i++) {
MOTOR_RM_t *motor = manager->motors[i];
if (motor && motor->param.id == param->id) {
BSP_CAN_Message_t rx_msg;
if (BSP_CAN_GetMessage(param->can, param->id, &rx_msg, BSP_CAN_TIMEOUT_IMMEDIATE) != BSP_OK) {
uint64_t now_time = BSP_TIME_Get();
if (now_time - motor->motor.header.last_online_time > 1000) {
motor->motor.header.online = false;
return DEVICE_ERR_NO_DEV;
}
return DEVICE_ERR;
}
motor->motor.header.online = true;
motor->motor.header.last_online_time = BSP_TIME_Get();
Motor_RM_Decode(motor, &rx_msg);
motor->motor.feedback = motor->feedback;
return DEVICE_OK;
}
}
return DEVICE_ERR_NO_DEV;
}
int8_t MOTOR_RM_UpdateAll(void) {
int8_t ret = DEVICE_OK;
for (int can = 0; can < BSP_CAN_NUM; can++) {
MOTOR_RM_CANManager_t *manager = MOTOR_RM_GetCANManager((BSP_CAN_t)can);
if (manager == NULL) continue;
for (int i = 0; i < manager->motor_count; i++) {
MOTOR_RM_t *motor = manager->motors[i];
if (motor != NULL) {
if (MOTOR_RM_Update(&motor->param) != DEVICE_OK) {
ret = DEVICE_ERR;
}
}
}
}
return ret;
}
int8_t MOTOR_RM_SetOutput(MOTOR_RM_Param_t *param, float value) {
if (param == NULL) return DEVICE_ERR_NULL;
MOTOR_RM_CANManager_t *manager = MOTOR_RM_GetCANManager(param->can);
if (manager == NULL) return DEVICE_ERR_NO_DEV;
if (value > 1.0f) value = 1.0f;
if (value < -1.0f) value = -1.0f;
if (param->reverse){
value = -value;
}
MOTOR_RM_t *motor = MOTOR_RM_GetMotor(param);
if (motor == NULL) return DEVICE_ERR_NO_DEV;
int8_t logical_index = MOTOR_RM_GetLogicalIndex(param->id, param->module);
if (logical_index < 0) return DEVICE_ERR;
MOTOR_RM_MsgOutput_t *output_msg = &manager->output_msg;
int16_t output_value = (int16_t)(value * (float)MOTOR_RM_GetLSB(param->module));
output_msg->output[logical_index] = output_value;
return DEVICE_OK;
}
int8_t MOTOR_RM_Ctrl(MOTOR_RM_Param_t *param) {
if (param == NULL) return DEVICE_ERR_NULL;
MOTOR_RM_CANManager_t *manager = MOTOR_RM_GetCANManager(param->can);
if (manager == NULL) return DEVICE_ERR_NO_DEV;
MOTOR_RM_MsgOutput_t *output_msg = &manager->output_msg;
BSP_CAN_StdDataFrame_t tx_frame;
uint16_t id = param->id;
switch (id) {
case M3508_M2006_FB_ID_BASE:
case M3508_M2006_FB_ID_BASE+1:
case M3508_M2006_FB_ID_BASE+2:
case M3508_M2006_FB_ID_BASE+3:
tx_frame.id = M3508_M2006_CTRL_ID_BASE;
tx_frame.dlc = MOTOR_TX_BUF_SIZE;
for (int i = 0; i < 4; i++) {
tx_frame.data[i*2] = (uint8_t)((output_msg->output[i] >> 8) & 0xFF);
tx_frame.data[i*2+1] = (uint8_t)(output_msg->output[i] & 0xFF);
}
break;
case M3508_M2006_FB_ID_BASE+4:
case M3508_M2006_FB_ID_BASE+5:
case M3508_M2006_FB_ID_BASE+6:
case M3508_M2006_FB_ID_BASE+7:
tx_frame.id = M3508_M2006_CTRL_ID_EXTAND;
tx_frame.dlc = MOTOR_TX_BUF_SIZE;
for (int i = 4; i < 8; i++) {
tx_frame.data[(i-4)*2] = (uint8_t)((output_msg->output[i] >> 8) & 0xFF);
tx_frame.data[(i-4)*2+1] = (uint8_t)(output_msg->output[i] & 0xFF);
}
break;
case GM6020_FB_ID_BASE+4:
case GM6020_FB_ID_BASE+5:
case GM6020_FB_ID_BASE+6:
tx_frame.id = GM6020_CTRL_ID_EXTAND;
tx_frame.dlc = MOTOR_TX_BUF_SIZE;
for (int i = 8; i < 11; i++) {
tx_frame.data[(i-8)*2] = (uint8_t)((output_msg->output[i] >> 8) & 0xFF);
tx_frame.data[(i-8)*2+1] = (uint8_t)(output_msg->output[i] & 0xFF);
}
tx_frame.data[6] = 0;
tx_frame.data[7] = 0;
break;
default:
return DEVICE_ERR;
}
return BSP_CAN_TransmitStdDataFrame(param->can, &tx_frame) == BSP_OK ? DEVICE_OK : DEVICE_ERR;
}
MOTOR_RM_t* MOTOR_RM_GetMotor(MOTOR_RM_Param_t *param) {
if (param == NULL) return NULL;
MOTOR_RM_CANManager_t *manager = MOTOR_RM_GetCANManager(param->can);
if (manager == NULL) return NULL;
for (int i = 0; i < manager->motor_count; i++) {
MOTOR_RM_t *motor = manager->motors[i];
if (motor && motor->param.id == param->id) {
return motor;
}
}
return NULL;
}
int8_t MOTOR_RM_Relax(MOTOR_RM_Param_t *param) {
return MOTOR_RM_SetOutput(param, 0.0f);
}
int8_t MOTOR_RM_Offine(MOTOR_RM_Param_t *param) {
MOTOR_RM_t *motor = MOTOR_RM_GetMotor(param);
if (motor) {
motor->motor.header.online = false;
return DEVICE_OK;
}
return DEVICE_ERR_NO_DEV;
}

View File

@@ -0,0 +1,132 @@
#pragma once
#include "motor.h"
#ifdef __cplusplus
extern "C" {
#endif
/* Includes ----------------------------------------------------------------- */
#include "device/device.h"
#include "device/motor.h"
#include "bsp/can.h"
/* Exported constants ------------------------------------------------------- */
#define MOTOR_RM_MAX_MOTORS 11
/* Exported macro ----------------------------------------------------------- */
/* Exported types ----------------------------------------------------------- */
typedef enum {
MOTOR_M2006,
MOTOR_M3508,
MOTOR_GM6020,
} MOTOR_RM_Module_t;
/*一个can最多控制11个电机*/
typedef union {
int16_t output[MOTOR_RM_MAX_MOTORS];
struct {
int16_t m3508_m2006_id201;
int16_t m3508_m2006_id202;
int16_t m3508_m2006_id203;
int16_t m3508_m2006_id204;
int16_t m3508_m2006_gm6020_id205;
int16_t m3508_m2006_gm6020_id206;
int16_t m3508_m2006_gm6020_id207;
int16_t m3508_m2006_gm6020_id208;
int16_t gm6020_id209;
int16_t gm6020_id20A;
int16_t gm6020_id20B;
} named;
} MOTOR_RM_MsgOutput_t;
/*每个电机需要的参数*/
typedef struct {
BSP_CAN_t can;
uint16_t id;
MOTOR_RM_Module_t module;
bool reverse;
bool gear;
} MOTOR_RM_Param_t;
typedef MOTOR_Feedback_t MOTOR_RM_Feedback_t;
typedef struct {
MOTOR_RM_Param_t param;
MOTOR_RM_Feedback_t feedback;
MOTOR_t motor;
// 多圈相关变量仅gear模式下有效
uint16_t last_raw_angle;
int32_t gearbox_round_count;
float gearbox_total_angle;
} MOTOR_RM_t;
/*CAN管理器管理一个CAN总线上所有的电机*/
typedef struct {
BSP_CAN_t can;
MOTOR_RM_MsgOutput_t output_msg;
MOTOR_RM_t *motors[MOTOR_RM_MAX_MOTORS];
uint8_t motor_count;
} MOTOR_RM_CANManager_t;
/* Exported functions prototypes -------------------------------------------- */
/**
* @brief 注册一个RM电机
* @param param 电机参数
* @return
*/
int8_t MOTOR_RM_Register(MOTOR_RM_Param_t *param);
/**
* @brief 更新指定电机数据
* @param param 电机参数
* @return
*/
int8_t MOTOR_RM_Update(MOTOR_RM_Param_t *param);
/**
* @brief 设置一个电机的输出
* @param param 电机参数
* @param value 输出值,范围[-1.0, 1.0]
* @return
*/
int8_t MOTOR_RM_SetOutput(MOTOR_RM_Param_t *param, float value);
/**
* @brief 发送控制命令到电机注意一个CAN可以控制多个电机所以只需要发送一次即可
* @param param 电机参数
* @return
*/
int8_t MOTOR_RM_Ctrl(MOTOR_RM_Param_t *param);
/**
* @brief 获取指定电机的实例指针
* @param param 电机参数
* @return
*/
MOTOR_RM_t* MOTOR_RM_GetMotor(MOTOR_RM_Param_t *param);
/**
* @brief 使电机松弛设置输出为0
* @param param
* @return
*/
int8_t MOTOR_RM_Relax(MOTOR_RM_Param_t *param);
/**
* @brief 使电机离线设置在线状态为false
* @param param
* @return
*/
int8_t MOTOR_RM_Offine(MOTOR_RM_Param_t *param);
/**
* @brief
* @param
* @return
*/
int8_t MOTOR_RM_UpdateAll(void);
#ifdef __cplusplus
}
#endif

View File

@@ -0,0 +1,249 @@
/*
VESC电机驱动
*/
/* Includes ----------------------------------------------------------------- */
#include "motor_vesc.h"
#include <stdbool.h>
#include <string.h>
#include "bsp/can.h"
#include "bsp/mm.h"
#include "bsp/time.h"
#include "component/user_math.h"
/* Private define ----------------------------------------------------------- */
/* Private macro ------------------------------------------------------------ */
/* Private typedef ---------------------------------------------------------- */
/* Private variables -------------------------------------------------------- */
/**************************************
* 限幅函数
**************************************/
void assert_param_duty(float *duty){
// 如果 duty 是 -1.0 ~ 1.0,则最大值用 wtrcfg_VESC_COMMAND_DUTY_MAX / 100
float max_duty = wtrcfg_VESC_COMMAND_DUTY_MAX / 100.0f;
if (fabsf(*duty) > max_duty) {
*duty = (*duty > 0) ? max_duty : -max_duty;
}
}
void assert_param_current(float *current){
if( fabsf(*current) > wtrcfg_VESC_COMMAND_CURRENT_MAX )
*current = *current > 0 ? wtrcfg_VESC_COMMAND_CURRENT_MAX : - wtrcfg_VESC_COMMAND_CURRENT_MAX ;
}
void assert_param_rpm(float *rpm){
if( fabsf(*rpm) > wtrcfg_VESC_COMMAND_ERPM_MAX )
*rpm = *rpm > 0 ? wtrcfg_VESC_COMMAND_ERPM_MAX : - wtrcfg_VESC_COMMAND_ERPM_MAX ;
}
void assert_param_pos(float *pos){
if( fabsf(*pos) > wtrcfg_VESC_COMMAND_POS_MAX )
*pos = *pos > 0 ? wtrcfg_VESC_COMMAND_POS_MAX : - wtrcfg_VESC_COMMAND_POS_MAX ;
}
static VESC_CANManager_t *can_managers[BSP_CAN_NUM] = {NULL};
// 获取指定CAN总线的电机管理器指针
static VESC_CANManager_t* MOTOR_GetCANManager(BSP_CAN_t can) {
if (can >= BSP_CAN_NUM) return NULL;
return can_managers[can];
}
// 为指定CAN总线创建电机管理器
static int8_t MOTOR_CreateCANManager(BSP_CAN_t can) {
if (can >= BSP_CAN_NUM) return DEVICE_ERR;
if (can_managers[can] != NULL) return DEVICE_OK;
can_managers[can] = (VESC_CANManager_t*)BSP_Malloc(sizeof(VESC_CANManager_t));
if (can_managers[can] == NULL) return DEVICE_ERR;
memset(can_managers[can], 0, sizeof(VESC_CANManager_t));
can_managers[can]->can = can;
return DEVICE_OK;
}
// 解析CAN报文更新电机反馈信息
static void Motor_VESC_Decode(VESC_t *motor, BSP_CAN_Message_t *msg)
{
if (motor == NULL || msg == NULL) return;
motor->motor.feedback.rotor_speed =
((int32_t)msg->data[0] << 24) |
((int32_t)msg->data[1] << 16) |
((int32_t)msg->data[2] << 8) |
((int32_t)msg->data[3]);
// torque_current: 低 2 字节 (data[4], data[5])
int16_t raw_current = (int16_t)((msg->data[5] << 8) | msg->data[4]);
motor->motor.feedback.torque_current = raw_current / 1000.0f; // 从 0.1A -> A
// duty_cycle: 低 2 字节 (data[6], data[7])
int16_t raw_duty = (int16_t)((msg->data[7] << 8) | msg->data[6]);
//motor->motor.feedback.duty_cycle = raw_duty / 1000.0f; // 从千分之一 -> (-1.0 ~ 1.0)
}
/* Exported functions ------------------------------------------------------- */
// 注册一个新的电机实例到管理器
int8_t VESC_Register(VESC_Param_t *param) {
if (param == NULL) return DEVICE_ERR_NULL;
if (MOTOR_CreateCANManager(param->can) != DEVICE_OK) return DEVICE_ERR;
VESC_CANManager_t *manager = MOTOR_GetCANManager(param->can);
if (manager == NULL) return DEVICE_ERR;
// 检查是否已注册
for (int i = 0; i < manager->motor_count; i++) {
if (manager->motors[i] && manager->motors[i]->param.id == param->id) {
return DEVICE_ERR_INITED;
}
}
// 检查数量
if (manager->motor_count >= VESC_MAX_MOTORS) return DEVICE_ERR;
// 创建新电机实例
VESC_t *new_motor = (VESC_t*)BSP_Malloc(sizeof(VESC_t));
if (new_motor == NULL) return DEVICE_ERR;
memcpy(&new_motor->param, param, sizeof(VESC_Param_t));
memset(&new_motor->motor, 0, sizeof(MOTOR_t));
new_motor->motor.reverse = param->reverse;
// 注册CAN接收ID
if (BSP_CAN_RegisterId(param->can, param->id, 3) != BSP_OK) {
BSP_Free(new_motor);
return DEVICE_ERR;
}
manager->motors[manager->motor_count] = new_motor;
manager->motor_count++;
return DEVICE_OK;
}
// 更新指定电机的反馈数据(扩展帧方式)
int8_t VESC_Update(VESC_Param_t *param)
{
if (param == NULL) return DEVICE_ERR_NULL;
VESC_CANManager_t *manager = MOTOR_GetCANManager(param->can);
if (manager == NULL) return DEVICE_ERR_NO_DEV;
VESC_t *motor = NULL;
for (int i = 0; i < manager->motor_count; i++) {
if (manager->motors[i] && manager->motors[i]->param.id == param->id) {
motor = manager->motors[i];
break;
}
}
if (motor == NULL) return DEVICE_ERR_NO_DEV;
// 根据电机 ID 获取对应扩展帧 ID
uint32_t ext_id = 0;
switch (param->id) {
case VESC_1: ext_id = CAN_VESC5065_M1_MSG1; break;
case VESC_2: ext_id = CAN_VESC5065_M2_MSG1; break;
case VESC_4: ext_id = CAN_VESC5065_M3_MSG1; break;
default: return DEVICE_ERR_NO_DEV;
}
BSP_CAN_Message_t rx_msg;
if (BSP_CAN_GetMessage(param->can, ext_id, &rx_msg, BSP_CAN_TIMEOUT_IMMEDIATE) != BSP_OK) {
uint64_t now_time = BSP_TIME_Get();
if (now_time - motor->motor.header.last_online_time > 1000) {
motor->motor.header.online = false;
return DEVICE_ERR_NO_DEV;
}
return DEVICE_ERR;
}
motor->motor.header.online = true;
motor->motor.header.last_online_time = BSP_TIME_Get();
Motor_VESC_Decode(motor, &rx_msg);
return DEVICE_OK;
}
// 更新所有CAN总线下所有电机的反馈数据
int8_t VESC_UpdateAll(void) {
int8_t ret = DEVICE_OK;
for (int can = 0; can < BSP_CAN_NUM; can++) {
VESC_CANManager_t *manager = MOTOR_GetCANManager((BSP_CAN_t)can);
if (manager == NULL) continue;
for (int i = 0; i < manager->motor_count; i++) {
VESC_t *motor = manager->motors[i];
if (motor != NULL) {
if (VESC_Update(&motor->param) != DEVICE_OK) {
ret = DEVICE_ERR;
}
}
}
}
return ret;
}
// 获取指定参数对应的电机实例指针
VESC_t* VESC_GetMotor(VESC_Param_t *param) {
if (param == NULL) return NULL;
VESC_CANManager_t *manager = MOTOR_GetCANManager(param->can);
if (manager == NULL) return NULL;
for (int i = 0; i < manager->motor_count; i++) {
VESC_t *motor = manager->motors[i];
if (motor && motor->param.id == param->id) {
return motor;
}
}
return NULL;
}
// 设置指定电机的输出值
int8_t VESC_SetOutput(VESC_Param_t *param, float value)
{
if (param == NULL) return DEVICE_ERR_NULL;
BSP_CAN_StdDataFrame_t tx_frame;
uint16_t command_id;
if (param->reverse) {
value = -value;
}
switch (param->mode)
{
case DUTY_CONTROL: {
assert_param_duty(&value); // 调用你现有的限幅函数
command_id = CAN_PACKET_SET_DUTY;
int32_t duty_val = (int32_t)(value * 1e5f); // duty 放大 1e5
memcpy(&tx_frame.data[0], &duty_val, 4);
tx_frame.dlc = 4;
break;
}
case RPM_CONTROL: {
assert_param_rpm(&value);
command_id = CAN_PACKET_SET_RPM;
int32_t rpm_val = (int32_t)value;
memcpy(&tx_frame.data[0], &rpm_val, 4);
tx_frame.dlc = 4;
break;
}
case CURRENT_CONTROL: {
assert_param_current(&value);
command_id = CAN_PACKET_SET_CURRENT;
int32_t cur_val = (int32_t)(value * 1e3f); // A -> mA 0-50A
memcpy(&tx_frame.data[0], &cur_val, 4);
tx_frame.dlc = 4;
break;
}
case POSITION_CONTROL: {
assert_param_pos(&value);
command_id = CAN_PACKET_SET_POS;
memcpy(&tx_frame.data[0], &value, 4);
tx_frame.dlc = 4;
break;
}
default:
return DEVICE_ERR;
}
tx_frame.id = (param->id << 5) | command_id;
return BSP_CAN_TransmitStdDataFrame(param->can, &tx_frame) == BSP_OK ? DEVICE_OK : DEVICE_ERR;
}
int8_t VESC_Relax(VESC_Param_t *param) {
return VESC_SetOutput(param, 0.0f);
}
int8_t VESC_Offine(VESC_Param_t *param) {
VESC_t *motor = VESC_GetMotor(param);
if (motor) {
motor->motor.header.online = false;
return DEVICE_OK;
}
return DEVICE_ERR_NO_DEV;
}

View File

@@ -0,0 +1,146 @@
#pragma once
#ifdef __cplusplus
extern "C"
{
#endif
/* Includes ----------------------------------------------------------------- */
#include "device/device.h"
#include "device/motor.h"
#include "bsp/can.h"
/* Private define ----------------------------------------------------------- */
#define wtrcfg_VESC_COMMAND_DUTY_MAX 100
#define wtrcfg_VESC_COMMAND_CURRENT_MAX 10
#define wtrcfg_VESC_COMMAND_POS_MAX 360
#define wtrcfg_VESC_COMMAND_ERPM_MAX 35000
#define wtrcfg_VESC_UART_TIMEOUT 0xff
// VESC数量根据实际情况调整
#define VESC_MAX_MOTORS 4
/* Exported constants ------------------------------------------------------- */
/* Exported macro ----------------------------------------------------------- */
/* Exported types ----------------------------------------------------------- */
typedef enum
{
VESC_1 = 1,
VESC_2 = 2,
VESC_3 = 3,
VESC_4 = 4,
CAN_VESC5065_M1_MSG1 = 0x901, // vesc的数据回传使用了扩展id[0:7]为驱动器id[8:15]为帧类型
CAN_VESC5065_M2_MSG1 = 0x902,
CAN_VESC5065_M3_MSG1 = 0x903,
CAN_VESC5065_M4_MSG1 = 0x904,
}VESC_ID;
typedef enum
{
CAN_PACKET_SET_DUTY = 0,
CAN_PACKET_SET_CURRENT = 1,
CAN_PACKET_SET_CURRENT_BRAKE = 2,
CAN_PACKET_SET_RPM = 3,
CAN_PACKET_SET_POS = 4,
CAN_PACKET_FILL_RX_BUFFER = 5,
CAN_PACKET_FILL_RX_BUFFER_LONG = 6,
CAN_PACKET_PROCESS_RX_BUFFER = 7,
CAN_PACKET_PROCESS_SHORT_BUFFER = 8,
CAN_PACKET_STATUS = 9,
CAN_PACKET_SET_CURRENT_REL = 10,
CAN_PACKET_SET_CURRENT_BRAKE_REL = 11,
CAN_PACKET_SET_CURRENT_HANDBRAKE = 12,
CAN_PACKET_SET_CURRENT_HANDBRAKE_REL = 13
} CAN_PACKET_ID;
// Control Modes
typedef enum
{
DUTY_CONTROL = 0x0,
RPM_CONTROL = 0x1,
CURRENT_CONTROL = 0x2,
POSITION_CONTROL = 0x3
} Control_Mode;
/*每个电机需要的参数*/
typedef struct
{
BSP_CAN_t can;
uint16_t id;
uint16_t mode;
bool reverse;
} VESC_Param_t;
/*电机实例*/
typedef struct ODrive_t
{
VESC_Param_t param;
MOTOR_t motor;
} VESC_t;
/*CAN管理器管理一个CAN总线上所有的电机*/
typedef struct
{
BSP_CAN_t can;
VESC_t *motors[VESC_MAX_MOTORS];
uint8_t motor_count;
} VESC_CANManager_t;
/* Exported functions prototypes -------------------------------------------- */
/**
* @brief 注册一个vesc电机
* @param param 电机参数
* @return
*/
int8_t VESC_Register(VESC_Param_t *param);
/**
* @brief 更新指定电机数据
* @param param 电机参数
* @return
*/
int8_t VESC_Update(VESC_Param_t *param);
/**
* @brief 更新所有电机数据
* @return
*/
int8_t VESC_UpdateAll(void);
/**
* @brief 设置一个电机的输出
* @param param 电机参数
* @param value 输出值
* @return
*/
int8_t VESC_SetOutput(VESC_Param_t *param, float value);
/**
* @brief 获取指定电机的实例指针
* @param param 电机参数
* @return
*/
VESC_t* VESC_GetMotor(VESC_Param_t *param);
/**
* @brief 使电机松弛设置输出为0
* @param param
* @return
*/
int8_t VESC_Relax(VESC_Param_t *param);
/**
* @brief 使电机离线设置在线状态为false
* @param param
* @return
*/
int8_t VESC_Offine(VESC_Param_t *param);
#ifdef __cplusplus
}
#endif

View File

@@ -0,0 +1,58 @@
/*
ACTION全场定位码盘ops9
*/
/* Includes ----------------------------------------------------------------- */
#include "device/ops9.h"
#include <string.h>
#include "bsp/uart.h"
#include "bsp/time.h"
/* Private define ----------------------------------------------------------- */
/* Private macro ------------------------------------------------------------ */
/* Private typedef ---------------------------------------------------------- */
/* Private variables -------------------------------------------------------- */
static osThreadId_t thread_alert;
static bool inited = false;
/* Private function -------------------------------------------------------- */
static void OPS9_RxCpltCallback(void) {
osThreadFlagsSet(thread_alert, SIGNAL_OPS9_RAW_REDY);
}
/* Exported functions ------------------------------------------------------- */
int8_t OPS9_init(OPS9_t *ops9) {
if (ops9 == NULL) return DEVICE_ERR_NULL;
if (inited) return DEVICE_ERR_INITED;
if ((thread_alert = osThreadGetId()) == NULL) return DEVICE_ERR_NULL;
BSP_UART_RegisterCallback(BSP_UART_OPS9, BSP_UART_RX_CPLT_CB,
OPS9_RxCpltCallback);
inited = true;
return DEVICE_OK;
}
int8_t OPS9_Restart(void) {
__HAL_UART_DISABLE(BSP_UART_GetHandle(BSP_UART_OPS9));
__HAL_UART_ENABLE(BSP_UART_GetHandle(BSP_UART_OPS9));
return DEVICE_OK;
}
int8_t OPS9_StartDmaRecv(OPS9_t *ops9) {
if (HAL_UART_Receive_DMA(BSP_UART_GetHandle(BSP_UART_OPS9),
(uint8_t *)&(ops9->data),
sizeof(ops9->data)) == HAL_OK)
return DEVICE_OK;
return DEVICE_ERR;
}
bool OPS9_WaitDmaCplt(uint32_t timeout) {
return (osThreadFlagsWait(SIGNAL_OPS9_RAW_REDY, osFlagsWaitAll, timeout) ==
SIGNAL_OPS9_RAW_REDY);
}

View File

@@ -0,0 +1,49 @@
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
/* Includes ----------------------------------------------------------------- */
#include <cmsis_os2.h>
#include <stdint.h>
#include "component/user_math.h"
#include "device/device.h"
/* Exported constants ------------------------------------------------------- */
#define OPS9_HEADER 0x0D0A
#define OPS9_TAIL 0x0A0D
/* Exported macro ----------------------------------------------------------- */
/* Exported types ----------------------------------------------------------- */
// 数据包结构体
typedef struct __packed {
uint16_t header; // 2字节
float yaw; // 4字节
float pitch; // 4字节
float roll; // 4字节
float x; // 4字节
float y; // 4字节
float angular_velocity; // 4字节
uint16_t tail; // 2字节
} OPS9_Data_t; // 共28字节
typedef struct {
DEVICE_Header_t header; // 设备头
OPS9_Data_t data; // 存储接收到的数据
} OPS9_t;
/* Exported functions prototypes -------------------------------------------- */
int8_t OPS9_init(OPS9_t *ops9);
int8_t OPS9_Restart(void);
int8_t OPS9_StartDmaRecv(OPS9_t *ops9);
bool OPS9_WaitDmaCplt(uint32_t timeout);
#ifdef __cplusplus
}
#endif

View File

@@ -0,0 +1,328 @@
/* Includes ----------------------------------------------------------------- */
#include "device/rc_can.h"
#include "bsp/time.h"
#include "device/device.h"
/* USER INCLUDE BEGIN */
/* USER INCLUDE END */
/* Private constants -------------------------------------------------------- */
/* USER DEFINE BEGIN */
/* USER DEFINE END */
/* Private macro ------------------------------------------------------------ */
/* Private types ------------------------------------------------------------ */
/* Private variables -------------------------------------------------------- */
/* USER VARIABLE BEGIN */
/* USER VARIABLE END */
/* USER FUNCTION BEGIN */
/* USER FUNCTION END */
/* Private function prototypes ---------------------------------------------- */
static int8_t RC_CAN_ValidateParams(const RC_CAN_Param_t *param);
static int8_t RC_CAN_RegisterIds(RC_CAN_t *rc_can);
/* Exported functions ------------------------------------------------------- */
/**
* @brief 初始化RC CAN发送模块
* @param rc_can RC_CAN结构体指针
* @param param 初始化参数
* @return DEVICE_OK 成功,其他值失败
*/
int8_t RC_CAN_Init(RC_CAN_t *rc_can, RC_CAN_Param_t *param) {
if (rc_can == NULL || param == NULL) {
return DEVICE_ERR_NULL;
}
// 参数验证
if (RC_CAN_ValidateParams(param) != DEVICE_OK) {
return DEVICE_ERR;
}
rc_can->param = *param;
// 初始化header
rc_can->header.online = false;
rc_can->header.last_online_time = 0;
// 手动初始化数据结构
rc_can->data.joy.ch_l_x = 0.0f;
rc_can->data.joy.ch_l_y = 0.0f;
rc_can->data.joy.ch_r_x = 0.0f;
rc_can->data.joy.ch_r_y = 0.0f;
rc_can->data.sw.sw_l = RC_CAN_SW_ERR;
rc_can->data.sw.sw_r = RC_CAN_SW_ERR;
rc_can->data.sw.ch_res = 0.0f;
rc_can->data.mouse.x = 0.0f;
rc_can->data.mouse.y = 0.0f;
rc_can->data.mouse.z = 0.0f;
rc_can->data.mouse.mouse_l = false;
rc_can->data.mouse.mouse_r = false;
rc_can->data.keyboard.key_value = 0;
for (int i = 0; i < 6; i++) {
rc_can->data.keyboard.keys[i] = RC_CAN_KEY_NONE;
}
// 注册CAN ID队列从机模式需要接收数据
if (rc_can->param.mode == RC_CAN_MODE_SLAVE) {
return RC_CAN_RegisterIds(rc_can);
}
return DEVICE_OK;
}
/**
* @brief 更新并发送数据到CAN总线
* @param rc_can RC_CAN结构体指针
* @param data_type 数据类型
* @return DEVICE_OK 成功,其他值失败
*/
int8_t RC_CAN_SendData(RC_CAN_t *rc_can, RC_CAN_DataType_t data_type) {
if (rc_can == NULL) {
return DEVICE_ERR_NULL;
}
if (rc_can->param.mode != RC_CAN_MODE_MASTER) {
return DEVICE_ERR;
}
BSP_CAN_StdDataFrame_t frame;
frame.dlc = 8;
// 边界裁剪宏
#define RC_CAN_CLAMP(x, min, max) ((x) < (min) ? (min) : ((x) > (max) ? (max) : (x)))
switch (data_type) {
case RC_CAN_DATA_JOYSTICK: {
frame.id = rc_can->param.joy_id;
float l_x = RC_CAN_CLAMP(rc_can->data.joy.ch_l_x, -0.999969f, 0.999969f);
float l_y = RC_CAN_CLAMP(rc_can->data.joy.ch_l_y, -0.999969f, 0.999969f);
float r_x = RC_CAN_CLAMP(rc_can->data.joy.ch_r_x, -0.999969f, 0.999969f);
float r_y = RC_CAN_CLAMP(rc_can->data.joy.ch_r_y, -0.999969f, 0.999969f);
int16_t l_x_i = (int16_t)(l_x * 32768.0f);
int16_t l_y_i = (int16_t)(l_y * 32768.0f);
int16_t r_x_i = (int16_t)(r_x * 32768.0f);
int16_t r_y_i = (int16_t)(r_y * 32768.0f);
frame.data[0] = (uint8_t)(l_x_i & 0xFF);
frame.data[1] = (uint8_t)((l_x_i >> 8) & 0xFF);
frame.data[2] = (uint8_t)(l_y_i & 0xFF);
frame.data[3] = (uint8_t)((l_y_i >> 8) & 0xFF);
frame.data[4] = (uint8_t)(r_x_i & 0xFF);
frame.data[5] = (uint8_t)((r_x_i >> 8) & 0xFF);
frame.data[6] = (uint8_t)(r_y_i & 0xFF);
frame.data[7] = (uint8_t)((r_y_i >> 8) & 0xFF);
break;
}
case RC_CAN_DATA_SWITCH: {
frame.id = rc_can->param.sw_id;
frame.data[0] = (uint8_t)(rc_can->data.sw.sw_l);
frame.data[1] = (uint8_t)(rc_can->data.sw.sw_r);
float ch_res = RC_CAN_CLAMP(rc_can->data.sw.ch_res, -0.999969f, 0.999969f);
int16_t ch_res_i = (int16_t)(ch_res * 32768.0f);
frame.data[2] = (uint8_t)(ch_res_i & 0xFF);
frame.data[3] = (uint8_t)((ch_res_i >> 8) & 0xFF);
frame.data[4] = 0; // 保留字节
frame.data[5] = 0; // 保留字节
frame.data[6] = 0; // 保留字节
frame.data[7] = 0; // 保留字节
break;
}
case RC_CAN_DATA_MOUSE: {
frame.id = rc_can->param.mouse_id;
// 鼠标x/y/z一般为增量若有极限也可加clamp
int16_t x = (int16_t)(rc_can->data.mouse.x);
int16_t y = (int16_t)(rc_can->data.mouse.y);
int16_t z = (int16_t)(rc_can->data.mouse.z);
frame.data[0] = (uint8_t)(x & 0xFF);
frame.data[1] = (uint8_t)((x >> 8) & 0xFF);
frame.data[2] = (uint8_t)(y & 0xFF);
frame.data[3] = (uint8_t)((y >> 8) & 0xFF);
frame.data[4] = (uint8_t)(z & 0xFF);
frame.data[5] = (uint8_t)((z >> 8) & 0xFF);
frame.data[6] = (uint8_t)(rc_can->data.mouse.mouse_l ? 1 : 0);
frame.data[7] = (uint8_t)(rc_can->data.mouse.mouse_r ? 1 : 0);
break;
}
case RC_CAN_DATA_KEYBOARD: {
frame.id = rc_can->param.keyboard_id;
frame.data[0] = (uint8_t)(rc_can->data.keyboard.key_value & 0xFF);
frame.data[1] = (uint8_t)((rc_can->data.keyboard.key_value >> 8) & 0xFF);
for (int i = 0; i < 6; i++) {
frame.data[2 + i] = (i < 6) ? (uint8_t)(rc_can->data.keyboard.keys[i]) : 0;
}
break;
}
default:
return DEVICE_ERR;
}
#undef RC_CAN_CLAMP
if (BSP_CAN_Transmit(rc_can->param.can, BSP_CAN_FORMAT_STD_DATA, frame.id,
frame.data, frame.dlc) != BSP_OK) {
return DEVICE_ERR;
}
return DEVICE_OK;
}
/**
* @brief 接收并更新CAN数据
* @param rc_can RC_CAN结构体指针
* @param data_type 数据类型
* @return DEVICE_OK 成功,其他值失败
*/
int8_t RC_CAN_Update(RC_CAN_t *rc_can, RC_CAN_DataType_t data_type) {
if (rc_can == NULL) {
return DEVICE_ERR_NULL;
}
// 只有从机模式才能接收数据
if (rc_can->param.mode != RC_CAN_MODE_SLAVE) {
return DEVICE_ERR;
}
BSP_CAN_Message_t msg;
switch (data_type) {
case RC_CAN_DATA_JOYSTICK:
if (BSP_CAN_GetMessage(rc_can->param.can, rc_can->param.joy_id, &msg,
BSP_CAN_TIMEOUT_IMMEDIATE) != BSP_OK) {
return DEVICE_ERR;
}
// 解包数据
int16_t ch_l_x = (int16_t)((msg.data[1] << 8) | msg.data[0]);
int16_t ch_l_y = (int16_t)((msg.data[3] << 8) | msg.data[2]);
int16_t ch_r_x = (int16_t)((msg.data[5] << 8) | msg.data[4]);
int16_t ch_r_y = (int16_t)((msg.data[7] << 8) | msg.data[6]);
// 转换为浮点数(范围:-1.0到1.0
rc_can->data.joy.ch_l_x = (float)ch_l_x / 32768.0f;
rc_can->data.joy.ch_l_y = (float)ch_l_y / 32768.0f;
rc_can->data.joy.ch_r_x = (float)ch_r_x / 32768.0f;
rc_can->data.joy.ch_r_y = (float)ch_r_y / 32768.0f;
break;
case RC_CAN_DATA_SWITCH:
if (BSP_CAN_GetMessage(rc_can->param.can, rc_can->param.sw_id, &msg,
BSP_CAN_TIMEOUT_IMMEDIATE) != BSP_OK) {
return DEVICE_ERR;
}
// 解包数据
rc_can->data.sw.sw_l = (RC_CAN_SW_t)msg.data[0];
rc_can->data.sw.sw_r = (RC_CAN_SW_t)msg.data[1];
int16_t ch_res = (int16_t)((msg.data[3] << 8) | msg.data[2]);
rc_can->data.sw.ch_res = (float)ch_res / 32768.0f;
break;
case RC_CAN_DATA_MOUSE:
if (BSP_CAN_GetMessage(rc_can->param.can, rc_can->param.mouse_id, &msg,
BSP_CAN_TIMEOUT_IMMEDIATE) != BSP_OK) {
return DEVICE_ERR;
}
// 解包数据
int16_t x = (int16_t)((msg.data[1] << 8) | msg.data[0]);
int16_t y = (int16_t)((msg.data[3] << 8) | msg.data[2]);
int16_t z = (int16_t)((msg.data[5] << 8) | msg.data[4]);
rc_can->data.mouse.x = (float)x;
rc_can->data.mouse.y = (float)y;
rc_can->data.mouse.z = (float)z;
rc_can->data.mouse.mouse_l = (msg.data[6] & 0x01) ? true : false;
rc_can->data.mouse.mouse_r = (msg.data[7] & 0x01) ? true : false;
break;
case RC_CAN_DATA_KEYBOARD:
if (BSP_CAN_GetMessage(rc_can->param.can, rc_can->param.keyboard_id, &msg,
BSP_CAN_TIMEOUT_IMMEDIATE) != BSP_OK) {
return DEVICE_ERR;
}
if (msg.dlc < 2) {
return DEVICE_ERR;
}
// 解包数据
rc_can->data.keyboard.key_value =
(uint16_t)((msg.data[1] << 8) | msg.data[0]);
for (int i = 0; i < 6 && (i + 2) < msg.dlc; i++) {
rc_can->data.keyboard.keys[i] = (RC_CAN_Key_t)(msg.data[2 + i]);
}
// 清空未使用的按键位置
for (int i = (msg.dlc > 2 ? msg.dlc - 2 : 0); i < 6; i++) {
rc_can->data.keyboard.keys[i] = RC_CAN_KEY_NONE;
}
break;
default:
return DEVICE_ERR;
}
// 更新header状态
rc_can->header.online = true;
rc_can->header.last_online_time = BSP_TIME_Get_us();
return DEVICE_OK;
}
/* Private functions -------------------------------------------------------- */
/**
* @brief 验证RC_CAN参数
* @param param 参数指针
* @return DEVICE_OK 成功,其他值失败
*/
static int8_t RC_CAN_ValidateParams(const RC_CAN_Param_t *param) {
if (param == NULL) {
return DEVICE_ERR_NULL;
}
// 检查CAN总线有效性
if (param->can >= BSP_CAN_NUM) {
return DEVICE_ERR;
}
// 检查工作模式有效性
if (param->mode != RC_CAN_MODE_MASTER && param->mode != RC_CAN_MODE_SLAVE) {
return DEVICE_ERR;
}
// 检查CAN ID是否重复
if (param->joy_id == param->sw_id || param->joy_id == param->mouse_id ||
param->joy_id == param->keyboard_id || param->sw_id == param->mouse_id ||
param->sw_id == param->keyboard_id ||
param->mouse_id == param->keyboard_id) {
return DEVICE_ERR;
}
return DEVICE_OK;
}
/**
* @brief 注册CAN ID
* @param rc_can RC_CAN结构体指针
* @return DEVICE_OK 成功,其他值失败
*/
static int8_t RC_CAN_RegisterIds(RC_CAN_t *rc_can) {
if (BSP_CAN_RegisterId(rc_can->param.can, rc_can->param.joy_id, 0) !=
BSP_OK) {
return DEVICE_ERR;
}
if (BSP_CAN_RegisterId(rc_can->param.can, rc_can->param.sw_id, 0) != BSP_OK) {
return DEVICE_ERR;
}
if (BSP_CAN_RegisterId(rc_can->param.can, rc_can->param.mouse_id, 0) !=
BSP_OK) {
return DEVICE_ERR;
}
if (BSP_CAN_RegisterId(rc_can->param.can, rc_can->param.keyboard_id, 0) !=
BSP_OK) {
return DEVICE_ERR;
}
return DEVICE_OK;
}
int8_t RC_CAN_OFFLINE(RC_CAN_t *rc_can){
if (rc_can == NULL) {
return DEVICE_ERR_NULL;
}
rc_can->header.online = false;
return DEVICE_OK;
}
/* USER CODE BEGIN */
/* USER CODE END */

View File

@@ -0,0 +1,157 @@
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
/* Includes ----------------------------------------------------------------- */
#include "bsp/can.h"
#include "device/device.h"
#include <stdint.h>
#include <stdbool.h>
/* USER INCLUDE BEGIN */
/* USER INCLUDE END */
/* USER DEFINE BEGIN */
/* USER DEFINE END */
/* Exported constants ------------------------------------------------------- */
/* Exported macro ----------------------------------------------------------- */
/* Exported types ----------------------------------------------------------- */
typedef enum {
RC_CAN_SW_ERR = 0,
RC_CAN_SW_UP = 1,
RC_CAN_SW_MID = 3,
RC_CAN_SW_DOWN = 2,
} RC_CAN_SW_t;
typedef enum {
RC_CAN_MODE_MASTER = 0, // 主机模式
RC_CAN_MODE_SLAVE = 1, // 从机模式
} RC_CAN_Mode_t;
typedef enum {
RC_CAN_DATA_JOYSTICK = 0,
RC_CAN_DATA_SWITCH,
RC_CAN_DATA_MOUSE,
RC_CAN_DATA_KEYBOARD
} RC_CAN_DataType_t;
typedef enum {
RC_CAN_KEY_NONE = 0xFF, // 无按键
RC_CAN_KEY_W = 0,
RC_CAN_KEY_S,
RC_CAN_KEY_A,
RC_CAN_KEY_D,
RC_CAN_KEY_SHIFT,
RC_CAN_KEY_CTRL,
RC_CAN_KEY_Q,
RC_CAN_KEY_E,
RC_CAN_KEY_R,
RC_CAN_KEY_F,
RC_CAN_KEY_G,
RC_CAN_KEY_Z,
RC_CAN_KEY_X,
RC_CAN_KEY_C,
RC_CAN_KEY_V,
RC_CAN_KEY_B,
RC_CAN_KEY_NUM,
} RC_CAN_Key_t;
// 遥杆数据包
typedef struct {
float ch_l_x;
float ch_l_y;
float ch_r_x;
float ch_r_y;
} RC_CAN_JoyData_t;
// 拨杆数据包
typedef struct {
RC_CAN_SW_t sw_l; // 左拨杆状态
RC_CAN_SW_t sw_r; // 右拨杆状态
float ch_res; // 第五通道
} RC_CAN_SwitchData_t;
// 鼠标数据包
typedef struct {
float x; // 鼠标X轴移动
float y; // 鼠标Y轴移动
float z; // 鼠标Z轴(滚轮)
bool mouse_l; // 鼠标左键
bool mouse_r; // 鼠标右键
} RC_CAN_MouseData_t;
// 键盘数据包
typedef struct {
uint16_t key_value; // 键盘按键位映射
RC_CAN_Key_t keys[16];
} RC_CAN_KeyboardData_t;
typedef struct {
RC_CAN_JoyData_t joy;
RC_CAN_SwitchData_t sw;
RC_CAN_MouseData_t mouse;
RC_CAN_KeyboardData_t keyboard;
} RC_CAN_Data_t;
// RC_CAN 参数结构
typedef struct {
BSP_CAN_t can; // 使用的CAN总线
RC_CAN_Mode_t mode; // 工作模式
uint16_t joy_id; // 遥杆CAN ID
uint16_t sw_id; // 拨杆CAN ID
uint16_t mouse_id; // 鼠标CAN ID
uint16_t keyboard_id; // 键盘CAN ID
} RC_CAN_Param_t;
// RC_CAN 主结构
typedef struct {
DEVICE_Header_t header;
RC_CAN_Param_t param;
RC_CAN_Data_t data;
} RC_CAN_t;
/* USER STRUCT BEGIN */
/* USER STRUCT END */
/* Exported functions prototypes -------------------------------------------- */
/**
* @brief 初始化RC CAN发送模块
* @param rc_can RC_CAN结构体指针
* @param param 初始化参数
* @return DEVICE_OK 成功,其他值失败
*/
int8_t RC_CAN_Init(RC_CAN_t *rc_can, RC_CAN_Param_t *param);
/**
* @brief 更新并发送数据到CAN总线
* @param rc_can RC_CAN结构体指针
* @param data_type 数据类型
* @return DEVICE_OK 成功,其他值失败
*/
int8_t RC_CAN_SendData(RC_CAN_t *rc_can, RC_CAN_DataType_t data_type);
/**
* @brief 接收并更新CAN数据
* @param rc_can RC_CAN结构体指针
* @param data_type 数据类型
* @return DEVICE_OK 成功,其他值失败
*/
int8_t RC_CAN_Update(RC_CAN_t *rc_can , RC_CAN_DataType_t data_type);
int8_t RC_CAN_OFFLINE(RC_CAN_t *rc_can);
/* USER FUNCTION BEGIN */
/* USER FUNCTION END */
#ifdef __cplusplus
}
#endif

View File

@@ -0,0 +1,47 @@
/*
pwm<77><6D><EFBFBD>ƶ<EFBFBD><C6B6>
*/
/*Includes -----------------------------------------*/
#include "bsp/pwm.h"
#include "servo.h"
/* USER INCLUDE BEGIN */
/* USER INCLUDE END */
#define SERVO_MIN_DUTY 0.025f
#define SERVO_MAX_DUTY 0.125f
/* USER DEFINE BEGIN */
/* USER DEFINE END */
/**
* @brief
* @param
* @retval BSP_OK / BSP_ERR
*/
int8_t SERVO_Init(SERVO_t *servo) {
if (servo == NULL) return BSP_ERR;
return BSP_PWM_Start(servo->pwm_ch);
}
int8_t SERVO_SetAngle(SERVO_t *servo, float angle) {
if (servo == NULL) return BSP_ERR;
/*<2A><><EFBFBD>ƽǶȷ<C7B6>Χ*/
if (angle < 0.0f) angle = 0.0f;
if (angle > 180.0f) angle = 180.0f;
/*<2A>Ƕ<EFBFBD>ӳ<EFBFBD>ռ<E4B5BD>ձ<EFBFBD>*/
float duty = servo->min_duty + (angle / 180.0f) * (servo->max_duty - servo->min_duty);
return BSP_PWM_Set(servo->pwm_ch, duty);
}
int8_t SERVO_Stop(SERVO_t *servo) {
if (servo == NULL) return BSP_ERR;
return BSP_PWM_Stop(servo->pwm_ch);
}

View File

@@ -0,0 +1,68 @@
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
/* Includes ----------------------------------------------------------------- */
#include <cmsis_os2.h>
#include "bsp/pwm.h"
/* USER INCLUDE BEGIN */
/* USER INCLUDE END */
/* USER DEFINE BEGIN */
/* USER DEFINE END */
/* Exported constants ------------------------------------------------------- */
/* Exported macro ----------------------------------------------------------- */
/* Exported types ----------------------------------------------------------- */
/**
* @brief
*/
typedef struct {
BSP_PWM_Channel_t pwm_ch;
float min_duty;
float max_duty;
} SERVO_t;
/* USER STRUCT BEGIN */
/* USER STRUCT END */
/**
* @brief
* @param servo
* @retval BSP_OK / BSP_ERR
*/
int8_t SERVO_Init(SERVO_t *servo);
/**
* @brief
* @param servo
* @param angle
* @retval BSP_OK / BSP_ERR
*/
int8_t SERVO_SetAngle(SERVO_t *servo, float angle);
/**
* @brief
* @param servo
* @retval BSP_OK / BSP_ERR
*/
int8_t SERVO_Stop(SERVO_t *servo);
/* USER FUNCTION BEGIN */
/* USER FUNCTION END */
#ifdef __cplusplus
}
#endif

View File

@@ -0,0 +1,106 @@
/* Includes ----------------------------------------------------------------- */
#include <stdio.h>
#include <string.h>
#include "device/vofa.h"
#include "bsp/uart.h"
/* USER INCLUDE BEGIN */
/* USER INCLUDE END */
/* Private define ----------------------------------------------------------- */
#define MAX_CHANNEL 64u // 根据实际最大通道数调整
#define JUSTFLOAT_TAIL 0x7F800000
/* USER DEFINE BEGIN */
/* USER DEFINE END */
/* Private macro ------------------------------------------------------------ */
/* Private typedef ---------------------------------------------------------- */
/* USER STRUCT BEGIN */
/* USER STRUCT END */
/* Private variables -------------------------------------------------------- */
static uint8_t vofa_tx_buf[sizeof(float) * MAX_CHANNEL + sizeof(uint32_t)];
static VOFA_Protocol_t current_protocol = VOFA_PROTOCOL_FIREWATER; // 默认协议
/* Private function -------------------------------------------------------- */
/* USER FUNCTION BEGIN */
/* USER FUNCTION END */
/************************ RawData *************************/
void VOFA_RawData_Send(const char* data, bool dma) {
BSP_UART_Transmit(BSP_UART_VOFA, (uint8_t*)data, strlen(data), dma);
}
/************************ FireWater *************************/
void VOFA_FireWater_Send(float *channels, uint8_t channel_count, bool dma)
{
if (channel_count == 0 || channel_count > MAX_CHANNEL)
return;
char *buf = (char *)vofa_tx_buf;
size_t len = 0;
for (uint8_t i = 0; i < channel_count; ++i) {
len += snprintf(buf + len,
sizeof(vofa_tx_buf) - len,
"%s%.2f",
(i ? "," : ""),
channels[i]);
}
snprintf(buf + len, sizeof(vofa_tx_buf) - len, "\n");
BSP_UART_Transmit(BSP_UART_VOFA, vofa_tx_buf, strlen(buf), dma);
}
/************************ JustFloat *************************/
void VOFA_JustFloat_Send(float *channels, uint8_t channel_count, bool dma)
{
if (channel_count == 0 || channel_count > MAX_CHANNEL)
return;
memcpy(vofa_tx_buf, channels, channel_count * sizeof(float));
uint32_t tail = JUSTFLOAT_TAIL; // 0x7F800000
memcpy(vofa_tx_buf + channel_count * sizeof(float), &tail, sizeof(tail));
BSP_UART_Transmit(BSP_UART_VOFA, vofa_tx_buf, channel_count * sizeof(float) + sizeof(tail), dma);
}
/* Exported functions ------------------------------------------------------- */
int8_t VOFA_init(VOFA_Protocol_t protocol) {
current_protocol = protocol;
return DEVICE_OK;
}
int8_t VOFA_Send(float* channels, uint8_t channel_count, bool dma) {
switch (current_protocol) {
case VOFA_PROTOCOL_RAWDATA:
{
char data[256];
if (channel_count >= 1) {
sprintf(data, "Channel1: %.2f", channels[0]);
if (channel_count >= 2) {
sprintf(data + strlen(data), ", Channel2: %.2f", channels[1]);
}
strcat(data, "\n");
VOFA_RawData_Send(data, dma);
}
}
break;
case VOFA_PROTOCOL_FIREWATER:
VOFA_FireWater_Send(channels, channel_count, dma);
break;
case VOFA_PROTOCOL_JUSTFLOAT:
VOFA_JustFloat_Send(channels, channel_count, dma);
break;
default:
return DEVICE_ERR;
}
return DEVICE_OK;
}

View File

@@ -0,0 +1,39 @@
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
/* Includes ----------------------------------------------------------------- */
#include "bsp/uart.h"
#include "device/device.h"
/* Exported constants ------------------------------------------------------- */
/* Exported macro ----------------------------------------------------------- */
/* Exported types ----------------------------------------------------------- */
/* Exported functions prototypes -------------------------------------------- */
typedef enum {
VOFA_PROTOCOL_RAWDATA,
VOFA_PROTOCOL_FIREWATER,
VOFA_PROTOCOL_JUSTFLOAT,
} VOFA_Protocol_t;
/**
* @brief 初始化VOFA设备
* @param protocol 设置通信协议
* @return
*/
int8_t VOFA_init(VOFA_Protocol_t protocol);
/**
* @brief 发送数据到VOFA
* @param channels 要发送的通道数据
* @param channel_count 通道数量
* @param dma 是否使用DMA发送
* @return
*/
int8_t VOFA_Send(float* channels, uint8_t channel_count, bool dma);
#ifdef __cplusplus
}
#endif

View File

@@ -0,0 +1,114 @@
/* Includes ----------------------------------------------------------------- */
#include "ws2812.h"
#include "device.h"
#include "bsp/pwm.h"
#include <stdlib.h>
/* USER INCLUDE BEGIN */
/* USER INCLUDE END */
/* Private define ----------------------------------------------------------- */
#define DEVICE_WS2812_T1H (uint16_t)(BSP_PWM_GetAutoReloadPreload(BSP_PWM_WS2812) * 0.56) // High-level width of logic-1 pulse
#define DEVICE_WS2812_T0H (BSP_PWM_GetAutoReloadPreload(BSP_PWM_WS2812) * 0.29) // High-level width of logic-0 pulse
#define DEVICE_WS2812_WS_REST 40 // Number of reset pulses (low level) after data stream
#define DEVICE_WS2812_DATA_LEN 24 // WS2812 data length: 24 bits (GRB) per LED
#define DEVICE_WS2812_RST_NUM 50 // Extra reset pulses reserved at the end of the buffer
/* USER DEFINE BEGIN */
/* USER DEFINE END */
/* Private macro ------------------------------------------------------------ */
/* Private typedef ---------------------------------------------------------- */
/* USER STRUCT BEGIN */
/* USER STRUCT END */
/* Private variables -------------------------------------------------------- */
static uint16_t DEVICE_WS2812_LED_NUM; // Total number of LEDs
static uint16_t *DEVICE_WS2812_RGB_Buff = NULL;// PWM duty buffer for DMA
/* Private function -------------------------------------------------------- */
/* USER FUNCTION BEGIN */
/* USER FUNCTION END */
/* Exported functions ------------------------------------------------------- */
/**
* Set color of a single WS2812 LED
* @param num LED index (1-based)
* @param R Red value (0-255)
* @param G Green value (0-255)
* @param B Blue value (0-255)
* @return DEVICE_OK on success, DEVICE_ERR if num is invalid
*/
uint8_t DEVICE_WS2812_Set(uint16_t num, uint8_t R, uint8_t G, uint8_t B)
{
if(num<1 || num>DEVICE_WS2812_LED_NUM) return DEVICE_ERR;
uint32_t indexx = (num-1) * DEVICE_WS2812_DATA_LEN;
/* WS2812 uses GRB order, MSB first */
for (uint8_t i = 0; i < 8; i++) {
// G
DEVICE_WS2812_RGB_Buff[indexx + i] = (G & (0x80 >> i)) ? DEVICE_WS2812_T1H : DEVICE_WS2812_T0H;
// R
DEVICE_WS2812_RGB_Buff[indexx + i + 8] = (R & (0x80 >> i)) ? DEVICE_WS2812_T1H : DEVICE_WS2812_T0H;
// B
DEVICE_WS2812_RGB_Buff[indexx + i + 16] = (B & (0x80 >> i)) ? DEVICE_WS2812_T1H : DEVICE_WS2812_T0H;
}
return DEVICE_OK;
}
/**
* Initialize WS2812 driver
* @param ledNum Number of LEDs in the strip
* @return DEVICE_OK on success, DEVICE_ERR if memory allocation or PWM setup fails
*/
uint8_t DEVICE_WS2812_Init(uint16_t ledNum)
{
DEVICE_WS2812_LED_NUM = ledNum;
if (DEVICE_WS2812_RGB_Buff != NULL)
{
free(DEVICE_WS2812_RGB_Buff);
DEVICE_WS2812_RGB_Buff = NULL;
}
/* Allocate new buffer: 24 PWM samples per LED + reset pulses */
size_t bufLen = ledNum * DEVICE_WS2812_DATA_LEN + DEVICE_WS2812_RST_NUM;
DEVICE_WS2812_RGB_Buff = (uint16_t *)malloc(bufLen * sizeof(uint16_t));
if (DEVICE_WS2812_RGB_Buff == NULL)
return DEVICE_ERR;
/* Initialize all LEDs to dim green */
for (int i = 1; i <= ledNum; i++)
DEVICE_WS2812_Set(i, 0, 20, 0);
/* Configure PWM frequency to 800 kHz and start DMA */
if (BSP_PWM_SetFreq(BSP_PWM_WS2812, 800000) == DEVICE_OK)
BSP_PWM_Start_DMA(
BSP_PWM_WS2812,
(uint32_t *)DEVICE_WS2812_RGB_Buff,
bufLen);
else
return DEVICE_ERR;
return DEVICE_OK;
}
/**
* De-initialize WS2812 driver
* Frees the DMA buffer and stops PWM
*/
void DEVICE_WS2812_DeInit()
{
for (int i = 1; i <= DEVICE_WS2812_LED_NUM; i++)
DEVICE_WS2812_Set(i, 0, 0, 0);
if (DEVICE_WS2812_RGB_Buff != NULL)
{
free(DEVICE_WS2812_RGB_Buff);
DEVICE_WS2812_RGB_Buff = NULL;
}
BSP_PWM_Stop_DMA(BSP_PWM_WS2812);
}

View File

@@ -0,0 +1,19 @@
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
/* Includes ----------------------------------------------------------------- */
#include <stdint.h>
/* Exported constants ------------------------------------------------------- */
/* Exported macro ----------------------------------------------------------- */
/* Exported types ----------------------------------------------------------- */
/* Exported functions prototypes -------------------------------------------- */
uint8_t DEVICE_WS2812_Init(uint16_t led_num);
uint8_t DEVICE_WS2812_Set(uint16_t num, uint8_t R, uint8_t G, uint8_t B);
void DEVICE_WS2812_DeInit();
#ifdef __cplusplus
}
#endif