In the topic "algorithm for quadrature encoder", elsewhere in the forum, the subject of Interrupts and more importantly, avoiding the use thereof, was raised. It got me thinking about a useful function that Arduino has and most other platforms don’t. The Millis() Function.
Why is it of interest? Well it keeps count of the number of Milliseconds elapsed since the MCU was last powered up or reset. Because it is stored as a 32 Bit value it will only roll over to zero every 50 Days. That allows us to measure very short or very long periods. Certainly adequate for most of our timing needs.
How is it relevant? As it uses a timer and generates an interrupt it is a good example of both using Timers and an introduction to Interrupts, it also gives us a valuable tool for our Great Cow Basic Libraries.
In the near future I will be writing this up as a tutorial and will also be looking at porting the code between this newer representative of the PIC 16F family and some of the Older PIC 16F familys. In the meantime, to whet your appetite, here is an example of using Millis() to Flash an LED without locking up the MCU in a tight loop.
' Millis() - a first look'' C.A.Roper'04/07/2016''-----Configuration'Chip Settings. #chip 16f18855,32 #config RSTOSC_HFINT32 'TMR0_Initialize()T0CON0=0x00' T0OUTPS 1:1; T0EN disabled; T016BIT 8-bit; T0CON1 = 0x45 'T0CSFOSC/4;T0CKPS1:32;T0ASYNCsynchronisedTMR0H=0xF9' TMR0H 249; Period 1ms TMR0L = 0x00 'TMR0L0;TMR0IF=0' Clear Interrupt flag before enabling the interrupt TMR0IE = 1 'EnablingTMR0interrupt.T0EN=1' Start TMR0 'AddahandlerfortheinterruptOnInterruptTimer0OverflowCallTMR0_ISR#defineLEDPortA.0' the follow variables is a long because the time, measured in 'miliseconds,willquicklybecomeabiggernumberthancanbe' stored in a byte or an integer. Dim t0_millis As long Dim interval As Long Dim currentMillis As Long Dim previousMillis As Long 'SetupLED=0interval=1000' interval at which to blink (milliseconds) Dir LED Out 'MainloopDocurrentMillis=millis()' Read Ellapsed Time if (currentMillis - previousMillis) > interval then previousMillis = currentMillis 'savecurrenttimeLED=!LED' if the LED is off turn it on and vice-versa end if Loop function millis() as long 'disableinterruptswhilewereadt0_millisorwemightgetan' inconsistent value (e.g. in the middle of a write to t0_millis) IntOff millis = t0_millis IntOn end Function Sub TMR0_ISR() TMR0H = 0xFA 'TMR0H250;Period1mst0_millis+=1EndSubEnd
As you can see the interrupt routine is nothing to be scared of and only consists of two parts:
1) Initialise the Interrupt Handling
On Interrupt Timer0Overflow Call TMR0_ISR
2) Handling the Interrupt
Sub TMR0_ISR()
TMR0H = 0xFA ' TMR0H 250; Period 1ms
t0_millis += 1
End Sub
Because Millis() is Interrupt driven the MCU can continue running other code in the main loop, such as reading inputs, and the LED will still flash at the rate set by interval.
Cheers
Chris
Last edit: Chris Roper 2016-07-05
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
At first glance,the above does not seem to be very interesting. It looks like just a more complicated way to blink a LED. However,it illustrates a very important concept known as a State Machine. Instead of relying on "wait x ms" to time the LED Flash, it remembers the current state of the LED and the last time it changed. On each pass through the loop, it looks at the millis() clock to see if it is time to change the state of the LED again.
Let’s look at a slightly more interesting variant that has an LED with different on-time and off-time, and a second LED that flashes at completely different rates.
' Millis() - Flash Two at a Time'' C.A.Roper'05/07/2016''-----Configuration'Chip Settings. #chip 16f18855,32 #config RSTOSC_HFINT32 'TMR0_Initialize()T0CON0=0x00' T0OUTPS 1:1; T0EN disabled; T016BIT 8-bit; T0CON1 = 0x45 'T0CSFOSC/4;T0CKPS1:32;T0ASYNCsynchronisedTMR0H=0xF9' TMR0H 249; Period 1ms TMR0L = 0x00 'TMR0L0;TMR0IF=0' Clear Interrupt flag before enabling the interrupt TMR0IE = 1 'EnablingTMR0interrupt.T0EN=1' Start TMR0 'AddahandlerfortheinterruptOnInterruptTimer0OverflowCallTMR0_ISR#defineLED1PortA.0#defineLED2PortA.1#defineLED3PortA.2#defineLED4PortA.3' the follow variables is a long because the time, measured in 'miliseconds,willquicklybecomeabiggernumberthancanbe' stored in a byte or an integer. Dim t0_millis As long 'Critical-UsedbytheInterrupt' working copy Dim curMillis As Long 'task1variablesDimState1DimOnTime1AsLongDimOffTime1AsLongDimOldMillis1AsLong' task 2 variables Dim State2 Dim OnTime2 As Long Dim OffTime2 As Long Dim OldMillis2 As Long 'SetupLED1=0' LED1 Initialy Off LED4 = 0 'LED4InitialyOffDirLED1OutDirLED4OutOnTime1=250' Time On in mS OffTime1 = 750 'TimeOffinmSOnTime2=330' Time On in mS OffTime2 = 400 'TimeOffinmS'Main loop Do curMillis = millis() 'ReadEllapsedTimeifState1=OnThenif(curMillis-OldMillis1)>=OnTime1ThenState1=Off' Change State OldMillis1 = curMillis 'savecurrenttimeLED1=State1' Update LED end if else if (curMillis - OldMillis1) >= OffTime1 Then State1 = On 'ChangeStateOldMillis1=curMillis' save current time LED1 = State1 'UpdateLEDendifendififState2=OnThenif(curMillis-OldMillis2)>=OnTime2ThenState2=Off' Change State OldMillis2 = curMillis 'savecurrenttimeLED4=State2' Update LED end if else if (curMillis - OldMillis2) >= OffTime2 Then State2 = On 'ChangeStateOldMillis2=curMillis' save current time LED4 = State2 'UpdateLEDendifendifLoopfunctionmillis()aslong' disable interrupts while we read t0_millis or we might get an 'inconsistentvalue(e.g.inthemiddleofawritetot0_millis)IntOffmillis=t0_millisIntOnendFunctionSubTMR0_ISR()TMR0H=0xFA'TMR0H250;Period1mst0_millis+=1EndSubEnd
Try it on your XPress Evaluation board and then try altering the Timeing or add in the Other Two LED's, It could be the basisis of your next string of Party Lights.
Cheers
Chris
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
Chris, these are excellent examples. I would actually like to implement this in a project using a different pic, but here is where I (and probably any new user) get totally confused. I'm sure that reading the data sheets would help, but it is a bit intimidating. Could you maybe explain a bit more what is going on with the following code and does this apply to any pic with a timer?
Thanks for the questions, they are points that definitely need addressing and will be covered in detail in the Tutorial series when I write it :)
In the interim though here is my reasoning.
I chose Timer0 as most PIC and AVR devices have a Timer0 module and because it is only an 8 Bit timer it is often available for use, as programmers tend to gravitate towards the higher resolution 16 bit timers for most functions. The problem in this instance is that the PIC16F18855 has a very sophisticated new Timer Module as Timer0 and as of writing the Builtin “InitTimer0” function did not support it so I resorted to inline register manipulation to manually configure the device.
In a nutshell it is saying:
Run as an 8 Bit Timer, set the clock source as FOSC/4 (which is the instruction cycle time or OSC divided by 4) with a prescale factor of 1:32. That was chosen because the clock speed of the PIC16F18855 in this instance is 32MHz. Use a lower ratio if your target device is running at a lower speed. The aim here is that the Timer0 overflows every 1.024 ms.
That timing is sufficiently accurate for most purposes and is only a couple of a percent out but as the PIC supports it I preloaded the Timer with the value 0xF9 which then generates an accurate 1ms overflow, this value is calculated from an equation in the datasheet but if you adjust the prescaler the value to suit your MCU speed it shold be a valid constant for all PIC's. You may also safely leave it out both here and where it is re-initialised in the ISR. It will reduce timing acuracy slightly but will improve portability.
In the the Interrupt Service Routine I re-initialize the timer (it will have overflowed to 0xFF) by setting it to the value 0xFA rather than 0xF9, that is to compensate for the time elapsed between the the actual overflow and the MCU entering the ISR. It is only a thumb suck, not based on any mathematical calculation, but I did confirm it by toggling a PIN in the ISR and read a frequency of 500 Hz which corresponds to out 1ms, with the value at 0xF9 I was measuring 508 Hz, much too fast.
Anobium has just sent me the correct syntax to use the GCBasic inbuilt Timer configuration. I have tested it and it works fine, so here is an updated copy of the second example that should work on any PIC:
' Millis() - Flash Two at a Time '
'
' C.A.Roper / Anobium '
' 05/07/2016 '
'
' ----- Configuration '
'Chip Settings. '
#chip 16f18855,32
#config RSTOSC_HFINT32
' TMR0_Initialize() '
InitTimer0(Osc, (TMR0_FOSC4 + PRE0_32) , POST0_1)
SetTimer(0, 0xF900) ' Preload Count for 1ms '
StartTimer 0
' Add a handler for the interrupt '
On Interrupt Timer0Overflow Call TMR0_ISR
#define LED1 PortA.0
#define LED2 PortA.1
#define LED3 PortA.2
#define LED4 PortA.3
'the following variables are long because the time, measured in'
'miliseconds, will quickly become a larger number than can be'
'stored in a byte or an integer. '
Dim t0_millis As long ' Critical - Used by the ISR'
' working copy '
Dim curMillis As Long
' task 1 variables '
Dim State1
Dim OnTime1 As Long
Dim OffTime1 As Long
Dim OldMillis1 As Long
' task 2 variables '
Dim State2
Dim OnTime2 As Long
Dim OffTime2 As Long
Dim OldMillis2 As Long
'Setup '
LED1 = 0 ' LED1 Initialy Off '
LED4 = 0 ' LED4 Initialy Off '
Dir LED1 Out
Dir LED4 Out
OnTime1 = 250 ' Time On in mS '
OffTime1 = 750 ' Time Off in mS '
OnTime2 = 330 ' Time On in mS '
OffTime2 = 400 ' Time Off in mS '
'Main loop '
Do
curMillis = millis() ' Read Ellapsed Time '
if State1 = On Then
if (curMillis - OldMillis1) >= OnTime1 Then
State1 = Off ' Change State '
OldMillis1 = curMillis ' save current time '
LED1 = State1 ' Update LED '
end if
else
if (curMillis - OldMillis1) >= OffTime1 Then
State1 = On ' Change State '
OldMillis1 = curMillis ' save current time '
LED1 = State1 ' Update LED '
end if
end if
if State2 = On Then
if (curMillis - OldMillis2) >= OnTime2 Then
State2 = Off ' Change State '
OldMillis2 = curMillis ' save current time
LED4 = State2 ' Update LED '
end if
else
if (curMillis - OldMillis2) >= OffTime2 Then
State2 = On ' Change State '
OldMillis2 = curMillis ' save current time
LED4 = State2 ' Update LED '
end if
end if
Loop
function millis() as long
' disable interrupts while we read t0_millis '
IntOff
millis = t0_millis
IntOn
end Function
Sub TMR0_ISR()
SetTimer(0, 0xFA00) ' reset count for 1ms adj for isr time'
t0_millis += 1
End Sub
End
I hope that Anobium’s code modifications, along with the explanation above, helps clarify it better.
Cheers
Chris
Last edit: Chris Roper 2016-07-05
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
After looking at this for a while, I was wondering if this method could be used somehow instead of using the wait statement. For example, if I want to flash an led and vary the time between flashes using a pot, normally I would do something like this... (This is a very simple example of what I am talking about)
do
turn led on
wait (read pot on A/D pin * 3) ms
turn led off
wait (read pot on A/D pin * 3) ms
loop
However, if the loop is in the wait state, and I turn the pot, it won't respond until the wait is done. How could you use this technique to vary delays using a pot to determine the actual delay? Or is there another way to do something like this to avoid the wait statement?
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
Looking some more (using your code from the first example) would you do something like this...
'Setup
LED = 0
'interval = 1000 ' interval at which to blink (milliseconds) <<<< move this line
Dir LED Out
'Main loop
Do
interval = (Read A/D pin with pot) <<<<<<<< Read the pot here to determine the delay????
currentMillis = millis() ' Read Ellapsed Time
if (currentMillis - previousMillis) > interval then
previousMillis = currentMillis ' save current time
LED =! LED ' if the LED is off turn it on and vice-versa
end if
Loop
function millis() as long
' disable interrupts while we read t0_millis or we might get an
' inconsistent value (e.g. in the middle of a write to t0_millis)
IntOff
millis = t0_millis
IntOn
end Function
Sub TMR0_ISR()
TMR0H = 0xFA ' TMR0H 250; Period 1ms
t0_millis += 1
End Sub
Last edit: viscomjim 2016-07-13
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
That looks OK at a quick glance but I would make the Interval a bit longer.
The ADC value will probably be in the range of 0 to 255 so you will only see a discernible flash at the extreme end of the pot's travel.
try:
interval = 10 * ValADC
I assume you still intend to flesh out the line:
interval = (Read A/D pin with pot)
I don't have the board on my desk at the moment so can't create any test code but let us know haow yours works out.
Cheers
Chris
Last edit: Chris Roper 2016-07-13
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
In the topic "algorithm for quadrature encoder", elsewhere in the forum, the subject of Interrupts and more importantly, avoiding the use thereof, was raised. It got me thinking about a useful function that Arduino has and most other platforms don’t. The Millis() Function.
Why is it of interest? Well it keeps count of the number of Milliseconds elapsed since the MCU was last powered up or reset. Because it is stored as a 32 Bit value it will only roll over to zero every 50 Days. That allows us to measure very short or very long periods. Certainly adequate for most of our timing needs.
How is it relevant? As it uses a timer and generates an interrupt it is a good example of both using Timers and an introduction to Interrupts, it also gives us a valuable tool for our Great Cow Basic Libraries.
In the near future I will be writing this up as a tutorial and will also be looking at porting the code between this newer representative of the PIC 16F family and some of the Older PIC 16F familys. In the meantime, to whet your appetite, here is an example of using Millis() to Flash an LED without locking up the MCU in a tight loop.
As you can see the interrupt routine is nothing to be scared of and only consists of two parts:
1) Initialise the Interrupt Handling
2) Handling the Interrupt
Because Millis() is Interrupt driven the MCU can continue running other code in the main loop, such as reading inputs, and the LED will still flash at the rate set by interval.
Cheers
Chris
Last edit: Chris Roper 2016-07-05
At first glance,the above does not seem to be very interesting. It looks like just a more complicated way to blink a LED. However,it illustrates a very important concept known as a State Machine. Instead of relying on "wait x ms" to time the LED Flash, it remembers the current state of the LED and the last time it changed. On each pass through the loop, it looks at the millis() clock to see if it is time to change the state of the LED again.
Let’s look at a slightly more interesting variant that has an LED with different on-time and off-time, and a second LED that flashes at completely different rates.
Try it on your XPress Evaluation board and then try altering the Timeing or add in the Other Two LED's, It could be the basisis of your next string of Party Lights.
Cheers
Chris
Chris, these are excellent examples. I would actually like to implement this in a project using a different pic, but here is where I (and probably any new user) get totally confused. I'm sure that reading the data sheets would help, but it is a bit intimidating. Could you maybe explain a bit more what is going on with the following code and does this apply to any pic with a timer?
and what does this do? I see that it is loading 0xFA into TMR0H but why does it need to be done and how did you pick FA?
Sorry for the noob questions, but I would really like to understand this better.
Thanks again for your efforts!
Thanks for the questions, they are points that definitely need addressing and will be covered in detail in the Tutorial series when I write it :)
In the interim though here is my reasoning.
I chose Timer0 as most PIC and AVR devices have a Timer0 module and because it is only an 8 Bit timer it is often available for use, as programmers tend to gravitate towards the higher resolution 16 bit timers for most functions. The problem in this instance is that the PIC16F18855 has a very sophisticated new Timer Module as Timer0 and as of writing the Builtin “InitTimer0” function did not support it so I resorted to inline register manipulation to manually configure the device.
In a nutshell it is saying:
Run as an 8 Bit Timer, set the clock source as FOSC/4 (which is the instruction cycle time or OSC divided by 4) with a prescale factor of 1:32. That was chosen because the clock speed of the PIC16F18855 in this instance is 32MHz. Use a lower ratio if your target device is running at a lower speed. The aim here is that the Timer0 overflows every 1.024 ms.
That timing is sufficiently accurate for most purposes and is only a couple of a percent out but as the PIC supports it I preloaded the Timer with the value 0xF9 which then generates an accurate 1ms overflow, this value is calculated from an equation in the datasheet but if you adjust the prescaler the value to suit your MCU speed it shold be a valid constant for all PIC's. You may also safely leave it out both here and where it is re-initialised in the ISR. It will reduce timing acuracy slightly but will improve portability.
In the the Interrupt Service Routine I re-initialize the timer (it will have overflowed to 0xFF) by setting it to the value 0xFA rather than 0xF9, that is to compensate for the time elapsed between the the actual overflow and the MCU entering the ISR. It is only a thumb suck, not based on any mathematical calculation, but I did confirm it by toggling a PIN in the ISR and read a frequency of 500 Hz which corresponds to out 1ms, with the value at 0xF9 I was measuring 508 Hz, much too fast.
In an older PIC you should be able to use the GCBasic Built in InitTimer0 function here:
http://gcbasic.sourceforge.net/help/_inittimer0.html
But if you get stuck let me know the #chip def you use and the clock speed and I will calculate the values to match your device.
Cheers
Chris
Last edit: Chris Roper 2016-07-05
Anobium has just sent me the correct syntax to use the GCBasic inbuilt Timer configuration. I have tested it and it works fine, so here is an updated copy of the second example that should work on any PIC:
I hope that Anobium’s code modifications, along with the explanation above, helps clarify it better.
Cheers
Chris
Last edit: Chris Roper 2016-07-05
After looking at this for a while, I was wondering if this method could be used somehow instead of using the wait statement. For example, if I want to flash an led and vary the time between flashes using a pot, normally I would do something like this... (This is a very simple example of what I am talking about)
do
turn led on
wait (read pot on A/D pin * 3) ms
turn led off
wait (read pot on A/D pin * 3) ms
loop
However, if the loop is in the wait state, and I turn the pot, it won't respond until the wait is done. How could you use this technique to vary delays using a pot to determine the actual delay? Or is there another way to do something like this to avoid the wait statement?
Looking some more (using your code from the first example) would you do something like this...
Last edit: viscomjim 2016-07-13
That looks OK at a quick glance but I would make the Interval a bit longer.
The ADC value will probably be in the range of 0 to 255 so you will only see a discernible flash at the extreme end of the pot's travel.
try:
I assume you still intend to flesh out the line:
I don't have the board on my desk at the moment so can't create any test code but let us know haow yours works out.
Cheers
Chris
Last edit: Chris Roper 2016-07-13