【STM32】利用C++/C编写STM32代码
0、前言
C++编写STM32代码,如何进行?
将编译器改成6
1、与STM32Cubemx的冲突
这是我的博客,虽然编译没有问题,但是代码放到STM32单片机上面运行不起来。
说明解决方法不对。但是大师兄就是用这种方法来实现的。先这么做吧!
http://t.csdnimg.cn/4Nj0D
不得不说,tortoiseGit是个好东西!!我今天发布了教程,可以去看看。
2、让STM32代码支持现代C++特性
http://t.csdnimg.cn/ftYmW
如果后期,我32代码用到了现代C++特性,参考这一篇文章
3、C/C++混合编程
3.1 在C++中调用C函数
有些器件,商家提供的是api函数是C语言编写,这时候想偷懒,直接调用,不想用C++重新编写了。
想在.cpp中调用c函数,首先要在cpp.h头文件中包含c函数的.h文件,同时在c函数的.h文件加上
#ifdef __cplusplus
extern "C" {
#endif
// 主体代码......
#ifdef __cplusplus
}
#endif
必须要全部括进去,我当时只括了我所用的函数,结果报错。
举个GO电机例子:
cpp_GoMotor.h
#ifndef __CPP_GOMOTOR_H
#define __CPP_GOMOTOR_H
#include <stdint.h>
#include "stm32g4xx_hal.h" // stm32 hal
#include "stm32g4xx_hal_def.h"
#include "GO_M8010_6.h"
class Go_MotorData
{
public:
Go_MotorData();
float Speed; // speed
float Torque_Current; // 当前实际电机输出力矩
float Angle; // 当前电机位置(主控0点修正,电机关节还是以编码器0点为准)
unsigned char motor_id; //电机ID
unsigned char mode; // 0:空闲, 5:开环转动, 10:闭环FOC控制
int Temp; //温度
unsigned char MError; //错误码
float footForce; // 足端气压传感器数据 12bit (0-4095)
};
class GoMotor
{
public:
GoMotor();
void State_Init(uint8_t id);
void Set_Blank(int id);
void Set_Torque(float goal_Torque, int id);
void receive_feedback(uint32_t id, uint8_t *data_p);
uint8_t ID_;
Go_MotorData motor_data_;
private:
};
#endif
cpp_GoMotor.cpp
#include "cpp_GoMotor.h"
#include "stm32g4xx_hal_def.h" // stm32 hal
#include "usart.h"
#include "stdio.h"
#include "cmsis_os.h"
#define GO_DELAY_MS 1
Go_MotorData::Go_MotorData()
{
Speed = 0; // speed
Torque_Current = 0; // 当前实际电机输出力矩
Angle = 0; // 当前电机位置(主控0点修正,电机关节还是以编码器0点为准)
motor_id = 0; //电机ID
mode = 0; // 0:空闲, 5:开环转动, 10:闭环FOC控制
Temp = 0; //温度
MError = 0; //错误码
footForce = 0; // 足端气压传感器数据 12bit (0-4095)
}
GoMotor::GoMotor()
{
}
void GoMotor::State_Init(uint8_t id)
{
Go_State_init(id); // C函数
}
void GoMotor::Set_Blank(int id)
{
Set_Blank(id);
}
void GoMotor::Set_Torque(float goal_Torque, int id)
{
GO_Set_Torque(goal_Torque, id);
}
void GoMotor::receive_feedback(uint32_t id, uint8_t *data_p)
{
// 关于数组标号,在GO_M8010_6.c的void GO_UART_IRQHandler(UART_HandleTypeDef *huart)
motor_data_.motor_id = data_p[0];
motor_data_.Angle = data_p[6];
motor_data_.Speed = data_p[5];
motor_data_.Torque_Current = data_p[4];
motor_data_.Temp = data_p[2];
motor_data_.mode = data_p[1];
motor_data_.footForce = data_p[7];
motor_data_.MError = data_p[3];
}
GO_M8010_6.h
#ifndef __MOTOR_CONTORL_H
#define __MOTOR_CONTORL_H
#include <stdint.h>
#include "stm32g4xx_hal_def.h" // stm32 hal
#include <stdbool.h>
#ifdef __cplusplus
extern "C" {
#endif
#define SET_485_DE_UP() HAL_GPIO_WritePin(RS485_DE_GPIO_Port, RS485_DE_Pin, GPIO_PIN_SET) //定义
#define SET_485_DE_DOWN() HAL_GPIO_WritePin(RS485_DE_GPIO_Port, RS485_DE_Pin, GPIO_PIN_RESET)
#define SET_485_RE_UP() HAL_GPIO_WritePin(RS485_DE_GPIO_Port, RS485_DE_Pin, GPIO_PIN_SET)
#define SET_485_RE_DOWN() HAL_GPIO_WritePin(RS485_DE_GPIO_Port, RS485_DE_Pin, GPIO_PIN_RESET)
#define SET_4852_DE_UP() HAL_GPIO_WritePin(RS4852_DE_GPIO_Port, RS4852_DE_Pin, GPIO_PIN_SET) //定义
#define SET_4852_DE_DOWN() HAL_GPIO_WritePin(RS4852_DE_GPIO_Port, RS4852_DE_Pin, GPIO_PIN_RESET)
#define SET_4852_RE_UP() HAL_GPIO_WritePin(RS4852_DE_GPIO_Port, RS4852_DE_Pin, GPIO_PIN_SET)
#define SET_4852_RE_DOWN() HAL_GPIO_WritePin(RS4852_DE_GPIO_Port, RS4852_DE_Pin, GPIO_PIN_RESET)
typedef signed char int8_t;
typedef signed short int int16_t;
typedef signed int int32_t;
typedef signed long long int64_t;
/* exact-width unsigned integer types */
typedef unsigned char uint8_t;
typedef unsigned short int uint16_t;
typedef unsigned int uint32_t;
typedef unsigned long long uint64_t;
typedef unsigned char bool_t;
typedef float fp32;
typedef double fp64;
extern HAL_StatusTypeDef GoMotor_SendRecv_IsOk;
/**
* @brief Go-M8010-6关节电机驱动 精简通讯指令集
* @version 0.1
* @date 2022-03-04
* @copyright Copyright (c) unitree robotics .co.ltd. 2022
*/
#pragma pack(1)
/**
* @brief 电机模式控制信息
*
*/
typedef struct
{
uint8_t id : 4; // 电机ID: 0,1...,13,14 15表示向所有电机广播数据(此时无返回)
uint8_t status : 3; // 工作模式: 0.锁定 1.FOC闭环 2.编码器校准 3.保留
uint8_t none : 1; // 保留位
} RIS_Mode_t; // 控制模式 1Byte
/**
* @brief 电机状态控制信息
*
*/
typedef struct
{
int16_t tor_des; // 期望关节输出扭矩 unit: N.m (q8)
int16_t spd_des; // 期望关节输出速度 unit: rad/s (q8)
int32_t pos_des; // 期望关节输出位置 unit: rad (q15)
int16_t k_pos; // 期望关节刚度系数 unit: -1.0-1.0 (q15)
int16_t k_spd; // 期望关节阻尼系数 unit: -1.0-1.0 (q15)
} RIS_Comd_t; // 控制参数 12Byte
/**
* @brief 电机状态反馈信息
*
*/
typedef struct
{
int16_t torque; // 实际关节输出扭矩 unit: N.m (q8)
int16_t speed; // 实际关节输出速度 unit: rad/s (q8)
int32_t pos; // 实际关节输出位置 unit: rad (q15)
int8_t temp; // 电机温度: -128~127°C
uint8_t MError : 3; // 电机错误标识: 0.正常 1.过热 2.过流 3.过压 4.编码器故障 5-7.保留
uint16_t force : 12; // 足端气压传感器数据 12bit (0-4095)
uint8_t none : 1; // 保留位
} RIS_Fbk_t; // 状态数据 11Byte
/*
* Actuator Communication Reduced Instruction Set
* Unitree robotics (c) .Co.Ltd. 2022 All Rights Reserved.
*/
typedef union
{
int32_t L;
uint8_t u8[4];
uint16_t u16[2];
uint32_t u32;
float F;
} COMData32;
typedef struct
{
// 定义 数据包头
unsigned char start[2]; // 包头
unsigned char motorID; // 电机ID 0,1,2,3 ... 0xBB 表示向所有电机广播(此时无返回)
unsigned char reserved;
} COMHead;
typedef struct
{
// 以 4个字节一组排列 ,不然编译器会凑整
// 定义 数据
uint8_t mode; // 当前关节模式
uint8_t ReadBit; // 电机控制参数修改 是否成功位
int8_t Temp; // 电机当前平均温度
uint8_t MError; // 电机错误 标识
COMData32 Read; // 读取的当前 电机 的控制数据
int16_t T; // 当前实际电机输出力矩 7 + 8 描述
int16_t W; // 当前实际电机速度(高速) 8 + 7 描述
float LW; // 当前实际电机速度(低速)
int16_t W2; // 当前实际关节速度(高速) 8 + 7 描述
float LW2; // 当前实际关节速度(低速)
int16_t Acc; // 电机转子加速度 15+0 描述 惯量较小
int16_t OutAcc; // 输出轴加速度 12+3 描述 惯量较大
int32_t Pos; // 当前电机位置(主控0点修正,电机关节还是以编码器0点为准)
int32_t Pos2; // 关节编码器位置(输出编码器)
int16_t INS[3]; // 电机驱动板6轴传感器数据
int16_t acc[3];
// 力传感器的数据
int16_t Fgyro[3];
int16_t Facc[3];
int16_t Fmag[3];
uint8_t Ftemp; // 8位表示的温度 7位(-28~100度) 1位0.5度分辨率
int16_t Force16; // 力传感器高16位数据
int8_t Force8; // 力传感器低8位数据
uint8_t FError; // 足端传感器错误标识
int8_t Res[1]; // 通讯 保留字节
} ServoComdV3; // 加上数据包的包头 和CRC 78字节(4+70+4)
typedef struct
{
uint8_t head[2]; // 包头 2Byte
RIS_Mode_t mode; // 电机控制模式 1Byte
RIS_Fbk_t fbk; // 电机反馈数据 11Byte
uint16_t CRC16; // CRC 2Byte
} MotorData_t; //返回数据
typedef struct
{
uint8_t none[8]; // 保留
} LowHzMotorCmd;
typedef struct
{
// 以 4个字节一组排列 ,不然编译器会凑整
// 定义 数据
uint8_t mode; // 关节模式选择
uint8_t ModifyBit; // 电机控制参数修改位
uint8_t ReadBit; // 电机控制参数发送位
uint8_t reserved;
COMData32 Modify; // 电机参数修改 的数据
//实际给FOC的指令力矩为:
// K_P*delta_Pos + K_W*delta_W + T
int16_t T; // 期望关节的输出力矩(电机本身的力矩)x256, 7 + 8 描述
int16_t W; // 期望关节速度 (电机本身的速度) x128, 8 + 7描述
int32_t Pos; // 期望关节位置 x 16384/6.2832, 14位编码器(主控0点修正,电机关节还是以编码器0点为准)
int16_t K_P; // 关节刚度系数 x2048 4+11 描述
int16_t K_W; // 关节速度系数 x1024 5+10 描述
uint8_t LowHzMotorCmdIndex; // 保留
uint8_t LowHzMotorCmdByte; // 保留
COMData32 Res[1]; // 通讯 保留字节 用于实现别的一些通讯内容
} MasterComdV3; // 加上数据包的包头 和CRC 34字节
typedef struct
{
// 定义 电机控制命令数据包
uint8_t head[2]; // 包头 2Byte
RIS_Mode_t mode; // 电机控制模式 1Byte
RIS_Comd_t comd; // 电机期望数据 12Byte
uint16_t CRC16; // CRC 2Byte
} ControlData_t; //电机控制命令数据包
#pragma pack()
//go电机发送接收数据结构体变量
typedef struct
{
// 定义 发送格式化数据
ControlData_t motor_send_data; //电机控制数据结构体
int hex_len; //发送的16进制命令数组长度, 34
long long send_time; //发送该命令的时间, 微秒(us)
// 待发送的各项数据
unsigned short id; //电机ID,0代表全部电机
unsigned short mode; // 0:空闲, 5:开环转动, 10:闭环FOC控制
//实际给FOC的指令力矩为:
// K_P*delta_Pos + K_W*delta_W + T
float T; //期望关节的输出力矩(电机本身的力矩)(Nm)
float W; //期望关节速度(电机本身的速度)(rad/s)
float Pos; //期望关节位置(rad)
float K_P; //关节刚度系数
float K_W; //关节速度系数
COMData32 Res; // 通讯 保留字节 用于实现别的一些通讯内容
} MOTOR_send;
typedef struct
{
// 定义 接收数据
MotorData_t motor_recv_data; //电机接收数据结构体,详见motor_msg.h
int hex_len; //接收的16进制命令数组长度, 78
long long resv_time; //接收该命令的时间, 微秒(us)
int correct; //接收数据是否完整(1完整,0不完整)
//解读得出的电机数据
unsigned char motor_id; //电机ID
unsigned char mode; // 0:空闲, 5:开环转动, 10:闭环FOC控制
int Temp; //温度
unsigned char MError; //错误码
float T; // 当前实际电机输出力矩
float W; // speed
float Pos; // 当前电机位置(主控0点修正,电机关节还是以编码器0点为准)
float footForce; // 足端气压传感器数据 12bit (0-4095)
} MOTOR_recv;
typedef struct
{
uint32_t HAL_OK_count; //自检
fp32 Postemp; //当前
fp32 Posinit;
uint32_t error_times;//丢包率
fp32 ErrorRate;
float RxRate;
} Go_Check_;
typedef struct
{
HAL_StatusTypeDef GO_state[4]; //用来显示四个电机发送是否成功
Go_Check_ Pos_Check[4]; //位置速度信息
bool Check_Flag;
uint8_t dma_rx_flag;
int total_times;
int dma_rx_times[4];
} Communicate_state;
//crc
/*
* This mysterious table is just the CRC of each possible byte. It can be
* computed using the standard bit-at-a-time methods. The polynomial can
* be seen in entry 128, 0x8408. This corresponds to x^0 + x^5 + x^12.
* Add the implicit x^16, and you have the standard CRC-CCITT.
* https://github.com/torvalds/linux/blob/5bfc75d92efd494db37f5c4c173d3639d4772966/lib/crc-ccitt.c
*/
static uint16_t crc_ccitt_byte(uint16_t crc, const uint8_t c);
static uint16_t crc_ccitt(uint16_t crc, uint8_t const *buffer, size_t len);
uint32_t crc32_core(uint32_t *ptr, uint32_t len);
int modify_data(MOTOR_send *motor_s);
int extract_data(MOTOR_recv *motor_r);
extern MOTOR_recv rx_data[16];
HAL_StatusTypeDef SERVO_1_Send_recv(MOTOR_send *pData, MOTOR_recv *rData, uint8_t id);
HAL_StatusTypeDef SERVO_2_Send_recv(MOTOR_send *pData, MOTOR_recv *rData, uint8_t id);
HAL_StatusTypeDef Send_recv(MOTOR_send *pData, MOTOR_recv *rData, uint8_t id);
HAL_StatusTypeDef Go_callback_handle(MOTOR_recv *rData, uint16_t rxlen);
void Go_Callback_Check(uint8_t id, uint16_t rxlen);
extern Communicate_state GO_State;
void Go_State_init(uint8_t id);
void GO_Set_Blank(int id);
void GO_Set_Speed(int goal_speed, int id);
void GO_Set_Pos(float goal_pos, float goal_T, float goal_Kp, int id);
void GO_Set_Damp(float goal_damp, int id);
void GO_Set_Torque(float goal_Torque, int id);
void GO_Set_Zero_Torque(int id);
void GO_UART_IRQHandler(UART_HandleTypeDef *huart);
#ifdef __cplusplus
}
#endif
#endif
3.2 在C中调用C++函数
在关键C/C++接口文件里面,做以下处理:
在C中调用C++:
作者:王哈哈、