|
|
View previous topic :: View next topic |
Author |
Message |
jecottrell
Joined: 16 Jan 2005 Posts: 559 Location: Tucson, AZ
|
Interrupts, Interaction Between CCP1 and TIMER0 |
Posted: Mon Jan 03, 2011 10:56 am |
|
|
3.249
18LF6627
3.3V
Normal Run Mode
10Mhz (w/ HS 40Mhz)
Hardware: Custom board specifically for this project.
I'm trying to measure the period between rising edges of a clean square wave of 0 - 70hz. I've read many of the posts about measuring long periods and the need to 'extend' timers. I was hoping to do something simpler.
My approach is to load a countdown ticker in the CCP_INT on each rising edge and decrement the ticker in the TIMER0_INT.
The general flow is:
CCP_INT - Read the current ticks and reload.
TIMER0_INT - Decrement the ticks if they're not 0, if they are 0 update the global variable. (This allows for incomplete periods or 0hz.)
The problem that I'm seeing is...
The 'COUNTS' display on the LCD when I trigger the rising edge is always the TMR0_TICK_LOAD value on the first display after the trigger. I have a hard time believing that no time ever passes from the rising edge to the LCD display of the 'COUNTS'.
Any ideas or suggestions of where to look?
Code: | #case
#include <18F6627.h>
#define XTAL_FREQ 40000000
#device adc=10
#fuses NOWDT,WDT128, H4, NOPROTECT, NOIESO, BROWNOUT, BORV25, PUT, STVREN
#fuses NOLVP, NOWRT, NOWRTD, NOWRTB, NOWRTC, NOCPD, NOCPB, NOEBTR, NOEBTRB //DEBUG,
#use delay(clock=XTAL_FREQ ,RESTART_WDT)
#define LED_GREEN PIN_D0
#define LED_YELLOW PIN_D1
#define LCD_POWER PIN_F0
#define LCD_DATA PIN_F1
#define PERI_PWR_SW PIN_F2
#define false 0
#define true 1
#include "SerLCD_V2_5.C"
// define constants to derive the 1ms system clock.
#define C_TMR0_1ms 256 - (XTAL_FREQ/(64*4*1000))
#define CTMR_Activity 2 // number of 100mS increments for LED flash
//////////////////////////////////////////////////////////////////////////
// timer control flags and counters
//////////////////////////////////////////////////////////////////////////
BYTE TMR_Short; // Short Timer Counter - used for general purpose timing functions with TMR0 interrupts
long TMR_Long; // Long Timer Counter - used for general purpose timing functions with TMR0 interrupts
BYTE TMR_100ms; // 100ms timer counter
BYTE TMR_Activity; // LED flash timer for activity - number of 100ms increments
short TF_Short; // short timer done flag
short TF_Long; // long timer done flag
// period length of utility timers (in mS)
#define TMR_LONG_LOAD 1000
#define TMR_SHORT_LOAD 32
#define CStatusLEDCount 5 // number of 100ms timer rollovers to toggle the Status LED
BYTE StatusLEDCount; // used for toggling the Status LED
#byte TMR0L = 0x0FD6
#define TMR0_TICK_LOAD 1200
int16 gTimer0_Ticks = TMR0_TICK_LOAD;
int16 gTotal_Counts = 0;
#INT_TIMER0
void timer0_isr()
{
TMR0L += C_TMR0_1ms;
clear_interrupt(INT_TIMER0);
// If gTimer0_Ticks is not 0, then decrement
if(gTimer0_Ticks)
{
--gTimer0_Ticks;
}
// If gTimer0_Ticks has reached zero, pass value to total
// (This ensures gTotal_Counts is updated if another
// rising isn't encountered after gTimer0_Ticks
// reaches 0.)
else
{
gTotal_Counts = 0;
}
// service the short timer
if(!--TMR_Short)
TF_Short = TRUE;
// service the long timer
if(!--TMR_Long)
TF_Long = TRUE;
// service the 100ms timer
if(!--TMR_100ms)
{
TMR_100ms = 100;
if (TMR_Activity)
if(!--TMR_Activity)
output_low(LED_YELLOW);
if (!--StatusLEDCount)
{
StatusLEDCount = CStatusLEDCount;
output_toggle(LED_GREEN);
}
}
}
//////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////
int1 gFirst_Rising_Edge_flg = true;
#INT_CCP1
void ccp1_isr(void)
{
gTotal_Counts = gTimer0_Ticks;
gTimer0_Ticks = TMR0_TICK_LOAD;
/*
if(gFirst_Rising_Edge_flg)
{
gFirst_Rising_Edge_flg = false;
gTimer0_Ticks = TMR0_TICK_LOAD;
}
else
{
gFirst_Rising_Edge_flg = true;
gTotal_Counts = gTimer0_Ticks;
}
*/
}
//////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////
int16 gPeriod = 0;
void Calculate_Period(void)
{
if(gTotal_Counts)
{
gPeriod = 2 * gTotal_Counts; //670
}
}
//////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////
void main()
{
int8 i = 0;
// Ensure unused modules are configured off
setup_adc_ports(NO_ANALOGS);
setup_adc(ADC_OFF);
setup_psp(PSP_DISABLED);
setup_spi(FALSE);
setup_wdt(WDT_OFF);
port_b_pullups(false);
// Enable CCP1 to interrupt on rising edges
setup_ccp1(CCP_CAPTURE_RE);
clear_interrupt(INT_CCP1);
enable_interrupts(INT_CCP1);
// setup the system timer - Timer0
setup_timer_0 (RTCC_INTERNAL | RTCC_DIV_64 | RTCC_8_BIT);
set_timer0 (C_TMR0_1ms);
clear_interrupt(INT_TIMER0);
enable_interrupts(INT_TIMER0);
// Don't enable the LCD_DATA pin until after power has been applied
output_low(LCD_DATA);
// Enable Interrupts
enable_interrupts(GLOBAL);
delay_ms(10);
// setup Status LED Toggle counter
StatusLEDCount = CStatusLEDCount;
output_low(LED_GREEN);
TMR_Activity = CTMR_Activity;
// Flash LED to indicate startup
for (i =0; i<6; i++)
{
output_toggle(LED_GREEN);
delay_ms(200);
}
output_low(LED_GREEN);
// Powerup LCD module
output_high(LCD_POWER);
delay_ms(100);
output_low(LCD_DATA);
delay_ms(500);
Set_LCD_Backlight(135);
delay_ms(500);
Send_CMD(CLR_DISP);
delay_ms(10);
Send_CMD(SET_CURSOR + 0);
delay_ms(10);
fprintf(LCD,"%s",__DATE__);
delay_ms(10);
Send_CMD(SET_CURSOR + 64);
delay_ms(10);
fprintf(LCD,"%s",__TIME__);
delay_ms(2000);
// initialize the countdown timer values
TMR_Long = TMR_LONG_LOAD;
TMR_Short = TMR_SHORT_LOAD;
// Turn on power to peripherals
output_high(PERI_PWR_SW);
/////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////
//ExecLoop
for (;;)
{
if (TF_Long)
{
///////////////////////////////////////////////////////////////////////
//events
//---------------------------------------------------------------------
Calculate_Period();
Send_CMD(CLR_DISP);
delay_ms(10);
Send_CMD(SET_CURSOR + 0);
delay_ms(10);
//fprintf(LCD,"PERIOD: %LU",gPeriod);
fprintf(LCD,"COUNTS: %LU",gTotal_Counts);
Send_CMD(SET_CURSOR + 64);
delay_ms(10);
fprintf(LCD," TICKS: %LU",gTimer0_Ticks);
//---------------------------------------------------------------------
///////////////////////////////////////////////////////////////////////
TF_Long = FALSE;
TMR_Long += TMR_LONG_LOAD;
}
//////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////
if (TF_Short)
{
///////////////////////////////////////////////////////////////////////
//events
//---------------------------------------------------------------------
output_low(LED_YELLOW);
//---------------------------------------------------------------------
///////////////////////////////////////////////////////////////////////
TF_Short = FALSE;
TMR_Short += TMR_SHORT_LOAD;
}
//////////////////////////////////////////////////////////////////
}
} |
|
|
|
jecottrell
Joined: 16 Jan 2005 Posts: 559 Location: Tucson, AZ
|
|
Posted: Fri Jan 07, 2011 9:32 pm |
|
|
Well, I haven't made any progress on this and have started to try to make the transition to PCM programmer's code that extends TIMER1 to 24 bits.
By extending the TIMER1 from 16 to 24 bits, is this correct:
10MHz crystal w/ PLL
40MHZ Clock
10MHz per timer tick (or 0.0000001 seconds)
Increase prescaler: T1_DIV_BY_8 (now 0.0000008 seconds)
Multiply by 24 bits to get maximum period of time to measure-
~13.42 seconds?
Thanks,
John |
|
|
PCM programmer
Joined: 06 Sep 2003 Posts: 21708
|
|
Posted: Fri Jan 07, 2011 9:38 pm |
|
|
The math is correct. |
|
|
customvinyl22
Joined: 07 Jan 2011 Posts: 1
|
|
|
jecottrell
Joined: 16 Jan 2005 Posts: 559 Location: Tucson, AZ
|
|
Posted: Fri Jan 07, 2011 10:39 pm |
|
|
PCM programmer wrote: | The math is correct. |
Thanks for the confirmation. I will test it on hardware in the morning.
John |
|
|
jecottrell
Joined: 16 Jan 2005 Posts: 559 Location: Tucson, AZ
|
|
Posted: Sat Jan 08, 2011 9:02 am |
|
|
Well, I should have looked at the edges of the signal a week ago. I knew when PCM programmer's code wasn't working as expected, it had to be something else.
I guess early on, when I saw the rising edge was nice and clean I just assumed the falling edge was as well.
So, do I debounce in hardware or software? Any easy way to do it in software since it's an interrupt?
EDIT:
First thoughts are to hold in the CCP1 ISR for 100uS.
But, that raises these questions:
When in the ISR is the INT bit cleared? (Looking at the LST file it looks like at the end of the ISR.)
How many counts in a loop do I need for a 100uS delay at 40MHz?
EDIT2:
Or, duh, I guess I can just use a simple:
Code: |
delay_us(100);
clear_interrupt(CCP1); |
EDIT3:
On further thought, to debounce the falling edge in the ISR, I'll need to change the CCP from RE to FE, no? |
|
|
PCM programmer
Joined: 06 Sep 2003 Posts: 21708
|
|
Posted: Sun Jan 09, 2011 5:57 pm |
|
|
I don't know how many of those pulses are considered to be valid.
Is it just the big pulses ? Or all of them ?
Maybe you need to use an external low-pass filter circuit and then
pass it through a schmitt-trigger circuit to square it up.
Also, the Vih is about 4.0v as shown on your scope image. That's right at
the borderline for the PIC's specification for the CCP pin (for a +5v Vdd).
Ideally it should be a little higher, to provide some margin. |
|
|
jecottrell
Joined: 16 Jan 2005 Posts: 559 Location: Tucson, AZ
|
|
Posted: Mon Jan 10, 2011 1:41 pm |
|
|
The first FE at about 16uS from the left is the end of the valid signal. All of the spikes after that are bounce.
Luckily, the required resolution allows for debouncing in software. But, I think if I revise the hardware I'll do that.
The part is LF and running at 3.3V. |
|
|
|
|
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
|