Уже стало
традицией в сообществе программистов микроконтроллеров начинать освоение новой
архитектуры с мигания светодиодом, подключенным к выходу порта МК. Попробуем
рассмотреть на практике, как можно мигать светодиодом с помощью МК серии STM8. В качестве подопытной
платформы будем использовать отладочную плату STM8S-Discovery совместно со средой разработки IAR Embedded Workbench, которая многим
знакома по платформам AVR и ARM.
На плате установлен МК типа STM8S105C6T6, светодиод подключен к нулевому разряду порта D (PD0). Для начала создадим новый проект с
файлом main.c. В опциях компилятора
установим тип процессора STM8S105C6, тип отладчика ST-Link. В начале текста программы подключим файл определений
регистров:
#include "iostm8s105c6.h"
Рассмотрим, что же нам нужно сделать, для того, чтобы
светодиод замигал. Задача имеет несколько способов решения, отличающихся
использованием различных аппаратных особенностей МК. Рассмотрим их по очереди
от простого к сложному.
Полную информацию о внутренней структуре и назначению
регистров МК можно найти в документе STM8S_Reference manual. Рекомендую ознакомиться с ним перед
тем, как продолжить работу. Для начала нам нужно настроить блок тактирования (CLK). Тактирование МК может
производиться либо от внутреннего генератора c частотой 16МГц (HSI), который является источником
импульсов по умолчанию, либо от генератора низкой частоты 128КГц (LSI), либо от генератора с
внешним кварцевым резонатором / внешнего источника импульсов (HSE). Причем схема переключения
допускает выбор источника тактирования на лету и обеспечивает автоматическое
переключение на внутренний источник (HSI/LSI) при
отказе HSE (в этом
случае возможен вызов обработчика прерывания). Диаграмма, иллюстрирующая устройство блока CLK:
На плате установлен кварцевый резонатор на 16МГц, поэтому
можно задействовать HSE для тактирования. Заодно мы научимся включать переход на HSI в
случае неисправности HSE.
Кроме этого, установим делители частоты тактирования ядра и внутреннего
генератора в 1, ведь мы хотим добиться от МК максимальной производительности. Для
этого запишем в функцию main()
следущие строки:
//Инициализируем CLK
CLK_ECKR_bit.HSEEN=1; //Разрешаем работу генератора с внешним кварцем (HSEEN) CLK_SWCR_bit.SWEN=1; //Разрешаем переключение генераторов; CLK_SWR=0xB4; //Выбираем clock от кварцевого генератора (HSE) CLK_CKDIVR=0; //Делители частоты внутреннего и внешнего генератора на 1 - частота ядра максимальная while(CLK_CMSR!=0xB4); //Ждем стабилизации частоты CLK_CSSR_bit.CSSEN=1; //Разрешаем автопереключение источника Clock при неисправности генератора
Первый способ решения задачи "в лоб” программная
запись значения порта и программная задержка. Для его реализации требуется
настройка режима работы соответствующего порта ввода-вывода (GPIO). Каждый порт имеет несколько
режимов работы, определяемых побитно тремя регистрами управления: Px_DDR отвечает за
направление передачи, Px_CR1 – за "подтяжку” к плюсу
питания в режиме ввода и за тип выхода (двухтактный или открытый сток) в режиме
вывода, Px_CR2 в режиме ввода разрешает
генерацию прерывания при изменении состояния входа, в режиме вывода
ограничивает максимальную крутизну фронта сигнала. Все возможные режимы работы портов сведены в таблицу: * Выходы с реальным открытым стоком определяются производителем для каждой модели МК. Для SM8S105 - это выходы PE1 и PE2, имеющие альтернативные функции I2C.
Для зажигания светодиода сконфигурируем разряд PD0 на вывод с открытым
стоком без ограничений по скорости нарастания. Для этого добавим в программу
следующие строки:
//Инициализируем GPIO. На Discovery светодиод подключен на PD0,
активный уровень - 0
PD_DDR_bit.DDR0=1; //PD0 - на вывод.
PD_CR1_bit.C10=0; //PD0 - открытый сток.
PD_CR2_bit.C20=0; //PD0 - ограничение скорости отключено.
Следует заметить, что все регистры блока GPIO после
сброса МК находятся в состоянии 0, поэтому две последние строчки можно не
писать, я просто иллюстрирую пример записи в данные регистры.
Теперь осталось только изменять состояние бита 0
регистра PD_ODR в цикле с задержкой:
//Собственно
мигание
long i;
while(1)
{
PD_ODR_bit.ODR0=~PD_ODR_bit.ODR0; //Переключим светодиод
for(i=0;i<200000;i++) //Подождем... { asm("nop"); //для исключения влияния оптимизатора
}
}
Скомпилируем полученную программу и запишем ее в МК с
помощью функции Download and Debug среды IAR,
после чего запустим ее на выполнение и увидим мигающий зеленый светодиод LD1 на нашей плате. Частоту
мигания можно изменить, скорректировав конечное значение переменной в цикле for.
Данный способ имеет несколько недостатков. Во-первых,
чтобы получить точное значение времени задержки нужно рассчитывать время
выполнения цикла по ассемблерным инструкциям. Во-вторых, программа не может
выполнять другие действия, пока находится в цикле задержки. Отработать задержку
с заданным временем нам поможет таймер. В МК STM8S105 имеется
4 таймера, Один из них (TIM1)
имеет максимальный набор функций (16-разрядный реверсивный счетчик, 4 канала
захвата/сравнения, предделитель с произвольным коэффициентом деления), два
других (TIM2, TIM3) имеют ограничения по
количеству каналов (3 и 2), нереверсивный счетчик, предделитель с выбором
коэффициента из ряда 2^n последний таймер (TIM4) имеет 8-разрядный нереверсивный счетчик. Все таймеры снабжены
схемой автозагрузки, которая позволяет запускать их в циклическом режиме. Общие характеристики всех таймеров семейства МК STM8S приведены в таблице:
Таймеры TIM5 и TIM6, имеющие возможность каскадного соединения с TIM1, присутствуют только у МК STM8S903K3 и являются его отличительной особенностью по отношению к STM8S103K3. Чтобы
подождать заданное время, мы должны записать в счетчик значение задержки,
разрешить счет в однократном режиме и ожидать остановки по переполнению. Поскольку
счетчик увеличивает свое значение с каждым импульсом тактовой частоты и имеет
256 возможных значений, для 8-разрядного счетчика требуемое начальное значение
может быть вычислено по формуле:
X = 256 - Fclk / (2n / T),
где: Fclk
– частота на выходе блока CLK,
n –
значение регистра предделителя (для TIM4 может быть от 0 до 7), T – время задержки. Максимальное время
задержки для 8-разрядного таймера при Fclk=16МГц равно 0,00204 с, для удобства округлим время до 2 мс,
тогда получим значение X
= 6. чтобы получить мигание светодиода с частотой 1 Гц, нужно изменять
состояние порта каждые 500мс. Для этого вводим дополнительный программный
счетчик на 250. Добавим в программу после инициализации GPIO инициализацию
TIM4:
TIM4_PSCR=0x07; //Предделитель на 2^7=128 16000000/128 = 125000 Hz
TIM4_CR1_bit.OPM=1; //Разрешаем
остановку счетчика по переполнению
Основной цикл программы изменим следующим образом:
//Собственно
мигание
char i=250;
while(1)
{
if(i) i--; //Программный делитель на 250 - мигаем с частотой
1 Гц.
else
{
i=250; PD_ODR_bit.ODR0=~PD_ODR_bit.ODR0; // Изменим состояние светодиода.
}
TIM4_CNTR=6; // Счетчик на
250 125000 / (256-6) = 500 Hz.
TIM4_CR1_bit.CEN=1; // Разрешаем счет.
while(TIM4_CR1_bit.CEN); //Ждем остановки счетчика.
}
В результате, цикл while(1) выполняется
каждые 2 мс, изменение состояния светодиода происходит по равенству нулю
переменной i каждые
500мс. Чтобы убедиться в этом, запишем полученную программу в МК.
Данный пример позволяет задать необходимый период
мигания, но не решает второй проблемы – освобождения временных ресурсов
программы при отработке задержки. Как это сделать с помощью прерываний, мы
рассмотрим в следующей статье.
В статье использованы иллюстрации из документа RM0016 Reference
manual STM8S
microcontroller family. www.st.com.
Проект для IAR-STM8 c примерами к статье >> STM8_001_01.zip Коды с использованием заголовков библиотеки от ST (main.c компилируется на Cosmic, Raisonance, IAR). >> STM8_001_STLIB.zip (!) Для Cosmic не забудьте поместить код обработчика прерывания из примера в файл stm8s_it.c |