demo sample to drive PCU9955 and PCA9629

Dependencies:   mbed I2C_slaves PCU9669 parallel_bus

Fork of mini_board_PCU9669 by InetrfaceProducts NXP

What is this?

This is a sample code to operate PCU9955 (16ch constant-current LED controller) and PCA9629 (intelligent stepper motor controller) through PCU9669 (3 channels (UltraFast mode * 2ch, FastModePlus *1ch) I2C bus controller).

This demo is written based on mini_board_PCU9669 sample code library and its API.
http://mbed.org/users/nxp_ip/code/mini_board_PCU9669/

Demo will shows how the LED controllers and stepper motor controllers works.
It uses a mini_board_PCU9669 board with mbed, 8 of PCU9955s and 5 PCA9629s.

/media/uploads/nxp_ip/dsc_0414ss.png
Demo setup
(left-top: PCU9955 boards, left-bottom: mini-board PCU9669 with mbed, right: PCA9629 x5 board)

/media/uploads/nxp_ip/demo-config-ss.png
Board connections and device addresses

Reference:

User manual of PCU9669 demo board: Mini board PCU9669

http://www.nxp.com/documents/user_manual/UM10580.pdf

sample code : mbed programs

Import programmini_board_PCU9669

mini board PCU9669 (and PCA9665) sample code

Import programPCA9955_Hello

PCA9955 16 channel current drive(sink) LED driver sample code

Import programPCA9955_simple

very simple sample code for PCA9955 (16 channel current control LED driver)

Import programPCA9629_Hello

Sample code for PCA9629 operation

device infomation

PCU9669 (Parallel bus to 1 channel Fm+ and 2 channel UFm I2C-bus controller)
PCU9955 (16-channel UFm I²C-bus 57 mA constant current LED driver)
PCA9955 (16-channel Fm+ I²C-bus 57 mA constant current LED driver)
PCU9629 (Fm+ I2C-bus stepper motor controller)

demo_patterns.cpp

Committer:
nxp_ip
Date:
2012-10-26
Revision:
20:a266fa588bd8
Child:
21:3b75b545ecfb

File content as of revision 20:a266fa588bd8:

#include "demo_patterns.h"
#include "transfer_manager.h"
#include "PCx9955_reg.h"
#include "PCA9629_reg.h"
#include "PCU9669_access.h" //  PCU9669 chip access interface

#include "mbed.h"

typedef struct  pattern_st {
    int     duration;
    void    (*pattern_func)( int count );
}
pattern;

char    gLEDs[ N_OF_LEDS ]  = { 0 };
int     gCount              = 0;

char    nine_step_intensity[]  = { 0x00, 0x01, 0x03, 0x07, 0x0F, 0x1F, 0x3F, 0x7F, 0xFF };
char    sixteen_step_intensity[]  = { 0x01, 0x03, 0x07, 0x0F, 0x1F, 0x3F, 0x7F, 0xFF, 0x7F, 0x3F, 0x1F, 0x0F, 0x07, 0x03, 0x01, 0x00 };

char     gMot_cntl_order[ 5 ]    = { MOT_ADDR5, MOT_ADDR6, MOT_ADDR7, MOT_ADDR8, MOT_ADDR9 };
char     gMot_cntl_order_halfturn[ 10 ]    = { MOT_ADDR5, 0, MOT_ADDR6, MOT_ADDR7, MOT_ADDR8, MOT_ADDR9, 0, MOT_ADDR8,  MOT_ADDR7, MOT_ADDR6 };
char     gMotor_count;

typedef enum {
    RED,
    GREEN,
    BLUE,
}
color;

char rgb_mask[ 3 ][ 15 ]  = {
    { 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0 },
    { 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0 },
    { 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0 }
};

char  led_init_array[] = {
    0x80,                //  Command
    0x00, 0x05,                                     //  MODE1, MODE2
    0xAA, 0xAA, 0xAA, 0xAA,                         //  LEDOUT[3:0]
    0x80, 0x00,                                     //  GRPPWM, GRPFREQ
    PWM_INIT,  PWM_INIT,  PWM_INIT,  PWM_INIT,  PWM_INIT,  PWM_INIT,  PWM_INIT,  PWM_INIT, //  PWM[7:0]
    PWM_INIT,  PWM_INIT,  PWM_INIT,  PWM_INIT,  PWM_INIT,  PWM_INIT,  PWM_INIT,  PWM_INIT, //  PWM[15:8]
    IREF_INIT, IREF_INIT, IREF_INIT, IREF_INIT, IREF_INIT, IREF_INIT, IREF_INIT, IREF_INIT, //  IREF[7:0]
    IREF_INIT, IREF_INIT, IREF_INIT, IREF_INIT, IREF_INIT, IREF_INIT, IREF_INIT, IREF_INIT, //  IREF[15:8]
    0x08                                            //  OFFSET: 1uS offsets
};

char  led_norm_op_array[] = {
    0x80 | PWM0,                //  Command
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,     //  PWM[7:0]
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,     //  PWM[14:8]
};

char    mot_init_array[] = {
    0x80,
    0x20, 0xE2, 0xE4, 0xE6, 0xE0, 0x02, 0x10,             //  for registers MODE - WDCNTL (0x00 - 0x06
    0x00, 0x00,                                           //  for registers IP and INTSTAT (0x07, 0x08)
    0x0F, 0x03, 0x0C, 0x0F, 0x03, 0x01, 0x01, 0x00, 0x01, //  for registers OP - INT_AUTO_CLR (0x09 - 0x11)
    0x00, 0x00, 0x30, 0x00, 0x82, 0x06, 0x82, 0x06,       //  for registers SETMODE - CCWPWH (0x12 - 0x19)
    0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF,       //  for registers CWSCOUNTL - CCWRCOUNTH (0x1A - 0x21)
    0x00, 0x00,                                           //  for registers EXTRASTEPS0 and EXTRASTEPS1 (0x22, 0x23)
    0x00, 0x00, 0x84                                      //  for registers RMPCNTL - MCNTL (0x24 - 0x26)
};

char    mot_all_stop[] = {
    0x26, 0x00
};

char    mot_all_start[] = {
    0x26, 0xBA
};

char    mot_ramp_array[] = {
    0x80,
    0x20, 0xE2, 0xE4, 0xE6, 0xE0, 0xFF, 0x10,             //  for registers MODE - WDCNTL (0x00 - 0x06
    0x00, 0x00,                                           //  for registers IP and INTSTAT (0x07, 0x08)
    0x0F, 0x03, 0x0C, 0x0F, 0x03, 0x00, 0x03, 0x03, 0x01, //  for registers OP - INT_AUTO_CLR (0x09 - 0x11)
    0x00, 0x00, 0x30, 0x00, 0x97, 0x04, 0x97, 0x04,       //  for registers SETMODE - CCWPWH (0x12 - 0x19)
    0x23, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,       //  for registers CWSCOUNTL - CCWRCOUNTH (0x1A - 0x21)
    0x0C, 0x0C,                                           //  for registers EXTRASTEPS0 and EXTRASTEPS1 (0x22, 0x23)
    0x37, 0x00, 0x88                                      //  for registers RMPCNTL - MCNTL (0x24 - 0x26)
};

char    mot_vib_array[] = {
    0x80,
    0x20, 0xE2, 0xE4, 0xE6, 0xE0, 0xFF, 0x10,             //  for registers MODE - WDCNTL (0x00 - 0x06
    0x00, 0x00,                                           //  for registers IP and INTSTAT (0x07, 0x08)
    0x0F, 0x03, 0x0C, 0x0F, 0x03, 0x00, 0x03, 0x03, 0x01, //  for registers OP - INT_AUTO_CLR (0x09 - 0x11)
    0x00, 0x00, 0x30, 0x00, 0x82, 0x06, 0x82, 0x06,       //  for registers SETMODE - CCWPWH (0x12 - 0x19)
    0x03, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00,       //  for registers CWSCOUNTL - CCWRCOUNTH (0x1A - 0x21)
    0x0C, 0x0C,                                           //  for registers EXTRASTEPS0 and EXTRASTEPS1 (0x22, 0x23)
    0x00, 0x00, 0xBA                                      //  for registers RMPCNTL - MCNTL (0x24 - 0x26)
};

//#define INTERVAL128MS
#ifdef INTERVAL128MS
char    mot_norm_op_array[] = {
    0x80,
    0x20, 0xE2, 0xE4, 0xE6, 0xE0, 0x02, 0x10,             //  for registers MODE - WDCNTL (0x00 - 0x06
    0x00, 0x00,                                           //  for registers IP and INTSTAT (0x07, 0x08)
    0x0F, 0x03, 0x0C, 0x0F, 0x03, 0x00, 0x01, 0x00, 0x01, //  for registers OP - INT_AUTO_CLR (0x09 - 0x11)
    0x00, 0x00, 0x30, 0x00, 0xF2, 0x06, 0xF2, 0x06,       //  for registers SETMODE - CCWPWH (0x12 - 0x19)
    0x30, 0x00, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00,       //  for registers CWSCOUNTL - CCWRCOUNTH (0x1A - 0x21)
    0x00, 0x00,                                           //  for registers EXTRASTEPS0 and EXTRASTEPS1 (0x22, 0x23)
    0x00, 0x00, 0x00                                      //  for registers RMPCNTL - MCNTL (0x24 - 0x26)
};

char    mot_half_array[] = {
    0x80,
    0x20, 0xE2, 0xE4, 0xE6, 0xE0, 0x02, 0x10,             //  for registers MODE - WDCNTL (0x00 - 0x06
    0x00, 0x00,                                           //  for registers IP and INTSTAT (0x07, 0x08)
    0x0F, 0x03, 0x0C, 0x0F, 0x03, 0x00, 0x01, 0x00, 0x01, //  for registers OP - INT_AUTO_CLR (0x09 - 0x11)
    0x00, 0x00, 0x30, 0x00, 0xF2, 0x06, 0xF2, 0x06,       //  for registers SETMODE - CCWPWH (0x12 - 0x19)
    0x18, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00,       //  for registers CWSCOUNTL - CCWRCOUNTH (0x1A - 0x21)
    0x00, 0x00,                                           //  for registers EXTRASTEPS0 and EXTRASTEPS1 (0x22, 0x23)
    0x00, 0x00, 0x00                                      //  for registers RMPCNTL - MCNTL (0x24 - 0x26)
};
#else
char    mot_norm_op_array[] = {
    0x80,
    0x20, 0xE2, 0xE4, 0xE6, 0xE0, 0x02, 0x10,             //  for registers MODE - WDCNTL (0x00 - 0x06
    0x00, 0x00,                                           //  for registers IP and INTSTAT (0x07, 0x08)
    0x0F, 0x03, 0x0C, 0x0F, 0x03, 0x00, 0x01, 0x00, 0x01, //  for registers OP - INT_AUTO_CLR (0x09 - 0x11)
    0x00, 0x00, 0x30, 0x00, 0xE4, 0x0D, 0xE4, 0x0D,       //  for registers SETMODE - CCWPWH (0x12 - 0x19)
    0x30, 0x00, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00,       //  for registers CWSCOUNTL - CCWRCOUNTH (0x1A - 0x21)
    0x00, 0x00,                                           //  for registers EXTRASTEPS0 and EXTRASTEPS1 (0x22, 0x23)
    0x00, 0x00, 0x00                                      //  for registers RMPCNTL - MCNTL (0x24 - 0x26)
};

char    mot_half_array[] = {
    0x80,
    0x20, 0xE2, 0xE4, 0xE6, 0xE0, 0x02, 0x10,             //  for registers MODE - WDCNTL (0x00 - 0x06
    0x00, 0x00,                                           //  for registers IP and INTSTAT (0x07, 0x08)
    0x0F, 0x03, 0x0C, 0x0F, 0x03, 0x00, 0x01, 0x00, 0x01, //  for registers OP - INT_AUTO_CLR (0x09 - 0x11)
    0x00, 0x00, 0x30, 0x00, 0xE4, 0x0D, 0xE4, 0x0D,       //  for registers SETMODE - CCWPWH (0x12 - 0x19)
    0x18, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00,       //  for registers CWSCOUNTL - CCWRCOUNTH (0x1A - 0x21)
    0x00, 0x00,                                           //  for registers EXTRASTEPS0 and EXTRASTEPS1 (0x22, 0x23)
    0x00, 0x00, 0x00                                      //  for registers RMPCNTL - MCNTL (0x24 - 0x26)
};
#endif


transaction     led_init_transfer[]   =   {
    { PCx9955_ADDR0, led_init_array, sizeof( led_init_array ) },
    { PCx9955_ADDR1, led_init_array, sizeof( led_init_array ) },
    { PCx9955_ADDR2, led_init_array, sizeof( led_init_array ) },
    { PCx9955_ADDR3, led_init_array, sizeof( led_init_array ) },
};

transaction     led_norm_op_transfer[]   =   {
    { PCx9955_ADDR0, led_norm_op_array, sizeof( led_norm_op_array ) },
    { PCx9955_ADDR1, led_norm_op_array, sizeof( led_norm_op_array ) },
    { PCx9955_ADDR2, led_norm_op_array, sizeof( led_norm_op_array ) },
    { PCx9955_ADDR3, led_norm_op_array, sizeof( led_norm_op_array ) },
};

transaction     mot_init_transfer[]   =   {
    { MOT_ADDR5, mot_init_array, sizeof( mot_init_array ) },
    { MOT_ADDR6, mot_init_array, sizeof( mot_init_array ) },
    { MOT_ADDR7, mot_init_array, sizeof( mot_init_array ) },
    { MOT_ADDR8, mot_init_array, sizeof( mot_init_array ) },
    { MOT_ADDR9, mot_init_array, sizeof( mot_init_array ) },
};

transaction     mot_norm_op_transfer[]   =   {
    { MOT_ADDR5, mot_norm_op_array, sizeof( mot_norm_op_array ) },
    { MOT_ADDR6, mot_norm_op_array, sizeof( mot_norm_op_array ) },
    { MOT_ADDR7, mot_norm_op_array, sizeof( mot_norm_op_array ) },
    { MOT_ADDR8, mot_norm_op_array, sizeof( mot_norm_op_array ) },
    { MOT_ADDR9, mot_norm_op_array, sizeof( mot_norm_op_array ) },
};

transaction     mot_half_transfer[]   =   {
    { MOT_ADDR5, mot_norm_op_array, sizeof( mot_norm_op_array ) },
    { MOT_ADDR6, mot_half_array, sizeof( mot_half_array ) },
    { MOT_ADDR7, mot_half_array, sizeof( mot_half_array ) },
    { MOT_ADDR8, mot_half_array, sizeof( mot_half_array ) },
    { MOT_ADDR9, mot_norm_op_array, sizeof( mot_norm_op_array ) },
};

transaction     mot_half_setup_transfer[]   =   {
    { MOT_ADDR5, mot_half_array, sizeof( mot_half_array ) },
    { MOT_ADDR6, mot_half_array, sizeof( mot_half_array ) },
    { MOT_ADDR7, mot_half_array, sizeof( mot_half_array ) },
    { MOT_ADDR8, mot_half_array, sizeof( mot_half_array ) },
    { MOT_ADDR9, mot_half_array, sizeof( mot_half_array ) },
};

transaction     mot_all_stop_transfer[]   =   {
    { MOT_ADDR5, mot_all_stop, sizeof( mot_all_stop ) },
    { MOT_ADDR6, mot_all_stop, sizeof( mot_all_stop ) },
    { MOT_ADDR7, mot_all_stop, sizeof( mot_all_stop ) },
    { MOT_ADDR8, mot_all_stop, sizeof( mot_all_stop ) },
    { MOT_ADDR9, mot_all_stop, sizeof( mot_all_stop ) },
};

transaction     mot_all_start_transfer[]   =   {
    { MOT_ADDR5, mot_all_start, sizeof( mot_all_start ) },
    { MOT_ADDR6, mot_all_start, sizeof( mot_all_start ) },
    { MOT_ADDR7, mot_all_start, sizeof( mot_all_start ) },
    { MOT_ADDR8, mot_all_start, sizeof( mot_all_start ) },
    { MOT_ADDR9, mot_all_start, sizeof( mot_all_start ) },
};

transaction     mot_all_vib_transfer[]   =   {
    { MOT_ADDR5, mot_vib_array, sizeof( mot_vib_array ) },
    { MOT_ADDR6, mot_vib_array, sizeof( mot_vib_array ) },
    { MOT_ADDR7, mot_vib_array, sizeof( mot_vib_array ) },
    { MOT_ADDR8, mot_vib_array, sizeof( mot_vib_array ) },
    { MOT_ADDR9, mot_vib_array, sizeof( mot_vib_array ) },
};

transaction     mot_all_ramp_transfer[]   =   {
    { MOT_ADDR5, mot_ramp_array, sizeof( mot_ramp_array ) },
    { MOT_ADDR6, mot_ramp_array, sizeof( mot_ramp_array ) },
    { MOT_ADDR7, mot_ramp_array, sizeof( mot_ramp_array ) },
    { MOT_ADDR8, mot_ramp_array, sizeof( mot_ramp_array ) },
    { MOT_ADDR9, mot_ramp_array, sizeof( mot_ramp_array ) },
};

void init_slave_devices( void ) {
    //  init all slave's registers
    setup_transfer( CH_FM_PLUS, mot_init_transfer, sizeof( mot_init_transfer ) / sizeof( transaction ) );
    setup_transfer( CH_UFM1, led_init_transfer, sizeof( led_init_transfer ) / sizeof( transaction ) );
    setup_transfer( CH_UFM2, led_init_transfer, sizeof( led_init_transfer ) / sizeof( transaction ) );
    start( CH_FM_PLUS );    //  motors are aligned using interrupt input of PCA9629
    start( CH_UFM1 );
    start( CH_UFM2 );

    wait_sec( 0.5 );

    //  update motor control buffer configuration

    setup_transfer( CH_FM_PLUS, mot_norm_op_transfer, sizeof( mot_norm_op_transfer ) / sizeof( transaction ) );
    setup_transfer( CH_UFM1, led_norm_op_transfer, sizeof( led_norm_op_transfer ) / sizeof( transaction ) );
    setup_transfer( CH_UFM2, led_norm_op_transfer, sizeof( led_norm_op_transfer ) / sizeof( transaction ) );
    start( CH_FM_PLUS );    //  just update PCA9629 registers. no motor action
    start( CH_UFM1 );
    start( CH_UFM2 );
}

void single_byte_write_into_a_PCA9629( char ch, char i2c_addr, char reg_addr, char val ) {
    transaction     t;
    char            data[ 2 ];

    data[ 0 ]   = reg_addr;
    data[ 1 ]   = val;

    t.i2c_address   = i2c_addr;
    t.data          = data;
    t.length        = 2;

    setup_transfer( ch, &t, 1 );
    start( CH_FM_PLUS );
}

void ramp_all_PCA9629( void ) {
    setup_transfer( CH_FM_PLUS, mot_all_ramp_transfer, sizeof( mot_all_ramp_transfer ) / sizeof( transaction ) );
    start( CH_FM_PLUS );
}
void vib_all_PCA9629( void ) {
    setup_transfer( CH_FM_PLUS, mot_all_vib_transfer, sizeof( mot_all_vib_transfer ) / sizeof( transaction ) );
    start( CH_FM_PLUS );
}
void start_all_PCA9629( void ) {
    setup_transfer( CH_FM_PLUS, mot_all_start_transfer, sizeof( mot_all_start_transfer ) / sizeof( transaction ) );
    start( CH_FM_PLUS );
}
void stop_all_PCA9629( void ) {
    setup_transfer( CH_FM_PLUS, mot_all_stop_transfer, sizeof( mot_all_stop_transfer ) / sizeof( transaction ) );
    start( CH_FM_PLUS );
}
void align_all_PCA9629( void ) {
    setup_transfer( CH_FM_PLUS, mot_init_transfer, sizeof( mot_init_transfer ) / sizeof( transaction ) );
    start( CH_FM_PLUS );
}

void all_LEDs_value( char v ) {
    for ( int i = 0; i < N_OF_LEDS; i++ )
        gLEDs[ i ]  = v;
}

void all_LEDs_turn_off() {
    all_LEDs_value( 0x00 );
}

void all_LEDs_turn_on() {
    all_LEDs_value( 0xFF );
}

void pattern_rampup( int count ) {
    if ( count == 0 ) {
        all_LEDs_turn_off();
        return;
    }

    for ( int i = 0; i < N_OF_LEDS; i++ )
        gLEDs[ i ] = count & 0xFF;
}

void pattern_rampup_and_down( int count ) {
    if ( !(count & 0xFF) ) {
        ramp_all_PCA9629();
        all_LEDs_turn_off();
        return;
    }

    for ( int i = 0; i < N_OF_LEDS; i++ )
        gLEDs[ i ] = (((count & 0x80) ? ~count : count) << 1) & 0xFF;
}

void pattern_rampup_and_down_w_color( int count ) {
    static int color;

    color   = (!count || (3 <= color)) ? 0 : color;

    if ( !(count & 0xFF) ) {
        ramp_all_PCA9629();
        all_LEDs_turn_off();
        color++;
        return;
    }

    for ( int i = 0; i < N_OF_LEDS; i++ )
        gLEDs[ i ] = (rgb_mask[ color ][ i % N_LED_PER_BOARD ]) ? (((count & 0x80) ? ~count : count) << 1) & 0xFF : 0x00;
}


void pattern_colors2( int count ) {

    if ( !count ) {
        gMotor_count     = 0;
        setup_transfer( CH_FM_PLUS, mot_norm_op_transfer, sizeof( mot_norm_op_transfer ) / sizeof( transaction ) );
        start( CH_FM_PLUS );    //  just update PCA9629 registers. no motor action
    }

    if ( !((count + 20) & 0x3F) ) {
        single_byte_write_into_a_PCA9629( CH_FM_PLUS, gMot_cntl_order[ gMotor_count % 5 ], 0x26, 0x80 | ((gMotor_count / 5) & 0x1) );
        gMotor_count++;
    }

#define PI 3.14159265358979323846

    float   r;
    float   g;
    float   b;
    float   k;
    float   c;

    k   = (2 * PI) / 300.0;
    c   = count;

#if 0
    r   = sin( k * (c +   0.0) );
    g   = sin( k * (c + 100.0) );
    b   = sin( k * (c + 200.0) );
    r   = (0 < r) ? r  * 255.0 : 0;
    g   = (0 < g) ? g  * 255.0 : 0;
    b   = (0 < b) ? b  * 255.0 : 0;
#else
    r   = (sin( k * (c +   0.0) ) + 1) * (127.5 * ((count < 500) ? (float)count / 500.0 : 1.0));
    g   = (sin( k * (c + 100.0) ) + 1) * (127.5 * ((count < 500) ? (float)count / 500.0 : 1.0));
    b   = (sin( k * (c + 200.0) ) + 1) * (127.5 * ((count < 500) ? (float)count / 500.0 : 1.0));
#endif
    for ( int i = 0; i < N_OF_LEDS; i += N_LED_PER_BOARD ) {
        for ( int j = 0; j < 12; j += 3 ) {
            gLEDs[ i + j + 0 ] = (char)r;
            gLEDs[ i + j + 1 ] = (char)g;
            gLEDs[ i + j + 2 ] = (char)b;
        }
        for ( int j = 12; j < 15; j ++ )
            gLEDs[ i + j ] = (char)((((2500 <= count) && (count < 3000)) ? ((float)(count - 2500) / 500.0) : 0.0) * 255.0);
    }
}

//void pattern_color_phase( int count ) {
void pattern_colors( int count ) {

    if ( !count ) {
        gMotor_count     = 0;
        setup_transfer( CH_FM_PLUS, mot_norm_op_transfer, sizeof( mot_norm_op_transfer ) / sizeof( transaction ) );
        start( CH_FM_PLUS );    //  just update PCA9629 registers. no motor action
    }

    if ( !((count + 20) & 0x3F) ) {
        single_byte_write_into_a_PCA9629( CH_FM_PLUS, gMot_cntl_order[ gMotor_count % 5 ], 0x26, 0x80 | ((gMotor_count / 5) & 0x1) );
        gMotor_count++;
    }

#define PI 3.14159265358979323846

    float   p;
    float   k;
    float   c;

    k   = (2 * PI) / 300.0;
    p   = 300.0 / N_OF_LEDS;
    c   = count;

    for ( int i = 0; i < N_OF_LEDS; i += N_LED_PER_BOARD ) {
        for ( int j = 0; j < 12; j += 3 ) {
            gLEDs[ i + j + 0 ] = (char)((sin( k * (c +   0.0) + p * (i + j) ) + 1) * (127.5 * ((count < 500) ? (float)count / 500.0 : 1.0)));
            gLEDs[ i + j + 1 ] = (char)((sin( k * (c + 100.0) + p * (i + j) ) + 1) * (127.5 * ((count < 500) ? (float)count / 500.0 : 1.0)));
            gLEDs[ i + j + 2 ] = (char)((sin( k * (c + 200.0) + p * (i + j) ) + 1) * (127.5 * ((count < 500) ? (float)count / 500.0 : 1.0)));
        }
        for ( int j = 12; j < 15; j ++ )
            gLEDs[ i + j ] = (char)((((2500 <= count) && (count < 3000)) ? ((float)(count - 2500) / 500.0) : 0.0) * 255.0);
    }
}





void pattern_one_by_one( int count ) {
    if ( count == 0 ) {
        all_LEDs_turn_off();
        return;
    }

    gLEDs[ (count / 9) % N_OF_LEDS ] = nine_step_intensity[ count % 9 ];
}

void pattern_random( int count ) {
    if ( count == 0 ) {
        all_LEDs_turn_off();
        return;
    }
    gLEDs[ rand() % N_OF_LEDS ] = rand() % 0xFF;
}

void pattern_random_with_decay0( int count ) {
    if ( count == 0 ) {
        all_LEDs_turn_off();
        return;
    }
    gLEDs[ rand() % N_OF_LEDS ] = 0xFF;

    for ( int i = 0; i < N_OF_LEDS; i++ )
        gLEDs[ i ] = (char)((float)gLEDs[ i ] * 0.90);
}

void pattern_random_with_decay1( int count ) {
    if ( count == 0 ) {
        all_LEDs_turn_off();
        return;
    }
    gLEDs[ rand() % N_OF_LEDS ] = 0xFF;

    for ( int i = 0; i < N_OF_LEDS; i++ )
        gLEDs[ i ] = (char)((float)gLEDs[ i ] * 0.7);
}

void pattern_flashing( int count ) {
#define SUSTAIN_DURATION    10
    static float    interval;
    static int      timing;
    static int      sustain;

    if ( count == 0 ) {
        interval    = 100.0;
        timing      = 0;
        sustain     = SUSTAIN_DURATION;
        vib_all_PCA9629();
    }
    if ( (timing == count) || !((int)interval) ) {
        sustain     = SUSTAIN_DURATION;
        all_LEDs_turn_on();
        start_all_PCA9629();

        timing      += (int)interval;
        interval    *= 0.96;
    } else {
        for ( int i = 0; i < N_OF_LEDS; i++ )
            gLEDs[ i ] = (char)((float)gLEDs[ i ] * 0.90);

        if ( sustain-- < 0 )
            stop_all_PCA9629();
    }
}

void stop( int count ) {
    if ( !count ) {
        all_LEDs_turn_off();
        stop_all_PCA9629();
    }
}

void pattern_init( int count ) {

    if ( !count )
        align_all_PCA9629();
}


void pattern_scan( int count ) {
    static char     gMotor_count;

    if ( !count ) {
        gMotor_count     = 0;
        setup_transfer( CH_FM_PLUS, mot_norm_op_transfer, sizeof( mot_norm_op_transfer ) / sizeof( transaction ) );
        start( CH_FM_PLUS );    //  just update PCA9629 registers. no motor action
    }

    if ( !(count & 0x1F) ) {
        single_byte_write_into_a_PCA9629( CH_FM_PLUS, gMot_cntl_order[ gMotor_count % 5 ], 0x26, 0x80 | ((gMotor_count / 5) & 0x1) );
        gMotor_count++;
    }

    all_LEDs_turn_off();
    gLEDs[ (count >> 3) % N_OF_LEDS ] = 0xFF;
}

void pattern_setup_half_turns( int count ) {

    if ( !count ) {
        align_all_PCA9629();
        gMotor_count    = 0;
    } else if ( count == 50 ) {
        setup_transfer( CH_FM_PLUS, mot_half_setup_transfer, sizeof( mot_half_transfer ) / sizeof( transaction ) );
        start( CH_FM_PLUS );
    } else if ( count == 60 ) {
        single_byte_write_into_a_PCA9629( CH_FM_PLUS, gMot_cntl_order[ 4 ], 0x26, 0x80 );
    } else if ( count == 75 ) {
        single_byte_write_into_a_PCA9629( CH_FM_PLUS, gMot_cntl_order[ 3 ], 0x26, 0x80 );
    } else if ( count == 90 ) {
        single_byte_write_into_a_PCA9629( CH_FM_PLUS, gMot_cntl_order[ 2 ], 0x26, 0x80 );
    } else if ( count == 115 ) {
        single_byte_write_into_a_PCA9629( CH_FM_PLUS, gMot_cntl_order[ 1 ], 0x26, 0x80 );
    } else if ( count == 150 ) {
        setup_transfer( CH_FM_PLUS, mot_half_transfer, sizeof( mot_half_transfer ) / sizeof( transaction ) );
        start( CH_FM_PLUS );
    }
}



void pattern_half_turns( int count ) {

#ifdef INTERVAL128MS
    if ( !(count & 0x0F) ) {
#else
    if ( !(count & 0x1F) ) {
#endif
        if ( gMot_cntl_order_halfturn[ gMotor_count ] )
            single_byte_write_into_a_PCA9629( CH_FM_PLUS, gMot_cntl_order_halfturn[ gMotor_count ], 0x26, 0x80 | ((gMot_cntl_order_halfturn[ gMotor_count ] >> 1) & 0x1) );
        gMotor_count++;

        gMotor_count = (gMotor_count == 10) ? 0 : gMotor_count;
    }


//    all_LEDs_turn_off();
//    gLEDs[ (count >> 3) % N_OF_LEDS ] = 0xFF;
}



void pattern_fadeout300( int count ) {

    if ( !count )
        stop_all_PCA9629();
    else if ( count == 100 )
        align_all_PCA9629();

    for ( int i = 0; i < N_OF_LEDS; i++ )
        gLEDs[ i ] = (char)((float)gLEDs[ i ] * 0.994469);  //  (0.994469 = 1/(255^(1/999))) BUT VALUE IS TRANCATED IN THE LOOP. IT DECALYS FASTER THAN FLOAT CALC.
}

void pattern_speed_variations( int count ) {

    char    data[ 4 ];
    int     v;

    if ( !count ) {
        setup_transfer( CH_FM_PLUS, mot_norm_op_transfer, sizeof( mot_norm_op_transfer ) / sizeof( transaction ) );

        data[ 0 ]   = 0x2;
        for ( int i = 0;  i < 5; i++ )
            buffer_overwrite( CH_FM_PLUS, i, 20, data, 1 );     //  Set half step operation

        for ( int i = 0;  i < 5; i++ ) {
            v   = 833;// 400pps for halfstep
            data[ 0 ]   = data[ 2 ]   = v & 0xFF;
            data[ 1 ]   = data[ 3 ]   = (i << 5) | (v >> 8);
            buffer_overwrite( CH_FM_PLUS, i, 23, data, 4 );
        }

        for ( int i = 0;  i < 5; i++ ) {
            v   = (0x1 << (5 - i));
            data[ 0 ]   = data[ 2 ]   = v & 0xFF;
            data[ 1 ]   = data[ 3 ]   = v >> 8;
            buffer_overwrite( CH_FM_PLUS, i, 31, data, 4 );
        }


    }

    if ( !(count % 500) ) {
        data[ 0 ]   = 0x84 | ((count / 500) & 0x1);
        for ( int i = 0;  i < 5; i++ )
            buffer_overwrite( CH_FM_PLUS, i, 39, data, 1 );
        start( CH_FM_PLUS );
    }
}

void pattern_mot_ramp_variations( int count ) {

    char    ramp_var[5][17] = {
        { 0x5D, 0x03, 0x5D, 0x03,   0x0B, 0x00, 0x00, 0x00,   0x0F, 0x00, 0x00, 0x00,   0x00, 0x00, 0x36, 0x00,   0x88 },
        { 0x4B, 0x05, 0x4B, 0x05,   0x25, 0x00, 0x00, 0x00,   0x11, 0x00, 0x00, 0x00,   0x00, 0x00, 0x37, 0x00,   0x88 },
        { 0x15, 0x06, 0x15, 0x06,   0x2D, 0x00, 0x00, 0x00,   0x12, 0x00, 0x00, 0x00,   0x00, 0x00, 0x38, 0x00,   0x88 },
        { 0x71, 0x06, 0x71, 0x06,   0x17, 0x00, 0x00, 0x00,   0x13, 0x00, 0x00, 0x00,   0x00, 0x00, 0x39, 0x00,   0x88 },
        { 0x9C, 0x06, 0x9C, 0x06,   0x23, 0x00, 0x00, 0x00,   0x13, 0x00, 0x00, 0x00,   0x00, 0x00, 0x3A, 0x00,   0x88 },
    };

    if ( !count ) {
        setup_transfer( CH_FM_PLUS, mot_norm_op_transfer, sizeof( mot_norm_op_transfer ) / sizeof( transaction ) );

        for ( int i = 0;  i < 5; i++ )
            buffer_overwrite( CH_FM_PLUS, i, 23, ramp_var[ i ], 17 );

        start( CH_FM_PLUS );
    }

    all_LEDs_turn_off();
    gLEDs[ count % N_OF_LEDS ] = 0xFF;

}

void pattern_knightrider( int count ) {
    short   led_pat[ 12 ] = { 0x7000, 0x0004, 0x0E00, 0x0002, 0x01C0, 0x0001, 0x0038, 0x0001, 0x01C0, 0x0002, 0x0E00, 0x0004 };
    short   led_bits;

#if 0
    //if ( !(count && 0x7) )
    {
        led_bits    = led_pat[ (count >> 4) % 12 ];
        for ( int i = 0; i < N_OF_LEDS; i += N_LED_PER_BOARD ) {
            for ( int j = 0; j < N_LED_PER_BOARD; j ++ ) {
                gLEDs[ i + j ] = ((led_bits << j) & 0x4000) ? 0xFF : gLEDs[ i + j ];
            }
        }
    }
    for ( int i = 0; i < N_OF_LEDS; i++ )
        gLEDs[ i ] = (char)((float)gLEDs[ i ] * 0.90 * ((700 < count) ? (float)(1000 - count) / 300.0 : 1.0));
#else
    //if ( !(count && 0x7) )
    {
        led_bits    = led_pat[ (count >> 4) % 12 ];
        for ( int i = 0; i < N_OF_LEDS; i += N_LED_PER_BOARD ) {
            for ( int j = 0; j < N_LED_PER_BOARD; j ++ ) {
                gLEDs[ i + j ] = ((led_bits << j) & 0x4000) ? ((744 < count) ? (1000 - count) : 255) : gLEDs[ i + j ];
//              gLEDs[ i + j ] = ((led_bits << j) & 0x4000) ? 0xFF : gLEDs[ i + j ];
            }
        }
    }
    for ( int i = 0; i < N_OF_LEDS; i++ )
        gLEDs[ i ] = (char)((float)gLEDs[ i ] * 0.90);
#endif

}
pattern _gPt[]    = {
//    { 256,      pattern_rampup },
    { 3000,      pattern_colors },
};
pattern gPt[]    = {
//    { 256,      pattern_rampup },
    { 20,       stop },
    { 120,      pattern_init },
    { 3000,     pattern_colors },
    { 300,      pattern_fadeout300 },
    { 3000,     pattern_speed_variations },
    { 250,      pattern_setup_half_turns },
    { 3000,     pattern_half_turns },
    { 20,       stop },
    { 120,      pattern_init },
    { 960,      pattern_rampup_and_down_w_color },
    { 960,      pattern_scan },
    { 20,       stop },
    { 120,      pattern_init },
    { 1024,     pattern_rampup_and_down },
    { 700,      pattern_mot_ramp_variations },
    { 700,      pattern_mot_ramp_variations },
    { 700,      pattern_mot_ramp_variations },
    { 700,      pattern_mot_ramp_variations },

    { 3000,     pattern_flashing },
    { 300,      pattern_fadeout300 },
    { 512,      pattern_random },
    { 512,      pattern_random_with_decay1 },
    { 512,      pattern_random_with_decay0 },
    { 9 * 120,  pattern_one_by_one },
    { 1000,     pattern_knightrider },

};



void pattern_update( int count ) {
    static int  next_pattern_change = 0;
    static int  local_count         = 0;
    static int  pattern_index      = -1;

    if ( next_pattern_change == count ) {
        local_count = 0;
        pattern_index++;
        pattern_index   = (pattern_index == (sizeof( gPt ) / sizeof( pattern )) ) ? 0 : pattern_index;
        next_pattern_change += gPt[ pattern_index ].duration;
    }

    (*gPt[ pattern_index ].pattern_func)( local_count++ );
}