Projects: Working Forth Programs

Note

All my projects are licensed under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later versions see http://www.gnu.org/licenses/. . Projects by other contributors are covered by the licence terms in their code.

Note

All project code is CMSIS-SVD compliant for easy code reuse.

  • You will need to load memory map words for all registers used. See Svd2forth-v2 for a easy way to generate a memmory mapped file for any of the projects below.

Window Comparator

  • Hardware: STM32F0 Discovery Board using a STM32F0xx MCU, but any STM32F0 with a Comparator should work

  • Clock: 8 Mhz using the stm32f0 target chip internal RC clock as default with Mecrisp-Stellaris

  • This program demonstrates Comparator 1 & 2 in ‘Window’ mode by lighting the BLUE and GREEN LEDS depending on voltage (potentiometer position), and gives a colored voltage status on the terminal.

  • standalone, no other files needed

/usr/home/tp/mecrisp-stellaris/f051-comparator-tests/f051-comparator-tests.fs

DOWNLOAD: f051-comparator-tests.fs

A Sunrise and Sunset Calculator

By Andrew Palm. Requires his maths library be loaded first.

DOWNLOAD: sunrise-sunset.fs

Note

Usage:

  1. set-zenith-code.

zenith code

type of sunrise/sunset

0

Official sunrise and sunset

1

civil twilight

2

nautical twilight

3

astronomical calculations

  1. Run “sunrise-sunset-utc” with inputs lat, long, year, month, day.

  2. To see the local sunrise and sunset times, plus hours of daylight and local solar noon, use the word “show-local-times” with input the integer utc offset for the time zone.

Examples

#include sunrise-sunset-v2.fs

0 set-zenith-code
-33,750000 150,700000  2018 02 25  sunrise-sunset-utc cr
11 show-local-times

1 set-zenith-code
-33,750000 150,700000  2018 02 25  sunrise-sunset-utc cr
11 show-local-times

2 set-zenith-code
-33,750000 150,700000  2018 02 25  sunrise-sunset-utc cr
11 show-local-times

3 set-zenith-code
-33,750000 150,700000  2018 02 25  sunrise-sunset-utc cr
11 show-local-times

\ 0 set-zenith-code  ok.
\ -33,750000 150,700000  2018 02 25  sunrise-sunset-utc cr
\  ok.
\ 11 show-local-times
\ Sunrise:        06:41:07
\ Sunset:         19:39:14
\ Daylight:       12:58:07
\ Solar noon:     13:10:10
\  ok.
\
\ 1 set-zenith-code  ok.
\ -33,750000 150,700000  2018 02 25  sunrise-sunset-utc cr
\  ok.
\ 11 show-local-times
\ Sunrise:        06:15:32
\ Sunset:         20:04:47
\ Daylight:       13:49:15
\ Solar noon:     13:10:10
\  ok.
\
\ 2 set-zenith-code  ok.
\ -33,750000 150,700000  2018 02 25  sunrise-sunset-utc cr
\  ok.
\ 11 show-local-times
\ Sunrise:        05:45:13
\ Sunset:         20:35:04
\ Daylight:       14:49:50
\ Solar noon:     13:10:08
\  ok.
\
\ 3 set-zenith-code  ok.
\ -33,750000 150,700000  2018 02 25  sunrise-sunset-utc cr
\  ok.
\ 11 show-local-times
\ Sunrise:        05:13:59
\ Sunset:         21:06:15
\ Daylight:       15:52:16
\ Solar noon:     13:10:07
\  ok.

Australian Metrological published times, Penrith, Sydney, Australia for 2018 02 25
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
\ 06:16 EDT   first light
\ 06:41 EDT   sunrise
\ 19:39 EDT   sunset
\ 20:05 EDT   last light

See also

s31.32 Insights

Benchmark Your Forth MCU ?

  • Hardware: STM32F0 Discovery Board using a STM32F0xx MCU

  • Clock: 8 Mhz

  • This program uses a Greatest Common Divisor algorithm to benchmark CPU speed, and measures the time with a Interrupt driven Sysclock. A list of other system results is included in the source file.

  • Instructions are in the sourcecode

/home/tp/mecrisp-stellaris/benchmarks/benchmarks-8mhz.fs

Download: Benchmark

Push button switch debounce demo

  • Hardware: STM32F0 Discovery Board using a STM32F0xx MCU

  • Clock: 8 Mhz using the stm32f0 target chip internal RC clock as default with Mecrisp-Stellaris

  • Must be used with the e4thcom Terminal. This is interactive, press return at each pause.

  • This program demonstrates the effect of increasing the debounce delay while counting USER push button presses.

  • The MCU just spins to provide the debounce delay

/home/tp/mecrisp-stellaris/f0-pb-db-spin/f0-pb-db-spin.fs

Download: Push button switch debounce demo

Square Wave Generator

  • Hardware: STM32F0 Discovery Board using a STM32F0xx MCU

  • Clock: 8 Mhz using the stm32f0 target chip internal RC clock as default with Mecrisp-Stellaris

  • Duty Cycle: 50 %

  • Frequency Range: 123 Hz to 350 KHz

  • Waveform: Square wave at 3.0 Volts

  • Output: on pin PB-1

  • Registers required: RCC, GPIOB, TIM14

  • Files required: freq-gen-memmap.txt and freq-gen.txt

  • Upload to your F0 Mecrisp-Stellaris Forth MCU in this order: freq-gen-memmap.txt, freq-gen.txt and you should see a 5000 Hz square wave appear on PB-1

  • Enter “<freq> hz” at the Forth prompt to change the frequency. “<freq>” must be between 123 and 350000 or PB-1 will just go to a High level.

\ Program Name: freq-gen.txt
\ Hardware: STM32F0 Discovery board which runs Mecrisp-Stellaris for the stm32f051 at 8 Mhz using the RC clock
\ Author:  t.porter <tery@tjporter.com.au> November 2016
\ All register names are CMSIS-SVD compliant so use Svd2forth-v2 to generate your memory map words or use freq-gen-memap.txt which is included in this project.

\ Overall Design
\ Registers used: GPIOB, TIM14
\ Timer = tim14
\ Tone output pin: PB-1


\ init words
: tim14-cc1e-as-output %1  0 lshift TIM14_CCER bis! ;		\ TIM14_CC1E 
: tim14-clock-enable %1  8 lshift RCC_APB1ENR bis! ;		\ TIM14 clock enable
: tim14-pwm_mode    %110  4 lshift TIM14_CCMR1_Output bis! ;	\ TIM14_OC1M 110: PWM mode 1 
: tim14-start %1  0 lshift TIM14_CR1 bis!  ;			\ TIM14_CCR1 counter enable (start timer)
: pb1-af0 %10  2 lshift GPIOB_MODER bis! ;			\ GPIOB_MODER1

\ program words
: pulse-length  TIM14_ARR ! ;	\ set frequency
: duty-cycle  TIM14_CCR1 ! ;	\ set duty cycle

: arr 				\ auto reload register value (frequency at 50% duty cycle (800 = 10.06khz))
dup dup pulse-length
." TIM14_ARR is $" hex. cr
shr				\ pulse-length divided by 2 for 50% duty cycle
duty-cycle
;

: hz DUP ." for a output of " . ." Hz (CLOCK = 8MHz, Prescaler = 0) " 8000000 swap / arr  ;
\ 123 Hz (minimum) = $FE10, 15Khz = $215, 20Khz = $190, 350KHz (max) = $16


\ init 
tim14-clock-enable
tim14-cc1e-as-output
tim14-pwm_mode
tim14-start
pb1-af0

\ output a 5 KHz square wave on PB-1
5000 hz			    \ 5025 Hz at 50.25% duty cycle



\  Screenshot:-
\ Forth> 20000 hz
\ -> 20000 hz
\ <- for a output of 20000 Hz (CLOCK = 8MHz, Prescaler = 0) TIM14_ARR is $00000190
\ <- ok.

Download: freq-gen-memmap.txt Download: freq-gen.txt

Screenshot:-
Forth> 20000 hz
-> 20000 hz
<- for a output of 20000 Hz (CLOCK = 8MHz, Prescaler = 0) TIM14_ARR is $00000190
<- ok.

100 Microsecond Systick

  • STM32F0 Discovery Board using a STM32F0xx MCU

  • Utilises the 8MHz MCO crystal controlled frequency from the SWD programmer

  • Overclocked to 96 MHz using the crystal derived 8 MHz MCO clock from the SWD programmer.

  • GPIOA_8 may be monitored to confirm the overclock is working. Look for 48MHz (1/2 clock) on this pin.

  • A marker pulse on GPIOB_2 is generated every Systick for external measurement. Period = 100us, Width = 800 nS

  • Flashes the BLUE LED (on GPIOC_8) at a rate of 1 second

\ 100 uS Systick by T. Porter
\ For a STM32 Discovery board running Mecrisp-Stellaris
\ Overclocked to 96 MHz using the crystal derived  8 MHz MCO clock from the SWD programmer. GPIOA_8 may be
\ monitored to confirm the overclock is working. Look for 48MHz (1/2 clock) on this pin.
\ A marker pulse on GPIOB_2 is generated every Systick for external measurement. Period = 100us, Width = 800  nS
\ Flashes the BLUE LED (on GPIOC_8) at a rate of 1 second


\ Overclock the default system clock from 8Mhz RC clock to 96 MHz Xtal clock via PLL
: 96mhz

 \ Set flash wait states to 2, the flash can't run faster than 35MHz ? )
 FLASH_ACR @
 %1111 bic 2 or
 FLASH_ACR !

 \ Enable HSE oscillator  
 1 16 lshift RCC_CR bis!
 BEGIN RCC_CR @ not 1 17 lshift and 0 = UNTIL

 \ Set PLL multiplication factor to 12, clock is 8Mhz MCO system clock from the SWD programmer.
 %1010 18 lshift	\ input x12 = 96 Mhz
 $10000 or		\ PLL SRC = HSE undivided 
 RCC_CFGR !

 \ Enable PLL
 1 24 lshift RCC_CR bis!

 \ Wait for PLL to stabilise
 BEGIN RCC_CR @ not 1 25 lshift and 0 = UNTIL

 \ Switch to HSE, set APB1 and APB2
 RCC_CFGR @
 %11 bic
 %10 or
 %100 11 lshift or	\ APB2CLK = HCLK
 %100 8 lshift or	\ APB1CLK = half HCLK
 RCC_CFGR ! 

 \ Wait for clock source to stabilize
 BEGIN RCC_CFGR @ 2 rshift 3 and %10 = UNTIL

 \ Update the Baud Rate Register so the serial comms is still 115200 baud.  
 416 USART1_BRR !  ( for 96 Mhz )


 \ Optional monitor the MCO frequency on GPIOA_8 with a scope or frequency counter. It will be half the 96 Mhz clock frequency
    %10  16 lshift GPIOA_MODER bis!         \ GPIOA_MODER8  SF1
    %111  24 lshift RCC_CFGR bis!	    \ MCO: Microcontroller clock output: 111: PLL clock selected
    %11  16 lshift GPIOA_OSPEEDR bis!       \ GPIOA_OSPEEDR8 hi speed
;


 \ Systick, note marker pulse is on GPIOB_2
 $E000E010 CONSTANT STK_CSR	\ SysTick control and status register. R/W reset value = $00000000
 $E000E014 CONSTANT STK_RVR	\ SysTick reload value register. R/W reset value = 6000 (6MHz clock) for the STM32F0
 $E000E018 CONSTANT STK_CVR	\ SysTick current value register. R/W value unknown  
 $E000E01C CONSTANT STK_CALIB	\ SysTick calibration value register. Read Only, $40001770 for the STM32F0

 0 variable counter
 
 %01  4 lshift GPIOB_MODER bis!     \ GPIOB_MODER2 set to push pull output for the Marker
 %01 8 2* lshift GPIOC_MODER bis!   \ GPIOC_8 as output for F0 Disco blue LED

 : marker-set
 %1  2 lshift GPIOB_BSRR bis!       \ GPIOB_BS2    set PB2 = 1
 ;

 : marker-clear
 %1  18 lshift GPIOB_BSRR bis!      \ GPIOB_BR2 clear PB2
 ;

 : toggle-led
    GPIOC_ODR @
    1 8 lshift xor
    GPIOC_ODR !
 ;

 : systick-calib-freq 9600 ;
 : systick-enable %101 STK_CSR bis! ;
 : systick-disable %101 STK_CSR bic! ;
 : systick-enable-irq %010 STK_CSR bis! ;
 : systick-disable-irq %010 STK_CSR bic! ;
 : systick-read STK_CVR @ ;

 systick-calib-freq STK_RVR !

 systick-enable

 : systick-handler
 marker-set
 counter @
 1+		    \ add one to the counter
 dup
 10000 >= if	    \ If counter is > = 10,000 ...
 toggle-led	    \ blink the LED at 1s rate
 drop		    \ drop the counter value of 10,000  from the stack
 0		    \ put 0 on the stack to load in the counter after the 'then'
 then
 counter !	    \ reset the counter to zero
 marker-clear
 ;

 ' systick-handler irq-systick !
 systick-enable-irq

 96mhz		    \ Overclock

Download: 100 Microsecond Systick

Overclock to 96 MHz

  • STM32F0 Discovery Board using a STM32F0xx MCU

  • Utilises the 8MHz MCO crystal controlled frequency from the SWD programmer

  • Increases the default Mecrisp-Stellaris speed by 12 times

  • Optional MCO frequency monitor pin on GPIOA-8

  • Power usage STM32F051: 20mA (3mA @ 8MHz)

  • May not program Flash memory reliably, but seems to run from Ram without any problems.

\ Overclock a F051 board to 96 Mhz by terry Porter, based on work by kfoltman May 2016
\ This is a 12 x increase over the standard 8MHz internal RC clock used by Mecrisp-Stellaris, so everything is 12 times faster.
\ Works on a STM32F0 Discovery board using the 8Mhz Xtal via the SWD programmer chip
\ Run the "96mhz" word when this file is loaded

: 96mhz

 \ Set flash wait states to 2, the flash can't run faster than 35MHz ? )
 FLASH_ACR @
 %1111 bic 2 or
 FLASH_ACR !

 \ Enable HSE oscillator  
 1 16 lshift RCC_CR bis!
 BEGIN RCC_CR @ not 1 17 lshift and 0 = UNTIL

 \ Set PLL multiplication factor to 12, clock is 8MHz xtal derived MCO system clock from the SWD programmer
 %1010 18 lshift	\ input x12 = 96 Mhz
 $10000 or		\ PLL SRC = HSE undivided 
 RCC_CFGR !

 \ Enable PLL
 1 24 lshift RCC_CR bis!

 \ Wait for PLL to stabilise
 BEGIN RCC_CR @ not 1 25 lshift and 0 = UNTIL

 \ Switch to HSE, set APB1 and APB2
 RCC_CFGR @
 %11 bic
 %10 or
 %100 11 lshift or	\ APB2CLK = HCLK
 %100 8 lshift or	\ APB1CLK = half HCLK
 RCC_CFGR ! 

 \ Wait for clock source to stabilize
 BEGIN RCC_CFGR @ 2 rshift 3 and %10 = UNTIL

 \ Update the Baud Rate Register so the serial comms is still 115200 baud.  
 416 USART1_BRR !  ( for 96 Mhz )


 \ Optional monitor the MCO frequency on GPIOA-8 with a scope or frequency counter. It will be half the 96 Mhz clock frequency
    %10  16 lshift GPIOA_MODER bis!         \ GPIOA_MODER8  SF1
    %111  24 lshift RCC_CFGR bis!	    \ MCO: Microcontroller clock output: 111: PLL clock selected
    %11  16 lshift GPIOA_OSPEEDR bis!       \ GPIOA_OSPEEDR8 hi speed
 
;

Download: Overclock to 96 MHz

F0 Discovery 48 MHz System Clock

\ Program Name: f0-disco-48mhz.fs
\ This program is standalone, no other files are needed
\ Date: Sat Apr 28 08:56:40 AEST 2018
\ Copyright 2018  t.porter <terry@tjporter.com.au>, licensed under the GPL
\ For Mecrisp-Stellaris by Matthias Koch.
\ https://sourceforge.net/projects/mecrisp/
\ Chip: STM32F051, Board: STM32F0 Discovery Board
\ All register names are CMSIS-SVD compliant
\ This Program : Increases the System Clock to 48 MHz using the Xtal controlled oscillator from the
\ F0 Discovery Board SWD subsystem. Can be easily be changed to HSI, see RCC_CFGR_PLLSRC below
\ ---------------------------------------------------------------------------\

 \ compiletoflash   
 compiletoram

  $4001380C constant USART1_BRR ( Baud rate register ) 

 : FLASH_ACR_LATENCY ( %XXX -- )  0  lshift $40022000 ;	 ( LATENCY )
 : FLASH_ACR_PRFTBE %1  4  lshift $40022000 ;		 ( PRFTBE Prefetch buffer enable )
 : RCC_CR_HSEON %1  16  lshift $40021000 ;		 ( External High Speed clock  enable )
 : RCC_CR_HSERDY? %1  17  lshift $40021000 bit@ ;	 ( External High Speed clock ready  flag )
 : RCC_CFGR_PLLMUL ( %XXXX -- )  18  lshift $40021004 ;	 ( PLL Multiplication Factor )
 : RCC_CFGR_PLLSRC ( %XX -- )  15  lshift $40021004 ;	 ( PLL clock source: %10: HSE/PREDIV, 01: HSI/PREDIV  )
 : RCC_CR_PLLON %1  24  lshift $40021000 ;		 ( PLL enable )
 : RCC_CR_PLLRDY? %1  25  lshift $40021000 bit@ ;	 ( PLL clock ready flag )
 : RCC_CFGR_SW ( %XX -- )  0  lshift $40021004 ;	 ( System clock Switch: $10: PLL selected as system clock)
 : RCC_CFGR_PPRE ( %XXX -- )  8  lshift $40021004  ;	 ( APB Low speed prescaler  APB1 )
 : RCC_CFGR_HPRE ( %XXXX -- )  4  lshift $40021004 ;	 ( AHB prescaler )

 : f0-disco-48mhz
 \ Set flash wait states to 1, turn on prefetch bit. One wait state, if 24 MHz < SYSCLK  48 MHz
 %001  FLASH_ACR_LATENCY bis! \ LATENCY --> max = 1 for the stm32f0, note more latency uses less power 
 FLASH_ACR_PRFTBE bis!
 
 \ Enable HSE oscillator  
 RCC_CR_HSEON bis!
 BEGIN  RCC_CR_HSERDY? UNTIL  \ Is HSE ready ?
  
 \ Set PLL multiplication factor to 6, clock is 8Mhz system clock
 %0100 RCC_CFGR_PLLMUL bis!   \ %0100: PLL input clock x 6
 %10 RCC_CFGR_PLLSRC bis!     \ %10: HSE/PREDIV, 01: HSI/PREDIV

 \ Enable PLL
 RCC_CR_PLLON bis!
 BEGIN RCC_CR_PLLRDY? UNTIL   \ Is PLL ready ?

 \ Switch to PLL, set ABP and AHB Prescalers
 %10 RCC_CFGR_SW bis!	      \ System clock Switch: %10 PLL selected as system clock
 %100  RCC_CFGR_PPRE bis!     \ APB Low speed prescaler APB1 (pclock): %100: HCLK divided by 2
 %0000 RCC_CFGR_HPRE bis!     \ AHB prescaler: 0xxx: SYSCLK not divided

 \ Update the Baud Rate Register so the serial comms is still 460800 Baud.
 \ ((pll_multiplier * clock)/Baud rate)/2 = ((6* 8000000)/460800)/2 = 52
 52 USART1_BRR !  \ for 115200 Baud change to 208
 ;

 f0-disco-48mhz

 compiletoram

Download: f0-disco-48mhz.fs

Delay using the interrupt driven 1 millisecond Systick

  • STM32F0 Discovery Board using a STM32F0xx MCU

  • Default Mecrisp-Stellaris 8Mhz RC system clock

  • Blue LED blinks at one second Interval

  • 1mS marker pulse is available on PB2 for scope fine tuning

  • ‘msdelay’ word provides a accurate time delay via the systick

  • By Terry Porter based on code by kfoltman

\ msdelay.txt by Terry Porter May2016
\ STM32F0 Discovery Board (running from the internal 8MHz RC clock) program to :-
\ provide a timed delay in 1mS intervals using the systick and a timer
\ i.e " 1000 msdelay " which gives a 1 second delay
\ Flash the BLUE led every second by counting 1000 systicks
\ A 1mS marker is available on PB2


 \ Define Systick memory mapping as its not in CMSIS-SVD
 $E000E010 CONSTANT STK_CSR	\ SysTick control and status register. R/W reset value = $00000000
 $E000E014 CONSTANT STK_RVR	\ SysTick reload value register. R/W reset value = 6000 for the STM32F0
 $E000E018 CONSTANT STK_CVR	\ SysTick current value register. R/W value unknown  
 $E000E01C CONSTANT STK_CALIB	\ SysTick calibration value register. Read Only, $40001770 for the STM32F0
 0 variable second_counter
 0 variable ms_second_counter


 : INIT-SYSTICK
 %01  4 lshift GPIOB_MODER bis!     \ set PB2 to push pull output for the Marker
 %01 8 2* lshift GPIOC_MODER bis!   \ PC8 as output for F0 Disco LED
 8080 STK_RVR !			    \ systick calib for 1ms using internal 8mhz osc <--CHANGE the 8080 to suit your own MCU !!
 %101 STK_CSR bis!		    \ systick enable
 ;

: ENABLE-SYSTICK-INTERRUPT %010 STK_CSR bis! ;

: marker-set			    \ 1mS pulse for scope at PB2
 %1  2 lshift GPIOB_BSRR bis!       \ set PB2 = 1
 ;

 : marker-clear
 %1  18 lshift GPIOB_BSRR bis!      \ clear PB2
 ;

 : toggle-systick-led
    GPIOC_ODR @
    1 8 lshift xor
    GPIOC_ODR !
 ;

  

 INIT-SYSTICK

 : systick-handler
 ms_second_counter @
 1+
 ms_second_counter !
 marker-set
 second_counter @
 1+			    \ add one to the counter
 dup
 1000 >= if		    \ If counter is > = 1000 ...
 toggle-systick-led	    \ blink the LED at 1s rate
 drop			    \ drop the counter value of 1000  from the stack
 0			    \ put 0 on the stack to load in the counter after the 'then'
 then
 second_counter !	    \ reset the counter to zero
 marker-clear
 ;

 ' systick-handler irq-systick !
 ENABLE-SYSTICK-INTERRUPT



: msdelay cr
>R			    \ save delay value on the return stack
0 ms_second_counter !	    \ reset ms_second_counter to zero
BEGIN
R@			    \ copy the delay value
ms_second_counter @	    \ get ms_second_counter value
=			    \ loop until equal to  ms_second_counter
UNTIL
R> DROP			    \ balance the Return Stack
;			    \ finished, delay is over


\ ******* screenpic of a 1 minute delay *********
\ 60000 msdelay 
\ ok.

Download: msdelay

Timer, 1mS Interrupt Systick

  • STM32F0 Discovery Board using a STM32F0xx MCU

  • Default Mecrisp-Stellaris 8Mhz RC system clock

  • Interrupt driven with 1mS Systick

  • Blue LED blinks at one second Interval

  • 1mS marker pulse is available on PB2 for scope fine tuning

  • Use to time software or hardware. ‘delay’ example included

  • Standalone code, no other files needed

  • By Terry Porter based on code by kfoltman

Download: timer.fs

\ Program Name: timer.fs
\ Date: 2016
\ Copyright 2017  t.porter <terry@tjporter.com.au>, licensed under the GPL
\ For Mecrisp-Stellaris by Matthias Koch
\ Chip: STM32F051
\ Board: STM32F0 Discovery Board
\ Terminal: e4thcom, Copyright (C) 2013-2017 Manfred Mahlow (GPL'd)  https://wiki.forth-ev.de/doku.php/en:projects:e4thcom
\ Clock: 8 Mhz using the internal STM32F051 RC clock, unless otherwise stated
\ All register names are CMSIS-SVD compliant
\ Note: gpio a,b,c,d,e, and uart1 are enabled by Mecrisp-Stellaris core as default.
\
\ This Program : 
\ * measures elapsed time using the SYSTICK.
\ * Flashes the BLUE led every second by counting 1000 systicks
\ * A 1mS marker pulse is available on PB2
\
\ Registers Used:- As defined below
\ timer.fs is self contained, no other files are required
\ To be done: modify the code to run from Flash after a reboot.
\ ------------------------------------------------------------------------------------------------------

 \ Define Systick memory mapping as its not in CMSIS-SVD
 $E000E010 constant STK_CSR	\ SysTick control and status register. R/W reset value = $00000000
 $E000E014 constant STK_RVR	\ SysTick reload value register. R/W reset value = 6000 (6MHz clock) for the STM32F0
 $E000E018 constant STK_CVR	\ SysTick current value register. R/W value unknown  
 $E000E01C constant STK_CALIB	\ SysTick calibration value register. Read Only, $40001770 for the STM32F0
 \ Define other regs
 $48000400 constant GPIOB_MODER ( GPIO port mode register )
 $48000800 constant GPIOC_MODER ( GPIO port mode register )
 $48000418 constant GPIOB_BSRR  ( GPIO port bit set/reset  register )
 $48000814 constant GPIOC_ODR

\ Variables
 0 variable second_counter
 0 variable ms_counter		\ can count to 32 bits or -> $ffffffff u. =  4294967295 mS or 4294967 seconds, or 71582 minutes or 1193 hours.

 : INIT-SYSTICK
 %01 4 lshift GPIOB_MODER bis!	    \  set PB2 to push pull output for the Marker
 %01 8 2* lshift GPIOC_MODER bis!   \ PC8 as output for F0 Disco LED
 8080 STK_RVR !			    \ systick calib for 1ms using internal 8mhz osc
 %101 STK_CSR bis!		    \ systick enable
 ;

 : marker-set			    \ 1mS pulse for scope at PB2
 %1 2 lshift GPIOB_BSRR bis!	    \ set PB2 = 1
 ;

 : marker-clear
 %1 18 lshift GPIOB_BSRR bis!      \ clear PB2
 ;

 : toggle-systick-led
    GPIOC_ODR @
    1 8 lshift xor
    GPIOC_ODR !
 ;

 : systick-handler
 marker-set
 ms_counter @
 1+
 ms_counter !
 second_counter @
 1+			    \ add one to the counter
 dup
 1000 >= if		    \ If counter is > = 1000 ...
 toggle-systick-led	    \ blink the LED at 1s rate
 drop			    \ drop the counter value of 1000  from the stack
 0			    \ put 0 on the stack to load in the counter after the 'then'
 then
 second_counter !	    \ reset the counter to zero
 marker-clear
 ;

 ' systick-handler irq-systick !    \ This 'hooks' the systick-handler word (above) to the systick irq
 ENABLE-SYSTICK-INTERRUPT	    \ enable it last


 : delay ( delay-value -- elapsed-time )				    
 0 ms_counter !			    \ reset the ms_counter to zero
 0 DO LOOP			    \ the simple delay loop to be timed
 ." = "
 ms_counter @ .			    \ this will be updated by the systick-handler every systick and we just read it when the delay finishes
 ." milliseconds " cr
 ;

 INIT-SYSTICK


\ ~~~~~~~~~~ Screenshots ~~~~~~~~~~~~
\ 10000 delay = 6 milliseconds
\ 1000000 delay = 625 milliseconds
\ 10000000 delay = 6256 milliseconds

Analog To digital 19ch scanner

  • STM32F0 Discovery Board using a STM32F0xx MCU

  • Default Mecrisp-Stellaris 8Mhz RC system clock

  • No DMA or interrupts used

  • Scans all 19 available channels of the ADC: PA0-7, PB0-1, PC0-5, VREF, VBAT and Temperature sensor in Degrees C

  • It works by scanning each channel and pushing the result on the Stack.

  • The End Of Conversion Flag is checked by a spinner.

  • After the sequence of 19 channel conversions is finished, it pops the results off the stack, scales and displays them along with the number of spins.

  • By Terry Porter

\ Program Name: f0-adc-read-all-ch.fs
\ Date: Thu Sep 7 07:36:28 AEST 2017
\ Copyright 2017  t.porter <terry@tjporter.com.au>, licensed under the GPL
\ For Mecrisp-Stellaris by Matthias Koch
\ Chip: STM32F051
\ Board: STM32F0 Discovery Board
\ Clock: 8 Mhz using the internal STM32F051 RC clock, unless otherwise stated
\ All register names must be CMSIS-SVD compliant
\ Note: gpio a,b,c,d,e, and uart1 are enabled by Mecrisp-Stellaris core as default.
\
\ This Program is a ADC all channel scanner, no DMA, no interrupts, using only a spin delay to wait for the end
\ of conversion sequence(s). It works by scanning each channel and pushing the results on the Stack.
\ After a conversion sequence of 19 channels is finished, it pops the results off the stack, scales and displays them.
\
\ Inputs: pa0-pa7, pb0-pb1, pc0-pc5
\ Outputs: terminal only
\
\ Registers Used: rcc, gpioa, gpiob, gpioc, adc
\
\
\ ADC Channels
\
\ ch  desc	  port and bit number or peripheral name
\ ==  =======	  ============================================
\ 0   adc_in0	  pa0 : this is the "USER" push button on a STM32F0 Discovery Board, normally LOW, goes to VCC when pressed
\ 1   adc_in1	  pa1 : in this instance I have a 10k pot connected across Vcc and 0V. The wiper is connected to pa1
\ 2   adc_in2	  pa2
\ 3   adc_in3	  pa3
\ 4   adc_in4	  pa4
\ 5   adc_in5	  pa5
\ 6   adc_in6	  pa6
\ 7   adc_in7	  pa7
\ 8   adc_in8	  pb0
\ 9   adc_in9	  pb1
\ 10  adc_in10	  pc0 (not present in 32pin qfn package)
\ 11  adc_in11    pc1 (not present in 32pin qfn package) 
\ 12  adc_in12    pc2 (not present in 32pin qfn package) 
\ 13  adc_in13    pc3 (not present in 32pin qfn package) 
\ 14  adc_in14    pc4 (not present in 32pin qfn package) 
\ 15  adc_in15    pc5 (not present in 32pin qfn package) 
\ 16  temp	  internal peripheral, must be turned on in rcc
\ 17  vref	  internal peripheral, must be turned on in rcc
\ 18  batt	  internal peripheral, must be turned on in rcc 
\
\ NOTE: What does ADC_INX correspond to in the real world, where is that information ?
\ It is shown in: https://www.st.com/resource/en/datasheet/stm32f051t8.pdf , see Table 13. Pin definitions
\  
\ ======================== screenpic ! ======================= 
\
\   t    
\  stm32f0 discovery board, no dma, no interrupts, all channel adc scan 
\  -------------------------------------------------------------------- 
\  battery = 2.964 v 
\  vref (typically 1.16 to 1.25 v) = 1.219 v 
\  temperature = 26.860 c 
\  pc5 = 1.951 v 
\  pc4 = 1.969 v 
\  pc3 = 2.054 v 
\  pc2 = 2.056 v 
\  pc1 = 2.056 v 
\  pc0 = 2.049 v 
\  pb1 = 1.748 v 
\  pb0 = 1.803 v 
\  pa7 = 1.914 v 
\  pa6 = 1.801 v 
\  pa5 = 1.731 v 
\  pa4 = 1.366 v 
\  pa3 = 1.617 v 
\  pa2 = 0.977 v 
\  pa1 = 0.009 v 
\  pa0 = 0.011 v 
\  
\  eoc flag state: reset
\  eos flag state: reset
\  adc has NOT overrun
\  eoc spincount, total over 19 channels = 95 
\   ok.
\  
\ ------------------------------------------------------------------------------------------------------  
\ Essential - Create and preload a Memory-Map file for registers used in this program. 
\
\ i.e. "$48000000 constant gpioa_moder \ GPIO port mode register"
\
\ For a easy way to do this see https://mecrisp-stellaris-folkdoc.sourceforge.io/svd2mem-sqlite.html
\
\ 31Dec2021: Sadly I wrote this program in 2018 using a now obsolete system, and there is no memory map file for it.
\ I'll rewrite this using my current system (SVD2FORTH V6) which generates everything needed for projects, inc
\ the memory in due course.
\ ------------------------------------------------------------------------------------------------------  
compiletoram

0 variable spincount	\ This is used to give a indication of how long the adc takes to convert a voltage

\ init words
 : pa0-7-analog  %111111111111111111  0 lshift gpioa_moder bis!	     ." pa0-7 set to analog mode " ;
 : pb0-1-analog  %1111 0 lshift gpiob_moder bis!		     ." pb0-1 set to analog mode " ;
 : pc0-5-analog  %111111111111 0 lshift gpioc_moder bis!	     ." pc0-5 set to analog mode " ;
 : adc-select-ch-0-18  %1111111111111111111 0 lshift adc_chselr bis! ." adc channels 0-18 select for scanning " ;
 : vref-enable  %1  22 lshift adc_ccr bis!			     ." adc vref enabled " ; 
 : battery-monitor-enable  %1  24 lshift adc_ccr bis!		     ." battery monitor enabled " ;
 : temperature-sensor-enable  %1  23 lshift adc_ccr bis!	     ." temp sensor enabled " ;
 : adc-clock-enable  %1 9 lshift rcc_apb2enr bis!		     ." adc clock enabled" ;
 : adc-set-sample-time  %111 adc_smpr bis!			     ." adc sample time set to 239.5 clock cycles " ;
 : adc-forward-scan  %1  2 lshift adc_cfgr1 bic!		     ." forward scan channel 0 to channel 18 selected" ;

 
\ general words
 : adc-disable %1  0 lshift adc_cr bic! ;				      \ adc disable  aden=0
 : adc-set-calibrate-bit %1  31 lshift adc_cr bis! ;			      \ adcal=1
 : adc-calibrate-bit? %1  31 lshift adc_cr bit@ ;			      \ adcal= ?
 : adc-calibrated? begin adc-calibrate-bit? not until ." adc calibrated" cr ; \ adcal=0, had the adc finished calibrating itself ?
 : adc-ready-flag?  %1 0 lshift adc_isr bit@  ;				      \ adc_adrdy flag, is the adc ready to do a read ?
 : adc-end-of-conversion-flag?  %1  2 lshift adc_isr bit@ ;		      \ adc_eoc conversion flag
 : adc-end-of-sequence-flag?  %1  3 lshift adc_isr bit@  ;		      \ eos flag is set when all conversions are finished, reset by application software
 : adc-end-of-sequence? begin adc-end-of-sequence-flag? until ;		      \ has adc scan sequence finished ?
 : adc-set-to-single-conversion-mode %1  13 lshift adc_cfgr1 bic! ;	      \ adc_cont is set to 0 on reset
 : adc-conversion-start %1  2 lshift adc_cr  bis! ;			      \ adc_adstart - start a adc conversion
 : adc-ready? begin adc-ready-flag? until  ;				      \ is the adc ready ?
 : scan0-18 %1  2 lshift adc_cfgr1 bic!	  ;				      \ adc_scandir scandir=0: forward scan channel 0 to channel 18
 : scan18-0 %1  2 lshift adc_cfgr1 bis!	  ;				      \ adc_scandir scandir=1: backward scan channel 18 to channel 0
 : adc-enable  %1  0 lshift adc_cr bis! ." adc enabled "
   adc-ready?
 ;
 : eoc?? adc-end-of-conversion-flag? if ." eoc flag state: set" else ." eoc flag state: reset" then cr ; \ the eoc flag is reset by reading the adc_dr register.
 : eos?? adc-end-of-sequence-flag? if   ." eos flag stare: set" else ." eos flag state: reset" then cr ; \ the eos flag is reset by writing a "1" to it.
 : overun?  %1  4 lshift adc_isr bit@  if ." adc has overrun, data is missed" else ." adc has NOT overrun" then cr ;    \ adc_ovr
 : reset-eos-flag  %1  3 lshift adc_isr bis! ;

: adc-calibrate ( -- calibrate adc) cr			
   adc-disable
   adc-set-calibrate-bit
   adc-calibrated?
 ;

 : portbit? ( adc channel -- portbit )	  
 case
 0 of	." pc5 = "  endof
 1 of	." pc4 = "  endof
 2 of	." pc3 = "  endof
 3 of	." pc2 = "  endof
 4 of	." pc1 = "  endof
 5 of	." pc0 = "  endof
 6 of	." pb1 = "  endof
 7 of	." pb0 = "  endof
 8 of	." pa7 = "  endof
 9 of	." pa6 = "  endof
 10 of	." pa5 = "  endof
 11 of	." pa4 = "  endof
 12 of	." pa3 = "  endof
 13 of	." pa2 = "  endof
 14 of	." pa1 = "  endof
 15 of	." pa0 = "  endof   
 endcase
 ;

\ init
   adc-calibrate
   pa0-7-analog
   pb0-1-analog
   pc0-5-analog
   adc-clock-enable	   \ must be set before following items, or they can't be set.
   adc-set-sample-time
   adc-forward-scan
   adc-select-ch-0-18
   vref-enable
   temperature-sensor-enable
   battery-monitor-enable
   adc-enable



 : read-voltages ( -- read all channels )
   begin			 
      adc-conversion-start
      begin
	1 spincount +!		       \ count the number of times we loop here, this is the 'eoc spincount'
	adc-end-of-conversion-flag?    \ jump to 'begin' unless the eoc flag is set
      until 
      adc_dr @			       \ push this ADC reading onto the Stack
   adc-end-of-sequence-flag?	       \ jump to 'begin' unless the eos flag is set     
   until
   reset-eos-flag		       \ eos-flag has to be reset by the user application
 ;

 : display-samples
   \ pop all channels off the stack, scale and assign names then pretty print them
   cr ." stm32f0 discovery board, no dma, no interrupts, all channel adc scan " cr
   ." -------------------------------------------------------------------- " cr
   \ the 3 special purpose voltages
   ." battery = " 2970 4096 */ 2 * 0 <#  # # # 46 hold #s  #>  type  ."  v "  cr
   ." vref (typically 1.16 to 1.25 v) = " 2970 4096 */ 0 <#  # # # 46 hold #s  #>  type  ."  v "  cr
   ." temperature = " 2970 4096 */ 1300 swap - 100 5 */ 29700 + 0 <#  # # # 46 hold #s  #>  type  ."  c "  cr
   \ the 16 gpio inputs
   16 0 do
   i portbit?
   2970 4096 */
   0 <#  # # # 46 hold #s  #> type
   ."  v " cr
   loop
 ;

: t		     \ scan, read and print with stats
   read-voltages
   display-samples  cr
   eoc??								
   eos??
   overun?	     \ Were we too slow and missed getting a conversion value before the next one was ready ?   
   ." eoc spincount, total over 19 channels = " spincount @  . cr
   0 spincount !     \ reset spincount 
 ;

 t    \ just enter 't' to rerun the scan

Download: Analog To digital 19ch scanner

Morse code generator

  • STM32F0 Discovery Board using a STM32F0xx MCU

  • Default Mecrisp-Stellaris 8Mhz RC system clock

  • LEDS blink for DASH and DOT’s

  • PA4 drives a piezo loudspeaker to hear the Morse Code

  • By quaak.haak, Terry Porter

\ morse-code-release.txt
\ Morse Code Generator for the STM32F0 Discovery Board running Mecrisp-Stellaris (by Matthias Koch)
\ The Disco Board LED's blink, green = Dot, Blue = Dash and PA4 can drive a Piezo loud speaker to hear the Morse Code as it's generated
\ All Register names are CMSIS SVD compliant
\ Registers Used: 
\ GPIOA , GPIOC 
\
\ ------------------------------------------------------------------------------------------------------  
\ 1) Essential - Create and load a Memory Map file to provide the Memory Mapped Register Words used in this program
\ (see Registers Used: above).To do this painlessly, see: http://128.199.141.78/_downloads/svd2forth-v2.zip
\ 2) Then load this file: morse-code-release.txt 
\ I recommend using the e4thcom Terminal to do the above: http://128.199.141.78/serial-terminals.html
\ ------------------------------------------------------------------------------------------------------
\ init must be run first, then morse, and send your message ....
\ By quaak.haak@gmx.com, and terry@tjporter.com.au Feb 2016
\ Project licensed under the terms of the GNU General Public License as published by the Free Software Foundation, either version
\ 3 of the License, or (at your option) any later version.




 : init ( -- )							\ Configure I/O, must be run first 
 %01 8 2* lshift 						
 %01 9 2* lshift or GPIOC_MODER bis!				\ Set PC8 and PC9 as output for LEDS
 %01 8 lshift GPIOA_MODER bis!					\ Set PA4 to General purpose output mode. it drives a Piezo loud speaker to hear the Morse Code
 ;

 : green-on   1 9 lshift GPIOC_BSRR ! ;				\ LED=green  = DOT
 : green-off  1 9 lshift GPIOC_BRR ! ;
 : blue-on   1 8 lshift GPIOC_BSRR ! ;				\ LED=blue   = DASH
 : blue-off  1 8 lshift GPIOC_BRR ! ;
 : PA4-H   %1 4  lshift GPIOA_BSRR  ! ;				\ Set PA4 "HIGH" around 2.8 volts
 : PA4-L   %1 20  lshift GPIOA_BSRR ! ;				\ Reset PA4 "LOW" around 0 volts
 : DELAY   300 0  DO LOOP ;					\ 2597 Hz
 : TONE  PA4-H DELAY PA4-L DELAY ;				\ Generate one cycle to power the Piezo
 : WAVE   0 DO  TONE  LOOP ;					\ one cycle only
 : WAIT	* 125000 0 DO  LOOP ;					\ Dit/Dah delay
 : dit   250 WAVE ;						\ Dit tone period
 : dah   650 WAVE ;						\ Dah tone period
 : dot	green-on dit green-off 1 WAIT ;				
 : dash	blue-on dah blue-off 1 WAIT ;				

 : morseemit ( -- )						\ Ascii list of available Morse Code characters to emit
 case
 32 OF  ." -space- "  100000 0 DO  LOOP		ENDOF
 39 OF  ." ' "  dot dash dash dash dash dot	ENDOF
 40 OF  ." ( "  dash dot dash dash dot		ENDOF
 41 OF  ." ) "  dash dot dash dash dot dash	ENDOF
 42 OF  ." * "  dot dash dash dot dash dot	ENDOF
 43 OF  ." + "  dot dash dot dash dot		ENDOF
 44 OF  ." , "  dash dash dot dot dash dash	ENDOF
 45 OF  ." - "  dash dot dot dot dot dash	ENDOF
 46 OF  ." . "  dot dash dot dash dot dash	ENDOF
 47 OF  ." / "  dash dot dot dash dot		ENDOF
 48 OF  ." 0 "  dash dash dash dash dash	ENDOF
 49 OF  ." 1 "  dot dash dash dash dash		ENDOF
 50 OF  ." 2 "  dot dot dash dash dash		ENDOF
 51 OF  ." 3 "  dot dot dot dash dash		ENDOF
 52 OF  ." 4 "  dot dot dot dot dash		ENDOF
 53 OF  ." 5 "  dot dot dot dot dot		ENDOF
 54 OF  ." 6 "  dash dot dot dot dot		ENDOF
 55 OF  ." 7 "  dash dash dot dot dot		ENDOF
 56 OF  ." 8 "  dash dash dash dot dot		ENDOF
 57 OF  ." 9 "  dash dash dash dash dot		ENDOF
 58 OF  ." : "  dash dash dash dot dot dot	ENDOF
 61 OF  ." = "  dash dot dot dot dash		ENDOF
 63 OF  ." ? "  dot dot dash dash dot dot	ENDOF
 64 OF  ." @ "  dot dash dash dot dash dot	ENDOF
 97 OF  ." a "  dot dash			ENDOF
 98 OF  ." b "  dash dot dot dot		ENDOF
 99 OF  ." c "  dash dot dash dot		ENDOF
 100 OF ." d "  dash dot dot			ENDOF
 101 OF ." e "  dot				ENDOF
 102 OF ." f "  dot dot dash dot		ENDOF
 103 OF ." g "  dash dash dot			ENDOF
 104 OF ." h "  dot dot dot dot			ENDOF
 105 OF ." i "  dot dot				ENDOF
 106 OF ." j "  dot dash dash dash		ENDOF
 107 OF ." k "  dash dot dash			ENDOF
 108 OF ." l "  dot dash dot dot		ENDOF
 109 OF ." m "  dash dash			ENDOF
 110 OF ." n "  dash dot			ENDOF
 111 OF ." o "  dash dash dash			ENDOF
 112 OF ." p "  dot dash dash dot		ENDOF
 113 OF ." q "  dash dash dot dash		ENDOF
 114 OF ." r "  dot dash dot			ENDOF
 115 OF ." s "  dot dot dot			ENDOF
 116 OF ." t "  dash				ENDOF
 117 OF ." u "  dot dot dash			ENDOF
 118 OF ." v "  dot dot dot dash		ENDOF
 119 OF ." w "  dot dash dash			ENDOF
 120 OF ." x "  dash dot dot dash		ENDOF
 121 OF ." y "  dash dot dash dash		ENDOF
 122 OF ." z "  dash dash dot dot		ENDOF   
 endcase
 ;

 : morsetype ( addr len -- )
 0 do
 dup i + c@  morseemit
 loop
 drop
 ;

 : morse ( -- )
 cr ." Type a message and press Enter: "
 query 0 parse 					\ Get a fresh line and parse everything inside, as character 0 never appears in input
 cr ." Sending morse code... "
 morsetype
 cr
 ;


\ ...................................................................................................................................
\
\ Note from Matthias
\
\ I like your morse example, it is very readable and a good piece to show on how Forth works. But I have a hint: Morse code characters are delimited by small delays
\ - instead you give a continuous stream of dashes and dots, without any character delays at all. Just insert a small pause after endcase, as morse is not a binary code :-)
\  And, perhaps, define a “basis time” constant from which all timings are generated. Then, I will insert it into the mainstream release.
\
\ The word PARIS is the standard for determing CW code speed. Each dit is one element, each dah is three elements, intra-character spacing is one element,
\ inter-character spacing is three elements and inter-word spacing is seven elements. The word PARIS is exactly 50 elements.
\
\  See: http://www.kent-engineers.com/codespeed.htm

Download: Morse code generator