Library for "I2C 8x8 LED matrix board" from Switch Science https://www.switch-science.com/catalog/2071/

Dependents:   PCA9622_LED8x8_Demo PCA9622_LED8x8_Hello PCA9622_LED8x8_x6_Demo shake-shake-machine

0. What is this?

I2C interface single color LED matrix module with PCA9622(LED controller).

This library provides interface from memory array to LEDs, manages scan.
Brightness control can be done for each pixels by setting data as float value (from 0.0 to 1.0).

topview
bottomview

1. How to use

1.1 Sample code

Import programPCA9622_LED8x8_Hello

This is a very simple sample code for the PCA9622_LED8x8 library. arget hardware : "I2C 8x8 LED matrix board" from Switch Science https://www.switch-science.com/catalog/2071/

1.2 Very basic sample

1.2.1 Displaying static image

Next is simplified code sample that makes still image on the LEDs.
In the main() function, the library operation is started by start() function and image data is set by set_data().
The image data is stored in 2 dimensional float array.

#include "mbed.h"
#include "PCA9622_LED8x8.h"

PCA9622_LED8x8  matrix( p28, p27 );     //  I2C pins. SDA and SCL

int main()
{
    float   image[ 8 ][ 8 ];

    matrix.start();

    //  making 8x8 image to "image" array
    for ( int i = 0; i < 8; i++ )
        for ( int j = 0; j < 8; j++ )
            image[ i ][ j ]   = (i + 1) * (j + 1) / 64.0;

    //  set the image into library internal bufer
    matrix.set_data( image );

    while(1)
        ;
}


/media/uploads/nxp_ip/img_3560.jpg
result of upper code

1.2.2 Image array elements' physical position

Each element of "float image[8][8]" array has physical position of..

leftright
topimage[0][0]image[0][1]image[0][2]image[0][3]image[0][4]image[0][5]image[0][6]image[0][7]
image[1][0]image[1][1]image[1][2]image[1][3]image[1][4]image[1][5]image[1][6]image[1][7]
image[2][0]image[2][1]image[2][2]image[2][3]image[2][4]image[2][5]image[2][6]image[2][7]
image[3][0]image[3][1]image[3][2]image[3][3]image[3][4]image[3][5]image[3][6]image[3][7]
image[4][0]image[4][1]image[4][2]image[4][3]image[4][4]image[4][5]image[4][6]image[4][7]
image[5][0]image[5][1]image[5][2]image[5][3]image[5][4]image[5][5]image[5][6]image[5][7]
image[6][0]image[6][1]image[6][2]image[6][3]image[6][4]image[6][5]image[6][6]image[6][7]
bottomimage[7][0]image[7][1]image[7][2]image[7][3]image[7][4]image[7][5]image[7][6]image[7][7]

1.2.3 Making the image moving

If you want to perform animation on the LEDs, it can be done by simple loop.
Keep the loop doing (1) store image data into the array and () call set_data() function.
The set_data() function can be called any time.

The set_data() function does copy the data in in-active side of internal buffer.
This library has two 8x8 buffers internally to perform "ping-pong" buffering. One side of the buffer is "active" and other side is "inactive".
The active side is the buffer for displaying data to LEDs. The inactive side is for user access.
The set_data() always access to the inactive side and after the inactive side filling is done, the active/inactive side switched at next scan start.

    while(1)
    {
        //  do something for image array to store data;
        ....
        ...
        matrix.set_data( image );
        wait( 0.1 );
    }

2. Tips

2.1 Frame rate

To minimize the flicker, default setting of frame rate is set to 100Hz.
But it is bit heavy rate to maintain the scan by MCU. If you going to use multiple modules, you may need to lower the I2C clock frequency.

Since mbed's I2C write routine is blocking function, frequent I2C transfer consumes much CPU time (just to wait transfer complete). To draw a line, one I2C transfer is done (14 bytes transfer including slave address). If the frame rate is 100Hz, 800 times I2C transfer happens in a second (8 lines/frame * 100Hz). If your mbed is supporting "I2C Fast mode plus (Fm+)", this will be a good option to reduce the CPU load. PCA9622 supports Fm+ which can handle the clock upto 1MHz.

2.2 Start/Stop

After start() function call, a periodic interrupt is installed to maintain the scanning the LEDs. This will be kept until stop() function called.

3. Links

shop page (Japanese)
schematic
PCA9622

Component page is available

Please refer to the component page for for more information.

I2C 8x8 LED matrix board" from Switch Science

Information

If you want to try displaying image which is prepared as file (PNG, JPEG, GIF, BMP, etc.), this tool may be useful.
This tool converts the image data into float array (in C source format).
https://gist.github.com/toyowata/bf576ee091e75071bc66

Committer:
nxp_ip
Date:
Thu Dec 25 01:51:30 2014 +0000
Revision:
1:a1bf164ff73b
Parent:
0:41234a8149bd
Child:
2:cd00372373e4
multiple instance support (tested with 6 devices. default frame rate is changed to 50Hz)

Who changed what in which revision?

UserRevisionLine numberNew contents of line
nxp_ip 0:41234a8149bd 1 /**
nxp_ip 0:41234a8149bd 2 * PCA9622 LED 8x8 library
nxp_ip 0:41234a8149bd 3 *
nxp_ip 0:41234a8149bd 4 * @author Tedd OKANO
nxp_ip 0:41234a8149bd 5 * @version 1.0
nxp_ip 0:41234a8149bd 6 * @date 28-Nov-2014
nxp_ip 0:41234a8149bd 7 *
nxp_ip 0:41234a8149bd 8 * Library for "I2C 8x8 LED matrix board" from Switch Science
nxp_ip 0:41234a8149bd 9 * https://www.switch-science.com/catalog/2071/
nxp_ip 0:41234a8149bd 10 *
nxp_ip 0:41234a8149bd 11 * The I2C LED controller PCA9622 is used on this module
nxp_ip 0:41234a8149bd 12 * that ebables to control the LEDs with PWM brightness control.
nxp_ip 0:41234a8149bd 13 *
nxp_ip 0:41234a8149bd 14 * For more information about the PCA9622:
nxp_ip 0:41234a8149bd 15 * http://www.nxp.com/documents/data_sheet/PCA9622.pdf
nxp_ip 0:41234a8149bd 16 */
nxp_ip 0:41234a8149bd 17
nxp_ip 0:41234a8149bd 18 #include "mbed.h"
nxp_ip 0:41234a8149bd 19 #include "PCA9622_LED8x8.h"
nxp_ip 0:41234a8149bd 20
nxp_ip 0:41234a8149bd 21 PCA9622_LED8x8::PCA9622_LED8x8( PinName sda, PinName scl, char slave_adr, float fr )
nxp_ip 0:41234a8149bd 22 :
nxp_ip 0:41234a8149bd 23 i2c_p( new I2C( sda, scl ) ),
nxp_ip 0:41234a8149bd 24 i2c( *i2c_p ),
nxp_ip 0:41234a8149bd 25 address( slave_adr ),
nxp_ip 0:41234a8149bd 26 framerate( fr ),
nxp_ip 0:41234a8149bd 27 in_operation( false ),
nxp_ip 1:a1bf164ff73b 28 line_counter( 0 ),
nxp_ip 0:41234a8149bd 29 frame_counter( 0 ),
nxp_ip 0:41234a8149bd 30 buffer_switch_request( false ),
nxp_ip 0:41234a8149bd 31 outgoing_buffer( 0 )
nxp_ip 0:41234a8149bd 32 {
nxp_ip 0:41234a8149bd 33 initialize();
nxp_ip 0:41234a8149bd 34 }
nxp_ip 0:41234a8149bd 35
nxp_ip 0:41234a8149bd 36 PCA9622_LED8x8::PCA9622_LED8x8( I2C &i2c_obj, char slave_adr, float fr )
nxp_ip 0:41234a8149bd 37 :
nxp_ip 0:41234a8149bd 38 i2c_p( NULL ),
nxp_ip 0:41234a8149bd 39 i2c( i2c_obj ),
nxp_ip 0:41234a8149bd 40 address( slave_adr ),
nxp_ip 0:41234a8149bd 41 framerate( fr ),
nxp_ip 0:41234a8149bd 42 in_operation( false ),
nxp_ip 1:a1bf164ff73b 43 line_counter( 0 ),
nxp_ip 0:41234a8149bd 44 frame_counter( 0 ),
nxp_ip 0:41234a8149bd 45 buffer_switch_request( false ),
nxp_ip 0:41234a8149bd 46 outgoing_buffer( 0 )
nxp_ip 0:41234a8149bd 47 {
nxp_ip 0:41234a8149bd 48 initialize();
nxp_ip 0:41234a8149bd 49 }
nxp_ip 0:41234a8149bd 50
nxp_ip 0:41234a8149bd 51 PCA9622_LED8x8::~PCA9622_LED8x8()
nxp_ip 0:41234a8149bd 52 {
nxp_ip 0:41234a8149bd 53 }
nxp_ip 0:41234a8149bd 54
nxp_ip 0:41234a8149bd 55 void PCA9622_LED8x8::initialize( void )
nxp_ip 0:41234a8149bd 56 {
nxp_ip 0:41234a8149bd 57 char init[ 2 ][ 3 ] = {
nxp_ip 0:41234a8149bd 58 { 0x80, 0x00, 0x05 }, // initialize MODE1 and MODE2 registers
nxp_ip 0:41234a8149bd 59 { 0x96, 0xAA, 0xAA } // initialize LEDOUT2 and LEDOUT3 registers
nxp_ip 0:41234a8149bd 60 };
nxp_ip 0:41234a8149bd 61
nxp_ip 0:41234a8149bd 62 i2c.frequency( 400 * 1000 );
nxp_ip 0:41234a8149bd 63 i2c.write( address, init[ 0 ], sizeof( init[ 0 ] ) );
nxp_ip 0:41234a8149bd 64 i2c.write( address, init[ 1 ], sizeof( init[ 1 ] ) );
nxp_ip 1:a1bf164ff73b 65
nxp_ip 1:a1bf164ff73b 66 start();
nxp_ip 0:41234a8149bd 67 }
nxp_ip 0:41234a8149bd 68
nxp_ip 0:41234a8149bd 69 void PCA9622_LED8x8::frame_rate( float rate )
nxp_ip 0:41234a8149bd 70 {
nxp_ip 0:41234a8149bd 71 int previous_state;
nxp_ip 0:41234a8149bd 72
nxp_ip 0:41234a8149bd 73 previous_state = in_operation;
nxp_ip 0:41234a8149bd 74
nxp_ip 0:41234a8149bd 75 stop();
nxp_ip 0:41234a8149bd 76 framerate = rate;
nxp_ip 0:41234a8149bd 77
nxp_ip 0:41234a8149bd 78 if ( previous_state )
nxp_ip 0:41234a8149bd 79 start();
nxp_ip 0:41234a8149bd 80 }
nxp_ip 0:41234a8149bd 81
nxp_ip 0:41234a8149bd 82 void PCA9622_LED8x8::start( void )
nxp_ip 0:41234a8149bd 83 {
nxp_ip 0:41234a8149bd 84 t.attach( this, &PCA9622_LED8x8::draw_a_line, 1.0 / (framerate * 8.0) );
nxp_ip 0:41234a8149bd 85 in_operation = true;
nxp_ip 0:41234a8149bd 86 }
nxp_ip 0:41234a8149bd 87
nxp_ip 0:41234a8149bd 88 void PCA9622_LED8x8::stop( void )
nxp_ip 0:41234a8149bd 89 {
nxp_ip 0:41234a8149bd 90 t.detach();
nxp_ip 0:41234a8149bd 91 in_operation = false;
nxp_ip 0:41234a8149bd 92 }
nxp_ip 0:41234a8149bd 93
nxp_ip 0:41234a8149bd 94 void PCA9622_LED8x8::draw_a_line( void )
nxp_ip 0:41234a8149bd 95 {
nxp_ip 0:41234a8149bd 96 char write_data[ 13 ];
nxp_ip 0:41234a8149bd 97
nxp_ip 0:41234a8149bd 98 if ( buffer_switch_request && !line_counter ) {
nxp_ip 0:41234a8149bd 99 // when the scan start, and if the buffer switching is requested ping-pong bufer will be switched
nxp_ip 0:41234a8149bd 100 outgoing_buffer = !outgoing_buffer;
nxp_ip 0:41234a8149bd 101 buffer_switch_request = false;
nxp_ip 0:41234a8149bd 102 }
nxp_ip 0:41234a8149bd 103
nxp_ip 0:41234a8149bd 104 write_data[ 0 ] = 0x80 | 0xA; // pointing PWM8 register with increment flag
nxp_ip 0:41234a8149bd 105 // write_data[ 9 ] = 0x00; // don't need to stuff any data because this register will be ignored (because global PWM setting is not used)
nxp_ip 0:41234a8149bd 106 // write_data[ 10 ] = 0x00; // don't need to stuff any data because this register will be ignored (because global PWM setting is not used)
nxp_ip 0:41234a8149bd 107
nxp_ip 0:41234a8149bd 108 for ( int i = 0; i < 8; i++ )
nxp_ip 0:41234a8149bd 109 write_data[ i + 1 ] = pattern[ outgoing_buffer ][ line_counter ][ i ]; // PWM data are set to PWM8..PWM15 registers (driving ROW)
nxp_ip 0:41234a8149bd 110
nxp_ip 0:41234a8149bd 111 // A line below works fine on Cortex-M3 but not on Cortex-M0. So I needed to rewrite it
nxp_ip 0:41234a8149bd 112 // *((unsigned short *)(write_data + 11)) = 0x0001 << (line_counter << 1); // channel 0 to 7 are used to ON/OFF the column
nxp_ip 0:41234a8149bd 113
nxp_ip 0:41234a8149bd 114 // A line above is rewritten as next 3 lines
nxp_ip 0:41234a8149bd 115 unsigned short tmp = 0x0001 << (line_counter << 1);
nxp_ip 0:41234a8149bd 116 write_data[ 11 ] = tmp & 0xFF;
nxp_ip 0:41234a8149bd 117 write_data[ 12 ] = tmp >> 8;
nxp_ip 0:41234a8149bd 118
nxp_ip 0:41234a8149bd 119 i2c.write( address, write_data, sizeof( write_data ) ); // I2C transfer
nxp_ip 0:41234a8149bd 120
nxp_ip 0:41234a8149bd 121 line_counter = (line_counter + 1) & 0x7;
nxp_ip 0:41234a8149bd 122
nxp_ip 0:41234a8149bd 123 if ( line_counter )
nxp_ip 0:41234a8149bd 124 frame_counter++;
nxp_ip 0:41234a8149bd 125 }
nxp_ip 0:41234a8149bd 126
nxp_ip 0:41234a8149bd 127 void PCA9622_LED8x8::set_data( float p[ 8 ][ 8 ] )
nxp_ip 0:41234a8149bd 128 {
nxp_ip 0:41234a8149bd 129 for ( int i = 0; i < 8; i++ )
nxp_ip 0:41234a8149bd 130 for ( int j = 0; j < 8; j++ )
nxp_ip 0:41234a8149bd 131 pattern[ !outgoing_buffer /* store image to in-active side */ ][ i ][ 7 - j ] = (char)(p[ i ][ j ] * 255.0);
nxp_ip 0:41234a8149bd 132
nxp_ip 0:41234a8149bd 133 buffer_switch_request = true; // when the buffer filling done, raise this flag to switch the pin-pong buffer
nxp_ip 0:41234a8149bd 134 }