Hi everyone,
I am using an Arduino Mega 2560 to control 72 high-powered LED 7 segment displays with 72 shift register (74HC595) driving transistors.
The setup:
- 4 rows of 18 digits (consisting 3 blocks of 6 digits)
- every row uses 4 pins on the Arduino Mega 2560 (data, clock, latch, master reset). used pins: 22 to 37 for the 4 rows
- each block of 6 digits has a PWM signal in order to change luminosity of the screens. used pins: 2 to 13 for the 12 blocks
- the numbers displayed on the screens are generated in realtime using MAX MSP and transmitted by serial port to the Arduino.
Serial communication is structured as follow:
There are two types of messages:
- control message, always 2 bytes
- content message, up to 18 bytes (depending on how many digits are active in a row)
The control message sends an index for the next row to be updated and the number of digits consisting the content message. Then Arduino will adjust the serial buffer to the needed size of the next content message and sending an ready-message to MAX MSP. Than MAX MSP sends back the content message up to 18 bytes to fill the digits of row. When the shift register is filled up Arduino returns 255 to indicate that the action has been executed and setting back the serial buffer to 2 bytes. And this way it is iterating through the 4 rows. This is working at the highest speed I can get, at about 15 milliseconds for updating one row.
Everything is working fine until this point. My problem is that I will need to change luminosity on runtime. Luminosity uses also the control message type of two bytes. first byte for index of the block and second byte for the PWM-value. If a PWM-change is needed MAX MSP will wait until the the actual row is finished and then send the PWM control-message before continuing with the next row.
At this point everything is getting out of control: either the segments of the digits are lightened more or less randomly or the entire row is flickering and jumping around.
I can't explain how this is happening, it seems that the PWM signal is affecting the pins for the shift register. When I am setting the PWM signal on setup of the Arduino (not chaining it on runtime) everything is working fine.
Any ideas on whats happening?
Here is the Arduino-code:
const int row_count = 4; //count of rows
const int blk_count = 3; //count of block per row
const int dpb_count = 6; //count of digits per block
const int pin_count = 8; //count of pins used per row
int actual_row;
int binary_pins[row_count][4] = {
/*PORTA*/{0, 1, 2, 3},
/*PORTA*/{4, 5, 6, 7},
/*PORTC*/{7, 6, 5, 4},
/*PORTC*/{3, 2, 1, 0}
};
int digit_pins[row_count][pin_count] = {
{22, 23, 24, 25, 2, 3, 4, 40}, //row 0 [0]data , [1]clock, [2]latch, [3]master reset, [4]pwm_b1, [5]pwm_b2, [6]pwm_b3, [7]control in
{26, 27, 28, 29, 5, 6, 7, 41}, //row 1 [0]data , [1]clock, [2]latch, [3]master reset, [4]pwm_b1, [5]pwm_b2, [6]pwm_b3, [7]control in
{30, 31, 32, 33, 8, 9, 10, 42}, //row 2 [0]data , [1]clock, [2]latch, [3]master reset, [4]pwm_b1, [5]pwm_b2, [6]pwm_b3, [7]control in
{34, 35, 36, 37, 11, 12, 13, 43} //row 3 [0]data , [1]clock, [2]latch, [3]master reset, [4]pwm_b1, [5]pwm_b2, [6]pwm_b3, [7]control in
};
/* DIGIT_PINS
[row][data, clock, latch, masterreset, pwm_b1, pwm_b2, pwm_b3, control in]
*/
boolean phrase_check[row_count]; //[rows] array to verifie if the register was completely filled with bits 0 or 1 [4]
int serial_length;
char serial_from_max[blk_count*dpb_count];
/*SERIAL_FROM_MAX
------------------------------------------------------------------
1. message set digits
[row][block]
row = 0-3 (row index) byte 1 (0-3)
block = 1-3 (block count) byte 2 (1-3)
2. message
[digit]*count
array with all the digits
------------------------------------------------------------------
[row/block][pwm] set luminosity
row = 1x-4x (row index) byte 1 (10-42)
block = x0-x2 (block index) byte 1
pwm = 0-255 (pwm luminosity) byte 2 (0-255) (0,1 = activity)
*/
void setup(){
Serial.begin(115200);
serial_length = 2;
for(int _row=0; _row<row_count; _row++){
phrase_check[_row] = 0; //set all rows to unverified
for(int _pin=0; _pin<pin_count; _pin++){
if(_pin<pin_count-1){ pinMode(digit_pins[_row][_pin], OUTPUT); } //declare outputs
else{ pinMode(digit_pins[_row][_pin], INPUT); } //declare inputs
}
}
resetAllRows();
}
void loop(){
while(Serial.available() > serial_length-1){
Serial.readBytes(serial_from_max, serial_length);
if(serial_length == 2){
byte index = byte(serial_from_max[0]); byte val = byte(serial_from_max[1]);
if(index < 4){
serial_length = val*dpb_count;
actual_row = index;
Serial.write((index*10)+val);
}
else{
setLuminosity(index, val);
Serial.write(255);
}
}
else{
shiftRow(actual_row, serial_length);
serial_length = 2;
Serial.write(255);
}
}
}
void setLuminosity(int _index, int _val){
analogWrite(digit_pins[int(floor((_index-10)/10))][(_index%10)+4], _val);
}
void shiftRow(int _row, int _length){
resetRow(_row);
writeBit(1,0);
for(int _digit=_length-1; _digit>=0; _digit--){
for(int _bit=0; _bit<8; _bit++){
writeBit((serial_from_max[_digit] >> _bit) & 1, _row); //read bit per bit the serial message
}
}
PORTA |= (1 << binary_pins[_row][2]); //latch to 1 causes to display the register
}
void writeBit(boolean _bit, int _row){
if(_row < 2){ //PORTA (pin 22-29)
PORTA = (_bit) ? PORTA | (1 << binary_pins[_row][0]) : PORTA & ~(1 << binary_pins[_row][0]); //data
PORTA |= (1 << binary_pins[_row][1]); //clock 1
PORTA &= ~(1 << binary_pins[_row][1]); //clock 0
}
else{ //PORTC (pin 26-33)
PORTC = (_bit) ? PORTC | (1 << binary_pins[_row][0]) : PORTC & ~(1 << binary_pins[_row][0]); //data
PORTC |= (1 << binary_pins[_row][1]); //clock 1
PORTC &= ~(1 << binary_pins[_row][1]); //clock 0
}
}
void resetRow(int _row){
if(_row < 2){
PORTA &= ~(1 << binary_pins[_row][2]); //latch to 0 bevor filling memory
PORTA &= ~(1 << binary_pins[_row][3]); //master reset 0
PORTA |= (1 << binary_pins[_row][3]); //master reset 1
}
else{
PORTC &= ~(1 << binary_pins[_row][2]); //latch to LOW bevor filling memory
PORTC &= ~(1 << binary_pins[_row][3]); //master reset
PORTC |= (1 << binary_pins[_row][3]); //master reset
}
}
void resetAllRows(){
for(int _row=0; _row<row_count; _row++){
digitalWrite(digit_pins[_row][2], LOW); //latch to LOW to fill the memory
digitalWrite(digit_pins[_row][3], LOW); //master reset
digitalWrite(digit_pins[_row][3], HIGH); //master reset
digitalWrite(digit_pins[_row][2], HIGH); //latch to HIGH to execute the memory
}
}
I hope this is more or less understandable, please ask if you need further informations.
Thanks for help in advance,
wunjeld