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
A Sunrise and Sunset Calculator¶
By Andrew Palm. Requires his maths library be loaded first.
Note
Usage:
set-zenith-code.
zenith code |
type of sunrise/sunset |
---|---|
0 |
Official sunrise and sunset |
1 |
civil twilight |
2 |
nautical twilight |
3 |
astronomical calculations |
Run “sunrise-sunset-utc” with inputs lat, long, year, month, day.
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
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
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
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
;
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
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.
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
\ 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
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