best solution for 12 analog outputs (5 bits)

dhenry:

How does that solve your problem?

It can be made to work.

Let's say that each time block is 1ms. You need 32ms to send 5 bits of data.

Run your timer isr in 1ms, 2ms, 4ms, 8ms, 16ms chunks. And set pins based on the data bits for each of those data chunks - essentially PPM.

Some pseudo code:

#define ANALOG_RESOLUTION 5 //analog resoultion, in bits

#define ANALOG_CHANNEL 12 //analog channel

const unsigned short duration[ANALOG_RESOLUTION]={1000, 2000, 4000, 8000, 16000}; //duration
unsigned char analog_output[ANALOG_CHANNEL]={2, 5, 1, 19, 2, ...};

timer_isr {
 static unsigned char i=0;
 set timer to interrupt after duration[i];
 increment i;
 if i = ANALOG_RESOLUTION then i = 0;
 loop:
   if analog_output[ch] & (1<<i) then set output pin for ch;
   else clear output pin for ch;
   increment ch;
 end loop
}




If you don't like ripples, you can shrink the minimum time block from 1ms to something smaller but I would go below 30 ticks (2us for an AVR at 16Mhz).

I understand the principles of this solution, but not the details in de pseudocode. Is it true that timer_isr is called at the beginning of each chuck, so after 1 ms, after 2 ms, after 4 ms and so on? Is it possible to change the timing of the interrupt within the timer_isr? If this is true then I don't understand the 1st line in timer_isr: where i is defined as 0.

This short sketch probably helps you understand how it works.

//generate multiple ppm output

//pin configuration
#define PWM_PORT    PORTB
#define PWM_DDR     DDRB
#define PWM0        (1<<0)
#define PWM1        (1<<1)
#define PWM2        (1<<2)
#define PWM3        (1<<3)
#define PWM4        (1<<4)
#define PWM5        (1<<5)
//pin configuration

//defining timing constants
#define PWM_ms      (F_CPU / 64 / 1000)  //milli seconds, 64:1 prescaler
#define PWM_1ms     (PWM_ms)
#define PWM_2ms     (PWM_1ms * 2)
#define PWM_4ms     (PWM_2ms * 2)
#define PWM_8ms     (PWM_4ms * 2)
#define PWM_16ms    (PWM_8ms * 2)
#define PWM_32ms    (PWM_16ms * 2)

//ppm parameters
const unsigned short pwm_duration[]={
  PWM_1ms,
  PWM_2ms,
  PWM_4ms,
  PWM_8ms,
  PWM_16ms
};

//durations
unsigned char duration[]={
  1,                                //expected output: 1 / 31 * 5v
  4,                                //expected output: 4 / 31 * 5v
  8,                                //expected output: 8 / 31 * 5v
  16,                                //expected output: 16 / 31 * 5v
  30,                                //expected output: 30 / 31 * 5v
  7};                                //expected output: 7 / 31 * 5v
  
//tmr1 ctc isr
ISR(TIMER1_COMPA_vect) {
  static unsigned char index = 0;    //index
  
  //update the period
  OCR1A = pwm_duration[index];
  
  //change the pins
  if (duration[0] & (1<<index)) PWM_PORT |= PWM0;  //set pwm0
  else PWM_PORT &=~PWM0;                        //clear pwm0
  
  if (duration[1] & (1<<index)) PWM_PORT |= PWM1;  //set pwm1
  else PWM_PORT &=~PWM1;                        //clear pwm1
  
  if (duration[2] & (1<<index)) PWM_PORT |= PWM2;  //set pwm2
  else PWM_PORT &=~PWM2;                        //clear pwm2
  
  if (duration[3] & (1<<index)) PWM_PORT |= PWM3;  //set pwm3
  else PWM_PORT &=~PWM3;                        //clear pwm3
  
  if (duration[4] & (1<<index)) PWM_PORT |= PWM4;  //set pwm4
  else PWM_PORT &=~PWM4;                        //clear pwm4
  
  if (duration[5] & (1<<index)) PWM_PORT |= PWM5;  //set pwm5
  else PWM_PORT &=~PWM5;                        //clear pwm5
  
  //update index
  index += 1;                                   // incrment index
  if (index == 5) index = 0;                    //wrap around index
}

//set up the timer1, 64:1 prescaler
void tmr1_init(unsigned short period) {
  TCCR1B &=~0x07;    //stop tmr1
  
  //set up tccr1a
  TCCR1A =    (0<<COM1A1) | (0<<COM1A0) |    //com1a10 = 0b00, normal operations
              (0<<COM1B1) | (0<<COM1B0) |    //com1b10 = 0b00, normal operations
              (0<<WGM11) | (0<<WGM10);       //wgm2..0 = 0b0100, top at OCR1A
  TCCR1B =    (0<<ICNC1) | (0<<ICES1) |      //disable input capture noise canceller and input capture edge select
              (0<<WGM13) | (1<<WGM12) |      //wgm3..0 = 0b0100, top at OCR1A
              (0<<CS12) | (1<<CS11) | (1<<CS10); //cs2..0 = 0b0011, 64:1 prescaler
  TCCR1C = 0x00;

  //reset the timer/counter
  TCNT1 = 0x0000;                            //reset the timer
  
  //load the period
  OCR1A = period;

  //reset the flag
  TIFR1 |= (1<<OCF1A);
  
  //enable the interrupt
  TIMSK1 |= (1<<OCIE1A);
}

void setup(void) {
  //initialize the output pins
  PWM_PORT |= PWM0 | PWM1 | PWM2 | PWM3 | PWM4 | PWM5; //set the pins
  PWM_DDR  |= PWM0 | PWM1 | PWM2 | PWM3 | PWM4 | PWM5; //set up the pins for output
  
  //set up the timer
  tmr1_init(PWM_1ms);
  
  //enable interrupt
  sei();
}

void loop(void) {
}

I implemented just 6 channels of output, on PORTB. duration[] specifies the corresponding output voltage, as expressed in 0..31.

Timing steps are defined in pwm_duration[], in 5 steps, from 1ms - 16ms.

The entire code is done in an isr and no impact on your user code.

Now, it is not exactly what you wanted (fewer channels and more steps) but you can easily modify it to your needs.

Note: as this approach (PPM) is essentially PWM. It suffers all the drawbacks of any PWM approach - a compromise between ripple and response time.

You have to make a decision whether it works for you or not.

dhenry:
This short sketch probably helps you understand how it works.

//generate multiple ppm output

//pin configuration
#define PWM_PORT    PORTB
#define PWM_DDR     DDRB
#define PWM0        (1<<0)
#define PWM1        (1<<1)
#define PWM2        (1<<2)
#define PWM3        (1<<3)
#define PWM4        (1<<4)
#define PWM5        (1<<5)
//pin configuration

//defining timing constants
#define PWM_ms      (F_CPU / 64 / 1000)  //milli seconds, 64:1 prescaler
#define PWM_1ms     (PWM_ms)
#define PWM_2ms     (PWM_1ms * 2)
#define PWM_4ms     (PWM_2ms * 2)
#define PWM_8ms     (PWM_4ms * 2)
#define PWM_16ms    (PWM_8ms * 2)
#define PWM_32ms    (PWM_16ms * 2)

//ppm parameters
const unsigned short pwm_duration[]={
  PWM_1ms,
  PWM_2ms,
  PWM_4ms,
  PWM_8ms,
  PWM_16ms
};

//durations
unsigned char duration[]={
  1,                                //expected output: 1 / 31 * 5v
  4,                                //expected output: 4 / 31 * 5v
  8,                                //expected output: 8 / 31 * 5v
  16,                                //expected output: 16 / 31 * 5v
  30,                                //expected output: 30 / 31 * 5v
  7};                                //expected output: 7 / 31 * 5v
 
//tmr1 ctc isr
ISR(TIMER1_COMPA_vect) {
  static unsigned char index = 0;    //index
 
  //update the period
  OCR1A = pwm_duration[index];
 
  //change the pins
  if (duration[0] & (1<<index)) PWM_PORT |= PWM0;  //set pwm0
  else PWM_PORT &=~PWM0;                        //clear pwm0
 
  if (duration[1] & (1<<index)) PWM_PORT |= PWM1;  //set pwm1
  else PWM_PORT &=~PWM1;                        //clear pwm1
 
  if (duration[2] & (1<<index)) PWM_PORT |= PWM2;  //set pwm2
  else PWM_PORT &=~PWM2;                        //clear pwm2
 
  if (duration[3] & (1<<index)) PWM_PORT |= PWM3;  //set pwm3
  else PWM_PORT &=~PWM3;                        //clear pwm3
 
  if (duration[4] & (1<<index)) PWM_PORT |= PWM4;  //set pwm4
  else PWM_PORT &=~PWM4;                        //clear pwm4
 
  if (duration[5] & (1<<index)) PWM_PORT |= PWM5;  //set pwm5
  else PWM_PORT &=~PWM5;                        //clear pwm5
 
  //update index
  index += 1;                                   // incrment index
  if (index == 5) index = 0;                    //wrap around index
}

//set up the timer1, 64:1 prescaler
void tmr1_init(unsigned short period) {
  TCCR1B &=~0x07;    //stop tmr1
 
  //set up tccr1a
  TCCR1A =    (0<<COM1A1) | (0<<COM1A0) |    //com1a10 = 0b00, normal operations
              (0<<COM1B1) | (0<<COM1B0) |    //com1b10 = 0b00, normal operations
              (0<<WGM11) | (0<<WGM10);       //wgm2..0 = 0b0100, top at OCR1A
  TCCR1B =    (0<<ICNC1) | (0<<ICES1) |      //disable input capture noise canceller and input capture edge select
              (0<<WGM13) | (1<<WGM12) |      //wgm3..0 = 0b0100, top at OCR1A
              (0<<CS12) | (1<<CS11) | (1<<CS10); //cs2..0 = 0b0011, 64:1 prescaler
  TCCR1C = 0x00;

//reset the timer/counter
  TCNT1 = 0x0000;                            //reset the timer
 
  //load the period
  OCR1A = period;

//reset the flag
  TIFR1 |= (1<<OCF1A);
 
  //enable the interrupt
  TIMSK1 |= (1<<OCIE1A);
}

void setup(void) {
  //initialize the output pins
  PWM_PORT |= PWM0 | PWM1 | PWM2 | PWM3 | PWM4 | PWM5; //set the pins
  PWM_DDR  |= PWM0 | PWM1 | PWM2 | PWM3 | PWM4 | PWM5; //set up the pins for output
 
  //set up the timer
  tmr1_init(PWM_1ms);
 
  //enable interrupt
  sei();
}

void loop(void) {
}




I implemented just 6 channels of output, on PORTB. duration[] specifies the corresponding output voltage, as expressed in 0..31.

Timing steps are defined in pwm_duration[], in 5 steps, from 1ms - 16ms.

The entire code is done in an isr and no impact on your user code.

Now, it is not exactly what you wanted (fewer channels and more steps) but you can easily modify it to your needs.

Thanks a lot for this sketch! I am going to test it.

I changed the code slightly to make it easier and more consistent.

//generate multiple ppm output

//pin configuration
#define PWM_PORT    PORTB
#define PWM_DDR     DDRB
#define PWM0        (1<<0)
#define PWM1        (1<<1)
#define PWM2        (1<<2)
#define PWM3        (1<<3)
#define PWM4        (1<<4)
#define PWM5        (1<<5)

//set pwm duration for each bit
#define PWM_Bit     (PWM_100us)     //duration of 1 bit
//pin configuration

//defining timing constants
#define PWM_ms      (F_CPU / 64 / 1000)  //milli seconds, 64:1 prescaler
#define PWM_1ms     (PWM_ms)
#define PWM_500us   (PWM_1ms / 2)
#define PWM_250us   (PWM_500us / 2)
#define PWM_100us   (PWM_500us / 5)
#define PWM_2ms     (PWM_1ms * 2)
#define PWM_5ms     (PWM_1ms * 5)
#define PWM_10ms    (PWM_1ms * 10)

//duration examples for 6 channels
unsigned char duration[]={
  1,                                //expected output: 1 / 31 * 5v
  4,                                //expected output: 4 / 31 * 5v
  8,                                //expected output: 8 / 31 * 5v
  16,                                //expected output: 16 / 31 * 5v
  30,                                //expected output: 30 / 31 * 5v
  7};                                //expected output: 7 / 31 * 5v
  
//tmr1 ctc isr
ISR(TIMER1_COMPA_vect) {
  static unsigned char index = 0;    //index
  
  //update the period
  if (index) OCR1A = OCR1A * 2;
  else OCR1A = PWM_Bit;
  
  //change the pins
  if (duration[0] & (1<<index)) PWM_PORT |= PWM0;  //set pwm0
  else PWM_PORT &=~PWM0;                        //clear pwm0
  
  if (duration[1] & (1<<index)) PWM_PORT |= PWM1;  //set pwm1
  else PWM_PORT &=~PWM1;                        //clear pwm1
  
  if (duration[2] & (1<<index)) PWM_PORT |= PWM2;  //set pwm2
  else PWM_PORT &=~PWM2;                        //clear pwm2
  
  if (duration[3] & (1<<index)) PWM_PORT |= PWM3;  //set pwm3
  else PWM_PORT &=~PWM3;                        //clear pwm3
  
  if (duration[4] & (1<<index)) PWM_PORT |= PWM4;  //set pwm4
  else PWM_PORT &=~PWM4;                        //clear pwm4
  
  if (duration[5] & (1<<index)) PWM_PORT |= PWM5;  //set pwm5
  else PWM_PORT &=~PWM5;                        //clear pwm5
  
  //update index
  index += 1;                                   // incrment index
  if (index == 5) index = 0;                    //wrap around index
}

//set up the timer1, 64:1 prescaler
void tmr1_init(unsigned short period) {
  TCCR1B &=~0x07;    //stop tmr1
  
  //set up tccr1a
  TCCR1A =    (0<<COM1A1) | (0<<COM1A0) |    //com1a10 = 0b00, normal operations
              (0<<COM1B1) | (0<<COM1B0) |    //com1b10 = 0b00, normal operations
              (0<<WGM11) | (0<<WGM10);       //wgm2..0 = 0b0100, top at OCR1A
  TCCR1B =    (0<<ICNC1) | (0<<ICES1) |      //disable input capture noise canceller and input capture edge select
              (0<<WGM13) | (1<<WGM12) |      //wgm3..0 = 0b0100, top at OCR1A
              (0<<CS12) | (1<<CS11) | (1<<CS10); //cs2..0 = 0b0011, 64:1 prescaler
  TCCR1C = 0x00;

  //reset the timer/counter
  TCNT1 = 0x0000;                            //reset the timer
  
  //load the period
  OCR1A = period;

  //reset the flag
  TIFR1 |= (1<<OCF1A);
  
  //enable the interrupt
  TIMSK1 |= (1<<OCIE1A);
}

void setup(void) {
  //initialize the output pins
  PWM_PORT |= PWM0 | PWM1 | PWM2 | PWM3 | PWM4 | PWM5; //set the pins
  PWM_DDR  |= PWM0 | PWM1 | PWM2 | PWM3 | PWM4 | PWM5; //set up the pins for output
  
  //set up the timer
  tmr1_init(PWM_Bit);
  
  //enable interrupt
  sei();
}

void loop(void) {
}

dhenry:
I changed the code slightly to make it easier and more consistent.

//generate multiple ppm output

//pin configuration
#define PWM_PORT    PORTB
#define PWM_DDR     DDRB
#define PWM0        (1<<0)
#define PWM1        (1<<1)
#define PWM2        (1<<2)
#define PWM3        (1<<3)
#define PWM4        (1<<4)
#define PWM5        (1<<5)

//set pwm duration for each bit
#define PWM_Bit     (PWM_100us)     //duration of 1 bit
//pin configuration

//defining timing constants
#define PWM_ms      (F_CPU / 64 / 1000)  //milli seconds, 64:1 prescaler
#define PWM_1ms     (PWM_ms)
#define PWM_500us   (PWM_1ms / 2)
#define PWM_250us   (PWM_500us / 2)
#define PWM_100us   (PWM_500us / 5)
#define PWM_2ms     (PWM_1ms * 2)
#define PWM_5ms     (PWM_1ms * 5)
#define PWM_10ms    (PWM_1ms * 10)

//duration examples for 6 channels
unsigned char duration[]={
  1,                                //expected output: 1 / 31 * 5v
  4,                                //expected output: 4 / 31 * 5v
  8,                                //expected output: 8 / 31 * 5v
  16,                                //expected output: 16 / 31 * 5v
  30,                                //expected output: 30 / 31 * 5v
  7};                                //expected output: 7 / 31 * 5v
 
//tmr1 ctc isr
ISR(TIMER1_COMPA_vect) {
  static unsigned char index = 0;    //index
 
  //update the period
  if (index) OCR1A = OCR1A * 2;
  else OCR1A = PWM_Bit;
 
  //change the pins
  if (duration[0] & (1<<index)) PWM_PORT |= PWM0;  //set pwm0
  else PWM_PORT &=~PWM0;                        //clear pwm0
 
  if (duration[1] & (1<<index)) PWM_PORT |= PWM1;  //set pwm1
  else PWM_PORT &=~PWM1;                        //clear pwm1
 
  if (duration[2] & (1<<index)) PWM_PORT |= PWM2;  //set pwm2
  else PWM_PORT &=~PWM2;                        //clear pwm2
 
  if (duration[3] & (1<<index)) PWM_PORT |= PWM3;  //set pwm3
  else PWM_PORT &=~PWM3;                        //clear pwm3
 
  if (duration[4] & (1<<index)) PWM_PORT |= PWM4;  //set pwm4
  else PWM_PORT &=~PWM4;                        //clear pwm4
 
  if (duration[5] & (1<<index)) PWM_PORT |= PWM5;  //set pwm5
  else PWM_PORT &=~PWM5;                        //clear pwm5
 
  //update index
  index += 1;                                   // incrment index
  if (index == 5) index = 0;                    //wrap around index
}

//set up the timer1, 64:1 prescaler
void tmr1_init(unsigned short period) {
  TCCR1B &=~0x07;    //stop tmr1
 
  //set up tccr1a
  TCCR1A =    (0<<COM1A1) | (0<<COM1A0) |    //com1a10 = 0b00, normal operations
              (0<<COM1B1) | (0<<COM1B0) |    //com1b10 = 0b00, normal operations
              (0<<WGM11) | (0<<WGM10);       //wgm2..0 = 0b0100, top at OCR1A
  TCCR1B =    (0<<ICNC1) | (0<<ICES1) |      //disable input capture noise canceller and input capture edge select
              (0<<WGM13) | (1<<WGM12) |      //wgm3..0 = 0b0100, top at OCR1A
              (0<<CS12) | (1<<CS11) | (1<<CS10); //cs2..0 = 0b0011, 64:1 prescaler
  TCCR1C = 0x00;

//reset the timer/counter
  TCNT1 = 0x0000;                            //reset the timer
 
  //load the period
  OCR1A = period;

//reset the flag
  TIFR1 |= (1<<OCF1A);
 
  //enable the interrupt
  TIMSK1 |= (1<<OCIE1A);
}

void setup(void) {
  //initialize the output pins
  PWM_PORT |= PWM0 | PWM1 | PWM2 | PWM3 | PWM4 | PWM5; //set the pins
  PWM_DDR  |= PWM0 | PWM1 | PWM2 | PWM3 | PWM4 | PWM5; //set up the pins for output
 
  //set up the timer
  tmr1_init(PWM_Bit);
 
  //enable interrupt
  sei();
}

void loop(void) {
}

Just for confirmation: is it true that the PWM frequency is 500 Hz with a PWM_Bit duration of 1 ms?

it would be 31*1ms.

1ms is for the shortest duration (a '1' at bit 0 if you will).

A short PWM_Bit will improve ripple, and response time, but it would tie up the mcu more.

I think 50us is probably the bare minimum you can go and I would actually go with a 1ms as that is more than sufficient for your stated response time of 1s.

dhenry:
it would be 31*1ms.

1ms is for the shortest duration (a '1' at bit 0 if you will).

A short PWM_Bit will improve ripple, and response time, but it would tie up the mcu more.

I think 50us is probably the bare minimum you can go and I would actually go with a 1ms as that is more than sufficient for your stated response time of 1s.

I tested 50 us and it worked for 12 channels. This means a PWM frequency of 1/31*50 us = 645 Hz.

Tested it with a R of 1 kOhm and a C of 47 uF -> this gives a ripple of 0.04 V p-p
Tested it with a R of 1 kOhm and a C of 4.7 uF -> this gives a ripple of 0.4 V p-p

A 1k resistor is likely too small. I would go with 10k or 100k if possible.

One short note: the 1st solution I provided can be easily modified to incorporate gamma correction.

The 2nd solution I provided allows easy adjustment of pwm frequency.

Otherwise, they are identical.

Quick note: not sure how you operated the output pins in the isr. You should not use digitalWrite() there, unless digitalWrite() is not used anywhere else.

I operated on the port to avoid that problem.

dhenry:
A 1k resistor is likely too small. I would go with 10k or 100k if possible.

10k or higher is not possible without additional buffering of the output signal, because of the impedance of the load (see circuit)

I use this sketch:

//generate multiple ppm output

//pin configuration
#define PWM_PORT1    PORTB
#define PWM_DDR1     DDRB
#define PWM_PORT2    PORTD
#define PWM_DDR2     DDRD
#define PWM0        (1<<0)
#define PWM1        (1<<1)
#define PWM2        (1<<2)
#define PWM3        (1<<3)
#define PWM4        (1<<4)
#define PWM5        (1<<5)
#define PWM6        (1<<2)
#define PWM7        (1<<3)
#define PWM8        (1<<4)
#define PWM9        (1<<5)
#define PWM10       (1<<6)
#define PWM11       (1<<7)

//set pwm duration for each bit
#define PWM_Bit     (PWM_50us)     //duration of 1 bit
//pin configuration

//defining timing constants
#define PWM_ms      (F_CPU / 64 / 1000)  //milli seconds, 64:1 prescaler
#define PWM_1ms     (PWM_ms)
#define PWM_500us   (PWM_1ms / 2)
#define PWM_250us   (PWM_500us / 2)
#define PWM_100us   (PWM_500us / 5)
#define PWM_50us    (PWM_500us / 10)
#define PWM_2ms     (PWM_1ms * 2)
#define PWM_5ms     (PWM_1ms * 5)
#define PWM_10ms    (PWM_1ms * 10)

//duration examples for 12 channels
unsigned char duration[]={
  0,              //expected output: 0 / 31 * 5v
  5,              //expected output: 5 / 31 * 5v
  10,             //expected output: 10 / 31 * 5v
  14,             //expected output: 14 / 31 * 5v
  18,             //expected output: 18 / 31 * 5v
  23,             //expected output: 23 / 31 * 5v
  31,             //expected output: 31 / 31 * 5v
  5,              //expected output: 5 / 31 * 5v
  10,             //expected output: 10 / 31 * 5v
  14,             //expected output: 14 / 31 * 5v
  18,             //expected output: 18 / 31 * 5v
  23};            //expected output: 23 / 31 * 5v
        
  
//tmr1 ctc isr
ISR(TIMER1_COMPA_vect) {
  static unsigned char index = 0;    //index
  
  //update the period
  if (index) OCR1A = OCR1A * 2;
  else OCR1A = PWM_Bit;
  
  //change the pins
  if (duration[0] & (1<<index)) PWM_PORT1 |= PWM0;  //set pwm0
  else PWM_PORT1 &=~PWM0;                        //clear pwm0
  
  if (duration[1] & (1<<index)) PWM_PORT1 |= PWM1;  //set pwm1
  else PWM_PORT1 &=~PWM1;                        //clear pwm1
  
  if (duration[2] & (1<<index)) PWM_PORT1 |= PWM2;  //set pwm2
  else PWM_PORT1 &=~PWM2;                        //clear pwm2
  
  if (duration[3] & (1<<index)) PWM_PORT1 |= PWM3;  //set pwm3
  else PWM_PORT1 &=~PWM3;                        //clear pwm3
  
  if (duration[4] & (1<<index)) PWM_PORT1 |= PWM4;  //set pwm4
  else PWM_PORT1 &=~PWM4;                        //clear pwm4
  
  if (duration[5] & (1<<index)) PWM_PORT1 |= PWM5;  //set pwm5
  else PWM_PORT1 &=~PWM5;                        //clear pwm5
  
  if (duration[6] & (1<<index)) PWM_PORT2 |= PWM6;  //set pwm6
  else PWM_PORT2 &=~PWM6;                        //clear pwm6
  
  if (duration[7] & (1<<index)) PWM_PORT2 |= PWM7;  //set pwm7
  else PWM_PORT2 &=~PWM7;                        //clear pwm7
  
  if (duration[8] & (1<<index)) PWM_PORT2 |= PWM8;  //set pwm8
  else PWM_PORT2 &=~PWM8;                        //clear pwm8
  
  if (duration[9] & (1<<index)) PWM_PORT2 |= PWM9;  //set pwm9
  else PWM_PORT2 &=~PWM9;                        //clear pwm9
  
  if (duration[10] & (1<<index)) PWM_PORT2 |= PWM10;  //set pwm10
  else PWM_PORT2 &=~PWM10;                        //clear pwm10
  
  if (duration[11] & (1<<index)) PWM_PORT2 |= PWM11;  //set pwm11
  else PWM_PORT2 &=~PWM11;                        //clear pwm11
  
  //update index
  index += 1;                                   // incrment index
  if (index == 5) index = 0;                    //wrap around index
}

//set up the timer1, 64:1 prescaler
void tmr1_init(unsigned short period) {
  TCCR1B &=~0x07;    //stop tmr1
  
  //set up tccr1a
  TCCR1A =    (0<<COM1A1) | (0<<COM1A0) |    //com1a10 = 0b00, normal operations
              (0<<COM1B1) | (0<<COM1B0) |    //com1b10 = 0b00, normal operations
              (0<<WGM11) | (0<<WGM10);       //wgm2..0 = 0b0100, top at OCR1A
  TCCR1B =    (0<<ICNC1) | (0<<ICES1) |      //disable input capture noise canceller and input capture edge select
              (0<<WGM13) | (1<<WGM12) |      //wgm3..0 = 0b0100, top at OCR1A
              (0<<CS12) | (1<<CS11) | (1<<CS10); //cs2..0 = 0b0011, 64:1 prescaler
  TCCR1C = 0x00;

  //reset the timer/counter
  TCNT1 = 0x0000;                            //reset the timer
  
  //load the period
  OCR1A = period;

  //reset the flag
  TIFR1 |= (1<<OCF1A);
  
  //enable the interrupt
  TIMSK1 |= (1<<OCIE1A);
}

void setup(void) {
  //initialize the output pins
  PWM_PORT1 |= PWM0 | PWM1 | PWM2 | PWM3 | PWM4 | PWM5 ; //set the pins
  PWM_DDR1  |= PWM0 | PWM1 | PWM2 | PWM3 | PWM4 | PWM5 ; //set up the pins for output
  PWM_PORT2 |= PWM6 | PWM7 | PWM8 | PWM9 | PWM10 | PWM11 ; //set the pins
  PWM_DDR2  |= PWM6 | PWM7 | PWM8 | PWM9 | PWM10 | PWM11 ; //set up the pins for output
  
  //set up the timer
  tmr1_init(PWM_Bit);
  
  //enable interrupt
  sei();
}

void loop(void) {
}

Capture.PNG

I use this sketch:

That would work.