CCS C Software and Maintenance Offers
FAQFAQ   FAQForum Help   FAQOfficial CCS Support   SearchSearch  RegisterRegister 

ProfileProfile   Log in to check your private messagesLog in to check your private messages   Log inLog in 

CCS does not monitor this forum on a regular basis.

Please do not post bug reports on this forum. Send them to CCS Technical Support

help with multiple timer interrupts on PIC16F877A

 
Post new topic   Reply to topic    CCS Forum Index -> General CCS C Discussion
View previous topic :: View next topic  
Author Message
swapnil14327



Joined: 25 Jul 2014
Posts: 11

View user's profile Send private message

help with multiple timer interrupts on PIC16F877A
PostPosted: Fri Jul 25, 2014 12:44 am     Reply with quote

Hello,
i am new to this forum.
i wanted help regarding timer interrupts on PIC16F877A

i have a logic where i use

input:: as any binary string (1010110..)

output:: i want 38khz sine wave with NEC protocol (http://www.sbprojects.com/knowledge/ir/nec.php)

I am able to create the 38khz sine wave using the below code
Code:

unsigned int8 value    = 53;
setup_timer_2(T2_DIV_BY_1, 104, 1);     // (1/16000000)*4*1*(104+1) =  26.2 uS      setup 38KHz freq.
   set_pwm1_duty(value);                 //set 50% duty cycle
   setup_ccp1(CCP_OFF);





for every 1 the wave will be ON for 560us and OFF for 1690us
for every 0 the wave will be ON and OFF for 560us.

Code:
switch (outstring[i++])
         {
            case '1' :     setup_ccp1(CCP_PWM);
                    delay_us(560);
                    setup_ccp1(CCP_OFF);
                    delay_us(1690);
                    break;
                  
            case '0' :   setup_ccp1(CCP_PWM);
                  delay_us(560);
                  setup_ccp1(CCP_OFF);
                  delay_us(560);
                  break;
         }



this above logic is working but with some timing difference
i want to create the above logic with timer/timers without delay function

i studied for timers but i am confused how to initialize it and then how do i stop it after timer interrupt of 560us and then again start other timer for next 1690us timer interrupt..

and so on for the next 1 or 0..

can some one please help me with this logic in timer interrupts....
Mike Walne



Joined: 19 Feb 2004
Posts: 1785
Location: Boston Spa UK

View user's profile Send private message

PostPosted: Fri Jul 25, 2014 2:38 am     Reply with quote

There are loads of ways to do this. Here's one.

560us period is ~21 cycles of your 38kHz.
Set up two flags, 'ISR_done' and 'pulse'.
Set up timer2 to initiate an ISR every 21 cycles.

In the ISR:-

1) If 'pulse' is TRUE set PWM width to give pulses, else no pulses.
2) Set 'ISR_done' flag to TRUE.

In main():-

1) Keep track of whether you are wanting to output pulses or not, ie set 'pulse' to TRUE or FALSE.
2) Wait for ISR to set 'ISR_done' to TRUE, then set 'ISR_done' to FALSE, move to next phase,

Mike
swapnil14327



Joined: 25 Jul 2014
Posts: 11

View user's profile Send private message

PostPosted: Fri Jul 25, 2014 3:39 am     Reply with quote

thanks a lot mike for the reply.

i did not get it..
timer 2 i have already used to produce 38khz wave.

can u please write in syntax format, i am a bit confused for

Quote:
Set up timer2 to initiate an ISR every 21 cycles


should i use
Quote:
set_timer2(0);

or
Quote:
setup_timer_2(); (already used for 38khz signal)
Ttelmah



Joined: 11 Mar 2010
Posts: 19520

View user's profile Send private message

PostPosted: Fri Jul 25, 2014 3:50 am     Reply with quote

Comments:

If you are building a transmitter, consider another PIC....

Thing is that assuming you want a relatively simple circuit, you would be much better off with one of the newer PIC's that has an internal oscillator. Result just a single chip, couple of smoothing capacitors, driver transistor and the IR LED.

Then 'sine wave'!.... No. The protocol uses a square wave, and this is what the PIC generates....

Don't get too neurotic about the timings. The whole 'point' of this type of protocol is to not require great precision. They are designed to happily cope with 10% or more variations in the signal frequencies and pulse widths, so that remote control transmitters do not need crystal oscillators.
Be sure whether you are using the Sony protocol or the NEC protocol. They are very similar. Sony uses 40KHz, rather than 38K, and 600uSec, versus 560uSec.

Mike's approach, is basically the way to go. Except, that Timer2, can't be set to the 21 pulses interval required. Instead, use a second timer. The 'core interval' of the protocol, is 560, or 600uSec (NEC/Sony). The basic timings are:

Start pulse - 4* interval continuous tone, 1* interval off
'1' - 2* interval tone, 1* interval off.
'0' - 1* interval tone, 1* interval off.

So have a timer set to interrupt at 600uSec, and just set the tone on/off as required.

Use a state machine (search here if you do not know what this is).

Have actions of perhaps 'send_start', 'send_0', and 'send_1'.

Then for each of these have a simple table of the number of timer intervals involved (5,3,1), and the pattern of on/off needed in these (11110,110,10).
For each state, you decrement the count, and when it gets to 0, move to the next bit in the source.

Then take your incoming 'word', and count through the bits in this, starting by sending 'start', then send_1 or send_0, for each bit in the word, advancing through the word as each bit sends. For the NEC protocol, you would go through each byte twice, sending for the '1' for each '1', and '0' for each '0', then a second time sending '0' for each '1', and vice versa.
swapnil14327



Joined: 25 Jul 2014
Posts: 11

View user's profile Send private message

PostPosted: Fri Jul 25, 2014 4:37 am     Reply with quote

Thanks Ttelmah

I am using nec protocol only (i.e 38khz)
and sorry for "sine wave" my mistake, pic pwm produces square wave which i am getting proper 38khz.


Quote:
Start pulse - 4* interval continuous tone, 1* interval off
'1' - 2* interval tone, 1* interval off.
'0' - 1* interval tone, 1* interval off.


its not multiple of 560, thats the problem.

ON time is 560ms and OFF time is (2250-560us) which is 1690us
if i set is ON for 3 * 560us it comes to 1680us

will it make difference in the protocol..

it not then when i use the below code in only few cycles i get the time difference of (2-5us) ..

Quote:
switch (outstring[i++])
{
case '1' :setup_ccp1(CCP_PWM);
delay_us(560);
setup_ccp1(CCP_OFF);
delay_us(1690);
break;

case '0' :setup_ccp1(CCP_PWM);
delay_us(560);
setup_ccp1(CCP_OFF);
delay_us(560);
break;
}



Also i will search for state machine.
Mike Walne



Joined: 19 Feb 2004
Posts: 1785
Location: Boston Spa UK

View user's profile Send private message

PostPosted: Fri Jul 25, 2014 6:22 am     Reply with quote

swapnil14327 wrote:
thanks a lot mike for the reply.

i did not get it..
timer 2 i have already used to produce 38khz wave.

can u please write in syntax format, i am a bit confused for

Quote:
Set up timer2 to initiate an ISR every 21 cycles


should i use
Quote:
set_timer2(0);

or
Quote:
setup_timer_2(); (already used for 38khz signal)


My initial response was a quickie. Had not taken the precaution of checking SETUP_TIMER(2) syntax.

Your line
Code:

setup_timer_2(T2_DIV_BY_1, 104, 1);     // (1/16000000)*4*1*(104+1) =  26.2 uS      setup 38KHz freq.
Does most of what is needed. You simply set the last parameter to the number of timer resets between interrupts.
As Mr T points out you can't set it to 21 as I suggested. (The max is 16)

You could set it 7 and work on every third interrupt.
OR
Accept slightly greater error and use a simpler scheme.

Mike
Ttelmah



Joined: 11 Mar 2010
Posts: 19520

View user's profile Send private message

PostPosted: Fri Jul 25, 2014 7:37 am     Reply with quote

and of course, he has the answer to his own question, in the 'spec'. It specifies the interval at 560uSec, which is not an exact multiple of the 38KHz. The whole protocol is just not 'that accurate' 21, or 22 pulses are well within the error margins. 21 as 3*7 is very easy, or 22 as 2*11, and using just one timer is the nice way to go, but instead of using CCP_OFF to control the PWM, just use:

set_pwm1_duty(53); //to turn the pulses on.

set_pwm1_duty(0); //to turn the pulses off.

This way the CCP remains running, and the interrupt will keep going, just outputting nothing. Otherwise the CCP risks losing pulses when changed.
Ttelmah



Joined: 11 Mar 2010
Posts: 19520

View user's profile Send private message

PostPosted: Fri Jul 25, 2014 8:41 am     Reply with quote

Now, this is 'brutal' code (designed to give nightmares...).
However:
Code:

#include <16F877.h>
#device ADC=12
#use delay(crystal=16000000)

int8 address=0;
int8 command=0;
int1 send=FALSE;
#define SEND_PULSE 53 //PWM pulse widths
#define SEND_OFF 0
//Macro designed to drive you insane....
//works through a byte (LSb first), sending the required patterns
#define OP_BYTE(byte, invert, job, bits, mask, state)\
if (--job>0) { \
   if (--bits==0){\
       if (((mask & byte)!=0) ^ invert)\
           bits=4; \
       else\
           bits=2; \
       mask*=2;\
       set_pwm1_duty(SEND_PULSE);\
   }\
   else \
       set_pwm1_duty(SEND_OFF);\
}\
else {\
   state++;\
   mask=1;\
   job=8; \
   bits=0; \
}

#INT_TIMER2
void seven_ticks(void)
{
    static int1 old_send=FALSE;
    static int8 mask=1;
    static int8 fast_clock=3;
    enum state_list {start,address_plus,address_minus,command_plus,command_minus};
    static state_list state=start;
    static int8 job_count=0;
    static int8 bit_work; //count used to generate the bit
    if (send==FALSE)
    {
        old_send=FALSE;
        return; //exit if not sending
    }
    if (--fast_clock == 0)
    {
       //Every third tick
       fast_clock=3;
       if (old_send==FALSE)
       {
           //start a new transmission
           old_send=TRUE;
           state=start;
           job_count=25; //for the 9/4.5mSec start
       }
       switch (state)
       {
       case start:
           //sending the start marker
           if (--job_count > 8)
              set_pwm1_duty(SEND_PULSE);
           else
           {
              if (job_count>0)
                 set_pwm1_duty(SEND_OFF);
              else
              {
                  //Finished the start marker
                  state++;
                  mask=1;
                  job_count=8; //eight bits to send
                  bit_work=0; //start a new bit
              }
           }
           break;
       case address_plus:
           //Now need to send the address byte
           OP_BYTE(address,0,job_count, bit_work, mask, state);
           if (state==address_plus)
              break;
       case address_minus:
           OP_BYTE(address,1,job_count, bit_work, mask, state); //send again inverted
           if (state==address_minus)
              break;
           break;
       case command_plus:
           OP_BYTE(command,0,job_count, bit_work, mask, state); //send the command byte
           if (state==command_plus)
              break;
           break;
       case command_minus:
           OP_BYTE(command,1,job_count, bit_work, mask, state);
           if (state>command_minus)
           {
              send=FALSE;
              state=start;
              //stop transmission
           }
           break;
       }
    }
}


void main()
{
   set_pwm1_duty(0); //ensure PWM starts 'off'
   setup_ccp1(CCP_PWM);
   setup_timer_2(T2_DIV_BY_1, 104, 7);     // (1/16000000)*4*1*(104+1) =  26.2 uS      setup 38KHz freq.
   enable_interrupts(INT_TIMER2);
   enable_interrupts(GLOBAL);
   command=0x55;
   address=0xAA;
   send=TRUE; //start a stransmission

   while(send)
   {
      delay_us(10); //wait for transmission to complete
   }
   
   //will get here when the address and command have been sent (possibly).
   
   while(TRUE) ;
}


No guarantees. I've probably miscounted a state or loop count somewhere. The protocol is meant to sent the start marker, then the address byte, then this inverted, then the command byte, then this inverted. 'send' will go false when the whole packet has transmitted (I hope...).


Last edited by Ttelmah on Sat Jul 26, 2014 7:17 am; edited 1 time in total
swapnil14327



Joined: 25 Jul 2014
Posts: 11

View user's profile Send private message

PostPosted: Sat Jul 26, 2014 4:29 am     Reply with quote

Thanks for the reply..I will go though it..
Display posts from previous:   
Post new topic   Reply to topic    CCS Forum Index -> General CCS C Discussion All times are GMT - 6 Hours
Page 1 of 1

 
Jump to:  
You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot vote in polls in this forum


Powered by phpBB © 2001, 2005 phpBB Group