I have an odd issue with initialisation at variable declaration.
The code sits in a timer interrupt routine. It sends some lines of text to a 4-line LCD without issue, but then there is a check to see if a value has changed since the last interrupt, before writing the 4th LCD line. Change is via user pressing buttons).
So, i declared and initialized the old value to 10. The current is calculated to 0.25 on the first pass (remains at 0.25 until a button is pressed). Using the serial terminal shows 10 and 0.25 on first pass, then 0.25 and 0.25 afterward as the old value is now the one it entered the ISR with, stored in new value.
If i use
float OldmmPerSecond = 10;
Then the display line does not update until i press a button and change the new value to something other than 0.25
if (OldmmPerSecond != mmPerSecond) //update display only if speed has changed
{
lcd.setCursor(0,3);
lcd.print("Speed is ");
lcd.setCursor(9,3);
lcd.print(mmPerSecond);
if (mmPerSecond < 10)
{
lcd.setCursor(13,3);
}
else
{
lcd.setCursor(14,3);
}
lcd.print("mm/s ");
OldmmPerSecond = mmPerSecond; // remeber old value -this si to stop refreshing display on evry interrupt and casuing it to flash
}
Any help appreciated, although using the setup works fine, i should be able to initialise at declaration
not shown in the context of a complete program that compiles and runs, even if it doesn’t yet do what you want.
But it will also be pointed out that having much code at all in an interrupt, if you even need to use interrupts at all, is a bad idea.
Set a flag in the interrupt routine.
In the non-interrupt part of your sketch, check that flag and if set, do the things you’ve now crammed into the interrupt service routine if it is set.
Clear the flag as the last step of responding to it, right there after you’ve finished whatever it was.
If that makes no sense, do some research until it does.
Otherwise using interrupts will be more trouble than you can make them worth.
And your research may lead you to conclude that using the interrupt mechanism is unnecessary.
I have a lot of micro experience and am very comfortable with Interrupts and have not used a flag approach as the interrupt occurs every 750mS and the ISR completes in a tiny fraction of that time, so there will never be an issue with ISR code length. This is also the reason i did not turn the interrupt off when entering the ISR and on again before leaving.
The code works fine if I initialise in the Setup() function, but not when i initialise it at declaration.
I am not a particularly experienced C programmer but expected the initialisation at declaration to work and thus posted my question as i want to find out if the issue is the Arduino language, the use of it on M4 or my lack of familiarity with C
As both people;e asked for it, here is the working code
// for new aluminium drive section using 1600 microstep on MSD556 and 2:1 gearing
// Mill Lead is 2mm
// For 5mm/s need to run at 8kHz, but with 2:1 gearing need 16kHz
// Calculations for clock settings and PWM Hz is in spreadsheet PWM calculator for Arduino Express M4.xlxs
#include <Arduino.h>
#include "SAMD51_InterruptTimer.h"
#include <LiquidCrystal_I2C.h>
float dblFreq = 200;
float Tcount = 0;
float mmPerSecond = 0;
float OldmmPerSecond; //user to prevent updating dispaly on every interrupt - only update on a change of speed
LiquidCrystal_I2C lcd(0x27,20,4); // set the LCD address to 0x27
void setup()
{
// 50% duty-cycle PWM signal with 16-bit resolution on A1:
MCLK->APBAMASK.reg |= MCLK_APBAMASK_TC0; // Activate timer TC0
// Set up the generic clock (GCLK7) used to clock timers
GCLK->GENCTRL[7].reg = GCLK_GENCTRL_DIV(1) | // No division on the 48MHz clock source
GCLK_GENCTRL_IDC | // Set the duty cycle to 50/50 HIGH/LOW
GCLK_GENCTRL_GENEN | // Enable GCLK7
GCLK_GENCTRL_SRC_DFLL; // Generate from 48MHz DFLL clock source
while (GCLK->SYNCBUSY.bit.GENCTRL7); // Wait for synchronization
GCLK->PCHCTRL[9].reg = GCLK_PCHCTRL_CHEN | // Enable perhipheral channel
GCLK_PCHCTRL_GEN_GCLK7; // Connect generic clock 7 to TC0
PORT->Group[g_APinDescription[A1].ulPort].PINCFG[g_APinDescription[A1].ulPin].bit.PMUXEN = 1; // Enable the peripheral multiplexer on pin A1
PORT->Group[g_APinDescription[A1].ulPort].PMUX[g_APinDescription[A1].ulPin >> 1].reg |= PORT_PMUX_PMUXO(4); // Set A1 the peripheral multiplexer to peripheral E(4): TC0, Channel 1
TC0->COUNT16.CTRLA.reg = TC_CTRLA_PRESCALER_DIV4| // Set prescaler to 4, giving (48MHz/GCLK_GENCTRL_DIV(1))/4 = 12MHz
TC_CTRLA_PRESCSYNC_PRESC | // Set the reset/reload to trigger on prescaler clock
TC_CTRLA_MODE_COUNT16; // Set the counter to 16-bit mode
TC0->COUNT16.WAVE.reg = TC_WAVE_WAVEGEN_MPWM; // Set-up TC0 timer for Match PWM mode (MPWM)
//frequency is f_GCLK_TC/N(TOP+1) where TOP is TC0->COUNT16.CC[0].reg (Tcount here) and f_GCLK_TC is the peripheral clock frequency (48MHz), N is prescaler value (1,2 ,4,8,16,32...)
TC0->COUNT16.CC[0].reg = Tcount; // Use CC0 register as TOP value
while (TC0->COUNT16.SYNCBUSY.bit.CC0); // Wait for synchronization
TC0->COUNT16.CC[1].reg = Tcount/2; // Set the duty cycle to 50% as we need a squarewave output of varying Hz
while (TC0->COUNT16.SYNCBUSY.bit.CC1); // Wait for synchronization
TC0->COUNT16.CTRLA.bit.ENABLE = 1; // Enable timer TC0
while (TC0->COUNT16.SYNCBUSY.bit.ENABLE); // Wait for synchronization
TC.startTimer(750000, myISR); // Call myISR() every 0.75 seconds
pinMode(5, INPUT); //Manual/Drive
pinMode(6, INPUT); //Direction
pinMode(10, INPUT); //1mm/s increase
pinMode(11, INPUT); //1mm/s decrease
pinMode(12, INPUT); //0.1mm/s increase
pinMode(13, INPUT); //0.11mm/s decrease
pinMode(24, OUTPUT); //Direction
lcd.init(); // initialize the lcd
lcd.backlight();
delay(50);
lcd.setCursor(0,0);
lcd.print(" X-AXIS POWER DRIVE");
lcd.setCursor(0,1);
lcd.print("Manaual mode");
lcd.setCursor(0,2);
lcd.print("Direction = Right");
OldmmPerSecond = 10; //needs to be different to mmPerSecond for first pass and initialising at declaration did not work
}
//timer 3 interrupt routine using the Dennis_van_Gils interrupt library with info in Evernote
//
void myISR() {
if (digitalRead(5) ) /// Are we in manual or power drive?
{
lcd.setCursor(0,1);
lcd.print("Manaual drive mode "); //clear out old value
}
else
{
lcd.setCursor(0,1);
lcd.print("Power drive mode "); //clear out old value
}
if (digitalRead(6) ) /// Are we in manual or auto?
{
lcd.setCursor(0,2);
lcd.print("Direction = Right "); //clear out old value
digitalWrite(24,HIGH);
}
else
{
lcd.setCursor(0,2);
lcd.print("Direction = Left "); //clear out old value
digitalWrite(24,LOW);
}
if (digitalRead(10)) /// don't let it go above approx. 10mm/s
{
dblFreq = dblFreq + 800;
if (dblFreq >8500)
{
dblFreq=8500;
}
}
else if (digitalRead(11)) /// don;t let it go below 1mm/s
{
dblFreq = dblFreq - 800;
if (dblFreq <800)
{
dblFreq=800;
}
}
else if (digitalRead(12)) /// don't let it go above approx. 10 mm/s
{
dblFreq = dblFreq + 80;
if (dblFreq >8500)
{
dblFreq=8500;
}
}
else if (digitalRead(13)) // keep it abpve 0.24mm/s. whic is 400hz
{
dblFreq = dblFreq - 80;
if (dblFreq <400)
{
dblFreq=160;
}
}
mmPerSecond = dblFreq/800; // 800 gives 1mm/s as 1600 pulses per rev and 2mm lead on screw
TC0->COUNT16.CC[0].reg = Tcount; // Use CC0 register as TOP value to update the frequency setting
while (TC0->COUNT16.SYNCBUSY.bit.CC0); // Wait for synchronization
TC0->COUNT16.CC[1].reg = Tcount/2; // Set the duty cycle to 50% (CC1 half of CC0)
while (TC0->COUNT16.SYNCBUSY.bit.CC1); // Wait for synchronization
Tcount = (12000000/dblFreq)-1; //update value for desired Hz - using GCLK_GENCTRL_DIV. TC_CTRLA_PRESCALER_DIV2 gives TOP = (48000000/1)/4. and Hz = TOP/(Tcount +1)
if (OldmmPerSecond != mmPerSecond) //update display only if speed has changed
{
lcd.setCursor(0,3);
lcd.print("Speed is ");
lcd.setCursor(9,3);
lcd.print(mmPerSecond);
if (mmPerSecond < 10)
{
lcd.setCursor(13,3);
}
else
{
lcd.setCursor(14,3);
}
lcd.print("mm/s ");
OldmmPerSecond = mmPerSecond; // remeber old value -this si to stop refreshing display on evry interrupt and casuing it to flash
}
}
void loop() {}
this version with the initialisation at declaration and the last line in startup() where i initialised the variable to 10, is now removed, and the initialoisation is at declaration
// Board is Adafruit Feather M4
// for new aluminium drive section using 1600 microstep on MSD556 and 2:1 gearing
// Mill Lead is 2mm
// For 5mm/s need to run at 8kHz, but with 2:1 gearing need 16kHz
// Calculations for clock settings and PWM Hz is in spreadsheet PWM calculator for Arduino Express M4.xlxs
#include <Arduino.h>
#include "SAMD51_InterruptTimer.h"
#include <LiquidCrystal_I2C.h>
float dblFreq = 200;
float Tcount = 0;
float mmPerSecond = 0;
float OldmmPerSecond = 10; //user to prevent updating dispaly on every interrupt - only update on a change of speed
LiquidCrystal_I2C lcd(0x27,20,4); // set the LCD address to 0x27
void setup()
{
// 50% duty-cycle PWM signal with 16-bit resolution on A1:
MCLK->APBAMASK.reg |= MCLK_APBAMASK_TC0; // Activate timer TC0
// Set up the generic clock (GCLK7) used to clock timers
GCLK->GENCTRL[7].reg = GCLK_GENCTRL_DIV(1) | // No division on the 48MHz clock source
GCLK_GENCTRL_IDC | // Set the duty cycle to 50/50 HIGH/LOW
GCLK_GENCTRL_GENEN | // Enable GCLK7
GCLK_GENCTRL_SRC_DFLL; // Generate from 48MHz DFLL clock source
while (GCLK->SYNCBUSY.bit.GENCTRL7); // Wait for synchronization
GCLK->PCHCTRL[9].reg = GCLK_PCHCTRL_CHEN | // Enable perhipheral channel
GCLK_PCHCTRL_GEN_GCLK7; // Connect generic clock 7 to TC0
PORT->Group[g_APinDescription[A1].ulPort].PINCFG[g_APinDescription[A1].ulPin].bit.PMUXEN = 1; // Enable the peripheral multiplexer on pin A1
PORT->Group[g_APinDescription[A1].ulPort].PMUX[g_APinDescription[A1].ulPin >> 1].reg |= PORT_PMUX_PMUXO(4); // Set A1 the peripheral multiplexer to peripheral E(4): TC0, Channel 1
TC0->COUNT16.CTRLA.reg = TC_CTRLA_PRESCALER_DIV4| // Set prescaler to 4, giving (48MHz/GCLK_GENCTRL_DIV(1))/4 = 12MHz
TC_CTRLA_PRESCSYNC_PRESC | // Set the reset/reload to trigger on prescaler clock
TC_CTRLA_MODE_COUNT16; // Set the counter to 16-bit mode
TC0->COUNT16.WAVE.reg = TC_WAVE_WAVEGEN_MPWM; // Set-up TC0 timer for Match PWM mode (MPWM)
//frequency is f_GCLK_TC/N(TOP+1) where TOP is TC0->COUNT16.CC[0].reg (Tcount here) and f_GCLK_TC is the peripheral clock frequency (48MHz), N is prescaler value (1,2 ,4,8,16,32...)
TC0->COUNT16.CC[0].reg = Tcount; // Use CC0 register as TOP value
while (TC0->COUNT16.SYNCBUSY.bit.CC0); // Wait for synchronization
TC0->COUNT16.CC[1].reg = Tcount/2; // Set the duty cycle to 50% as we need a squarewave output of varying Hz
while (TC0->COUNT16.SYNCBUSY.bit.CC1); // Wait for synchronization
TC0->COUNT16.CTRLA.bit.ENABLE = 1; // Enable timer TC0
while (TC0->COUNT16.SYNCBUSY.bit.ENABLE); // Wait for synchronization
TC.startTimer(750000, myISR); // Call myISR() every 0.75 seconds
pinMode(5, INPUT); //Manual/Drive
pinMode(6, INPUT); //Direction
pinMode(10, INPUT); //1mm/s increase
pinMode(11, INPUT); //1mm/s decrease
pinMode(12, INPUT); //0.1mm/s increase
pinMode(13, INPUT); //0.11mm/s decrease
pinMode(24, OUTPUT); //Direction
lcd.init(); // initialize the lcd
lcd.backlight();
delay(50);
lcd.setCursor(0,0);
lcd.print(" X-AXIS POWER DRIVE");
lcd.setCursor(0,1);
lcd.print("Manaual mode");
lcd.setCursor(0,2);
lcd.print("Direction = Right");
}
//timer 3 interrupt routine using the Dennis_van_Gils interrupt library with info in Evernote
//
void myISR() {
if (digitalRead(5) ) /// Are we in manual or power drive?
{
lcd.setCursor(0,1);
lcd.print("Manaual drive mode "); //clear out old value
}
else
{
lcd.setCursor(0,1);
lcd.print("Power drive mode "); //clear out old value
}
if (digitalRead(6) ) /// Are we in manual or auto?
{
lcd.setCursor(0,2);
lcd.print("Direction = Right "); //clear out old value
digitalWrite(24,HIGH);
}
else
{
lcd.setCursor(0,2);
lcd.print("Direction = Left "); //clear out old value
digitalWrite(24,LOW);
}
if (digitalRead(10)) /// don't let it go above approx. 10mm/s
{
dblFreq = dblFreq + 800;
if (dblFreq >8500)
{
dblFreq=8500;
}
}
else if (digitalRead(11)) /// don;t let it go below 1mm/s
{
dblFreq = dblFreq - 800;
if (dblFreq <800)
{
dblFreq=800;
}
}
else if (digitalRead(12)) /// don't let it go above approx. 10 mm/s
{
dblFreq = dblFreq + 80;
if (dblFreq >8500)
{
dblFreq=8500;
}
}
else if (digitalRead(13)) // keep it abpve 0.24mm/s. whic is 400hz
{
dblFreq = dblFreq - 80;
if (dblFreq <400)
{
dblFreq=160;
}
}
mmPerSecond = dblFreq/800; // 800 gives 1mm/s as 1600 pulses per rev and 2mm lead on screw
TC0->COUNT16.CC[0].reg = Tcount; // Use CC0 register as TOP value to update the frequency setting
while (TC0->COUNT16.SYNCBUSY.bit.CC0); // Wait for synchronization
TC0->COUNT16.CC[1].reg = Tcount/2; // Set the duty cycle to 50% (CC1 half of CC0)
while (TC0->COUNT16.SYNCBUSY.bit.CC1); // Wait for synchronization
Tcount = (12000000/dblFreq)-1; //update value for desired Hz - using GCLK_GENCTRL_DIV. TC_CTRLA_PRESCALER_DIV2 gives TOP = (48000000/1)/4. and Hz = TOP/(Tcount +1)
if (OldmmPerSecond != mmPerSecond) //update display only if speed has changed
{
lcd.setCursor(0,3);
lcd.print("Speed is ");
lcd.setCursor(9,3);
lcd.print(mmPerSecond);
if (mmPerSecond < 10)
{
lcd.setCursor(13,3);
}
else
{
lcd.setCursor(14,3);
}
lcd.print("mm/s ");
OldmmPerSecond = mmPerSecond; // remeber old value -this si to stop refreshing display on evry interrupt and casuing it to flash
}
}
void loop() {}
Declaring a variable as volatile tells the compiler that the variable might change at any time so it needs to be available all of the time, even in an ISR, and to be put in a memory location not in a processor register.
The reason for this is that when in an ISR the registers may get used for other purposes as they are very fast to access, so on entry to an ISR their current values are hidden from view of the program
As a result, if an ISR updates the value of a variable previously saved in a register then when the original value in the register is restored on exit from the ISR it will not have the updated value
Hence, variables updated in ISRs should be declared as volatile as their value held in a memory location can safely be updated and will be available on exit from the ISR
There are more formal ways to describe this process but this one makes sense to me
The Arduino is programmed C/C++, although great lengths have been taken to make it look like it mightn’t be.
Look into the nightmare which the IDE preprocessor can be… IMO it does more damage than good, but that’s the charter of Arduino, programming made easy.
And I see you OK w/ interrupts, no way to know but a great majority of inquiries here involving interrupts are usually problems that can, and arguably should be solved without.
What you say makes sense to me to, but i don't think this si the issue as the variable works fine in the code except for on the first run through the ISR
Using float OldmmPerSecond; and mmPerSecond has 0.25 at startup
The serial monitor shows OldmmPerSecond = 10 and mmPerSecond = 0.25 on the first pass, then 0.25 and 0.25 on the second and subsequent passes as it should (old is then equal to current value until i press a button).
However, the LCD is not updated after if (OldmmPerSecond != mmPerSecond) despite containing 10 and 0.25
Using float OldmmPerSecond; and mmPerSecond has 0.25 at startup
and then in setup() using OldmmPerSecond = 10;
It works perfectly and updates the display on the first run through the ISR
I should stress that there is no problem with my ISRs or interrupt generation, both run perfectly and exactly as intended - as long as i initialize after the variable declaration. I really do not understand why anyone would not use interrupts and take the laod off the core processor
I suspected it was a bug in the IDE.
With respect to interrupts or not, i guess it depends on the application. I have been writing code for industrial control systems since the early '90s (originally designing single board computers too) and in my experience, particularly when writing in assembler, interrupts are essential, not optional in most cases and polling is not a realistic and viable alternate in time-critical or time-dependent systems.
This bit of code is not for work, it is me playing with a new toy (32-bit Arduino) for my milling machine in the garage
I looked at circuit python and absolutely hate it (I/O manipulation 100x slower than C?????), so decided to move to C on the Adafruit boards (love these boards) and that is definitely an area where my knowledge needs to grow a LOT!!
We live in an interesting time and i am jealous as i approach retirement of all the wonderful kit available out there, when i started i had to design a board with 1MHz Z80, RAM, EPROM (UV erasable), peripheral chips, and build it myself before i could start programming.
Now I can buy beautiful 32-bit boards for the price of a few Starbucks coffees