Re: Multiplexing - how to speed up




Pubudu wrote:

roxlu wrote:
Don Lancaster wrote:
roxlu wrote:
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:

http://imagebin.org/5459

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:
<code>
// 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 <= 7)
{
a_matrix[c_col_in] = c_row_in;
c_called = 0;
}
else
{
// usart_write('!');
}
}
}

// We didn't receive anything, reset counter and state.
if (c_called >= 255)
{
c_called = 0;
f_state = 0;
}

c_called++;
}


void main()
{

TRISA = 0x00; // All ports on PORTA are outputs.
PORTA = 0; // All outputs low

// Initialize USART (rs-232)
usart_init(57600);

// Main loop
while(1)
{
update_matrix();
for (i = 0; i < num_cols; i++)
{
matrix_off();
SHIFT_OE = 0;
put_col_on(i);
shift_byte(~a_columns[i]);
SHIFT_OE = 1;
}
}
}

</code>

http://www.tinaja.com/glib/muse152.pdf


--
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@xxxxxxxxxx

Please visit my GURU's LAIR web site at http://www.tinaja.com

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

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.

4. 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.

5. 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.

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: http://imagebin.org/5466

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:
<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 <= num_cols)
{
a_columns[c_col_in] = c_row_in; // changed from a_matrix
c_called = 0;
}
else
{
// usart_write('!');
}
}
}

// We didn't receive anything, reset counter and state.
if (c_called >= 255)
{
c_called = 0;
f_state = 0;
}

c_called++;
}


void main()
{

TRISA = 0x00; // All ports on PORTA are outputs.
PORTA = 0; // All outputs low

// Put all analog functions off, so we can read the switch
CMCON = 0x07;

// For the ROW shift register.
TRISB.F0 = 0; // OUTPUT ENABLE INPUT
TRISB.F5 = 0;
TRISB.F6 = 0;
TRISB.F7 = 0;

// Initialize USART (rs-232)
usart_init(57600);

// Main loop
while(1)
{
update_matrix();


// First clear the rows else we get an 'pre-image'.
SHIFT_ROW_STROBE = 0;
set_row_flags(0b11111111);
SHIFT_ROW_STROBE = 1;

// Select column
SHIFT_STROBE = 0;
put_next_col_on();
SHIFT_STROBE = 1;

// Load the new row flags
SHIFT_ROW_STROBE = 0;
set_row_flags(~a_columns[i_current_column]);
SHIFT_ROW_STROBE = 1;
}
}
</code>

.



Relevant Pages