Arduino Mega 2560 and 7 segment LED displays (74HC595) with PWM

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

I can't explain how this is happening, it seems that the PWM signal is affecting the pins for the shift register.

Of course it is! You are sending one bit at a time into the bit register, and with PWM that bit is "randomly" turned on & off. (It's not really random, but there is no logical relationship to the shift-clock.)

PWM also doesn't work with matrixing, since the rows & columns are "sharing" PWM outputs.

Thank you for your reply

Just to be sure that I am understanding you correctly.

I am using different pins for PWM and the data-out for the shift register. For example PWM is on pin 2 and the data-out on 22, clock on 23, latch on 24 and reset on 25. Even in this case the PWM is writing bits into the shift register?

And what do you mean exactly with "matrixing"? Could you please explain further what is exactly shared with the PWM, what do you mean by rows & columns?

Sorry I am new to this ;)