Library for MAX14871 Shield, MAXREFDES89#

Dependencies:   MAX5387 MAX7300

Dependents:   MAXREFDES89_MAX14871_Shield_Demo MAXREFDES89_Test_Program Line_Following_Bot Line_Following_Bot_Pololu

MAXREFDES89# Component Page

max14871_shield.cpp

Committer:
j3
Date:
2016-05-13
Revision:
6:dc06cc75c1c8
Parent:
5:a206f6505109
Child:
7:c0bee1397f3e

File content as of revision 6:dc06cc75c1c8:

/******************************************************************//**
* @file max14871_shield.cpp
* Copyright (C) 2015 Maxim Integrated Products, Inc., All Rights Reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included
* in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL MAXIM INTEGRATED BE LIABLE FOR ANY CLAIM, DAMAGES
* OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*
* Except as contained in this notice, the name of Maxim Integrated
* Products, Inc. shall not be used except as stated in the Maxim Integrated
* Products, Inc. Branding Policy.
*
* The mere transfer of this software does not imply any licenses
* of trade secrets, proprietary technology, copyrights, patents,
* trademarks, maskwork rights, or any other form of intellectual
* property whatsoever. Maxim Integrated Products, Inc. retains all
* ownership rights.
**********************************************************************/


#include "max14871_shield.h"

#define MIN_PERIOD (0.00002f) //50KHz

//Motor Driver control inputs
#define MD_EN    (0x01)
#define MD_DIR   (0x02)
#define MD_MODE0 (0x04)
#define MD_MODE1 (0x08)

#define MAX_VREF (2.0f)

//GPIO Expander Default Configurations
#define MAX7300_ALL_OUTPUTS    (0x55)
#define MAX7300_ALL_INPUTS     (0xFF)
#define MAX7300_OUTPUT_DEFAULT (0xBB)


//*********************************************************************
Max14871_Shield::Max14871_Shield(I2C *i2c_bus, bool default_config): _p_i2c(i2c_bus)
{
    _i2c_owner = false;
    
    if(default_config)
    {
        _p_io_expander = new Max7300(_p_i2c, Max7300::MAX7300_I2C_ADRS0);
        _p_digi_pot1 = new Max5387(_p_i2c, Max5387::MAX5387_I2C_ADRS0);
        _p_digi_pot2 = new Max5387(_p_i2c, Max5387::MAX5387_I2C_ADRS1);
        
        _p_pwm1 = new PwmOut(D4);
        _p_pwm2 = new PwmOut(D5);
        _p_pwm3 = new PwmOut(D9);
        _p_pwm4 = new PwmOut(D10);
    }
    else
    {
        _p_io_expander = new Max7300(_p_i2c, Max7300::MAX7300_I2C_ADRS1);
        _p_digi_pot1 = new Max5387(_p_i2c, Max5387::MAX5387_I2C_ADRS2);
        _p_digi_pot2 = new Max5387(_p_i2c, Max5387::MAX5387_I2C_ADRS3);
        
        _p_pwm1 = new PwmOut(D3);
        _p_pwm2 = new PwmOut(D6);
        _p_pwm3 = new PwmOut(D8);
        _p_pwm4 = new PwmOut(D11);
    }
    
    init_board();
}


//*********************************************************************
Max14871_Shield::Max14871_Shield(PinName sda, PinName scl, bool default_config)
{
     _p_i2c = new I2C(sda, scl);
    _i2c_owner = true;
    
    if(default_config)
    {
        _p_io_expander = new Max7300(_p_i2c, Max7300::MAX7300_I2C_ADRS0);
        _p_digi_pot1 = new Max5387(_p_i2c, Max5387::MAX5387_I2C_ADRS0);
        _p_digi_pot2 = new Max5387(_p_i2c, Max5387::MAX5387_I2C_ADRS1);
        
        _p_pwm1 = new PwmOut(D4);
        _p_pwm2 = new PwmOut(D5);
        _p_pwm3 = new PwmOut(D9);
        _p_pwm4 = new PwmOut(D10);
    }
    else
    {
        _p_io_expander = new Max7300(_p_i2c, Max7300::MAX7300_I2C_ADRS1);
        _p_digi_pot1 = new Max5387(_p_i2c, Max5387::MAX5387_I2C_ADRS2);
        _p_digi_pot2 = new Max5387(_p_i2c, Max5387::MAX5387_I2C_ADRS3);
        
        _p_pwm1 = new PwmOut(D3);
        _p_pwm2 = new PwmOut(D6);
        _p_pwm3 = new PwmOut(D8);
        _p_pwm4 = new PwmOut(D11);
    }
    
    init_board();
}


//*********************************************************************
Max14871_Shield::~Max14871_Shield()
{
    if(_i2c_owner) 
    {
        delete _p_i2c;
    }
    
    delete _p_io_expander;
    delete _p_digi_pot1;
    delete _p_digi_pot2;
    delete _p_pwm1;
    delete _p_pwm2;
    delete _p_pwm3;
    delete _p_pwm4;
}


//*********************************************************************
int16_t Max14871_Shield::set_operating_mode(max14871_motor_driver_t md, 
                                            max14871_operating_mode_t mode)
{
    int16_t result = 0;
    int16_t port_data;
    
    Max7300::max7300_port_number_t low_port;
    
    //determine the low port of an 8 bit register to read/write 
    if(md < MD3)
    {
        low_port = Max7300::MAX7300_PORT_04;
    }
    else
    {
        low_port = Max7300::MAX7300_PORT_12;
    }
    
    //get current state of outputs
    port_data = _p_io_expander->read_8_ports(low_port);

    switch(mode)
    {
        //if(md % 2) for following cases, modify control bits
        //of odd motor driver

        case COAST:
            if(md % 2)
            {
                port_data |= MD_EN;
            }
            else
            {
                port_data |= (MD_EN << 4);
            }
            
            set_pwm_duty_cycle(md, 0.0f);
        break;
        
        case BRAKE:
            if(md % 2)
            {
                port_data &= ~MD_EN;
            }
            else
            {
                port_data &= ~(MD_EN << 4);
            }
            
            set_pwm_duty_cycle(md, 0.0f);
        break;
        
        case REVERSE:
            if(md % 2)
            {
                port_data &= ~(MD_EN + MD_DIR);
            }
            else
            {
                port_data &= ~((MD_EN + MD_DIR) << 4);
            }
        break;
        
        case FORWARD:
            if(md % 2)
            {
                port_data &= ~MD_EN;
                port_data |= MD_DIR;
            }
            else
            {
                port_data &= ~(MD_EN << 4);
                port_data |= (MD_DIR << 4);
            } 
        break;
        
        default:
            result = -1;
        break;
    }
    
    if(!result)
    {
        //write data back to port
        result = _p_io_expander->write_8_ports(low_port, (uint8_t) port_data);
        
        if(!result)
        {
            _motor_data_array[(md - 1)].op_mode = mode;
        }
    }

    return result;
}


//*********************************************************************
int16_t Max14871_Shield::set_current_regulation_mode(max14871_motor_driver_t md, 
                                                     max14871_current_regulation_mode_t mode,
                                                     float vref)
{
    int16_t result = 0;
    int16_t port_data;
    uint8_t local_vref = 0;
    
    Max7300::max7300_port_number_t low_port;
    Max5387 *p_digi_pot;
    
    if(vref > MAX_VREF)
    {
        vref = MAX_VREF;
    }
    local_vref = ((uint8_t) ((vref * 255.0f) / 3.3f));
    
    //determine the low port of an 8 bit register to read/write 
    //and digipot associated with motor driver
    if(md < MD3)
    {
        low_port = Max7300::MAX7300_PORT_04;
        p_digi_pot = _p_digi_pot1;
    }
    else
    {
        low_port = Max7300::MAX7300_PORT_12;
        p_digi_pot = _p_digi_pot2;
    }
    
    //get current state of outputs
    port_data = _p_io_expander->read_8_ports(low_port);
        
    switch(mode)
    {
        case RIPPLE_25_INTERNAL_REF:
            if(md % 2)
            {
                port_data &= ~MD_MODE0;
                port_data |= MD_MODE1;
                p_digi_pot->write_ch_A(0);
            }
            else
            {
                port_data &= ~(MD_MODE0 << 4);
                port_data |= (MD_MODE1 << 4);
                p_digi_pot->write_ch_B(0);
            }
        break;
        
        case RIPPLE_25_EXTERNAL_REF:
            if(md % 2)
            {
                port_data &= ~MD_MODE0;
                port_data |= MD_MODE1;
                p_digi_pot->write_ch_A(local_vref);
            }
            else
            {
                port_data &= ~(MD_MODE0 << 4);
                port_data |= (MD_MODE1 << 4);
                p_digi_pot->write_ch_B(local_vref);
            }
        break;
        
        case TCOFF_FAST_INTERNAL_REF:
            if(md % 2)
            {
                port_data |= (MD_MODE1 + MD_MODE0);
                p_digi_pot->write_ch_A(0);
            }
            else
            {
                port_data |= ((MD_MODE1 + MD_MODE0) << 4);
                p_digi_pot->write_ch_B(0);
            }
        break;
        
        case TCOFF_SLOW_INTERNAL_REF:
            if(md % 2)
            {
                port_data |= MD_MODE0;
                port_data &= ~MD_MODE1;
                p_digi_pot->write_ch_A(0);
            }
            else
            {
                port_data |= (MD_MODE0 << 4);
                port_data &= ~(MD_MODE1 << 4);
                p_digi_pot->write_ch_B(0);
            }
        break;
        
        case TCOFF_FAST_EXTERNAL_REF:
            if(md % 2)
            {
                port_data |= (MD_MODE1 + MD_MODE0);
                p_digi_pot->write_ch_A(local_vref);
            }
            else
            {
                port_data |= ((MD_MODE1 + MD_MODE0) << 4);
                p_digi_pot->write_ch_B(local_vref);
            }
        break;
        
        case TCOFF_SLOW_EXTERNAL_REF:
            if(md % 2)
            {
                port_data |= MD_MODE0;
                port_data &= ~MD_MODE1;
                p_digi_pot->write_ch_A(local_vref);
            }
            else
            {
                port_data |= (MD_MODE0 << 4);
                port_data &= ~(MD_MODE1 << 4);
                p_digi_pot->write_ch_B(local_vref);
            }
        break;
        
        default:
            result = -1;
        break;
    }
    
    if(!result)
    {
        //write data back to port
        result = _p_io_expander->write_8_ports(low_port, (uint8_t) port_data);
        
        if(!result)
        {
            _motor_data_array[(md - 1)].i_reg_mode = mode;
            _motor_data_array[(md - 1)].v_ref = vref;
        }
    }
    
    return result;
}


//*********************************************************************
int16_t Max14871_Shield::set_pwm_channel(max14871_motor_driver_t md, PinName ch)
{
    
    int16_t result = 0;
    float pwm_duty_cycle;
    float pwm_period;
    
    
    if((ch != D3) && (ch != D4) && (ch != D5) && (ch != D6) &&
       (ch != D8) && (ch != D9) && (ch != D10) && (ch != D11))
    {
        result = -1;
    }
    else
    {        
        switch(md)
        {
            case MD1:
                if((ch != D3) && (ch != D4))
                {
                    result = -1;
                }
                else
                {
                    pwm_duty_cycle = get_pwm_duty_cycle(md);
                    pwm_period = get_pwm_period(md);
                    
                    _p_pwm1->pulsewidth_us(0);
                    
                    delete _p_pwm1;
                    _p_pwm1 = new PwmOut(ch);
                    
                    set_pwm_period(md, pwm_period);
                    set_pwm_duty_cycle(md, pwm_duty_cycle);
                }
            break;
            
            case MD2:
                if((ch != D5) && (ch != D6))
                {
                    result = -1;
                }
                else
                {
                    pwm_duty_cycle = get_pwm_duty_cycle(md);
                    pwm_period = get_pwm_period(md);
                    
                    _p_pwm2->pulsewidth_us(0);
                    
                    delete _p_pwm2;
                    _p_pwm2 = new PwmOut(ch);
                    
                    set_pwm_period(md, pwm_period);
                    set_pwm_duty_cycle(md, pwm_duty_cycle);
                }
            break;
            
            case MD3:
                if((ch != D8) && (ch != D9))
                {
                    result = -1;
                }
                else
                {
                    pwm_duty_cycle = get_pwm_duty_cycle(md);
                    pwm_period = get_pwm_period(md);
                    
                    _p_pwm3->pulsewidth_us(0);
                    
                    delete _p_pwm3;
                    _p_pwm3 = new PwmOut(ch);
                    
                    set_pwm_period(md, pwm_period);
                    set_pwm_duty_cycle(md, pwm_duty_cycle);
                }
            break;
            
            case MD4:
                if((ch != D10) && (ch != D11))
                {
                    result = -1;
                }
                else
                {
                    pwm_duty_cycle = get_pwm_duty_cycle(md);
                    pwm_period = get_pwm_period(md);
                    
                    _p_pwm4->pulsewidth_us(0);
                    
                    delete _p_pwm4;
                    _p_pwm4 = new PwmOut(ch);
                    
                    set_pwm_period(md, pwm_period);
                    set_pwm_duty_cycle(md, pwm_duty_cycle);
                }
            break;
            
            default:
                result = -1;
            break;
        }
    }
    
    return result;
}


//*********************************************************************
int16_t Max14871_Shield::set_pwm_period(max14871_motor_driver_t md, float period)
{
    int16_t result = 0;
    
    if(period < MIN_PERIOD)
    {
        result = -1;
    }
    else
    {
        switch(md)
        {
            case MD1:
                _p_pwm1->period(period);
                _motor_data_array[(md - 1)].period = period;
            break;
            
            case MD2:
                _p_pwm2->period(period);
                _motor_data_array[(md - 1)].period = period;
            break;
            
            case MD3:
                _p_pwm3->period(period);
                _motor_data_array[(md - 1)].period = period;
            break;
            
            case MD4:
                _p_pwm4->period(period);
                _motor_data_array[(md - 1)].period = period;
            break;
            
            default:
                result = -1;
            break;
        }
    }
    
    return result;
}


//*********************************************************************
int16_t Max14871_Shield::set_pwm_duty_cycle(max14871_motor_driver_t md, float duty_cycle)
{
    int16_t result = 0;
    
    switch(md)
    {
        case MD1:
            _p_pwm1->write(duty_cycle);
            _motor_data_array[(md - 1)].duty_cycle = duty_cycle;
        break;
        
        case MD2:
            _p_pwm2->write(duty_cycle);
            _motor_data_array[(md - 1)].duty_cycle = duty_cycle;
        break;
        
        case MD3:
            _p_pwm3->write(duty_cycle);
            _motor_data_array[(md - 1)].duty_cycle = duty_cycle;
        break;
        
        case MD4:
            _p_pwm4->write(duty_cycle);
            _motor_data_array[(md - 1)].duty_cycle = duty_cycle;
        break;
        
        default:
            result = -1;
        break;
    }
    
    return result;
}


//*********************************************************************
Max14871_Shield::max14871_operating_mode_t Max14871_Shield::get_operating_mode(max14871_motor_driver_t md)
{
    return(_motor_data_array[(md - 1)].op_mode);
}


//*********************************************************************
Max14871_Shield::max14871_current_regulation_mode_t Max14871_Shield::get_current_regulation_mode(max14871_motor_driver_t md)
{
    return(_motor_data_array[(md - 1)].i_reg_mode);
}


//*********************************************************************
float Max14871_Shield::get_pwm_duty_cycle(max14871_motor_driver_t md)
{
    return(_motor_data_array[(md - 1)].duty_cycle);
}


//*********************************************************************
float Max14871_Shield::get_pwm_period(max14871_motor_driver_t md)
{
    return(_motor_data_array[(md - 1)].period);
}


//*********************************************************************
float Max14871_Shield::get_external_voltage_ref(max14871_motor_driver_t md)
{
    return(_motor_data_array[(md - 1)].v_ref);
}
      

//*********************************************************************
void Max14871_Shield::init_board(void)
{
    //configure these ports as outputs
    _p_io_expander->config_4_ports(Max7300::MAX7300_PORT_04, MAX7300_ALL_OUTPUTS);
    _p_io_expander->config_4_ports(Max7300::MAX7300_PORT_08, MAX7300_ALL_OUTPUTS);
    _p_io_expander->config_4_ports(Max7300::MAX7300_PORT_12, MAX7300_ALL_OUTPUTS);
    _p_io_expander->config_4_ports(Max7300::MAX7300_PORT_16, MAX7300_ALL_OUTPUTS);
    
    //Set /EN and DIR pin of all motor drivers and set mode pin 
    //of all motor drivers to 0.75V
    _p_io_expander->write_8_ports(Max7300::MAX7300_PORT_04, MAX7300_OUTPUT_DEFAULT);
    _p_io_expander->write_8_ports(Max7300::MAX7300_PORT_12, MAX7300_OUTPUT_DEFAULT);
    
    //configure these ports as inputs w/pull-up, 
    _p_io_expander->config_4_ports(Max7300::MAX7300_PORT_20, MAX7300_ALL_INPUTS);
    _p_io_expander->config_4_ports(Max7300::MAX7300_PORT_24, MAX7300_ALL_INPUTS);
    _p_io_expander->config_4_ports(Max7300::MAX7300_PORT_28, MAX7300_ALL_INPUTS);
    
    //config port 31 as output for interrupt
    _p_io_expander->config_port(Max7300::MAX7300_PORT_31, Max7300::MAX7300_PORT_OUTPUT);
    
    _p_io_expander->enable_transition_detection();
    _p_io_expander->enable_ports();
    
    //set Vref pin of all motor drivers to 0V, 
    //internal vref used for current regulation
    _p_digi_pot1->write_ch_AB(0);
    _p_digi_pot2->write_ch_AB(0);
    
    //set switching frequency of all motor drivers to 50KHz
    _p_pwm1->period(MIN_PERIOD);  
    _p_pwm2->period(MIN_PERIOD);
    _p_pwm3->period(MIN_PERIOD);
    _p_pwm4->period(MIN_PERIOD);
    
    //init motor data to defaults
    for(uint8_t idx = 0; idx < 4; idx++)
    {
        _motor_data_array[idx].op_mode = COAST;
        _motor_data_array[idx].i_reg_mode = RIPPLE_25_INTERNAL_REF;
        _motor_data_array[idx].duty_cycle = 0.0f;
        _motor_data_array[idx].period = MIN_PERIOD;
        _motor_data_array[idx].v_ref = 0.0f;
    }
}