Skip to content
Snippets Groups Projects
Commit da4694a7 authored by AAC A's avatar AAC A
Browse files

added systick_irq_millis example

parent fa4d6480
No related branches found
No related tags found
Loading
TARGET:=systick_irq_millis
all : flash
PREFIX:=riscv64-unknown-elf
GPIO_Toggle:=EXAM/GPIO/GPIO_Toggle/User
CH32V003FUN:=../../ch32v003fun
MINICHLINK:=../../minichlink
CFLAGS:= \
-g -Os -flto -ffunction-sections \
-static-libgcc \
-march=rv32ec \
-mabi=ilp32e \
-I/usr/include/newlib \
-I$(CH32V003FUN) \
-nostdlib \
-I. -DSTDOUT_UART -Wall
LDFLAGS:=-T $(CH32V003FUN)/ch32v003fun.ld -Wl,--gc-sections -L../../misc -lgcc
SYSTEM_C:=$(CH32V003FUN)/ch32v003fun.c
$(TARGET).elf : $(SYSTEM_C) $(TARGET).c
$(PREFIX)-gcc -o $@ $^ $(CFLAGS) $(LDFLAGS)
$(TARGET).bin : $(TARGET).elf
$(PREFIX)-size $^
$(PREFIX)-objdump -S $^ > $(TARGET).lst
$(PREFIX)-objdump -t $^ > $(TARGET).map
$(PREFIX)-objcopy -O binary $< $(TARGET).bin
$(PREFIX)-objcopy -O ihex $< $(TARGET).hex
flash : $(TARGET).bin
$(MINICHLINK)/minichlink -w $< flash -b
clean :
rm -rf $(TARGET).elf $(TARGET).bin $(TARGET).hex $(TARGET).lst $(TARGET).map $(TARGET).hex
# Systick IRQ demonstration
This example shows how to set up the built-in Systick IRQ to generate periodic
interrupts for timing. Many bare-metal and RTOS based embedded applications will
use the Systick IRQ for timing, periodic housekeeping and task arbitration so
knowing how to set that up is useful.
Systick.h now also contains an arduino-like millis() function to return the
current time.
The example has been adapted to use this millis() function for a lock-free program.
Here, you compare the difference of current time and the last time a task was
executed against a desired interval. If enough time has passed, run the next
step.
The benefit of this dance is that the CPU doesn't spend 250ms (12,000,000
cycles!) drooling onto the carpet.
Other important tasks (reading buttons, DDoSing your grandma) can run until it's time.
Of course a real RTos would be much more powerful.
Note that this example is *NOT* compatible with the Delay_Ms() function that's
part of the ch32v003fun library - that function uses the Systick counter for
doing busy-wait delays and will interfere with its use in generating predictable
IRQs. Do not use the built-in Delay_Ms() and related functions when using Systick
for IRQs.
Note also the use of the `__attribute__((interrupt))` syntax in declaring the
IRQ handler. Some of the IRQ examples from the WCH HAL library have slightly
different syntax to make use of a fast IRQ mode but which is not compatible with
generic RISC-V GCC so that feature is not used here.
# Use
Connect LEDs (with proper current limit resistors) to GPIO pins C0, D0, D4 and
a 3.3V logic level serial terminal to PD5. The LEDs will flash and an incrementing
count will be printed to the serial port at rates that are controlled by the
Systick IRQ.
/*
* Single-File-Header for using SysTick with millisecond interrupts
* 03-25-2023 E. Brombaugh
*/
#ifndef _SYSTICK_H
#define _SYSTICK_H
#include <stdint.h>
/* some bit definitions for systick regs */
#define SYSTICK_SR_CNTIF (1<<0)
#define SYSTICK_CTLR_STE (1<<0)
#define SYSTICK_CTLR_STIE (1<<1)
#define SYSTICK_CTLR_STCLK (1<<2)
#define SYSTICK_CTLR_STRE (1<<3)
#define SYSTICK_CTLR_SWIE (1<<31)
volatile uint32_t systick_cnt;
/*
* Start up the SysTick IRQ
*/
void systick_init(void)
{
/* enable the SysTick IRQ */
NVIC_EnableIRQ(SysTicK_IRQn);
/* Clear any existing IRQ */
SysTick->SR &= ~SYSTICK_SR_CNTIF;
/* Set the tick interval to 1ms for normal op */
SysTick->CMP = (SYSTEM_CORE_CLOCK/1000)-1;
/* Start at zero */
SysTick->CNT = 0;
systick_cnt = 0;
/* Enable SysTick counter, IRQ, HCLK/1, auto reload */
SysTick->CTLR = SYSTICK_CTLR_STE | SYSTICK_CTLR_STIE |
SYSTICK_CTLR_STCLK | SYSTICK_CTLR_STRE;
}
#if 1
/*
* SysTick ISR just counts ticks
* note - the __attribute__((interrupt)) syntax is crucial!
*/
void SysTick_Handler(void) __attribute__((interrupt));
void SysTick_Handler(void)
{
/* clear IRQ */
SysTick->SR &= 0;
/* update counter */
systick_cnt++;
}
#endif
/*
* Millisecond delay routine
*/
void systick_delay_ms(uint32_t milliseconds)
{
/* compute end time */
uint32_t etime = systick_cnt + milliseconds;
/* wait for current time == end time */
while(systick_cnt != etime);
}
uint32_t millis() {
return systick_cnt;
}
#endif
/*
* Example for using SysTick with IRQs
* 03-25-2023 E. Brombaugh
*/
// Could be defined here, or in the processor defines.
#define SYSTEM_CORE_CLOCK 48000000
#define APB_CLOCK SYSTEM_CORE_CLOCK
#include "ch32v003fun.h"
#include <stdio.h>
#include "systick.h"
int main()
{
uint32_t count = 0;
SystemInit48HSI();
// start serial @ default 115200bps
SetupUART( UART_BRR );
printf("\r\r\n\nsystick_irq example\n\r");
printf("SysTick_Handler = 0x%08X\n\r", SysTick_Handler);
// init systick @ 1ms rate
printf("initializing systick...");
systick_init();
printf("done.\n\r");
// Enable GPIOs
RCC->APB2PCENR |= RCC_APB2Periph_GPIOD | RCC_APB2Periph_GPIOC;
// GPIO D0 Push-Pull
GPIOD->CFGLR &= ~(0xf<<(4*0));
GPIOD->CFGLR |= (GPIO_Speed_10MHz | GPIO_CNF_OUT_PP)<<(4*0);
// GPIO D4 Push-Pull
GPIOD->CFGLR &= ~(0xf<<(4*4));
GPIOD->CFGLR |= (GPIO_Speed_10MHz | GPIO_CNF_OUT_PP)<<(4*4);
// GPIO C0 Push-Pull
GPIOC->CFGLR &= ~(0xf<<(4*0));
GPIOC->CFGLR |= (GPIO_Speed_10MHz | GPIO_CNF_OUT_PP)<<(4*0);
const uint16_t led_i = 250;
uint32_t led_t_last;
uint8_t led_progstep = 0;
printf("looping...\n\r");
while(1)
{
if (millis() - led_t_last >= led_i) {
switch (led_progstep) {
case 0:
GPIOD->BSHR = 1 | (1<<4); // Turn on GPIOs
break;
case 1:
GPIOC->BSHR = 1;
break;
case 2:
GPIOD->BSHR = (1<<16) | (1<<(16+4)); // Turn off GPIODs
break;
case 3:
GPIOC->BSHR = (1<<16);
printf( "Count: %lu\n\r", count++ );
break;
}
led_progstep++;
led_t_last = millis();
if (led_progstep > 3) {
led_progstep = 0;
}
}
}
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment