TODOPIC
Microcontroladores PIC => Almacén del Assembler => Mensaje iniciado por: felper en 19 de Julio de 2012, 03:44:10
-
Hola A todos!!
Estoy programando un pwm a 50 hz con el pic16f877 en mplab, para controlar un servomotor que trabaja desde 1 ms a 2 ms, una entrada analoga conectada a un potenciometro que controla el ciclo de trabajo pero no me esta funcionando la simulacion en proteus 7.8 sp 2:
Fosc=500Khz;
Fpwm=50Hz;
Preescale=16;
PR2=[Fosc/(Fpwm*TMR2 Prescale Value)*4]-1
PR2=155
Resolucion=log[4(PR2+1)]/(log(2)) bits
Resolucion=10 bits
TEmpTMR2=[Prescale*(Pr2+1)*PostScale]*[4/Fosc]=[16*157*1][4/500000]=20 ms
CCPRxL:CCPxCON<5:4>=Valor_max
0- 100 % del ciclo de trabajo, entonces el duty cycle tendra valores entre 0- (1/50Hz), pasando de frecuencia a tiempo tenemos---->(0 seg. ---20 mseg.),
PWM duty cycle=(Valor_máx) *Tosc*(Valor del preescalador del TMR2)
20ms=(Valor_max)*(1/500Khz)*(16)
despejando Valor_max tenemos
Valor_max=625--->100%=Duty Time
/Programa sacará un PWM de 50Hz (20ms)
//con un duty time al 10% de 2ms
//f=50Hz
//Tpwm=20ms
//duty time= 2ms
//Tpwm=(PR2+1)4 Tosc PRtm2
//Tpwm=(155+1)(4)(1/500kHz)(16)=20ms
//fpwm=1/20ms=50Hz
//duty time=CCP Tosc PRtm2
//duty time=63(1/500kHz)(16)=2ms
//Res=log[4(PR2+1)]/(log(2)) ]=7.39=7bit
//63d=0x3F=00111111b
LIST P=16F887
INCLUDE <P16F887.INC>
__CONFIG _CONFIG1, _INTRC_OSC_NOCLKOUT & _WDT_OFF & _PWRTE_OFF & _MCLRE_ON & _CP_OFF & _CPD_OFF & _BOR_ON & _IESO_OFF & _FCMEN_OFF & _LVP_OFF & _DEBUG_OFF;
__CONFIG _CONFIG2, _WRT_OFF & _BOR40V;
CBLOCK 0x20
R_ContA ;Registro Para el Retardo
ENDC
org 0x04
banksel ADCON1 ;Seleccionar Registro ADCON1
movlw B'00000000' ;8 Bits para configurar ADCON1
movwf ADCON1 ;ADFM=0, justificacion izquierda
banksel TRISA ;Seleccionar Registro TRISA
bsf TRISA,0 ; PORTA--> A0 como entrada
banksel TRISC ;Seleccionar Registro TRISC
bcf TRISC,2 ; PORTC--> CPP1 como salida
banksel ANSEL ;Selec Regsitro Analogicas/Digital
bsf ANSEL,0 ;A0-->1, Entrada Analoga
banksel OSCCON ;REgistro OSCCON reloj interno
movlw B'00110101' ;reloj f=500Khz
movwf OSCCON ;
banksel ADCON0 ;Selec Registro ADCON
movlw B'01000001' ;Fosc/8
movwf ADCON0 ;Prender Funcionamiento Analogico
banksel PR2 ;REgistro PR2
movlw B'10011011' ;
movwf PR2 ;Asignacion del periodo PWM 20mseg
banksel CCP1CON ;REgistro CCP1CON
movlw B'00111100' ;b'00111100'
movwf CCP1CON ;Asignacion de modulo como pwm1
banksel CCPR1L ;REgistro CCPR1L
movlw B'00001111' ; duty ciclo
movwf CCPR1L ;Asignacion de modulo como pwm1
banksel T2CON ;REgistro T2CON
movlw B'00000111' ;8 bits del T2CON
movwf T2CON ;Postscaler 1:1, Prescaler 1:16, Timer2 ON
;clrf ADCON1
Bucle
call Retardo200us ;<- Delay for TaD
bsf ADCON0,GO ;Inicia Conversion
Back1 btfsc ADCON0,GO ;Conversion Terminada?
goto Back1 ;No retrocede 1 paso
banksel ADRESH ;Si, leer ADRESH (8 bits)
movfw ADRESH
banksel CCPR1L
movwf CCPR1L ;Mostrar en Puerto CCP1
;bcf ADCON0,GO
goto Bucle
Retardo200us
movlw d'5' ;ciclomaquina=Tiempo*Fosc/4
movwf R_ContA ;Tiempo=4*(1/fosc)*ciclomaquina
R1ms_Bucleinterno ;5+4*K=13-->K=2--->100uS
nop ;5+4*K total de ciclos maquina
decfsz R_ContA,F ;de la subrutina Retardo1ms
goto R1ms_Bucleinterno
return
END
Anexo: la simulacion proteus 7.8 sp 2 , .asm, .hex --->ok
-
Nunca hice los PWMs con el modulo CCP, a mi me resulta mas sencillo hacerlos por sofware, ademas de esta manera con un TIMER y bucles consigo hacer tantos PWMs como salidas tiene el PIC.
Veo un par de fallos en ese programa:
CBLOCK 0x0c
R_ContA ;Registro Para el Retardo
ENDC
No puedes ponerlo en 0x0c, tienes que ponerlo en 0x20.
Retardo10ms
movlw d'311' ;ciclomaquina=Tiempo*Fosc/4
movwf R_ContA ;Tiempo=4*(1/fosc)*ciclomaquina
R1ms_Bucleinterno ;5+4*K=13-->K=2--->100uS
nop ;5+4*K total de ciclos maquina
decfsz R_ContA,F ;de la subrutina Retardo1ms
goto R1ms_Bucleinterno
return
No puedes mover d'311' el numero mas alto en decimal con 8 bits es 255.
Yo es que ademas creo que ese programa no esta completo, pero como nunca trabaje con ese modulo, no lo puedo asegurar.
-
Hola Tapi8
Gracias por la observaciones pero sigue igual aunque solo he simulado en proteus y pero no sale, ya que mencionas lo hacer pwm por software no tienes un ejemplo para guiarme???
-
Aqui tienes una explicacion con ejemplos del modulo CCP, mira a ver si tienes algun fallo, en la configuracion del modulo o en otro sitio:
http://www.todopic.com.ar/foros/index.php?topic=24720.msg207734#msg207734
Por software pones un TIMER para 20ms, y despues activas la salida o salidas donde estan los sevos, para 0º llams a un retardo de 0.3ms (depende del servo), para 90º llamas una de 1.3ms y para 180º una de 2.3ms, cuando retorna de la temporizacion pones a 0 la salida y esperas a que el TIMER se desborde, durante ese tiempo puedes hacer la lectura del ADC y listo. Debes tener en cuenta que si quieres que ese servo este en dicha posicion 1 seg, por ejemplo, debes repetirlo 50 veces, ya que a los servos hay que enviarles señal constantemente.
-
Gracias Tapi8 por ayudar a resolver el problema :-/, el problema estaba en el simulador proteus toca indicarle el valor de reloj (500k) ya q lo tenia en default .
Como quedara bien los _CONFIG1 y _CONFIG2 en hexa ya que el mplab muestra error con los siguientes valores?
; __CONFIG _CONFIG1, 0xE3E5
; __CONFIG _CONFIG2, 0xFFFF
-
Yo en MPLAB siempre configuro en "configure" segunda opcion "configuration bits" le pongo los valores que quiero y listo.
Puede que el problema por ese de 500k, ya que este PIC tiene 4Mz 8Mz Y CREO QUE 32K pero 500K, no me suena que lo tenga......
-
Datasheet pic16f88x:
IRCF<2:0>: Internal Oscillator Frequency Select bits
111 = 8 MHz
110 = 4 MHz (default)
101 = 2 MHz
100 = 1 MHz
011 = 500 kHz
010 = 250 kHz
001 = 125 kHz
000 = 31 kHz (LFINTOSC)
gracias por el dato del mplab opcion de "configuration bits"
-
Pues si tien para esa frecuencia, vaya, perdon por el error, acostumbrado a trabajar siempre a 4 o 8Mz....
¿Ya te va al configurarlo asi?
-
Si ya me va con la configuracion.
Haciendo el pwm por software encontre uno por ahi, pero esta incompleto no se si esta bien:
LIST P=16F887
INCLUDE <P16F887.INC>
__CONFIG _CONFIG1, _LVP_OFF & _FCMEN_ON & _IESO_OFF & _BOR_OFF & _CPD_OFF & _CP_OFF & _MCLRE_ON & _PWRTE_ON & _WDT_OFF & _INTRC_OSC_NOCLKOUT & _DEBUG_OFF
__CONFIG _CONFIG2, _WRT_OFF & _BOR40V
CBLOCK 0x20
CicloTrabajo
ENDC
ORG 0
goto Inicio
ORG 4
goto Interrupcion_Timers
Inicio
banksel OSCCON
movlw b'01100101' ; Reloj 4MHZ
movwf OSCCON
banksel PORTA
clrf PORTA
banksel ANSEL
clrf ANSEL ;Puerto A como digital I/O
banksel TRISA
bcf TRISA,1 ;PortA,1 como salida
movf TRISA
banksel OPTION_REG
movlw 0xC2 ; Pull-Up deshabiltado, , Timer0|interno, 1:8
movwf OPTION_REG
banksel PR2
movlw d'250' ;Cargamos PR2 para el Timer2
movwf PR2
banksel T2CON
movlw 0x26 ; Timer2: Pre=1:16, Post=1:5
movwf T2CON
banksel PIR1
clrf PIR1 ; Limpiamos Banderas
bsf STATUS,RP0 ; Banco 1.-
bsf PIE1,TMR2IE ; Habilitamos interrupcion por Timer2
bcf STATUS,RP0 ; Banco 0.-
bsf INTCON,GIE ; Habilitacion general de interrupciones
bsf INTCON,PEIE ; Habilitacion de interrupciones Perifericas
Principal
movlw d'68'
movwf CicloTrabajo
goto $
Interrupcion_Timers
ISR_TIMER2
; Tratamiento de Interrupcion
movf CicloTrabajo,0 ; Cargamos ciclo de trabajo.-
movwf TMR0
bsf INTCON,T0IE ; Habilitamos interrupcion por Timer0
bsf PORTA,1 ; Llevamos a 1 lógico salida de PWM.
bcf PIR1,TMR2IF ; Borramos bandera
goto Fin_ISR
;.......................
;.......................
ISR_TMR0
; Tratamiento de Interrupcion
bcf INTCON,T0IE ; des-habilitamos interrupcion por Timer0
bcf PORTA,1 ; Llevamos a 0 lógico salida de PWM.
bcf INTCON,T0IF ; Limpiamos bandera
goto Fin_ISR
;.......................
Fin_ISR
retfie
end
-
Si, ese programa no tiene buena pinta, ¿al final no te funciona con el modulo CCP?
¿Que resulucion quieres en el servo, 1º, 2º, 5º,10º?
-
El modulo cpp funciona, si no tambien quiero probarlo con timers, el codigo anterior utiliza dos timers, el timer 2 tiene el perido de 20 ms y el timer 0 el ciclo de trabajo , ambos por interrupciones .
Resolucion pues un 1º seria perfecto.
-
Si te va bien con el modulo CCP creo que para este caso concreto te va a ir mejor, de la manera que yo te digo va a ser un poco compleja, porque ademas tienes que leer el ADC a 8 bits eso te da 256 lecturas diferentes y traducirlo a 180 posiciones, tendrias que intercalar algunos valores en que el valor del ADC no variara la posicion, pero te digo como seria.
Bien para resolucion de 1º necesitarias hacer temporizaciones de 11.11 microsegundos, cada eses 11us se moveria 1º, por lo tanto lo repetirias tantas veces como grados quiere que se mueva, y despues esperarias a que se desbordara el TIMER que estaria para 20ms.
DUTTY
bsf PORTA,2 :ponemos servo a 1, empezamos tiempo en alto
nop ;hacemos nops para completar 11us
nop
nop
nop
nop
incf CONTADOR,1 ;contador para numero de desbordes
movf REPETICIONES,0 ; en REPETICIONES se guardaria el numero de repeticiones
subwf CONTADOR,0 ;comparamos si ya van las repeticione necesarias
btfss STATUS,Z
goto DUTTY ; todavia no van seguimos haciendo bucles
bcf PORTA,2 ; ponemos a 0 DUTTY, ya transcurrio el tiempo
Hago la temporizacion de esta manera porque es muy pequeña para hacer un bucle aparte.
En REPETICIONES pondrias el valor a repetir, 90º=90 repeticiones y asi para todos los valores, este valor saldria mediante una tabla de 256 valores (uno para cada grado y alguno que saltarias), a esta tabla la llamarias con el valor de la lectura del ADC, al que habria que suprimirle 256-180=76 valores, que se pueden eliminar en en la tabla dandole el mismo valor en grados a dos numeros consecutivos de ADC:
TABLA
retlw 1
retlw 2
retlw 3
retlw 3 ; habria que insertar 76 asi
retlw 4
Bueno tu veras si te interesa hacerlo asi. Para un servo o dos igual no vale la pena pero si son muchos.....
-
Gracias tapi8 por las sugerencias pero encontre un pwm por software muy buen hecho con el pic 16f88, ya que no utiliza interrupciones. ni tablas:
Lee un potenciometro sobre RA3 y mueve el servo sobre RB3. Servos de 1 y 2 ms con periodo de 20 ms.
;###############################################################################
; Title: ADC, Servo Demo
; Author: Mike Webb
; Date: 21 Nov 2006
;################################################################################
include "p16f88.inc"
errorlevel -302 ; stop the annoying " Register in operand not in bank 0." error.
__config _CONFIG1, _CP_OFF & _CCP1_RB0 & _DEBUG_ON & _WRT_PROTECT_OFF & _CPD_OFF & _LVP_OFF & _BODEN_OFF & _MCLR_ON & _PWRTE_ON & _WDT_OFF & _INTRC_IO
__config _CONFIG2, _IESO_OFF & _FCMEN_OFF
;Just for Mike McLaren, here's some psuedo C code.
;main{
; while(1){
; W=ReadADC()>>2; //get 8 most significant bits of ADC
; ServoOut=1; //Turn on output
; Delay8(250); //delay 1mS
; If(W!=0) //skip if ADC=0
; Delay8(W); //delay W*8 clock cycles = 0 to 1,03mS
; ServoOut=0; //Turn off output
; W=256-W; //calculate off time to make total 2mS
; if(W!=0) //Skip if zero
; Delay8(W); //delay to make 2mS
; For(x=0;x<18;x++){ //Repeat 18 times
; Delay8(250); //Delay 8*250 clock cycles = 1mS
; }
; }
;}
; Setup Variables
cblock 0x20
Count
OnTime
endc
org 0h
nop ; required for ICD2
bcf STATUS,RP1; bank 0 or 2
bsf STATUS,RP0; select bank 1
movlw (0<<NOT_RBPU|0<<INTEDG|0<<T0CS|0<<T0SE|0<<PSA|B'000'<<PS0)
movwf OPTION_REG; setup option reg
bcf TRISB,3 ; make b3 output
movlw B'111'<<IRCF0; =b'01110000'
movwf OSCCON; select 8MHz clock
movlw b'00001000'; Select bit 3
movwf ANSEL; ADC on RA3
bcf STATUS,RP0; back to bank 0
Loop Call ReadADC; read A3
movwf OnTime; Store it for later
movlw d'250'; 250*8 = 2000 cycles = 1mS
bsf PORTB,3; Turn output on
call DelayW; delay the 1mS
movfw OnTime; Get value from ADC
btfss STATUS,Z; if it's zero don't do any delay
call DelayW; delay W*8 = 0 to 255 = 0 to 1.03 mS -- near enough
bcf PORTB,3; Turn output off - pulse is complete
comf OnTime,W; Complement ADC value
addlw 1; com and inc = negate.
btfsc STATUS,Z; Don't do a delay of zero
call DelayW; delay (256-ADC value)*8 = time required to get to 2mS
movlw d'18'; We need to delay another 18mS
movwf Count; so setup a counter
TimeLoop movlw d'250'; 250*8 = 2000 clock cycles = 1mS
call DelayW; do the delay
decfsz Count,F; repeat it?
goto TimeLoop; yes, go around again
goto Loop; Go back and do it all again
DelayW
; will delay W*8 cycles including the call/return
addlw 0xff; add -1 to W 1
btfsc STATUS,Z; =zero? 1/2
goto DoneDelay; yes, better get out of here 2
nop; delay a bit 1
goto $+1; delay a bit more 2
goto DelayW; go around again 2
DoneDelay return ; 2
ReadADC movlw b'01'<<ADCS0|b'011'<<CHS0|b'0'<<GO|b'1'<<ADON
movwf ADCON0; select pin A3 and 16 TOSC
bsf STATUS,RP0; bank 1
movlw b'0'<<ADFM|b'1'<<ADCS2|b'00'<<VCFG0
movwf ADCON1; Left justify
bcf STATUS,RP0; back to bank 0
movlw 5; wait 5*8 = 40 cycles
call DelayW; for ADC capacitor to charge
bsf ADCON0,GO; Start the conversion
WaitADC btfsc ADCON0,GO; Conversion complete?
goto WaitADC; No, so keep waiting
movfw ADRESH; return with result in W
return
END
Funciona, al menos en simulacion en proteus.
-
Hombre es que ese tiene tres movimientos ¿no?, el que yo te digo tiene 180 movimientos de 1º, que con un par de pequeñas modificaciones se pueden convertir en 256 posiciones de 0.7º, pero si con ese te vale, pues mucho mas sencillo, claro.
-
no, tambien es de 0 a 180° pues tambien vale OK
-
Estoy programando un pwm a 50 hz con el pic16f877 en mplab
No se puede alcanzar esa frecuencia con el PWM del 887, es demasiado baja.
Saludos!