diff --git a/examples/systick_irq_millis/Makefile b/examples/systick_irq_millis/Makefile new file mode 100644 index 0000000000000000000000000000000000000000..266cce9d08ec1b7132c81b4d3256f5218df1f44b --- /dev/null +++ b/examples/systick_irq_millis/Makefile @@ -0,0 +1,41 @@ +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 + diff --git a/examples/systick_irq_millis/README.md b/examples/systick_irq_millis/README.md new file mode 100644 index 0000000000000000000000000000000000000000..f9d469a8e1efcd6bea087d822b0133785116b694 --- /dev/null +++ b/examples/systick_irq_millis/README.md @@ -0,0 +1,35 @@ +# 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. diff --git a/examples/systick_irq_millis/systick.h b/examples/systick_irq_millis/systick.h new file mode 100644 index 0000000000000000000000000000000000000000..1f3700e660fecb4821dfd9528404fc0c36118618 --- /dev/null +++ b/examples/systick_irq_millis/systick.h @@ -0,0 +1,75 @@ +/* + * 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 diff --git a/examples/systick_irq_millis/systick_irq_millis.c b/examples/systick_irq_millis/systick_irq_millis.c new file mode 100644 index 0000000000000000000000000000000000000000..b306cf6ff2cdeb3df15c5b6a7c27750dc07171b8 --- /dev/null +++ b/examples/systick_irq_millis/systick_irq_millis.c @@ -0,0 +1,76 @@ +/* + * 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; + } + } + } +}