Рейтинг@Mail.ru
STM32. Уроки по программированию STM32F4. Урок № 4. Программный многозадачный таймер STM32F4.
Войти
или
Зарегистрироваться
Главная Файлы Видеоматериалы Форум
Карта сайта
Главная -> РУБРИКИ: -> Программирование STM32F4 -> STM32. Уроки по программированию STM32F4. Урок № 4. Программный многозадачный таймер STM32F4.

Статья опубликована: 2015-12-09/13:38:21-admin

STM32. Уроки по программированию STM32F4. Урок № 4. Программный многозадачный таймер STM32F4.



Начало здесь:


STM32. Уроки по программированию STM32F4. Урок № 0. Вводный. Описание. Установка IDE.


STM32. Уроки по программированию STM32F4. Урок № 1. Система тактирования STM32F4.


STM32. Уроки по программированию STM32F4. Урок № 2. Мигание светодиодом STM32F4.


STM32. Уроки по программированию STM32F4. Урок № 0. Update № 1.Портирование из STM32CubeMX в SW4STM32.


STM32. Уроки по программированию STM32F4. Урок № 3. Системный таймер SysTick STM32F4.




 

Вместо предисловия.

 

На прошлом уроке мигание светодиодом осуществлялось при помощи аппаратного таймера SysTick STM32. Реализация оного обеспечивало неблокирующую задержку для осуществления мигания дабы дать больше времени процессору для выполнения более важных задач. Но организация такой задержки не была структуирована. На этом уроке мы организуем модуль программных многозадачных таймеров для организации неблокирующих задержек для рутинных задач.

 

Предисловие.

 

В РУНЕТе есть несколько реализаций данного модуля, как для STM, так и для других МК. Вот небольшой список данных статей:

  1. Программные таймеры. Часть 1

  2. Многозадачный программный таймер.

  3. Многозадачный программный таймер, ver 2.0

  4. Программный таймер. Применение HAL

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

 

Мини ТЗ.

 

Необходимо составить необльшое ТЗ для данного модуля, что б было проще реализовывать в коде.

Пристутим:

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

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

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

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

Зачем такое множество?

 

Давайте рассмотрим примеры, где нужны эти таймеры.

Циклический таймер. Тут должно быть понятно. Есть датчик, измеряющий инерционную велечину, температуры например, измерять на каждом шаге цикла while смысла нет, достаточно раз в секунду. Отлично, завели таймер на одну секунду и вперед, делаем замеры с интервалом в одну секунду.

Одиночный импульс. Рутинные задачи которые необходимо выполнять после небольшой задержки. Например, отправили запрос по УАРТУ, запустили таймер для ответа, и если таймер досчитал, а ответа не было, выполнили какие то действия по таймауту.

Таймер с задержкой на включение. Защита от банального дребезга контактов. Установили задержку в пару тройку миллсекунд, и если таймер свое осчитал, значит кнопка нажата. Или необходимо диагностировать долгое нажатие на кнопку для перехода в подмену. Секунды две. Тоже данный таймер в помощь. Опять же программное реле времени реализовать.

Таймер с задержкой на выключение. Банальное реле времени или включение подсветки  после нажатия кнопки, а выключение по истечению определенного интервала.

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

 

От слов к делу.

 

Реализуем модуль в виде двух файлов: хидер и сорц, очень оригинально банально.

Вот код хидера:

 

#ifndef SW_TIMER_H_
#define SW_TIMER_H_

#define SwTimerCount  64	//Количество программных таймеров

/*Режимы работы таймеров*/
typedef enum
	{
		SWTIMER_MODE_EMPTY,
		SWTIMER_MODE_WAIT_ON,
		SWTIMER_MODE_WAIT_OFF,
		SWTIMER_MODE_CYCLE,
		SWTIMER_MODE_SINGLE
} SwTimerMode;


/*Стурктура программного таймера*/
typedef struct
    {
	unsigned LocalCount:32;	        //Переменная для отсчета таймера
    	unsigned Count:24;		//Переменная для хранения задержки
    	unsigned Mode:3;		//Режим работы
    	unsigned On:1;			//Разрешиющий бит
    	unsigned Reset:1;		//Сброс счета таймера без его выключения
    	unsigned Off:1;			//Останавливающий бит
    	unsigned Out:1;			//Выход таймера
    	unsigned Status:1;		//Состояние таймера
}SW_TIMER;

#if (SwTimerCount>0)
volatile SW_TIMER TIM[SwTimerCount]; //Объявление софтовых таймеров
#endif


void SwTimerWork(SW_TIMER* TIM, unsigned char Count);	
//Сама функция для обработки таймеров
void OnSwTimer(SW_TIMER* TIM, SwTimerMode Mode, unsigned int SwCount);	
//Подготовливает выбранный из массива таймер
unsigned char GetStatusSwTimer(SW_TIMER* TIM);	
//Считывание статуса таймера
#endif /* SW_TIMER_H_ */

 

Структура для одного таймера занимает 8 байт. Сам таймер является 24-х битным. И при временном интервале аппаратного таймера в 1 мс максимальная задержка программного таймера состовляет 4,66 часа или 16 777 секунд. Вполне достаточно. А если вдруг и этого мало, то можно каскадировать таймеры, запуская следующий выходом предыдущего таймера.

 

Функции.

 

void SwTimerWork(SW_TIMER* TIM, unsigned char Count){
	unsigned short i=0;
		for (i=0; iMode==SWTIMER_MODE_EMPTY){
                              TIM++;
                              continue;
                        }
			if (TIM->Mode==SWTIMER_MODE_WAIT_ON){
 //Если таймер на задержку включения
				if (TIM->On){
					if (TIM->LocalCount>0) TIM->LocalCount--;
						else {
							TIM->Out=1;
							TIM->Status=1;
						}
				}
				else {
					TIM->Out=0;
					TIM->LocalCount=TIM->Count-1;
				}
			}
			if (TIM->Mode==SWTIMER_MODE_WAIT_OFF){
 //Если таймер на задержку выключения
				if (TIM->On){
					TIM->Out=1;
					TIM->Status=1;
					TIM->LocalCount=TIM->Count-1;
				}
				else {
					if (TIM->LocalCount>0) TIM->LocalCount--;
						else TIM->Out=0;
				}
			}
			if (TIM->Mode==SWTIMER_MODE_CYCLE){
				if (TIM->Off){
					if (TIM->On){
						TIM->Off=0;
						if (TIM->LocalCount>0) TIM->LocalCount--;
					}
				}
				else{
					if (TIM->LocalCount>0) {
						TIM->LocalCount--;
						TIM->Out=0;
					}
					else {
						TIM->Out=1;
						TIM->Status=1;
						TIM->LocalCount=TIM->Count-1;
					}
				}
				if (TIM->Reset){
					TIM->LocalCount=TIM->Count-1;
					TIM->Out=0;
					TIM->Status=0;
				}
			}
			if (TIM->Mode==SWTIMER_MODE_SINGLE){
				if (TIM->Off){
					if (TIM->On){
						TIM->Off=0;
						if (TIM->LocalCount>0) TIM->LocalCount--;
					}
				}
				else{
					if (TIM->LocalCount>0) {
						TIM->LocalCount--;
						TIM->Out=0;
					}
					else {
						TIM->Out=1;
						TIM->Status=1;
						TIM->LocalCount=TIM->Count-1;
						TIM->Off=1;
					}
				}
				if (TIM->Reset){
					TIM->LocalCount=TIM->Count-1;
					TIM->Out=0;
					TIM->Status=0;
				}
			}

			TIM++;
		}
}

 

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

 

void OnSwTimer(SW_TIMER* TIM, SwTimerMode Mode, unsigned int SwCount){
		TIM->Mode=Mode;
		if (SwCount){
			TIM->Count=SwCount;
			TIM->LocalCount=SwCount-1;
		}
		if (TIM->Mode==SWTIMER_MODE_CYCLE || TIM->Mode==SWTIMER_MODE_SINGLE){
			TIM->Off=1;
		}
}

 

Функция инициализации таймера в массиве. Указываем режим работы и временной интервал. В данном примере переполнение системного таймера происходит с пероидом в 1 мс, так что временная задержка указывается в мс.

 

unsigned char GetStatusSwTimer(SW_TIMER* TIM){
	unsigned char status=0;
	if (TIM->Mode==SWTIMER_MODE_EMPTY) return -1;
	if (TIM->Status){
		TIM->Status=0;
		status=1;
	}
	else {
		status=0;
	}
	return status;
}

Функция считывания статусного бита таймера, если он сработал, функция вернет нам 1, если нет то 0. Если мы указали пустой таймер, функция вернет -1. Статусный бит будет установлен до тех пор пока не будет прочитан.

 

Где обрабатывать функцию обработки таймеров?

 

Варианта в принципе 2.

  1. В обработчике прерывания.
  2. В функции майн по флагу из прерывания.

Рассмотрим и тот и другой.

/* USER CODE BEGIN 0 */
#include "sw_timer.h"
/* USER CODE END 0 */

void SysTick_Handler(void)
{
  /* USER CODE BEGIN SysTick_IRQn 0 */

  /* USER CODE END SysTick_IRQn 0 */
          HAL_IncTick();
          HAL_SYSTICK_IRQHandler();

  /* USER CODE BEGIN SysTick_IRQn 1 */
         SwTimerWork(TIM,SwTimerCount);
  /* USER CODE END SysTick_IRQn 1 */
}

 

Или из майна.

/* USER CODE BEGIN 0 */
extern uint8_t FlagSwTimer;
/* USER CODE END 0 */

void SysTick_Handler(void)
{
  /* USER CODE BEGIN SysTick_IRQn 0 */

  /* USER CODE END SysTick_IRQn 0 */
  HAL_IncTick();
  HAL_SYSTICK_IRQHandler();

  /* USER CODE BEGIN SysTick_IRQn 1 */
  FlagSwTimer=1;
  /* USER CODE END SysTick_IRQn 1 */
}

/*В майне*/
while(1){
        if (FlagSwTimer){
                #if (SwTimerCount>0)

                        //Обработчик программных таймеров из sw_timer.c
                        SwTimerWork(TIM,SwTimerCount);

                #endif
        FlagSwTimer=0;
}

Для быстрых прерываний второй вариант предпочтительней. Но если основной цикл майна долгий, больше времени переполнения аппаратного таймера, то модуль будет работать не коректно в плане задержек. Вариантов решения тоже несколько:

  1. Вынести данную функцию в прерывание
  2. Оптимизировать код майна
  3. Несколько раз опрашивать флаг по ходу исполнения основного цикла
  4. Увеличить время переполнения таймера
  5. Может что то еще

 

Примеры использования.

 

#define I_DI_READ_0() HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_0)
 //Считывание кнопки

        OnSwTimer(&TIM[0],SWTIMER_MODE_WAIT_OFF,1000);
        OnSwTimer(&TIM[1],SWTIMER_MODE_SINGLE,1000);
        OnSwTimer(&TIM[2],SWTIMER_MODE_WAIT_ON,1000);
        OnSwTimer(&TIM[3],SWTIMER_MODE_CYCLE,1000);
        TIM[3].Off=0;    
        TIM[3].On=1;    //Запустили циклический таймер
        TIM[1].Off=0;
        TIM[1].On=1;    //Запустили одиночный таймер

 

Инициализируем таймеры и запустим циклический и одиночный таймер установкой соответствующих битов.

В основном цикле продемонстрируем возможности.

 

 while (1){
        TIM[2].On=I_DI_READ_0();    
//Считываем кнопку, и пока нажата таймер отсчитывает время
        HAL_GPIO_WritePin(GPIOD,GPIO_PIN_15,TIM[2].Out);    
//Как до тикал, зажгли светодиод
        TIM[0].On=I_DI_READ_0();   
 //Тот же фокус, но с таймером с задержкой на выключение
        HAL_GPIO_WritePin(GPIOD,GPIO_PIN_14,TIM[0].Out);    
//Пока тикает, горит светодиод
        if (GetStatusSwTimer(&TIM[3])){
            HAL_GPIO_TogglePin(GPIOD,GPIO_PIN_13);   
 //Обрабатываем флаг циклического таймера, инвертируем выход
        }
        if (GetStatusSwTimer(&TIM[1])){
            HAL_GPIO_TogglePin(GPIOD,GPIO_PIN_12);    
//Обрабатываем флаг одиночного таймера, инвертируем выход
        }
        TIM[3].Reset=I_DI_READ_0();    
//Пока нажата кнопка, ресетим циклический таймер
}

Останавливать таймер с задержкой на включение или выключение установкой бита Off нет смысла, так как таймер тикает в первом случае пока есть разрешающий бит, во втором, как только есть нисходящий фронт на входе разрешающего бита. Так же как и смысл их ресетить.
Если необходимо остановить циклический или одиночный таймер, то необходимо сбросить бит включения и выставить бит отключения.

    TIM[3].On=0;
    TIM[3].Off=1;

Повторное включение через установку запускающего бита On.
Считывание статуса можно производить как функцией, так и непосредственно считывая бит.

if (GetStatusSwTimer(&TIM[1]))     //Можно так
    if (TIM[3].Status){                //Или так
        TIM[3].Status=0;                
//Сбрасываем бит, так как он сбросится только после очередного вызова
//основной функции обработки массива,
//то есть через переполнение аппаратного таймера
        /*UserCode*/
    }

Если таймер больше не нужен, то сократить время выполнения функции обработки массива таймеров можно если не просто остановить таймер, а удалить его, то есть перевести в режим ПУСТО. Для этого вызываем функцию подготовки таймера с режимом SWTIMER_MODE_EMPTY. Или прямо это указываем.

    OnSwTimer(&TIM[3],SWTIMER_MODE_EMPTY,0);    //Можно так
    TIM[3].Mode=SWTIMER_MODE_EMPTY;            //Или так

Не много разные по смыслу первые два таймера и вторые два объединенны в одну структуру, дабы не плодить лишних функций и т.д.

 

Остальное смотри в видео к уроку посвященному программному многозадачному таймеру на STM32.

Уроки по программированию STM32F4. Урок № 4. Программный многозадачный таймер STM32F4. Видео.

 

Проект данного урока для Эклипс - Урок №4. Программный многозадачный таймер на STM32F4 скачать можно от сюда: тыц для скачивания.

 

Архив с файлами модуля можно скачать от сюда.

 

З.Ы. коментарии, вопросы и предложения складываем тут

 


Продолжение здесь:


STM32. Уроки по программированию STM32F4. Урок № 5. Работа с АЦП+DMA + фильтр скользящее среднее.


STM32. Уроки по программированию STM32F4. Урок № 6. Работа с таймерами TIM7 и TIM1.



Просмотров: 13154



Комментарии: (0)

Оставить комментарий

Да, Я Хочу Всегда Быть В Курсе Новых Событий На Сайте!

Подпишитесь прямо сейчас, и получайте обновления на свой E-Mail:

Ваш E-Mail в безопасности


Рекомендованные статьи:



Данная статья является продолжением цикла видео уроков по изучению МК STM32 на базе STM32F407. На данном уроке разберем модуль АЦП. Его работу и передачу данных с каналов АЦП через DMA в регистры ОЗУ. Дальнейшей фильтрацией по методу скользящего среднего.


Изучение STM32 на основе STM32F4. Системный таймер SysTick STM32F4. Рассмотрим работу системного таймера STM32.


РУБРИКИ:








Последняя статья:

Часть I. Статья №6. Верстка подвала – блока футтер

На прошлом уроке мы сверстали основной блок контент. Оформили  статьи, которые отображаются на главной странице. Оформили их стилями, не входящими в макет сайта, но вписывающимися в общий дизайн сайта. На сегодняшнем уроке мы оформим подвал сайта. В данный блок войдут ссылки на стандартные страницы любого сайта, а также копирайт.

Читать далее »


Справка Обратная связь Вопросы и ответы Контакты RSS-лента © 2013-2016, ДРУиД - Дом Рационально-Умный и Душевный
Рейтинг@Mail.ru Яндекс.Метрика