c++
#ifndef ELECTRONBOT_FW_ROBOT_H
#define ELECTRONBOT_FW_ROBOT_H
#include "stm32f4xx.h"
#include "screen.h"
#include "i2c.h"
#define ANY 0
class Robot
{
public:
Robot(SPI_HandleTypeDef* _screenSpi, I2C_HandleTypeDef* _motorI2c) :
screenSpi(_screenSpi), motorI2c(_motorI2c)
{
lcd = new Screen(screenSpi);
/********* Need to adjust parameters for specific hardware *********/
joint[ANY] = JointStatus_t{
0,
-180,
180,
90
};
joint[1] = JointStatus_t{ // Head
2,
70,
95,
0,
-15,
15,
true
};
joint[2] = JointStatus_t{ // Left arm roll
4,
-10,
10,
0,
0,
30,
false
};
joint[3] = JointStatus_t{ // Left arm pitch
6,
-16,
117,
0,
-20,
180,
false
};
joint[4] = JointStatus_t{ // Right arm roll
8,
120,
140,
0,
0,
30,
true
};
joint[5] = JointStatus_t{ // Right arm pitch
10,
15,
150,
0,
-20,
180,
true
};
joint[6] = JointStatus_t{ // Body
12,
0,
180,
0,
-90,
90,
false
};
/********* Need to adjust parameters for specific hardware *********/
}
struct UsbBuffer_t
{
uint8_t extraDataTx[32]; //32字节的发送缓冲区
uint8_t rxData[2][60 * 240 * 3 + 32]; // 43232bytes, 43200 of which are lcd buffer,这是一个巨大的接收缓冲区,它有两条滑道。这就是经典的"乒乓缓冲 (Ping-Pong Buffer)"设计。240 * 240 是屏幕分辨率。* 3 代表每个像素是 RGB 三个字节。60 行,所以 60 * 240 * 3 正好是 1/4 屏的图像数据大小 (43200 字节)。+ 32 是用来存放伴随图像数据一起发来的指令数据(比如新的关节角度)。
/*业务逻辑:
滑道 0 正在被 USB 的 DMA("卸货机器人")写入新数据。
与此同时,CPU("仓库管理员")可以安全地从滑道 1 中读取上一批已经卸好的货物,而不用担心数据被中途修改。
当滑道 0 写满后,系统会切换,让 DMA 开始往滑道 1 写,CPU 则去处理滑道 0 的数据。
这种"一个在写,一个在读"的并行机制,极大地提高了数据处理的吞吐量和效率。*/
volatile uint16_t receivedPacketLen = 0; // 记录当前收到的这一包数据,到底有多少个字节。
volatile uint8_t pingPongIndex = 0; // 滑道切换开关
volatile uint32_t rxDataOffset = 0; // 记录在当前这条滑道上,货物已经卸到了哪个位置。相当于货物是大于一个滑道的
};
UsbBuffer_t usbBuffer;
struct JointStatus_t
{
uint8_t id;
float angleMin;
float angleMax;
float angle; // 当前角度,这个角度作者设计的是当前模型的角度
float modelAngelMin;
float modelAngelMax;
bool inverted = false; //反不反转
};
JointStatus_t joint[7];
//SwitchPingPongBuffer 和 GetPingPongBufferPtr 是 CDC_Receive_HS 回调函数使用的底层接口,负责"切换"和"准备"。
uint8_t* GetPingPongBufferPtr();// 获取当前空闲滑道的地址,返回当前应该用于接收数据的那个空闲缓冲区的内存地址
uint8_t* GetLcdBufferPtr();// 返回刚刚接收完成的那块、包含屏幕动画数据的缓冲区的内存地址。
uint8_t* GetExtraDataRxPtr();// 获取附加指令数据,返回刚刚接收完成的那块、包含关节角度等指令的缓冲区的内存地址。
void SwitchPingPongBuffer(); // 切换当前正在被 DMA 使用的接收缓冲区
void SendUsbPacket(uint8_t* _data, uint32_t _len); //这是一个封装,用来向上位机发送数据。
bool ReceiveUsbPacketUntilSizeIs(uint32_t _count); //等到指定的快递
void SetJointId(JointStatus_t &_joint, uint8_t _id);
void SetJointKp(JointStatus_t &_joint, float _value);
void SetJointKi(JointStatus_t &_joint, float _value);
void SetJointKv(JointStatus_t &_joint, float _value);
void SetJointKd(JointStatus_t &_joint, float _value);
void SetJointEnable(JointStatus_t &_joint, bool _enable);
void SetJointInitAngle(JointStatus_t &_joint, float _angle);
void SetJointTorqueLimit(JointStatus_t &_joint, float _percent);
//物理角度,直接和舵机进行通信
void UpdateServoAngle(JointStatus_t &_joint);
void UpdateServoAngle(JointStatus_t &_joint, float _angleSetPoint);
//模型角度,和上位机进行通信
void UpdateJointAngle(JointStatus_t &_joint);
void UpdateJointAngle(JointStatus_t &_joint, float _angleSetPoint);
void JointADCValueRead(JointStatus_t &_joint, uint8_t _id); // 就是远程命令舵机,让它把此刻测量到的、来自电位器的原始 ADC 数字值,报告上来。提供一个底层的硬件诊断接口,允许开发者远程查询并获取指定舵机内部、位置反馈电位器的原始 ADC 读数及其对应的电压值
//下面三个是测试舵机的函数
void JointPWMChnel1Test(JointStatus_t &_joint, uint8_t _id);
void JointPWMChnel2Test(JointStatus_t &_joint, uint8_t _id);
void JointPWMChnelTestClose(JointStatus_t &_joint, uint8_t _id);
//查询舵机版本
char* JointFamerWareVersionRead(JointStatus_t &_joint, uint8_t _id);
char* JointIdRead(Robot::JointStatus_t &_joint, uint8_t _id);
void JointSystemReset(JointStatus_t &_joint, uint8_t _id);
Screen* lcd;
private:
SPI_HandleTypeDef* screenSpi;
I2C_HandleTypeDef* motorI2c;
uint8_t i2cRxData[8];
uint8_t i2cTxData[8];
uint8_t usbExtraData[32];
bool TransmitAndReceiveI2cPacket(uint8_t _id);
};
#endif //ELECTRONBOT_FW_ROBOT_H
在这个robot类里面定义了一个结构体数组来表示机器人六个舵机的参数
