 |
 |
View previous topic :: View next topic |
Author |
Message |
samiceng
Joined: 23 Oct 2018 Posts: 9
|
Solved. Reading CCP1 value on PIC18F46Q84 |
Posted: Sun May 18, 2025 2:14 pm |
|
|
Hi everybody
CCS ver 5.112 & PIC18F46Q84
I am trying to measure frequency up to 100Hz (in 1Hz resolution).
I did that in the past (2021) on a PIC16F914.
But when I try the same way on PIC18F46Q84. It didn’t work.
In my code, I got the reading of ‘wave_width_MSD’ right. But the value of ‘wave_width_LSD’ is always zero.
I am feeding a 31Hz signal, so by math the ‘wave_width’ will be:
64000000/(4*Timer3_Pre*Freq) = 64000000/(4*1*31) = 516129 DEC
Which is ‘0111 110 0000 0010 0001’ Bin. The most right 4 bits (0111) is what will be in ‘wave_width_MSD’, and I got that.
But the remaining 16 bit (should be in ‘wave_width_LSD’ coming from CCP1) always reads zero.
The line that load ‘CCP_hi_byte’ into ‘wave_width_MSD’ is inside the CCP1 interrupt.
And it is working, which mean the CCP1 is working.
But the line that read CCP1 is not. Why?
wave_width_LSD = get_capture(1); is not working.
Code: |
#include <18F46Q84.h>
#device ADC=12
#FUSES NOWDT //No Watch Dog Timer
#use delay(internal=64MHz)
#pin_select CCP1=PIN_C0
#use I2C(master, sda=PIN_C4, scl=PIN_C5, FAST=400000, stream = SSD1306_STREAM)
// PIN42 PIN43
#define SSD1306_I2C_ADDRESS 0x78
#include <SSD1306OLED.c> //
char buffer[6]; //
int8 CCP_hi_byte=0; // Timer3 8 bit extension
int16 wave_width_LSD=0; // To hold the 16 bit LSD Timer3 value on Captuer
int16 wave_width_MSD=0; // To hold the 8 bit MSD Timer3 value on Captuer
int32 wave_width=0; // To hold the compined LSD & MSD
int32 freq=0; // To hold the freq
short freq_in=0; // To indicate that there is a Capture need to be processed
#INT_CCP1
void CCP1_isr(void)
{
wave_width_LSD = get_capture(1); // Get the 16 bit LSD count value on captuer.
wave_width_MSD = CCP_hi_byte; // Get the 8 bit MSD count value on captuer.
set_timer3 (0); // Clear the count value (LSD & MSD) to
CCP_hi_byte = 0; // prepare for the new captuer event.
freq_in = 1;
}
#INT_TIMER3
void TIMER3_isr(void)
{
CCP_hi_byte++; // Increment the extra 8 bit of Timer3.
}
void main()
{
SSD1306_Begin(SSD1306_SWITCHCAPVCC, SSD1306_I2C_ADDRESS); //
SSD1306_ClearDisplay();
SSD1306_Display();
delay_ms(100);
setup_timer_3(T3_INTERNAL | T3_DIV_BY_1);
setup_ccp1(CCP_CAPTURE_RE|CCP_USE_TIMER3_AND_TIMER4);
clear_interrupt (INT_CCP1); // Clear CCP1 interrupt flag
clear_interrupt (INT_TIMER3); // Clear Timer3 interrupt flag
enable_interrupts(INT_CCP1);
enable_interrupts(INT_TIMER3);
set_timer3 (0); // Set Timer3 to 0
enable_interrupts(GLOBAL);
while(TRUE)
{
if ( freq_in == 1)
{
freq_in = 0;
wave_width = make32(wave_width_MSD,wave_width_LSD);
freq = 16000000/wave_width;
// 64000000/(4*Timer3_Pre*Freq) = wave_width
// so Freq = 64000000/(4*1*wave_width)
// or Freq = 16000000/wave_width in Hz.
}
else
{
freq = 0;
wave_width_LSD = 0;
wave_width_MSD = 0;
wave_width = 0;
}
sprintf(buffer, "%05Lu", freq);
SSD1306_DrawText(0,8*0,buffer,3);
sprintf(buffer, "%05Lu", wave_width_LSD);
SSD1306_DrawText(0,8*3,buffer,3);
sprintf(buffer, "%05Lu", wave_width_MSD);
SSD1306_DrawText(0,8*6,buffer,3);
SSD1306_Display();
}
}
|
Last edited by samiceng on Mon May 19, 2025 5:39 am; edited 1 time in total |
|
 |
Ttelmah
Joined: 11 Mar 2010 Posts: 19840
|
|
Posted: Mon May 19, 2025 2:32 am |
|
|
OK.
I would just use CCP_1 to read the CCP value. However a quick build shows
that the get_capture is accessing the correct register. Just tested with your
compiler version. Register 340/341, which is the CCPR1 values.
So, that you are getting the interrupt, says it is capturing, and that this has
the correct overflow value from the timer, says the timer is running
correctly. You are not selecting to use the CCP pin, but this is the default
(CCP_CAPTURE_INPUT_CCP_PIN).
So all looks good. The timer is not getting stored into the CCP register.
One thing glares in the data sheet:
Quote: |
Timer1 Mode for Capture
Timer1 must be running in Timer mode or Synchronized Counter mode for the CCP module to use the capture
feature. In Asynchronous Counter mode, the capture operation may not work.
See the “TMR1 - Timer1 Module with Gate Control” chapter for more information on configuring Timer1.
|
Now, things that apply to Timer1, will apply to any other timer used
instead.
This is again in the dats sheet:
Quote: |
Important: References to the module Timer1 apply to all the odd numbered timers on this device.
|
So it is saying that the timer must be used in synchronous mode to be
used with the CCP.
So:
T3_SYNC
You have the timer running in asynchronous mode. Possibly this is the
problem.
I have to say 'well done'. Good example. Compiler version given, you have
correctly setup PPS, and selected the timer for the CCP.  |
|
 |
samiceng
Joined: 23 Oct 2018 Posts: 9
|
|
Posted: Mon May 19, 2025 5:37 am |
|
|
Again thank you Ttelmah. Adding T3_SYNC to the timer3 setup line solved it.
I see what you mean by using CCP_1, that worked too.
But i am confused! you said that I did not select to use the CCP pin.
I have this line in may code: #pin_select CCP1=PIN_C0;
Then at the end, you said that I have correctly setup PPS.
Isn't that line doing it?
One more thing, after I posted this, I was playing around it and find a work around. I make it work by reading Timer3 value using wave_width_LSD = get_timer3();
I know it is not the right way to do it. but in may case here, both way gives exactly the same result. |
|
 |
Ttelmah
Joined: 11 Mar 2010 Posts: 19840
|
|
Posted: Mon May 19, 2025 7:10 am |
|
|
Brilliant.
You see that I say to use the pin is the default.
The point is that on the later chip the CCP can be clocked from a lot of
sources. So:
setup_ccp1(CCP_CAPTURE_RE | CCP_USE_TIMER3_AND_TIMER4 | CCP_CAPTURE_INPUT_CCP_PIN);
is technically the setup to use the pin defined as the CCP input. (With PPS
set as you show).
The point is though that this selection is '0', so makes no difference.
However it is worth perhaps specifying it, so that someone looking in
the future can say 'Ah, CCP input pin used'.
On the older chip the CCP could only be fed from the pin. On this one, it
can be fed from things like the CLC signals, and the IOC interrupt instead.
On the work around. They won't give _quite_ the same result. The CCP
captures the timer count at the moment the event happens. Your read
of the timer inside the interrupt will have the time it takes to get into the
interrupt included in the count. So perhaps 30 extra counts. |
|
 |
samiceng
Joined: 23 Oct 2018 Posts: 9
|
|
Posted: Mon May 19, 2025 2:49 pm |
|
|
If you notice. I am using the internal clock. which is not so accurate.
Also the feed signal is generated using a PIC16F819 and that too was using its internal clock. (just for testing).
In both ways, read CCP or timer. I was getting flickering reading.
That's why I said both are same. |
|
 |
Ttelmah
Joined: 11 Mar 2010 Posts: 19840
|
|
Posted: Tue May 20, 2025 12:29 am |
|
|
The 'flickering' is almost certainly because of how the code is done, not the
clocks. Though the internal clocks are not 'accurate', over the short term,
they are quite stable. A maximum of just a count or two should be all you
see.
However there are issues with the code approach:
Forst thing, this will be generating a reading every time a new edge comes
in. So 31* a second. Now the OLED takes time to wirite and display. Probably
better to disable the interrupt when a reading is taken, then when you are
ready to take a new reading, clear the timer, clear the interrupt, and
enable it again.
This way you get a single reading, and have time to display it without
it changing. I suspect you may find that in fact sometimes the interrupt
is being called during the display, so you get an erroneous reading and
more flicker than is really needed. You will always get a count of jitter, since
the clock that is sampling is not synchronous to the external signal, but
any more than one or two counts is a sign that there is an issue. The reading
will vary with temperature, and over time, but short term should be pretty
much perfect. If you stuck the clock from the 819, into a DFM, it might
we'll be 31.27Hz, bit expect the reading from the DFM, to still be saying
31.27, in a couple of minutes, It won't jump around much at all. The clock
on the Q84 will have similar behaviour. |
|
 |
samiceng
Joined: 23 Oct 2018 Posts: 9
|
|
Posted: Tue May 20, 2025 2:42 pm |
|
|
I thought to do that, but what will grantee that enabling the interrupt will be at the start of the input signal. have a look at this funky timing diagram:
____|````|____|````|____|````|____ input signal
------|------T-----| -> correct count value 'T'
----------|
----------| here was enabling the interrupt
----------|
----------|---t----| -> this will be the count value 't'
in that case I have to sync enabling the interrupt with the input signal too.
it is a slow signal and there will be other stuff the PIC will do. I only used the OLED as test way to see the result. |
|
 |
|
|
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
|