Se puede....
Suponete que tenes un ADC de 8 bits (255 posiciones, siendo 100% el 255 y 0 el 0%)
Para ser mas simple vamos a hacer un PWM de 8 bits de resolucion
1 segundo / 255 = 3.92156862 ms = 255Hz
Tu periodo del clock es de 4/4Mhz = 1us
necesitarias contar segun el preescaler que tengas:
1:1 = 3921.5686
1:2 = 1960.7843
1:4 = 980.3921
1:8 = 490.196
1:16 = 245.098
1:32 = 122.54
El tema es que ningun es entero, ademas notar que si usamos preescaler de 1 a 8 o hacemos un par de calculos para usarlo con un timer de 8bits o simplemente usamos el timer de 16bits, luego con 1/16 y 1/32 vemos que se peude usar el timer de 8 bits.
Veamos ahora la frecuencia que logramos al ponerle numeros enteros, redondeando a los mas cercanos
1:1 = 3922 = 254.97Hz
1:2 = 1961 = 254.97Hz
1:4 = 980 = 255.10Hz
1:8 = 490 = 255.10Hz
1:16 = 245 = 255.10Hz
1:32 = 123 = 254.07Hz
Eso te deja con el timer que quieras, voy a hacer el de 2 para un PIC16F877A, uno de TMR0(8bits) y otro de TMR1(16bits)
1:16 = 245 = 255.10Hz -> TMR0
1:1 = 3922 = 254.97Hz -> TMR1
TMR0// Interrupt Handler
void interrupt()
{
// Timer0 Interrupt - Freq = 255.10 Hz - Period = 0.003920 seconds
if (INTCON.TMR0IF ==1) // timer 0 interrupt flag
{
PORTB.F0 = ~PORTB.F0; // Toggle PORTB bit0 LED
INTCON.TMR0IF = 0; // clear the flag
INTCON.TMR0IE = 1; // reenable the interrupt
TMR0 = 11; // reset the timer preset count
}
}
// code starts here...
void main()
{
// setup portb to show the interrupts by blibking LEDs
TRISB = 0x00; // PORT is all output...to show the interrupts
PORTB = 0; // start with all outputs low
//Timer0 Registers Prescaler= 16 - TMR0 Preset = 11 - Freq = 255.10 Hz - Period = 0.003920 seconds
OPTION_REG.T0CS = 0; // bit 5 TMR0 Clock Source Select bit...0 = Internal Clock (CLKO) 1 = Transition on T0CKI pin
OPTION_REG.T0SE = 0; // bit 4 TMR0 Source Edge Select bit 0 = low/high 1 = high/low
OPTION_REG.PSA = 0; // bit 3 Prescaler Assignment bit...0 = Prescaler is assigned to the Timer0
OPTION_REG.PS2 = 0; // bits 2-0 PS2:PS0: Prescaler Rate Select bits
OPTION_REG.PS1 = 1;
OPTION_REG.PS0 = 1;
TMR0 = 11; // preset for timer register
// Interrupt Registers
INTCON = 0; // clear the interrpt control register
INTCON.TMR0IE = 1; // bit5 TMR0 Overflow Interrupt Enable bit...1 = Enables the TMR0 interrupt
INTCON.TMR0IF = 0; // bit2 clear timer 0 interrupt flag
INTCON.GIE = 1; // bit7 global interrupt enable
while(1) //endless loop
{
}
}
TMR1// Interrupt Handler
void interrupt()
{
// Timer1 Interrupt - Freq = 254.97 Hz - Period = 0.003922 seconds
if (PIR1.TMR1IF == 1) // timer 1 interrupt flag
{
PORTB.F1 = ~PORTB.F1; // Toggle PORTB bit1 LED
PIR1.TMR1IF = 0; // interrupt must be cleared by software
PIE1.TMR1IE = 1; // reenable the interrupt
TMR1H = 240; // preset for timer1 MSB register, Value TMR1 = 61614
TMR1L = 174; // preset for timer1 LSB register
}
}
// code starts here...
void main()
{
// setup portb to show the interrupts by blibking LEDs
TRISB = 0x00; // PORT is all output...to show the interrupts
PORTB = 0; // start with all outputs low
//Timer1 Registers Prescaler= 1 - TMR1 Preset = 61614 - Freq = 254.97 Hz - Period = 0.003922 seconds
T1CON.T1CKPS1 = 0; // bits 5-4 Prescaler Rate Select bits
T1CON.T1CKPS0 = 0; // bit 4
T1CON.T1OSCEN = 1; // bit 3 Timer1 Oscillator Enable Control bit 1 = on
T1CON.T1SYNC = 1; // bit 2 Timer1 External Clock Input Synchronization Control bit...1 = Do not synchronize external clock input
T1CON.TMR1CS = 0; // bit 1 Timer1 Clock Source Select bit...0 = Internal clock (FOSC/4)
T1CON.TMR1ON = 1; // bit 0 enables timer
TMR1H = 240; // preset for timer1 MSB register
TMR1L = 174; // preset for timer1 LSB register
// Interrupt Registers
INTCON = 0; // clear the interrpt control register
INTCON.TMR0IE = 0; // bit5 TMR0 Overflow Interrupt Enable bit...0 = Disables the TMR0 interrupt
PIR1.TMR1IF = 0; // clear timer1 interupt flag TMR1IF
PIE1.TMR1IE = 1; // enable Timer1 interrupts
INTCON.TMR0IF = 0; // bit2 clear timer 0 interrupt flag
INTCON.GIE = 1; // bit7 global interrupt enable
INTCON.PEIE = 1; // bit6 Peripheral Interrupt Enable bit...1 = Enables all unmasked peripheral interrupts
while(1) //endless loop
{
}
}
Ahora ya tenemos configurado el Timer para que haga 255 interrupciones por segundo
Lo unico que queda hacer es...
Poner un contador en la rutina de interrupcion que cuando llegue a 256, se ponga a 0 y comienze nuevamente.
El valor del ADC ( valor de 0 a 255 ) se compara con ese contador.
Suponete que el ADC mide 200. Cuando llega a 0 el contador pone a 1 la salida, y cuando llega a 200 el contador pone a 0 la salida
Contador++;
if ( Contador > ADCResultado )
{
Salida = 0;
}
else
{
Salida =1;
}
if ( Contador == 255 ) { Contador = 0;}
Se podria evitar el ultimo if, si uno declara a contador como un numero sin signo y de 8 bits
Y tambien leeria el ADC un poco mas lento o solo actualizaria el valor del ADC ( me refiero a mover el dato de ADRES ( ADRESH:ADRESL si usas 16bits) a ADCResultado.
Asi evitas posibles mal funcionamientos.
PD:
Te dejo una calculadora del TMR
http://eng-serve.com/pic/pic_timer.htmlY lo hice en C, por que no sabia en que querias hacerlo, pero si usas ASM vas a poder pasarlo facilmente.
Fin...