Multiplexing - how to speed up

Hi all,

I'm working on a project where I need to multiplex a matrix of 512 LEDs using a pic16f628. Currently I'm testing with 5 cascaded shift registers (serial in, parallel out). One shift register is used to control which LEDs must be on, in the current column. The other 4 are used for the columns.

Here is a design of the circuit as example. I left out some parts, but I hope you'll get an idea of the project:

formatting link

After adding the 5th shift register the image started flickering. I've talked to some people telling me its definitely possible to control at least 40 columns. How can I achieve this? Are there any "tricks" to speeds this up?

Here is my code for the pic16f628:

// Define the shift registers pins #define SHIFT_DATA PORTA.F1 #define SHIFT_CLOCK PORTA.F0 #define SHIFT_OE PORTA.F2 // Output enable.

// The a_matrix holds the row values for the 8 columns // in the 8x8 matrix. The keys of the matrix 0-7 are // the columns, the corresponding values the row flags. char a_matrix[8] = { 0b01010101 ,0b01010101 ,0b00100000 ,0b00010000 ,0b00001000 ,0b00000100 ,0b00000010 ,0b00000001 };

char a_columns[32] = { 0b10000000 ,0b01000000 ,0b00100000 ,0b00010000 ,0b00001000 ,0b00000100 ,0b00000010 ,0b00000001 // 8 ,0b00000010 ,0b00000100 ,0b00001000 ,0b00010000 ,0b00100000 ,0b01000000 ,0b10000000 ,0b00000001 // 16 ,0b00000010 ,0b00000100 ,0b00001000 ,0b00010000 ,0b00100000 ,0b01000000 ,0b10000000 ,0b00000001 // 24 ,0b00000010 ,0b00000100 ,0b00001000 ,0b00010000 ,0b00100000 ,0b01000000 ,0b10000000 ,0b00000001 // 32

};

char c_in = 0; // Used for serial input. int f_state = 0; // Used to update the matrix. (update_matrix()) int c_col_in = 0; // update_matrix() int c_row_in = 0; // update_matrix(); int c_called = 0; // update_matrix(), counting the number of times method is called int num_cols = 32; int i = 0; int j = 0; int i_col = 0; char c_column; // main()

// Shift one byte into the shift register. void shift_byte(char pByte) { int i; for (i = 0; i < 8; i++) { // Get bit (1 or 0) at position i of pByte. SHIFT_DATA = (pByte >> i) & 1;

// Make clock pin high and low again, so data is shifted into the registers. SHIFT_CLOCK = 1; SHIFT_CLOCK = 0; } } void shift_bit(int fPin) { SHIFT_DATA = fPin; // 0 or 1 SHIFT_CLOCK = 1; SHIFT_CLOCK = 0; } void put_col_on(int iCol) { // First we shift out several '0' to fill all the registers j = 16 - iCol; for (i_col = 0; i_col < j; i_col++) { shift_bit(0); }

// The first bit is for the column which must be turned on. shift_bit(1);

// All the other columns must be turned off. for (i_col = 0; i_col < (iCol - 1); i_col++) { shift_bit(0); } }

// Make all the columns low. void matrix_off() { SHIFT_OE = 0; shift_byte(0x00); shift_byte(0x00); shift_byte(0x00); shift_byte(0x00); shift_byte(0x00); SHIFT_OE = 1; }

// This function checks if there is data available // on the serial port. And if so, it will update the // matrix table. The data which is received using rs-232 // is in the format (2 bytes): // [column][row_flags] void update_matrix() { // Read data. if (usart_data_ready() == 1) { c_in = usart_read(); if (f_state == 0) { c_col_in = c_in; f_state = 1; } else if (f_state == 1) { c_row_in = c_in; // not really needed, we can use c_in f_state = 0; if (c_col_in

Reply to
roxlu
Loading thread data ...

formatting link

--
Many thanks,

Don Lancaster                          voice phone: (928)428-4073
Synergetics   3860 West First Street   Box 809 Thatcher, AZ 85552
rss: http://www.tinaja.com/whtnu.xml   email: don@tinaja.com

Please visit my GURU\'s LAIR web site at http://www.tinaja.com
Reply to
Don Lancaster

Thanx for the article! I think that the code for the uC will be a lot more complicated which will probably make the display even slower which causes flickering as well. Though I'm not sure; have you tried this? And would this work using shift reigsters?

Greetings

Reply to
roxlu

Your biggest problem is you only have 8 leds on at once (8x40). Rearrange your circuit to have 40 leds on (40x8). The leds will be refreshed 5 times more often.I haven't checked your code so there maybe a problem there as well.

Reply to
cbarn24050

Try using inline assembly for the multiplex. I suggest triggering the refresh from a timer interrupt, so you'd write the ISR in assembly. Another possiblity, maybe not for this MCU, is to use on-chip SPI hardware, which will allow you to shift out data at fosc/4, IIRC. You can also use maximum clock frequency if that's not enough, probably 20Mhz for that chip.

Best regards, Spehro Pefhany

--
"it\'s the network..."                          "The Journey is the reward"
speff@interlog.com             Info for manufacturers: http://www.trexon.com
Embedded software/hardware/analog  Info for designers:  http://www.speff.com
Reply to
Spehro Pefhany

Hi,

Flickerig happens because your update rate is becoming slower. For a naked eye it is required to have 25 frames ( as I remember ) or more to keep smooth images on the screen. For me still I wonder how this even works for 4 registers. Is this for text display or graphics with animations ?.

First scanning (selecting columns) should not happen when row values are written. those operations have to synchronised well. This is how it should happen.

  1. Load the row values to row register. make sure register output is not enabled. i.e. value is written to the internal buffer but not to the output pins of that register. This should be a parallel buffer not a shift register. Shift registers also will work but it need more time to load 8 bit values which might slower the operation.
  2. Now select the right column and enabled the row register at the same time. (assume first column is selected) Row values should be displayed now on the first column. This is the tricky part. You should activate both functions at the same time. A time difference will lead to flickering specially when there are more columns.
  3. Now Load the right row values for the second column. During this operation output should be disabled. ( I mean the values are written to the internal register but not to the output .Output should still contain the previous values. There are buffers available for this.
  4. Again select the output register and the second column at the same time. Now right row values should appear at the second column.

Repeat this procedure. You should not see any flickering unless the scanning speed is not enough. I did this and worked very fine.

The most important thing is that the time taken for column selection should kept at a constant rate. change in this time difference also will lead to flickering. This is crutial when u try to load the row register. Make should you scan the entire matrix atleast (beginning to end) 25 times per second. Higher the better.

I think this might help you,

Pubudu.

Reply to
Pubudu

For an 8 x 64 matrix if you only drive 8 LEDS at a time each LED can only be on for 1/64th of the time and a complete display refresh requires 64 multiplex cycles.

If you drive the matrix the other way round each LED is on for 1/8th of the time and you only require 8 multiplex cycles for a complete refresh.

The display will be 8 times brighter and take 1/8th of the time for a complete refresh. (You obviously also need 8 times as much power supply).

Here you talk about SHIFT_OE which actually drives the shift register parallel latch strobe It isn't an OE it is a strobe.

There is no need to fill the registers with zeros before clocking in new display data and matrix_off() raises the strobe which puts those zeros onto the LEDs until it is raised again after clocking out the new data. You are clocking out twice as many bits as you need to and driving the LEDs for something less than half the possible time.

Depending on your PIC clock speed and how good your C compiler is you may need to optimise some of the code to get acceptable refresh rates.

Reply to
nospam

Thanks a lot for you explanation. What I don't understand is that strobe pin. I thought that was used to bring the buffer to the registers. And indeed there is also a "output enable" pin on the shift register. Can I share the clock and data lines, and have one output enable line for the rows and columns?

Thanks.

Reply to
roxlu

Maybe I'm first gonna try with 8 columns and 16 rows. This means that I'll sink 16 * [ampere_per_led]. Will this be possible with my circuit?

formatting link

Thanks, greetings

Reply to
roxlu

I'd go for either (32 x 16) or (24 x 24) LEDs. Meaning

4 by 2 or 3 by 3 shiftregisters with 8 bits each.

Rene

--
Ing.Buero R.Tschaggelar - http://www.ibrtses.com
& commercial newsgroups - http://www.talkto.net
Reply to
Rene Tschaggelar

Oke thanks, gonna try some possibilities to find out which gives me the best results. BTW, is there a way I could share some lines, like the clock and data? I know its possible, but how can I, when data is shared, put data only into the columns or rows for example?

Greetings and thanks!

Reply to
roxlu

Bonzaii!!!

This one definitely goes in my "Bag-O-PIC-Tricks"!

Luhan

Reply to
Luhan

Unfortunately, it's utterly useless for large arrays as the OP is interested in.

Best regards, Spehro Pefhany

--
"it\'s the network..."                          "The Journey is the reward"
speff@interlog.com             Info for manufacturers: http://www.trexon.com
Embedded software/hardware/analog  Info for designers:  http://www.speff.com
Reply to
Spehro Pefhany

Hi nospam!

I changed my code a bit and its now working with a matrix of 8x40. Thanks for the advice. Though, now I've used 6 pins of my PIC, 3 to control the row flags and 3 for the cascaded column shift register. Is there a way to share some lines such as the clock?

// Define the shift registers pins #define SHIFT_DATA PORTA.F1 #define SHIFT_CLOCK PORTA.F0 #define SHIFT_OE PORTA.F2 // Output enable.

// Here we define the pins for the ROW register. #define SHIFT_ROW_DATA PORTB.F5 #define SHIFT_ROW_CLOCK PORTB.F6 #define SHIFT_ROW_STROBE PORTB.F7

// The a_matrix holds the row values for the 8 columns // in the 8x8 matrix. The keys of the matrix 0-7 are // the columns, the corresponding values the row flags. char a_matrix[8] = { 0b01010101 ,0b01010101 ,0b00100000 ,0b00010000 ,0b00001000 ,0b00000100 ,0b00000010 ,0b00000001 };

char a_columns[40] = { 0b10000000 ,0b01000000 ,0b00100000 ,0b00010000 ,0b00001000 ,0b00000100 ,0b00000010 ,0b00000001 // 8 ,0b00000010 ,0b00000100 ,0b00001000 ,0b00010000 ,0b00100000 ,0b01000000 ,0b10000000 ,0b00000001 // 16 ,0b00000010 ,0b00000100 ,0b00001000 ,0b00010000 ,0b00100000 ,0b01000000 ,0b10000000 ,0b00000001 // 24 ,0b00000010 ,0b00000100 ,0b00001000 ,0b00010000 ,0b00100000 ,0b01000000 ,0b10000000 ,0b00000001 // 32 ,0b00000010 ,0b00000100 ,0b00001000 ,0b00010000 ,0b00100000 ,0b01000000 ,0b10000000 ,0b00010101 // 40

};

char c_in = 0; // Used for serial input. int f_state = 0; // Used to update the matrix. (update_matrix()) int c_col_in = 0; // update_matrix() int c_row_in = 0; // update_matrix(); int c_called = 0; // update_matrix(), counting the number of times method is called int num_cols = 40; int i = 0; int j = 0; int i_col = 0; char c_column; // main() int i_current_column = 0;

// Shift one byte into the shift register. void shift_byte(char pByte) { int i; for (i = 0; i < 8; i++) { // Get bit (1 or 0) at position i of pByte. SHIFT_DATA = (pByte >> i) & 1;

// Make clock pin high and low again, so data is shifted into the registers. SHIFT_CLOCK = 1; SHIFT_CLOCK = 0; } } void shift_column_bit(int fPin) { SHIFT_DATA = fPin; // 0 or 1 SHIFT_CLOCK = 1; SHIFT_CLOCK = 0; }

// This method will fill the row register void set_row_flags(char pByte) { int i; for (i = 0; i < 8; i++) { // Get bit (1 or 0) at position i of pByte. SHIFT_ROW_DATA = (pByte >> i) & 1;

// Make clock pin high and low again, so data is shifted into row register SHIFT_ROW_CLOCK = 1; SHIFT_ROW_CLOCK = 0; } } // This method will shift one bit into the column registers. void put_next_col_on() { // When we are at the first column insert a 1 if (i_current_column == 0) { shift_column_bit(1); } else if (i_current_column >= num_cols) { i_current_column = 0; shift_column_bit(1); } else { shift_column_bit(0); } i_current_column++; }

// This function checks if there is data available // on the serial port. And if so, it will update the // matrix table. The data which is received using rs-232 // is in the format (2 bytes): // [column][row_flags] void update_matrix() { // Read data. if (usart_data_ready() == 1) { c_in = usart_read(); if (f_state == 0) { c_col_in = c_in; f_state = 1; } else if (f_state == 1) { c_row_in = c_in; // not really needed, we can use c_in f_state = 0; if (c_col_in

Reply to
roxlu

You don't absolutely need more than 3 pins, clock, data and strobe.

You can have a 72 bit shift register with the first (or last) 8 bits driving one side of the matrix and the other 64 driving the rest.

Clock out 72 bits of data, pulse the strobe high then low to transfer it from the shift register into the parallel latches and you can start clocking out the next 72 bits immediately. If the LED drivers turn on faster than they turn off there will be a slight overlap of drive which may show as a faint ghost image. It may also take an extra spike of current out of the power supply.

To avoid that use another pin to drive the OE on at least one side of the matrix and disable the outputs for a few microseconds before pulsing the strobe then enable the outputs again.

Personally I would drive 64 columns low (you can get a single chip with 8 or 16 bit shift register, latch and current limited low side drivers) and drive 8 rows high with a 3 to 8 demultiplexer to ensure you can never drive more than one row at a time.

Reply to
nospam

Well, you can serially cascade the 6 shift registers. Meaning the controller only needs the clock & data & latch line.

Rene

--
Ing.Buero R.Tschaggelar - http://www.ibrtses.com
& commercial newsgroups - http://www.talkto.net
Reply to
Rene Tschaggelar

Hi Pubudu,

Thanks a lot for you explanation. I have given it a try and I've got a non-flickering image.... but..I can't get a 'clear/bright' image because the "next" row flags are shown to early but than really vague. Here is an image to make it a bit more clear:

formatting link

I think this problem occurs because the rows are set a bit to early.. The only solution I found to 'fix' these 'double' images is to clear the rows first. I'm not using the output enable of the row register. Is this oke to do, or am I doing something wrong?

And I've got a question about shift reigsters. I get the clock and data pins, but I'm not totally sure about the STROBE and OUTPUT ENABLE pins. The datasheet states, that each shift register stage is transferred to the storage register when the strobe input is high. But what is a 'shift register stage' ? And can the 'storage register' be seen as a buffer? The output enable pin has to be taken high to let the storage register values appear out the output pins, right? So as you can use the strobe to move the data to the storage register only the strobe will be enough for my situation... if I'm correct?

(for my code see below)

Thanks a lot! Greetings.

Here is my new (working) code:

// Define the shift registers pins #define SHIFT_DATA PORTA.F1 #define SHIFT_CLOCK PORTA.F0 #define SHIFT_STROBE PORTA.F2 // Output enable.

// Here we define the pins for the ROW register. #define SHIFT_ROW_DATA PORTB.F5 #define SHIFT_ROW_CLOCK PORTB.F6 #define SHIFT_ROW_STROBE PORTB.F7 #define SHIFT_ROW_OE PORTB.F0

char a_columns[48] = { 0b10000000 ,0b01000000 ,0b00100000 ,0b00010000 ,0b00001000 ,0b00000100 ,0b00000010 ,0b00000001 // 8 ,0b00000010 ,0b00000100 ,0b00001000 ,0b00010000 ,0b00100000 ,0b01000000 ,0b10000000 ,0b00000001 // 16 ,0b00000010 ,0b00000100 ,0b00001000 ,0b00010000 ,0b00100000 ,0b01000000 ,0b10000000 ,0b00000001 // 24 ,0b00000010 ,0b00000100 ,0b00001000 ,0b00010000 ,0b00100000 ,0b01000000 ,0b10000000 ,0b00000001 // 32 ,0b00000010 ,0b00000100 ,0b00001000 ,0b00010000 ,0b00100000 ,0b01000000 ,0b10000000 ,0b11011111 // 40 ,0b00000010 ,0b00000100 ,0b00001000 ,0b00010000 ,0b00100000 ,0b01000000 ,0b10000000 ,0b00010101 // 48

};

//char a_columns[48] = {0}; char c_in = 0; // Used for serial input. int f_state = 0; // Used to update the matrix. (update_matrix()) int c_col_in = 0; // update_matrix() int c_row_in = 0; // update_matrix(); int c_called = 0; // update_matrix(), counting the number of times method is called int num_cols = 48; int i = 0; int j = 0; int i_col = 0; char c_column; // main() int i_current_column = 0;

// Shift one byte into the shift register. void shift_byte(char pByte) { int i; for (i = 0; i < 8; i++) { // Get bit (1 or 0) at position i of pByte. SHIFT_DATA = (pByte >> i) & 1;

// Make clock pin high and low again, so data is shifted into the registers. SHIFT_CLOCK = 1; SHIFT_CLOCK = 0; } } void shift_column_bit(int fPin) { SHIFT_DATA = fPin; // 0 or 1 SHIFT_CLOCK = 1; SHIFT_CLOCK = 0; }

// This method will fill the row register void set_row_flags(char pByte) { int i; for (i = 0; i < 8; i++) { // Get bit (1 or 0) at position i of pByte. SHIFT_ROW_DATA = (pByte >> i) & 1;

// Make clock pin high and low again, so data is shifted into row register SHIFT_ROW_CLOCK = 1; SHIFT_ROW_CLOCK = 0; } } // This method will shift one bit into the column registers. void put_next_col_on() { // When we are at the first column insert a 1 if (i_current_column == 0) { shift_column_bit(1); } else if (i_current_column >= num_cols) { i_current_column = 0; shift_column_bit(1); } else { shift_column_bit(0); } i_current_column++; }

// This function checks if there is data available // on the serial port. And if so, it will update the // matrix table. The data which is received using rs-232 // is in the format (2 bytes): // [column][row_flags] void update_matrix() { // Read data. if (usart_data_ready() == 1) { c_in = usart_read(); if (f_state == 0) { c_col_in = c_in; f_state = 1; } else if (f_state == 1) { c_row_in = c_in; // not really needed, we can use c_in f_state = 0; if (c_col_in

Reply to
roxlu

ElectronDepot website is not affiliated with any of the manufacturers or service providers discussed here. All logos and trade names are the property of their respective owners.