STM8 - программирование, прошивка и всё-всё-всё

Наряду с Arduino у любителей микроконтроллеров сейчас популярна и линейка продуктов от компании STMicroelectronics, включающая 8-разрядные микроконтроллеры STM8 и 32-разрядные микроконтроллеры STM32 (на ядре Cortex).

Микроконтроллеры STM8 состоят из нескольких линееек:
STM8S - основная линейка,
STM8A - для автомобильной промышленности,
STM8L - со сверхнизким потреблением энергии,
STM8T - емкостный сенсор для детектирования прикосновения или приближения.

Отладочную плату с микроконтроллером STM8 на борту можно приобрести за 1 (!) доллар и даже дешевле. Я приобрел несколько таких плат  на основе микроконтроллера STM8S103F3P6 на торговой площадке ebay:
STM8S103F3P6

контакт назначение
D4 PD4 / UART_CLK
D5 PD5 / TX
D6 PD6 / RX
RST сброс
A1 PA1 / Oscin
A2 PA2 / Oscin
GND земля
5V вход стабилизатора
3V3 выход стабилизатора
A3 PA3 / SS
D3 PD3 / Ain4
D2 PD2 / Ain3
D1 PD1 / SWIM
C7 PC7 / MISO
C6 PC6 / MOSI
C5 PC5 / SCK
C4 PC4 / Ain2
C3 PC3
B4 PB4 / SCL (шина I2C)
B5 PB5 / SDA (шина I2C)
Вот альтернативное представление распиновки микроконтроллера:
                            -----------
UART1_CK / TIM2_CH1 / PD4  |  1     20  |  PD3 / AIN4 / TIM2_CH2 / ADC_ETR
    UART1_TX / AIN5 / PD5  |  2     19  |  PD2 / AIN3
    UART1_RX / AIN6 / PD6  |  3     18  |  PD1 / SWIM
                     NRST  |  4     17  |  PC7 / SPI_MISO
              OSCIN / PA1  |  5     16  |  PC6 / SPI_MOSI
             OSCOUT / PA2  |  6     15  |  PC5 / SPI_CLK
                Vss (GND)  |  7     14  |  PC4 / TIM1_CH4 / CLK_CCO / AIN2
                VCAP (*1)  |  8     13  |  PC3 / TIM1_CH3 /
                Vdd (+Ub)  |  9     12  |  PB4 / I2C_SCL
           TIM2_CH3 / PA3  | 10     11  |  PB5 / I2C_SDA
                            -----------

Микроконтроллер STM8S103F3P6 содержит 8 КБайт флэш-памяти с ресурсом стирания 10 000 раз, 640 байт EEPROM и 1 КБайт RAM. Тактовая частота 8-битного процессора серии STM8S составляет 16 МГц.

Для питания платы можно использовать следующие варианты:

  • подключение источника напряжением 4,5 ... 15 В к контактам + или 5V и - или GND;
  • подключение кабеля к microUSB-разъему (этот разъем используется только для питания!);
  • подключение источника питания напряжением 3,3 В к контактам 3V3 и - или GND.

На плате установлен стабилизатор AMS1117-3.3. Вход стабилизатора соединен с контактом 5V, а выход - с контактом 3V3.

Разработка в среде IdeaSTM8

Установка среды разработки

Для программирования под микроконтроллеры STM8 можно использовать среду разработки IdeaSTM8 от компании Cosmic Software (в версии CXSTM8 special edition package - доступна с марта 2016 года, не имеет ограничений):
IdeaSTM8

Для загрузки дистрибутива следует перейти по этой ссылке: http://cosmicsoftware.com/download_stm8_32k.php.
При этом для использования кросс-компилятора от Cosmic Software перед скачиванием необходимо пройти регистрацию, указав имя (Name), название компании (Company), страну (Other), адрес электронной почты (E-mail), а затем нажав для отправки сведений кнопку «Submit».
В версии 4.4.6 объем дистрибутива (cxstm8_FSE_stm32_32K.exe) составляет 20,7 МБайт. 
Для получения годовой (затем продляемой) бесплатной лицензии при инсталляции необходимо нажать кнопку «Register on the Web», что приведет к отправке файла лицензии на адрес электронной почты, указанный при регистрации. Лицензия привязывается к компьютеру, на котором установлен компилятор (с помощью HOSTNAME, HOSTID и т.п.).
После получения файла license.lic следует разместить его в папке \COSMIC\FSE_Compilers\CXSTM8\License:
лицензия для IdeaSTM8

Разработка программы

В качестве примера создадим программу мигания светодиодом (Hello, world! в мире микроконтроллеров) TEST, размещенным на плате и подключенным к контакту PB.5.

Создаем новый проект, выполняя команду New Application:
IdeaSTM8 создание проекта

Выбираем в качестве целевой платформы микроконтроллер STM8S103F3:
STM8 программирование

Копируем в папку проекта заголовочный файл с определениями stm8s.h, предварительно раскомментировав в нем определение используемого микроконтроллера STM8S103:

 /* #define STM8AF626x */    /*!< STM8A Medium density devices */
  #define STM8S103       /*!< STM8S Low density devices */
 /* #define STM8S903 */      /*!< STM8S Low density devices */

Создаем новый файл (tst.c) с исходным кодом:

создаем файл:
Cosmic Software

выбираем в качестве типа файла - файл с исходным кодом на C:
программа для STM8

Добавляем созданный файл в проект:
IdeaSTM8 программирование

Пишем код программы в созданном файле:

#include <stm8s.h>

static void delay(uint32_t t) //процедура задержки
{
    while(t--) {}
}

int main(void)
{
    GPIOB->DDR |= (1 << 5); //настройка контакта PB.5 на выход
    GPIOB->CR1 |= (1 << 5); //настройка контакта PB.5 как push-pull, можно пропустить
    GPIOB->ODR |= (1 << 5); //вывод 1 в порт
    while(1)
    {
        GPIOB->ODR |= (1 << 5); //вывод 1 в порт
        delay(100000UL); //задержка
        GPIOB->ODR &= ~(1 << 5); //вывод 0 в порт
        delay(100000UL); //задержка
    }
}

Для компиляции и сборки проекта следует нажать клавишу F7 или выполнить команду Build:
сборка проекта для STM8

В результате сборки в папке проекта создается файл с именем проекта и расширением .sm8 (TST.sm8).

Для преобразования файла с расширением .sm8 в готовый для прошивки в микроконтроллер hex-файл  я использую COSMIC Software Hexa Translator (chex.exe) с помощью команды:

chex -oTST.hex -fi TST.sm8  ,

где TST - имя проекта.

Полученный hex-файл (TST.hex) содержит информацию, необходимую для прошивки микроконтроллера:

:20800000820080808200000082000000820000008200000082000000820000008200000050
:20802000820000008200000082000000820000008200000082000000820000008200000030
:20804000820000008200000082000000820000008200000082000000820000008200000010
:208060008200000082000000820000008200000082000000820000008200000082000000F0
:20808000AE03FF94CD809F20FE961C0003CD80F3961C0003A601CD80C9CD80DF26EB8172FB
:2080A0001A5007721A5005721A5005AE86A089AE000189ADD45B04721B5005AE86A089AE2B
:2080C000000189ADC45B0420DE40EB03E703250EE6026A024D2607E60126017A6A01819C1F
:2080E0003D00260E3D0126083D0226043D032702A6018188F6B700E601B701E602B702E64E
:0581000003B7038481B8
:00FFFF0101

SDCC
Использование компилятора SDCC

Установка компилятора

При разработке под микроконтроллеры STM8 можно использовать открытый (под лицензией GPLv3) набор компиляторов SDCC (Small Device C Compiler suite) языка программирования ANSI C под множество архитектур - от Intel 8051 до STMicroelectronics STM8. Версии под различные ОС доступны на http://sdcc.sourceforge.net.

Разработка программы

Пишем код программы в файле TST.c:

#include <stdint.h>

#define     __IO    volatile         /*!< defines 'read / write' permissions  */

typedef struct GPIO_struct
{
  __IO uint8_t ODR; /*!< Output Data Register */
  __IO uint8_t IDR; /*!< Input Data Register */
  __IO uint8_t DDR; /*!< Data Direction Register */
  __IO uint8_t CR1; /*!< Configuration Register 1 */
  __IO uint8_t CR2; /*!< Configuration Register 2 */
}
GPIO_TypeDef;

#define GPIOB_BaseAddress       0x5005
#define GPIOB ((GPIO_TypeDef *) GPIOB_BaseAddress)

static void delay(uint32_t t)
{
    while(t--) {}
}

int main(void)
{
    GPIOB->DDR |= (1 << 5);
    GPIOB->ODR |= (1 << 5);
    while(1)
    {
        GPIOB->ODR |= (1 << 5);
        delay(100000UL);
        GPIOB->ODR &= ~(1 << 5);
        delay(100000UL);
    }
}

Выполняем компиляцию программы командой

sdcc -mstm8 --std-c99 TST.c

При компиляции создается hex-файл TST.ihx:

:2080000082008083820000008200000082000000820000008200000082000000820000004D
:20802000820000008200000082000000820000008200000082000000820000008200000030
:20804000820000008200000082000000820000008200000082000000820000008200000010
:208060008200000082000000820000008200000082000000820000008200000082000000F0
:1D808300AE00002707724F00005A26F9AE00002709D68100D700005A26F7CC80807B
:03808000CC80C8E9
:2080A0005208160B17051E0D1F03160517011D00017B06A2006B067B05A2006B0516032626
:2080C000E7160126E35B0881AE5007F6AA20F7AE5005F6AA20F7AE5005F6AA20F74BA04B4F
:2080E000864B014B00CD80A05B04AE5005F6A4DFF74BA04B864B014B00CD80A05B0420D60F
:0181000081FD
:00000001FF

Прошивка

Для прошивки платы я приобрел на торговой площадке ebay программатор ST-LINK V2:
ST-LINK V2

Разъем программатора имеет 10 контактов:

Номер Название Назначение
1 RST сброс
2 SWIM SWIM-интерфейс (для STM8)
3 GND земля
4 3.3V + 3,3 В
5 5.0V + 5 В
6 SWCLK синхронизация (SWD-интерфейс, для STM32)
7 SWDIO данные (SWD-интерфейс, для STM32)
8 GND земля
9 3.3V + 3,3 В
10 5.0V + 5 В

Для подключения программатора к плате я использую 4 контакта на разъеме программатора и на плате - 3.3V(3V3), SWIM(SWM), GND, RST(NRST):
ST-LINK V2 контакты

При общении программатора с платой используется коммуникационный протокол SWIM (через однопроводной интерфейс - контакт SWIM).

Для прошивки я использую утилиту stm8flash, для запуска которой следует выполнить команду:

stm8flash -c stlinkv2 -p stm8s103f3 -w TST.hex (или TST.ihx)

Проект stm8flash размещен на GitHub: https://github.com/vdudouyt/stm8flash

Бинарная версия проекта для ОС Windows (можно взять здесь) содержит два необходимых файла:

stm8flash.exe - исполнимый файл
libusb-1.0.dll - библиотека для доступа к USB-устройствам

После запуска утилиты она выполняет прошивку программы в память и отчитывается о числе записанных байт:
прошивка STM8

После прошивки указанной выше программы мигания светодиодом он начинает мигать с периодом около шести секунд.

Visual Studio Code
Работа в редакторе Visual Studio Code

Для написания исходного кода программы, а также для автоматизации процесса компиляции и прошивки удобно использовать бесплатный редактор Visual Studio Code от компании Microsoft (страница загрузки).

Вот как выглядит исходный код программы в Visual Studio Code:
STM8 Visual Studio Code

Для удобства работы следует создать папку (например, sdcc), в которой будут располагаться файлы проектов для STM8. Затем следует добавить эту папку в рабочую область. В эту же папку помещаем файлы:
compile.cmd - с содержимым: c:/sdcc/bin/sdcc -mstm8 --std-c99 %~n1.c ,
где c:/sdcc -  папка компилятора SDCC
flash.cmd
- с содержимым: stm8flash -c stlinkv2 -p stm8s103f3 -w %1
stm8flash.exe
libusb-1.0.dll

В этой папке следует создать папку .vscode, в которой разместить файл tasks.json с таким содержимым:

{
    "version": "2.0.0",
    "tasks": [
        {
            "label": "compile",
            "type": "shell",
            "command": "compile ${file}",
            "problemMatcher": []
        },
        {
            "label": "flash",
            "type": "shell",
            "command": "flash ${fileDirname}\\${fileBasenameNoExtension}.ihx",
            "problemMatcher": []
        }
    ]
}

Этот файл описывает две задачи:
compile - компиляция текущего открытого файла с исходным кодом  (с расширением .c)
flash - прошивка скомпилированного ранее hex-файла (c расширением .ihx)

Выбирая соответствующую задачу ("Задачи" > "Запустить задачу...")
 программирование STM8
можно запустить:
компиляцию
компиляция STM8
прошивку
прошивка STM8

Полезные примеры кода

подключение заголовочных файлов

#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>

задержки

//константы для CLK #define CLK_DIVR (*(volatile uint8_t *)0x50c6)
...
static void delay(uint32_t t)
{
    while(t--) {}
}
...
delay(44000000UL); //задержка на одну минуту

работа с UART

//константы для CLK
#define CLK_DIVR    (*(volatile uint8_t *)0x50c6)
#define CLK_PCKENR1    (*(volatile uint8_t *)0x50c7)
//константы для UART
#define UART1_SR    (*(volatile uint8_t *)0x5230)
#define UART1_DR    (*(volatile uint8_t *)0x5231)
#define UART1_BRR1    (*(volatile uint8_t *)0x5232)
#define UART1_BRR2    (*(volatile uint8_t *)0x5233)
#define UART1_CR2    (*(volatile uint8_t *)0x5235)
#define UART1_CR3    (*(volatile uint8_t *)0x5236)
#define UART_CR2_TEN (1 << 3)
#define UART_CR3_STOP2 (1 << 5)
#define UART_CR3_STOP1 (1 << 4)
#define UART_SR_TXE (1 << 7)
...
void putchar(char c) //вывод символа в UART
{
    while(!(UART1_SR & UART_SR_TXE));
    UART1_DR = c;
}
...
CLK_DIVR = 0x00; //установка тактовой частоты 16 MГц
CLK_PCKENR1 = 0xFF; //включение периферии
UART1_CR2 = UART_CR2_TEN; //разрешение TX и RX
UART1_CR3 &= ~(UART_CR3_STOP1 | UART_CR3_STOP2); //1 стоп-бит
UART1_BRR2 = 0x03; UART1_BRR1 = 0x68; //9600 бод
...
printf("Hello,world!\r\n"); //вывод строки в UART

Вот как выглядит собранная схема из микроконтроллера, USB-UART преобразователя и программатора:
STM8 UART

работа  с таймером

//регистры таймера
#define TIM1_CR1    (*(volatile uint8_t *)0x5250)
#define TIM1_IER    (*(volatile uint8_t *)0x5254)
#define TIM1_SR1    (*(volatile uint8_t *)0x5255)
#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)
...
TIM1_PSCRH = 0x09; //настройка предделителя таймера
TIM1_PSCRL = 0x89;
TIM1_CR1 = 0x01; //включение таймера
TIM1_IER = 0x01; //включение прерываний от таймера
__asm__ ("rim"); //включение прерываний
...
//обработчик прерываний таймера
void TIM1_overflow_Handler() __interrupt(11)
{
     TIM1_SR1 &= ~1; //сброс флага прерывания
     //выполнение требуемых действий
}

(1/16000000)*65536*предделитель = интервал в секундах
10 секунд = 2441 0x0989

работа с АЦП

typedef struct ADC1_struct
 {
  __IO uint8_t DB0RH;         /*!< ADC1 Data Buffer Register (MSB)  */
  __IO uint8_t DB0RL;         /*!< ADC1 Data Buffer Register (LSB)  */
  __IO uint8_t DB1RH;         /*!< ADC1 Data Buffer Register (MSB)  */
  __IO uint8_t DB1RL;         /*!< ADC1 Data Buffer Register (LSB)  */
  __IO uint8_t DB2RH;         /*!< ADC1 Data Buffer Register (MSB)  */
  __IO uint8_t DB2RL;         /*!< ADC1 Data Buffer Register (LSB)  */
  __IO uint8_t DB3RH;         /*!< ADC1 Data Buffer Register (MSB)  */
  __IO uint8_t DB3RL;         /*!< ADC1 Data Buffer Register (LSB)  */
  __IO uint8_t DB4RH;         /*!< ADC1 Data Buffer Register (MSB)  */
  __IO uint8_t DB4RL;         /*!< ADC1 Data Buffer Register (LSB)  */
  __IO uint8_t DB5RH;         /*!< ADC1 Data Buffer Register (MSB)  */
  __IO uint8_t DB5RL;         /*!< ADC1 Data Buffer Register (LSB)  */
  __IO uint8_t DB6RH;         /*!< ADC1 Data Buffer Register (MSB)  */
  __IO uint8_t DB6RL;         /*!< ADC1 Data Buffer Register (LSB)  */
  __IO uint8_t DB7RH;         /*!< ADC1 Data Buffer Register (MSB)  */
  __IO uint8_t DB7RL;         /*!< ADC1 Data Buffer Register (LSB)  */
  __IO uint8_t DB8RH;         /*!< ADC1 Data Buffer Register (MSB)  */
  __IO uint8_t DB8RL;         /*!< ADC1 Data Buffer Register (LSB)  */
  __IO uint8_t DB9RH;         /*!< ADC1 Data Buffer Register (MSB)  */
  __IO uint8_t DB9RL;         /*!< ADC1 Data Buffer Register (LSB)  */
  uint8_t RESERVED[12];       /*!< Reserved byte */
  __IO uint8_t CSR;           /*!< ADC1 control status register */
  __IO uint8_t CR1;           /*!< ADC1 configuration register 1 */
  __IO uint8_t CR2;           /*!< ADC1 configuration register 2 */
  __IO uint8_t CR3;           /*!< ADC1 configuration register 3  */
  __IO uint8_t DRH;           /*!< ADC1 Data high */
  __IO uint8_t DRL;           /*!< ADC1 Data low */
  __IO uint8_t TDRH;          /*!< ADC1 Schmitt trigger disable register high */
  __IO uint8_t TDRL;          /*!< ADC1 Schmitt trigger disable register low */
  __IO uint8_t HTRH;          /*!< ADC1 high threshold register High*/
  __IO uint8_t HTRL;          /*!< ADC1 high threshold register Low*/
  __IO uint8_t LTRH;          /*!< ADC1 low threshold register high */
  __IO uint8_t LTRL;          /*!< ADC1 low threshold register low */
  __IO uint8_t AWSRH;         /*!< ADC1 watchdog status register high */
  __IO uint8_t AWSRL;         /*!< ADC1 watchdog status register low */
  __IO uint8_t AWCRH;         /*!< ADC1 watchdog control register high */
  __IO uint8_t AWCRL;         /*!< ADC1 watchdog control register low */
 }
 ADC1_TypeDef;
#define ADC1_BaseAddress        0x53E0
#define ADC1 ((ADC1_TypeDef *) ADC1_BaseAddress)
...
//считывание данных из АЦП
unsigned int val=0;
ADC1->CSR |= ((0x0F)канал); //выбор канала
ADC1->CR2 |= (1<<3); //данные выравниваются справа
ADC1->CR1 |= (1<<0); //включение АЦП
ADC1->CR1 |= (1<<0); //запуск преобразования
while(((ADC1->CSR)&(1<<7))== 0); //ожидание завершения преобразования
val |= (unsigned int)ADC1->DRL;
val |= (unsigned int)ADC1->DRH<<8;
ADC1->CR1 &= ~(1<<0); //остановка преобразования
val &= 0x03ff; //результат

Необходимо задать номер канала АЦП, соответствующего используемому входу (например, вывод D2 - канал 3).

Напряжение можно определить умножением считанного из АЦП значения на VCC/1023, где VCC - напряжение питания на шине 3.3V.
Например, при подключении к входу 3.3V STM выхода 3.3V преобразователя USB-UART напряжение на нем составило 3,24 В. При этом масштабный коэффициент равен 3,24/1023 = 0,00317 В.

Работа с шиной I2C

...

Преобразователь PS/2 - UART

Разработанный мной преобразователь для проекта cpm4nano позволяет подключать PS/2-клавиатуру через последовательный порт (UART).

...

Продолжение следует

Яндекс.Метрика