Как всем известно, с марта этого года компилятор 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 там, где есть необходимость.
Сначала нам нужно скачать 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
В этот раз все в порядке:
Состовляем простейший 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 я постарался разобрать.
В 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
Результат работы:
На этом пожалуй все.