MX-12W Servo Library

Dependents:   DISCO_L475VG_IOT01-Sensors-BSP

Files at this revision

API Documentation at this revision

Comitter:
tsoul
Date:
Fri Apr 16 14:52:24 2021 +0000
Parent:
4:9ffc4009a463
Commit message:
Switch to state machine for more reliable parsing

Changed in this revision

CMakeLists.txt Show annotated file Show diff for this revision Revisions of this file
MX12.cpp Show annotated file Show diff for this revision Revisions of this file
MX12.h Show annotated file Show diff for this revision Revisions of this file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/CMakeLists.txt	Fri Apr 16 14:52:24 2021 +0000
@@ -0,0 +1,9 @@
+cmake_minimum_required(VERSION 3.19.0)
+
+add_library(mx12
+	MX12.cpp
+)
+
+target_link_libraries(mx12 mbed-os)
+
+target_include_directories(mx12 PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
--- a/MX12.cpp	Fri Jan 15 09:15:47 2021 +0000
+++ b/MX12.cpp	Fri Apr 16 14:52:24 2021 +0000
@@ -1,168 +1,168 @@
 #include "MX12.h"
 
 MX12::MX12(PinName tx, PinName rx, int baud) : _mx12(tx, rx) {
-    _baud = baud;
-    _mx12.baud(_baud);
-    _mx12.format(8, SerialBase::None, 1);
-    _mx12.attach(callback(this, &MX12::_ReadCallback), SerialBase::RxIrq);
-    _state = State::Available;
+	// Serial configuration
+	_mx12.baud(baud);
+	_mx12.format(8, SerialBase::None, 1);
+	_mx12.attach(callback(this, &MX12::_ReadCallback), SerialBase::RxIrq);
+
+	// Internal defaults
+	_sstate = SerialState::Idling;
+	_pstate = ParsingState::Header;
 }
 
 void MX12::SetSpeed(unsigned char mot_id, float speed) {
-    char data[2];
- 
-    // Speed absolute value
-    int goal = (0x3ff * abs(speed));
+	char data[2];
+
+	// Speed absolute value
+	int goal = (0x3ff * abs(speed));
  
-    // Spin direction (CW is negative)
-    if (speed < 0) {
-        goal |= (0x1 << 10);
-    }
+	// Spin direction (CW is negative)
+	if (speed < 0) {
+		goal |= (0x1 << 10);
+	}
 
-    data[0] = goal & 0xff;
-    data[1] = goal >> 8;
-    
-    // Enter writing state
-    _state = State::Writing;
- 
-    // Send instruction
-    rw(mot_id, 0x20, 2, data);
+	data[0] = goal & 0xff;
+	data[1] = goal >> 8;
+	
+	// Send instruction
+	_sstate = SerialState::Writing;
+	rw(mot_id, 0x20, 2, data);
 }
 
 char MX12::IsAvailable(void) {
-    return _state == State::Available;
-}
-
-void MX12::ReadPosition(unsigned char mot_id) {
-    // Make a request, interrupt takes care of everything else
-    _state = State::ReadingPosition;
-    rw(mot_id, 0x24, 2, NULL);
-}
-
-float MX12::GetPosition(unsigned char mot_id) {
-    return _angle[mot_id];
-}
-
-MX12::Status MX12::GetStatus(void) {
-    return _status;
-}
-
-// Debug function to print Serial read
-void MX12::PrintAnswer() {
-    for(char i = 0; i < MX12_ANSWER_MAX_SIZE; i++) {
-        printf("%x ", _res[i]);
-    }
-    
-    printf("\r\n");
+	return (_sstate == SerialState::Idling);
 }
 
 void MX12::rw(unsigned char mot_id, char adress, char len, char *data) {
-    _res_count = 0;
-    memset(_res, 0, MX12_ANSWER_MAX_SIZE);
-    
-    // Forge packet
-    char cmd[16];
-    
-    cmd[0] = 0xff;
-    cmd[1] = 0xff;
-    cmd[4] = 0x02 + (data != NULL);
-    
-    cmd[2] = mot_id;
-    
-    if(data == NULL) cmd[3] = 4;
-    else cmd[3] = 3 + len;
-    
-    cmd[5] = adress;
-    
-    // Compute checksum
-    if(data == NULL) {
-        cmd[6] = len;
-        cmd[7] = 0xFF - (mot_id + 4 + 2 + adress + len);
-        
-        // [XXX] Force length to one to force send
-        len = 1;
-    } else {
-        char cs = mot_id + len + adress + 6;
-        
-        for(char i = 0; i < len; i++) {
-            cmd[6 + i] = data[i];
-            cs += data[i];
-        }
-        
-        cmd[6 + len] = 0xFF - cs;
-    }
-    
-    // Send packet
-    if(mot_id != 0xFE) {
-        for(char i = 0; i < (7 + len); i++) {
-            _mx12.write(&cmd[i], 1);
-        }
-    }
+	_answer = 0;
+	memset(_current_frame.data, 0, MX12_DATA_MAX_SIZE);
+
+	// Forge packet
+	char cmd[16];
+	
+	cmd[0] = 0xff;
+	cmd[1] = 0xff;
+	cmd[4] = 0x02 + (data != NULL);
+	
+	cmd[2] = mot_id;
+	
+	if(data == NULL) cmd[3] = 4;
+	else cmd[3] = 3 + len;
+	
+	cmd[5] = adress;
+	
+	// Compute checksum
+	if(data == NULL) {
+		cmd[6] = len;
+		cmd[7] = 0xFF - (mot_id + 4 + 2 + adress + len);
+		
+		// Force length to one to force send
+		len = 1;
+	} else {
+		char cs = mot_id + len + adress + 6;
+		
+		for(char i = 0; i < len; i++) {
+			cmd[6 + i] = data[i];
+			cs += data[i];
+		}
+		
+		cmd[6 + len] = 0xFF - cs;
+	}
+	
+	// Send packet
+	if(mot_id != 0xFE) {
+		for(char i = 0; i < (7 + len); i++) {
+			_mx12.write(&cmd[i], 1);
+		}
+	}
+}
+
+void MX12::PrintSerial() {
+	for(int i = 0; i < _frame_pointer; i++) {
+		printf("%x ", _stored_frame[i]);
+	}
+
+	printf("\n");
+}
+
+MX12::Status MX12::GetStatus() {
+	// Return the corresponding status code
+	switch(_current_frame.data[0]) {
+		case 0:
+			return Ok;
+			break;
+		case 1 << 0:
+			return InputVoltageError;
+			break;
+		case 1 << 1:
+			return AngleLimitError;
+			break;
+		case 1 << 2:
+			return OverheatingError;
+			break;
+		case 1 << 3:
+			return RangeError;
+			break;
+		case 1 << 4:
+			return ChecksumError;
+			break;
+		case 1 << 5:
+			return OverloadError;
+			break;
+		case 1 << 6:
+			return InstructionError;
+			break;
+		default:
+			return Unknown;
+	}
 }
 
 void MX12::_ReadCallback() {
-    char c;
-    
-    // Loop on reading serial
-    if(_mx12.read(&c, 1)) {
-        _res[_res_count] = c;
-        _res_count++;
-        if(_res_count >= MX12_ANSWER_MAX_SIZE) _res_count = 0;
-        
-        // Find answer in buffer
-        char ans_i = 2;
-        for(; (_res[ans_i] != 0xFF) && (ans_i <= MX12_ANSWER_MAX_SIZE - 1); ans_i++);
-        if(ans_i >= MX12_ANSWER_MAX_SIZE) return;
-        
-        ans_i += 2;
-        char mot_id = _res[ans_i++];
-        char len = _res[ans_i++];
-        _chksm = _res[ans_i + len - 1];
-        
-        // [TODO] Verify checksum
-        if(len != 0 && _chksm != 0) {
-            // Interpret answer depending on state
-            switch(_state) {
-                case State::ReadingPosition:
-                    _angle[mot_id] = (((uint16_t) _res[ans_i + 1] << 8) | (uint16_t) _res[ans_i]) * 0.088;
-                    _state = State::Available;
-                    break;
-                case State::Writing:
-                    // Return the corresponding status code
-                    switch(_res[ans_i]) {
-                        case 0:
-                            _status = Status::Ok;
-                            break;
-                        case 1 << 0:
-                            _status = Status::InputVoltageError;
-                            break;
-                        case 1 << 1:
-                            _status = Status::AngleLimitError;
-                            break;
-                        case 1 << 2:
-                            _status = Status::OverheatingError;
-                            break;
-                        case 1 << 3:
-                            _status = Status::RangeError;
-                            break;
-                        case 1 << 4:
-                            _status = Status::ChecksumError;
-                            break;
-                        case 1 << 5:
-                            _status = Status::OverloadError;
-                            break;
-                        case 1 << 6:
-                            _status = Status::InstructionError;
-                            break;
-                        default:
-                            _status = Status::Unknown;
-                    }
+	char c;
+	
+	// Try to read serial
+	if(_mx12.read(&c, 1)) {
+		_stored_frame[_frame_pointer++] = c;
+		_scontext.checksum += c;
+
+		// State-machine parsing
+		switch(_pstate) {
+			case Header:
+				if(++(_scontext.headingCount) >= 2) {
+					_scontext.headingCount = 0;
+					_pstate = Id;
+				}
+
+				_scontext.checksum -= c;
+				break;
+			
+			case Id:
+				_current_frame.motorId = c;
+				_pstate = Length;
+				break;
 
-                    _state = State::Available;
-                    break;
-                default:
-                    _status = Status::Unknown;
-            }
-        }
-    }
-}
\ No newline at end of file
+			case Length:
+				_current_frame.length = c - 1;
+				_pstate = Data;
+				break;
+
+			case Data:
+				_current_frame.data[_scontext.dataCount] = c;
+				
+				if(++(_scontext.dataCount) >= _current_frame.length) {
+					_scontext.dataCount = 0;
+					_pstate = Checksum;
+				}
+				break;
+			
+			case Checksum:
+				_current_frame.valid = (_scontext.checksum == 0xFF);
+				_scontext.checksum = 0;
+				_pstate = Header;
+				if(_answer) _sstate = Idling;
+				_answer = 1;
+				break;
+		}
+	}
+}
--- a/MX12.h	Fri Jan 15 09:15:47 2021 +0000
+++ b/MX12.h	Fri Apr 16 14:52:24 2021 +0000
@@ -3,55 +3,70 @@
 
 #include "mbed.h"
 
-#define MX12_ANSWER_MAX_SIZE 32
-#define MX12_MOTOR_COUNT 16
+#define MX12_DATA_MAX_SIZE 256
+#define MX12_MAX_MOTOR_COUNT 16
 
 class MX12 {
 public:
-    enum Status {
-        InstructionError,
-        OverloadError,
-        ChecksumError,
-        RangeError,
-        OverheatingError,
-        AngleLimitError,
-        InputVoltageError,
-        Unknown,
-        Ok
-    };
-    
-    enum State {
-        ReadingPosition,
-        Writing,
-        Available,
-    };
+	enum Status {
+		InstructionError,
+		OverloadError,
+		ChecksumError,
+		RangeError,
+		OverheatingError,
+		AngleLimitError,
+		InputVoltageError,
+		Unknown,
+		Ok
+	};
+	
+	enum ParsingState {
+		Header,
+		Id,
+		Length,
+		Data,
+		Checksum,
+	};
 
-    MX12(PinName tx, PinName rx, int baud=115200);
-    
-    void SetSpeed(unsigned char mot_id, float speed);
-    char IsAvailable(void);
-    MX12::Status GetStatus(void);
-    void ReadPosition(unsigned char mot_id);
-    float GetPosition(unsigned char mot_id);
-    void PrintAnswer();
-    
-    void rw(unsigned char mot_id, char adress, char len, char *data);
-    
-    void _ReadCallback();
-    
-    char _chksm;
+	enum SerialState {
+		Writing,
+		Reading,
+		Idling,
+	};
+
+	struct Frame {
+		unsigned char motorId;
+		unsigned char length;
+		unsigned char data[MX12_DATA_MAX_SIZE];
+		unsigned char valid;
+	};
+
+	struct StateContext {
+		unsigned char headingCount;
+		unsigned char dataCount;
+		unsigned char checksum;
+	};
+
+	MX12(PinName tx, PinName rx, int baud=115200);
+	
+	void SetSpeed(unsigned char mot_id, float speed);
+	char IsAvailable(void);
+	void rw(unsigned char mot_id, char adress, char len, char *data);
+	void PrintSerial();
+	MX12::Status GetStatus(void);
 
 private:
-    UnbufferedSerial _mx12;
-    MX12::Status _status;
-    
-    char _res[MX12_ANSWER_MAX_SIZE];
-    char _res_count;
-    char _len;
-    
-    MX12::State _state;
-    float _angle[MX12_MOTOR_COUNT];
-    int _baud;
+	UnbufferedSerial _mx12;
+	MX12::ParsingState _pstate;
+	MX12::SerialState _sstate;
+	MX12::StateContext _scontext;
+	MX12::Frame _current_frame;
+
+	unsigned char _answer;
+	unsigned char _stored_frame[MX12_DATA_MAX_SIZE];
+	unsigned char _frame_pointer;
+
+	void _ReadCallback();
 };
 
-#endif /* MBED_MX12_H */
\ No newline at end of file
+#endif /* MBED_MX12_H */