Наряду с Arduino у любителей микроконтроллеров сейчас популярна и линейка продуктов от компании STMicroelectronics, включающая 8-разрядные микроконтроллеры STM8 и 32-разрядные микроконтроллеры STM32 (на ядре Cortex).
Микроконтроллеры STM8 состоят из нескольких линееек:
STM8S - основная линейка,
STM8A - для автомобильной промышленности,
STM8L - со сверхнизким потреблением энергии,
STM8T - емкостный сенсор для детектирования прикосновения или приближения.
Отладочную плату с микроконтроллером STM8 на борту можно приобрести за 1 (!) доллар и даже дешевле. Я приобрел несколько таких плат на основе микроконтроллера STM8S103F3P6 на торговой площадке ebay:
контакт |
назначение |
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.
Использование компилятора SDCC
Установка компилятора
При разработке под микроконтроллеры STM8 можно использовать открытый (под лицензией GPLv3) набор компиляторов SDCC (Small Device C Compiler suite) языка программирования ANSI C под множество архитектур - от Intel 8051 до STMicroelectronics STM8. Версии под различные ОС - Windows, MacOS, Linux - (текущая новейшая стабильная версия - 4.4.0) доступны на http://sdcc.sourceforge.net.
Для ОС Linux загружаем последнюю версию (например, sdcc-4.4.0-rc2-amd64-unknown-linux2.5.tar.bz2), распаковываем и копируем содержимое полученной папку в ./usr/local .
Для проверки вводим команду
sdcc -v
(отображается версия компилятора).
Разработка программы
Пишем код программы в файле TST.c:
#include <stdint.h>
#define __IO volatile
...........................
}
Компиляция программы
Выполняем компиляцию программы командой
sdcc -mstm8 --std-c99 TST.c
При компиляции создается hex-файл TST.ihx:
:2080000082008083820000008200000082000000820000008200000082000000820000004D
:20802000820000008200000082000000820000008200000082000000820000008200000030
...................................................................................................................................
:00000001FF
Также создаются файлы с расширениями:
asm
lk
lst
map
rel
rst
sym
Прошивка
Для прошивки платы я приобрел на торговой площадке ebay программатор ST-LINK V2:
При первоначальном подключении к USB-порту компьютера программатор определяется как "неизвестное устройство" с VID 0483 и PID 3748:
На сайте ST доступен драйвер для программатора - STSW-LINK009:
После его установки при повторном подключении программатор распознается как "устройство USB":
Разъем программатора имеет 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):
При общении программатора с платой используется коммуникационный протокол 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-устройствам
Для использования утилиты под ОС Linux необходимо предварительно установить библиотеки:
sudo apt-get install pkg-config libusb-1.0-0-dev
Затем скачиваем архив проекта stm8flash-master.zip , распаковываем его и запускаем в папке проекта:
для компиляции
make
для установки
sudo make install
(файл stm8flash копируется в папку /usr/local/bin).
После запуска утилиты она выполняет прошивку программы в память и отчитывается о числе записанных байт:
После прошивки указанной выше программы мигания светодиодом он начинает мигать с периодом около шести секунд.
Прошивка программатора может быть обновлена с помощью утилиты, доступной для скачивания на сайте ST - STSW-LINK007:
После запуска приложения (для Windows - ST-LinkUpgrade.exe) необходим присоединить программатор к USB-порту компьютера, нажать кнопку Device Connect - при этом отобразятся текущая версия прошивки программатора (Version) и его тип (Type), а также версия, до которой можно обновить прошивку (Upgrade to Firmware). Для запуска процесса обновления прошивки необходимо нажать кнопку Yes>>>>.
Работа в редакторе Visual Studio Code
Для написания исходного кода программы, а также для автоматизации процесса компиляции и прошивки удобно использовать бесплатный редактор Visual Studio Code от компании Microsoft (страница загрузки).
Вот как выглядит исходный код программы в 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)
Выбирая соответствующую задачу ("Задачи" > "Запустить задачу...")
можно запустить:
компиляцию
прошивку
Полезные подсказки
работа с портами
задание базовых адресов портов:
порт A
#define GPIOA_BaseAddress 0x5000
порт B
#define GPIOB_BaseAddress 0x5005
порт C
#define GPIOC_BaseAddress 0x500A
порт D
#define GPIOD_BaseAddress 0x500F
установка режима порта (DDR - регистр направления порта):
настройка вывода N порта X (PXN) на выход
GPIOX->DDR |= (1 << N);
настройка вывода N порта X (PXN) на вход
GPIOX->DDR &= ~(1 << N);
вывод (ODR - регистр выходных данных):
установка вывода N порта X (PXN) в состояние "1" :
GPIOX->ODR |= (1 << N);
установка вывода N порта X (PXN) в состояние "0" :
GPIOX->ODR &= ~(1 << N);
подключение заголовочных файлов
#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 преобразователя и программатора:
работа с таймером
//регистры таймера
#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 В.
Пример простейшего проекта, мигающего светодиодом
#include <stdint.h>
#include <stdio.h>
#define CLK_DIVR (*(volatile uint8_t *)0x50c6)
#define CLK_PCKENR1 (*(volatile uint8_t *)0x50c7)
#define __IO volatile
typedef struct GPIO_struct
{
__IO uint8_t ODR;
__IO uint8_t IDR;
__IO uint8_t DDR;
__IO uint8_t CR1;
__IO uint8_t CR2;
}
GPIO_TypeDef;
#define GPIOB_BaseAddress 0x5005
#define GPIOB ((GPIO_TypeDef *) GPIOB_BaseAddress)
#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)
volatile uint8_t led = 0;
void TIM1_overflow_Handler() __interrupt(11)
{
TIM1_SR1 &= ~1;
if (led == 1) {
GPIOB->ODR |= (1 << 5);
}
else
{
GPIOB->ODR &= ~(1 << 5);
}
led ^= 1;
}
void main(void)
{
CLK_DIVR = 0x00;
CLK_PCKENR1 = 0xFF;
GPIOB->DDR |= (1 << 5);
GPIOB->ODR |= (1 << 5);
TIM1_PSCRH = 0x00;
TIM1_PSCRL = 0xF4;
TIM1_CR1 = 0x01;
TIM1_IER = 0x01;
__asm__ ("rim");
while(1)
{
__asm__ ("WFI");
}
}
Разработка в среде IdeaSTM8
Установка среды разработки
Для программирования под микроконтроллеры STM8 можно использовать среду разработки IdeaSTM8 от компании Cosmic Software (в версии CXSTM8 special edition package - доступна с марта 2016 года, не имеет ограничений):
Для загрузки дистрибутива следует перейти по этой ссылке: 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:
Разработка программы
В качестве примера создадим программу мигания светодиодом (Hello, world! в мире микроконтроллеров) TEST, размещенным на плате и подключенным к контакту PB.5.
Создаем новый проект, выполняя команду New Application:
Выбираем в качестве целевой платформы микроконтроллер STM8S103F3:
Копируем в папку проекта заголовочный файл с определениями stm8s.h, предварительно раскомментировав в нем определение используемого микроконтроллера STM8S103:
/* #define STM8AF626x */ /*!< STM8A Medium density devices */
#define STM8S103 /*!< STM8S Low density devices */
/* #define STM8S903 */ /*!< STM8S Low density devices */
Создаем новый файл (tst.c) с исходным кодом:
создаем файл:
выбираем в качестве типа файла - файл с исходным кодом на C:
Добавляем созданный файл в проект:
Пишем код программы в созданном файле:
#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:
В результате сборки в папке проекта создается файл с именем проекта и расширением .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
Для прошивка этого файла в память микроконтроллера можно использовать описанный выше программатор ST-LINK V2.
Мои проекты на STM8
Преобразователь PS/2 - UART
Преобразователь для проекта cpm4nano позволяет подключать PS/2-клавиатуру через последовательный порт (UART).
Дистанционно управляемая аэролодка
Микроконтроллер STM8 принимает сигнал ИК-пульта и управляет движением модели аэролодки.
Источник ультрафиолетового излучения из лампы ДРЛ
Микроконтроллер STM8 управляет питанием ультрафиолетовой лампы.
Продолжение следует