![](templates/subSilver/images/CCSLogo.jpg) |
![CCS C Software and Maintenance Offers](templates/subSilver/images/forumAd6.jpg) |
View previous topic :: View next topic |
Author |
Message |
swapnil14327
Joined: 25 Jul 2014 Posts: 11
|
help with multiple timer interrupts on PIC16F877A |
Posted: Fri Jul 25, 2014 12:44 am |
|
|
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.... |
|
![](templates/subSilver/images/spacer.gif) |
Mike Walne
Joined: 19 Feb 2004 Posts: 1785 Location: Boston Spa UK
|
|
Posted: Fri Jul 25, 2014 2:38 am |
|
|
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 |
|
![](templates/subSilver/images/spacer.gif) |
swapnil14327
Joined: 25 Jul 2014 Posts: 11
|
|
Posted: Fri Jul 25, 2014 3:39 am |
|
|
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
or
Quote: | setup_timer_2(); (already used for 38khz signal) |
|
|
![](templates/subSilver/images/spacer.gif) |
Ttelmah
Joined: 11 Mar 2010 Posts: 19366
|
|
Posted: Fri Jul 25, 2014 3:50 am |
|
|
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. |
|
![](templates/subSilver/images/spacer.gif) |
swapnil14327
Joined: 25 Jul 2014 Posts: 11
|
|
Posted: Fri Jul 25, 2014 4:37 am |
|
|
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. |
|
![](templates/subSilver/images/spacer.gif) |
Mike Walne
Joined: 19 Feb 2004 Posts: 1785 Location: Boston Spa UK
|
|
Posted: Fri Jul 25, 2014 6:22 am |
|
|
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
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 |
|
![](templates/subSilver/images/spacer.gif) |
Ttelmah
Joined: 11 Mar 2010 Posts: 19366
|
|
Posted: Fri Jul 25, 2014 7:37 am |
|
|
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. |
|
![](templates/subSilver/images/spacer.gif) |
Ttelmah
Joined: 11 Mar 2010 Posts: 19366
|
|
Posted: Fri Jul 25, 2014 8:41 am |
|
|
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 |
|
![](templates/subSilver/images/spacer.gif) |
swapnil14327
Joined: 25 Jul 2014 Posts: 11
|
|
Posted: Sat Jul 26, 2014 4:29 am |
|
|
Thanks for the reply..I will go though it.. |
|
![](templates/subSilver/images/spacer.gif) |
|
|
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
|