PCA9629A component library
Revision 0:0c797805233b, committed 2014-09-12
- Comitter:
- nxp_ip
- Date:
- Fri Sep 12 08:10:19 2014 +0000
- Commit message:
- PCA9629 "A". The "A" version component library. Version 1.1.
Changed in this revision
PCA9629A.cpp | Show annotated file Show diff for this revision Revisions of this file |
PCA9629A.h | Show annotated file Show diff for this revision Revisions of this file |
diff -r 000000000000 -r 0c797805233b PCA9629A.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/PCA9629A.cpp Fri Sep 12 08:10:19 2014 +0000 @@ -0,0 +1,294 @@ +/** A sample code for PCA9629A + * + * @author Akifumi (Tedd) OKANO, NXP Semiconductors + * @version 1.1 + * @date 12-Sep-2012 + * + * revision history (PCA9629A) + * version 0.1 (05-Jun-2013) : PCA9629"A" initial version + * version 0.2 (05-Jun-2013) : register name fix, register description added + * version 1.0 (23-Apr-2014) : unnecessary parameter for constructor has been removed + * version 1.1 (12-Sep-2014) : constructor variation added. ramp parameter API added + * + * Released under the Apache 2 license License + * + * An operation sample of PCU9629A stepper motor controller. + * The mbed accesses the PCU9629A registers through I2C. + * + * About PCA9629A: + * http://www.nxp.com/products/interface_and_connectivity/i2c/i2c_bus_controller_and_bridge_ics/PCA9629APW.html + */ + +#include "mbed.h" +#include "PCA9629A.h" + +#define STEP_RESOLUTION 333333 // 1/(3e-6) = 333333 + +char *reg_name[] = { + "MODE", "WDTOI", "WDCNTL", "IO_CFG", + "INTMODE", "MSK", "INTSTAT", "IP", "INT_MTR_ACT", "EXTRASTEPS0", "EXTRASTEPS1", + "OP_CFG", "OP_STAT_PHS", + "RUCNTL", "RDCNTL", + "PMA", "LOOPDLY_CW", "LOOPDLY_CCW", + "CWSCOUNTL", "CWSCOUNTH", "CCWSCOUNTL", "CCWSCOUNTH", + "CWPWL", "CWPWH", "CCWPWL", "CCWPWH", + "MCNTL", + "SUBADR1", "SUBADR2", "SUBADR3", "ALLCALLADR", + "STEPCOUNT0", "STEPCOUNT1", "STEPCOUNT2", "STEPCOUNT3" +}; + +PCA9629A::PCA9629A( + PinName I2C_sda, + PinName I2C_scl, + char I2C_address +) : i2c_p( new I2C( I2C_sda, I2C_scl ) ), i2c( *i2c_p ), i2c_addr( I2C_address ) +{ + i2c.frequency( I2C_SCL_FREQUENCY ); + init_registers(); +} + +PCA9629A::PCA9629A( + I2C &i2c_, + char I2C_address +) : i2c_p( NULL ), i2c( i2c_ ), i2c_addr( I2C_address ) +{ + init_registers(); +} + +PCA9629A::~PCA9629A() +{ + if ( NULL != i2c_p ) + delete i2c_p; +} + +void PCA9629A::software_reset( void ) +{ + char v = 0x06; + + i2c.write( 0x00, &v, 1 ); + wait_ms( 1 ); +} + +void PCA9629A::init_registers( void ) +{ + char init_array[] = { 0x80, // register access start address (0x00) with incremental access flag (MSB) + 0x20, 0x0A, 0x00, 0x03, 0x13, 0x1C, // for registers MODE - MSK (0x00 - 0x07 + 0x00, 0x00, 0x68, 0x00, 0x00, // for registers INTSTAT - EXTRASTEPS1 (0x06, 0xA) + 0x10, 0x80, // for registers OP_CFG_PHS and OP_STAT_TO (0x0B - 0xC) + 0x09, 0x09, 0x01, 0x7D, 0x7D, // for registers RUCNTL - LOOPDLY_CCW (0xD- 0x10) + 0xFF, 0x01, 0xFF, 0x01, 0x05, 0x0D, 0x05, 0x0D, // for registers CWSCOUNTL - MCNTL (0x12 - 0x1A) + 0x20, // for register MCNTL (0x1A) + 0xE2, 0xE4, 0xE6, 0xE0 // for registers SUBADR1 - ALLCALLADR (0x1B - 0x1E) + }; + + set_all_registers( init_array, sizeof( init_array ) ); +} + +void PCA9629A::set_all_registers( char *a, char size ) +{ + int error_code; + + error_code = i2c.write( i2c_addr, a, size ); + + if ( error_code ) + error( "error @ initializing PCA9629A" ); +} + +void PCA9629A::write( RegisterName register_name, char value ) +{ + int error_code; + char cmd[ 2 ]; + + cmd[ 0 ] = register_name; + cmd[ 1 ] = value; + + error_code = i2c.write( i2c_addr, cmd, 2 ); + + if ( error_code ) + error( "PCA9629A writing failed\r\n" ); +} + +void PCA9629A::write( RegisterNameFor16bitAccess register_name, short value ) +{ + int error_code; + char cmd[ 3 ]; + + cmd[ 0 ] = register_name; + cmd[ 1 ] = value & 0xFF; + cmd[ 2 ] = value >> 8; + + error_code = i2c.write( i2c_addr, cmd, 3 ); + + if ( error_code ) + error( "PCA9629A writing failed\r\n" ); +} + +char PCA9629A::read( RegisterName register_name ) +{ + int error_code; + char cmd; + char data; + + cmd = register_name; + + error_code = i2c.write( i2c_addr, &cmd, 1, false ); + + if ( error_code ) + error( "PCA9629A reading (command phase) failed\r\n" ); + + error_code = i2c.read( i2c_addr, &data, 1 ); + + if ( error_code ) + error( "PCA9629A reading (data phase) failed\r\n" ); + + return ( data ); +} + +short PCA9629A::read( RegisterNameFor16bitAccess register_name ) +{ + int error_code; + char cmd; + char data[ 2 ]; + + cmd = register_name; + + error_code = i2c.write( i2c_addr, &cmd, 1, false ); + + if ( error_code ) + error( "PCA9629A reading (command phase) failed\r\n" ); + + error_code = i2c.read( i2c_addr, data, 2 ); + + if ( error_code ) + error( "PCA9629A reading (data phase) failed\r\n" ); + + return ( data[ 1 ] << 8 | data[ 0 ] ); +} + +long PCA9629A::read( RegisterNameFor32bitAccess register_name ) +{ + int error_code; + char cmd; + char data[ 4 ]; + + cmd = register_name | 0x80; + + error_code = i2c.write( i2c_addr, &cmd, 1, false ); + + if ( error_code ) + error( "PCA9629A reading (command phase) failed\r\n" ); + + error_code = i2c.read( i2c_addr, data, 4 ); + + if ( error_code ) + error( "PCA9629A reading (data phase) failed\r\n" ); + + printf( "0x %02X %02X %02X %02X\r\n", data[3], data[2], data[1], data[0] ); + + return ( data[ 3 ] << 24 | data[ 2 ] << 16 | data[ 1 ] << 8 | data[ 0 ] ); +} + +void PCA9629A::start( Direction dir ) +{ + write( MCNTL, 0xC0 | dir ); +} + +void PCA9629A::stop( void ) +{ + write( MCNTL, 0xA0 ); +} + + +short PCA9629A::pps( Direction dir, PrescalerRange prescaler, int pps ) +{ + int step_pulse_width; + + step_pulse_width = STEP_RESOLUTION / ((1 << prescaler) * pps); + + if ( step_pulse_width & 0xE000 ) { //error( "pps setting: out of range" ); + step_pulse_width = 0x1FFF; + printf( "the pps forced in to the range that user specified.. %fpps\r\n", (float)STEP_RESOLUTION / ((float)0x1FFF * (float)(1 << prescaler)) ); + } + if ( !step_pulse_width ) { //error( "pps setting: out of range" ); + step_pulse_width = 0x1; + printf( "the pps forced in to the range that user specified.. %fpps\r\n", (float)STEP_RESOLUTION / (float)(1 << prescaler) ); + } + + step_pulse_width |= (prescaler << 13); + + write( (dir == CW) ? CW__STEP_WIDTH : CCW_STEP_WIDTH, step_pulse_width ); + + return ( step_pulse_width ); +} + +short PCA9629A::pps( Direction dir, float pulse_per_second ) +{ + char p = 0; + char ratio; + + ratio = (char)(40.6901 / pulse_per_second); + + p = (ratio & 0x01) ? 1 : p; + p = (ratio & 0x02) ? 2 : p; + p = (ratio & 0x04) ? 3 : p; + p = (ratio & 0x08) ? 4 : p; + p = (ratio & 0x10) ? 5 : p; + p = (ratio & 0x20) ? 6 : p; + p = (ratio & 0x40) ? 7 : p; + + return ( pps( dir, (PrescalerRange)p, (int)pulse_per_second ) ); +} + +void PCA9629A::steps( Direction dir, int step_count ) +{ + write( (dir == CW) ? CW__STEP_COUNT : CCW_STEP_COUNT, step_count ); +} + +void PCA9629A::ramp_up( char rate ) +{ + write( RUCNTL, rate ? (0x30 | (rate -1)) : 0x00 ); +} + +void PCA9629A::ramp_down( char rate ) +{ + write( RDCNTL, rate ? (0x30 | (rate -1)) : 0x00 ); +} + +void PCA9629A::register_dump( void ) +{ + char data[ 0x23 ]; + char cmd = 0x80; + int i; + int j; + + i2c.write( i2c_addr, &cmd, 1 ); + i2c.read( i2c_addr, data, sizeof( data ) ); + + printf( "PCA9629A register dump\r\n" ); + + for ( i = 0, j = 0x14; i <= 0x12; i++, j++ ) + printf( " %-13s (0x%02X): 0x%02X %-13s (0x%02X): 0x%02X\r\n", reg_name[ i ], i, data[ i ], reg_name[ j ], j, data[ j ] ); + + printf( " %-13s (0x%02X): 0x%02X\r\n", reg_name[ 0x13 ], 0x13, data[ 0x13 ] ); +} + + +void PCA9629A::speed_change( unsigned short pw ) +{ +#ifdef MBED_PCA9629 + // this is for PCA9629(non-A version) + char cmd0[] = { PCA9629A::MCNTL, 0x00}; // data for stop the motor + char cmd1[] = { PCA9629A::CW__STEP_WIDTH, pw & 0xFF, pw >> 8 }; // data for rewrite pulse width + char cmd2[] = { PCA9629A::MCNTL, 0xB4}; // start // data for start again + wait_us(10); + + i2c.write( i2c_addr, cmd0, sizeof( cmd0 ), true ); // stop the motor + wait_us(50); + i2c.write( i2c_addr, cmd1, sizeof( cmd1 ), true ); // rewrite pulse width + i2c.write( i2c_addr, cmd2, sizeof( cmd2 ), false ); // start again +#else + char cmd1[] = { PCA9629A::CW__STEP_WIDTH, pw & 0xFF, pw >> 8 }; // data for rewrite pulse width + + i2c.write( i2c_addr, cmd1, sizeof( cmd1 ), true ); // rewrite pulse width +#endif +}
diff -r 000000000000 -r 0c797805233b PCA9629A.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/PCA9629A.h Fri Sep 12 08:10:19 2014 +0000 @@ -0,0 +1,340 @@ +/** A sample code for PCA9629A + * + * @author Akifumi (Tedd) OKANO, NXP Semiconductors + * @version 1.1 + * @date 12-Sep-2012 + * + * revision history (PCA9629A) + * version 0.1 (05-Jun-2013) : PCA9629"A" initial version + * version 0.2 (05-Jun-2013) : register name fix, register description added + * version 1.0 (23-Apr-2014) : unnecessary parameter for constructor has been removed + * version 1.1 (12-Sep-2014) : constructor variation added. ramp parameter API added + * + * Released under the Apache 2 license License + * + * An operation sample of PCA9629A stepper motor controller. + * The mbed accesses the PCA9629A registers through I2C. + * + * About PCA9629A: + * http://www.nxp.com/products/interface_and_connectivity/i2c/i2c_bus_controller_and_bridge_ics/PCA9629APW.html + */ + +#ifndef MBED_PCA9629A +#define MBED_PCA9629A + +#define DEFAULT_PCA9629A_ADDR 0x42 +#define I2C_SCL_FREQUENCY (400 * 1000) // I2C SCL clock frequency (for when the cunstructor with pin names is used) +#define ALLCALL_ADDR 0xE0 + + +/** PCA9629A class + * + * This is a driver code for the PCA9629A stepper motor controller. + * This class provides interface for PCA9629A operation and accessing its registers. + * Detail information is available on next URL. + * http://www.nxp.com/products/interface_and_connectivity/i2c/i2c_bus_controller_and_bridge_ics/PCA9629APW.html + * + * Example: + * @code + * #include "mbed.h" + * #include "PCA9629A.h" + * + * PCA9629A motor( p28, p27, 0x42 ); // SDA, SCL, I2C_address (option) + * + * int main() { + * + * // Speed setting 200pps for clockwise (CW) rotation + * motor.pps( PCA9629A::CW, 200 ); + * + * // Set 24 steps for CW + * motor.steps( PCA9629A::CW, 24 ); + * + * // Speed setting 100pps for counterclockwise (CCW) rotation + * motor.pps( PCA9629A::CCW, 100 ); + * + * // Set 48 steps for CCW + * motor.steps( PCA9629A::CCW, 24 ); + * + * while ( 1 ) { + * motor.start( PCA9629A::CW ); // Motor start for CW + * wait( 1.0 ); + * + * motor.start( PCA9629A::CCW ); // Motor start for CCW + * wait( 1.0 ); + * } + * } + * @endcode + */ + + +class PCA9629A +{ +public: + /** name of the PCA9629A registers */ + typedef enum { + MODE, /**< 0x00 Mode register */ + WDTOI, /**< 0x01 WatchDog Time-Out Interval register */ + WDCNTL, /**< 0x02 WatchDog Control register */ + IO_CFG, /**< 0x03 I/O Configuration register */ + INTMODE, /**< 0x04 Interrupt Mode register */ + MSK, /**< 0x05 Mask interrupt register */ + INTSTAT, /**< 0x06 Interrupt Status register */ + IP, /**< 0x07 Input Port register */ + INT_MTR_ACT, /**< 0x08 Interrupt motor action control register */ + EXTRASTEPS0, /**< 0x09 Extra steps count for INTP0 control register */ + EXTRASTEPS1, /**< 0x0A Extra steps count for INTP1 control register */ + OP_CFG_PHS, /**< 0x0B Output Port Configuration and Phase control register */ + OP_STAT_TO, /**< 0x0C Output state and time-out control register */ + RUCNTL, /**< 0x0D Ramp-up control register */ + RDCNTL, /**< 0x0E Ramp-down control register */ + PMA, /**< 0x0F Perform multiple of actions control register */ + LOOPDLY_CW, /**< 0x10 Loop delay timer for CW to CCW control register */ + LOOPDLY_CCW, /**< 0x11 Loop delay timer for CCW to CW control register */ + CWSCOUNTL, /**< 0x12 Number of clockwise steps register (low byte) */ + CWSCOUNTH, /**< 0x13 Number of clockwise steps register (high byte) */ + CCWSCOUNTL, /**< 0x14 Number of counter-clockwise steps register (low byte) */ + CCWSCOUNTH, /**< 0x15 Number of counter-clockwise steps register (high byte) */ + CWPWL, /**< 0x16 Clockwise step pulse width register (low byte) */ + CWPWH, /**< 0x17 Clockwise step pulse width register (high byte) */ + CCWPWL, /**< 0x18 Counter-clockwise step pulse width register (low byte) */ + CCWPWH, /**< 0x19 Counter-clockwise step pulse width register (high byte) */ + MCNTL, /**< 0x1A Motor control register */ + SUBADR1, /**< 0x1B I2C-bus subaddress 1 */ + SUBADR2, /**< 0x1C I2C-bus subaddress 2 */ + SUBADR3, /**< 0x1D I2C-bus subaddress 3 */ + ALLCALLADR, /**< 0x1E All Call I2C-bus address */ + STEPCOUNT0, /**< 0x1F Step counter registers: STEPCOUNT0 */ + STEPCOUNT1, /**< 0x20 Step counter registers: STEPCOUNT1 */ + STEPCOUNT2, /**< 0x21 Step counter registers: STEPCOUNT2 */ + STEPCOUNT3 /**< 0x22 Step counter registers: STEPCOUNT3 */ + } RegisterName; + +private: + /* register names for 2 bytes accessing */ + typedef enum { + CWPW_ = CWPWL | 0x80, /**< Step pulse width for CW rotation */ + CCWPW_ = CCWPWL | 0x80, /**< Step pulse width for CCW rotation */ + CWSCOUNT_ = CWSCOUNTL | 0x80, /**< Number of steps CW */ + CCWSCOUNT_ = CCWSCOUNTL | 0x80, /**< Number of steps CCW */ + + } _RegisterNameFor16bitAccess; + +public: + /** register names for 2 bytes accessing */ + typedef enum { + CW__STEP_WIDTH = CWPW_, + CCW_STEP_WIDTH = CCWPW_, + CW__STEP_COUNT = CWSCOUNT_, + CCW_STEP_COUNT = CCWSCOUNT_, + } RegisterNameFor16bitAccess; + + /** register names for 4 bytes accessing */ + typedef enum { + STEPCOUNT = STEPCOUNT0, + } RegisterNameFor32bitAccess; + + /** keyword to select direction of rotation */ + typedef enum { + CW = 0, /**< Clockwise direction */ + CCW /**< ConterClockwise direction */ + } Direction; + + /** Create a PCA9629A instance connected to specified I2C pins with specified address + * + * @param I2C_sda I2C-bus SDA pin + * @param I2C_scl I2C-bus SCL pin + * @param I2C_address I2C-bus address (default: 0x42) + */ + PCA9629A( + PinName I2C_sda, + PinName I2C_scl, + char I2C_address = DEFAULT_PCA9629A_ADDR + ); + + /** Create a PCA9629A instance connected to specified I2C pins with specified address + * + * @param I2C object (instance) + * @param I2C_address I2C-bus address (default: 0x42) + */ + PCA9629A( + I2C &i2c_, + char I2C_address = DEFAULT_PCA9629A_ADDR + ); + + /** Destractor + * + * + * + */ + ~PCA9629A(); + + /** Software reset + * + * Performs software reset through I2C bus + */ + void software_reset( void ); + + /** Initialize all registers + * + * The initializing values are defined in the function + */ + void init_registers( void ); + + /** Initialize all registers + * + * The initializing values are defined in the function + */ + void set_all_registers( char *a, char size ); + + /** Write 1 byte data into a register + * + * Setting 8 bits data into a register + * + * @param register_name the register name: data writing into + * @param value 8 bits writing data + */ + void write( RegisterName register_name, char value ); + + /** Write 2 bytes data into a register + * + * Setting 16 bits data into registers + * + * @param register_name the register name: data writing into (it can be "SROTN_", "CWPW_", "CCWPW_", "CWRCOUNT_", "CCWSCOUNT_", "CWRCOUNT_" or "CCWRCOUNT_" ) + * @param value 16 bits writing data + */ + void write( RegisterNameFor16bitAccess register_name, short value ); + + /** Read 1 byte data from a register + * + * Setting data into a register + * + * @param register_name the register name: data reading from + * @return read 8 bits data from the register + */ + char read( RegisterName register_name ); + + /** Read 2 byte data from registers + * + * Setting data into a register + * + * @param register_name the register name: data writing into (it can be "SROTN_", "CWPW_", "CCWPW_", "CWRCOUNT_", "CCWSCOUNT_", "CWRCOUNT_" or "CCWRCOUNT_" ) + * @return read 16 bits data from the registers + */ + short read( RegisterNameFor16bitAccess register_name ); + + /** Read 4 byte data from registers + * + * Setting data into a register + * + * @param register_name the register name: data writing into (it can be "STEPCOUNT") + * @return read 32 bits data from the registers + */ + long read( RegisterNameFor32bitAccess register_name ); + + /** Motor start + * + * Start command + * This function starts motor operation with hard-stop flag and rotation+step enabled, no repeat will be performed + * If custom start is required, use "write( PCA9629A::MCNTL, 0xXX )" to control each bits. + * + * @param dir rotate direction ("CW" or "CCW") + */ + void start( Direction dir ); + + /** Motor stop + * + * Stop command + * + */ + void stop( void ); + + /** Set PPS + * + * Setting PulsePerSecond + * This interface can be used to set CWPWx or CCWPWx registers + * + * @param dir rotate direction ("CW" or "CCW") + * @param pulse_per_second pps defineds pulse width for the motor. The pulse width will be 1/pps + * @return 16 bit data that what set to the CWPWx or CCWPWx registers + */ + short pps( Direction dir, float pulse_per_second ); + + /** Set steps count + * + * Setting step count + * This interfaces CWSCOUNTx and CCWSCOUNTx registers + * + * @param dir rotate direction ("CW" or "CCW") + * @param step_count sets number of steps with 16 bit value + */ + void steps( Direction dir, int step_count ); + + /** Ramp-up rate + * + * Setting ramp-up rate + * + * @param rate set acceleration ratio. From 0 to 13. 0 for disable. Bigger valure accelerate faster. + */ + void ramp_up( char rate ); + + /** Ramp-down rate + * + * Setting ramp-down rate + * + * @param rate set deceleration ratio. From 0 to 13. 0 for disable. Bigger valure decelerate faster. + */ + void ramp_down( char rate ); + + /** Register dump + * + * Dumping all register data to serial console + * + */ + void register_dump( void ); + + /** Register dump + * + * Dumping all register data to serial console + * + */ + void speed_change( unsigned short pw ); + + /** Register dump + * + * Dumping all register data to serial console + * + */ + void calibration( float coefficient ); + + +private: + /* plescaler range setting */ + typedef enum { + PRESCALER_FROM_40_TO_333333, /*< Prescaler range from 3us(333333pps) to 24.576ms(40 pps) */ + PRESCALER_FROM_20_TO_166667, /*< Prescaler range from 6us(166667pps) to 49.152ms(20 pps) */ + PRESCALER_FROM_10_TO_83333, /*< Prescaler range from 12us( 83333pps) to 98.304ms(10 pps) */ + PRESCALER_FROM_5_TO_41667, /*< Prescaler range from 24us( 41667pps) to 196.608ms( 5 pps) */ + PRESCALER_FROM_2_5_TO_20833, /*< Prescaler range from 48us( 20833pps) to 393.216ms( 2.5 pps) */ + PRESCALER_FROM_1_27_TO_10416, /*< Prescaler range from 96us( 10416pps) to 786.432ms( 1.27pps) */ + PRESCALER_FROM_0_64_TO_5208, /*< Prescaler range from 192us( 5208pps) to 1572.864ms( 0.64pps) */ + PRESCALER_FROM_0_32_TO_2604, /*< Prescaler range from 384us( 2604pps) to 3145.728ms( 0.32pps) */ + } PrescalerRange; + + /* Set PPS + * + * Setting PulsePerSecond + * This interface can be used to set CWPWx or CCWPWx registers + * + * @param dir rotate direction ("CW" or "CCW") + * @param prescaler prescaler setting (for 3 bits setting range from 0 to 0x7. See datasheet) + * @param pulse_per_second pps defineds pulse width for the motor. The pulse width will be 1/pps + * @return 16 bit data that what set to the CWPWx or CCWPWx registers + */ + short pps( Direction dir, PrescalerRange prescaler, int pulse_per_second ); + + I2C *i2c_p; + I2C &i2c; + char i2c_addr; +}; + +#endif // MBED_PCA9629A