|
|
View previous topic :: View next topic |
Author |
Message |
WideBoy Guest
|
Using interrupt to generate 16 bit timer |
Posted: Mon Mar 19, 2007 5:17 am |
|
|
(Using a PIC 12F683)
I've setup the internal timer:
Code: | setup_timer_0(RTCC_INTERNAL | RTCC_8_BIT | RTCC_DIV_256); |
and this works fine. Because the chip is currently working at 4MHz this will tick every 256us, and overflow after approx 65ms. I want this level of precision. I also require a longer range to this timing - I need to get an accurate time interval of several seconds.
To do this I am trying to use an interrupt to increment a second 8bit counter:
Code: | #int_RTCC
void RTCC_isr(void) {
timer1++;
} |
As far as I understand, every time timer0 overflows it generates an interrupt. The interrupt should be serviced which should increment the timer1 counter. This is not happening reliably! I think some other part of my code must be interfering with the timing, but (as you can no doubt tell) I am new to this so don't know where to start...
(My other code is taking these two 8bit "time" codes, manipulating them and sending them via an IR pulse train)
I hope I have posted enough info, but let me know what else I need to post so you can help!
Thanks,
Ben
Here's the important stuff (I think):
Code: |
void SendPreamble();
void SendEnd();
void SendData(int data);
void SendTime(int sender);
int reverse(int backwards );
int timer1=0, sender1, sender2;
#int_RTCC
void RTCC_isr(void) {
timer1++;
}
int main()
{
CMCON0 = 7; // Turn off the comparators
ADCON0 = 0; // Set all i/o pins to digital
ANSEL = 0; // Turn off the A/D converter
set_tris_a(0b00011011); //set port a directions
port_a_pullups(0b011011);
set_options(0b00001000); //GPWU, GPPU, T0CS, T0SE, PSA, PS2, PS1, PS0
setup_timer_0(RTCC_INTERNAL | RTCC_8_BIT | RTCC_DIV_256);
VISLED=0;
IRLED=0;
timer1=0;
sender1=0;
sender2=0;
enable_interrupts(GLOBAL);
enable_interrupts(INT_RTCC);
while(true){
if(!SW1 && SW2 && SW3){
VISLED=1;
sender1=reverse(timer1);
sender2=reverse(get_rtcc());
SendPreamble();
SendData(0);
SendTime(sender1);
SendTime(sender2);
SendEnd();
delay_ms(20);
VISLED=0;
}
} //end of while(true)
} //end of main loop
void SendPulse()
{
int i;
for(i=0;i<32;i++){
IRLED = 1;
delay_us(13);
IRLED = 0;
delay_us(6);
}
}
void SendNull()
{
int j;
for(j=0;j<32;j++){
IRLED = 0;
delay_us(13);
IRLED = 0;
delay_us(6);
}
}
void SendPreamble()
{
SendPulse();
SendNull();
SendPulse();
SendPulse(); //Preamble = 1011
}
void SendEnd()
{
SendNull();
SendPulse();
SendPulse(); //End = 011
}
void SendData(int data)
{
if(data==0)
{
SendNull();
SendNull();
SendNull();
}
if(data==1)
{
SendNull();
SendNull();
SendPulse();
}
if(data==2)
{
SendNull();
SendPulse();
SendNull();
}
}
int reverse( int backwards )
{
int result=0;
int x;
for( x = 0; x < 8; x++)
{
if(bit_test(backwards,7-x))
bit_set(result,x);
else
bit_clear(result,x);
}
return(result);
}
void SendTime(int sender)
{
int k;
for(k=0;k<8>>=1;
}
} |
|
|
|
Guest
|
|
Posted: Mon Mar 19, 2007 6:54 am |
|
|
1) Define "reliably". It misses some steps, it falis to increment, it increments with different times between increments?
iīm supposing that you are missing points, like if it was increased twice... letīs see how much time an interaction of your code takes:
VISLED=1; // negligibe
sender1=reverse(timer1); // negligibe
sender2=reverse(get_rtcc()); // negligibe
SendPreamble(); // 76us
SendData(0); // 57us
SendTime(sender1); // donīt know (code mangled)
SendTime(sender2); // donīt know
SendEnd(); // 57us
delay_ms(20); // 20ms
VISLED=0; // negligibe
total: about 20.17ms (without the unknown parts) so about 1/3 of the overflow time. It looks ok. Maybe the problem is at the receiver: If it gets the data and tries to print it maybe it canīt be done in under 65ms so it looses data. |
|
|
WideBoy Guest
|
|
Posted: Mon Mar 19, 2007 8:17 am |
|
|
Thanks for getting back to me.
I'm picking up the IR data train with a USB receiver. I can then see the data along with a timestamp from my USB monitor - and they don't correspond!
I've tried monitoring the output with my oscilloscope, but I can't see enough signal repetitions with a high enough resolution to draw any conclusions. If I change the program so that it just sends 2 numbers for the 2 8bit times then the sending/receiving works fine, and I can monitor the USB bus and see that the correct signal is being received every ~45ms.
The problem only starts to occur when I want to send the correct timing!
The missing code didn't paste properly, so I've reposted the function below:
Code: |
void SendTime(int sender)
{
int k;
for(k=0;k<8;k++)
{
if(sender&0b00000001==0b00000001)
SendPulse();
else
SendNull();
sender>>=1;
}
} |
|
|
|
WideBoy Guest
|
|
Posted: Mon Mar 19, 2007 8:26 am |
|
|
With regards to the timing, each pulse (high or low) consists of 32 pulses, which take approx 28us each. So each pulse (bit) is approx 900us. Thus:
SendPreamble (4 bits) = 3.6ms
SendData (3 bits) = 2.7ms
SendTime (8 bits) = 7.2ms
SendTime (8 bits) = 7.2ms
SendEnd (3 bits) = 2.7ms
total train is 26 bits and takes approx 23.4ms. |
|
|
Ttelmah Guest
|
|
Posted: Mon Mar 19, 2007 9:28 am |
|
|
Your problem is almost certainly not with the timer, but with using 'delay_ms'.
Handling the timer interrupt takes some time. Typically about 60 instruction times (60uSec on your system). Now this is happening every 65536 instruction times, so whenever one of the delays happens to straddle this operation, the delay will be slow by this amount.
Now these times are asynchronous to the timer event, so every few loops, one fixed delay, will run slow by this amount.
The best solution, is to not used these 'fixed' delays, but instead to generate your own delay code, which calculates the 'finish' value required for the hardware timer, and it's overflow, and waits for this to happen.
Best Wishes |
|
|
WideBoy Guest
|
|
Posted: Mon Mar 19, 2007 10:42 am |
|
|
Thanks for the input,
I think you are right about the source of the problem. Speaking to a friend a short while ago he also thought the problem might arise because of the way I generate the "pulses". I'm not sure I understand your suggested solution though!
Quote: |
The best solution, is to not used these 'fixed' delays, but instead to generate your own delay code, which calculates the 'finish' value required for the hardware timer, and it's overflow, and waits for this to happen.
|
I know I'm trying to generate a square wave, with a particular period (and 50% duty cycle), but I'm not sure I understand what you are suggesting with regard to calculating a 'finish' value and then waiting for that value....
Are you suggesting I monitor timer0 to provide the timing for the pulses, and at the same time check for overflows?
Any further help would be gratefully received |
|
|
Guest
|
|
Posted: Mon Mar 19, 2007 11:19 am |
|
|
Ttelmahīs ideia is that sometimes the interrupt will happen when the main code is inside a delay, so instead of delay_us(20) generating 20us of delay it will generate 20us + execution time of the interrupt. The correction for this is to use in place of delay)us a function that:
1) gets the current value of the timer
2) adds the value of the time that we wants to delay
3) waits for the timer to achieve that value
this way it will not matter if the interrupt gets executed inside the delay (well... thinking again this is not completely true... what if it wappens at the end of the count? decisions.... hard decisions.... |
|
|
Ttelmah Guest
|
|
Posted: Mon Mar 19, 2007 1:32 pm |
|
|
Spot on.
However you can go one stage further, which helps.
If you have (say) a sequence of 'events', which need to happen at (say), 1mSec, 4mSec, 8mSec etc., from a 'start' time, then if you calculate all the required counts from the starting point, though one will be 'late' if the interrupt occurs, the effect will not be cumulative, and the latter times will still be OK. It needs some thought and care, but it ought to help.
The other thing to consider is the possibility of doing the 16bit count in hardware, either using timer1, or using timer2, to feed timer0.
The big problem is with the short times in 'send_pulse'.
Best Wishes |
|
|
WideBoy Guest
|
|
Posted: Tue Mar 20, 2007 4:46 am |
|
|
Thanks for all the input.
I'll try to implement your suggestions and see how that goes....
I've already looked at using timer1, but the scalers on that means it will overflow all 16 bits too quickly. I haven't thought about using timer2 to feed timer0 - so I'll also look into that.
Cheers |
|
|
|
|
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
|