STM8S003 C Example Program Reference
STM8S003 Example Program Reference
The STM8S003 (typically STM8S003F3 or STM8S003K3) is an 8-bit microcontroller from STMicroelectronics with a maximum CPU frequency of 16 MHz, 8 KB Flash, 1 KB RAM, 128 bytes EEPROM, 10-bit ADC, three timers, UART, SPI, I²C, and up to 28 GPIOs depending on package.
The examples below are written in C using SDCC (Small Devices C Compiler), the most common open-source toolchain for STM8. They use direct register access (bare-metal style) rather than the deprecated STM8 Standard Peripheral Library (SPL), as this is more transparent and widely used in modern STM8 projects.
Common setup notes:
- Compile with SDCC:
sdcc -mstm8 --std-c99 main.c -o output.ihx - Flash using stm8flash or openOCD with an ST-LINK.
- No built-in filesystem exists → "append to file" is not possible without external storage (e.g., SD card via SPI, not covered here).
- UART output appears on PD5 (TX) / PD6 (RX) at 115200 baud (common default).
1. GPIO pin high/low at 1 ms intervals (toggle / blink)
This toggles PD0 every 1 ms using a busy-wait loop calibrated approximately for 16 MHz.
#include <stm8s.h>
#define LED_PORT GPIOD
#define LED_PIN GPIO_PIN_0
void delay_ms(uint16_t ms)
{
while (ms--)
{
uint16_t i = 16000; // Approx 1 ms @ 16 MHz (tuned empirically)
while (i--);
}
}
void main(void)
{
CLK->CKDIVR = 0x00; // No division → CPU @ 16 MHz (HSI)
// Configure PD0 as push-pull output, fast mode
LED_PORT->DDR |= LED_PIN; // Direction: output
LED_PORT->CR1 |= LED_PIN; // Push-pull
LED_PORT->CR2 |= LED_PIN; // Fast mode (up to 10 MHz)
while (1)
{
LED_PORT->ODR ^= LED_PIN; // Toggle
delay_ms(1);
}
}
Note: Busy-wait is imprecise; use TIM4 or TIM2 for better accuracy in production code.
2. Read ADC 20 times per second
The STM8S003 has a 10-bit successive approximation ADC (ADC1) with up to 10 multiplexed channels (e.g., AIN2 on PD2). No filesystem is available natively.
This example reads channel 2 (PD2 / AIN2) at 20 Hz and prints the value (0–1023) over UART.
#include <stm8s.h>
#include <stdio.h> // For printf (requires retargeting, see UART section)
void delay_ms(uint16_t ms); // Same delay function as above
void adc_init(void)
{
ADC1->CR1 = 0x00; // ADC off, single conversion, fADC = f/2
ADC1->CR2 = 0x08; // Right aligned, no external trigger
ADC1->CR1 |= ADC1_CR1_ADON; // Enable ADC (first write powers on)
for (volatile uint8_t i = 0; i < 10; i++); // Short delay after power-on
ADC1->CR1 |= ADC1_CR1_ADON; // Second write starts calibration
while (ADC1->CR1 & ADC1_CR1_ADON); // Wait for calibration end
}
uint16_t adc_read(uint8_t channel)
{
ADC1->CSR = (channel & 0x0F); // Select channel
ADC1->CR1 |= ADC1_CR1_ADON; // Start conversion
while (!(ADC1->CSR & ADC1_CSR_EOC));
ADC1->CSR &= ~ADC1_CSR_EOC;
return (uint16_t)((ADC1->DRH << 8) | ADC1->DRL);
}
void main(void)
{
CLK->CKDIVR = 0x00; // 16 MHz
adc_init();
// UART init here (see section 4)
while (1)
{
uint16_t value = adc_read(2); // Channel 2 (PD2)
printf("ADC: %u\n", value);
delay_ms(50); // 20 Hz
}
}
Hardware note: Connect analog signal to PD2 (AIN2). VREF+ = VDD, VREF- = VSS.
3. PWM example with 5 different duty cycles
TIM2 (16-bit general-purpose timer) supports PWM on up to three channels. This example uses TIM2_CH1 on PD4 at ~1 kHz with 10-bit resolution (ARR=999).
#include <stm8s.h>
void tim2_pwm_init(void)
{
TIM2->PSCR = 0x04; // Prescaler = 16 → timer clock = 1 MHz
TIM2->ARRH = 0x03; // ARR = 999 (period = 1 ms → 1 kHz)
TIM2->ARRL = 0xE7;
TIM2->CCMR1 = 0x60 | 0x08; // PWM mode 1 + OC1PE (preload enable)
TIM2->CCER1 = 0x01; // CC1E = 1 (enable output)
TIM2->CR1 = 0x80 | 0x01; // ARPE + CEN (auto-reload preload + enable)
}
void main(void)
{
CLK->CKDIVR = 0x00;
// PD4 as alternate function (TIM2_CH1)
GPIOD->DDR |= GPIO_PIN_4;
GPIOD->CR1 |= GPIO_PIN_4;
GPIOD->CR2 |= GPIO_PIN_4; // Fast mode
tim2_pwm_init();
uint16_t duties[] = {0, 250, 500, 750, 999}; // ≈0%, 25%, 50%, 75%, 100%
while (1)
{
for (uint8_t i = 0; i < 5; i++)
{
TIM2->CCR1H = duties[i] >> 8;
TIM2->CCR1L = duties[i] & 0xFF;
// printf("Duty: %u / 999\n", duties[i]); // optional
for (volatile uint32_t d = 0; d < 800000; d++); // ~2 s delay
}
}
}
Alternative channels: TIM2_CH2 (PD3), TIM2_CH3 (PA3 / remapped).
4. Output to serial port (UART1)
STM8S003 has one UART (UART1) with TX on PD5, RX on PD6. No native USB interface exists.
This example prints a counter at 115200 baud.
#include <stm8s.h>
#include <stdio.h>
// Minimal putchar retargeting for printf
int putchar(int c)
{
while (!(UART1->SR & UART1_SR_TXE));
UART1->DR = (uint8_t)c;
return c;
}
void uart_init(void)
{
// 16 MHz → 115200 baud
UART1->BRR2 = 0x03;
UART1->BRR1 = 0x01; // Mantissa + fraction
UART1->CR2 = UART1_CR2_TEN; // Enable TX only
// PD5 (TX) as alternate function
GPIOD->DDR |= GPIO_PIN_5;
GPIOD->CR1 |= GPIO_PIN_5;
GPIOD->CR2 |= GPIO_PIN_5;
}
void main(void)
{
CLK->CKDIVR = 0x00;
uart_init();
uint32_t counter = 0;
while (1)
{
printf("Counter: %lu\n", counter++);
for (volatile uint32_t d = 0; d < 1600000; d++); // ≈1 s
}
}
Compile with -lstm8 if using SDCC's minimal libc, or provide your own putchar.
5. Clocks on the STM8S003 and how to read them
The STM8S003 clock tree includes:
- HSI — Internal 16 MHz RC oscillator (factory calibrated, ±1% at 25 °C)
- HSE — External crystal/ceramic resonator (1–16 MHz)
- LSI — Internal 128 kHz RC (for watchdog or AWU)
- fMASTER — Master clock (HSI, HSE, or LSI; prescaled by 1/2/4/8/16/32/64/128 via CLK_CKDIVR)
- fCPU — CPU clock = fMASTER (can be further divided in low-power modes)
- Peripheral clocks — Gated individually (e.g., TIM2, ADC, UART)
No direct frequency measurement API exists. Read via:
CLK->CKDIVR— Current prescalerCLK->SWCR&CLK->SWIM— Clock source status / switchingCLK->ICKCR— HSI/LSI ready & trimCLK->ECKCR— HSE ready & bypass
Example program printing clock configuration:
#include <stm8s.h>
#include <stdio.h> // Assumes UART init from section 4
void uart_init(void); // From section 4
void main(void)
{
CLK->CKDIVR = 0x00; // Max speed (16 MHz, no division)
uart_init();
while (1)
{
uint8_t div = CLK->CKDIVR & 0x07;
uint32_t f_master = 16000000UL >> div;
printf("fMASTER ≈ %lu Hz (CKDIVR=0x%02X)\n", f_master, CLK->CKDIVR);
if (CLK->SWCR & CLK_SWCR_SWIF)
printf("HSI ready\n");
if (CLK->ECKCR & CLK_ECKCR_HSERDY)
printf("HSE ready\n");
if (CLK->ICKCR & CLK_ICKCR_LSIRDY)
printf("LSI ready\n");
for (volatile uint32_t d = 0; d < 3200000; d++); // ≈2 s
}
}
Typical startup values:
- fMASTER = 16 MHz (HSI, CKDIVR = 0x00)
- To use HSE: enable in CLK_ECKCR, switch via CLK_SWR / CLK_SWCR
These examples are compatible with SDCC and bare-metal STM8 development. For production use, calibrate delays/timing with timers and consider enabling interrupts where appropriate. If you require examples using sduino (Arduino-like layer for STM8) or Cosmic compiler syntax, please specify.