A library for the MD25 motor controller from www.robot-electronics.co.uk. The MD25 supports both I2C and UART, but this library is designed for use with I2C only.

MD25.cpp

Committer:
UBAL
Date:
2012-02-20
Revision:
0:a06e2ad45792

File content as of revision 0:a06e2ad45792:

/********************************************************************
 * @filename    MD25.cpp
 * @author      Thomas B. Joergensen (thomas.bal.jorgensen@gmail.com)
 * @date        06 FEB 2012
 * @version     1.0
 * @target      mbed (NXP LPC1768 - ARM Cortex M3 - 32-bit)
 *
 * @desciption  A library for interacting with the MD25 DC motor
 *              controller. Find more at:
 *              http://www.robot-electronics.co.uk/htm/md25tech.htm
 *              http://www.robot-electronics.co.uk/htm/md25i2c.htm
 *******************************************************************/

/* Includes */
#include "MD25.h"

/*** CONSTRUCTOR AND DESTRUCTOR ***/

MD25::MD25(I2C *i2c_interface, char i2c_address) {
    this->i2c_interface = i2c_interface;
    this->i2c_address = i2c_address;
    
    /* Default values */
    mode = 0;
}

MD25::~MD25(void) {};

/*** GENERIC METHODS ***/

/**
 * Write a number of bytes.
 *
 * @param reg_addr      Address of register to access.
 * @param data          Pointer to where data is stored.
 * @param bytes         Number of bytes to write.
 */
int MD25::write(char reg_addr, char *data, int bytes) {
    /* Variables */
    char data_out[bytes + 1];
    
    /* Add register address to char array */
    data_out[0] = reg_addr;
    
    /* Populate data array */
    for (int i = 1; i < bytes + 1; i++) {
        data_out[i] = data[i - 1];
    }
    
    /* Write address and data */
    if (i2c_interface->write(i2c_address + I2C_WRITE_BIT, &data_out[0], bytes + 1, true)) {
        return -1;
    }

    /* Return success */
    return 0;
}

/**
 * Read a number of bytes.
 *
 * @param reg_addr      Address of register to access.
 * @param data          Pointer to where data it to be stored.
 * @param bytes         Number of bytes to read.
 */
int MD25::read(char reg_addr, char *data, int bytes) {
    /* Setup register to read */
    if (!i2c_interface->write(i2c_address + I2C_WRITE_BIT, &reg_addr, 1, true)) {
        /* Read register */
        if (i2c_interface->read(i2c_address + I2C_READ_BIT, data, bytes, false)) {
            return -1;
        }
    } else {
        return -1;
    }

    /* Return success */
    return 0;
}

/**
 * Discover I2C devices.
 *
 * @return  char containing indicator of i2c device presence in form of bits
 *          i.e. if LSB of returned char is 1 then a device is present at I2C_START_ADDR.
 *          If bit 2 of char is 1 then a device is present at I2C_START_ADDR + 2.
 */
char MD25::doDiscover() {
    /* Initialize device char */
    char device = 0;
    
    /* Check for devices */
    for (int i = 0; i < 8; i++) {
        if (!i2c_interface->write(I2C_START_ADDR + (2 * i), NULL, 0)) { // 0 returned is ok
            device |= (1<<i);
        }
    }
        
    /* Return device char */
    return device;
}

/*** CONTROL METHODS ***/

/**
 * Set mode of operation.
 *
 * @param mode      Set mode of operation (0 - 3) (default: 0).
 */
int MD25::mode_set(int mode) {
    /* Check input is valid */
    if (mode < 0 || mode > 3) return -1;
    
    /* Variables */
    char data = (char)mode;
    
    /* Set mode */
    if (write(REG_MODE, &data, 1) == -1) return -1;
    
    /* Check set */
    data = mode_get();    
    if (data != mode) return -1;
    
    /* Set mode variable */
    this->mode = mode;
    
    /* Return success */
    return 0;
}

/**
 * Get mode of operation.
 */
int MD25::mode_get() {
    /* Variables */
    char data;
    
    /* Get mode */
    if (read(REG_MODE, &data, 1) == -1) return -1;
    
    /* Return mode */
    return data;
}

/**
 * Reset the encoder registers.
 */
int MD25::encoder_reset() {
    /* Variables */
    char data = CMD_ENCODER_RESET;

    /* Reset encoders */
    if (write(REG_COMMAND, &data, 1) == -1) return -1;
    
    /* Return success */
    return 0;
}

/**
 * Enable/disable automatic speed regulation (default: enabled).
 *
 * @param enabled   enable = 1 | disable = 0.
 */
int MD25::auto_speed_set(bool enabled) {
    /* Variables */
    char data;
    
    /* Set data */
    if (enabled) {
        data = CMD_AUTO_SPEED_ENABLE;
    } else {
        data = CMD_AUTO_SPEED_DISABLE;
    }

    /* Reset encoders */
    if (write(REG_COMMAND, &data, 1) == -1) return -1;

    /* Return success */
    return 0;
}

/**
 * Enable/disable 2 sec timeout of motors when no I2C comms (default: enabled).
 *
 * @param enabled   enable = 1 | disable = 0.
 */
int MD25::timeout_set(bool enabled) {
    /* Variables */
    char data;
    
    /* Set data */
    if (enabled) {
        data = CMD_TIMEOUT_ENABLE;
    } else {
        data = CMD_TIMEOUT_DISABLE;
    }

    /* Reset encoders */
    if (write(REG_COMMAND, &data, 1) == -1) return -1;

    /* Return success */
    return 0;
}

/**
 * Set a new I2C device address (Default: 0xB0).
 *
 * @param address   Address of device (B0 -> BE; inc: 2).
 */
int MD25::i2c_addr_set(char address) {
    /* Check input is valid */
    if (address < I2C_START_ADDR || address > I2C_START_ADDR + 0x0F) return -1;
    if (address % 2 == 1) return -1;
    
    /* Set data */
    char data[4];
    data[0] = CMD_CHANGE_I2C_ADDR_1;
    data[1] = CMD_CHANGE_I2C_ADDR_2;
    data[2] = CMD_CHANGE_I2C_ADDR_3;
    data[3] = address;
    
    /* Set new address */
    if (write(REG_COMMAND, &data[0], 4) == -1) return -1;
    
    /* Set address variable */
    i2c_address = address;
    
    /* Return success */
    return 0;
}

/*** DATA METHODS ***/

/**
 * Set the speed of motor 1. (Only mode 0 or 1).
 *
 * @param speed     Faster the higher a number (mode 0: 0 -> 255 | mode 1: -128 to 127).
 */
int MD25::speed1_set(int speed) {
    /* Check input is valid */
    if (mode == 0) {
        if (speed < 0 || speed >255) return -1;
    } else if (mode == 1) {
        if (speed < -128 || speed > 127) return -1;
    } else {
        return 0;
    }
    
    /* Variable */
    char data = (char)speed;
    
    /* Set data */
    if (mode == 1) data = (signed char)data;
    
    /* Set speed */
    if (write(REG_SPEED1, &data, 1) == -1) return -1;
    
    /* Return success */
    return 0;
}

/**
 * Get the set speed of motor 1. (Only mode 0 or 1).
 *
 * @return  Faster the higher a number (mode 0: 0 -> 255 | mode 1: -128 to 127).
 */
int MD25::speed1_get() {
    /* Check valid mode */
    if (mode < 0 || mode > 1) return -1;
    
    /* Variables */
    char data;
    
    /* Get speed */
    if (read(REG_SPEED1, &data, 1) == -1) return -1;
    
    /* Return speed */
    if (mode == 1) return (signed char)data;
    return data;
}

/**
 * Set the speed of motor 2. (Only mode 0 or 1).
 *
 * @param speed     Faster the higher a number (mode 0: 0 -> 255 | mode 1: -128 to 127).
 */
int MD25::speed2_set(int speed) {
    /* Check input is valid */
    if (mode == 0) {
        if (speed < 0 || speed >255) return -1;
    } else if (mode == 1) {
        if (speed < -128 || speed > 127) return -1;
    } else {
        return 0;
    }
    
    /* Variable */
    char data = (char)speed;
    
    /* Set data */
    if (mode == 1) data = (signed char)data;
    
    /* Set speed */
    if (write(REG_SPEED2, &data, 1) == -1) return -1;
    
    /* Return success */
    return 0;
}

/**
 * Get the set speed of motor 2. (Only mode 0 or 1).
 *
 * @return  Faster the higher a number (mode 0: 0 -> 255 | mode 1: -128 to 127).
 */
int MD25::speed2_get() {
    /* Check valid mode */
    if (mode < 0 || mode > 1) return -1;
    
    /* Variables */
    char data;
    
    /* Get speed */
    if (read(REG_SPEED2, &data, 1) == -1) return -1;
    
    /* Return speed */
    if (mode == 1) return (signed char)data;
    return data;
}

/**
 * Set the speed. (Only mode 2 or 3).
 *
 * @param speed     Faster the higher a number (mode 2: 0 -> 255 | mode 3: -128 to 127).
 */
int MD25::speed_set(int speed) {
    /* Check input is valid */
    if (mode == 2) {
        if (speed < 0 || speed > 255) return -1;
    } else if (mode == 3) {
        if (speed < -128 || speed > 127) return -1;
    } else {
        return 0;
    }
    
    /* Set data */
    char data = (char)speed;
    
    /* Set speed */
    if (write(REG_SPEED1, &data, 1) == -1) return -1;
    
    /* Return success */
    return 0;
}

/**
 * Get the set speed. (Only mode 2 or 3).
 *
 * @return  Faster the higher a number (mode 2: 0 -> 255 | mode 3: -128 to 127).
 */
int MD25::speed_get() {
    /* Check valid mode */
    if (mode < 2 || mode > 3) return -1;
    
    /* Variables */
    char data;
    
    /* Get speed */
    if (read(REG_SPEED1, &data, 1) == -1) return -1;
    
    /* Return speed */
    return data;
}

/**
 * Set the turn. (Only mode 2 or 3).
 *
 * @param turn      Faster the higher a number (mode 2: 0 -> 255 | mode 3: -128 to 127).
 */
int MD25::turn_set(int turn) {
    /* Check input is valid */
    if (mode == 2) {
        if (turn < 0 || turn > 255) return -1;
    } else if (mode == 3) {
        if (turn < -128 || turn > 127) return -1;
    } else {
        return 0;
    }
    
    /* Set data */
    char data = (char)turn;
    
    /* Set turn */
    if (write(REG_SPEED2, &data, 1) == -1) return -1;
    
    /* Return success */
    return 0;
}

/**
 * Get the set turn. (Only mode 2 or 3).
 *
 * @return  Faster the higher a number (mode 2: 0 -> 255 | mode 3: -128 to 127).
 */
int MD25::turn_get() {
    /* Check valid mode */
    if (mode < 2 || mode > 3) return -1;
    
    /* Variables */
    char data;
    
    /* Get turn */
    if (read(REG_SPEED2, &data, 1) == -1) return -1;
    
    /* Return turn */
    return data;
}

/**
 * Set the desired acceleration rate.
 *
 * if new speed > current speed:
 *      steps = (new speed - current speed) / acceleration register 
 * if new speed < current speed:
 *      steps = (current speed - new speed) / acceleration register
 * time = steps * 25ms 
 * Example: 
 *      Time/step: 25ms | Current speed: 0 | New speed: 255 | Steps: 255 | Acceleration time: 6.375s.
 * @param acceleration  Faster the higher a number (default: 5).
 */
int MD25::acceleration_set(int acceleration) {
    /* Check input is valid */    
    if (acceleration < 0 || acceleration > 255) return -1;
    
    /* Set data */
    char data = (char)acceleration;
    
    /* Set acceleration */
    if (write(REG_ACCELERATION_RATE, &data, 1) == -1) return -1;
    
    /* Return success */
    return 0;
}

/**
 * Get the set desired acceleration rate.
 *
 * if new speed > current speed:
 *      steps = (new speed - current speed) / acceleration register 
 * if new speed < current speed:
 *      steps = (current speed - new speed) / acceleration register
 * time = steps * 25ms 
 * Example: 
 *      Time/step: 25ms | Current speed: 0 | New speed: 255 | Steps: 255 | Acceleration time: 6.375s.
 * @return  Faster the higher a number (default: 5).
 */
int MD25::acceleration_get() {
    /* Variables */
    char data;
    
    /* Get acceleration */
    if (read(REG_ACCELERATION_RATE, &data, 1) == -1) return -1;
    
    /* Return acceleration */
    return data;
}

/**
 * Get encoder 1 position.
 */
int MD25::encoder1_get() {
    /* Variables */
    char data[4];
    int position;
    
    /* Get encoder bytes */
    if (read(REG_ENC1A, &data[0], 4) == -1) return -1;
    
    /* Combine the position bytes */
    position = (data[0]<<24) | (data[1]<<16) | (data[2]<<8) | data[3];
    
    /* Return position */
    return position;
}

/**
 * Get encoder 2 position.
 */
int MD25::encoder2_get() {
    /* Variables */
    char data[4];
    int position;
    
    /* Get encoder bytes */
    if (read(REG_ENC2A, &data[0], 4) == -1) return -1;
    
    /* Combine the position bytes */
    position = (data[0]<<24) | (data[1]<<16) | (data[2]<<8) | data[3];
    
    /* Return position */
    return position;
}

/**
 * Get battery voltage.
 * It reads 10 times the voltage i.e. 121 for 12.1V.
 *
 * @return  Normalized float.
 */
float MD25::bat_voltage_get() {
    /* Variables */
    char data;
    
    /* Get battery voltages */
    if (read(REG_BATTERY_VOLTS, &data, 1) == -1) return -1;
    
    /* Return battery voltage */
    return (float)(data / 10.0);
}

/**
 * Get current of motor 1.
 * It reads 10 times the current i.e. 25 for 2.5A.
 *
 * @return  Normalized float.
 */
float MD25::motor1_current_get() {
    /* Variables */
    char data;
    
    /* Get motor current */
    if (read(REG_MOTOR1_CURRENT, &data, 1) == -1) return -1;
    
    /* Return motor current */
    return (float)(data / 10.0);
}

/**
 * Get current of motor 2.
 * It reads 10 times the current i.e. 25 for 2.5A.
 *
 * @return  Normalized float.
 */
float MD25::motor2_current_get() {
    /* Variables */
    char data;
    
    /* Get motor current */
    if (read(REG_MOTOR2_CURRENT, &data, 1) == -1) return -1;
    
    /* Return motor current */
    return (float)(data / 10.0);
}

/**
 * Get the software revision number in the PIC16F873 controller.
 */
int MD25::software_rev_num_get() {
    /* Variables */
    char data;
    
    /* Get software revision number */
    if (read(REG_SOFTWARE_REVISION, &data, 1) == -1) return -1;
    
    /* Return software revision number */
    return data;
}