Stellaris Launchpad. 2. Время, время!

Apr 06, 2013 00:49

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

Не знаю, как вас, а меня в мигании диодом нарягла, в первую очередь, невозможность адекватно управлять задержками и системным временем.
Нет, понятно, что раз речь идет об одном потоке в условиях известной частоты, задержко можно соорудить простым циклом.
Но, во-первых, точность будет в этих условиях плюс-минус лапоть, учитывая, причуды компилятора с точки зрения оптимизации, а во-вторых этот подход хорош только до тех пор, пока нам нужна просто тупая задержка, во время которй мы ничего не делаем. А что делать, если пока горид диод нам еще чем-нибудь полечным позаниматься надо? Да еще с непредсказуемым временем исполнения основного цикла?
Итак, на помощь нам приходят таймеры.
В общей сложности у нас есть 12 независимых таймеров общего назначения. 6 тридцатидвухбитных и 6 шестидесятичетырехбитных.
Причем каждый из них можут быть разделен на два независимых таймера вдвое меньшего разрешения.
То есть, всего в программе можно использовать до 24 таймеров.
Каждый таймер может использоваться в следующем виде:
  • Повторяющийся счет. Считает от установленного значения до нуля (в режиме count-down) или от нуля вверх (в режиме count-up). Досчитав до конца, выставляет сигнальный бит, возможно вызывает прерывание, после чего начинает считать сначала.
  • One-shot Все тоже самое, но досчитав до конца останавливаемся.
  • Счетчик фронтов входного сигнала. Считает фронты на указанном пине. Может считать передние, задние или любые фронты. Насчитав нужное количество фронтов выставляет флаг, возможно вызывает прерывание, после чего, в зависимости от режима либо продолжает считать с нуля, либо останавливается.
  • Счетчик времени между фронтами входного сигнала. Считает количество тактов между двумя соседними фронтами. Опять же, может быть настроен на передний, задний или оба фронта.
  • RTC (в даташите написано, что при этом нужен внешний 32768Гц сигнал, подробнее вопрос не изучал).
  • РулитьШИМом (с управлением частоты и скважности)
  • Генерить тригеры для АЦП (пора померять) или другого таймера (пора стартовать).
Вообще-то, описание таймеров в даташите занимает 70 страниц, так что все, что я написал - это очень короткая выжмка.
Кроме таймеров общего назначения есть еще один системный таймер и два сторожевых. Их мы пока оставим в покое, а использовать будет один из таймеров общего назначения.

Для начала определимся, чего мы хотим. Пусть все дубед просто - глобальная переменная, которая показывает количество "тиков" с начала старта МК. Один тик пусть будет 10 мкс. И пару функций - заснуть на столько-то мс и заснуть на столько-то мкс.

#ifndef _TIMER_UTILS_H_
#define _TIMER_UTILS_H_

extern unsigned long sysTime;
void sysSoftTimerInit(unsigned long timerBase, unsigned long timerPart);
void delay_milli(unsigned long delTime);
void delay_micro(unsigned long delTime);
#endif

И реализация:

#include "timerUtils.h"
#include "inc/hw_types.h"
#include "inc/hw_memmap.h"
#include "driverlib/sysctl.h"
#include "driverlib/timer.h"
#include "driverlib/interrupt.h"

unsigned long sysTime;
unsigned long sysSoftTimerTimerBase;
unsigned long sysSoftTimerTimeoutClearFlag; //четыре байта ОЗУ нам менее критичны, чем пара тактов в прерывании таймера, которое должно быть максимально быстрым,
//так что вычислим этот флаг раз и навсегда

void sysSoftTimerIntHandler()
/* эта функция - обработчик прерывания по таймеру, она срабатывает всякий раз,
* когда таймер насчитает нужное время. В данном примере - 10мкс.
*/
{
TimerIntClear(sysSoftTimerTimerBase, sysSoftTimerTimeoutClearFlag); //очищаем флаг "прерывание случилось"
sysTime++;
}

void sysSoftTimerInit(unsigned long timerBase, unsigned long timerPart)
{
//используем указанный таймер в качестве "системного" для отсчета времени.
//timerBase - какой таймер используем (0..6)
//timerPart = TIMER_A, TIMER_B
unsigned long timerPeriph;
switch (timerBase)
{
case TIMER0_BASE: timerPeriph = SYSCTL_PERIPH_TIMER0; break;
case TIMER1_BASE: timerPeriph = SYSCTL_PERIPH_TIMER1; break;
case TIMER2_BASE: timerPeriph = SYSCTL_PERIPH_TIMER2; break;
case TIMER3_BASE: timerPeriph = SYSCTL_PERIPH_TIMER3; break;
case TIMER4_BASE: timerPeriph = SYSCTL_PERIPH_TIMER4; break;
case TIMER5_BASE: timerPeriph = SYSCTL_PERIPH_TIMER5; break;
} //TODO добавить поддержку 64битных таймеров и контроль за корректностью входных значений

SysCtlPeripheralEnable(timerPeriph); //Включаем наш таймер
sysSoftTimerTimerBase = timerBase; //понадобится в обработчике, чтобы знать флаг какого таймера гасить.
sysSoftTimerTimeoutClearFlag = ( timerPart==TIMER_B ? TIMER_TIMB_TIMEOUT : TIMER_TIMA_TIMEOUT ); //и это тоже

TimerConfigure(sysSoftTimerTimerBase, ( timerPart==TIMER_B ? TIMER_CFG_B_PERIODIC : TIMER_CFG_A_PERIODIC ) ); //Устанавливаем таймер на работу периодически
TimerLoadSet(sysSoftTimerTimerBase, timerPart, SysCtlClockGet() / 100000); //и срабатывать 10 раз в милисекунду

IntMasterEnable(); //разрешаем прерывания вообще
TimerIntEnable(sysSoftTimerTimerBase, sysSoftTimerTimeoutClearFlag); //и нужного таймера в частности
sysTime = 0;
TimerIntRegister(sysSoftTimerTimerBase, timerPart, sysSoftTimerIntHandler); //timerIntHandler будет вызываться по срабатыванию таймера
TimerEnable(sysSoftTimerTimerBase, timerPart); //включаем таймер
}

//Немножко ардуино-стайл функций и макросов
#define millis() (sysTime/100)
#define micros() (sysTime*10)
void delay_milli(unsigned long delTime)
{
unsigned long start = sysTime;
while(sysTime < start+delTime*100);
}

void delay_micro(unsigned long delTime)
{
unsigned long start = sysTime;
while(sysTime < start+delTime/10);
}

Сразу скажу, что по какой-то непонятной причине заставить работать таймеры B мне не удалось - только А. Почему так - буду разбираться.

Ну и главная функция, для проверки того, что все работает как надо.
Как водится, помигаем диодиком.

#include "inc/hw_gpio.h"
#include "inc/hw_memmap.h"
#include "inc/hw_types.h"
#include "driverlib/gpio.h"
#include "driverlib/sysctl.h"
#include "driverlib/timer.h"

#include "timerUtils.h"

#define GREEN_LED GPIO_PIN_3

int
main(void)
{
char led_state;
sysSoftTimerInit(TIMER2_BASE, TIMER_A);

SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOF);
GPIOPinTypeGPIOOutput(GPIO_PORTF_BASE, GREEN_LED);
led_state = 0;

while(1){ //loop forever
led_state ^= GREEN_LED;
delay_micro(500000);
GPIOPinWrite(GPIO_PORTF_BASE, GREEN_LED, led_state);
}
}

В следующий раз пообщаемся с компом по виртуальному ком-порту.
А вот чем заниматься после - предложения принимаются. Если подключю LCD экран, попробую поэкспериментировать с ним.

stellaris launchpad, Микроконтроллеры

Previous post Next post
Up