Compare commits
16 Commits
vesc_contr
...
master
Author | SHA1 | Date | |
---|---|---|---|
0734d65146 | |||
701243ad89 | |||
a74791e2aa | |||
496e2556f4 | |||
cb5939eefb | |||
caaa92eca9 | |||
9e256acdaa | |||
a331746ede | |||
1acdbfe14e | |||
2c6fb114f2 | |||
2e0f97d6dd | |||
964acb82f0 | |||
b6f0d6a185 | |||
8c6b432d7e | |||
dfa9e2178e | |||
c176bb40a2 |
31
CMakeLists.txt
Normal file
31
CMakeLists.txt
Normal file
@ -0,0 +1,31 @@
|
||||
set(headers
|
||||
bobbycar-can.h
|
||||
bobbycar-common.h
|
||||
bobbycar-serial.h
|
||||
)
|
||||
|
||||
set(sources
|
||||
)
|
||||
|
||||
set(dependencies
|
||||
)
|
||||
|
||||
idf_component_register(
|
||||
INCLUDE_DIRS
|
||||
.
|
||||
SRCS
|
||||
${headers}
|
||||
${sources}
|
||||
REQUIRES
|
||||
${dependencies}
|
||||
)
|
||||
|
||||
target_compile_options(${COMPONENT_TARGET}
|
||||
PRIVATE
|
||||
-fstack-reuse=all
|
||||
-fstack-protector-all
|
||||
-Wno-unused-function
|
||||
-Wno-deprecated-declarations
|
||||
-Wno-missing-field-initializers
|
||||
-Wno-parentheses
|
||||
)
|
294
bobbycar-can.h
Normal file
294
bobbycar-can.h
Normal file
@ -0,0 +1,294 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
namespace bobbycar {
|
||||
namespace protocol {
|
||||
namespace can {
|
||||
|
||||
enum : uint16_t { // vvv'
|
||||
DeviceTypeMotorController = 0b000'00000000,
|
||||
DeviceTypeBoardcomputer = 0b001'00000000
|
||||
};
|
||||
|
||||
template<bool isBack, bool isRight>
|
||||
class MotorController
|
||||
{
|
||||
private:
|
||||
enum : uint16_t { // ...v
|
||||
MotorControllerCommand = 0b00000000000, // send only TO the motor controller
|
||||
MotorControllerFeedback = 0b00010000000, // send only FROM the motor controller
|
||||
};
|
||||
|
||||
enum : uint16_t { // .........v
|
||||
MotorControllerFront = 0b00000000000,
|
||||
MotorControllerBack = 0b00000000010,
|
||||
};
|
||||
|
||||
enum : uint16_t { // ..........v
|
||||
MotorControllerLeft = 0b00000000000,
|
||||
MotorControllerRight = 0b00000000001,
|
||||
};
|
||||
|
||||
enum : uint16_t {
|
||||
MotorControllerMask =
|
||||
uint16_t(DeviceTypeMotorController) |
|
||||
uint16_t(isBack ? MotorControllerBack : MotorControllerFront) |
|
||||
uint16_t(isRight ? MotorControllerRight : MotorControllerLeft)
|
||||
};
|
||||
|
||||
public:
|
||||
MotorController() = delete;
|
||||
~MotorController() = delete;
|
||||
|
||||
// send only TO the motor controller
|
||||
class Command
|
||||
{
|
||||
private:
|
||||
enum : uint16_t {
|
||||
MotorControllerCommandMask =
|
||||
uint16_t(MotorControllerMask) |
|
||||
uint16_t(MotorControllerCommand)
|
||||
};
|
||||
|
||||
public:
|
||||
Command() = delete;
|
||||
~Command() = delete;
|
||||
|
||||
enum : uint16_t { // ....'vvvvv'..
|
||||
Enable = 0b0000'00000'00 | MotorControllerCommandMask,
|
||||
InpTgt = 0b0000'00001'00 | MotorControllerCommandMask,
|
||||
CtrlTyp = 0b0000'00010'00 | MotorControllerCommandMask,
|
||||
CtrlMod = 0b0000'00011'00 | MotorControllerCommandMask,
|
||||
IMotMax = 0b0000'00100'00 | MotorControllerCommandMask,
|
||||
IDcMax = 0b0000'00101'00 | MotorControllerCommandMask,
|
||||
NMotMax = 0b0000'00110'00 | MotorControllerCommandMask,
|
||||
FieldWeakMax = 0b0000'00111'00 | MotorControllerCommandMask,
|
||||
PhaseAdvMax = 0b0000'01000'00 | MotorControllerCommandMask,
|
||||
CruiseCtrlEna = 0b0000'01001'00 | MotorControllerCommandMask,
|
||||
CruiseMotTgt = 0b0000'01010'00 | MotorControllerCommandMask,
|
||||
BuzzerFreq = 0b0000'01011'00 | MotorControllerCommandMask,
|
||||
BuzzerPattern = 0b0000'01100'00 | MotorControllerCommandMask,
|
||||
Led = 0b0000'01101'00 | MotorControllerCommandMask,
|
||||
Poweroff = 0b0000'01110'00 | MotorControllerCommandMask
|
||||
};
|
||||
};
|
||||
|
||||
// send only FROM the motor controller
|
||||
class Feedback
|
||||
{
|
||||
private:
|
||||
enum : uint16_t {
|
||||
MotorControllerFeedbackMask =
|
||||
uint16_t(MotorControllerMask) |
|
||||
uint16_t(MotorControllerFeedback)
|
||||
};
|
||||
|
||||
public:
|
||||
Feedback() = delete;
|
||||
~Feedback() = delete;
|
||||
|
||||
enum : uint16_t { // ....'vvvvv'..
|
||||
DcLink = 0b0000'00000'00 | MotorControllerFeedbackMask,
|
||||
Speed = 0b0000'00001'00 | MotorControllerFeedbackMask,
|
||||
Error = 0b0000'00010'00 | MotorControllerFeedbackMask,
|
||||
Angle = 0b0000'00011'00 | MotorControllerFeedbackMask,
|
||||
DcPhaA = 0b0000'00100'00 | MotorControllerFeedbackMask, // unused
|
||||
DcPhaB = 0b0000'00101'00 | MotorControllerFeedbackMask, // unused
|
||||
DcPhaC = 0b0000'00110'00 | MotorControllerFeedbackMask, // unused
|
||||
Chops = 0b0000'00111'00 | MotorControllerFeedbackMask,
|
||||
Hall = 0b0000'01000'00 | MotorControllerFeedbackMask,
|
||||
Voltage = 0b0000'01001'00 | MotorControllerFeedbackMask,
|
||||
Temp = 0b0000'01010'00 | MotorControllerFeedbackMask,
|
||||
Id = 0b0000'01011'00 | MotorControllerFeedbackMask,
|
||||
Iq = 0b0000'01100'00 | MotorControllerFeedbackMask,
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
class Boardcomputer
|
||||
{
|
||||
private:
|
||||
enum : uint16_t { // ...v
|
||||
BoardcomputerCommand = 0b00000000000, // send only TO the boardcomputer
|
||||
BoardcomputerFeedback = 0b00010000000, // send only FROM the boardcomputer
|
||||
};
|
||||
|
||||
public:
|
||||
Boardcomputer() = delete;
|
||||
~Boardcomputer() = delete;
|
||||
|
||||
// used with ButtonPressed and ButtonReleased
|
||||
enum class Button {
|
||||
Profile0 = 1,
|
||||
Profile1 = 2,
|
||||
Profile2 = 4,
|
||||
Profile3 = 8,
|
||||
Left = 16,
|
||||
Right = 32,
|
||||
Up = 64,
|
||||
Down = 128,
|
||||
Left2 = 256,
|
||||
Right2 = 512,
|
||||
Up2 = 1024,
|
||||
Down2 = 2048,
|
||||
};
|
||||
|
||||
// send only TO the boardcomputer
|
||||
class Command
|
||||
{
|
||||
private:
|
||||
enum : uint16_t {
|
||||
BoardcomputerCommandMask =
|
||||
uint16_t(DeviceTypeBoardcomputer) |
|
||||
uint16_t(BoardcomputerCommand)
|
||||
};
|
||||
|
||||
public:
|
||||
Command() = delete;
|
||||
~Command() = delete;
|
||||
|
||||
enum : uint16_t { // ....'vvvvvvv
|
||||
RawButtonPressed = 0b0000'0000000 | BoardcomputerCommandMask,
|
||||
RawButtonReleased = 0b0000'0000001 | BoardcomputerCommandMask,
|
||||
ButtonPressed = 0b0000'0000010 | BoardcomputerCommandMask,
|
||||
ButtonReleased = 0b0000'0000011 | BoardcomputerCommandMask,
|
||||
RawGas = 0b0000'0000100 | BoardcomputerCommandMask,
|
||||
RawBrems = 0b0000'0000101 | BoardcomputerCommandMask
|
||||
};
|
||||
};
|
||||
|
||||
// send only FROM the boardcomputer
|
||||
class Feedback
|
||||
{
|
||||
private:
|
||||
enum : uint16_t {
|
||||
BoardcomputerFeedbackMask =
|
||||
uint16_t(DeviceTypeBoardcomputer) |
|
||||
uint16_t(BoardcomputerFeedback)
|
||||
};
|
||||
|
||||
public:
|
||||
Feedback() = delete;
|
||||
~Feedback() = delete;
|
||||
|
||||
enum : uint16_t { // ....'vvvvvvv
|
||||
ButtonLeds = 0b0000'0000000 | BoardcomputerFeedbackMask,
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
inline const char *bobbycarCanIdDesc(uint16_t id)
|
||||
{
|
||||
switch (id)
|
||||
{
|
||||
case MotorController<false, false>::Command::Enable: return "Enable (MotorController, Command, Front, Left)";
|
||||
case MotorController<false, true>::Command::Enable: return "Enable (MotorController, Command, Front, Right)";
|
||||
case MotorController<true, false>::Command::Enable: return "Enable (MotorController, Command, Back, Left)";
|
||||
case MotorController<true, true>::Command::Enable: return "Enable (MotorController, Command, Back, Right)";
|
||||
case MotorController<false, false>::Command::InpTgt: return "InpTgt (MotorController, Command, Front, Left)";
|
||||
case MotorController<false, true>::Command::InpTgt: return "InpTgt (MotorController, Command, Front, Right)";
|
||||
case MotorController<true, false>::Command::InpTgt: return "InpTgt (MotorController, Command, Back, Left)";
|
||||
case MotorController<true, true>::Command::InpTgt: return "InpTgt (MotorController, Command, Back, Right)";
|
||||
case MotorController<false, false>::Command::CtrlTyp: return "CtrlTyp (MotorController, Command, Front, Left)";
|
||||
case MotorController<false, true>::Command::CtrlTyp: return "CtrlTyp (MotorController, Command, Front, Right)";
|
||||
case MotorController<true, false>::Command::CtrlTyp: return "CtrlTyp (MotorController, Command, Back, Left)";
|
||||
case MotorController<true, true>::Command::CtrlTyp: return "CtrlTyp (MotorController, Command, Back, Right)";
|
||||
case MotorController<false, false>::Command::CtrlMod: return "CtrlMod (MotorController, Command, Front, Left)";
|
||||
case MotorController<false, true>::Command::CtrlMod: return "CtrlMod (MotorController, Command, Front, Right)";
|
||||
case MotorController<true, false>::Command::CtrlMod: return "CtrlMod (MotorController, Command, Back, Left)";
|
||||
case MotorController<true, true>::Command::CtrlMod: return "CtrlMod (MotorController, Command, Back, Right)";
|
||||
case MotorController<false, false>::Command::IMotMax: return "IMotMax (MotorController, Command, Front, Left)";
|
||||
case MotorController<false, true>::Command::IMotMax: return "IMotMax (MotorController, Command, Front, Right)";
|
||||
case MotorController<true, false>::Command::IMotMax: return "IMotMax (MotorController, Command, Back, Left)";
|
||||
case MotorController<true, true>::Command::IMotMax: return "IMotMax (MotorController, Command, Back, Right)";
|
||||
case MotorController<false, false>::Command::IDcMax: return "IDcMax (MotorController, Command, Front, Left)";
|
||||
case MotorController<false, true>::Command::IDcMax: return "IDcMax (MotorController, Command, Front, Right)";
|
||||
case MotorController<true, false>::Command::IDcMax: return "IDcMax (MotorController, Command, Back, Left)";
|
||||
case MotorController<true, true>::Command::IDcMax: return "IDcMax (MotorController, Command, Back, Right)";
|
||||
case MotorController<false, false>::Command::NMotMax: return "NMotMax (MotorController, Command, Front, Left)";
|
||||
case MotorController<false, true>::Command::NMotMax: return "NMotMax (MotorController, Command, Front, Right)";
|
||||
case MotorController<true, false>::Command::NMotMax: return "NMotMax (MotorController, Command, Back, Left)";
|
||||
case MotorController<true, true>::Command::NMotMax: return "NMotMax (MotorController, Command, Back, Right)";
|
||||
case MotorController<false, false>::Command::FieldWeakMax: return "FieldWeakMax (MotorController, Command, Front, Left)";
|
||||
case MotorController<false, true>::Command::FieldWeakMax: return "FieldWeakMax (MotorController, Command, Front, Right)";
|
||||
case MotorController<true, false>::Command::FieldWeakMax: return "FieldWeakMax (MotorController, Command, Back, Left)";
|
||||
case MotorController<true, true>::Command::FieldWeakMax: return "FieldWeakMax (MotorController, Command, Back, Right)";
|
||||
case MotorController<false, false>::Command::PhaseAdvMax: return "PhaseAdvMax (MotorController, Command, Front, Left)";
|
||||
case MotorController<false, true>::Command::PhaseAdvMax: return "PhaseAdvMax (MotorController, Command, Front, Right)";
|
||||
case MotorController<true, false>::Command::PhaseAdvMax: return "PhaseAdvMax (MotorController, Command, Back, Left)";
|
||||
case MotorController<true, true>::Command::PhaseAdvMax: return "PhaseAdvMax (MotorController, Command, Back, Right)";
|
||||
case MotorController<false, false>::Command::BuzzerFreq: return "BuzzerFreq (MotorController, Command, Front, Left)";
|
||||
case MotorController<false, true>::Command::BuzzerFreq: return "BuzzerFreq (MotorController, Command, Front, Right)";
|
||||
case MotorController<true, false>::Command::BuzzerFreq: return "BuzzerFreq (MotorController, Command, Back, Left)";
|
||||
case MotorController<true, true>::Command::BuzzerFreq: return "BuzzerFreq (MotorController, Command, Back, Right)";
|
||||
case MotorController<false, false>::Command::BuzzerPattern: return "BuzzerPattern (MotorController, Command, Front, Left)";
|
||||
case MotorController<false, true>::Command::BuzzerPattern: return "BuzzerPattern (MotorController, Command, Front, Right)";
|
||||
case MotorController<true, false>::Command::BuzzerPattern: return "BuzzerPattern (MotorController, Command, Back, Left)";
|
||||
case MotorController<true, true>::Command::BuzzerPattern: return "BuzzerPattern (MotorController, Command, Back, Right)";
|
||||
case MotorController<false, false>::Command::Led: return "Led (MotorController, Command, Front, Left)";
|
||||
case MotorController<false, true>::Command::Led: return "Led (MotorController, Command, Front, Right)";
|
||||
case MotorController<true, false>::Command::Led: return "Led (MotorController, Command, Back, Left)";
|
||||
case MotorController<true, true>::Command::Led: return "Led (MotorController, Command, Back, Right)";
|
||||
case MotorController<false, false>::Command::Poweroff: return "Poweroff (MotorController, Command, Front, Left)";
|
||||
case MotorController<false, true>::Command::Poweroff: return "Poweroff (MotorController, Command, Front, Right)";
|
||||
case MotorController<true, false>::Command::Poweroff: return "Poweroff (MotorController, Command, Back, Left)";
|
||||
case MotorController<true, true>::Command::Poweroff: return "Poweroff (MotorController, Command, Back, Right)";
|
||||
case MotorController<false, false>::Feedback::DcLink: return "DcLink (MotorController, Feedback, Front, Left)";
|
||||
case MotorController<false, true>::Feedback::DcLink: return "DcLink (MotorController, Feedback, Front, Right)";
|
||||
case MotorController<true, false>::Feedback::DcLink: return "DcLink (MotorController, Feedback, Back, Left)";
|
||||
case MotorController<true, true>::Feedback::DcLink: return "DcLink (MotorController, Feedback, Back, Right)";
|
||||
case MotorController<false, false>::Feedback::Speed: return "Speed (MotorController, Feedback, Front, Left)";
|
||||
case MotorController<false, true>::Feedback::Speed: return "Speed (MotorController, Feedback, Front, Right)";
|
||||
case MotorController<true, false>::Feedback::Speed: return "Speed (MotorController, Feedback, Back, Left)";
|
||||
case MotorController<true, true>::Feedback::Speed: return "Speed (MotorController, Feedback, Back, Right)";
|
||||
case MotorController<false, false>::Feedback::Error: return "Error (MotorController, Feedback, Front, Left)";
|
||||
case MotorController<false, true>::Feedback::Error: return "Error (MotorController, Feedback, Front, Right)";
|
||||
case MotorController<true, false>::Feedback::Error: return "Error (MotorController, Feedback, Back, Left)";
|
||||
case MotorController<true, true>::Feedback::Error: return "Error (MotorController, Feedback, Back, Right)";
|
||||
case MotorController<false, false>::Feedback::Angle: return "Angle (MotorController, Feedback, Front, Left)";
|
||||
case MotorController<false, true>::Feedback::Angle: return "Angle (MotorController, Feedback, Front, Right)";
|
||||
case MotorController<true, false>::Feedback::Angle: return "Angle (MotorController, Feedback, Back, Left)";
|
||||
case MotorController<true, true>::Feedback::Angle: return "Angle (MotorController, Feedback, Back, Right)";
|
||||
case MotorController<false, false>::Feedback::DcPhaA: return "DcPhaA (MotorController, Feedback, Front, Left)";
|
||||
case MotorController<false, true>::Feedback::DcPhaA: return "DcPhaA (MotorController, Feedback, Front, Right)";
|
||||
case MotorController<true, false>::Feedback::DcPhaA: return "DcPhaA (MotorController, Feedback, Back, Left)";
|
||||
case MotorController<true, true>::Feedback::DcPhaA: return "DcPhaA (MotorController, Feedback, Back, Right)";
|
||||
case MotorController<false, false>::Feedback::DcPhaB: return "DcPhaB (MotorController, Feedback, Front, Left)";
|
||||
case MotorController<false, true>::Feedback::DcPhaB: return "DcPhaB (MotorController, Feedback, Front, Right)";
|
||||
case MotorController<true, false>::Feedback::DcPhaB: return "DcPhaB (MotorController, Feedback, Back, Left)";
|
||||
case MotorController<true, true>::Feedback::DcPhaB: return "DcPhaB (MotorController, Feedback, Back, Right)";
|
||||
case MotorController<false, false>::Feedback::DcPhaC: return "DcPhaC (MotorController, Feedback, Front, Left)";
|
||||
case MotorController<false, true>::Feedback::DcPhaC: return "DcPhaC (MotorController, Feedback, Front, Right)";
|
||||
case MotorController<true, false>::Feedback::DcPhaC: return "DcPhaC (MotorController, Feedback, Back, Left)";
|
||||
case MotorController<true, true>::Feedback::DcPhaC: return "DcPhaC (MotorController, Feedback, Back, Right)";
|
||||
case MotorController<false, false>::Feedback::Chops: return "Chops (MotorController, Feedback, Front, Left)";
|
||||
case MotorController<false, true>::Feedback::Chops: return "Chops (MotorController, Feedback, Front, Right)";
|
||||
case MotorController<true, false>::Feedback::Chops: return "Chops (MotorController, Feedback, Back, Left)";
|
||||
case MotorController<true, true>::Feedback::Chops: return "Chops (MotorController, Feedback, Back, Right)";
|
||||
case MotorController<false, false>::Feedback::Hall: return "Hall (MotorController, Feedback, Front, Left)";
|
||||
case MotorController<false, true>::Feedback::Hall: return "Hall (MotorController, Feedback, Front, Right)";
|
||||
case MotorController<true, false>::Feedback::Hall: return "Hall (MotorController, Feedback, Back, Left)";
|
||||
case MotorController<true, true>::Feedback::Hall: return "Hall (MotorController, Feedback, Back, Right)";
|
||||
case MotorController<false, false>::Feedback::Voltage: return "Voltage (MotorController, Feedback, Front, Left)";
|
||||
case MotorController<false, true>::Feedback::Voltage: return "Voltage (MotorController, Feedback, Front, Right)";
|
||||
case MotorController<true, false>::Feedback::Voltage: return "Voltage (MotorController, Feedback, Back, Left)";
|
||||
case MotorController<true, true>::Feedback::Voltage: return "Voltage (MotorController, Feedback, Back, Right)";
|
||||
case MotorController<false, false>::Feedback::Temp: return "Temp (MotorController, Feedback, Front, Left)";
|
||||
case MotorController<false, true>::Feedback::Temp: return "Temp (MotorController, Feedback, Front, Right)";
|
||||
case MotorController<true, false>::Feedback::Temp: return "Temp (MotorController, Feedback, Back, Left)";
|
||||
case MotorController<true, true>::Feedback::Temp: return "Temp (MotorController, Feedback, Back, Right)";
|
||||
case Boardcomputer::Command::RawButtonPressed: return "RawButtonPressed (Boardcomputer, Command)";
|
||||
case Boardcomputer::Command::RawButtonReleased: return "RawButtonReleased (Boardcomputer, Command)";
|
||||
case Boardcomputer::Command::ButtonPressed: return "ButtonPressed (Boardcomputer, Command)";
|
||||
case Boardcomputer::Command::ButtonReleased: return "ButtonReleased (Boardcomputer, Command)";
|
||||
case Boardcomputer::Command::RawGas: return "RawGas (Boardcomputer, Command)";
|
||||
case Boardcomputer::Command::RawBrems: return "RawBrems (Boardcomputer, Command)";
|
||||
case Boardcomputer::Feedback::ButtonLeds: return "ButtonLeds (Boardcomputer, Feedback)";
|
||||
}
|
||||
return "Unknown";
|
||||
}
|
||||
|
||||
} // namespace can
|
||||
} // namespace protocol
|
||||
} // namespace bobbycar
|
22
bobbycar-common.h
Normal file
22
bobbycar-common.h
Normal file
@ -0,0 +1,22 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
namespace bobbycar {
|
||||
namespace protocol {
|
||||
|
||||
enum class ControlType : uint8_t {
|
||||
Commutation,
|
||||
Sinusoidal,
|
||||
FieldOrientedControl
|
||||
};
|
||||
|
||||
enum class ControlMode : uint8_t {
|
||||
OpenMode,
|
||||
Voltage,
|
||||
Speed, // Only with FieldOrientedControl
|
||||
Torque // Only with FieldOrientedControl
|
||||
};
|
||||
|
||||
} // namespace protocol
|
||||
} // namespace bobbycar
|
198
bobbycar-serial.h
Normal file
198
bobbycar-serial.h
Normal file
@ -0,0 +1,198 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include "bobbycar-common.h"
|
||||
|
||||
namespace bobbycar {
|
||||
namespace protocol {
|
||||
namespace serial {
|
||||
|
||||
struct MotorState {
|
||||
bool enable = false;
|
||||
int16_t pwm = 0;
|
||||
ControlType ctrlTyp = ControlType::FieldOrientedControl;
|
||||
ControlMode ctrlMod = ControlMode::OpenMode;
|
||||
uint8_t iMotMax = 15; // [A] Maximum motor current limit
|
||||
uint8_t iDcMax = 17; // [A] Maximum DC Link current limit (This is the final current protection. Above this value, current chopping is applied. To avoid this make sure that I_DC_MAX = I_MOT_MAX + 2A)
|
||||
uint16_t nMotMax = 1000; // [rpm] Maximum motor speed limit
|
||||
uint8_t fieldWeakMax = 10; // [A] Maximum Field Weakening D axis current (only for FOC). Higher current results in higher maximum speed.
|
||||
uint8_t phaseAdvMax = 40; // [deg] Maximum Phase Advance angle (only for SIN). Higher angle results in higher maximum speed.
|
||||
bool cruiseCtrlEna = false;
|
||||
int16_t nCruiseMotTgt = 0;
|
||||
};
|
||||
|
||||
inline uint16_t calculateChecksum(MotorState state) {
|
||||
return
|
||||
uint16_t(state.enable) ^
|
||||
state.pwm ^
|
||||
uint16_t(state.ctrlTyp) ^
|
||||
uint16_t(state.ctrlMod) ^
|
||||
state.iMotMax ^
|
||||
state.iDcMax ^
|
||||
state.nMotMax ^
|
||||
state.fieldWeakMax ^
|
||||
state.phaseAdvMax ^
|
||||
uint16_t(state.cruiseCtrlEna) ^
|
||||
state.nCruiseMotTgt;
|
||||
}
|
||||
|
||||
struct BuzzerState {
|
||||
uint8_t freq = 0;
|
||||
uint8_t pattern = 0;
|
||||
};
|
||||
|
||||
inline uint16_t calculateChecksum(BuzzerState state) {
|
||||
return state.freq ^ state.pattern;
|
||||
}
|
||||
|
||||
struct Command {
|
||||
static constexpr uint16_t VALID_HEADER = 0xAAAA;
|
||||
static constexpr uint16_t INVALID_HEADER = 0xFFFF;
|
||||
|
||||
uint16_t start;
|
||||
|
||||
MotorState left, right;
|
||||
|
||||
BuzzerState buzzer;
|
||||
|
||||
bool poweroff = false;
|
||||
bool led = false;
|
||||
|
||||
uint16_t checksum;
|
||||
};
|
||||
|
||||
inline uint16_t calculateChecksum(Command command) {
|
||||
return command.start ^
|
||||
calculateChecksum(command.left) ^
|
||||
calculateChecksum(command.right) ^
|
||||
calculateChecksum(command.buzzer) ^
|
||||
command.poweroff ^
|
||||
command.led;
|
||||
}
|
||||
|
||||
struct MotorFeedback {
|
||||
int16_t angle = 0;
|
||||
int16_t speed = 0;
|
||||
uint8_t error = 0;
|
||||
int16_t dcLink = 0;
|
||||
int16_t dcPhaA = 0;
|
||||
int16_t dcPhaB = 0;
|
||||
int16_t dcPhaC = 0;
|
||||
uint16_t chops = 0;
|
||||
int16_t id = 0;
|
||||
int16_t iq = 0;
|
||||
bool hallA = false;
|
||||
bool hallB = false;
|
||||
bool hallC = false;
|
||||
};
|
||||
|
||||
inline uint16_t calculateChecksum(MotorFeedback feedback) {
|
||||
return feedback.angle ^ feedback.speed ^
|
||||
feedback.error ^ feedback.dcLink ^
|
||||
feedback.dcPhaA ^ feedback.dcPhaB ^
|
||||
feedback.dcPhaC ^ feedback.chops ^
|
||||
feedback.hallA ^ feedback.hallB ^ feedback.hallC;
|
||||
}
|
||||
|
||||
struct Feedback {
|
||||
static constexpr uint16_t VALID_HEADER = 0xAAAA;
|
||||
static constexpr uint16_t INVALID_HEADER = 0xFFFF;
|
||||
|
||||
uint16_t start;
|
||||
|
||||
MotorFeedback left, right;
|
||||
|
||||
int16_t batVoltage = 0;
|
||||
int16_t boardTemp = 0;
|
||||
|
||||
int16_t timeoutCntSerial = 0;
|
||||
|
||||
uint16_t checksum;
|
||||
};
|
||||
|
||||
inline uint16_t calculateChecksum(Feedback feedback) {
|
||||
return feedback.start ^
|
||||
calculateChecksum(feedback.left) ^
|
||||
calculateChecksum(feedback.right) ^
|
||||
feedback.batVoltage ^
|
||||
feedback.boardTemp ^
|
||||
feedback.timeoutCntSerial;
|
||||
}
|
||||
|
||||
|
||||
#define ASSERT_LAYOUT(st, memb, off) \
|
||||
//static_assert(offsetof(st, memb) == off, "struct layout wrong");
|
||||
|
||||
ASSERT_LAYOUT(Feedback, start, 0);
|
||||
ASSERT_LAYOUT(Feedback, left, 2);
|
||||
ASSERT_LAYOUT(Feedback, left.angle, 2);
|
||||
ASSERT_LAYOUT(Feedback, left.speed, 4);
|
||||
ASSERT_LAYOUT(Feedback, left.error, 6);
|
||||
ASSERT_LAYOUT(Feedback, left.dcLink, 8);
|
||||
ASSERT_LAYOUT(Feedback, left.dcPhaA, 10);
|
||||
ASSERT_LAYOUT(Feedback, left.dcPhaB, 12);
|
||||
ASSERT_LAYOUT(Feedback, left.dcPhaC, 14);
|
||||
ASSERT_LAYOUT(Feedback, left.chops, 16);
|
||||
ASSERT_LAYOUT(Feedback, left.hallA, 18);
|
||||
ASSERT_LAYOUT(Feedback, left.hallB, 19);
|
||||
ASSERT_LAYOUT(Feedback, left.hallC, 20);
|
||||
|
||||
ASSERT_LAYOUT(Feedback, right, 22);
|
||||
ASSERT_LAYOUT(Feedback, right.angle, 22);
|
||||
ASSERT_LAYOUT(Feedback, right.speed, 24);
|
||||
ASSERT_LAYOUT(Feedback, right.error, 26);
|
||||
ASSERT_LAYOUT(Feedback, right.dcLink, 28);
|
||||
ASSERT_LAYOUT(Feedback, right.dcPhaA, 30);
|
||||
ASSERT_LAYOUT(Feedback, right.dcPhaB, 32);
|
||||
ASSERT_LAYOUT(Feedback, right.dcPhaC, 34);
|
||||
ASSERT_LAYOUT(Feedback, right.chops, 36);
|
||||
ASSERT_LAYOUT(Feedback, right.hallA, 38);
|
||||
ASSERT_LAYOUT(Feedback, right.hallB, 39);
|
||||
ASSERT_LAYOUT(Feedback, right.hallC, 40);
|
||||
|
||||
ASSERT_LAYOUT(Feedback, batVoltage, 42);
|
||||
ASSERT_LAYOUT(Feedback, boardTemp, 44);
|
||||
ASSERT_LAYOUT(Feedback, timeoutCntSerial, 46);
|
||||
ASSERT_LAYOUT(Feedback, checksum, 48);
|
||||
|
||||
//static_assert(sizeof(Command) == 38, "sizeof(Command) wrong");
|
||||
|
||||
ASSERT_LAYOUT(Command, start, 0);
|
||||
|
||||
ASSERT_LAYOUT(Command, left, 2);
|
||||
ASSERT_LAYOUT(Command, left.enable, 2);
|
||||
ASSERT_LAYOUT(Command, left.pwm, 4);
|
||||
ASSERT_LAYOUT(Command, left.ctrlTyp, 6);
|
||||
ASSERT_LAYOUT(Command, left.ctrlMod, 7);
|
||||
ASSERT_LAYOUT(Command, left.iMotMax, 8);
|
||||
ASSERT_LAYOUT(Command, left.iDcMax, 9);
|
||||
ASSERT_LAYOUT(Command, left.nMotMax, 10);
|
||||
ASSERT_LAYOUT(Command, left.fieldWeakMax, 12);
|
||||
ASSERT_LAYOUT(Command, left.phaseAdvMax, 13);
|
||||
ASSERT_LAYOUT(Command, left.cruiseCtrlEna, 14);
|
||||
ASSERT_LAYOUT(Command, left.nCruiseMotTgt, 15);
|
||||
|
||||
ASSERT_LAYOUT(Command, right.enable, 18);
|
||||
ASSERT_LAYOUT(Command, right.pwm, 19);
|
||||
ASSERT_LAYOUT(Command, right.ctrlTyp, 21);
|
||||
ASSERT_LAYOUT(Command, right.ctrlMod, 22);
|
||||
ASSERT_LAYOUT(Command, right.iMotMax, 23);
|
||||
ASSERT_LAYOUT(Command, right.iDcMax, 24);
|
||||
ASSERT_LAYOUT(Command, right.nMotMax, 25);
|
||||
ASSERT_LAYOUT(Command, right.fieldWeakMax, 27);
|
||||
ASSERT_LAYOUT(Command, right.phaseAdvMax, 28);
|
||||
ASSERT_LAYOUT(Command, right.cruiseCtrlEna, 29);
|
||||
ASSERT_LAYOUT(Command, right.nCruiseMotTgt, 31);
|
||||
|
||||
ASSERT_LAYOUT(Command, buzzer, 33);
|
||||
ASSERT_LAYOUT(Command, buzzer.freq, 33);
|
||||
ASSERT_LAYOUT(Command, buzzer.pattern, 27);
|
||||
|
||||
ASSERT_LAYOUT(Command, poweroff, 28);
|
||||
ASSERT_LAYOUT(Command, led, 29);
|
||||
ASSERT_LAYOUT(Command, checksum, 30);
|
||||
|
||||
} // namespace serial
|
||||
} // namespace protocol
|
||||
} // namespace bobbycar
|
122
protocol.h
122
protocol.h
@ -1,122 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
namespace {
|
||||
|
||||
enum class ControlType : uint8_t {
|
||||
Commutation,
|
||||
Sinusoidal,
|
||||
FieldOrientedControl
|
||||
};
|
||||
|
||||
enum class ControlMode : uint8_t {
|
||||
OpenMode,
|
||||
Voltage,
|
||||
Speed, // Only with FieldOrientedControl
|
||||
Torque // Only with FieldOrientedControl
|
||||
};
|
||||
|
||||
struct MotorState {
|
||||
bool enable = false;
|
||||
int16_t pwm = 0;
|
||||
ControlType ctrlTyp = ControlType::FieldOrientedControl;
|
||||
ControlMode ctrlMod = ControlMode::OpenMode;
|
||||
int16_t iMotMax = 15; // [A] Maximum motor current limit
|
||||
int16_t iDcMax = 17; // [A] Maximum DC Link current limit (This is the final current protection. Above this value, current chopping is applied. To avoid this make sure that I_DC_MAX = I_MOT_MAX + 2A)
|
||||
int16_t nMotMax = 1000; // [rpm] Maximum motor speed limit
|
||||
int16_t fieldWeakMax = 10; // [A] Maximum Field Weakening D axis current (only for FOC). Higher current results in higher maximum speed.
|
||||
int16_t phaseAdvMax = 40; // [deg] Maximum Phase Advance angle (only for SIN). Higher angle results in higher maximum speed.
|
||||
};
|
||||
|
||||
uint16_t calculateChecksum(MotorState state) {
|
||||
return
|
||||
uint16_t(state.enable) ^
|
||||
state.pwm ^
|
||||
uint16_t(state.ctrlTyp) ^
|
||||
uint16_t(state.ctrlMod) ^
|
||||
state.iMotMax ^
|
||||
state.iDcMax ^
|
||||
state.nMotMax ^
|
||||
state.fieldWeakMax ^
|
||||
state.phaseAdvMax;
|
||||
}
|
||||
|
||||
struct BuzzerState {
|
||||
uint8_t freq = 0;
|
||||
uint8_t pattern = 0;
|
||||
};
|
||||
|
||||
uint16_t calculateChecksum(BuzzerState state) {
|
||||
return state.freq ^ state.pattern;
|
||||
}
|
||||
|
||||
struct Command {
|
||||
static constexpr uint16_t VALID_HEADER = 0xAAAA;
|
||||
static constexpr uint16_t INVALID_HEADER = 0xFFFF;
|
||||
|
||||
uint16_t start;
|
||||
|
||||
MotorState left, right;
|
||||
|
||||
BuzzerState buzzer;
|
||||
|
||||
bool poweroff = false;
|
||||
bool led = false;
|
||||
|
||||
uint16_t checksum;
|
||||
};
|
||||
|
||||
uint16_t calculateChecksum(Command command) {
|
||||
return command.start ^
|
||||
calculateChecksum(command.left) ^
|
||||
calculateChecksum(command.right) ^
|
||||
calculateChecksum(command.buzzer) ^
|
||||
command.poweroff ^
|
||||
command.led;
|
||||
}
|
||||
|
||||
struct MotorFeedback {
|
||||
int16_t angle = 0;
|
||||
int16_t speed = 0;
|
||||
uint8_t error = 0;
|
||||
int16_t current = 0;
|
||||
uint32_t chops = 0;
|
||||
bool hallA = false,
|
||||
hallB = false,
|
||||
hallC = false;
|
||||
};
|
||||
|
||||
uint16_t calculateChecksum(MotorFeedback feedback) {
|
||||
return feedback.angle ^ feedback.speed ^
|
||||
feedback.error ^ feedback.current ^
|
||||
feedback.chops ^
|
||||
feedback.hallA ^ feedback.hallB ^ feedback.hallC;
|
||||
}
|
||||
|
||||
struct Feedback {
|
||||
static constexpr uint16_t VALID_HEADER = 0xAAAA;
|
||||
static constexpr uint16_t INVALID_HEADER = 0xFFFF;
|
||||
|
||||
uint16_t start;
|
||||
|
||||
MotorFeedback left, right;
|
||||
|
||||
int16_t batVoltage = 0;
|
||||
int16_t boardTemp = 0;
|
||||
|
||||
int16_t timeoutCntSerial = 0;
|
||||
|
||||
uint16_t checksum;
|
||||
};
|
||||
|
||||
uint16_t calculateChecksum(Feedback feedback) {
|
||||
return feedback.start ^
|
||||
calculateChecksum(feedback.left) ^
|
||||
calculateChecksum(feedback.right) ^
|
||||
feedback.batVoltage ^
|
||||
feedback.boardTemp ^
|
||||
feedback.timeoutCntSerial;
|
||||
}
|
||||
|
||||
}
|
167
utils/bobbycan.lua
Normal file
167
utils/bobbycan.lua
Normal file
@ -0,0 +1,167 @@
|
||||
local p_bobbycan = Proto("bobbycan", "BobbyCAN")
|
||||
p_bobbycan.fields = {}
|
||||
|
||||
local function format_voltage(val)
|
||||
return string.format("%.2f V", val / 100)
|
||||
end
|
||||
|
||||
local function format_cmd_current(val)
|
||||
return string.format("%d A", val)
|
||||
end
|
||||
|
||||
local function format_fb_current(val)
|
||||
return string.format("%.2f A", -val / 50)
|
||||
end
|
||||
|
||||
local function format_angle(val)
|
||||
return string.format("%d°", val)
|
||||
end
|
||||
|
||||
local function format_temperature(val)
|
||||
return string.format("%.1f °C", val / 10)
|
||||
end
|
||||
|
||||
local function format_speed(val)
|
||||
return string.format("%d RPM", val)
|
||||
end
|
||||
|
||||
local motor_cmds = {
|
||||
[ 0] = { len = 1, abbr = "enable", name = "Enable", type = ftypes.BOOLEAN },
|
||||
[ 1] = { len = 2, abbr = "inp_tgt", name = "Input target", type = ftypes.INT16 },
|
||||
[ 2] = { len = 1, abbr = "ctrl_typ", name = "Control type", type = ftypes.UINT8,
|
||||
valuestring = {[0] = "Commutation", [1] = "Sinusoidal", [2] = "FOC"} },
|
||||
[ 3] = { len = 1, abbr = "ctrl_mod", name = "Control mode", type = ftypes.UINT8,
|
||||
valuestring = {[0] = "Open", [1] = "Voltage", [2] = "Speed", [3] = "Torque"} },
|
||||
[ 4] = { len = 1, abbr = "imotmax", name = "Maximum motor current", type = ftypes.UINT8, format = format_cmd_current },
|
||||
[ 5] = { len = 1, abbr = "idcmax", name = "Maximum DC link current", type = ftypes.UINT8, format = format_cmd_current },
|
||||
[ 6] = { len = 2, abbr = "nmotmax", name = "Maximum speed", type = ftypes.UINT16, format = format_speed },
|
||||
[ 7] = { len = 1, abbr = "fieldweakmax", name = "Maximum field weakening current", type = ftypes.UINT8, format = format_cmd_current },
|
||||
[ 8] = { len = 1, abbr = "phaseadvmax", name = "Maximum phase advance angle", type = ftypes.UINT16, format = format_angle },
|
||||
[ 9] = { len = 1, abbr = "cruisectrlena", name = "Enable cruise control", type = ftypes.BOOLEAN },
|
||||
[10] = { len = 1, abbr = "cruisemottgt", name = "Cruise control target", type = ftypes.INT16, format = format_speed },
|
||||
[11] = { len = 1, abbr = "buzzerfreq", name = "Buzzer freq. divider", type = ftypes.UINT8 },
|
||||
[12] = { len = 1, abbr = "buzzerpattern", name = "Buzzer pattern", type = ftypes.UINT8 },
|
||||
[13] = { len = 1, abbr = "led", name = "LED state", type = ftypes.BOOLEAN },
|
||||
[14] = { len = 1, abbr = "poweroff", name = "Power off", type = ftypes.BOOLEAN },
|
||||
}
|
||||
|
||||
local motor_errors = {
|
||||
[0] = "None",
|
||||
[1] = "Hall sensor not connected",
|
||||
[2] = "Hall sensor short circuit",
|
||||
[4] = "Motor NOT able to spin"
|
||||
}
|
||||
|
||||
local motor_fbs = {
|
||||
[ 0] = { len = 2, abbr = "dc_link", name = "DC Link Current", type = ftypes.INT16, format = format_fb_current },
|
||||
[ 1] = { len = 2, abbr = "speed", name = "Speed", type = ftypes.INT16 },
|
||||
[ 2] = { len = 1, abbr = "error", name = "Error", type = ftypes.UINT8, valuestring = motor_errors },
|
||||
[ 3] = { len = 2, abbr = "angle", name = "Electrical angle", type = ftypes.INT16, format = format_angle },
|
||||
[ 4] = { len = 2, abbr = "dc_a", name = "Phase A Current", type = ftypes.INT16, format = format_fb_current },
|
||||
[ 5] = { len = 2, abbr = "dc_b", name = "Phase B Current", type = ftypes.INT16, format = format_fb_current },
|
||||
[ 6] = { len = 2, abbr = "dc_c", name = "Phase C Current", type = ftypes.INT16, format = format_fb_current },
|
||||
[ 7] = { len = 4, abbr = "chops", name = "Chops since last update", type = ftypes.UINT32 },
|
||||
[ 8] = { len = 1, abbr = "hall", name = "Hall Sensors", type = ftypes.UINT8 },
|
||||
[ 9] = { len = 2, abbr = "voltage", name = "Supply Voltage", type = ftypes.INT16, format = format_voltage },
|
||||
[10] = { len = 2, abbr = "temp", name = "MCU Temperature", type = ftypes.INT16, format = format_temperature },
|
||||
[11] = { len = 2, abbr = "curr_id", name = "Id current", type = ftypes.INT16, format = format_fb_current },
|
||||
[12] = { len = 2, abbr = "curr_iq", name = "Iq current", type = ftypes.INT16, format = format_fb_current },
|
||||
}
|
||||
|
||||
local motor_generated = {
|
||||
left = { abbr = "left", name = "Is left", type = ftypes.BOOLEAN },
|
||||
right = { abbr = "right", name = "Is right", type = ftypes.BOOLEAN },
|
||||
front = { abbr = "front", name = "Is front", type = ftypes.BOOLEAN },
|
||||
back = { abbr = "back", name = "Is back", type = ftypes.BOOLEAN },
|
||||
cmd = { abbr = "cmd", name = "Is command", type = ftypes.BOOLEAN },
|
||||
fb = { abbr = "fb", name = "Is feedback", type = ftypes.BOOLEAN },
|
||||
}
|
||||
|
||||
local function create_fields(prefix, proto)
|
||||
for k,p in pairs(proto) do
|
||||
p.field = ProtoField.new(p.name, prefix .. p.abbr, p.type, p.valuestring, p.base, p.mask, p.descr)
|
||||
table.insert(p_bobbycan.fields, p.field)
|
||||
end
|
||||
end
|
||||
create_fields("bobbycan.motor.", motor_cmds)
|
||||
create_fields("bobbycan.motor.", motor_fbs)
|
||||
create_fields("bobbycan.motor.", motor_generated)
|
||||
|
||||
local f_can_id = Field.new("can.id")
|
||||
|
||||
function to_int(t, tvsr)
|
||||
if t == ftypes.UINT8 or t == ftypes.UINT16 or t == ftypes.UINT32 then
|
||||
return tvsr:le_uint()
|
||||
else
|
||||
return tvsr:le_int()
|
||||
end
|
||||
end
|
||||
|
||||
function p_bobbycan.dissector(buf, pkt, tree)
|
||||
pkt.cols.protocol:set("BobbyCAN")
|
||||
|
||||
local can_id = f_can_id()()
|
||||
pkt.cols.info:set("test " .. can_id)
|
||||
|
||||
local devtype = bit32.rshift(bit32.band(can_id, 0x700), 8)
|
||||
if devtype == 0 then
|
||||
local command = bit32.band(can_id, 0x80) == 0
|
||||
local subid = bit32.rshift(bit32.band(can_id, 0x7c), 2)
|
||||
local tbl
|
||||
local prefix
|
||||
|
||||
local generated_fields = { nil, nil, nil }
|
||||
if command then
|
||||
tbl = motor_cmds
|
||||
prefix = " CMD "
|
||||
generated_fields[1] = motor_generated.cmd.field
|
||||
else
|
||||
tbl = motor_fbs
|
||||
prefix = " FB "
|
||||
generated_fields[1] = motor_generated.fb.field
|
||||
end
|
||||
|
||||
local front = bit32.band(can_id, 0x02) == 0
|
||||
if front then
|
||||
prefix = prefix .. "F"
|
||||
generated_fields[2] = motor_generated.front.field
|
||||
else
|
||||
prefix = prefix .. "B"
|
||||
generated_fields[2] = motor_generated.back.field
|
||||
end
|
||||
|
||||
local left = bit32.band(can_id, 0x01) == 0
|
||||
if left then
|
||||
prefix = prefix .. "L "
|
||||
generated_fields[3] = motor_generated.left.field
|
||||
else
|
||||
prefix = prefix .. "R "
|
||||
generated_fields[3] = motor_generated.right.field
|
||||
end
|
||||
|
||||
local meta = tbl[subid]
|
||||
local tvsr = buf(0,meta.len)
|
||||
local subtree = tree:add(p_bobbycan, tvsr)
|
||||
|
||||
local field_val = to_int(meta.type, tvsr)
|
||||
local ti = subtree:add_packet_field(meta.field, tvsr, ENC_LITTLE_ENDIAN)
|
||||
local val_fmt
|
||||
if meta.format then
|
||||
val_fmt = meta.format(field_val)
|
||||
elseif meta.valuestring and meta.valuestring[field_val] then
|
||||
val_fmt = string.format("%s (%d)", meta.valuestring[field_val], field_val)
|
||||
else
|
||||
val_fmt = tostring(field_val)
|
||||
end
|
||||
ti.text = meta.name..": "..val_fmt
|
||||
pkt.cols.info:set(prefix..meta.name.." = "..val_fmt)
|
||||
|
||||
-- Add generated fields (front, back, left, right, cmd, fb)
|
||||
for k,f in ipairs(generated_fields) do
|
||||
subtree:add(f, true):set_generated()
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
can_table = DissectorTable.get("can.subdissector")
|
||||
can_table:add_for_decode_as(p_bobbycan)
|
Reference in New Issue
Block a user