Sie sind auf Seite 1von 3

SOFTW PWM para SERVOS

Con un cristal a 8Mhz podemos controlar 5 servos a 56Hz (los servos responden a frecuencias 50 / 57Hz) con resolucion de 256bits (0->255) 1/57=0.01754 => 17.5ms de ciclo para 57Hz De los 17.5ms de ciclo, los 2.5ms informan al servo de la posicion a adoptar. de los 2.5ms, los 0.5 iniciales son la posicon -90 los 2ms restantes se dividen en 256 posiciones posibles con la resolucion de 256 bits. Los (17.5-2.5 =>)15ms restantes son en estado low (ver img izq) para que el servo llegue a su posicion. Con + Mhz podemos controlar + canales o + resolucion

#include <p18f24k20.h> #include <delays.h> unsigned char pos;; unsigned char servo[5]; #pragma code low_vector = 0x08 #pragma interrupt isr void isr(void) { pos=0; LATA = 0b11111111; Delay1KTCYx(1); //delay de 500us _asm movlw 0 decfsz servo+0, 1,1 iorlw 1 decfsz servo+1, 1,1 iorlw 2 decfsz servo+2, 1,1 iorlw 4 decfsz servo+3, 1,1 iorlw 8 decfsz servo+4, 1,1 iorlw 16 andwf LATA, 1,0 movlw 0 incfsz pos, 1,1 bra -15 _endasm INTCONbits.TMR0IF=0; TMR0H=0x88; TMR0L=0xB8; } #pragma code

void main(void){ LATA=0x00; TRISA=0x00; TMR0L=0x00; TMR0H=0x00; T0CON = 0b00001000; //bypass preescaler para obt 1:1 RCONbits.IPEN = 0; //Enable Interrupt Priorities //INTCONbits.GIEH = 1; //Enable Low Priority Interrupt //INTCONbits.PEIE = 0; //Enable GlobalInterrupt //INTCONbits.TMR0IE = 1; //Enable Timer0 Interrupt INTCON=0b10100000; T0CONbits.TMR0ON = 1; //Start Timer0 while(1){ //Recibir por SPI o por USART y asignar valores a los servos. servo[0]=255; servo[1]=125; servo[2]=75; servo[3]=62; servo[4]=50; Delay10KTCYx(10); } }

TIMER: Lo primero que hay que onseguir es una interrupcion del timer cada 17.5ms (aprox 20ms) ..por lo que: 8MHz => 1/2000000 => 0.0000005 0.0000005*65535 => 0.0327675 ---> Con preescaler 1:1, el timer se desborda cada 32.7ms (tiene k ser lo + cercano a 20ms) (65535 * 0.02) / 0.03276 => 40000 65535-40000 => el timer tiene que empezar a contar desde 25535 para desbordar cada 40000

OJO el preescaler minimo de TMR0 es 1:2 -->si queremos 1:1 lo que hay que hacer es un bypass del preescaler seteando en T0CON = 0b00001000; T0CONbits.PSA TImer0 prescaler is NOT assigned. Timer0 clock input bypasses prescaler. ASM: Lo que hace el ASM es un bucle que se repite 256 veces (de ah la resolucion de 256) el conteo de las pasadas lo lleva la variable pos en cada pasada lo que hace es irse a cada posicion de servo[x] y le resta un 1 al valor que contiene. decfsz servo+0, 1,1 si el resultado de la operacion es 0, se salta la siguiente instruccion. de lo contrario se deduce que el servo tiene que estar en High en esa pasada, con lo que la instruccion iorlw 1 lo que hace es escribir el literal en el registro w. 1,2,4,6,8,16,32 es lo mismo que 00000001 000000010 00000100 00001000 00010000 ...... Ejemplo: Tenemos srv 0,2 y3 ON 1 y 4 OFF en la pasada x decfsz en srv0 no es < 0, con lo que iorlw mete un 1 a w ------------>00000001 decfsz en srv1 es =0 con lo que se salta iorlw ------------>00000001 decfsz en srv2 no es < 0, con lo que iorlw mete un 4 a w ----------->00000101 decfsz en srv3 no es < 0, con lo que iorlw mete un 6 a w ----------->00001101 decfsz en srv4 es =0 con lo que se salta iorlw ------------>00001101 andwf LATA, 1,0 Realiza un and de lo que tenemos en W con lo que hay en LATA LATA => 0b11111111 W => 0b00001101 -----------------------------------LATA = 0b00001101 incfsz pos, 1,1 Suma 1 a pos . Si pos = 0, (ocurre cuando desborda al llegar a 255), se salta la sig intruccion bra -15 como pos no ha desbordado, se ejecuta y retrocede x lineas hasta el inicio movlw 0 AJUSTE: Ejecutar el bucle completo de asm dura exactamente 2ms hay que sumarle los 0,5ms iniciales de los servos (en el delay previo
Delay1KTCYx(1); //delay de 500us)

Si con 8 Mhz el bucle ejecuta en 2ms 5 puertos, con uno de 40Mhz podemos controlar unos 30 puertos. EL BUCLE SIEMPRE TIENE QUE EJECUTARSE EN 2ms....si nos quedamos cortos, podemos meterle nop hasta dejarlo niquelado. El timer desbordaba cada 40000 pero ahora hay que sumarle el tiempo que tarda en ejecutarse el bucle, as que: 40000 * 0.0025 /0.02 => 2.5ms son 5000 ticjs de timer 40000-5000 => 35000 => 0x88B8
TMR0H=0x88; TMR0L=0xB8;

Con cristal 16Mhz


#include <p18f24k20.h> #include <delays.h> unsigned char pos; unsigned char servo[16];

14PWM
vvoid main(void){ unsigned char tmp=0; LATA=0x00; TRISA=0x00; LATB=0x00; TRISB=0x00; TMR0L=0x00;

#pragma code low_vector = 0x08 #pragma interrupt isr

void isr(void) { //20ms a 16mhz //Toc=0.00000025 ---necesitamos preescaler 1:2 //con preesc 1:2 65535 se ejecutan en 0.0327675 //500us => pos=0; LATA = 0b11111111; LATB = 0b11111111; Delay1KTCYx(1); //delay de 500us _asm movlw 0 decfsz servo+0, 1,1 iorlw 1 decfsz servo+1, 1,1 iorlw 2 decfsz servo+2, 1,1 iorlw 4 decfsz servo+3, 1,1 iorlw 8 decfsz servo+4, 1,1 iorlw 16 decfsz servo+5, 1,1 iorlw 32 decfsz servo+6, 1,1 iorlw 64 decfsz servo+7, 1,1 iorlw 128 andwf LATA, 1,0 movlw 0 decfsz servo+8, 1,1 iorlw 1 decfsz servo+9, 1,1 iorlw 2 decfsz servo+10, 1,1 iorlw 4 decfsz servo+11, 1,1 iorlw 8 decfsz servo+12, 1,1 iorlw 16 decfsz servo+13, 1,1 iorlw 32 andwf LATB, 1,0 movlw 0 incfsz pos, 1,1 bra -35 _endasm INTCONbits.TMR0IF=0; TMR0H=0x88; TMR0L=0xB8; } #pragma code

TMR0H=0x00; T0CON = 0b00000000; //preescaler 1:2 RCONbits.IPEN = 0; //Enable Interrupt Priorities //INTCONbits.GIEH = 1; //Enable Low Priority Interrupt //INTCONbits.PEIE = 0; //Enable GlobalInterrupt //INTCONbits.TMR0IE = 1; //Enable Timer0 Interrupt INTCON=0b10100000; T0CONbits.TMR0ON = 1; //Start Timer0 while(1){ for(tmp=0;tmp<15;tmp++){ servo[tmp]=255; } } }

Das könnte Ihnen auch gefallen