setup_timer_2(t2_div_by_x,y,1);
set_pwm1_duty((int16)z);#USE PWM(OUTPUT=PIN_c2, FREQUENCY=5kHz, DUTY=50) explicada aquí http://www.ccsinfo.com/newsdesk_info.php?newsdesk_id=182 (http://www.ccsinfo.com/newsdesk_info.php?newsdesk_id=182)Es una situacion bastante rara la que planteas, y si, solamente variar la el periodo no va a poder ser.
Se me ocurren 2 sola opciones.
Si son pocos pasos de cambio de periodo, seria tener una tabla con los valores de periodo-duty, entonces para cada valor de periodo que uses, tenes tu valor de duty correspondiente. ( seria la implementacion mas simple y pre calculadda) Incluso en el PEOR de los casos que quieras tener los 255 valores de periodo te llevaria unos 510 bytes (0.5Kb aprox - de los 32K que posee el micro-) ocupados que los podes poner en la memoria de programa
Si ya son demasiados pasos o buscas que si aumentas 1 en el periodo el duty tambien lo siga, deberias de calcularlo en el mismo programa.
#include <stdio.h>
#include <math.h>
#define Fosc 48000000
int T2DIV; // preescaler de la funcion setup_timer_2, puede ser 1, 4 o 16
int Fpwm; // frecuencia que busco
float dutycycle; // valor que introducimos en la funcion set_pwm1_duty(dutycycle)
float duty; // de 0 a 1 me indica el % del duty cycle que queremos
int dutycalculator(); // función donde operamos para obtener el valor de la variable dutycycle
int calculo_de_prescaler(); // función donde operamos para calcular el preescaler adecuado a la frecuencia que necesitamos
int main(void) {
Fpwm= 5000; // ejemplo con 5 kHz
duty= 0.5; // duty 50%
calculo_de_prescaler();
return 0;
}
int calculo_de_prescaler(){
if (Fosc/(Fpwm*1*4)-1<0xFF) {
// setup_timer_2(T2_DIV_BY_1, (int8)(Fosc/(Fpwm*1*4))-1, 1);
T2DIV=1;
}
else if (Fosc/(Fpwm*4*4)-1<0xFF) {
// setup_timer_2(T2_DIV_BY_4, (int8)(Fosc/(Fpwm*4*4))-1, 1);
T2DIV=4;
}
else if (Fosc/(Fpwm*16*4)-1<0xFF) {
// setup_timer_2(T2_DIV_BY_16, (int8)(Fosc/(Fpwm*16*4))-1, 1);
T2DIV=16;
}
dutycalculator();
}
int dutycalculator(){
int resolucion = log10(Fosc/(Fpwm*T2DIV))/log10(2); // calculamos la resolucion de dicho valor de frecuencia
dutycycle = pow(2,resolucion)*duty; // hacemos 2^(resolucion) y multiplicamos por variable duty
printf("%d\n",T2DIV);
printf ("%d\n",resolucion);
printf("%.2f",dutycycle);
}PR2 = Periodo = 149
Preescaler = 16
Duty = 299 ( Formado por CCPR1L + CCP1CON<5:4> )Periodo = 149
Preescaler = 16
Duty = 298Para mi, con un simple:Código: C
Periodo = 0x00; // Esto puede ir de 0x00 hasta 0xFF, es decir lo que iria en PR2 del TMR2 // Valores a cargar en el CCPRxL + 2bits del CCPxCON: Duty1 = ((50 * Periodo) / 100) << 2; Duty2 = ((15 * Periodo) / 100) << 2;
Se podria simplificar estas ecuaciones en:Código: C
Duty1 = (2 * (uint16_t)Periodo); Duty2 = (6 * (uint16_t)Periodo) / 10;
Ahi, cuando periodo (PR2) es por ejemplo 150, el duty al 50% deberia valer 300.
Por que esto ?, El periodo se genera con los 8bits del PR2 desplazado a la izquierda 2 veces. Es decir multiplicado por 4, Si tenemos 150 es 600.
Esto se hace para que la comparacion sea de 10 bits. Ahora si quisiera generar un duty de 50% deberia cargar la mitad, es decir 300. Si quisiera 15% de duty, es decir el 15% de 600 es de 90.
Fin.. Eso fue todo el calculo que debiste en lo que es llenar los registros. Observa que no afecta en nada los preescalers ni nada por el estilo.
Ahora si queres una conversion desde una frecuencia como estas planteando, entonces deberiamos Primero llegar al duty, que es algo parecido a lo que hiciste, pero hubiera intentado que se realizen menos veces las divisiones y multiplicaciones.Código: C
void SetearPWM(int16 Fpwm, int8 Duty){ int16 Periodo = (Fosc/4)/Fpwm; int8 Preescaler = 1; int8 DutyCalculado; int8 i; for(i=0;i<3;i++) { if( Periodo < 0x100) { break; } Periodo >>= 2 Preescaler <<= 2; } if( Preescaler > 16) { return; //Error, no hay valor posible } Periodo--; DutyCalculado = (Duty*4*Periodo) / 100; }
-------------------------------------
Segun la pagina que pasaste para 5000 Hz y 50% duty tendrias:Código: [Seleccionar]PR2 = Periodo = 149
Preescaler = 16
Duty = 299 ( Formado por CCPR1L + CCP1CON<5:4> )
CCPR1L = 0b01001010 ;
CCP1CON = 0b00111100 ;
O 0b0100101011 = 299d
Segun el codigo que pase, tendriamos al comienzo del script:
Periodo = 2400
Preescaler = 1
Los pasos en el for quedarian los siguientes:
i=0 , pasa completamente por que Periodo es mayor que 0x100 ( 256 )
i=1 , los valores quedan culpa de la anterior vuelta:
Periodo = 600
Preescaler = 4
Aun es grande, asi que pasa.
i=2, los valores al comenzar esta vuelta es de
Periodo = 150
Preescaler = 16
Ahora entra al if y procede a ejecutar el break y salir del for, no ejecutandose las lineas que siguen.
Luego hay una "proteccion" por si es que ni con un Preescaler de 16 se puede realizar. Se decrementa 1 de Periodo dejandolo en 149, como queremos.
Finalmente el duty, tendria un valor de (50 * 4 * 149) / 100 = 298
Por lo tanto tendrias como salida:Código: [Seleccionar]Periodo = 149
Preescaler = 16
Duty = 298
Bastante aproximado a lo que deberia ser.
-----------------------------------------------------------
Pero por supuesto, nada se compara respecto a velocidad a usar tablas, solo tendrias el trabajo de crearlas, que con un script de python lo harias en un ratito.
Y la otra creo que mi acercamiento es mas rapido que el tuyo, ya que no uso logaritmos ni nada raro, ademas trato de calcular lo menos posible. Asi es mas rapido.