systick timer on SAMC - Page 1
Forum section: electronics / general

author: Simon
author_rank: Unknown
timestamp: January 04, 2023, 10:09:03 am
content: I'm trying to implement the systick timer on a samc for general program timing. I don't see a specific interrupt line for it in the ARM interrupt controller, is it always active anyway? I see there is a dummy handler for it in the header files and it has a bit in the register to enable an "exception request".

author: Rudolph Riedel
author_rank: Regular Contributor
timestamp: January 04, 2023, 10:31:30 am
content: There is SysTick_Config() in the CMSIS headers.https://github.com/RudolphRiedel/FT800-FT813/blob/5.x/examples/EVE_Test_SAMC21_EVE2-50G/main.c

author: Simon
author_rank: Unknown
timestamp: January 04, 2023, 12:28:10 pm
content: right, in there "somewhere". I see some code that sets the tick interval but that is all.

author: Rudolph Riedel
author_rank: Regular Contributor
timestamp: January 04, 2023, 12:39:16 pm
content: As I wrote, this is part of CMSIS:https://developer.arm.com/documentation/dui0646/b/BABIDIGI

author: wek
author_rank: Frequent Contributor
timestamp: January 04, 2023, 01:15:15 pm
content: SysTick interrupt is permanently enabled, see ARMv7-M Architecture Reference Manual, chapterB1.5 Armv7-M exception model. See ataradov's post below You can enable/disable SysTick counter, though, effectively enabling/disabling the related interrupt. The same applies to ARMv6-M (Cortex-M0/M0+)JW

author: ataradov
author_rank: Super Contributor
timestamp: January 04, 2023, 08:04:23 pm
content: SysTick interrupt can be enabled independently from the timer. CSR.TICKINT enables the interrupt and CSR.ENABLE enables the timer itself.The SysTick interrupt handler is in the first "core" group of the interrupts. You don't need to define it (have it be dummy) if CSR.TICKINT=0. And I assume your "exception request" is TICKINT.

author: westfw
author_rank: Super Contributor
timestamp: January 04, 2023, 09:29:36 pm
content: QuoteSystick is part of the ARM "core", so it doesn't go through the NVIC. Also it frequently won't show up in vendor documentation that only documents what the "core" considers "external interrupts." SysTick on an CM0 is always exception number 15 (and #16 is the first external interrupt.)I guess that makes it "permanently enabled" in the sense that you don't have to turn it on (and you can't change it's priority) in the NVIC. You just have to turn on the interrupt bit in SysTick itself.

author: ataradov
author_rank: Super Contributor
timestamp: January 04, 2023, 09:35:15 pm
content: You can change SysTick priority though SCB.SHPR3 register.

author: Simon
author_rank: Unknown
timestamp: January 05, 2023, 03:39:49 pm
content: Uh, yes I'm not done yet, the interrupt fires but then never again, naturally ARM documentation does not point you around, found this collection of registers:https://developer.arm.com/documentation/dui0497/a/cortex-m0-peripherals/system-control-block?lang=enwith a Interrupt Control and State Register. I'm still confused though as this is the old manual clearing of the interrupt to stop it constantly firing logic that I am going down but I actually have the opposite, it fire once and never again.Is there a place where I can see some of the code that the automatic code generator uses? I don't want to have to actually dable with the horrible mess that the configurator in mplabx is and I am using studio as it's less distracting but it feels like looking at examples may be more informative than trying to read a self generated manual.

author: voltsandjolts
author_rank: Unknown
timestamp: January 05, 2023, 04:48:10 pm
content: Are you sure you are writing the reload register SysTick->LOAD?I've never used SAMC but setup can't be too different from STM32...Code:volatile uint32_t systicks10ms;void SysTick_Handler(void) {systicks10ms++;}static inline void SuspendTick(void) {SysTick->CTRL &= ~SysTick_CTRL_TICKINT_Msk;}static inline void ResumeTick(void) {SysTick->CTRL |= SysTick_CTRL_TICKINT_Msk;}void main(void) {NVIC_SetPriority(SysTick_IRQn, TICK_INT_PRIORITY);SysTick->LOAD = (SystemCoreClock / 100) - 1; /* set auto reload register for 10ms systick rate */SysTick->VAL = 0UL;SysTick->CTRL = SysTick_CTRL_CLKSOURCE_Msk |/* clock is HCLK, remove this for HCLK/8 */SysTick_CTRL_TICKINT_Msk | SysTick_CTRL_ENABLE_Msk; /* Enable SysTick IRQ and SysTick Timer */}

author: ataradov
author_rank: Super Contributor
timestamp: January 05, 2023, 05:22:34 pm
content: Quote from: Simon on January 05, 2023, 03:39:49 pmFirst of all, download the PDF version so you have everything at once and a meaningful search. Arm documentation for the core only briefly mentions the registers that are implemented in this core, the actual description of the operation is in the Architecture Reference Manual (DDI0419C_arm_architecture_v6m_reference_manual.pdf), which is referenced from the main core document.The code above is correct. You don't need to do anything special, just configure LOAD value and set TICKINT and ENABLE. Interrupt handler would be called automatically and you just run the code you need there, no need for any other maintenance, interrupt clearing or anything like that.

author: wek
author_rank: Frequent Contributor
timestamp: January 05, 2023, 11:45:25 pm
content: Quote from: ataradov on January 04, 2023, 08:04:23 pmI standcorrected, thanks.Wading through ARMv6M ARM (btw.this is telling, too), I stumbled upon an interesting fact: while in ARMv7M SysTick is integral part of processor, in ARMv6M it is optional.JW

author: Simon
author_rank: Unknown
timestamp: January 06, 2023, 03:35:21 pm
content: This is what I have, it transmits once rather than once every 350msCode:#define systick_reset_value 0x00FFFFFF // 350ms value between 0x0 and 0x00FFFFFF (24 bits)// https://developer.arm.com/documentation/dui0497/a/cortex-m0-peripherals/optional-system-timer--systick?lang=en#define SYST_CSR ( *(uint32_t *) 0xE000E010 ) // SysTick Control and Status Register#define SYST_RVR ( *(uint32_t *) 0xE000E014 ) // SysTick Reload Value Register#define SYST_CVR ( *(uint32_t *) 0xE000E018 ) // SysTick Current Value Register// #define SYST_CALIB ( *(uint32_t *) 0xE000E01C ) // SysTick Calibration Value Register (not implemented)// https://developer.arm.com/documentation/dui0497/a/cortex-m0-peripherals/system-control-block?lang=en#define ICSR ( *(uint32_t *) 0xE000ED04 ) // Interrupt Control and State Registervoid systick_setup(){SYST_RVR = systick_reset_value ;SYST_CVR = 0x0 ;SYST_CSR = 0x1 << 2 | 0x1 << 1 | 0x1 << 0 ;}void SysTick_Handler(){sys_com_tx( 0 , 0) ;}

author: eutectique
author_rank: Frequent Contributor
timestamp: January 06, 2023, 03:51:12 pm
content: You are transmitting from the interrupt handler. Not seeing the implementation ofsys_com_tx(), I would not say that it's wrong. But it's suspicious.

author: ataradov
author_rank: Super Contributor
timestamp: January 06, 2023, 05:00:17 pm
content: The code is fine assuming vector table has SysTick_Handler in the corresponding entry. Also, what is your core frequency? 350 ms is a strange value.So yes, test it some other way. Toggle a pin or something.

author: westfw
author_rank: Super Contributor
timestamp: January 07, 2023, 08:21:04 am
content: QuoteWhy are you doing this instead of just #including the appropriate CMSIS include file? (Not that setting up all the -I include paths to cover both vendor and ARM definitions isn't a pain, but if an IDE doesn't do it for you, you should only have to do it once, somewhere.)Quote350ms is about what you get when you divide a 48MHz clock (SAMC max) by the ~16e6 maximum systick divisor. It ought to show nicely on a LED, which I guess is nice.I've never actually used sysTick with the maximum divisor, but I can't imagine that it would have a bug there.QuoteI haven't ever seen an implementation without it...

author: ataradov
author_rank: Super Contributor
timestamp: January 07, 2023, 08:28:05 am
content: SysTick is clocked directly by the core clock, there is no configurable divisor.Misinterpreted your sentence about the divider.But yes, I originally miscalculated. (1<<24) / 48e6 is 350 ms, so this makes sense.It would work with the maximum value, I use that all the time to implement a full system time. Just let the timer overflow and in the interrupt increment remaining bits. When you need to take the time, combine the current value of the counter with the incrementing variable. This way you get very granular system time for cheap.

author: wek
author_rank: Frequent Contributor
timestamp: January 07, 2023, 08:40:40 am
content: QuoteFPU in CM4 is optional, too. I believe all initial implementations did include it, and only later appeared implementations which chose to omit it (I may be wrong and it may be only me not being aware of the early FPU-less implementations). At any case, it raised enough confusion so that ARM and some (most?) implementers rebadged the FPU-containing CM4 as CM4F.OTOH, FPU is probably quite a chunk of silicon while SysTick is probably miniscule in size, so it probably doesn't make any sense to omit it. I don't see any reason for it to be optional, but then I am not a decision maker in ARM.JW

author: ataradov
author_rank: Super Contributor
timestamp: January 07, 2023, 08:48:07 am
content: It is probably optional because the first core using ARMv6-M was Cortex-M1 targeting FPGAs. And there SysTick may be costly and not necessary. Plus it is far easier to have options in the FPGAs.

author: Simon
author_rank: Unknown
timestamp: January 08, 2023, 08:31:39 am
content: I'll set the pin up as a digital output tomorrow and toggle it.

author: Simon
author_rank: Unknown
timestamp: January 09, 2023, 09:36:27 am
content: OK that works, i have problems somewhere else.

author: sslupsky
author_rank: Contributor
timestamp: January 11, 2023, 10:11:11 pm
content: There is some tricky stuff you need to take care of when the systick wraps. There are some drivers in the zephyr project repo that might be helpful reference:https://github.com/zephyrproject-rtos/zephyr/blob/main/drivers/timer/cortex_m_systick.c

author: westfw
author_rank: Super Contributor
timestamp: January 12, 2023, 09:43:41 am
content: QuoteWell, that's the most complex systick code I've ever seen. And it wasn't helpful at all, because It doesn't explain why it's doing things the way it IS.I *think* their idea is to run sysTick with varying counts, so as to enable sleeping between "ticks" or something. That's not the USUAL way to use sysTick. (although to be fair, having the system wake up every millisecond or so just to increment a tick counter is probably not a great way to achieve low-power devices.)

author: sslupsky
author_rank: Contributor
timestamp: January 13, 2023, 04:43:53 am
content: Quote from: westfw on January 12, 2023, 09:43:41 amYes, Zephyr can be configured to use a tick or to be "tickless".The clock API's are documented here:https://docs.zephyrproject.org/latest/doxygen/html/group__clock__apis.htmlWhich provides a bit more explanation about what this driver is supposed to do.Unfortunately, the Systick in the Cortex M0 is not very power efficient. For SAMD you can achieve quite low power operation using the RTC to generate the system timer.

author: cv007
author_rank: Frequent Contributor
timestamp: January 13, 2023, 12:49:22 pm
content: QuoteA low power timer (32k clock source) is a good alternative in any case as you can keep time moving through low power modes, plus you get a single clock speed running the time that doesn't change or depend on a 'ticks per second' define (as is used in the zephyr project which means your time is based on one cpu speed that cannot change).For a sam with a rtc in 32bit count mode, you get ~36 hours to work with at highest resolution (~30us, plenty good for general use), and by also keeping track of overflows you get a system time that never rolls over.//using a time stamp, manually checking timeled.on(); //on for 10 secondsauto t = now() + 10_sec;while( now() < t ){}led.off();//blocking wait in low power mode, default low power mode is standby, uses rtc compare to wakewhile( true ){ wait(1_sec); led.toggle(); }//wait random time between 100ms/2secwhile( true ){ wait(100_ms, 2_sec); led.toggle(); }The above code works the same when using an avr0/1 series for example with its rtc peripheral (16bit). The details are a little different like available low power modes and the timer width, but the app code will look the same. There is usually a low power timer in the modern mcu that is typically unused.

author: Simon
author_rank: Unknown
timestamp: January 13, 2023, 07:20:43 pm
content: Quote from: westfw on January 12, 2023, 09:43:41 amNOTHING anyone links to as the answer ever works. All code I look at just seems over complicated and reading it won't help explain something as it was written from the perspective of someone very knowledgeable in the hardware and coding. I'm not that experienced in coding and I just find myself tied in knots to the point it is easier to understand the hardware and write my own code. I don't know if this is my inexperience or if people just like to show off their skill unneccessarily. I have not read code yet that does not contain so much stuff that references other files that it's just impossible to peice it together. I need to learn this persons system before I can use the code to learn how something works.

author: sslupsky
author_rank: Contributor
timestamp: March 20, 2023, 03:49:59 am
content: Looks like Zephyr has added support for SAMC20 and SAMC21 if that is any help to you. Here is the commit that did that:soc: atmel: add base support for C2x SOCAdds Atmel SAMC20 and SAMC21 soc. C series is based on Cortex-M0+.C21 contains CAN interface.The init routines are same for SAMC20 and SAMC21. They use oneclock OSC48M without configuration.The code is inspirated from atmel_sam0/samd21.You would have a learning curve to come up to speed on using Zephyr. The wealth of drivers available in Zephyr might be worth it for you. Zephyr also has stacks for networking, wireless, file systems and more. Zephyr's device driver model uses devicetree which is similar to Linux.

author: Simon
author_rank: Unknown
timestamp: March 20, 2023, 06:53:57 pm
content: Zephyr is not what I need. They don't support IC's but "boards", err yuk, what is this crap? the list of supported hardware is very short and clearly it will not support the hardware fully. I really wish the world of "look what we can do in 5 seconds" would just go and jump off a damn cliff. So it supports the C21N development board, not sure what that means. So does that mean that I am forced to use this variant? C21E/G/J are all the same but for different pin counts, C21N is really not the same chip, it has extra's on some peripherals the other 3 don't and is much more expensive, but then if you are too cheap to invest time I guess you can throw away money while hoping you can buy the parts. The Atmel development boards are the most horrible crap to work with I have ever come across. So bad I just made my own!the systick is a couple of lines of code, seriously I have written more text setting up my own definitions for the registers as they are not defined by Atmel than I have written to run the thing.

author: Rudolph Riedel
author_rank: Regular Contributor
timestamp: March 25, 2023, 09:05:05 am
content: Follow the link in my post which is the first answer.And to extract the relevant parts related to systick:Code:#include "sam.h"volatile uint8_t system_tick = 0;void SysTick_Handler(void){system_tick = 42;}void init_clock(void){...}int main(void){uint8_t led_delay = 0;init_clock();SysTick_Config(48000000 / 200); // configure and Enable Systick for 5 ms tickswhile (1){if(system_tick){system_tick = 0;led_delay++;if(led_delay > 39){led_delay = 0;PORT->Group.OUTTGL.reg = PORT_PA27;}}}}This is ATSAMC21 code for microchip studio, SysTick_Handler is declared with the attribute weak in the supplied startup_samc21.c.And SysTick_Config() is from the CMSIS package which is referenced when including "sam.h".This is doing what SysTick is supposed to do, provide a timebase for the scheduler.And I chose to use the SysTick_Handler to set a flag and nothing else.It works and there really is not anything else in regards to SysTick.Why SysTick_Config(48000000 / 200) for 5ms ticks?The parameter is the amount of ticks to the next interrupt.And the timer runs with the SystemCoreClock.48000000Hz * 0.005s == 48000000 / (1 / 0.005) == 48000000 / 200 -> 240000What I left out is putting the controller to sleep at the end of the while(1) and use SysTick_Handler() to wake it up.It really does not make any difference for my application since that example is for using a 5" TFT.

author: ataradov
author_rank: Super Contributor
timestamp: March 25, 2023, 04:31:29 pm
content: If you are only using systick to set a flag, then you might as well disable the interrupt and read CSR.COUNTFLAG to get the same result. Saves context saver/restore and a variable.

author: Rudolph Riedel
author_rank: Regular Contributor
timestamp: March 26, 2023, 12:25:51 am
content: Quote from: ataradov on March 25, 2023, 04:31:29 pmI just tried that on an ATSAME51 and it saved a whopping 16 bytes of programm memory and zero bytes SRAM.Code:...SysTick_Config(120000000 / 200); /* configure and enable Systick for 5ms ticks */SysTick->CTRL = SysTick_CTRL_CLKSOURCE_Msk | SysTick_CTRL_ENABLE_Msk; /* disable systick interrupt */...while (1){if(SysTick->CTRL & SysTick_CTRL_COUNTFLAG_Msk)//if(system_tick){//system_tick = 0;I rather stick to more generic code that would also allow putting the controller to sleep.

author: ataradov
author_rank: Super Contributor
timestamp: March 26, 2023, 01:34:21 am
content: It also saves at least 24 clock cycles for each interrupt call.And whether SysTick even runs in sleep modes depends on the system implementation. But with CLKSOURCE=1, it will be running on the processor clock, which would be disabled except in the most shallow sleep modes.

author: Rudolph Riedel
author_rank: Regular Contributor
timestamp: March 26, 2023, 11:22:03 am
content: Well, 24 or more clock cycles do not concern me either.But you are right, looks like not even IDLE is an option.While looking around I found this:https://microchipdeveloper.com/32arm:samd21-systickI currently have the datasheets for SAM C2x, E5x and D21 open and these have close to no information about SysTick.All three say that SysTick is clocked by CLK_CPU while the page above says it is clocked by AHBCLK for the SAM D21.CLK_CPU makes more sense though since SysTick is part of the core.And CLK_CPU is stopped in all modes except ACTIVE.There is no indication in the datasheets what would happen if the clocksource bit is not set.So I just changed my SAM E51 code to this:Code:void init_systick(uint32_t ticks){SysTick->CTRL = 0;SysTick->LOAD = ticks - 1U; /* set the reload register */SysTick->VAL = 0; /* clear current counter value */SysTick->CTRL = SysTick_CTRL_CLKSOURCE_Msk | SysTick_CTRL_ENABLE_Msk; /* enable systick */}int main(void){uint8_t led_delay = 0;init_clock();init_io();init_systick(120000000 / 200); /* configure and enable Systick for 5ms ticks */CMCC->CTRL.bit.CEN = 1;/* enable Cache-Controller */while (1){if(SysTick->CTRL & SysTick_CTRL_COUNTFLAG_Msk){//system_tick = 0;led_delay++;It works and also replacing SysTick_Config() to not even enable the systick interrupt saves 32 bytes of program memory in total.There really is no argument to make for saving 32 bytes out of 128 kiB, 256 kiB or even 512 kiB flash.I am even compiling with -O2 now as my software runs faster while the binary is a little bigger.A few clock cycles also do not make any difference for me now.Well, my argument to be able to use sleep this way seems to be invalid.Hmm, I got nothing to argue against this minor improvement so I might just keep it. :-)Thanks for the tip and the oportunity to have a closer look at this!

author: bson
author_rank: Unknown
timestamp: March 30, 2023, 06:53:13 pm
content: Quote from: westfw on January 12, 2023, 09:43:41 amSysTick though is a core peripheral, so runs off the core clock, which means if it's used the core clock can't be shut down like with any of the typical peripheral bus timers. In reality though I've compared them on STM32 and any difference was sub-µA territory so I just didn't care. It just didn't matter. So I use SysTick since it's universally available (in off-the-shelf processors) and requires less porting work just to get the basics going. I don't use vendor libraries either, so it's typically compile and it just works stuff.