News:

Let's find out together what makes a PIC Tick!

Main Menu

Measure timer

Started by joesaliba, Apr 25, 2021, 04:55 PM

Previous topic - Next topic

joesaliba

I need to measure timing on two pins, PORTC.0 and PORTC.1 using 18F26K22. I would like to alternate between pins without adding double code for same reason, as in future I may be measuring 5 pins.

Dim Pin_Num as Byte

Main:
Pin_Num = PIN_C0
GoSub Measure_Pin
DelayMs 500
Pin_Num = PIN_C1
GoSub Measure_Pin
DelayMs 500

Goto Main

'=================================================================================================

Measure_Pin:

While Pin_Num = 1 : Wend    '
While Pin_Num = 0 : Wend    '

TMR1ON = 1                  ' Turn ON timer 1 counter
While Pin_Num = 1 : Wend    '

TMR1ON = 0                  ' Turn OFF timer 1 counter

The above will not work.

The following will work but I will have oscillations in the reading, I think it is because of the GetPin: -

Measure_Pin:

While GetPin Pin_Num = 1 : Wend    '
While Getpin Pin_Num = 0 : Wend    '

TMR1ON = 1                  ' Turn ON timer 1 counter
While GetPin Pin_Num = 1 : Wend    '

TMR1ON = 0                  ' Turn OFF timer 1 counter

Any idea if it can be done similar to example one please?

I think I cannot change pins the way I tried above with HPWM, HRSIn etc...

Regards

Joseph

top204

#1
Remember... The compiler's commands and functions that use a constant or variable to hold a Port.Pin reference need to call a few subroutines to generate a mask of the pin and the address of the port, then combine them. I have created the underlying code to calculate and combine masks and addresses to be as fast as is possible, but it will always take a few microseconds longer than accessing a Port.Pin directly.

So for measuring time, the faster the better when accessing a pin.

You would be better creating a procedure that has a byte variable as the parameter, and depending on the value in the parameter, a combination of routines are placed within If-Thens, but using different Port.Pins that directly reference a Port's SFR. For speed, easy access is not always a friend, and neither is smaller code size sometimes!

If you see C and C++ code with all of that left and right shifting with Ors and Ands for Port.Pin access, that is what the compiler does invisibly, and a whole lot faster than them, when a Port.Pin is not a direct reference to a Port's SFR, but it can never be as fast.

trastikata

#2
Why do you want to assign pin to a variable and then use the same Subroutine? Just write a separate subroutine for each pin and access it when needed, can't you spare the space?

Also I don't see the rest of the code - how do you access the timer counter, how do you reset it and how do you deal with overflows - maybe this could be part of the problem?

joesaliba

Quote from: trastikata on Apr 25, 2021, 07:45 PMWhy do you want to assign pin to a variable and then use the same Subroutine? Just write a separate subroutine for each pin and access it when needed, can't you spare the space?

Also I don't see the rest of the code - how do you access the timer counter, how do you reset it and how do you deal with overflows - maybe this could be part of the problem?

Yes, I use the same routine for code space for future larger code. The rest of the code do take care of clearing the necessary variables to reset the counter.

@top204 For measuring time, I will access the pin directly. Will duplicate some routines.

Thank you

Joseph

Stephen Moss

Are you locked in to using port C pins?
If not then I would think using PortB may be better as you can use the interrupt function on those pins. I think using interrupt to indicate when to start/stop the timer is preferable to checking the input pin status in code (unless you have no other choice) due to the execution time delaying the reaction to the change. You could then change which pin is measured by changing which pins interrupt is enabled. In your ISR start/stop the timer and get the count as applicable.

Instead of turning the timer on and off, after initially turning it on you may be better off writing $00 to clear it than turning on and just reading (Variable = TMR1) instead of turning it off.

Have you looked at the gate facility of the timer, as you are only interested in pulse width then that would be a good way of getting the count as the timer count only increments while the signal is high, and generates an interrupt once if falls low and the count has stopped so you know when to read the value. However, it is a little difficult for reading multiple inputs in the way you code outlined, if you have a couple of spare I/O pins routing the signal sources to the gate pin via an external multiplexer would be the way to select them.

Back to your original query, wasn't there thing where the port pins are numbered 0 to X where 0 = portA.0, 7 = PortA.7, 8 = PortB.0 and so on, if so then you could create a loop in you main code, and pass the port pin to a procedure, something like this...
Dim Pin_Num as Byte
Dim My_Count as Word  'Hold count of signal length
TMR1ON = 1                  ' Turn ON timer 1 counter

Main:
For Pin_Num = 16 to 17 (PIN_C0 & PIN_C1 respectively if my calculations are correct
My_Count = Measure_Pin(Pin_Number)  'Change Gosub to procedure
DelayMs 500
Next
Goto Main

'=================================================================================================

Proc Measure_Pin(ByVal Pin_Number as byte), Word

While Pin_Num = 1 : Wend    'Input synchronisation, wait until pulse goes high to start count
While Pin_Num = 0 : Wend    '

TMR1= $00                  ' Clear timer 1 value
While Pin_Num = 1 : Wend    '

Result = TMR1ON            ' Read timer 1 count (applied to My_Count on return)
End Proc
If I am mistaken about the Port Pin = a number thing (or the pin numbers are not consecutive) then you could use a 0 to X For-Next loop and place a Select-Case after the For, to assign the Port pin that is passed to the procedure i.e. Case=0
   Pin_Num = Pin_C0
Case=1
   Pin_Num = PIN_C1

top204

#5
Depending on the time that is required to measure, you can use a device that has PPS (Peripheral Pin Select) and use a CCP peripheral to measure the time between high and low pulses, or vice-versa.

The PPS will allow the pin used for the CCP peripheral to be changed depending on what pins to measure from. As long as the compiler's built-in PPS_Unlock meta-macro is used first, and the config fuse for locking PPS is disabled (compiler's default), the PPS can be changed as many times as required.

I know it sounds more complex, but once you get used to PPS, it is extremely useful. Note that not all pins are suitable for PPS, so make sure the pins you use are suitable by looking at the datasheet's table for input PPS.

joesaliba

Thank you all for your input.

@Stephen Moss I tried with pin number but that will not work to get the state of the pin. See post 1. I used a similar method to yours but needed to use the GetPin to make it work, otherwise a good method.

Regards

Joseph

top204

#7
Using your code from above, the program below will give a very fast time because it uses the Port.Pin directly for measurements, but the parameter chooses which pin to measure from:

    Measure_Pin(1)
    Measure_Pin(2)
    Measure_Pin(3)
    Measure_Pin(4)

'---------------------------------------------------------------------------------
' Measure time between pin pulses
' Input     : pPin chooses which pin to measure based upon the Symbol aliases inside it for the actual pins to use
' Output    : Timer1 has the time between pulses
' Notes     : Uses Repeat-Until instead of While-Wend for more efficient, and faster, assembler code
'
Proc Measure_Pin(pPin As Byte)
'
' Create aliases to the Port.Pin that each parameter value represents
'
    Symbol Pin1 = PORTB.0
    Symbol Pin2 = PORTB.1
    Symbol Pin3 = PORTB.2
    Symbol Pin4 = PORTB.3

    If pPin = 1 Then                        ' Is it pin 1 that needs to be measured?
        Repeat: Until Pin1 = 0              ' Yes. So wait until Pin1 is low
        Repeat: Until Pin1 = 1              ' Wait for Pin1 to go high   
        T1CONbits_TMR1ON = 1                ' Turn on Timer1
        Repeat: Until Pin1 = 0              ' Wait for Pin1 to go low  
   
    ElseIf pPin = 2 Then                    ' Is it pin 2 that needs to be measured?
        Repeat: Until Pin2 = 0              ' Yes. So wait until Pin2 is low
        Repeat: Until Pin2 = 1              ' Wait for Pin2 to go high     
        T1CONbits_TMR1ON = 1                ' Turn on Timer1
        Repeat: Until Pin2 = 0              ' Wait for Pin2 to go low
   
    ElseIf pPin = 3 Then                    ' Is it pin 3 that needs to be measured?
        Repeat: Until Pin3 = 0              ' Yes. So wait until Pin3 is low
        Repeat: Until Pin3 = 1              ' Wait for Pin3 to go high   
        T1CONbits_TMR1ON = 1                ' Turn on Timer1
        Repeat: Until Pin3 = 0              ' Wait for Pin3 to go low
   
    ElseIf pPin = 4 Then                    ' Is it pin 4 that needs to be measured?
        Repeat: Until Pin4 = 0              ' Yes. So wait until Pin4 is low
        Repeat: Until Pin4 = 1              ' Wait for Pin4 to go high
        T1CONbits_TMR1ON = 1                ' Turn on Timer1
        Repeat: Until Pin4 = 0              ' Wait for Pin4 to go low
    EndIf   
    T1CONbits_TMR1ON = 0                    ' Turn Off Timer1
EndProc

On a PIC18F25K20 device, the above code running at 64MHz took only 136 bytes of code memory, which makes it both fast and efficient. Notice I changed the While-Wends into Repeat-Untils because they operate more efficiently because the comparison is at the end of the loop, not the beginning.

joesaliba

Thank you for the code Les. I will use this method. I must admit I still get to use more the Proc. Looks very neat and easy to follow.

top204

#9
You are very welcome Joe.

I've altered the above code slightly to make the actual pin aliases local types within the procedure itself, but they are still direct access to the Port.Pin, it is just that they now have a unigue name and will not be created unless the procedure is called in a program.

Procedures where a very hard thing to add to the compiler and took months of dedicated work while I was still suffering the major aftermath of the brain hemorage and changes of life that it brought, but they bring the compilers into the 21st century and make coding a whole lot more fun. :-)

What makes them special is that procedures are not mandatory like most other procedural languages these days that are created using "compiler creator programs", so the language still has the ease of use of BASIC, but a lot more power.

joesaliba

That is looking so good.

I did not know that Symbols can reside into a procedure.

Regards

Joe