|
|
View previous topic :: View next topic |
Author |
Message |
apakSeO
Joined: 07 Dec 2016 Posts: 60 Location: Northeast USA
|
CCP Module, Max edge capture rate, and interrupt latency |
Posted: Fri May 31, 2019 11:26 am |
|
|
PIC16F1779
CCS v5.081
Goal: I'm trying to capture both rising and falling edges of a 56kHz signal in one of this PICs CCP modules; specifically, CCP8. This input signal has a frequency of 56kHz maximum; thus the minimum time between rising/falling edges is 1/2*56kHz = 8.93us
I wish to capture every rising/falling edge into a 16-bit circular buffer, for later processing in the main loop.
I find that running at 16MHz clock, I am struggling with (a) the interrupt latency and (b) the amount of time to service the ISR. It is pretty disheartening to find out that a measly 56kHz square wave cannot be measured by this uC running at 16MHz. Can anything be done in my code to speed either of these operations?
Here is example code:
Code: |
#include <16F1779.h>
#device PIC16F1779
#device ICD=TRUE
#device ADC=10
#use delay(INTERNAL=16MHZ, CLOCK=16MHZ)
#fuses INTRC_IO
#fuses NOWDT
#fuses NOPROTECT
#fuses DEBUG
#fuses CLKOUT
#fuses NOBROWNOUT
#fuses NOPUT
#include <stdint.h>
volatile uint16_t edges[16] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
volatile uint8_t edges_idx = 0;
void main(void)
{
// Set B0 as an input
set_tris_b(0b00000001);
port_b_pullups(0b00000000);
set_tris_c(0b00000000);
port_c_pullups(0b00000000);
// Configure CCP8 pin to CCP function
#pin_select CCP8 = PIN_B0
setup_ccp8(CCP_CAPTURE_EE|CCP_CAPTRUE_INPUT_CCP_PIN);
// Setup timer1 ( 16-bit ) as the CCP time base.
setup_timer_1(T1_INTERNAL|T1_DIV_BY_1); // "T1_INTERNAL" --> T1CON:CS is "00" --> T1 Clock is Instruction Clock, Fosc/4
// "T1_FOSC" --> T1CON:CS is "01" --> T1 Clock is System Clock, Fosc
set_timer1(0);
// Enable the CCP8 interrupt
enable_interrupts(INT_CCP8);
// Enable interrupts globally
enable_interrupts(GLOBAL);
while(1)
{
}//while(1)
}//void main())
#int_ccp8
void ccp8_capture_isr(void)
{
output_high(PIN_C7);
edges[edges_idx] = get_capture(8);
if( edges_idx < 15 ) edges_idx++;
else edges_idx = 0;
output_low(PIN_C7);
}
|
Now, a few screen shots to illlustrate what is happening:
( In the above scope capture, ignore the momentary spike; my signal generator is acting wonky today. )
YELLOW is input signal, a 56kHz, 50% duty cycle square wave.
BLUE is the PIN_C7 toggling high/low inside the ISR, for a rough timing estimate.
So you see the first spike, the ISR triggers ( let's ignore this spurious noise for sake of this convo )
Then you see the 1st real rising edge, then the ISR enters a whopping 10.2us later.
We spend about 7.2us inside the ISR.
The combination of ISR entrance latency + the time to service the ISR overflows into the next edge time, be it rising or falling.
Is there anything obvious I am doing in my code above that is increasing the time to enter the ISR, or to service it? Could there be another way of approaching this task in order to capture all the rising/falling edges?
Thanks for any help you may provide ahead of time |
|
|
Ttelmah
Joined: 11 Mar 2010 Posts: 19535
|
|
Posted: Fri May 31, 2019 11:39 am |
|
|
Look at using #INT_GLOBAL instead of the standard handling.
Example here (on a timer):
<http://www.ccsinfo.com/forum/viewtopic.php?t=49614&highlight=int+global>
Key is that your code will have to save every register that is used in
your handler code.
The standard interrupt handler save all registers used, and then checks
which interrupt has triggered. With a single interrupt this check is not
needed, and your code only needs to save registers it does use.
Result you can probably halve the latency. |
|
|
PCM programmer
Joined: 06 Sep 2003 Posts: 21708
|
|
Posted: Fri May 31, 2019 1:21 pm |
|
|
If your code does nothing else, or if you are willing to dedicate the
processor to only capturing for a short time, you could just
poll the CCP interrupt flag. Don't use an isr. |
|
|
Ttelmah
Joined: 11 Mar 2010 Posts: 19535
|
|
Posted: Fri May 31, 2019 1:31 pm |
|
|
Very true.
It is important to remember that you can always just 'poll' an interrupt
(if (interrupt_active(INT_XXX))) and this is far faster than calling any
form of interupt handler.
With both #INT_GLOBAL or polling, remeber you have to clear the
interrupt. |
|
|
apakSeO
Joined: 07 Dec 2016 Posts: 60 Location: Northeast USA
|
|
Posted: Wed Jun 05, 2019 11:26 am |
|
|
Thanks Guys,
Yes you're right; luckily in this application, I can poll endlessly in a tight loop with no impact to the main program while waiting for the first edge to come in.
Thanks for the suggestion. I can see this tool of ISR flag polling being useful in many circumstances in the future .... |
|
|
|
|
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
|