Map uint32_t Variable to different PORTn registers

hey folks,

i’d like to know if theres a good way to map long bytes (uint32) to several ports (PORTA … PORTL).

i’m trying to interface 60 leds with an arduino mega. i setup all the i/o pins to act as outputs. i use two uint32_t variables to store the on/off-state of my leds.

now i’m searching for a smart way to map these bits to their specific portregisters? because at the moment my led-mapping is like that:

led port, bit
1 PORTL, 3
2 PORTL, 1
3 PORTB, 3
4 PORTG, 0
5 PORTB, 1

at the moment i’m using an two-dimensional array to access the ledsled[nr][port, bit]
with that array i’m able to build a function like switchon(int led), which would check all the ports. like that:

if(led[nr][0]==0){ PORTA = 1 << led[nr][1]; }
if(led[nr][0]==1){ PORTB = 1 << led[nr][1]; }
if(led[nr][0]==2){ PORTC = 1 << led[nr][1]; }
//etc?

for each PORT? not that nice.

is there a common technique to map 1 long byte to several bytes? any other suggestions on how to make that code faster and slicker are highly appreciated :slight_smile:

thanks in advance!
hans

i do it like this now – hope it is fast enough to support software-pwm for all pins? (but it don’t think so)

this is my function to switch an led ON or OFF

int switch_led (int nr, int state)
{
  byte pos = led[nr][1];
  byte mask = B11111111;
  mask -= 1<<pos;
  byte bit = state<<pos;

  switch(led[nr][0])
  {
  case 0:
    PORTA &= mask;
    PORTA |= bit;
    break;
  case 1:
    PORTB &= mask;
    PORTB |= bit;
    break;
  case 2:
    PORTC &= mask;
    PORTC |= bit;
    break;
  case 3:
    PORTD &= mask;
    PORTD |= bit;
    break;
  case 4:
    PORTE &= mask;
    PORTE |= bit;
    break;
  case 5:
    PORTF &= mask;
    PORTF |= bit;
    break;
  case 6:
    PORTG &= mask;
    PORTG |= bit;
    break;
  case 7:
    PORTH &= mask;
    PORTH |= bit;
    break;
  case 8:
    PORTK &= mask;
    PORTK |= bit;
    break;
  case 9:
    PORTL &= mask;
    PORTL |= bit;
    break;
  }

  return 0;

}

and this is the array (an excerpt) i use for mapping to the correct ports and bits:

byte led[28][2] = {
  {5,3 },
  {5,2},
  {5,1},
  {5,0},
  {7,5}, 
  ...
  {1,4}
  };

I would do it like this:

uint32_t leds[2]; // Stores 1 bit state for each LED
typedef struct {
  uint8_t which;  // Which element of leds[]
  uint8_t bit;       // Which bit of this element
  volatile uint8_t *port; // Which port does this LED go to
  uint8_t pbit;     // Which bit on this port
} ledPort_t;

ledPort_t mapping[] = {
   { 0, 2, &PORTA, 3 }, // leds[0] bit 2 --> PA3
   { 0, 5, &PORTB, 2 }, // leds[0] bit 5 --> PB2
   { 1, 7, &PORTD, 7 }, // leds[1] bit 7 --> PD7
   ... etc ...
};

void scan() {
  int i;
  ledPort_t *ptr;
  for (i=0; i < sizeof(mapping)/sizeof(mapping[0]); i++) {
    ptr = &mapping[i];
    if (leds[ptr->which] & _BV(ptr->bit)) {
       *(ptr->port) |= _BV(ptr->pbit);
    } else {
       *(ptr->port) &= ~_BV(ptr->pbit);
    }
  }
}

wow – thanks a lot. i'll give that a try and reply soon!

ruggedcircuits – thank you so much. it works fine, but:
i can drive both led-banks only from led #1 to led #16

here’s the code i use (please note that i only drive 28, not 30 leds per bank):

uint32_t leds[2]; // Stores 1 bit state for each LED

typedef struct {
  uint8_t which;  // Which element of leds[]
  uint8_t bit;       // Which bit of this element
  volatile uint8_t *port; // Which port does this LED go to
  uint8_t pbit;     // Which bit on this port
} 

ledPort_t;

ledPort_t mapping[] = {
   //ORANGE
   { 0, 0,  &PORTL, 3 },
   { 0, 1,  &PORTL, 1 },
   { 0, 2,  &PORTB, 3 },
   { 0, 3,  &PORTG, 0 },
   { 0, 4,  &PORTB, 1 },
   { 0, 5,  &PORTA, 1 },
   { 0, 6,  &PORTA, 3 },
   { 0, 7,  &PORTK, 2 },
   { 0, 8,  &PORTA, 0 },
   { 0, 9,  &PORTA, 2 },
   { 0, 10, &PORTA, 4 },
   { 0, 11, &PORTA, 6 },
   { 0, 12, &PORTE, 4 },
   { 0, 13, &PORTE, 5 },
   { 0, 14, &PORTG, 5 },
   { 0, 15, &PORTE, 3 },
   { 0, 16, &PORTC, 6 },
   { 0, 17, &PORTD, 1 },
   { 0, 18, &PORTD, 0 },
   { 0, 19, &PORTB, 0 },
   { 0, 20, &PORTB, 2 },
   { 0, 21, &PORTL, 0 },
   { 0, 22, &PORTL, 2 },
   { 0, 23, &PORTL, 4 },
   { 0, 24, &PORTC, 4 },
   { 0, 25, &PORTC, 2 },
   { 0, 26, &PORTC, 0 },
   { 0, 27, &PORTG, 2 },
   
   //WHITE
   { 1, 0,  &PORTF, 3 },
   { 1, 1,  &PORTF, 2 },
   { 1, 2,  &PORTF, 1 },
   { 1, 3,  &PORTF, 0 },
   { 1, 4,  &PORTH, 5 },
   { 1, 5,  &PORTH, 6 },
   { 1, 6,  &PORTB, 4 },
   { 1, 7,  &PORTB, 5 },
   { 1, 8,  &PORTK, 6 },
   { 1, 9,  &PORTK, 5 },
   { 1, 10, &PORTK, 4 },
   { 1, 11, &PORTK, 3 },
   { 1, 12, &PORTD, 7 },
   { 1, 13, &PORTG, 1 },
   { 1, 14, &PORTL, 7 },
   { 1, 15, &PORTL, 5 },
   { 1, 16, &PORTK, 1 },
   { 1, 17, &PORTK, 0 },
   { 1, 18, &PORTC, 7 },
   { 1, 19, &PORTC, 5 },
   { 1, 20, &PORTC, 3 },
   { 1, 21, &PORTH, 4 },
   { 1, 22, &PORTB, 7 },
   { 1, 23, &PORTB, 6 },
   { 1, 24, &PORTF, 7 },
   { 1, 25, &PORTF, 6 },
   { 1, 26, &PORTF, 5 },
   { 1, 27, &PORTF, 4 }
};

void clear_ports(void)
{
  PORTA = 0x00;
  PORTB = 0x00;
  PORTC = 0x00;
  PORTD = 0x00;
  PORTE = 0x00;
  PORTF = 0x00;
  PORTG = 0x00;
  PORTH = 0x00;
  PORTJ = 0x00;
  PORTK = 0x00;
  PORTL = 0x00;
}


void scan() 
{
  ledPort_t *ptr;
  
  int i;  
  for (i=0; i < sizeof(mapping)/sizeof(mapping[0]); i++)
  {
    ptr = &mapping[i];
    
    if (leds[ptr->which] & _BV(ptr->bit)){
       *(ptr->port) |= _BV(ptr->pbit);
    }
    else{
       *(ptr->port) &= ~_BV(ptr->pbit);
    }
  }
}

void setup()
{
  Serial.begin(19200);

  DDRA  = 0xff;
  DDRB  = 0xff;
  DDRC  = 0xff;
  DDRD  = 0xff;
  DDRE  = 0xff;
  DDRF  = 0xff;
  DDRG  = 0xff;
  DDRH  = 0xff;
  DDRJ  = 0xff;
  DDRK  = 0xff;
  DDRL  = 0xff;

  clear_ports();
}

void loop()
{
  int i;
  for(i=0; i<28; i++){
    leds[0] |= 1<<i;
    scan();
    delay(200);
  }
}

Hmm…sounds like you’re running up against the 16-bit word size for integers. Try replacing:

leds[0] |= 1<<i;

with:

leds[0] |= 1UL<<i;

Similarly, in the code I suggested replace:

_BV(xxx)

with:

(1UL << xxx)

thanks again! the _BV replacement did the job!

i’m trying to implement pwm and serial communication. the pwm part works somehow. when i set the PWM_STEPS to 10 it stops flickering. i think the for-loops are a bit too slow?

but anyway: serial doesn’t work together with my software-pwm (timer1).

maybe someone of you spots my error:

#define F_CPU 16000000UL                  // Systemtakt in Hz
#define F_PWM 100                       // PWM-Frequenz in Hz
#define PWM_STEPS 10                   // PWM-Schritte pro Zyklus(1..256)

// ab hier nichts ändern, wird alles berechnet
#define T_PWM (F_CPU/(F_PWM*PWM_STEPS)) // Systemtakte pro PWM-Takt
 
#if (T_PWM<(152+5))
    #error T_PWM zu klein, F_CPU muss vergrösst werden oder F_PWM oder PWM_STEPS verkleinert werden
#endif

 
// globale Variablen
volatile uint8_t pwm_orange[28];  // Einstellungen für die einzelnen PWM-Kanäle
volatile uint8_t pwm_white[28];   // Einstellungen für die einzelnen PWM-Kanäle
volatile uint32_t leds[2];        // Stores 1 bit state for each LED

// Timer 1 Output COMPARE A Interrupt
ISR(TIMER1_COMPA_vect) {
    static uint8_t pwm_cnt=0;  //# wird hochgezählt
    uint32_t tmp_orange=0, tmp_white=0;
    uint8_t i=0;
    uint32_t j=1;   //# zähler 
 
    OCR1A += (uint16_t)T_PWM;  //# interner counter
 
    for (i=0; i<28; i++) 
    {    
          if (pwm_orange[i] > pwm_cnt) tmp_orange |= j; //wenn größer - setze auf 1
          if (pwm_white[i] > pwm_cnt) tmp_white |= j; //wenn größer - setze auf 1    
        j <<= 1UL;    
    }
    
    leds[0] = tmp_orange;                         // PWMs aktualisieren
    leds[1] = tmp_white;                         // PWMs aktualisieren

    scan();
    
    if (pwm_cnt==(uint8_t)(PWM_STEPS-1))
        pwm_cnt=0;
    else
        pwm_cnt++;
}

typedef struct {
  uint8_t which;  // Which element of leds[]
  uint8_t bit;       // Which bit of this element
  volatile uint8_t *port; // Which port does this LED go to
  uint8_t pbit;     // Which bit on this port
} ledPort_t;

ledPort_t mapping[] = {
   //ORANGE
   { 0, 0,  &PORTL, 3 },
   { 0, 1,  &PORTL, 1 },
   { 0, 2,  &PORTB, 3 },
   { 0, 3,  &PORTG, 0 },
   { 0, 4,  &PORTB, 1 },
   { 0, 5,  &PORTA, 1 },
   { 0, 6,  &PORTA, 3 },
   { 0, 7,  &PORTK, 2 },
   { 0, 8,  &PORTA, 0 },
   { 0, 9,  &PORTA, 2 },
   { 0, 10, &PORTA, 4 },
   { 0, 11, &PORTA, 6 },
   { 0, 12, &PORTE, 4 },
   { 0, 13, &PORTE, 5 },
   { 0, 14, &PORTG, 5 },
   { 0, 15, &PORTE, 3 },
   { 0, 16, &PORTC, 6 },
   { 0, 17, &PORTD, 1 },
   { 0, 18, &PORTD, 0 },
   { 0, 19, &PORTB, 0 },
   { 0, 20, &PORTB, 2 },
   { 0, 21, &PORTL, 0 },
   { 0, 22, &PORTL, 2 },
   { 0, 23, &PORTL, 4 },
   { 0, 24, &PORTC, 4 },
   { 0, 25, &PORTC, 2 },
   { 0, 26, &PORTC, 0 },
   { 0, 27, &PORTG, 2 },
   
   //WHITE
   { 1, 0,  &PORTF, 3 },
   { 1, 1,  &PORTF, 2 },
   { 1, 2,  &PORTF, 1 },
   { 1, 3,  &PORTF, 0 },
   { 1, 4,  &PORTH, 5 },
   { 1, 5,  &PORTH, 6 },
   { 1, 6,  &PORTB, 4 },
   { 1, 7,  &PORTB, 5 },
   { 1, 8,  &PORTK, 6 },
   { 1, 9,  &PORTK, 5 },
   { 1, 10, &PORTK, 4 },
   { 1, 11, &PORTK, 3 },
   { 1, 12, &PORTD, 7 },
   { 1, 13, &PORTG, 1 },
   { 1, 14, &PORTL, 7 },
   { 1, 15, &PORTL, 5 },
   { 1, 16, &PORTK, 1 },
   { 1, 17, &PORTK, 0 },
   { 1, 18, &PORTC, 7 },
   { 1, 19, &PORTC, 5 },
   { 1, 20, &PORTC, 3 },
   { 1, 21, &PORTH, 4 },
   { 1, 22, &PORTB, 7 },
   { 1, 23, &PORTB, 6 },
   { 1, 24, &PORTF, 7 },
   { 1, 25, &PORTF, 6 },
   { 1, 26, &PORTF, 5 },
   { 1, 27, &PORTF, 4 }
};

void clear_ports(void)
{
  PORTA = 0x00;
  PORTB = 0x00;
  PORTC = 0x00;
  PORTD = 0x00;
  PORTE = 0x00;
  PORTF = 0x00;
  PORTG = 0x00;
  PORTH = 0x00;
  PORTJ = 0x00;
  PORTK = 0x00;
  PORTL = 0x00;
}


void scan() 
{
  ledPort_t *ptr;
  
  int i;  
  
  
  for (i=0; i < sizeof(mapping)/sizeof(mapping[0]); i++)
  //for (i=0; i < 28; i++)
  {
    ptr = &mapping[i];
    
    if (leds[ptr->which] & (1UL << ptr->bit)){
       *(ptr->port) |= (1UL << ptr->pbit);
    }
    else{
       *(ptr->port) &= ~_BV(ptr->pbit);
    }
  }
}

void setup()
{

  DDRA  = 0xff;
  DDRB  = 0xff;
  DDRC  = 0xff;
  DDRD  = 0xff;
  DDRE  = 0xff;
  DDRF  = 0xff;
  DDRG  = 0xff;
  DDRH  = 0xff;
  DDRJ  = 0xff;
  DDRK  = 0xff;
  DDRL  = 0xff;

  clear_ports();
  
  Serial.begin(19200);
  
  // Timer 1 OCRA1, als variablem Timer nutzen
  TCCR1B = 1;             // Timer läuft mit vollem Systemtakt
  TIMSK1 |= (1<<OCIE1A);   // Interrupt freischalten
  
  int i;
  for(i=0; i<28; i++)
    pwm_orange[i] = i;

  sei();                  // Interrupts gloabl einschalten
}

byte byte1, byte2;

void loop()
{

  Serial.println("HALLO");
  delay(200);

}

thanks for your time.

bye bye – hans