Skip to content

STM8S Programming

Thomas edited this page Oct 30, 2020 · 102 revisions

Getting Started with STM8 Programming

This page once was started as a scratchpad for the authors first steps to STM8 programming using Free Open Source Software (FOSS). It has since evolved into a page for sharing information on this subject (and on STM8 eForth too, of course ;-) ).

If free-to-use commercial tools are available, why bother using FOSS tools?

First, FOSS tools run on Linux and can be used in a Docker container which is perfect for Continuous Integration and test automation.

This is something you won't hear commercial tool providers bragging about - just try installing a license on Travis-CI! While most FOSS tools are available for Windows, automation can be very difficult. This is why some people have used SDCC (and the tools for building STM8 eForth) on a Raspberry Pi, Odroid, or a Mac. Microsoft now offers the Windows Subsystem for Linux (WSL), and the situation for Windows users has improved: most Linux binaries can be used on WSL, and the rest should work on WSL2 (but I didn't try that).

Second, access to the source code not only allows understanding how the tools work, it also allows improving tools, or engaging with a community of developers.

I recommend using the free SDCC (Small Device C Compiler) tool chain which also enables building and testing STM8 code cloud-computing style. The quality of the produced code is competitive, and the developer community is very active.

If you're looking for prototyping hardware the STM8 breakout board is worth checking out, and then there are low cost boards that, after some modest modification, might be a good fit for your automation project.

If you're used to programming C you may find the lightweight STM8 eForth useful for exploring STM8 features, for testing a new PCB step by step and for test automation, or for prototyping new ideas, and in some cases Forth might be the interactive feature you don't need to write. If you want to try using Forth as a scripting engine for your C application, e.g. on an STM8S105, please open a GitHub issue. I'm in.

In any case it's worth a try: get a binary from the releases page, and try flashing your STM8 board. It's amazing to "talk" to a µC and in the next sections you'll need a binary for practicing anyway ;-)

STM8 Devices

All STM8 devices share the same 8 bit core, and the programming manual PM0044 applies.

STM8 devices can be divided in three-and-a-half families:

Family Industrial-Commercial Automotive Manual
Mainstream STM8S STM8AF RM0016
Low Power STM8L STM8AL RM0031
Low Power/2 STM8Lx01 - RM0013
Low Power/3 STM8TL5xxx - RM0312

STM8S Families

The ST marketing department presents "product lines" and things are sometimes a bit hard to follow. For example, in the STM8S family there are STM8Sx03 "low density", STM8Sx05 "medium density", and STM8Sx07 "high density" devices which are presented as product lines:

STM8Sx Line Examples
x=0 Value Line STM8S001, STM8S003, STM8S005, STM8S007
x=1 Access Line STM8S103, STM8S105
x=2 Performance Line STM8S207
x=9 Application Specific Line STM8S903

The label "Value", "Access" and "Performance" doesn't have anything to do with the performance, and for hobbyists and makers the "Value Line" is the most accessible anyway. The "Value Line" includes the same "low density", "medium density" or "high density" chips as the other lines. The difference is what's guaranteed in the specs (including memory size). An STM8S007 has the same "performance" as an STM8S207, and experiments show that an "Access Line" STM8S105 can run at 24MHz (an STM8S103 can't add a wait-state). There is "value" in the "Access Line" if your application needs to guarantee retaining Flash contents after, say, 100 erase-write cycles: unless you buy the more expensive device you might not be able to pass on the hot potato if it doesn't ;-). Finally, the "Performance Line" STM8S207 might be good value for money: it's often cheap enough to fit the bill.

The "User manual STM8 SWIM communication protocol and debug module" UM0430 gives away that reality isn't that complicated:

UM0430 Table 6

The highlighted automotive STM8AF appears to be the "source" of STM8S devices, and the information on this page applies to them quite naturally. On the other hand, exploring STM8AF datasheets sometimes helps to uncover undocumented features.

UM0430 also contains a rare recent reference to "256 Kbyte die" devices (see STM8AF51Bx, STM8AF61Bx in the 2009 product line-up) - these are no longer listed.

Datasheets sometimes appear to be the result of editing without any regard to the underlying silicon, sometimes hiding features that are obviously there (e.g. the 96-bit unique ID in STM8S Value Line devices). Some own research (and of course to be taken with a large grain of salt) is presented on the pages STM8 Low Density Devices and STM8 Medium and High Density Devices.

STM8L Families

This page concentrates on programming the STM8S. Of course, programming is "about the core" and most of the contents can be applied to STM8L, STM8AL and, probably, the "touch UI" SoCs STM8TL5xxx.

Devices in the STM8L family RM0031 have a much richer set of peripherals than STM8S devices and it may take some time to understand the different layers of configuration (clock tree, peripheral, routing interface, analog MUX, interrupts, DMA and timers.

It's a good idea to keep the following in mind:

  • the GPIO interrupt configuration is quirky but neat
  • timers are mostly the same
  • first of all configure the clock tree
  • learn to know the Routing Interface (RI)
  • the analog features have at least one level of indirection more than you think they have

STM8L Low Density devices in the RM0013 family are no more complex than STM8S devices (clock configuration is a bit different and the GPIO interrupt configuration is just like in the larger STM8L family) - on the contrary some STM8S features are absent.

Flashing the STM8

The normal way of programming an STM8 device is soldering the bare device to a circuit board and using ICP (In Circuit Programming). For ICP at least PD1/SWIM needs to be accessible. Usually there is an ICP connector with the pins VDD, SWIM, GND and NRST (this is the order on ST's demo boards but I've seen all kinds of variants and NRST isn't always needed).

If you don't know the order of the ICP pins try this:

  1. find the GND pin and connect your programmer (this should be easy by checking what's connected with a known GND point)
  2. identify the VDD (supply) pin and connect 3.3V from your STLINK programmer
  3. connect NRST and SWIM the remaining pins and try to read from your device (see below)

If this doesn't work try different pin combinations in steps 2 and 3. I've never seen a STM8 board die by doing it (I've tried ;-) ), avoid using 5V (unless you're certain that your board runs on 5V), and by all means avoid reversing GND and VDD.

Note that sometimes PCB manufacturers chose to program uCs before assembly, and their design doesn't provide an ICP connector. Often PD1/SWIM can be traced but the ICP pin NRST isn't accessible. If the GPIO PD1/SWIM is in input mode, ICP programming works anyway (usually it takes one retry). The low-pin-count device STM8S001J3 doesn't have an NRST pin and relies solely on this method.

Alternative ways of programming STM8 devices are:

  • IAP (In Application Programming) with factory ROM code,
  • or IAP with a user defined method, e.g. with a tool like stm8gal

After the initial programming with ICP, STM8 eForth uses IAP for compiling machine code directly into memory.

Flash can be used like RAM, literally: after unlocking Flash memory, when you write one or more bytes, the memory bridge performs a read-modify-write process which halts the STM8 core (i.e. even code execution from RAM will be stalled). This ain't fast but it's very easy to use! At one time, there was a bug in the STM8 eForth kernel that caused Flash memory to be used as the scratch-pad in interpreter mode - the bug was discovered because of unexplained core stalling, and not because of wear-out! STM8 Flash memory won't wear out easily - development chips survive many thousand write cycles without noticeable effect even if the datasheet says "100 write cycles" (e.g. STM8S003 Value Line). Of course, one should never exceed the specified number of write cycles in "production systems", and a development chip with a high number of writes should never be deployed!

STM8L devices (e.g. STM8S050J3M3 or STM8L152R8T6) do IAP just like an STM8S chip. STM8L101 family devices, including STM8L001J3M3, appeared to show a different behavior. The only real difference is how resetting the read-out protection works!.

STM8FLASH for Linux Systems

For initial flashing of an STM8 device with ICP you'll need an ST-LINK V2 compatible adapter, e.g. a $2 device like this one:

ST-LINK V2

An ordinary ICP adapter applies serial resistors in the control lines, and when using the 3.3V supply output pin it will happily power up the µC. I've never seen confusing PD1/SWIM and NRST (even with GND or VCC) causing damage to a device, but I always take care of not swapping supply lines and I rarely use the 5V output pin. ST's "official ST-Link" (or cheap clones for $5) need an external power supply.

The open source project stm8flash provides basic access to memory areas of STM8S devices (Flash, option bytes, EEPROM, RAM). STM8FLASH should also work on the Raspberry Pi and on macOS. I've had difficulties using stm8flash for STM8L101 devices with cheap $2 adapters or with a convincing looking ST-Link V2 clone. The situation may have improved - if it works please let me know it!

STM8FLASH Installation

The following steps for installing stm8flash worked for me:

  • clone stm8flash
  • provide libusb-dev (e.g. sudo apt-get install libusb-1.0-0-dev)
  • in the stm8flash folder, run make and sudo make install
  • create /etc/udev/rules.d/99-stlinkv2.rules with contents SUBSYSTEM=="usb", ATTRS{idVendor}=="0483", ATTRS{idProduct}=="3748", MODE="0666"

The udev rules file in the last step can be created with the following commands:

echo 'SUBSYSTEM=="usb", ATTRS{idVendor}=="0483", ATTRS{idProduct}=="3748", MODE="0666"' | sudo tee /etc/udev/rules.d/99-stlinkv2.ruleS > /dev/null

For testing, connect an ST-Link adapter (or one any of the many $2 clones, preferably ST-Link V2 compatible) to the SWIM connector of your STM8S board (mind the voltage and the polarity!), and try reading from your STM8 µC, e.g.:

$ stm8flash -c stlinkv2 -p stm8s103f3 -r test.ihx
Determine FLASH area
Reading 8192 bytes at 0x8000... OK
Bytes received: 8192

Running stm8flash without parameters will show the command line options.

Resetting STM8S devices to factory defaults

For unfathomable reasons some manufacturers of STM8SF103F3P6 breakout boards protect their "Blinking LED intellectual property" by setting the Flash protection option bits. The result is the following error message when you try to actually use your board:

NOTE: never apply the defaults on the following line to a STM8L device since doing that may render the chip unusable!

stm8flash -c stlinkv2 -p stm8s103f3 -w led.ihx 
Determine FLASH area
Writing Intel hex file 233 bytes at 0x8000... Tries exceeded

The solution is a reset to factory defaults of the µC. The stm8ef Makefile has a make target for this:

$ make defaults
stm8flash -c stlinkv2 -p stm8s103f3 -s opt -w tools/stm8s103FactoryDefaults.bin
Determine OPT area
Writing binary file 11 bytes at 0x4800... OK
Bytes written: 11

There is also a defaults file for Medium Density devices (STM8S105). Find out more about differences between the device families here.

Setting STM8 Option Bytes from the Command Line

Any option bytes settings can be easily programmed from the command line.

Here is how to create defaults:

echo "00 00 ff 00 ff 00 ff 00 ff 00 ff" | xxd -r -p > factory_defaults.bin
stm8flash -c stlinkv2 -p stm8s103f3 -s opt -w factory_defaults.bin

In order to produce PWM on PC6 of an STM8S103F3P6 Bit0 of OPT2 needs to be set (from the STM8S103F3P6 datasheet: Port C5 alternate function = TIM2_CH1; port C6 alternate function = TIM1_CH1; port C7 alternate function = TIM1_CH2.).

Note that all option bytes but OPT0 need to be written normal and bit-inverted. Thus setting OPT2 to 1 works like this:

echo "00 00 ff 01 fe 00 ff 00 ff 00 ff" | xxd -r -p > set_opt2_to_1.bin
stm8flash -c stlinkv2 -p stm8s103f3 -s opt -w set_opt2_to_1.bin

Hint: in STM8 eForth the library word OPT! can be used for interactive (or program controlled) setting of option bytes (an example is the servo control demo).

Setting STM8 Option Bytes with STVP

The Windows tool STVP provided by ST only shows the normal, not the inverted memory cells (which makes sense). The sequence 0x00 0x00 0x00 0x00 0x00 0x00 for OPT0 to OPT5 of an STM8S Low Density device will be converted to the sequence 0x00 0x00 0xFF 0x00 0xFF 0x00 0xFF 0x00 0xFF 0x00 0xFF.

ST-LINK Utility for Windows

For Windows please follow the instructions on ST's STVP-STM32 page (STVP also supports ST 8bit µC families).

Using SDCC for STM8

Most examples for the STM8 controller family are based on a commercial C compiler. The FOSS SDCC tool chain offers support for STM8 and other 8-bit µCs and the generated code is quite good. The SDCC documentation for STM8 is rather thin, but what's not documented in the docs can be found by browsing the source code.

Notable gaps in the documentation for STM8 include linker, the assembly-C-interface (but see here), and the hard-coded startup code (function stm8_genInitStartup in /sdcc/src/stm8/main.c). SDCC won't allocate RAM address 0x0000 to avoid that a test for NULL accidentally goes wrong. When you use the RAM location 0x000000 in Assembly or Forth be aware that SDCC's RAM initialization starts at address 0x0001, too.

SDCC: using the Assembler

I've never used the free-to-use ST STM8 Assembler since it requires Windows. Since µC assembler code isn't known for portability (e.g. macros, includes, lifetime of symbols ...) the main question is if it works well in a tool chain (as long as it isn't too limited).

The SDCC tool chain uses a fork of ASxxxx Cross Assemblers V1.7x as an integral part of the code generator chain. The ASxxxx assembler suite supports many µCs and µPs, and it offers powerful features (e.g. macros, paging support). However, the set of directives differs a lot from the standard ST assembler, and hence the original stm8ef.asm code had to be converted to ASxxxx syntax with the help of an AWK script.

Due to licensing concerns the ASXXXX fork has been out of sync with the upstream development for a long time, and new features from recent versions of ASxxxx were added selectively (e.g. the macro feature). However, it's not documented in the SDCC project which of the later ASxxxx 4.0 or 5.0 features were added, and which are still missing. Furthermore, the integration of SDASSTM8 into SDCC is rather rough, and the existing documentation of the SDCC assembler interface concentrates on the MCS51 architecture.

From the outset, I had planned to enable mixing Forth with C which meant that I had to work with the ASxxxx fork used in SDCC. Since little is documented, figuring out how to work with STM8 assembly code, and how to work with mixed sources in the Makefile, required reading the SDCC sources and to experimenting with generated C code.

In SDAS V2.0.0 of release SDCC V3.6.0, and development version SDCC V3.6.6 (rev. 9930), I observed the following:

  • SDASSTM8 doesn't pass all tests in the ASXXXX 4.x.x tool chain's *tst8.asm test file (the issues are minor, e.g. RLWA X,A must be replaced with RLWA X and the feature Base STM8 Instructions with External symbols is unsupported)
  • the patched version supports "r,(offs,SP)" addressing for ADC, ADCW, SUB and SUBW (added to ASXXXX only in version 5.3)
  • the new directive .optsdcc passes the SDCC target parameter (e.g. -mstm8) to the linker
  • conditional assembly with boolean expressions doesn't always work as expected. As a work-around I used arithmetic expressions, e.g. multiplication instead of AND or addition instead of OR.
  • comparing stm8pst.c shows that the following ASxxxx V5.20 are missing in SDCC:
    • .ifdef, .ifndef, .ifb, .ifnb, .ifidn, and .ifdif
    • .iifdef, .iifndef .iifb, .iifnb, .iifidn, and .iifdif
    • .define, and .undefine
    • .4byte, .quad, .blk4, and .bank
    • .msg, .assume, and .error
    • .msb, .lohi, .hilo, .8bit, .16bit, .24bit, and .32bit
  • startup code, and linker features for STM8 are hard coded, and depend on main.c
  • in SDCC V3.6.0 interrupts can only be defined in main.c (this is maybe about to change in development version SDCC V3.6.6). Defining interrupts in Forth works by simply writing a pointer to the interrupt vector (which should also work in C).
  • in recent SDCC development revisions (e.g. 9933) start-up code may be placed in the unused interrupt vectors 0x806C .. 0x807f
  • the frequent use of assembler macros in STM8 eForth exposed some bugs in the macro expansion code: some things just don't work for unfathomable reasons (e.g. macros used just before end of include files)
  • macros have to be defined before use (unlike other symbols)
  • expansion of macros with strings that contain assembler deliminator characters ("," and ";") can result in a segfault. The bug is described here and a patch I proposed has since been applied. Using octal escapes for "," and ";" helps.

In SDCC it's not possible to define symbols on the command line (at least up to SDCC V3.6.6). A work-around is putting configuration files with defines in folders, and setting the directory search path with the "-I" option (that's how TG9541/STM8EF board-support folders work).

Combining C and Assembly

The SDCC manual section 3.2.3 Projects with Multiple Source Files describes how a main component can be linked with outer object files (the .rel output files from the assembly component are the object files in SDCC).

Both the old stable SDCC 3.6.0 and the current development branch (e.g. SDCC 3.8.4 R10768) do parameter passing in the following way:

  1. parameters are copied to the return stack in the relevant size, ready for access with "SP indirect addressing", and in the reverse order of the definition
  2. Return values are passed in registers:
  • 8bit: A
  • 16bit: X
  • 24bit: X and YL
  • 32bit: X (MSW) and Y (LSW)
  • 64bit: hidden pointer
  • returning structs isn't implemented (error 54).

There is discussion in Issue #235 about mixing C with Forth (or assembler routines). According to this thread it can be considered "somewhat stable" even if it's an implementation detail of the backend, and it was documented in the development branch of the SDCC documentation (which means it's now semi-official).

SDCC: the Simulator uCsim

The SDCC tool chain includes the multi µC simulator uCsim. The development version SDCC V3.6.6 (from rev. 9923 on) was the first that could run STM8 eForth in the simulator sstm8 - that's quite a feat! Thanks to the kind support from the uCsim author, from the development version ucsim-0.6-pre29 there is basic Flash control. Running Forth, and compiling Forth code into simulated Flash memory (IAP) just works.

A more stable support with "lockstep" between UART simulation and the telnet-UART interface was introduced in SDCC R10770 (see details). Timers and interrupts now also work - STM8 eForth tests a simple form of multi-tasking in Travis-CI.

Run sstm8 in a first terminal window:

./sstm8 -g -w -tS103 -Z10001 -Suart=1,port=10000 out/MINDEV/MINDEV.ihx

For interacting with the Forth console (or any other application that uses the UART for communication) in the simulated µC start telnet in a second terminal:

telnet localhost 10000

Note that CTRL-h works as backspace.

In the example above accessing the uCsim console works with telnet localhost 10001 or with nc. Check out simload.sh for some test automation examples!

From STM8 eForth v2.2.13.snapshot on, tools are provided to compile Forth code into a STM8S binary using uCsim using interactive execution (refer to Build and Test Automation).

Finally, a step-by-step guide for using a containerized SDCC environment in STM8 eForth Hands On with uCsim.

The SDCC Tool Chain by Example

This chapter contains some results from my first steps with SDCC and the STM8S target.

At the time of writing ST had imposed overly restrictive licenses to their STM8S Standard Peripheral Library (SPL) that made it using it a legal hazard (under any usage scenario, open source or not). Since the SDCC linker doesn't support libraries as object code like commercial compilers (why should it?) most examples code used direct register address references and flag defines to work with STM8 peripherals. In the meantime

Ken Yap pulled together the available SPL derived header files and tools and wrote a how-to. So, please go ahead and try using the SPL in your STM8 SDDC project!

The SDCC Assembler and Linker

In SDCC 3.6.0 the assembler and linker are a fork of the outdated V2 of the ASSxxxx assembler suite. Both tools are called from with the SDCC compiler main.c as integral part of the optimization & link process. The required parameters and intermediate files find no mention in the manual, and I had to read them from src/stm8/main.c.

The following procedure worked:

  • execute the SDCC led.c test procedure described before, and confirm that all works as expected
  • remove all generated files but led.asm, and led.lk
  • store the following file as build.sh
#!/bin/sh
sdasstm8 - -plosgffw $1
sdldstm8 -nf $1.lk
stm8flash -c stlinkv2 -p stm8s103f3 -w $1.ihx
  • run ./build.sh led, and confirm that the test works as expected

Note:

  • led.lk contains the .area declarations for HOME = 0x8000, and DATA = 0x0001.
  • the test works fine with DATA = 0x0000. In a talk at Fosdem 2015 the authors states that this is due to concerns about null-pointers.

Minimal SDCC test: led.c

Patched for the cheap STM8S103F3 breakout board without crystal:

led.c

#include <stdint.h>

#define CLK_DIVR	(*(volatile uint8_t *)0x50c6)
#define CLK_PCKENR1	(*(volatile uint8_t *)0x50c7)

#define TIM1_CR1	(*(volatile uint8_t *)0x5250)
#define TIM1_CNTRH	(*(volatile uint8_t *)0x525e)
#define TIM1_CNTRL	(*(volatile uint8_t *)0x525f)
#define TIM1_PSCRH	(*(volatile uint8_t *)0x5260)
#define TIM1_PSCRL	(*(volatile uint8_t *)0x5261)

#define PB_ODR	(*(volatile uint8_t *)0x5005)
#define PB_DDR	(*(volatile uint8_t *)0x5007)
#define PB_CR1	(*(volatile uint8_t *)0x5008)


unsigned int clock(void)
{
	unsigned char h = TIM1_CNTRH;
	unsigned char l = TIM1_CNTRL;
	return((unsigned int)(h) << 8 | l);
}

void main(void)
{
	CLK_DIVR = 0x00; // Set the frequency to 16 MHz

	// Configure timer
	// 1000 ticks per second
	TIM1_PSCRH = 0x3e;
	TIM1_PSCRL = 0x80;
	// Enable timer
	TIM1_CR1 = 0x01;

	PB_DDR = 0x20;
	PB_CR1 = 0x20;

	for(;;)
		PB_ODR = clock() % 1024 < 256 ? 0x20 : 0;
}

Source by Philipp Klaus Krause with hint about the used GPIO from here, and missing register addresses from the datasheet (hint: in the meantime the original code has been updated and can be used right away).

Compile and flash with:

sdcc -mstm8 --std-c99 led.c 
stm8flash -c stlinkv2 -p stm8s103f3 -w led.ihx 

Using GDB-STM8

First notes on using openocd and gdb-stm8 are in my gdb-stm8 hands-on Gist.

Various problems & solutions

This section contains some of the problems I encountered and their solutions.

Code Execution from Different Memory Areas

The STM8 applies Harvard architecture (separate instruction and data buses) although the core's bus interface hides that fact: a bus bridge maps all memory classes into a linear memory layout. However, access to RAM doesn't have the performance advantage of a 32 bit fetch, and the code run from RAM is slower than code from Flash memory.

Contrary to RAM, the bus bridge doesn't allow for code execution from the EEPROM memory:

\ Write RET to the first byte in RAM
$81 0 C! 0 EXECUTE ok
\ Unlock the EEPROM and write RET to the first byte
ULOCK $81 $4000 C! LOCK ok
\ try to execute from EEPROM
$4000 EXECUTE
STM8eForth 2.2.20 ok
\ execution from the EEPROM caused a reset

As of STM8 eForth v2.2.21 certain Forth dictionary entries can be created in the EEPROM so that some space in the Flash memory can be conserved. Please refer to issue #178.

Time Critical Code

The STM8S architecture has a 3-stage pipeline, and the length of instructions varies from 1 to 5 bytes with an average size of 2 bytes. A fetch from Flash gets 4 bytes (aligned at 32bit boundaries) in one clock cycle filling a pre-fetch buffer. In linear code, this makes the execution time less dependent on the instruction length. However, after a branch, the pipeline is flushed, and code execution time then depends heavily on the location of instructions relative to a 32bit boundary. When moving a group of "spin loop" instructions by just one byte large execution time changes can be observed!

For writing code where the exact execution time is crucial the following approaches can be taken:

  • for coarse granularity (e.g. 100µs) use a timer
  • in Flash, unroll loops, or align code carefully with 32bit boundaries
  • copy (small) routines to RAM for execution

STM8EF uses a timer for optional UART simulation code.

Examples are some examples for generating video on the STM8, and there have been attempts for implementing virtual USB on the STM8S103F3 which demonstrate that time-critical STM8S bit-banging is feasible. So far, however, non of this has been shown to be as usable as solutions with non-pipeline µCs.

STM8 16 Bit Register Access

To be blunt, the STM8 memory interface to 16bit peripherals is kludgy. What's documented in the STM8S Reference Manual wouldn't surprise anyone if it where in an errata sheet:

E.g. 17.3.1 Reading and writing to the 16-bit counter:

An 8-bit buffer is implemented for the read. Software must read the MS byte first, after which the LS byte value is buffered automatically ... This buffered value remains unchanged until the 16-bit read sequence is completed. Note: Do not use the LDW instruction to read the 16-bit counter. It reads the LS byte first and returns an incorrect result.

Or also 7.3.2 Write sequence for 16-bit TIM1_ARR register:

16-bit values are loaded in the TIM1_ARR register through preload registers. This must be performed by two write instructions, one for each byte. The MS byte must be written first. The shadow register update is blocked as soon as the MS byte has been written, and stays blocked until the LS byte has been written. Do not use the LDW instruction as this writes the LS byte first which produces incorrect results.

The bottom line is: check the reference manual before coding your hardware register access routines.

Note that TG9541/STM8EF provides the words 2C@ and 2C! for 16 bit reversed order-of-access read- and write operations that can be readily used when the reference manual requires a "MS byte first" 16 bit access.

UART Transmit Interrupt Handling

Sending the contents of a buffer with UART TXE and TC interrupt events turned out not to work as I had expected, and there is little guidance on how to initialize, start, spin down and end a transmission.

It turns out that most has to be done with the interrupt interrupt enable flags TIEN and TCIEN in UART_CR2.

This is what worked for me:

  • both TIEN and TCIEN should be normally disabled
  • after setting up buffers and pointers, enable TIEN to start a transmission
  • in the ISR, during the transmission, writing to UART_DR clears the interrupt event
  • in the ISR, when the buffer is empty, disable TIEN and enable TCIEN to spin down the ISR chain
  • in the ISR, read UART_SR to check if TC is set. If that's the case, clear TCIEN

The advantage of this procedure is that media access control, e.g. for RS485, can be woven into the ISR chain.

Example code (in Forth) is here.

STM8S Programming Primers

Linux Primers

All using the low cost STM8S103F3 breakout board, an ST-LINK V2 clone, SDCC, and stm8flash:

Other Primers

  • Blinking LED: IAR workbench, different STM8S103F3 breakout, same ST-LINK V2 clone
Clone this wiki locally