PSL_2021 / servomotor_MX12_Lorenzo

Dependents:   PSL_ROBOT_lorenzo robot_lorenzo recepteur_mbed_os_6

MX12.cpp

Committer:
denis2nis
Date:
2021-11-04
Revision:
5:0cf54586a4be
Parent:
3:add8b050eb86
Child:
8:e74ef93ae660

File content as of revision 5:0cf54586a4be:

#include "MX12.h"

MX12::MX12(PinName tx, PinName rx, int baud) : _mx12(tx, rx) {
    // Serial configuration
    _mx12.baud(baud);
    _mx12.format(8, SerialBase::None, 1);
    _mx12.attach(callback(this, &MX12::_ReadCallback), SerialBase::RxIrq);
    
    // Internal defaults
    _sstate = SerialState::Idle;
    _pstate = ParsingState::Header;
}

void MX12::SetSpeed(unsigned char mot_id, float speed) {
    char data[2];
 
    // Speed absolute value
    int goal = (0x3ff * abs(speed));
 
    // Spin direction (CW is negative)
    if (speed < 0) {
        goal |= (0x1 << 10);
    }

    data[0] = goal & 0xff;
    data[1] = goal >> 8;
    
    // Send instruction
    _sstate = SerialState::Writing;
    rw(mot_id, CONTROL_TABLE_MOVING_SPEED, 2, data);
}

char MX12::IsAvailable(void) {
    return (_sstate == SerialState::Idle);
}

void MX12::rw(unsigned char mot_id, char address, char len, char *data) {
    
    /* Set variables for reception from servovotor */
    _answer  = 0;
    memset(_current_frame.data, 0, MX12_DATA_MAX_SIZE);
    
    /* Initialise instruction packet to forge.
     * Instruction Packet is the command data sent to the servomotor.
     *
     * |Header1|Header2|Packet ID|Length|Instruction|Param1...ParamN|Checksum|
     * |-------|-------|---------|------|-----------|---------------|--------|
     * |  0xFF |  0xFF |Packet ID|Length|Instruction|Param1...ParamN| CHKSUM |
     * | cmd[0]| cmd[1]|  cmd[2] |cmd[3]|  cmd[4]   |cmd[5]...      |        |
     *                  \__  ___/                    \_  _/ \__  __/
     *                     \/           |              \/      \/            |
     *                   mot_id         |           address   data           |
     *                                  |                     (len = N-1)    |
     *                                  \__________________  ________________/
     *                                                     \/
     *                                                  Length ( cmd[3] )
     */
    char packet[16];
    unsigned char packet_length;
    
    /* Initialise checksum to calculate
     * It is used to check if packet is damaged during communication. 
     * Status Checksum is calculated according to the following formula:
     *
     * Status Checksum = ~( ID + Length + Error + Parameter1 + … Parameter N )
     */
    char checksum = 0x00;
        
    /* header 1 = 0xFF (dynamixel protocol 1.0) */
    packet[0] = 0xff;
    
    /* header 2 = 0xFF (dynamixel protocol 1.0) */
    packet[1] = 0xff; 
    
     /* packet ID i.e. servomotor id (dynamixel protocol 1.0) */
    packet[2] = mot_id;
    checksum += packet[2];
    
    /* Guess instruction type. NULL for read, not NULL for write */
    if(data == NULL) // read instruction
    {
        /* byte length of the instruction: parameter and checksum field.   */
        /* for read instruction:   1 INSTR +                               */
        /*       2 PARAM (starting address, length of data) + 1 CHKSUM     */
        packet[3] = 4;
        checksum += packet[3];

        /* set write instruction */
        packet[4] = PROTOCOL_INSTRUCTION_READ;
        checksum += packet[4];
        
        /* Param 1: address to read in the Control Table of RAM Area */
        packet[5] = address;
        checksum += packet[5];
        
        /* Param 2: number of bytes to read in the Control Table of RAM Area */
        packet[6] = len;
        checksum += packet[6];
        
        /* Checksum = ~( ID + Length + Instruction + Param1 + … Param N ) */
        packet[7] = ~checksum;
        
        packet_length = 8;
    }
    else // write instruction
    {
        /* byte length of the instruction: parameter and checksum field     */
        /* For write instruction:   1 INSTR +                               */
        /*       (1+len)PARAM (starting Address, bytes to write) + 1 CHKSUM */
        packet[3] = 3 + len; 
        checksum += packet[3];

        /* set read instruction */
        packet[4] = PROTOCOL_INSTRUCTION_WRITE;
        checksum += packet[4];

        /* Param 1: address to write in the "Control Table of RAM Area" */
        packet[5] = address;
        checksum += packet[5];
        
        /* Param 2 to N: data to write in the Control Table of RAM Area */
        for(char i = 0; i < len; i++) {
            packet[6 + i] = data[i];
            checksum += data[i];
        }
        
        /* Checksum = ~( ID + Length + Instruction + Param1 + … Param N ) */
        packet[6 + len] = ~checksum;
        
        packet_length = 7 + len;
    }
    
    // Send packet
    if(mot_id != 0xFE) {
        for(char i = 0; i < packet_length; i++) {
            _mx12.write(&packet[i], 1);
        }
    }
}

// Debug function to print Serial read
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;
    
    // Try to read serial
    if(_mx12.read(&c, 1)) {
        _stored_frame[_frame_pointer++] = c;
        //_res[_res_count] = c;
        //_res_count++;
        _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;
 
            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 = Idle;
                _answer = 1;
                break;
        }        
    }
}

/*

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];
}

*/