【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++:

作者:王哈哈、

物联沃分享整理
物联沃-IOTWORD物联网 » 【STM32】利用C++/C编写STM32代码

发表回复