I've been diving in to forth on and off for a while now. It's a thing that takes over my whole brain, and I really welcome the distraction from time to time. And it's for my work, so there's that. I've been bouncing between writing code in C using SDCC, writing forth on linux, writing C in the Arduino IDE, and back to forth again. It's all very enjoyably difficult context switching for me, and i'm good at context switching, or at least I once was. So that's where this idea came from. I thought, well, if it is difficult for me to context switch to forth after a few months of programming in C using SDCC on linux, then maybe it's difficult for others too. Maybe someone will find this useful.
My environment is linux. The chip I started out with was the STM8S103F3, and if you're just trying to write some forth on a micro, this is awesome way to go. The little $1 boards are great and cheap. Check out the great write up on Breakout Boards from Thomas. I used sduino bits added to the Arduino IDE to get started and really made my first go at the project I'm working on in under a week. Then we needed to change it up and I could not get all that I needed from that, so I also started working with SDCC from the command line in the shell with a Makefile and built the patched STM8 SPL with SDCC and made some better results with control and speed there. I also was bringing up my ability to use forth, with no small number of chat sessions, emails, forum reads and question posts, and a good old-fashioned mentoring from Thomas, who I owe a mountain of tasty beverages and a nice meal if I ever make it to Germany (or if he ever visits the US).
Then we changed micros for the project, to get the 12bit ADC at the least, and for other reasons, and we went from STM8S103F3 to STM8L051. The transition from STM8S to STM8L was a rough one, and I'm not sure I'd every wish upon anyone to have to make the context switch between them as often as I've had to do recently. This cut away the sduino/IDE development entirely, as I did not yet think it was possible (for me a that time, mere months ago) to import the SPL STM8L into the Arduino IDE like in the fashion. And, yet, now looking back, and with the ability to at least make an attempt at doing it successfully, I would probably not. If I were going to do something major along those lines, I'd probably focus my energy on helping out the folks over at SDCC and The SDCC Wiki get more work done on their efforts. I have received tons of good replies and comments and advice from them regarding SDCC, and it is well supported.
Another current project uses yet another chip, the STM8S001J3, which is super cool because I don't have to solder tssop 20's and because it's just so cute. It is tiny, itsy-bitsy, small, petite, and just plain not very big at all! Yet, it packs some good functionality, and can be programmed using a few different methods. Now let's go blink us some LED!
So, without any further comment...
\res MCU: STM8S103 \res export PA_ODR PA_DDR PA_CR1 3 B! ;: init ( -- ) [ ]B! [ ]B! ; : OUT! ( f -- ) PA_ODR
This was a really great moment for me. I had already been able to do some basic forth programming, and really written quite a few actually productive, functional bits of code for the other two STM8 micros that I thought this would be fun to write about. I got forth onto the chip and was able to connect immediately and start testing the parts of functionality that I knew I'd need. For those who'd like to follow along with their own hardware, I'm using stm8ef running on the STM8S001J3, accessed via usb on linux, and using e4thcom to connect to a modified cheap CH341 serial dongle to program the chip mounted on a tsop-8 breakout board plugged into a breadboard with an LED wired with a small (100R) resistor from pin 5 (PA3) to ground, and a 10K-25K pot from each rail with the wiper to pin 1 (ADC1 - AIN 6), and which I loaded stm8ef onto using an inexpensive stlinkv2 usb programmer. The SWIM programmer is connected in 2-wire fashion to pin 8 (SWIM) and ground. I do apologize for my run-on-stream-of-thought-sentences. Here's a short one.
So how did we get here? You need to be able to connect to your chip. And to make that a lot easier for you than it was for me, I made this script/wrapper for the e4thcom utility. And this also uses the good old-school programming pushd/popd trick to get you into the working direcgtory of your stm8ef installation so your comm terminal will automatically have access to the mcu, target and lib directories. And when you exit, you're back where you started in case, like me, you would otherwise loose your place. This was a great hint/correction from Thomas! I had previously been using -p argument incorrectly, which I had mis-interpreted as a "path to a default configuration file" not a default path, or what most folks will understand as "a list of directories" which the terminal will know that it can search for files in. This has made all the examples I was trying to learn from finally work. It is important to have your working environment set up so that you are comfortable in it, and also so that it works, particularly when you have it set up so it does not work and you don't realize it.
pushd /home/yournamehere/stm8ef e4thcom -t stm8ef --hdm -d ttyUSB0 -p mcu:target:lib popd
Once you can connect, it will look something like this. Press enter, and you should see the 'ok' if you're live.
e4thcom-0.8.2.64 : Serial Terminal for Embedded Forth Systems Copyright (C) 2020 email@example.com. This is free software under the conditions of the GNU General Public License with ABSOLUTELY NO WARRANTY. * Loading plug-in for stm8eForth > 2.2.14 (stm8ef.efc) Use the TAB key to cancel an upload process. * Loading config file (.e4thcom-0.8.2) editor command : /usr/bin/vi PATH: cwd:cwd/mcu:cwd/target:cwd/lib cwd : /home/clapp/Desktop/stm8-ef/stm8ef/ SIO : /dev/ttyUSB0 open hdl=3 B9600 8N1 half-duplex Type '\ [Enter]' to close the Terminal ok
Next thing I did was create a text file with some forth code in it, and this was work, because it was not the next thing the first time. I've been doing this for a while and trying things with my environment screwed up, so it never worked as easy as this. But here the program again.
\res MCU: STM8S103 \res export PA_ODR PA_DDR PA_CR1 3 B! ;: init ( -- ) [ ]B! [ ]B! ; : OUT! ( f -- ) PA_ODR
The \res and #require and \r and #r commands (along with the \i and #i and #include commands) do not all work from inside my forth terminal or from a file loaded, and/or the file would not even load and it was all very confusing. You should be able to type some code into a file and program your micro with it right? And that's programming right? I can compile something from a file (or files) with SDCC and make a binary image and flash it onto a micro and it runs. So that's kinda been my approach. And I hope to come back to that. But we're using forth, which is a shell, and an interpreter, and a language, and a compiler, and ... it's kinda confusing.
Starting from our 'ok' prompt (response?) we can "include" the file from inside our forth "shell" with shorthand "\i filename", provided it's in your current directory, or in your path, which we set above. It runs/interprets/compiles it as it reads it.
ok \i myout Uploading: ./myout \res MCU: STM8S103 \res export PA_ODR PA_DDR PA_CR1 $5000 CONSTANT PA_ODR ok $5002 CONSTANT PA_DDR ok $5003 CONSTANT PA_CR1 ok #require lib/]B! Uploading: ./lib/]B! \ STM8EF : ]B! MM-170927 \ ------------------------------------------------------------------------------ RAM ok \ Enable the compile mode and compile the code to set|reset the bit at addr. : ]B! ( 1|0 addr bit -- ) OK ROT 0= 1 AND SWAP 2* $10 + + $72 C, C, , ] OK ; IMMEDIATE ok \ ------------------------------------------------------------------------------ \\ Example: Closing: ./lib/]B! ok : init ( -- ) OK [ 1 PA_DDR 3 ]B! OK [ 1 PA_CR1 3 ]B! OK ; ok : OUT! ( f -- ) reDef OUT! OK PA_ODR 3 B! OK ; ok Closing: ./myout ok
Now I can turn on and off my LED! I get to use a fancy OUT! routine that writes to my output, in this case, and LED.
1 OUT! ok (LED turns on!) 0 OUT! ok (LED turns off!)
Note in particular the places forth has decided to say 'ok' during your file load, and when starting the e4thcom program too, and pretty much any time you're using forth. That's a prime clue! If you're not 'ok', then things are not going the way we want. Here's an example of how not to do it. Here I connect without setting my path and without changing to the directory with the file I'm looking for.
\i myout Uploading: myout ? Msg from HOST File open error \res MCU: STM8S103 ? Msg from HOST Unknown terminal directive \r MCU: STM8S103 Uploading: MCU: ? Msg from HOST File open error
You can see it does not know what "\res" is or where files are. You can use "e4thcom -h" to get a quick display of some of the options when writing you own script to launch it. I suggest writing scripts for anything that you have to copy and paste because it is too long too type often. That's what the shell is for. There are more commands, like "#e" for editing files, and "#ls" to see what is in your current directory. If there were "#cd", "#pwd" and a few others, I'd be super happy. Maybe have to go write that myself. Anyway, this is another example. Here, I'm in the stm8ef directory, and I have some files here. One is a timer example.
\r timer Uploading: ./timer : timer TIM 655 / . ; ok Closing: ./timer ok timer -24 ok : timer TIM 655 / U. ; reDef timer ok timer 65518 ok timer 30 ok
I open the timer example with "\r" to read it in. Great! Then I run it and it prints out some number, which is supposed to be like a number of seconds, but it's negative, and I remember that it should be displayed as an unsigned int, not a regular int. So I fix it on the fly. Then I'll have to remember to fix the file later. I wait a bit and run it again and it's rolled over where an unsigned int should. We're good.
Now I want to clean slate this and try some other things. Still in my e4thcom instance started in the correct place, but with no path set. The "reset" clears the immediately stored stuff and let's me try to load the timer code a few different ways. I verify first that timer is indeed no longer available.
ok reset STM8eForth 2.2.24 ok timer timer? \res timer ? Msg from HOST Unknown terminal directive #require timer Uploading: ./timer : timer TIM 655 / . ; ok Closing: ./timer ok timer 3 ok #r timer ok reset STM8eForth 2.2.24 ok #r timer Uploading: ./timer : timer TIM 655 / . ; ok Closing: ./timer ok
Note that \res does not work. But #require does then I reset and #r does. I'm not 100% sure where \res comes from, but #r, #require and \r are all from e4thcom. As well as their conterparts #i, #include and \i. There is a subtle difference in these and how they load things.
"The difference between #i and #r is that #i always loads a file and #r loads a file only if a word with the same name doesn't exist. If at the end of the file it still doesn't exist it creates a dummy word with the same name as the file." -Thomas
So there's an interesting effect you see in the above example where #r timer just replies with ok, and does not load it. This is gong to be different if we use one of the include methods like #i.
Well I promised you we'd blink some LED, so here goes.
: delay 0 do loop ; ok : blink 1 out! 10000 delay 0 out! 10000 delay ; reDef blink ok : blinks 0 do blink loop ; ok 30 blinks ok
This blinks the LED 30 times, as you might have guessed.