I am trying to make a debounce circuit using SysTick on a Cortex M0 Feather Adalogger. I've done a variety of methods that work, but the file writing overhead makes me lose a couple magnetic reed switch based cycles and an RC filter is undesirable.
I can get by without millis() and micros() in my code, so i thought i could reprogram the Cortex-m0 SysTick to check after 1 mS has gone by (chatter is in the 600 nS range and event duration can be 20 mS or longer).
I am attaching a test program i put together from a variety of sources. The idea is that after every 250 mS sysTickCounter will get incremented. However, it doesn't work. I've dug into the files and sysTickHook() should work to increment the counter. I've tried SysTick_Handler and SysTick_DefaultHandler. During compile, SysTick_Handler states it is already defined (as it should).
The sysTick values are going into the registers ok and are decrementing.
Any ideas what i am missing?
[code]
#define versionInfo "SysTick quick test v0.1 sn A6 20230828 1636"
#include "sdios.h"
// SERIAL PORT #DEFINES AND STREAMS NOTE: serial is needed for SDFat, but can't use or locks up w/o serial port
#define serialPortSpeed 9600 // make this a #define 115200 is fast. Use default of 9600.
unsigned int start_time, stop_time, cycle_count = 0;
volatile int sysTickCounter = 0;
void setup() {
while (! Serial); // Wait until Serial is ready--
Serial.begin(serialPortSpeed);
Serial.println(versionInfo); // only print to serial port if enabled
SysTick_Initialize ( 0xB71AFF ); // 0xBB7F is 1 mS & 0xB71AFF is 250mS for the 48 MHz Adalogger clock
}
void loop () {
int gee = 0; // dummy loop counter to eat up a little processor time
start_time = SysTick->VAL; // Obtains the start time
Serial.print(" SysTick begin is ");
Serial.println(SysTick->VAL );
for (int i = 0; i++; i <1500000000){ // slow serial outputs down a bit. Cortex ints go to 2^31 - 1; basically a delay without using micros/millis.
gee = gee - i;
}
stop_time = SysTick->VAL; // Obtains the stop time
cycle_count = start_time - stop_time; // Calculates the number of CPU clock cycles taken
Serial.print( "sysTickCounter is " );
Serial.println(sysTickCounter);
Serial.print( "Cycle_count is " );
Serial.println(cycle_count);
Serial.print(" SysTick end is ");
Serial.println(SysTick->VAL);
Serial.println(""); // give a little space. Could have used cout but too lazy
}
// void SysTick_Handler (void) { // SysTick interrupt service routine -- gets an error because one already exists
// void SysTick_DefaultHandler (void) { // SysTick default interrupt service routine-- can use this but nothing happens to sysTickCounter
// bool sysTickHook( ) { if sysTickHook returns true, then SysTick_DefaultHandler is NOT called
bool sysTickHook( void ){ // if sysTickHook returns true, then SysTick_DefaultHandler is NOT called
sysTickCounter++; // increment sysTick counter (volatile int)
return( true );
}
void SysTick_Initialize (uint32_t ticks) {
SysTick->CTRL = 0; // Disable SysTick totally
SysTick->LOAD = ticks; // Set the Reload value
// Set interrupt priority of SysTick to least urgency (i.e., largest priority value)
// NVIC_SetPriority (SysTick_IRQn, (1<<__NVIC_PRIO_BITS) - 1); // didn't work
SysTick->VAL = 0; // Reset the SysTick counter value (must do before starting a systick or could be old value w/ undetermined results)
SysTick->CTRL |= 0x7; // Enables the SysTick ISR. Use processor clock, enable interrupts, and enable timer (b2=CLKSRC, b1=TICint, b0=EN)
}
[/code]
That loop will almost certainly be deleted by the compiler, as it does nothing and has no side effects, so your sysTickCounter will probably be the same after the loop as before.
try making gee into a "volatile"
Edit: better make i the volatile instead of gee. For some reason making gee volatile seems uneffective.
Thank you for responding.
The question is in the beginning paragraphs and rereading makes it not obvious; as in the comments 'gee' is a dummy loop counter.
Let me see if i can explain more clearly.
For a more complex program, i wanted to see if i could make a custom SysTick based interrupt. I don't need millis() or micros() so i want to see if it is possible to use a SysTick interrupt instead of hardware or a more complex delay based debounce. Per Cortex documentation, this is entirely possible. Putting volatile sysTickCounter inside sysTickHook() should work per the arduino cortex header files. However, the SysTick interrupt does not seem to be happening.
The count down initialization works from the serial debug code output (count down works & wraps around to be reinitialized).
I am either
A. Messing up the interrupt initialization,
B. Stepping on some other code (note that none of the timer headers are #included), or
C. I've misread/misinterpreted the documentation.
It has been a while since i used an stm32 device but when i did I used the SysTick_Handler which is the systick interrupt handler. All I did was increment a counter variable in the interrupt so if you want a non-blocking delay you can use it like (for example)
if (switch == 1)
start_time = mycounter;
if (mycounter - start_time > 20) { //switch has been pressed for 20 tick interrupts
//do something
//reset start_time
}
here is a snippet of the code in stm32f0xx_it.c
/* USER CODE BEGIN 0 */
extern uint32_t mycounter;
/* USER CODE END 0 */
/
/**
* @brief This function handles System tick timer.
*/
void SysTick_Handler(void)
{
/* USER CODE BEGIN SysTick_IRQn 0 */
mycounter++;
/* USER CODE END SysTick_IRQn 0 */
/* USER CODE BEGIN SysTick_IRQn 1 */
/* USER CODE END SysTick_IRQn 1 */
}
Exactly! But i can't get it to work for my Feather M0 Adalogger!
When i try to use "void SysTick_Handler (void)" instead of in my code instead of "bool sysTickHook( void )", i get a "c:508: multiple definition of `SysTick_Handler';" error.
Here is the full error
c:/patr/arduinodata/packages/adafruit/tools/arm-none-eabi-gcc/9-2019q4/bin/../lib/gcc/arm-none-eabi/9.2.1/../../../../arm-none-eabi/bin/ld.exe: C:\Users\RICHAR~1\AppData\Local\Temp\arduino_build_977759/core\core.a(cortex_handlers.c.o): in function SysTick_Handler': C:\patr\ArduinoData\packages\adafruit\hardware\samd\1.7.13\cores\arduino/cortex_handlers.c:508: multiple definition of SysTick_Handler'; sketch\patr_sysTickTimerTest.ino.cpp.o:C:\patr\Arduino\patrCode\patr_sysTickTimerTest/patr_sysTickTimerTest.ino:44: first defined here
collect2.exe: error: ld returned 1 exit status
exit status 1
Error compiling for board Adafruit Feather M0 (SAMD21).
I am pasting the code again but with SysTick_Handler instead of sysTickHook:
[code]
#define versionInfo "SysTick quick test v0.1a sn A6 20230828 1636"
#include "sdios.h"
// SERIAL PORT #DEFINES AND STREAMS NOTE: serial is needed for SDFat, but can't use or locks up w/o serial port
#define serialPortSpeed 9600 // make this a #define 115200 is fast. Use default of 9600.
unsigned int start_time, stop_time, cycle_count = 0;
volatile int sysTickCounter = 0;
void setup() {
while (! Serial); // Wait until Serial is ready--
Serial.begin(serialPortSpeed);
Serial.println(versionInfo); // only print to serial port if enabled
SysTick_Initialize ( 0xB71AFF ); // 0xBB7F is 1 mS & 0xB71AFF is 250mS for the 48 MHz Adalogger clock
}
void loop () {
int gee = 0; // dummy loop counter to eat up a little processor time
start_time = SysTick->VAL; // Obtains the start time
Serial.print(" SysTick begin is ");
Serial.println(SysTick->VAL );
for (int i = 0; i++; i <1500000000){ // slow serial outputs down a bit. Cortex ints go to 2^31 - 1; basically a delay without using micros/millis.
gee = gee - i;
}
stop_time = SysTick->VAL; // Obtains the stop time
cycle_count = start_time - stop_time; // Calculates the number of CPU clock cycles taken
Serial.print( "sysTickCounter is " );
Serial.println(sysTickCounter);
Serial.print( "Cycle_count is " );
Serial.println(cycle_count);
Serial.print(" SysTick end is ");
Serial.println(SysTick->VAL);
Serial.println(""); // give a little space. Could have used cout but too lazy
}
// void SysTick_Handler (void) { // SysTick interrupt service routine -- gets an error because one already exists
// void SysTick_DefaultHandler (void) { // SysTick default interrupt service routine-- can use this but nothing happens to sysTickCounter
// bool sysTickHook( void ) { // if sysTickHook returns true, then SysTick_DefaultHandler is NOT called
// return( true ); // for sysTickHook ()
void SysTick_Handler (void){ // if sysTickHook returns true, then SysTick_DefaultHandler is NOT called
sysTickCounter++; // increment sysTick counter (volatile int)
}
void SysTick_Initialize (uint32_t ticks) {
SysTick->CTRL = 0; // Disable SysTick totally
SysTick->LOAD = ticks; // Set the Reload value
// Set interrupt priority of SysTick to least urgency (i.e., largest priority value)
// NVIC_SetPriority (SysTick_IRQn, (1<<__NVIC_PRIO_BITS) - 1); // didn't work
SysTick->VAL = 0; // Reset the SysTick counter value (must do before starting a systick or could be old value w/ undetermined results)
SysTick->CTRL |= 0x7; // Enables the SysTick ISR. Use processor clock, enable interrupts, and enable timer (b2=CLKSRC, b1=TICint, b0=EN)
}
[/code]
The compiler's saying that the Systick interrupt service routine: Systick_Handler(), has already been defined and implemented in the Arduino SAMD21 core code. It's located at line 508 of the "cortex_handlers.c" file:
I did find it but in multiple locations in slightly different flavors
In ..\packages\adafruit\hardware\samd\1.7.13\cores\arduino\cortex_handlers.c
i have on line 505-512:
which is why i successfully defined "void sysTickHook(void)" but it did not do anything.
Man, this is confusing. I am still learning to use the feather, but used to do A LOT of programming in old fashioned C. I sure appreciate the response-- this is closer to what i am, trying to figure out.
If you just require a timing delay, it might be easier to uses on of the SAMD21's spare TC timers: TC3, TC4 and TC5. Their interrupt service routine functions aren't used by the Arduino SAMD core code.
MartinL
Wow-- I had no idea they existed! That is just what i am looking for!
It is difficult to find things out-- there is no substitute for experience. I will dig into them.
MartinL, thank you very much. You have answered questions i have spent a long time researching.
I found your post in
and it is just what i was looking for. I am trying to reverse engineer how all of it works since this is a good framework with comments.
I do wish there was a single location where i could find the timer settings and bit masks with a description (such as TC_INTFLAG_OVF, GCLK_CLKCTRL_ID_TC4_TC5, and REG_TC4_INTENSET).
Some are in gclk.h, but they are pretty hard to decipher and the names don't appear to have an exact cross over to the Cortex-M0 manuals. Hopefully if i get to do another Arduino controller (this is my first), i'll learn more,