Bueno decidí abrir éste post aparte para ahondar en detalles sobre como podríamos lograr éste objetivo de controlar X cantidad de servos ó señales PWM con un mismo PIC mediante una sola interrupción del TIMER0, la idea original es de
RedPic allí pueden conseguir un poco de teoría y ver como nació éste algoritmo, que ahora es de todos nosostros.
Para entender todo lo que a continuación se explicará es necesario de varias herramientas:
1.- Mplab +
Plug-in del Ccs2.- CCS Compiler
3.- Proteus (7.2 SP2)
4.- Y ganas de aprender

Después de tener todo instalado, abrimos el MPLAB y creamos un nuevo proyecto, elegimos nombre y ubicación, y le damos OK (yo elegí por ejemplo: pic_pwm):

[PIC16F877]
Luego vamos a File --> New y en la ventana que se nos abre copiamos éste código:
#include <16f877.h>
#fuses HS,NOWDT,NOPROTECT,NOLVP,NOBROWNOUT,PUT
#use delay (clock=20000000)
#USE RS232 (baud=9600,bits=8,parity=N,xmit=PIN_C6,rcv=PIN_C7)
#use fast_io(A)
#use fast_io(B)
#use fast_io(C)
#use fast_io(D)
#use fast_io(E)
#byte PORTA = 0x05
#byte PORTB = 0x06
#byte PORTC = 0x07
#byte PORTD = 0x08
#byte PORTE = 0x09
#bit servo_00 = PORTB.0
#bit servo_01 = PORTB.1
#bit servo_02 = PORTB.2
#bit servo_03 = PORTB.3
#bit servo_04 = PORTB.4
#bit servo_05 = PORTB.5
#bit servo_06 = PORTB.6
#bit servo_07 = PORTB.7
#bit servo_08 = PORTD.0
#bit servo_09 = PORTD.1
#bit servo_10 = PORTD.2
#bit servo_11 = PORTD.3
#bit servo_12 = PORTD.4
#bit servo_13 = PORTD.5
#bit servo_14 = PORTD.6
#bit servo_15 = PORTD.7
#define n 16// número de servos
//para otro número de servos hay que hacer mediciones de tiempo
#define max_theCount 200// 200*100us=20ms
#define derecha 26// Éste valor fué determinado simulando el proceso
// en el proteus pero en teoría debería ser (25) 25*100us=2,5ms
// Sería cuestión de probar con servos reales y ajustar valores ;-)
#define centro 15// 15*100us=1,5ms
#define izquierda 5// 5*100us =0,5ms
#define val_timer0 88
int8 theCount=1;
int8 duty[n];
#INT_TIMER0
void isr_timer0(){// Cada 100us llega aqui de forma cíclica
#asm
nop
#endasm
if (duty[0] >= theCount)
{
servo_00=1;
}
else
{
servo_00=0;
#asm
nop
#endasm
}
if (duty[1] >= theCount)
{
servo_01=1;
}
else
{
servo_01=0;
#asm
nop
#endasm
}
if (duty[2] >= theCount)
{
servo_02=1;
}
else
{
servo_02=0;
#asm
nop
#endasm
}
if (duty[3] >= theCount)
{
servo_03=1;
}
else
{
servo_03=0;
#asm
nop
#endasm
}
if (duty[4] >= theCount)
{
servo_04=1;
}
else
{
servo_04=0;
#asm
nop
#endasm
}
if (duty[5] >= theCount)
{
servo_05=1;
}
else
{
servo_05=0;
#asm
nop
#endasm
}
if (duty[6] >= theCount)
{
servo_06=1;
}
else
{
servo_06=0;
#asm
nop
#endasm
}
if (duty[7] >= theCount)
{
servo_07=1;
}
else
{
servo_07=0;
#asm
nop
#endasm
}
if (duty[8] >= theCount)
{
servo_08=1;
}
else
{
servo_08=0;
#asm
nop
#endasm
}
if (duty[9] >= theCount)
{
servo_09=1;
}
else
{
servo_09=0;
#asm
nop
#endasm
}
if (duty[10] >= theCount)
{
servo_10=1;
}
else
{
servo_10=0;
#asm
nop
#endasm
}
if (duty[11] >= theCount)
{
servo_11=1;
}
else
{
servo_11=0;
#asm
nop
#endasm
}
if (duty[12] >= theCount)
{
servo_12=1;
}
else
{
servo_12=0;
#asm
nop
#endasm
}
if (duty[13] >= theCount)
{
servo_13=1;
}
else
{
servo_13=0;
#asm
nop
#endasm
}
if (duty[14] >= theCount)
{
servo_14=1;
}
else
{
servo_14=0;
#asm
nop
#endasm
}
if (duty[15] >= theCount)
{
servo_15=1;
}
else
{
servo_15=0;
#asm
nop
#endasm
}
theCount=theCount+1;
if (theCount>max_theCount)
{
theCount=1;
}
else
{
delay_us(1);
#asm
nop
#endasm
}
set_timer0(val_timer0);
}
/////////////////////////////////////// PRINCIPAL /////////////////////////////////////
void main()
{
int k;
int8 i=0;// es el índice exclusivo de duty[] en la recepción serial
int1 band=0;
set_tris_b(0x00);
set_tris_c(0b10000000);
set_tris_d(0x00);
PORTB=0x00;
PORTD=0x00;
port_b_pullups(FALSE);
for(k=0;k<=(n-1);k++)
duty[k]=izquierda;//
setup_adc_ports(NO_ANALOGS);
setup_adc(ADC_OFF);
setup_timer_1(T1_DISABLED);
setup_timer_2(T2_DISABLED,0,1);
setup_timer_0(RTCC_DIV_2);
set_timer0(val_timer0);
enable_interrupts(INT_TIMER0);
enable_interrupts(GLOBAL);
while (1)
{
// Recepción de datos
if (kbhit()){
if (band==0){
i=getc();
band=1;
}
else{
duty[i]=getc();
band=0;
}
}
}
}Lo guardaremos en la misma carpeta con el nombre pic_pwm.c (pueden elegir el que quiera claro)
Luego en la ventana: pic_pwm.mcp, hacemos click derecho eliginedo "Add Files..." buscamos elegimos el archivo creado pic_pwm.c

Nos vamos a: Configure --> Select Device... y elegimos el PIC16F877
Luego en Debugger --> Select Tool, seleccionamos MPLAB SIM
En Debugger --> Settings..., colocamos 20Mhz a Processor Frecuency
En Project --> Select Language Toolsuite... elegimos CCS C Compiler for PIC... (aquí mismo en la sección de Location: "C:\Archivos de programa\PICC\Ccsc.exe" vá la ruta del compilador que en mi caso es ésta) y le damos OK
En éste punto podemos salvar nuestro proyecto.
Ahora bien compilamos y abrimos el StopWatch que está en Debugger, acomodando las dos ventanas para visualizarlas al mismo tiempo y a su vez aprovechamos de colocar un BreakPoint en éste punto del programa (Ver imagen):

Le damos a run y esta debe ser la imagen resultante:

Podemos observar que tardó 129us en llegar aquí la primera vez, es decir con una sola vez que le hemos dado a RUN, ahora si limpiamos con el botón "Zero" del StopWatch y le volvemos a dar veremos la siguiente imagen:

Podemos observar que en la segunda corrida llega en 99,8us, volvemos a colocar a cero el contador del StopWatch con el botón "Zero" y corremos nuevamente:

Podemos observar que llegó a los 100us exactos y de ahora en adelante siempre que le demos al botón RUN obtendremos 100us exactos en ese punto, y eso lo podemos extrapolar para cualquier otra línea que esté en ese código que maneja la interrupción del TIMER0.
Una de las tantas preguntas que se podrían estar haciendo en éste momento ¿Y para qué me sirve esto?
Bueno el truco está en mantener esos 100us si agregamos o quitamos servos (ó generar señales mediante PWM)
¿Cómo?
Vamos a fijarnos en ésta parte del programa:

El índice de duty[], nos indica el valor del ciclo útil que queremos obtener en un pin, en este caso el pin RB0 (servo_00=PORTB.0)
En éste ciclo utilizamos una variable llamada "theCount" cuyo valor máximo es 200 y es inicializada en 1, como siempre entramos cada 100us eso implica que si aumentamos de uno en uno ésta variable, al cabo de 200 pasadas equivaldrían en tiempo a 20ms (200*100us=20.000us=20ms) ahora bien si queremos un ciclo útil de 0,5ms en el pin RB0, cargamos duty[0] con 5 y la comparación
if (duty[0] >= theCount) será cierta 5 veces 100us lo que es igual a 500us=0,5ms por lo tanto el pin RB0 estará en alto ese tiempo y el resto en bajo, facilmente podemos deducir que para 2,5ms habría que cargar duty[0] con 25

Ok, todo bien hasta ahora.
La otra pregunta que se estarían haciendo (entre otras) sería: ¿Para que esos nop y esos delays?
La respuesta es la siguiente: si analizamos el código equivalente a la instrucción if en ASM nos daremos cuenta que si la condición es cierta tarda 1 ciclo más de instrucción que en el caso de que sea falsa, y esos desequilibrios son indeseados, por lo tanto agregando ese nop en la instrucción
else logramos hacer que si pasa por
if (true) como por
else se tarde siempre la misma cantidad de ciclos y de esta forma logramos igualdad en tiempo para ambos casos.
Cómo verán esas líneas de código se repiten para tantos señales de pwm queramos generar, así que podemos agregar o quitar señales pero siempre respetando que cada 100us exactos ocurra nuestra interrupción por TIMER0, quizás no en las 2 primeras corridas (como hemos visto anteriormente) lo logremos pero si en las sucesivas y de allí en adelante.
Esto último lo podemos lograr variando el valor del timer "
val_timer0=88" y agregando unos cuantos nop ó delay_cycles() en la rutina de interrupción, preferiblemente al entrar en ella.
Nota: Yo no aconsejaría utilizar otras interrupciones en éste pic, porque se generarían desequilibrios de tiempo indeseados.
El mismo análisis aplica para el PIC18F4550, ajustando los valores que sean necesarios para lograr que con una interrupción del TIMER0 cada 100us se ejecute ésta línea de código
if (duty[0] >= theCount)Éste es el código para el 18f4550:
#include <18F4550.h>
#fuses HSPLL,NOWDT,NOPROTECT,NOLVP,NODEBUG,USBDIV,PLL5,CPUDIV1,VREGEN,NOPBADEN,NOBROWNOUT
#use delay(clock=48000000)//48MHz
#USE RS232 (baud=9600,bits=8,parity=N,xmit=PIN_C6,rcv=PIN_C7)
//#build(reset=0x000800,interrupt=0x000808:0x000818)
// Necesario para indicarle al compilador a partir de donde alojar mi programa
// Si se quiere usar bootloader se pueden habilitar estás dos líneas
//#ORG 0x000, 0x07FF {} // Espacio reservado para alojar el bootloader
#byte PORTA = 0xF80// Son las direcciones de memoria de los puertos en este PIC
#byte PORTB = 0xF81
#byte PORTC = 0xF82
#byte PORTD = 0xF83
#byte PORTE = 0xF84
/////////////////////////////////////////////////////////////////////////////
//
// CCS Library dynamic defines. For dynamic configuration of the CCS Library
// for your application several defines need to be made. See the comments
// at usb.h for more information
//
/////////////////////////////////////////////////////////////////////////////
#define USB_HID_DEVICE FALSE //deshabilitamos el uso de las directivas HID
#define USB_EP1_TX_ENABLE USB_ENABLE_BULK //turn on EP1(EndPoint1) for IN bulk/interrupt transfers
#define USB_EP1_RX_ENABLE USB_ENABLE_BULK //turn on EP1(EndPoint1) for OUT bulk/interrupt transfers
#define USB_EP1_TX_SIZE 2 //size to allocate for the tx endpoint 1 buffer
#define USB_EP1_RX_SIZE 2 //size to allocate for the rx endpoint 1 buffer
/////////////////////////////////////////////////////////////////////////////
//
// If you are using a USB connection sense pin, define it here. If you are
// not using connection sense, comment out this line. Without connection
// sense you will not know if the device gets disconnected.
// (connection sense should look like this:
// 100k
// VBUS-----+----/\/\/\/\/\----- (I/O PIN ON PIC)
// |
// +----/\/\/\/\/\-----GND
// 100k
// (where VBUS is pin1 of the USB connector)
//
/////////////////////////////////////////////////////////////////////////////
//#define USB_CON_SENSE_PIN PIN_B2 //CCS 18F4550 development kit has optional conection sense pin
/////////////////////////////////////////////////////////////////////////////
//
// Include the CCS USB Libraries. See the comments at the top of these
// files for more information
//
/////////////////////////////////////////////////////////////////////////////
#include <pic18_usb.h> //Microchip PIC18Fxx5x Hardware layer for CCS's PIC USB driver
#include <PicUSB.h> //Configuración del USB y los descriptores para este dispositivo
#include <usb.c> //handles usb setup tokens and get descriptor reports
#use fast_io(A)
#use fast_io(B)
#use fast_io(C)
#use fast_io(D)
#use fast_io(E)
#bit servo_00 = PORTB.0
#bit servo_01 = PORTB.1
#bit servo_02 = PORTB.2
#bit servo_03 = PORTB.3
#bit servo_04 = PORTB.4
#bit servo_05 = PORTB.5
#bit servo_06 = PORTB.6
#bit servo_07 = PORTB.7
#bit servo_08 = PORTD.0
#bit servo_09 = PORTD.1
#bit servo_10 = PORTD.2
#bit servo_11 = PORTD.3
#bit servo_12 = PORTD.4
#bit servo_13 = PORTD.5
#bit servo_14 = PORTD.6
#bit servo_15 = PORTD.7
#define i_usb recibe[0]// es el índice que se recibe vía usb
#define pos_usb recibe[1]// es la posición que se recibe vía usb
#define n 16// número de servos
//para otro número de servos hay que hacer mediciones de tiempo
#define max_theCount 200// 200*100us=20ms
#define derecha 25// 25*100us=2,5ms
#define centro 15// 15*100us=1,5ms
#define izquierda 5// 5*100us =0,5ms
#define val_timer0 127
int8 theCount=1;
int8 duty[n];
#INT_TIMER0
void isr_timer0(){
delay_cycles(5);
if (duty[0] >= theCount)// Cada 100us llega aqui de forma cíclica
{
servo_00=1;
}
else
{
servo_00=0;
#asm
nop
#endasm
}
if (duty[1] >= theCount)
{
servo_01=1;
}
else
{
servo_01=0;
#asm
nop
#endasm
}
if (duty[2] >= theCount)
{
servo_02=1;
}
else
{
servo_02=0;
#asm
nop
#endasm
}
if (duty[3] >= theCount)
{
servo_03=1;
}
else
{
servo_03=0;
#asm
nop
#endasm
}
if (duty[4] >= theCount)
{
servo_04=1;
}
else
{
servo_04=0;
#asm
nop
#endasm
}
if (duty[5] >= theCount)
{
servo_05=1;
}
else
{
servo_05=0;
#asm
nop
#endasm
}
if (duty[6] >= theCount)
{
servo_06=1;
}
else
{
servo_06=0;
#asm
nop
#endasm
}
if (duty[7] >= theCount)
{
servo_07=1;
}
else
{
servo_07=0;
#asm
nop
#endasm
}
if (duty[8] >= theCount)
{
servo_08=1;
}
else
{
servo_08=0;
#asm
nop
#endasm
}
if (duty[9] >= theCount)
{
servo_09=1;
}
else
{
servo_09=0;
#asm
nop
#endasm
}
if (duty[10] >= theCount)
{
servo_10=1;
}
else
{
servo_10=0;
#asm
nop
#endasm
}
if (duty[11] >= theCount)
{
servo_11=1;
}
else
{
servo_11=0;
#asm
nop
#endasm
}
if (duty[12] >= theCount)
{
servo_12=1;
}
else
{
servo_12=0;
#asm
nop
#endasm
}
if (duty[13] >= theCount)
{
servo_13=1;
}
else
{
servo_13=0;
#asm
nop
#endasm
}
if (duty[14] >= theCount)
{
servo_14=1;
}
else
{
servo_14=0;
#asm
nop
#endasm
}
if (duty[15] >= theCount)
{
servo_15=1;
}
else
{
servo_15=0;
#asm
nop
#endasm
}
theCount=theCount+1;
if (theCount>max_theCount)
{
theCount=1;
}
else
{
delay_us(1);
#asm
nop
#endasm
}
set_timer0(val_timer0);
}
/////////////////////////////////////// PRINCIPAL /////////////////////////////////////
void main()
{
int k;
int8 i=0;// es el índice exclusivo de duty[] en la recepción serial
int1 band=0;
int8 recibe[2];// Buffer de recepción vía usb
//int8 envia[2];// Habilitar en caso de necesitar
set_tris_b(0x00);
set_tris_c(0b10000000);
set_tris_d(0x00);
PORTB=0x00;
PORTD=0x00;
port_b_pullups(FALSE);
for(k=0;k<=(n-1);k++)
duty[k]=centro;//
setup_adc_ports(NO_ANALOGS);
setup_adc(ADC_OFF);
setup_timer_0(RTCC_DIV_8 | RTCC_8_BIT);
set_timer0(val_timer0);
enable_interrupts(INT_TIMER0);
enable_interrupts(GLOBAL);
usb_init();//inicializamos el USB
usb_task();//habilita periférico usb e interrupciones
usb_wait_for_enumeration();//esperamos hasta que el PicUSB sea configurado por el host
while (TRUE)
{
if(usb_enumerated())//si el PicUSB está configurado
//if (TRUE)
{
// Recepción de datos via rs232
if (kbhit()){
if (band==0){
i=getc();
band=1;
}
else{
duty[i]=getc();
band=0;
}
}
// Recepción vía USB
if (usb_kbhit(1))//El endpoint de salida contiene datos del host (EP1)?
{
usb_get_packet(1, recibe, 2);// Recibimos los 2 bytes: (servo,pos)
duty[i_usb]=pos_usb;
}
}
}
}