Jump to content
Sign in to follow this  
Tar

Servo Control

Recommended Posts

I am trying to control a simple futaba servo by creating a pulse on pin 4 of portb. I am trying to create a pulse from 1ms to 2ms. I believe with the settings below tmr0 is interrupting ever 100us. I also generate an interrupt when the usart receives a character. If the character is '>' I increment the pw variable and if the character is '<' I decrement the pw variable. I am trying to use the timer to create the pulse so I don't have to use delays. Maybe I am going about this problem totally wrong so any suggestions are appreciated. I have been printing tmr0 to the serial port and I notice that very often it is less than 156. How is this happening? When the tmr0 interrupt happens I set it 156. Anyone see any problems with the code below?

 

 

#include

#include "main.h"

 

//Define device config block

asm

{

list p=PIC16F628

__config H'3FE9' ;XT,!CP,!WDT,!PWRT,MCLR,BODEN,LPV,!CPD

}

 

void printCharDec(char n);

void sendChar(char n);

 

void printCharDec(char n)

{

if(n >= 100)

{

sendChar(((n / 100) % 10) + '0');

}

if(n >= 10)

{

sendChar(((n / 10) % 10) + '0');

}

sendChar((n % 10) + '0');

sendChar(13);

sendChar(10);

}

 

void sendChar(char n)

{

while(!test_bit(pir1, TXIF))

{

nop();

}

txreg = n;

set_bit(txsta,TXEN);

}

 

unsigned char data;

unsigned char pw;

unsigned char count;

 

//////////////////////////////////////////////

// Interrupt handler

//////////////////////////////////////////////

void interrupt( void )

{

if(test_bit(intcon,T0IF))

{

if(count == 0)

{

set_bit(portb, 4);

}

if(count == pw)

{

clear_bit(portb, 4);

}

count++;

if(count == 200)

{

count = 0;

}

tmr0 = 156;

clear_bit(intcon,T0IF);

 }

 

if(test_bit(pir1, RCIF))

{

   if(test_bit(rcsta, FERR) || test_bit(rcsta, OERR))

{

     clear_bit(rcsta,CREN);

     nop();  // Put a nop between bit operations on the same register to avoid PIC read-modify-write problems

     set_bit(rcsta,CREN);

   }

   data = rcreg;

   if(data == '<')

   {

pw--;

}

else if(data == '>')

{

pw++;

}

if(pw < 10)

{

pw = 10;

}

if(pw > 20)

{

pw = 20;

}

printCharDec(tmr0);

 }

}

 

//////////////////////////////////////////////

// Main program entry

//////////////////////////////////////////////

main()

{  

 set_bit(txsta,BRGH);

 spbrg = 25;

OPTION_REG = 8;

enable_interrupt(GIE);

 

set_bit(intcon, T0IE);

clear_bit(txsta,SYNC);  // SYNC=0;  // Async Mode

 set_bit(rcsta,SPEN);    // SPEN=1;  // Serial Port Enable

set_bit(txsta,TXEN);    // Transmit Enable

set_bit(rcsta,CREN);    // Constant Receive Enable

set_bit(pie1,RCIE);     // Receive Interrupt Enable

 set_bit(intcon,PEIE);   // Peripherial Interrupt Enable

 

 

 

 

//Init ports

//A and B port initialization

 cmcon = 8;

 trisa = 0;

 trisb = 0x02;

 

 porta = 0;

 portb = 0;

 

 count = 0;

 pw = 10;

 tmr0 = 0;

 

//Infinite loop

while(1)

{

clear_wdt();

}

}

 

Share this post


Link to post
Share on other sites
Guest Dave

Tar,

 

I like the idea of using the timer interrupt code for a nice consistent PWM output, this is what I would do, although if this is the only thing you are doing, you could consider doing the PWM with simple delays.

 

My first quick glance causes me to ask the question about serial comms. You have that code firing off in your timer interrupt, how long do you thing it takes to transmitt that character ?

At 9600 baud that 1ms per character, so you timer will be wrapping round before you leave the interrupt routine - I guess this is what you are seeing, the serial comms is messing up the timing!

 

Move the serial comms code out of the timer interrupt routine, put it in the main routine loop instead.

 

Regards

Dave

Share this post


Link to post
Share on other sites
Guest Dave

Tar,

 

I just look again at you code, I think I was a little wrong.

I must just be that you code is executing more that 25 ( 100us/4) instructions, casing TMR0 to wrap and keep timing before displaying the TMR0 value, also adding this display routine in you timer interrupt routine is not a good idea.

 

Regards

Dave

Share this post


Link to post
Share on other sites

Dave thank you for your input. When I get home tonight I am going to remove the serial code from the interrupt and also try to cut down the number of instructions inside the interrupt and see if that helps. Other than that does my logic look correct or at least on the right path for what I am trying to accomplish? In the future I am going to have more code so I am trying to figure out pwm using interrupts. I think cycles will be "wasted" using delays, correct?

Share this post


Link to post
Share on other sites
Guest Dave

Tar,

 

Generally your logic looks correct, its always hard to say its 100% with spending a long time looking at code, and even then without testing there is always uncertainty.

 

code to put in interrupt routine:

 

void interrupt()
{
tmr0 = 156; // reset immediately so next interrupt timing starts now
clear_bit(intcon,T0IF); // clear current interrupt

if( ++pwm_count == 200 ) // count value range 0 to 199
pwm_count = 0;

if ( pwm_count == on_val )
set_bit(portb, 4);

if ( pwm_count == off_val )
clear_bit(portb,4);

}

 

change off_val and on_val in main loop to keep overhead in interrupt routine to a minimum.

 

Regards

Dave

Share this post


Link to post
Share on other sites

Join the conversation

You are posting as a guest. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

Loading...
Sign in to follow this  

×
×
  • Create New...