PWM de 50 Hz con pic16f87

Hola, necesito controlar un servomotor con precisión mayor a un grado de libertad.
He hecho los cálculos y, para una precisión de 0.5 grados necesito cargar un valor de 11 bits para generar el periodo. Sin embargo sólo dispongo de 8 bits en PR2.
¿Cómo puedo hacerlo?

Por otra parte, no entiendo cómo el ciclo de trabajo (duty cycle) dispone de 10 bits para cargar su valor y el periodo sólo de 8, cuando el periodo siempre será mayor que el ciclo de trabajo.

Igual no he entendido bien el funcionamiento del registro CCP modo PWM.
A ver si me podéis ayudar...

Raúl
 
Por otra parte, no entiendo cómo el ciclo de trabajo (duty cycle) dispone de 10 bits para cargar su valor y el periodo sólo de 8, cuando el periodo siempre será mayor que el ciclo de trabajo.

El período de trabajo se compone de los 8 bits del registro, más dos bits adicionales que corresponden a las fases del reloj.

Del reloj del sistema se derivan internamente 3 señales más, o fases de reloj, quedando la fase principal (0º) y las otras 3 (90º, 180º, 270º). (Esto se usa para la decodificación interna de instrucciones creo).
Con esos dos bits (que no están en el registro de período) estarías seleccionando con que fase de reloj se hace la cuenta del período.
Por suerte esto es transparente al usuario, la cosa funciona como si el período fuera de 10 bits.

Igual no he entendido bien el funcionamiento del registro CCP modo PWM.

El modo PWM da una salida a un pin físico del PIC, con una frecuencia determinada por el registro de período, y un ciclo de trabajo fijado por el registro de ciclo de trabajo (las fórmulas están en las hojas de datos de cada PIC, depende también de la selección que se haga de los pre/post-escaladores, del reloj del sistema, etc).

Hay una base de tiempo, que no es otra cosa que un contador, que inicia la cuenta al habilitarse el módulo, y que tendrá una entrada de reloj también pre-establecida. Cuando la cuenta llega al valor establecido en el registro de ciclo de trabajo, el pin de salida cambia de estado (si es 0 y 1 también depende de la configuración del módulo), y cuando el mismo contador llega al valor establecido en el registro de período (más los dos bits ficticios por esto de las fases del reloj) la salida vuelve a cambiar de estado y se vuelve a iniciar la cuenta (en los PIC's que usan módulos PWM para control de motores = PCPWM, puede hacerse que llegado a este punto se inicie la cuenta regresiva.... igual no creo que sea tu caso).

He hecho los cálculos y, para una precisión de 0.5 grados necesito cargar un valor de 11 bits para generar el periodo. Sin embargo sólo dispongo de 8 bits en PR2.
¿Cómo puedo hacerlo?

Bueno, según tengo entendido (que me corrigan los colegas del foro si me equivoco*) cuando la base de tiemp/el contador del módulo PWM completa un período, esto es, alcanza el valor del registro de período, se tiene la opción de generar un pedido de interrupción.
En esa rutina de interrupción tendríamos que simular un bit más (son 8 de PR2 + 2 de fases de reloj = 10, nos falta uno).
Lo que haría sería configurar la base de tiempo del módulo PWM para que cuente más rápido, 2 veces más rápido que la frecuencia que quiero generar (a 100 Hz).
Luego en el registro de período cargo los 10 bits menos significativos, y el más significativo lo cargo en donde me quede cómodo (llamémoslo MSB).
La idea sería arrancar el módulo normalmente, se produce la primera interrupción (a los 10 ms porque la base de tiempo esta puesta a la mitad del que yo quiero 20ms<-50 Hz).
Hay dos casos, si el MSB es = 1 la rutina de interrupción no haría absolutamente nada.
Pero si es igual a cero la rutina debe poner el registro de ciclo de trabajo = 0, habiéndolo guardado previamente en una variable temporal.
Luego en la próxima rutina de interrupción se recupera el valor del registro de ciclo de trabajo, y así en las sucesiva interrupciones se iría alternando en el registro de ciclo de trrabajo los valores y el valor inicial (que son los 10 bits menos significativos).

En los PIC's con PCPWM se puede anular la escritura del pin por parte del módulo, y mandar el pin a un valor de "override" (anulación sería, figura así en sus hojas de datos), pero ese mecanismo no está en los módulos CCP.

La idea podría extenderse para aumentar la resolución en n bits, pero el límite estaría dado por la velocidad de cuenta de la base de tiempo del PIC y la velocidad de ejecución de la rutina de interrupción.

Bueno, ojalá me hayas entendido algo, y que estos 15 minutos que te dediqué sirvan para algo.

Saludos
 
Hola Ardogan.
Muchas gracias por tu respuesta.
Finalmente no voy a utilizar el 16f87, pero seguro que la información que has dejado le servirá a alguien cuando busque en el histórico del foro.

Ya he averiguado por qué el valor almacenado para generar el ciclo de trabajo es mayor que el del periodo: en la fórmula para calcular PR2 multiplicamos por cuatro mientras que en la variable del ciclo de trabajo no.
Por ello, si ambos tuvieran una longitud similar, necesitaríamos 2 bits más (equivale a multiplicar por cuatro) para el ciclo de trabajo.

En mi caso, los pulsos están mucho más tiempo con el valor a cero que a uno, ya que el periodo es unas diez veces mayor que el ciclo de trabajo. Como necesito mucha precisión (5 us) no logro generar el periodo (tengo que multiplicar por 7000 (13 bits) y sólo dispongo de 8 bits).

Finalmente, lo voy a solucionar generando la onda por software: mediante interrupciones del timer0 consigo tanto el ciclo de trabajo como el periodo. Lo que hago es multiplicar el número necesario de veces los ciclos de 5us.

Para lograrlo voy a utilizar el 16c621 a 40 Mhz. A continuación muestro el código en ensamblador. Los nops están para ajustar los tiempos y que así para cualquier camino de la subrutina la interrupción dure lo mismo.

Tengo intención de mejorarlo haciendo que la salida se obtenga por PortA y que así disponga de los 8 bits de portB para posicionar el motor (ahora utilizo 7, por lo que sólo tendría un rango de 60º para el posicionamiento, y necesito 120).

Un saludo,
Raúl



LIST P=16C621
INCLUDE <P16C621.INC>
__CONFIG _CP_OFF & _WDT_OFF & _PWRTE_ON & _HS_OSC

; CBLOCK 0x0C
CBLOCK 0x20
FactorAlto
Auxiliar
Contador_Bucle2

ENDC

TMR0_CARGA EQU -d'38'

ALTO_CERO_GRADOS EQU d'610'
TIEMPO_PATRON EQU d'5'

FACTOR_MINIMO EQU ALTO_CERO_GRADOS/TIEMPO_PATRON

#DEFINE Salida PORTB,0

; ZONA DE CÓDIGOS ********************************************************************

ORG 0
goto Inicio
ORG .4
goto Timer0_Interrupcion
Inicio
bsf STATUS,RP0
bcf Salida
movlw b'11111110'
movwf PORTB
movlw b'00001000'
movwf OPTION_REG
bcf STATUS,RP0
movlw TMR0_CARGA
movwf TMR0
movlw b'10100000'
movwf INTCON
movlw .249
movwf Contador_Bucle2
Principal
movf PORTB,W
movwf Auxiliar
rrf Auxiliar,W
andlw b'01111111'
addlw FACTOR_MINIMO
movwf FactorAlto
goto Principal


CBLOCK
Guarda_W
Guarda_STATUS
Timer0_ContadorA
Contador_Bucle1

ENDC

Timer0_Interrupcion
movwf Guarda_W
swapf STATUS,W
movwf Guarda_STATUS
bcf STATUS,RP0
movlw TMR0_CARGA
movwf TMR0
movf Timer0_ContadorA,W
andlw b'11111111'
btfss STATUS,Z
goto Ajuste
btfsc Salida
goto EstabaAlto
movf Contador_Bucle1,W
andlw b'11111111'
btfss STATUS,Z
goto Bucle_Interrupcion1
EstabaBajo
nop
nop
nop
nop
bsf Salida
movf FactorAlto,W
movwf Timer0_ContadorA
goto Fin_Timer0_Interrupcion
EstabaAlto
nop
nop
nop
nop
nop ;5
nop
nop
nop
bcf Salida
movf FactorAlto,W
sublw .249
movwf Timer0_ContadorA
movlw .15
movwf Contador_Bucle1
Fin_Timer0_Interrupcion
swapf Guarda_STATUS,W
movwf STATUS
swapf Guarda_W,F
swapf Guarda_W,W
bcf INTCON,RBIF
bcf INTCON,T0IF
retfie

Ajuste
nop
nop
nop
nop
nop ;5
nop
nop
nop
nop
nop ;10
nop
nop
nop
decf Timer0_ContadorA
goto Fin_Timer0_Interrupcion

Bucle_Interrupcion1
movf Contador_Bucle2,W
andlw b'11111111'
btfsc STATUS,Z
goto Fin_Bucle2
nop
nop
nop
decf Contador_Bucle2,F
goto Fin_Timer0_Interrupcion
Fin_Bucle2
movlw .249;
movwf Contador_Bucle2
decf Contador_Bucle1,F
goto Fin_Timer0_Interrupcion


END
 
Hola, tengo la duda de como calcular el periodo de una interrupcion despues de usar el CCP2 en modo compare, estoy usando ;Prediv= 1:1, Fosc/4, en Timer 1, alguna ayuda se agradece.
 
Hola que tal a todos espero que alguien me pueda ayudar con lo que estoy haciendo....
Mi problema es el siguiente:::

Tengo un pwm de 25Khz, lo estoy variando con el puertos RA0

Lo que estoy haciendo es tomar una señal de referencia de 0 a 5volts con un potenciometro.. lo meto al convertidor A/D.. pero obtengo un valor de 10 bits.. como le hago para introducir ese valor en el pwm.....

Lo pongo directo en CCPR1L pero al variar el potenciometro me despliega tres veces el pwm

Dicho de otra manera:
comienzo a girar el potenciometro y comienza el pulso desde cero hasta un valor maximo.. al seguir variando el potenciometro, el pulso comienza nuevamente desde cero y va hasta su valor maximo.. asi lo hace hasta 4 veces hasta que el potenciometro llega hasta el final...

yo quiero que lo haga solo una vez....
 
Asi como lo dices, pueden ser dos cosas:
-Si solo estas cargando el valor en CCPR1L, te faltan cargar los 2 bits de mas peso, que estan en CCP1CON, bit 4 y 5. Esto es seguro.
-La otra cosa, si estas cargando los 10 bits del PWM, es que solo estas usando 8 bits del ADC, controla que tenes el resultado en 10 bits, me paso muchas veces que solo me devolvia 8 bits.

Espero haberte ayudado, saludos
 
=O sii sii te entendi Gracias!!!
pero eso es el problema estoy utilizando 10 bits weno eso pienso jeje... lo estoy programando en un lengua je BASIC y utilizo el PIC SIMULATOR IDE... aqui esta el programa no entiendo que es lo que esta mal.. :unsure:

-----------------------------------------------------------------------
AllDigital

ADCON1 = %10001110 'RA0 analogico ,RA1:RA7 E/S digitales
ADCON0 = %11000101 'RA0 entrada analogica,clock A/D

Dim conversion As Word
Dim variable As Word
Dim a As Bit
Dim b As Bit

PR2 = 39
CCP1CON.2 = 1 'CCP1 a modulo PWM
CCP1CON.3 = 1
pwm:
Adcin 0, conversion
variable = conversion And %0011111111
a = conversion.8
b = conversion.9

CCPR1L = variable '8 bits MSB del Ciclo de Trabajo
CCP1CON.5 = b '2 bits menos significativos
CCP1CON.4 = a 'del Ciclo de Trabajo

TRISC.2 = 0

T2CON = 0x04 'prescaler 1:1 y ativacion del TMR2

WaitUs 50
Goto pwm
End
 
Aparentemente tu programa esta bien... No se que estara andando mal...
Un consejo:

NO TE BASES EN LO QUE TE MUESTRA UN SIMULADOR!!
:eek:

Por muchisima experiencia que tuve con muchos simuladores, te digo que ni pierdas el tiempo usándolos, yo perdi mas tiempo tratando de hacer andar el simulador que en probar el circuito en si:enfadado::enfadado::enfadado:... Encima de renegar un monton! je
Incluso proteus (creo que se llama asi?) no sirve!!!! Tenia placas andando perfectamente en la mano y en el simulador aparecian errores a cada rato. Que pase al reves es aceptable.

Te sugiero que pruebes tu programa con un pic, si no anda vemos otras posibilidades. :D

Igual creo que te puede estar faltando agregar una directiva, adc_resolution = 10, aunque en la dodumentacion del compilador dice que si no se especifica se setea a la resolucion del micro.:unsure:
Otra cosa, que micro estas usando?? Es posible que el adc sea de 8 bits... ;)
 
jeje weno stoy usando un PIC 16F877A con un oscilador de 4Mhz mm weno con respecto a esa instruccion "adc_resolution = 10" el PIC SIMULATOR IDE no lo acepta =S
 
No la acepta el pic simulator o el compilador??

Si no la acepta el simulador, entonces ese puede ser el problema.
Si no la acepta el complilador, busca en la documentacion, es posible que se llame de otra forma.

El adc de ese pic es de 10bits...
 
weno el pic simulator cada ves ke acepta una instruccion cambio de color ..esta instucciom no cambia y ademas el compilador no lo acept.. y i claro el adc d este pic es de 10 bits pero no entiendo cual es la faia T_T
 
Pero mira que esta no es una instruccion! Es una directiva para el compilador, nada mas. No te la va a marcar como una instruccion. Si el compilador no la toma, busca en la ayuda del mismo. Ahi tiene que estar como se llama...

De ultima, hace la rutina que lee el adc a mano y proba asi, para descartar que no sea algo del compilador.
 
Atrás
Arriba