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 Feb 26 00:01:06 2015 +0000
Revision:
3:fb23972da0f3
Parent:
2:cd00372373e4
Version 1.1.1 : Fix in destructor. To dispose I2C instance if it has own.

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 3:fb23972da0f3 5 * @version 1.1.1
nxp_ip 3:fb23972da0f3 6 * @date 26-Feb-2015
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 3:fb23972da0f3 53 if ( NULL != i2c_p )
nxp_ip 3:fb23972da0f3 54 delete i2c_p;
nxp_ip 0:41234a8149bd 55 }
nxp_ip 0:41234a8149bd 56
nxp_ip 0:41234a8149bd 57 void PCA9622_LED8x8::initialize( void )
nxp_ip 0:41234a8149bd 58 {
nxp_ip 0:41234a8149bd 59 char init[ 2 ][ 3 ] = {
nxp_ip 0:41234a8149bd 60 { 0x80, 0x00, 0x05 }, // initialize MODE1 and MODE2 registers
nxp_ip 0:41234a8149bd 61 { 0x96, 0xAA, 0xAA } // initialize LEDOUT2 and LEDOUT3 registers
nxp_ip 0:41234a8149bd 62 };
nxp_ip 0:41234a8149bd 63
nxp_ip 0:41234a8149bd 64 i2c.frequency( 400 * 1000 );
nxp_ip 0:41234a8149bd 65 i2c.write( address, init[ 0 ], sizeof( init[ 0 ] ) );
nxp_ip 0:41234a8149bd 66 i2c.write( address, init[ 1 ], sizeof( init[ 1 ] ) );
nxp_ip 1:a1bf164ff73b 67
nxp_ip 1:a1bf164ff73b 68 start();
nxp_ip 0:41234a8149bd 69 }
nxp_ip 0:41234a8149bd 70
nxp_ip 0:41234a8149bd 71 void PCA9622_LED8x8::frame_rate( float rate )
nxp_ip 0:41234a8149bd 72 {
nxp_ip 0:41234a8149bd 73 int previous_state;
nxp_ip 0:41234a8149bd 74
nxp_ip 0:41234a8149bd 75 previous_state = in_operation;
nxp_ip 0:41234a8149bd 76
nxp_ip 0:41234a8149bd 77 stop();
nxp_ip 0:41234a8149bd 78 framerate = rate;
nxp_ip 0:41234a8149bd 79
nxp_ip 0:41234a8149bd 80 if ( previous_state )
nxp_ip 0:41234a8149bd 81 start();
nxp_ip 0:41234a8149bd 82 }
nxp_ip 0:41234a8149bd 83
nxp_ip 0:41234a8149bd 84 void PCA9622_LED8x8::start( void )
nxp_ip 0:41234a8149bd 85 {
nxp_ip 0:41234a8149bd 86 t.attach( this, &PCA9622_LED8x8::draw_a_line, 1.0 / (framerate * 8.0) );
nxp_ip 0:41234a8149bd 87 in_operation = true;
nxp_ip 0:41234a8149bd 88 }
nxp_ip 0:41234a8149bd 89
nxp_ip 0:41234a8149bd 90 void PCA9622_LED8x8::stop( void )
nxp_ip 0:41234a8149bd 91 {
nxp_ip 0:41234a8149bd 92 t.detach();
nxp_ip 0:41234a8149bd 93 in_operation = false;
nxp_ip 0:41234a8149bd 94 }
nxp_ip 0:41234a8149bd 95
nxp_ip 0:41234a8149bd 96 void PCA9622_LED8x8::draw_a_line( void )
nxp_ip 0:41234a8149bd 97 {
nxp_ip 0:41234a8149bd 98 char write_data[ 13 ];
nxp_ip 0:41234a8149bd 99
nxp_ip 0:41234a8149bd 100 if ( buffer_switch_request && !line_counter ) {
nxp_ip 0:41234a8149bd 101 // when the scan start, and if the buffer switching is requested ping-pong bufer will be switched
nxp_ip 0:41234a8149bd 102 outgoing_buffer = !outgoing_buffer;
nxp_ip 0:41234a8149bd 103 buffer_switch_request = false;
nxp_ip 0:41234a8149bd 104 }
nxp_ip 0:41234a8149bd 105
nxp_ip 0:41234a8149bd 106 write_data[ 0 ] = 0x80 | 0xA; // pointing PWM8 register with increment flag
nxp_ip 0:41234a8149bd 107 // 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 108 // 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 109
nxp_ip 0:41234a8149bd 110 for ( int i = 0; i < 8; i++ )
nxp_ip 0:41234a8149bd 111 write_data[ i + 1 ] = pattern[ outgoing_buffer ][ line_counter ][ i ]; // PWM data are set to PWM8..PWM15 registers (driving ROW)
nxp_ip 0:41234a8149bd 112
nxp_ip 0:41234a8149bd 113 // A line below works fine on Cortex-M3 but not on Cortex-M0. So I needed to rewrite it
nxp_ip 0:41234a8149bd 114 // *((unsigned short *)(write_data + 11)) = 0x0001 << (line_counter << 1); // channel 0 to 7 are used to ON/OFF the column
nxp_ip 0:41234a8149bd 115
nxp_ip 0:41234a8149bd 116 // A line above is rewritten as next 3 lines
nxp_ip 0:41234a8149bd 117 unsigned short tmp = 0x0001 << (line_counter << 1);
nxp_ip 0:41234a8149bd 118 write_data[ 11 ] = tmp & 0xFF;
nxp_ip 0:41234a8149bd 119 write_data[ 12 ] = tmp >> 8;
nxp_ip 0:41234a8149bd 120
nxp_ip 0:41234a8149bd 121 i2c.write( address, write_data, sizeof( write_data ) ); // I2C transfer
nxp_ip 0:41234a8149bd 122
nxp_ip 0:41234a8149bd 123 line_counter = (line_counter + 1) & 0x7;
nxp_ip 0:41234a8149bd 124
nxp_ip 0:41234a8149bd 125 if ( line_counter )
nxp_ip 0:41234a8149bd 126 frame_counter++;
nxp_ip 0:41234a8149bd 127 }
nxp_ip 0:41234a8149bd 128
nxp_ip 0:41234a8149bd 129 void PCA9622_LED8x8::set_data( float p[ 8 ][ 8 ] )
nxp_ip 0:41234a8149bd 130 {
nxp_ip 0:41234a8149bd 131 for ( int i = 0; i < 8; i++ )
nxp_ip 0:41234a8149bd 132 for ( int j = 0; j < 8; j++ )
nxp_ip 0:41234a8149bd 133 pattern[ !outgoing_buffer /* store image to in-active side */ ][ i ][ 7 - j ] = (char)(p[ i ][ j ] * 255.0);
nxp_ip 0:41234a8149bd 134
nxp_ip 0:41234a8149bd 135 buffer_switch_request = true; // when the buffer filling done, raise this flag to switch the pin-pong buffer
nxp_ip 0:41234a8149bd 136 }