💾 Archived View for tozip.chickenkiller.com › 2022-06-26-guile-mcu.gmi captured on 2023-06-16 at 16:13:33. Gemini links have been rewritten to link to archived content
⬅️ Previous capture (2022-07-16)
-=-=-=-=-=-=-
Created 2022-06-26
The STM32 has an IDE that has a pin configurator. I wondered if perhaps we could code at a higher level than C by using some other kind of generator. I decided to see if Guile could be used as a generator language. I'm really just dabbling at this stage to see if I could get something working. Here, for example, is the "complete" code to toggle pin PA5 every 500ms:
(forever (toggle 'a 5) (delayish 500))
A little interesting, perhaps. Note that the pin doesn't have to be initialised as an output pin. Guile figures it out. The code it produces is as follows:
#define STM32F411xE #include "stm32f4xx.h" #include "delay.h" int main (void) { // might as well turn on all the port clocks RCC->AHB1ENR |= RCC_AHB1ENR_GPIOAEN | RCC_AHB1ENR_GPIOBEN | RCC_AHB1ENR_GPIOCEN; GPIOA->MODER |= GPIO_MODER_MODER5_0; while(1) { GPIOA->ODR ^= GPIO_ODR_OD5; delayish(500); }; }
The indentation needs a bit to be desired, but hey-ho. It uses CMSIS, plus some of my home-brewed code, like the delayish() function. There's also a Makefile and linker script involved that I also won't bother to explain.
One thing to notice is how the output code figured out that port A had to have pin 5 set to output mode, like so:
GPIOA->MODER |= GPIO_MODER_MODER5_0;
There's no magic as such. The function "toggle", when called, keeps a note that the underlying pin needs to be an output pin. When code is generated, it inserts the appropriate instruction to make it an output pin. Useful, because it means that you don't have to worry about a lot of nitty gritty details. A useful enhancement would be to check to see if pins were being used in some incompatible way.
Could there be other useful features one could add, like cooperative multitasking? So maybe you could write something like:
(forever (toggle 'a 5) (delayish 500)) (forever (toggle 'b 6) (delayish 50))
and it would transparently toggle the pins synchronously. Yes, of course you can rustle up something in C yourself, or use an RTOS, but maybe you could write things at a higher level this way. There doesn't seem to be a huge gain so far, but you never know, the gains might be additive. Scheme seems a reasonable choice of language because it means you can do your own custom expansions, and you don't have to write your own parser and all that jazz. You just use Scheme.
Here's the source code, crude as it is:
( define hdr " #define STM32F411xE #include \"stm32f4xx.h\" #include \"delay.h\" int main (void) { // might as well turn on all the port clocks RCC->AHB1ENR |= RCC_AHB1ENR_GPIOAEN | RCC_AHB1ENR_GPIOBEN | RCC_AHB1ENR_GPIOCEN; ") (define output-pins '()) (define main hdr) (define init "") (define body "") (define-syntax-rule (+= var e ...) (set! var (string-append var e ...))) (define (nop) ( += body "\tasm (\"nop\");\n")) (define (toggle port pin) (define gpio (string-append "GPIO" (symbol->string port))) (string-upcase! gpio) (set! pin (number->string pin)) ;(+= init "RCC->AHB1ENR |= RCC_AHB1ENR_" gpio "EN;\n") ;(add-out-pin port pin) (set! output-pins (cons (list gpio pin) output-pins)) ;(+= init gpio "->MODER |= GPIO_MODER_MODER" pin "_0;\n") (+= body gpio "->ODR ^= GPIO_ODR_OD" pin ";\n") #t) (define (delayish ms) (+= body "\tdelayish(" (number->string ms) ");\n")) (define-syntax-rule (forever e ...) (begin (+= body "while(1) {\n") e ... (+= body "};\n"))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; actual user code (nop) ; don't do anything (forever (toggle 'a 5) (delayish 500)) ;; end of user code ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (let loop ((pins output-pins)) (unless (null? pins) (let* ((port-pin (car pins)) (gpio (car port-pin)) (pin (cadr port-pin))) (+= init gpio "->MODER |= GPIO_MODER_MODER" pin "_0;\n") (loop (cdr pins))))) (+= main init "\n" body "\n}\n") (let ((output-port (open-file "main.c" "w"))) (display main output-port) (newline output-port) (close output-port))