Зачем использовать аппаратный ШИМ? У AVR обычно мало каналов.
Индикация с плавной сменой цифр и задержкой при переключении разрядов, а также, при желании, регулировкой яркости, прекрасно реализуется на одном таймере с прерываниями по переполнению и совпадению (в простом случае - два прерывания по совпадению, если немного подумать, можно обойтись одним, но перенастраиваемым).
Я использую такой способ:
1) Прерывание по переполнению - n++, вывод первой цифры разряда n, включение лампы n.
2) Прерывание по совпадению 1 - вывод второй цифры того же разряда n
3) Прерывание по совпадению 2 - гашение разряда n.
В зависимости от того, в какой момент наступит "Прерывание по совпадению 1", будет меняться соотношения яркостей двух цифр в одном разряде. "Прерывание по совпадению 2" определяет яркость (и задержку для подавления засветки).
Где-нибудь, например, в прерывании по переполнению, нужно проверять, на изменилась ли информация на индикаторах, если изменилась, начинать с некоторой скоростью изменять значение в регистре сравнения прерывания 1. Одна цифра заменится на другую плавно. После завершения смены сделать вторую цифру равной первой (новой).
Есть ещё такой:
Два чередующихся цикла индикации, отдельно для новых и старых цифр. Сначала выводим все новые, потом все старые. Ну, или наоборот. Яркость каждой из двух цифр в каждом разряде можно задавать отдельно. Если нужна одна цифра, оба раза выводим одно и то же.
1) Прерывание по переполнению - n++, вывод нужной цифры разряда n (новой или старой), включение лампы n.
2) Прерывание по совпадению - гашение разряда n.
Пример кода (CVAVR, без второго прерывания (задержки между разрядами), зато с эффектом смены с прокруткой (определяется переменной mode)):
Спойлер
Код: Выделить всё
// Timer2 overflow interrupt service routine
interrupt [TIM2_OVF] void timer2_ovf_isr(void)
{
if(counter<5) counter++;
else counter=0;
PORTD=PIND&0b11000000;
if((ind[counter]!=_ind[counter])&&mode){
if(change==0){
if(mode==1) {lum=-1; OCR2=16; TIMSK|=0x80;}
if(mode==2) rotate=0;
div=-1;
}
change|=(1<<counter);
}
if(change==0) _ind[counter]=ind[counter];
if((mode==2)&&(change&(1<<counter))) buffer=code[rotate];
else buffer=code[ind[counter]];
if(ind[6]&(1<<(5-counter))) buffer=buffer|0b10;
PORTC=(PINC&1)|buffer;
PORTD=PIND|razr[counter];
if(div<24) div++;
else{
div=0;
if(change&&(mode==1)){
if(lum<14) lum++;
else{
lum=0;
change=0;
TIMSK&=(~0x80);
_ind[0]=ind[0];
_ind[1]=ind[1];
_ind[2]=ind[2];
_ind[3]=ind[3];
_ind[4]=ind[4];
_ind[5]=ind[5];
}
OCR2=16+14*lum;
}
if(change&&(mode==2)){
if(rotate<9) rotate++;
else{
change=0;
_ind[0]=ind[0];
_ind[1]=ind[1];
_ind[2]=ind[2];
_ind[3]=ind[3];
_ind[4]=ind[4];
_ind[5]=ind[5];
}
}
}
}
// Timer2 output compare interrupt service routine
interrupt [TIM2_COMP] void timer2_comp_isr(void)
{
if(mode==1){
if(change&(1<<counter)){
buffer=code[_ind[counter]];
if(ind[6]&(1<<(5-counter))) buffer=buffer|0b10;
PORTC=(PINC&1)|buffer;
}
}
}
По millis и вопросу про занятый таймер понял, что речь про Андурино. Совет: использовать это железо можно, а вот писать лучше на чистом Си, без применения андуриновких библиотек и функций. Тогда не будет вопросов про занятый неизвестно чем таймер, а также код влезет в куда менее "жирный" по памяти МК, чем 328-я Мега.