I am also using a GPS which has Pulse Per Second output on PortC.3 to get me an accurate 1PPS.
I want to count how many pulses on PortC.4 that occur between pulses on PortC.3 (accurate 1 second GPS pulses) to hopefully get an accurate frequency in HZ.
Hardware wise I'm not sure if I should have pull-down resistor's on both the GPS and frequency line.
I have an interrupt routine setup to interrupt when Timer1 overflows then I'm adding 65536 to the counter.
I think I am close but not getting the correct answer. I'm getting like 31250 counts. What I'm not sure of is the proper setup of timer1 and If I'm counting overflows correctly.
Can anyone assist? I know this chip is familiar to Evan as he has featured a similar MCU in his YouTube instruction videos, and I'm showing only the critical parts of the code:
#CHIPPIC18F16Q40'Compiler indicates its using the 64Mhz Internal oscillator
#optionexplicit'Generated by PIC PPS Tool for Great Cow Basic'PPS Tool version: 0.0.6.2'PinManager data: Not available (3)'Generated for 18f16q40''Template comment at the start of the config file'
#startupInitPPS, 85
#definePPSToolPart18f16q40SubInitPPS'Module: TMR1T1CKIPPS=0x0014'RC4 > T1CKI'Module: TMR2 pin directionDIRPortC.3In'GPS Pulse InputDIRPortC.4In'Si5351a Clock inputEndSubDimTimerValueasLongDimFrequencyCountasLong'Initialize timer 1 (Should I use a prescaler?? currently 1:1 InitTimer1ExtOsc,0OnInterruptTimer1OverflowCallIncCounterMainLoop:
DoForeverClearTimer1'Wait for GPS PulseWaitUntilPORTC.3=OnStartTimer1FrequencyCount=0TimerValue=0WaitUntilPORTC.3=Off'Wait until GPS PULSE comes back after 1 secondWaitUntilPORTC.3=OnStopTimer1'Read Timer1TimerValue=FrequencyCount+Timer1comport=2HSerPrintstr32(TimerValue),2HSerSend13,2HSerSend10,2comport=1LoopSubIncCounterFrequencyCount=FrequencyCount+65536Endsub
thanks ! any help is appreciated.
Jeff
Last edit: Jeff Weinmann 2022-08-12
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
Timer1 is a 16-bit timer, meaning it will overflow after 65535 pulses. You are wanting to count ~7,000,000 pulses over a time of ~1 second, so you are counting overflows and adding remainders and such. This is not an ideal method given the capability of the chip.
I would suggest you open the datasheet to Chapter 27 (Page 398) and familiarize yourself with the "Signal Measurement Timer". This is a 24-bit Timer that can count up to 16,777,215 pulses and has several modes of operation. I suggest you use the "Gated Counter Mode" Where the 1Hz pulse gates the timer on/off. The timer automatically starts when the "gate" goes high and a result is returned when the gate goes low. An interrupt can be set to trigger when the count is done
If after studying the SMT section of the datasheet and trying it out as best you can, and if you are still lost, let us know and more information can be provided.
William
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
I've read up on chapter 27 and I've gotten a better (not complete) understanding of how to use the SMT.
Here is the state of the code below. I've added the SMT signal and window input pins via the PPS tool, and tried to document what I think I know.
I'm pretty sure I set the mode to be 'Gated Counter'.
The code runs but I get a bunch of 0's then some numbers. but I do see the numbers change when a GPS pulse does occur. I'm not sure how to set the interrupt so I have a loop until for now.
I think the bones are there, I just need a little push.
Jeff
#startup InitPPS, 85#define PPSToolPart 18f16q40SubInitPPS'Module: I2C1RB6PPS=0x0021'SCL1 > RB6I2C1SCLPPS=0x000E'RB6 > SCL1 (bi-directional)RB4PPS=0x0022'SDA1 > RB4I2C1SDAPPS=0x000C'RB4 > SDA1 (bi-directional)'Module: SMT1SMT1SIGPPS=0x0014'RC4 > SMT1SIGSMT1WINPPS=0x0013'RC3 > SMT1WINDIRPortC.4In'Si5351a Clock inputDirPortC.3In'PPS from GPS'Module: UART1RC6PPS=0x0010'TX1 > RC6U1RXPPS=0x0017'RC7 > RX1'Module: UART2RC0PPS=0x0013'TX2 > RC0U2RXPPS=0x0011'RC1 > RX2'Module: UART pin directionsDirPORTC.6Out' Make TX1 pin an outputDirPORTC.7In' Make RX1 pin an inputDirPORTC.0Out' Make TX2 pin an outputDirPORTC.1In' Make RX2 pin an inputEndSub'Signal Measurement Timer Setup'SMT1CON0 - Control Register'BIT NAME VALUE'7 EN -SMT Enable 1 = SMT is enabled, 0 = SMT is disabled'6 NA'5 STP -SMT Counter Halt Enable 1 = When SMT1TMR = SMT1PR Counter remains at SMT1PR period match interrupt occurs when clocked, 0 = When SMT1TMR = SMT1PR Counter resets to 0x000000'4 WPOL -SMT window input Polarity 1 = window input is active low/falling edge enabled, 0 = windo input is active high/rising edge enabled'3 SPOL -SMT Signal Input Polarity 1 = SMT_signal input is active-low/falling edge enabled, 0 = rising edge enabled'2 CPOL -SMT Clock Input Polarity 1 = SMT1TMR increments on falling edge of selected clock signal, 0 = increments on rising edge of clock signal'1 PS - SMT Prescale Select 1 1:8 1 1:4 0 1:2 0 1:1'0 PS - SMT Prescale Select 1 0 1 0'Enable SMTSMT1CON0.7=1'SMT1CON1 - 2nd Control Register'BIT NAME VALUE'7 GO -SMT GO Data Aquisition 1 = Incrementing/Enabled, 0 = Incrementing,Disabled'6 REPEAT-SMT Repeat Acquisiton 1 = Repeat Data Aquisition Enabled, 0 = Aquisition Disabled'5 NA'4 NA'3 MODE -SMT Operation Mode Select 1 Windowed Counter 1 Gated Counter 1 Counter 0 Time of Flight 0 Gated Window Measurement 0 Windowed Measurement ....etc'2 MODE -SMT Operation Mode Select 0 0 0 1 1 1'1 MODE -SMT Operation Mode Select 1 0 0 1 0 0'0 MODE -SMT Operation Mode Select 0 1 0 0 1 0'Select Gated Counter ModeSMT1CON1.3=1SMT1CON1.2=0SMT1CON1.1=0SMT1CON1.0=1'Stop Counting for nowSMT1CON1.7=0'I'mnotusingtheclock?takingdefaultoFOSC/4SMT1CLK=0'SMT Window Signal Selection - I'mprettysureI'm taking the default of 0=SMT1WINPPS or the GPS 1PPS Signal on PORTC.3SMT1WIN=0'SMT Signal Selection - I'mprettysureI'm taking the default of 0=SMG1SIG or the 7Mhz signal on PORTC.4SMT1SIG=0'SMT Timer RegisterSMT1TMR=0MainLoop:'The SMTxTMR register is the 24-bit counter/timer used for measurement in each of the modes of the SMT. Setting'the RST bit clears the SMTxTMR register to 0x000000. It can be written to and read by software. It is not guarded for'atomic access, therefore reads and writes to the SMTxTMR register must be made only when GO = 0.SMT1TMR=0'The counter can be prevented from resetting at the end of the timer period by using the STP bit. When STP = 1,'the SMTxTMR will stop and remain equal to the SMTxPR register. When STP = 0, the SMTxTMR register resets to'0x000000 at the end of the period.STP1=1DoForever'Setting the RST bit clears the SMT1TMR Register to 0SMT1TMR=0'The counter can be prevented from resetting at the end of the timer period by using the STP bit. When STP = 1,'the SMTxTMR will stop and remain equal to the SMTxPR register.STP=1SMT1GO=1'Go Status: Timer run status is indicated by the TS bit. The TS bit is delayed in time by synchronizer delays in'non-counter modes.DoUntilTS=0'I'mnotsurehowtosettheInterruptforSMT.Ifyoucanhelpinthisareamuchappreciated.'Print out value of SMT1Timercomport=2HSerPrintstr32(SMT1TMR),2HSerSend13,2HSerSend10,2comport=1LoopLoop
Last edit: Jeff Weinmann 2022-08-13
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
You may also find Bresenham's Algorithm useful.
Here is a goods explanation and example code, but not in GCBASIC so you would need to adapt the algorithm.
I decided to refer to and older project where I did something very similar using Timer1. I'm satisfied enough with the results and I'm only 2 to 3 Hertz off the measured frequencies with my scope and commercial frequency counter.
Method:
1) Set the frequency to be measured to 2Mhz
2) Count the frequency using the below code
3) Take the difference between measured and actual
4) Apply a scale factor by taking desired frequency / 2Mhz
Example: 14Mhz / 2 Mhz is a scale factor of 7 so if my difference in frequency at 2Mhz is 27, My difference I will apply at 14Mhz is : 189. I do have to pay attention whether the difference is positive or negative
SubInitPPS'Module: I2C1RB6PPS=0x0021'SCL1 > RB6I2C1SCLPPS=0x000E'RB6 > SCL1 (bi-directional)RB4PPS=0x0022'SDA1 > RB4I2C1SDAPPS=0x000C'RB4 > SDA1 (bi-directional)'Module: SMT1'SMT1SIGPPS = 0x0014 'RC4>SMT1SIG'SMT1WINPPS = 0x0013 'RC3>SMT1WIN'Module: TMR1T1CKIPPS=0x0014'RC4 > T1CKI'Module: UART1RC6PPS=0x0010'TX1 > RC6U1RXPPS=0x0017'RC7 > RX1'Module: UART2RC0PPS=0x0013'TX2 > RC0U2RXPPS=0x0011'RC1 > RX2'Module: UART pin directionsDirPORTC.6Out' Make TX1 pin an outputDirPORTC.7In' Make RX1 pin an inputDirPORTC.0Out' Make TX2 pin an outputDirPORTC.1In' Make RX2 pin an inputDIRPortC.4In'Si5351a Clock inputDirPortC.3In'PPS from GPSEndSub
;Setup Timer1 for frequency counter
InitTimer1 Ext, PS1_1
On Interrupt Timer1Overflow Call IncCounter
Offset = 0
Do Forever
ClearTimer1FrequencyCount=0WaitUntilPORTC.3=1'GPS Pulse onStartTimer1WaitUntilPORTC.3=0'GPS Pulse offWaitUntilPORTC.3=1'GPS Pulse onStopTimer1FrequencyCount=FrequencyCount+Timer1'The difference in frequency when compared to a frequency counter is spot on.'Divide the Target frequency by 2 Mhz and the frequency deviation is close enough!'Avoid Negative NumberifFrequencyCount>2000000thenTempLong=FrequencyCount-2000000Offset=CW_Frequency/2000000*TempLongOffset=Offset*-1elseifFrequencyCount=2000000thenOffset=0elseTempLong=2000000-FrequencyCountOffset=CW_Frequency/2000000*TempLongendifTempString=str32(FrequencyCount)comport=2HSerPrint"Frequency Count = ", 2HSerPrintTempString, 2HSerPrint" Deviation = "+StrInteger(Offset), 2HSerSend13,2HSerSend10,2comport=1
EndLoop:
Loop
Sub IncCounter
FrequencyCount = FrequencyCount + 65536
End sub
~~~
Last edit: Jeff Weinmann 2022-08-14
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
This compiles, but you will need a GPS with PPS out and signal source of 2Mhz:
#CHIP PIC18F16Q40#CONFIG LVP=ON#option explicit#Include <SMT_Timers.h>#startup InitPPS, 85#define PPSToolPart 18f16q40SubInitPPS'Module: I2C1RB6PPS=0x0021'SCL1 > RB6I2C1SCLPPS=0x000E'RB6 > SCL1 (bi-directional)RB4PPS=0x0022'SDA1 > RB4I2C1SDAPPS=0x000C'RB4 > SDA1 (bi-directional)'Module: TMR1T1CKIPPS=0x0014'RC4 > T1CKI'Module: UART1RC6PPS=0x0010'TX1 > RC6U1RXPPS=0x0017'RC7 > RX1'Module: UART2RC0PPS=0x0013'TX2 > RC0U2RXPPS=0x0011'RC1 > RX2'Module: UART pin directionsDirPORTC.6Out' Make TX1 pin an outputDirPORTC.7In' Make RX1 pin an inputDirPORTC.0Out' Make TX2 pin an outputDirPORTC.1In' Make RX2 pin an inputDIRPortC.4In'2Mhz Frequency inputDirPortC.3In'PPS from GPSEndSub#Define LED PORTB.7DirLEDOut'LED PORTB.7'USART settings for UART1 and UART2#define USART_BAUD_RATE 9600#define USART_BLOCKING#define USART_TX_BLOCKING#define USART2_BAUD_RATE 9600'#define USART2_BLOCKING'#define USART2_TX_BLOCKING'#define USART_DELAY OFFDimTempStringasstringDimTempLongasLongDimTimerValueasWordDimFrequencyCountasLongDimFrequencyLowasLongDimOffsetasInteger'Frequency Offset from CalibrationDimTargetFrequencyasLong'The frequency to apply the offset to'Setup Timer1 for frequency counterInitTimer1Ext,PS1_1'Interrupt routineOnInterruptTimer1OverflowCallIncCounterTargetFrequency=7000000'Target Frequency to be addjustedOffset=0MainLoop:DoForever'*** This Program wont do anything unless there is a GPS MODULE WITH A FIX AND PULSING ****'This program will need:'1)A GPS Pulse Per Second Output Attached to PORTC.3 pin'2)A not quite accurate input signal of 2Mhz - this will be compared to what the Timer1 Counter + the GPS Gate comes up with'3)Terminal info sent out on COM2 (PORTC.0 and PORTC.1)ClearTimer1FrequencyCount=0WaitUntilPORTC.3=1'GPS Pulse onStartTimer1WaitUntilPORTC.3=0'GPS Pulse off - we have to look for this GPS ON/OFF sequence to proceedWaitUntilPORTC.3=1'GPS Pulse on - The Second Pulse gives us our 1 second window or gate.'If the GPS misses a pulse the reading is inaccuate.StopTimer1FrequencyCount=FrequencyCount+Timer1'The difference in frequency when compared to a frequency counter is spot on.'Divide the Target frequency by 2 Mhz and the frequency deviation is close enough!'Avoid Negative NumberifFrequencyCount>2000000thenTempLong=FrequencyCount-2000000Offset=TargetFrequency/2000000*TempLongOffset=Offset*-1elseifFrequencyCount=2000000thenOffset=0elseTempLong=2000000-FrequencyCountOffset=TargetFrequency/2000000*TempLongendifTempString=str32(FrequencyCount)comport=2HSerPrint"Frequency Count = ",2HSerPrintTempString,2HSerPrint" Offset = "+StrInteger(Offset),2HSerSend13,2HSerSend10,2comport=1'Wait aprox 1/2 second between pulses to capture next oneWait500msLoop'Interrupt RoutineSubIncCounterFrequencyCount=FrequencyCount+65536Endsub
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
Thank you for all the videos that are specific to this chip family! makes it much easier when you repetitively demonstrate through your videos the setup of these things.
I'm a bit weary of trying to find a chip that will suit my needs (and is available!). I'm downsizing from the 40 pin monsters. I have just enough, with 20 pins. And the smaller size allows me to go back to old school DIP chips allowing for quick swap out.
Maybe someday I'll go back to a SMT PIC, but It will definitely be this chip in an SOIC package.
thanks again,
Jeff
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
I went ahead a setup a PIC18F16Q41 to demonstrate how the Signal Measurement Timer (SMT) can be used in this case. I used a PIC18F27Q43 to emulate the GPS 7MHz signal using its Numerically Controlled Oscillator(NCO) and to supply the 1 second pulse. I put a switch on the 18F27Q43 that when pressed "gates" the SMT on the PIC18F26Q41,
Here is the demo code:
#CHIP 18F16Q41, 4#CONFIG LVP = ON#CONFIG MCLRE = ON#OPTION EXPLICIT#STARTUP InitPPS, 85#DEFINE USART_BAUD_RATE 9600#DEFINE USART_TX_BLOCKINGWait400msHsersend12'clear terminal (Using CoolTerm as Terminal)Wait100msHserprint"Starting Counter Test":HSerprintCRLF2'===========================================================DirPORTC.3IN' 7.0 MHz Input ( External 7 MHz Clock)DirPORTC.1IN' SMT Gate Input (1 sec signal)DimDUMMYasByte:Dummy=0DimPulseCountAliasDUMMY,SMT1TMRU,SMT1TMRH,SMT1TMRLASLONG'Gated Counter Single Acquisition Mode'Acquisition Starts when 1 s Gate goes high'Acqisition Stops when 1 s Gate goes low and triggers Interrupt'Counts pulses on PortC.3 for 1 secSMT1CON1=0b00001001'Set Mode to Gated counterSMT1CON0_EN=1'Enable SMT PeripheralSMT1GO=1'Ready for 1 s Gate on C.1'Interrupt 1 sec data acquisition completesOnINterruptSMT1PulseWidthAcquiredCallShowPulseCount'Main LoopDo'Waiting for interruptloop'================== Subs & Functions==========================SubShowPulseCountHSerprintPulseCount:HSerprintCRLFPulseCount=0' Clear SMT1 TIMER/CounterSMT1GO=1' Enable Next AcquisitionEndSUBSubInitPPS'Module: SMT1SMT1SIGPPS=0x000F'RB7 > SMT1SIGSMT1WINPPS=0x0016'RC6 > SMT1WIN'Module: UART pin directionsDirPORTB.4Out' Make TX1 pin an output'Module: UART1RB4PPS=0x0010'TX1 > RB4EndSub'=====================================================================
Very nice! I will be testing out today. I understood most of the code and the one brick wall that was stopping me from moving forward with SMT was this line you wrote:
On INterrupt SMT1PulseWidthAcquired Call ShowPulseCount
The actual Interrupt name of 'SMT1PulseWidthAquired'. I had no idea where to look for that.
SMT is definitely the way to go as I may have to sample frequencies as high as 50Mhz (the 6 meter Ham band) .
Another question: I dont see an #include smt_timers.h in the code. Is it picking up the reserved words automatically?
I will let you know how the implementation goes. What an amazing little chip the Q line is.
Thanks again for your help,
Jeff
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
I did not include smt_timers.h because that library does not support Gated Counter Mode and was therefore not needed. I set all the registers manually and created the necessary Long Variable as an alias.
You can find the interrupts by opening the PicInfo Tool then look at the interrupts tab. In this case you would have also needed to look at the datasheet to determine which of the two to use.
As far as counting pulses at 50 MHz I am not sure if the SMT can handle that speed. My signal generator is packed away in the closet and not accessible right now. You may need to divide the 50Mhz signal by 2, 4, or 8 first. But please when you try that let us know how it works.
As far as the 18FQ chips go, these have nearly every peripheral you might need and are generally less expensive than the older PICs.
Note that I used 4MHz as the Chip Speed. However the code will work at 8,16,32,or 64 MHz with no other changes required to the code.
William
Last edit: William Roth 2022-08-16
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
Here is the 18F27Q43 Code that was used to generate the 7 MHz signal and the 1 second gate. It is nothing special. The NCO is great for generating odd & precise frequencies.
#CHIP18F27Q43, 32
#CONFIGLVP=ON
#CONFIGMCLRE=ON
#startupInitPPS, 85
#OPTIONEXPLICITWait400ms;stabilize
#DEFINEGatePortC.6DIRPORTC.6OUT
#DEFINESWITCHPORTC.3DirPORTC.3INDimDummy : Dummy=0DIMNCO1_INCAliasDummy, NCO1INCU,NCO1INCH, NCO1INCLasLONGNCO1_INC=457988'Equates to 7 Mhz ( adjust as needed)NCO1CLK=0b00000000'Default (FOSC)NCO1CON.7=1'Start NCO OscillatorDoWaitUntilSwitch=ONSetGateOnWait1001700us'precice 1 second delay per scopeSetGateOffLoop'================== Subs & Functions==========================SubInitPPS'Module: NCO1RC4PPS=0x003F'NCO1 > RC4DirPortC.4OutEndSub'=====================================================================
Last edit: William Roth 2022-08-16
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
One thing I did notice when counting on my end is that I'm losing the last digit of the Pulse count. So in the interrupt, I printed the Upper, High and Low bytes of the SMT count:
PulseCount is dimensioned as a "Long" Variable. A long is 32 bits with each of the 4 bytes in the list being 8 bits. Dummy is a place holder for the "extended" byte. It must be zero. The other bytes of PulseCount are aliased to the SMT1TMRU/H/L registers. These three bytes make up the 24-bit timer counter variable called "PulseCount". You can read up about Variables and Aliasing in the GCB help. Open the Help ===> Variable operations ===> Dim
Your print out of the individual bytes suggests that something is likely amiss in your program. However, it could be a serial or terminal issue . The maximum value of each of the bytes that make up "PulseCount" is 255. yet you are showing values that are inconsitent with byte values. The test program I posted works correcty here.
We will need to see your complete GCB program that is failing, as well as the ASM to see what's going on.
In the mean time for testing and debug purposes you may want to try a different Terminal Application. You may also want to try using TX1 instead of TX2 to see if that makes any difference but I doubt it will.
William
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
'Interrupt routineSubShowPulseCountcomport=2'Manually change Pulsecount to a value I'mexpectingPulseCount=7040123HSerprintPulseCount,2HSerPrintCRLF,2comport=1PulseCount=0' Clear SMT1 TIMER/CounterSMT1GO=1' Enable Next AcquisitionEndSUB
The value of 7040123 went through the UART2 without a problem.
I changed the PulseCount back to the SMTValues and the return goes back to 6 digits.
attached is the entire program with all the extra GPS and Si5351 (clock gen) stuff in it, but your interrupt and variables are used for deriving the PulseCount.
I cannot debug this due to the complexity of the code and the fact that I do not have the gps module or the si clock device.
My suggestion is to strip the code down to a bare minimum where the GPS is providing a consistent frequency and the clock is providing the 1 sec gate signal. Then Test. This means
removing everything not related to providing a 7 MHz clock and a 1 sec gate.
When you get this working start adding back sections of code one at a time until it fails. This is a standard/ troubleshoooting method.
When a Long variable is defined:
DimMyLongvarasLong
There is absolutely no need to define the bytes that make up the variable. The compiler does this automatically. I am not sure how this will affect memory allocation. So let the compiler assign the bytes for the Long Variable. See your variable "MS0P1 " and others.
***Dontdothis!***DimMyLongVarasLong;OkDimMyLongVar_E; Not recommended !DimMyLongVar_U; Not recommended !DimMyLongVar_H; Not recommended !
If you want to work with the individual bytes in a Long Variable one trick is to us Aliasing and dim the individual bytes like this.
DimLV_ExtByteLV_UpByte,LV_HiByte,LV_LoByteasByteDIMLongVarasLONGAliasLV_ExtByte,LV_UpByte,LV_HiByte,LV_LoByteLongVar=0' initalize variable to assure it is allocated in ASM
Do not use _E , _U or _H in byte variable names as these are reserved
Single letter variables are not recommended:
Suggest you cange "I" to "II" or something else like "index". Meaningful variable names are preferred, especially if others will be working with the code.
You have dimmed "I" as Long variable. I cannot see in your code where I will ever be greater than 65535 but I could have missed something. Use a Word instead to save memory unless indeed "I" could be greater than 65535.
Also, by not specifically providing a clock speed with #Chip the speed defaults to the chips maximum of 64Mhz. Is this really what you want? I would suggest slowing it down a bit for development. Then if you really need 64Mhz you can change it.
#chip 18F16Q40, 32
William
Last edit: William Roth 2022-08-18
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
I've tried several other frequency bands and the last digit truncation is consistent If I simply multiply the result I'm getting by 10 I'm accurate to within 10 Hertz, which is completely fine for my project. I will circle back to this as the workaround will for now get me answers that are close enough.
Thanks for taking a look
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
Looking at the pulsecount issue a little further I gathered what I believe are the correct values coming back from the counter gate:
SMT1TMRU: Accesses the upper byte TMR[23:16] ==> 21 decimal
SMT1TMRH: Accesses the high byte TMR[15:8] ==> 130 decimal
SMT1TMRL: Accesses the low byte TMR[7:0] ==> 174 decimal
'I also checked the capture values and they are exactly the same as above
SMTxCPRU: Accesses the upper byte CPR[23:16]
SMTxCPRH: Accesses the high byte CPR[15:8]
SMTxCPRL: Accesses the low byte CPR[7:0]
Converting to binary and combining bytes to get [23:0]:
If the DUMMY is always zero it shouldn't affect the answer [31:24]
Both my scope and frequency counter are counting close to 14,097,100 so that frequency is real.
Is there some type of resolution issue possibly with the chip or GCB config issue with PIC18F16Q40? I guess I could get a hold of the chip that you are testing with to see if I get the same result.
I am using a PIC18F16Q41 which has the exact same SMT peripheral as your 18F16Q40 so I would not think there is any value in changing chips. Evan and I discussed this and I will be doing some further testing using your code , but injecting the 7Mhz clock and the 1 sec gate from an external source.
However your main loop code calls a subroutine called "get_val" that uses "HserRecieve", which has been defined as a blocking command. This means that program flow will hang there if no serial data is received. I have no clue what that serial data is or where is comes from so it is difficult to emulate. I could also inject serial data if I knew what the program is expecting.
Lets see what I can come up with over the weekend as I want to see what happens when the interrupt occurs while waiting on or processing serial data via HserReceive.
Edit: I now see that it is looking for $GPGGA before moving on to start counting commas and other stuff. So I think I can injsct serial data to fake it out, at least for a while.
Can you please post screen shots from your scope of the 7Mhz signal and the 1 sec pulse?
William
Last edit: William Roth 2022-08-19
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
Yes get_val is looking for NMEA from the GPS module. This is a constant stream of data coming out of the module, sending data out once per second basically the whole do forever loop is counting on this. you can basically remark out the entire Do forever loop as its parsing GPS data (GPS TIME, Status, GPS Location) continuously.
TX_OFF() and TX_ON() just turn my7Mhz clock source off and on they can be remmed out too
All the EPreads are not of consequence either i dont think.
I have found what I think to be a bug in the USART.h library that is likely causing this issue.
It appears that there is a system variable conflict when USART1 and USART2 are both used in a program. I have not yet dug deep into the USART.h library so I have no definite answer/resolution.
This bug only appears when 2 (or more) USARTs are used in a program. It affects WORD and INTEGER variables and likely LONG Variables.
I will be writing up a bug report and sending it to the boss after I get some sleep.
William
Last edit: William Roth 2022-08-20
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
Hello,
I have an input frequency of 7 Mhz on PortC.4
I am also using a GPS which has Pulse Per Second output on PortC.3 to get me an accurate 1PPS.
I want to count how many pulses on PortC.4 that occur between pulses on PortC.3 (accurate 1 second GPS pulses) to hopefully get an accurate frequency in HZ.
Hardware wise I'm not sure if I should have pull-down resistor's on both the GPS and frequency line.
I have an interrupt routine setup to interrupt when Timer1 overflows then I'm adding 65536 to the counter.
I think I am close but not getting the correct answer. I'm getting like 31250 counts. What I'm not sure of is the proper setup of timer1 and If I'm counting overflows correctly.
Can anyone assist? I know this chip is familiar to Evan as he has featured a similar MCU in his YouTube instruction videos, and I'm showing only the critical parts of the code:
thanks ! any help is appreciated.
Jeff
Last edit: Jeff Weinmann 2022-08-12
Timer1 is a 16-bit timer, meaning it will overflow after 65535 pulses. You are wanting to count ~7,000,000 pulses over a time of ~1 second, so you are counting overflows and adding remainders and such. This is not an ideal method given the capability of the chip.
I would suggest you open the datasheet to Chapter 27 (Page 398) and familiarize yourself with the "Signal Measurement Timer". This is a 24-bit Timer that can count up to 16,777,215 pulses and has several modes of operation. I suggest you use the "Gated Counter Mode" Where the 1Hz pulse gates the timer on/off. The timer automatically starts when the "gate" goes high and a result is returned when the gate goes low. An interrupt can be set to trigger when the count is done
If after studying the SMT section of the datasheet and trying it out as best you can, and if you are still lost, let us know and more information can be provided.
William
Thank you for your kind assistance!
I've read up on chapter 27 and I've gotten a better (not complete) understanding of how to use the SMT.
Here is the state of the code below. I've added the SMT signal and window input pins via the PPS tool, and tried to document what I think I know.
I'm pretty sure I set the mode to be 'Gated Counter'.
The code runs but I get a bunch of 0's then some numbers. but I do see the numbers change when a GPS pulse does occur. I'm not sure how to set the interrupt so I have a loop until for now.
I think the bones are there, I just need a little push.
Jeff
Last edit: Jeff Weinmann 2022-08-13
You may also find Bresenham's Algorithm useful.
Here is a goods explanation and example code, but not in GCBASIC so you would need to adapt the algorithm.
https://www.romanblack.com/one_sec.htm
Cheers
Chris
I decided to refer to and older project where I did something very similar using Timer1. I'm satisfied enough with the results and I'm only 2 to 3 Hertz off the measured frequencies with my scope and commercial frequency counter.
Method:
1) Set the frequency to be measured to 2Mhz
2) Count the frequency using the below code
3) Take the difference between measured and actual
4) Apply a scale factor by taking desired frequency / 2Mhz
Example: 14Mhz / 2 Mhz is a scale factor of 7 so if my difference in frequency at 2Mhz is 27, My difference I will apply at 14Mhz is : 189. I do have to pay attention whether the difference is positive or negative
Here is what I did in code:
~~~
startup InitPPS, 85
define PPSToolPart 18f16q40
;Setup Timer1 for frequency counter
InitTimer1 Ext, PS1_1
On Interrupt Timer1Overflow Call IncCounter
Offset = 0
Do Forever
EndLoop:
Loop
Sub IncCounter
FrequencyCount = FrequencyCount + 65536
End sub
~~~
Last edit: Jeff Weinmann 2022-08-14
You make it look easy. Well done.
Can you the program? the code above does not compile. I just tried. Vars are missing, and other key things. Thank you.
This compiles, but you will need a GPS with PPS out and signal source of 2Mhz:
Thank you. I just compiled in the latest/updated compiler. All was good.
Nice piece of code.
Consider adding the following to optimise your code. This will
This will improve overall program performance and make the code a bit smaller.
Evan
Thank you for all the videos that are specific to this chip family! makes it much easier when you repetitively demonstrate through your videos the setup of these things.
I'm a bit weary of trying to find a chip that will suit my needs (and is available!). I'm downsizing from the 40 pin monsters. I have just enough, with 20 pins. And the smaller size allows me to go back to old school DIP chips allowing for quick swap out.
Maybe someday I'll go back to a SMT PIC, but It will definitely be this chip in an SOIC package.
thanks again,
Jeff
Pleasure!
I went ahead a setup a PIC18F16Q41 to demonstrate how the Signal Measurement Timer (SMT) can be used in this case. I used a PIC18F27Q43 to emulate the GPS 7MHz signal using its Numerically Controlled Oscillator(NCO) and to supply the 1 second pulse. I put a switch on the 18F27Q43 that when pressed "gates" the SMT on the PIC18F26Q41,
Here is the demo code:
Enjoy
William
Last edit: William Roth 2022-08-16
William,
Very nice! I will be testing out today. I understood most of the code and the one brick wall that was stopping me from moving forward with SMT was this line you wrote:
On INterrupt SMT1PulseWidthAcquired Call ShowPulseCount
The actual Interrupt name of 'SMT1PulseWidthAquired'. I had no idea where to look for that.
SMT is definitely the way to go as I may have to sample frequencies as high as 50Mhz (the 6 meter Ham band) .
Another question: I dont see an #include smt_timers.h in the code. Is it picking up the reserved words automatically?
I will let you know how the implementation goes. What an amazing little chip the Q line is.
Thanks again for your help,
Jeff
Hi Jeff
I did not include smt_timers.h because that library does not support Gated Counter Mode and was therefore not needed. I set all the registers manually and created the necessary Long Variable as an alias.
You can find the interrupts by opening the PicInfo Tool then look at the interrupts tab. In this case you would have also needed to look at the datasheet to determine which of the two to use.
As far as counting pulses at 50 MHz I am not sure if the SMT can handle that speed. My signal generator is packed away in the closet and not accessible right now. You may need to divide the 50Mhz signal by 2, 4, or 8 first. But please when you try that let us know how it works.
As far as the 18FQ chips go, these have nearly every peripheral you might need and are generally less expensive than the older PICs.
Note that I used 4MHz as the Chip Speed. However the code will work at 8,16,32,or 64 MHz with no other changes required to the code.
William
Last edit: William Roth 2022-08-16
Bill, post the q43 - this looks like a good demo to put in GitHub.
Here is the 18F27Q43 Code that was used to generate the 7 MHz signal and the 1 second gate. It is nothing special. The NCO is great for generating odd & precise frequencies.
Last edit: William Roth 2022-08-16
Attached is a crude schematic of the test setup.
Nice simulation.
One thing I did notice when counting on my end is that I'm losing the last digit of the Pulse count. So in the interrupt, I printed the Upper, High and Low bytes of the SMT count:
These are the results I get:
L = 56
H = 704056190
U = 70405610
Pulsecount = 704056 < missing the last digit
FYI, I'm using a Northern Software programmer and its UART connection. Strange. I don't see why the last digit in the count is not showing. I tried:
Tempstring = str32(Pulsecount)
and its still just 6 digits.
Northern software has a command prompt utility called 'nsuart' that I can monitor the traffic (see attached)
Really weird.
Also I'm not quite sure how the variables Pulsecount is dimensioned:
this suggested to me that PulseCount is dimensioned as a Byte since Dummy is a Byte?
Jeff
Last edit: Jeff Weinmann 2022-08-16
Hi Jeff
PulseCount is dimensioned as a "Long" Variable. A long is 32 bits with each of the 4 bytes in the list being 8 bits. Dummy is a place holder for the "extended" byte. It must be zero. The other bytes of PulseCount are aliased to the SMT1TMRU/H/L registers. These three bytes make up the 24-bit timer counter variable called "PulseCount". You can read up about Variables and Aliasing in the GCB help. Open the Help ===> Variable operations ===> Dim
Your print out of the individual bytes suggests that something is likely amiss in your program. However, it could be a serial or terminal issue . The maximum value of each of the bytes that make up "PulseCount" is 255. yet you are showing values that are inconsitent with byte values. The test program I posted works correcty here.
We will need to see your complete GCB program that is failing, as well as the ASM to see what's going on.
In the mean time for testing and debug purposes you may want to try a different Terminal Application. You may also want to try using TX1 instead of TX2 to see if that makes any difference but I doubt it will.
William
William,
I changed the value of PulseCount:
The value of 7040123 went through the UART2 without a problem.
I changed the PulseCount back to the SMTValues and the return goes back to 6 digits.
attached is the entire program with all the extra GPS and Si5351 (clock gen) stuff in it, but your interrupt and variables are used for deriving the PulseCount.
Thanks and feel free to markup my code!
Jeff
Last edit: Jeff Weinmann 2022-08-17
I cannot debug this due to the complexity of the code and the fact that I do not have the gps module or the si clock device.
My suggestion is to strip the code down to a bare minimum where the GPS is providing a consistent frequency and the clock is providing the 1 sec gate signal. Then Test. This means
removing everything not related to providing a 7 MHz clock and a 1 sec gate.
When you get this working start adding back sections of code one at a time until it fails. This is a standard/ troubleshoooting method.
When a Long variable is defined:
There is absolutely no need to define the bytes that make up the variable. The compiler does this automatically. I am not sure how this will affect memory allocation. So let the compiler assign the bytes for the Long Variable. See your variable "MS0P1 " and others.
If you want to work with the individual bytes in a Long Variable one trick is to us Aliasing and dim the individual bytes like this.
Do not use _E , _U or _H in byte variable names as these are reserved
Single letter variables are not recommended:
Suggest you cange "I" to "II" or something else like "index". Meaningful variable names are preferred, especially if others will be working with the code.
You have dimmed "I" as Long variable. I cannot see in your code where I will ever be greater than 65535 but I could have missed something. Use a Word instead to save memory unless indeed "I" could be greater than 65535.
Also, by not specifically providing a clock speed with #Chip the speed defaults to the chips maximum of 64Mhz. Is this really what you want? I would suggest slowing it down a bit for development. Then if you really need 64Mhz you can change it.
William
Last edit: William Roth 2022-08-18
William,
Thanks for taking a look at it.
I've tried several other frequency bands and the last digit truncation is consistent If I simply multiply the result I'm getting by 10 I'm accurate to within 10 Hertz, which is completely fine for my project. I will circle back to this as the workaround will for now get me answers that are close enough.
Thanks for taking a look
Looking at the pulsecount issue a little further I gathered what I believe are the correct values coming back from the counter gate:
SMT1TMRU: Accesses the upper byte TMR[23:16] ==> 21 decimal
SMT1TMRH: Accesses the high byte TMR[15:8] ==> 130 decimal
SMT1TMRL: Accesses the low byte TMR[7:0] ==> 174 decimal
'I also checked the capture values and they are exactly the same as above
SMTxCPRU: Accesses the upper byte CPR[23:16]
SMTxCPRH: Accesses the high byte CPR[15:8]
SMTxCPRL: Accesses the low byte CPR[7:0]
Converting to binary and combining bytes to get [23:0]:
<----21---> <--130---> <--174--->
0001 0101 1000 0010 1010 1110
The decimal value for the above is:
1,409,710 which is off by a factor of 10:
It should be 14,097,100
If the DUMMY is always zero it shouldn't affect the answer [31:24]
Both my scope and frequency counter are counting close to 14,097,100 so that frequency is real.
Is there some type of resolution issue possibly with the chip or GCB config issue with PIC18F16Q40? I guess I could get a hold of the chip that you are testing with to see if I get the same result.
Just trying to look a little deeper.
Jeff
Last edit: Jeff Weinmann 2022-08-19
I am using a PIC18F16Q41 which has the exact same SMT peripheral as your 18F16Q40 so I would not think there is any value in changing chips. Evan and I discussed this and I will be doing some further testing using your code , but injecting the 7Mhz clock and the 1 sec gate from an external source.
However your main loop code calls a subroutine called "get_val" that uses "HserRecieve", which has been defined as a blocking command. This means that program flow will hang there if no serial data is received. I have no clue what that serial data is or where is comes from so it is difficult to emulate. I could also inject serial data if I knew what the program is expecting.
Lets see what I can come up with over the weekend as I want to see what happens when the interrupt occurs while waiting on or processing serial data via HserReceive.
Edit: I now see that it is looking for $GPGGA before moving on to start counting commas and other stuff. So I think I can injsct serial data to fake it out, at least for a while.
Can you please post screen shots from your scope of the 7Mhz signal and the 1 sec pulse?
William
Last edit: William Roth 2022-08-19
Yes get_val is looking for NMEA from the GPS module. This is a constant stream of data coming out of the module, sending data out once per second basically the whole do forever loop is counting on this. you can basically remark out the entire Do forever loop as its parsing GPS data (GPS TIME, Status, GPS Location) continuously.
TX_OFF() and TX_ON() just turn my7Mhz clock source off and on they can be remmed out too
All the EPreads are not of consequence either i dont think.
Attached is my Dev board FYI
Jeff
Last edit: Jeff Weinmann 2022-08-20
Hi Jeff,
I have found what I think to be a bug in the USART.h library that is likely causing this issue.
It appears that there is a system variable conflict when USART1 and USART2 are both used in a program. I have not yet dug deep into the USART.h library so I have no definite answer/resolution.
This bug only appears when 2 (or more) USARTs are used in a program. It affects WORD and INTEGER variables and likely LONG Variables.
I will be writing up a bug report and sending it to the boss after I get some sleep.
William
Last edit: William Roth 2022-08-20