STM8+SPL+COSMIC+STVD: Быстрый старт

разделы: STM8 , среда разработки , дата: 6 ноября 2016г.

Как всем известно, с марта этого года компилятор COSMIC for STM8 стал полностью бесплатен и без ограничений на размер генерируемого кода. Он имеет полную поддержку SPL(Standard Peripheral Library) и фирменой среды разработки - STVD(ST Visual develop IDE).

К сожалению, Cosmic работает только под операционными системами Windows, и для активации требует лецензионный ключ который можно прождать несколько дней. С другой стороны, stm8flash в Linux не умеет прошивать STM8L151C8, а с COSMIC через виртуалку вполне можно работать, да из под Wine он тоже запускается.

Ок, для начала нам потребуется скачать SPL и STVD c сайта https://my.st.com, а с сайта http://www.cosmic-software.com/download.php сам компилятор COSMIC. Как скачать SPL я рассматривал год назад в Введение в STM8: программирование и прошивка с помощью клона ST-Link v2, версия для Linux, а связку STVP+STVD весной в STM8 + IAR + ST-LINK2: программирование, прошивка и отладка из под Windows. Однако с тех многие ссылки побились, поэтому предлагаю пройти квест заново. Потому что поиск чего-то на st.com, действительно напоминает дурной квест.

В качестве целевого чипа я буду использовать STM8S103F3P6, изредка переключаясь на STM8L051/STM8L151 там, где есть необходимость.

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

Сначала нам нужно скачать SPL и STVD. Будем считать, что драйвер ST-LINK v2 уже установлен. Заходим на сайт фирмы ST Microelectonics https://my.st.com, регистируемся или логинимся, и в стоке поиска вводим: "STM8S103F3". В появившимся окне кликаем по ссылке с нужным чипом:

Открывшуюся страницу проматываем вниз до пункта "Tools and Software", и щелкаем по вкладке "EMBEDDED SOFTWARE":

В открывшийся таблице нас будет интересовать пункт "STM8S/A Standerd peripheral library":

Щелкаем и скачиваем:

Точно также, поиском, по например: STM8L051F3, можно найти SPL для L-cерии:

STVD находится поиском по самому себе:

Скачаные архивы следует распаковать, а STVD нужно будет установить. Напомню, что в связке с STVD ставиться флешер - STVP(ST Visual Programmer). STVD - это фирменная среда программирования на ассеблере STM8 и STM7. Что бы программировать на Си, нужно будет установить Си-компилятор с сайта http://www.cosmic-software.com/download.php

В придачу нам пытаются втюрить копилятор для STM32 с ограничением в 32 Кбайта. Придется брать;)

Однако, прежде нам предлагают заполнить анкету:

После установки, будет предложено получить лицензионный ключ. Для этого нужно отправить емэйл на адрес stm8_free@cosmic.fr указав в качестве темы: "STM8FSE, STM32 32K License Request":

После закрытия "блокнота", появится текст который который следует скопировать в тело емайлa:

Т.к. я отправлял запрос в то время когда во Франции были праздничные дни, ихний почтовый робот мне в ответ отправил сообщение, о том, что лицензионый ключ мне будет отправлен после праздников, и просили не отправлять повторные запросы:

Что и было сделано в свое время емэйлом, в аттаче которого содержался ключ:

В самом емэйле написано, что ключ следут скопировать в папку с программой Cosmic.

Выполнив все рекомендации можно будет для проверки запустить Cosmic, и если никаких пердупреждений не появилось, значит все номрально. Если же нет, по появится окно, через которое попросят указать путь к ключу. От Cosmic нам нужен будет только компилятор, поэтому программу можно будет закрыть и запусить STVD:

Через меню->File->New Workspace создаем новый проект:

Задаем название workspace'а и выбираем каталог:

Пишем имя проекта, в Toolchain из выпадающего списка выбираем "Cosmic STM8", указываем путь к компилятору:

Выбираем тип микроконтроллера:

В древовидном списоке проекта, в source файлах, находим main.c и двойным щелчком отрываем его. Пробуем скомпилировать проект щеклнув по кнопке Build(F7):

Теперь нужно будет скопировать SPL в папку проекта:

Кроме того пондобиться скопировать файл stm8s_conf.h из папки Project/STM8S_StdPeriph_Template в папку inc:

Осталось дело за малым, добавить SPL в проект. Для этого, в древовидном списоке проекта, следут щелкнуть правой кнопкой мышки на "Include Files" и выбрать опцию "Add Files to Folder..."

После чего выбрать заголовочные файлы SPL:

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

и... получаем ошибку. Что бы ее исправить, откроем файл stm8s.h и в самом начале найдем такой блок из дефайнов:

Здесь предлагается раскоментировать строку со своей линейкой чипов. Однако более грамотно будет задать это через свойства проекта. Для этого через меню->Project->Settings... задаем нужный define в свойствах Си компилятора:

Пробуем пересобрать проект еще раз, и в этот раз все должно пройти успешно:

Теперь можно начинать программировать. Для начала, попробуем добавить вызов функции GPIO_DeInit(GPIOB), и скомпилировать проект:

И снова получаем ошибку, на этот раз от линковщика. Что бы ее испрать, в "Source Files" нужно добавить файл stm8s_gpio.c:

Жмем "Rebuild All" и снова получаем ошибку:

Виной всему служит неопределенная функция assert_failed в файле stm8_conf.h. Чтобы исправить ситуацию, добавим после функции main() следующий код:

#ifdef USE_FULL_ASSERT

/**
  * @brief  Reports the name of the source file and the source line number
  *   where the assert_param error has occurred.
  * @param file: pointer to the source file name
  * @param line: assert_param error line source number
  * @retval None
  */
void assert_failed(uint8_t* file, uint32_t line)
{
  /* User can add his own implementation to report the file name and line number,
     ex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */

  /* Infinite loop */
  while (1)
  {
  }
}
#endif

В этот раз все в порядке:

2) Примеры для STM8S103F3

Состовляем простейший blink:

/* MAIN.C file
 * 
 * Copyright (c) 2002-2005 STMicroelectronics
 */

#include "stm8s.h"

#define LED GPIO_PIN_5
#define LED_PORT GPIOB

static void delay(uint32_t t) {
    while(--t);
}
main()
{
    GPIO_DeInit(LED_PORT);
    GPIO_Init(LED_PORT, LED, GPIO_MODE_OUT_PP_LOW_FAST);

    for(;;){
        delay(60000);
        GPIO_WriteReverse(LED_PORT, LED);
    }
}

#ifdef USE_FULL_ASSERT

/**
  * @brief  Reports the name of the source file and the source line number
  *   where the assert_param error has occurred.
  * @param file: pointer to the source file name
  * @param line: assert_param error line source number
  * @retval None
  */
void assert_failed(uint8_t* file, uint32_t line)
{
  /* User can add his own implementation to report the file name and line number,
     ex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */

  /* Infinite loop */
  while (1)
  {
  }
}
#endif

Теперь можно подключить программатор к компьютеру. Чтобы загрзить прошивку в чип и приступить к отладке, в меню->Debug Instrument->Target Settings... выберем Swim ST-Link, после чего можно будет нажать на кнопку начала отладки на панели инструментов:

Панель отладки станет активной, и откроется окно с ассемблерным кодом. Мы можем нажать на восклицательный знак, дав команду на выполнение - Run, или мы можем нажать на красный крестик завершая сеанс отладки, после чего микроконтроллер перезагрузится и начнет выполнение программы:

Зеленый светодиод начнет периодически мигать.

Попробуем добавить тактирование. Для этого в Source Files добавим файл stm8s_clk.c, а программу приведем к следующему виду:

#include "stm8s.h"

#define LED GPIO_PIN_5
#define LED_PORT GPIOB

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

uint8_t i;
main()
{
    GPIO_DeInit(LED_PORT);
    GPIO_Init(LED_PORT, LED, GPIO_MODE_OUT_PP_LOW_FAST);

    CLK_DeInit();

    i=0;
    for(;;){
        delay(60000);
        GPIO_WriteReverse(LED_PORT, LED);

        if (++i == 5)
        {
            CLK_SYSCLKConfig(CLK_PRESCALER_CPUDIV1);
            CLK_SYSCLKConfig(CLK_PRESCALER_HSIDIV1); // set 16 MHz for CPU
        }
    }
}

#ifdef USE_FULL_ASSERT
void assert_failed(uint8_t* file, uint32_t line){
    while (1);
}
#endif

После двух медленных миганий светодиода, частота мигания должна ускориться:

Теперь о прерываниях. По мотивам поста STM8S+STM8L+SDCC+SPL: функции delay_ms() и delay_us() на таймере TIM4 попробуем составить Blink с задержкой по таймеру TIM4. Для этого, в директорию проекта нужно будет добавить заголовочный файл main.h с объявлением обработчика прерывания:

#ifndef __MAIN_H
#define __MAIN_H

@far @interrupt void IRQ_Handler_TIM4(void);

#endif

Этот заголовочный файл нужно будет добавить в "Include Files" проекта.

Файл main.c будет такого содержания:

#include "stm8s.h"

#define LED       GPIO_PIN_5
#define LED_PORT  GPIOB
#define TIM4_PERIOD   124

volatile uint16_t count;

@far @interrupt void IRQ_Handler_TIM4(void)
{
    if (count)
        count--;

    TIM4_ClearITPendingBit(TIM4_IT_UPDATE);
}

void delay_ms(uint16_t ms)
{
        TIM4_Cmd(DISABLE);       // stop
        TIM4_TimeBaseInit(TIM4_PRESCALER_128, TIM4_PERIOD);
        TIM4_ClearFlag(TIM4_FLAG_UPDATE);
        TIM4_ITConfig(TIM4_IT_UPDATE, ENABLE);

        count = ms;

        TIM4_Cmd(ENABLE);       // let's go

        while(count);
}

ErrorStatus status = FALSE;

main()
{
    // ---------- GPIO CONFIG ---------------------
    GPIO_DeInit(LED_PORT);
    GPIO_Init(LED_PORT, LED, GPIO_MODE_OUT_PP_LOW_FAST);

    // ---------- CLK CONFIG -----------------------
    CLK_DeInit();

    CLK_SYSCLKConfig(CLK_PRESCALER_CPUDIV1);
    CLK_SYSCLKConfig(CLK_PRESCALER_HSIDIV1); // set 16 MHz for CPU

    // uncomment if use HSE on Quartz
    //status = CLK_ClockSwitchConfig(CLK_SWITCHMODE_AUTO, CLK_SOURCE_HSE, DISABLE,
    //                CLK_CURRENTCLOCKSTATE_DISABLE);

    // ---------- TIM4 CONFIG -----------------------
    TIM4_DeInit();

    enableInterrupts();

    for(;;){
        GPIO_WriteReverse(LED_PORT, LED);
        delay_ms(500);
    }
}

#ifdef USE_FULL_ASSERT
void assert_failed(uint8_t* file, uint32_t line){
    while (1);
}
#endif

Само собой, файл stm8s_tim4.c должен быть добавлен в Source Files проекта.

Осталось изменить пару строк в файле stm8_interrupt_vector.c

Нужно добавить объявление заголовочного файла main.c:

И добавить имя функции-обработчика 23-го прерывания в таблицу векторов:

Теперь можно собирать проект нажав на кнопку "Rebuild All" и загрузить в чип нажав значек "D" на панели отладки. Должно работать.

При возникновении проблем, на обратчике прерывания можно поставить точку остановки, и нажав Run(восклицательный знак на панели отладки), проследить за выполнением кода:

Кроме того, открыв вкладку Console мы, попадем в консоль прекрастно знакомого отладчика gdb, с возможностью управления прогаммой в ручном режиме:

Теперь попробуем включить UART интерфейс. Как не сложно догдаться, первым делом потребуется добавить файл stm8s_uart1.c в "Source Files".

Исходник main.c примет следующий вид:

#include "stm8s.h"
#include "stdio.h"

#define LED           GPIO_PIN_5
#define LED_PORT      GPIOB
#define TIM4_PERIOD   124
#define PUTCHAR_PROTOTYPE char putchar(char c)

volatile uint16_t count;
uint16_t i;

@far @interrupt void IRQ_Handler_TIM4(void)
{
    if (count)
        count--;

    TIM4_ClearITPendingBit(TIM4_IT_UPDATE);
}

void delay_ms(uint16_t ms)
{
        TIM4_Cmd(DISABLE);       // stop
        TIM4_TimeBaseInit(TIM4_PRESCALER_128, TIM4_PERIOD);
        TIM4_ClearFlag(TIM4_FLAG_UPDATE);
        TIM4_ITConfig(TIM4_IT_UPDATE, ENABLE);

        count = ms;

        TIM4_Cmd(ENABLE);       // let's go

        while(count);
}

ErrorStatus status = FALSE;

main()
{
    // ---------- GPIO CONFIG ---------------------
    GPIO_DeInit(LED_PORT);
    GPIO_Init(LED_PORT, LED, GPIO_MODE_OUT_PP_LOW_FAST);

    // ---------- CLK CONFIG -----------------------
    CLK_DeInit();

    CLK_SYSCLKConfig(CLK_PRESCALER_CPUDIV1);
    CLK_SYSCLKConfig(CLK_PRESCALER_HSIDIV1); // set 16 MHz for CPU

    // uncomment if use HSE on Quartz
    //status = CLK_ClockSwitchConfig(CLK_SWITCHMODE_AUTO, CLK_SOURCE_HSE, DISABLE,
    //                CLK_CURRENTCLOCKSTATE_DISABLE);

    // ---------- TIM4 CONFIG -----------------------
    TIM4_DeInit();

    // ------------ UART1 -------------------
    UART1_DeInit();

    // 8N1
    UART1_Init((uint32_t)9600, UART1_WORDLENGTH_8D, UART1_STOPBITS_1, UART1_PARITY_NO,
              UART1_SYNCMODE_CLOCK_DISABLE, UART1_MODE_TXRX_ENABLE);

    enableInterrupts();
    i=0;
    for(;;){
        GPIO_WriteReverse(LED_PORT, LED);
        delay_ms(1000);
        printf("count: %u\n",++i);
    }
}

PUTCHAR_PROTOTYPE
{
    // Write a character to the UART1
    UART1_SendData8(c);
    // Loop until the end of transmission 
    while (UART1_GetFlagStatus(UART1_FLAG_TXE) == RESET);
}

#ifdef USE_FULL_ASSERT
void assert_failed(uint8_t* file, uint32_t line){
    while (1);
}
#endif

Результат работы выглядит как-то так:

На этом думаю можно остановиться, основные вопросы использования компилятора Cosmic я постарался разобрать.

3) Примеры для STM8L151C8

В L-серии, так же как и в STM32 чипы делятся по размеру флеш-паняти. 151-я линейка делится на Medium-density с рамером флеш-пямяти 32 Кбайта и на High-density с размерм флеш-пямяти 64-Кбайта. STM8L151C8 относится к high-density чипам:

Поэтому в опциях препроцессора слеует указать STM8L15X_HD:

Простейший Blink будет выглядить так:

#include "stm8l15x.h"

#define PORT GPIOA
#define LED  GPIO_Pin_2

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

main()
{
    GPIO_DeInit(PORT);
    GPIO_Init(PORT, LED, GPIO_Mode_Out_PP_Low_Fast);

    for(;;){
        delay(60000);
        GPIO_ToggleBits(PORT, LED);
    }
}

#ifdef  USE_FULL_ASSERT
void assert_failed(uint8_t* file, uint32_t line)
{
  while (1);
}
#endif

Здесь следует добавить в проект файл stm8l15x_gpio.c.

Теперь добавим к Source Files файл stm8l15x_clk.c а в main.c добавим фукции настройки скоростного генератора - HSI:

#include "stm8l15x.h"

#define PORT GPIOA
#define LED  GPIO_Pin_2

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

main()
{
    // ---------- GPIO CONFIG ---------------------
    GPIO_DeInit(PORT);
    GPIO_Init(PORT, LED, GPIO_Mode_Out_PP_Low_Fast);

    // ---------- CLK CONFIG -----------------------
    CLK_DeInit();
    CLK_SYSCLKDivConfig(CLK_SYSCLKDiv_1); // set HSI on 16MHz

    for(;;){
        delay(60000);
        GPIO_ToggleBits(PORT, LED);
    }
}

#ifdef  USE_FULL_ASSERT
void assert_failed(uint8_t* file, uint32_t line)
{
  while (1);
}
#endif

Светодиод начал мигать значительно быстре.

Чтобы добавить прерывание по таймеру TIM4, нужно добавить в проект заголовочный файл main.h. Его содержимое тоже, что и для STM8S103F3:

#ifndef __MAIN_H
#define __MAIN_H

@far @interrupt void IRQ_Handler_TIM4(void);

#endif

Так же должен быть добавлен к Source Files файл stm8l15x_tim4.c.

В файле stm8_interrupt_vector.c дожен быть объявлен заголовочный файл main.h, а в таблице прерываний обраотчик должен быть объявлен как 25-е прерывание, а не 23-е как stm8s103f3.

Тогда Blink с задержкой на таймере TIM4 для STM8L151C8 будет выглядить так:

#include "stm8l15x.h"

#define PORT GPIOA
#define LED  GPIO_Pin_2
#define TIM4_PERIOD 124

volatile uint16_t count;

@far @interrupt void IRQ_Handler_TIM4()
{
    if (count)
            count--;

    TIM4_ClearITPendingBit(TIM4_IT_Update);
}

void delay_ms(uint16_t ms)
{
        TIM4_Cmd(DISABLE);       // stop

        TIM4_TimeBaseInit(TIM4_Prescaler_128, TIM4_PERIOD);
        TIM4_ClearFlag(TIM4_FLAG_Update);
        TIM4_ITConfig(TIM4_IT_Update, ENABLE);

        count = ms;

        TIM4_Cmd(ENABLE);       // let's go

        while(count);
}

main()
{
    // ---------- GPIO CONFIG ---------------------
    GPIO_DeInit(PORT);
    GPIO_Init(PORT, LED, GPIO_Mode_Out_PP_Low_Fast);

    // ---------- CLK CONFIG -----------------------
    CLK_DeInit();
    CLK_SYSCLKDivConfig(CLK_SYSCLKDiv_1); // set HSI on 16MHz
    CLK_PeripheralClockConfig(CLK_Peripheral_TIM4, ENABLE);

    // ---------- TIM4 Init -----------------------------
    TIM4_DeInit();

    enableInterrupts();

    for(;;){
        delay_ms(1000);
        GPIO_ToggleBits(PORT, LED);
    }
}

#ifdef  USE_FULL_ASSERT
void assert_failed(uint8_t* file, uint32_t line)
{
  while (1);
}
#endif

Первый UART в STM8L151C8 висит на ножках 41(RX) и 42(TX). Для его использования в Source Files следует добавть файл SPL - stm8l15x_usart.c

Исходник будет такой:

#include "stm8l15x.h"
#include "stdio.h"

#define PORT GPIOA
#define LED  GPIO_Pin_2
#define TIM4_PERIOD 124

#define PUTCHAR_PROTOTYPE char putchar (char c)

volatile uint16_t count;
uint16_t i;

@far @interrupt void IRQ_Handler_TIM4()
{
      if (count)
            count--;

      TIM4_ClearITPendingBit(TIM4_IT_Update);
}

void delay_ms(uint16_t ms)
{
        TIM4_Cmd(DISABLE);       // stop

        TIM4_TimeBaseInit(TIM4_Prescaler_128, TIM4_PERIOD);
        TIM4_ClearFlag(TIM4_FLAG_Update);
        TIM4_ITConfig(TIM4_IT_Update, ENABLE);

        count = ms;

        TIM4_Cmd(ENABLE);       // let's go

        while(count);
}

main()
{
    // ---------- GPIO CONFIG ---------------------
    GPIO_DeInit(PORT);
    GPIO_Init(PORT, LED, GPIO_Mode_Out_PP_Low_Fast);

    // ---------- CLK CONFIG -----------------------
    CLK_DeInit();
    CLK_SYSCLKDivConfig(CLK_SYSCLKDiv_1); // set HSI on 16MHz
    CLK_PeripheralClockConfig(CLK_Peripheral_TIM4, ENABLE);
    CLK_PeripheralClockConfig(CLK_Peripheral_USART1, ENABLE);

    // ---------- TIM4 Init -----------------------------
    TIM4_DeInit();

    // ---------- USART Init ---------------------------
    USART_DeInit(USART1);

    USART_Init(USART1, (uint32_t)9600, USART_WordLength_8b, USART_StopBits_1,
                    USART_Parity_No, (USART_Mode_TypeDef)(USART_Mode_Tx | USART_Mode_Rx));

    enableInterrupts(); i=0;

    for(;;){
        delay_ms(1000);
        GPIO_ToggleBits(PORT, LED);
        printf("count: %d\n",++i);
    }
}

PUTCHAR_PROTOTYPE
{
    /* Write a character to the USART */
    USART_SendData8(USART1, c);
    /* Loop until the end of transmission */
    while (USART_GetFlagStatus(USART1, USART_FLAG_TC) == RESET);

//    return (c);
}

#ifdef  USE_FULL_ASSERT
void assert_failed(uint8_t* file, uint32_t line)
{
  while (1);
}
#endif

Результат работы:

На этом пожалуй все.

поделиться: