Работа с таймером
Документ применим к микросхемам 1892ВМ3Т, 1892ВМ2Я, 1892ВМ5Я, 1892ВМ8Я, 1892ВМ10Я, 1892ВМ12Т, 1892ВМ12АТ, 1892ВМ12Т, 1892ВМ15Ф, 1892ВМ15АФ. В документе поясняются принципы работы с таймерами в процессорах на базе MIPS серии «Мультикор».
Интервальный таймер
Интервальный таймер (IT) предназначен для выработки периодических прерываний на основе деления тактовой частоты CPU, либо внешней тактовой частоты (XTI или RTCXTI). Основные характеристики таймера:
- число разрядов делителя – 32;
- число разрядов предделителя – 8;
- программное управление стартом и остановкой таймера;
- доступ ко всем регистрам обеспечивается в любой момент времени.
Для того чтобы выбрать режим тактирования (CLK - тактовая частота CPU или внешние частоты), нужно записать соответствующее значение в поле CLK_SEL регистра ITCSR, см. руководство пользователя на микросхему.
Сторожевой таймер. Режимы работы и как их применять
Сторожевой таймер (WDT) предназначен для:
- вывода системы из зависания, если программное обеспечение зациклилось и не формирует соответствующих управляющих воздействий̆;
- выработки прерываний на основе деления тактовой частоты CPU.
Основные характеристики таймера:
- число разрядов основного делителя – 32;
- число разрядов предделителя – 8;
- программное управление стартом и остановкой таймера;
- два режима работы: режим сторожевого таймера (WDM) и режим интервального таймера (ITM);
- два режима отработки временных интервалов: однократный и периодический;
- доступ ко всем регистрам обеспечивается в любой момент времени.
Сторожевой таймер может работать в двух режимах – в режиме сторожевого либо интервального таймера. Режим работы таймера задается полем MODE регистра WTCSR (только при значении поля EN=0):MODE=0 – режим сторожевого таймера (WDM); MODE=1 – режим интервального таймера (ITM). В исходном состоянии WDT находится в режиме сторожевого таймера.
WDT в режиме сторожевого таймера
WDT в режиме сторожевого таймера генерирует NMI. В этом случае происходит останов по HW-точке останова на адресе 0xBFC0_0000. Сторожевой таймер в режиме WDM необходимо периодически обслуживать. То есть, если он был активизирован в режиме WDM, то для того, чтобы не возникло состояния Timeout, необходимо периодически выполнять следующую последовательность действий:
- переключить таймер из режима WDM в режим ITM посредством последовательной записи в поле KEY регистра WTCSR кодов А0 и F5;
- остановить таймер посредством записи 0 в бит EN регистра WTCSR;
- установить MODE=0.Если вслед за значением А0 в поле KEY будет записано значение ≠ F5, то таймер перейдет в состояние Timeout.
Если после активизации таймера в режиме WDM, он не будет переведен в режим ITM, то, когда оба
счетчика SCOUNT и WTCOUNT достигнут нулевого значения (через {(WTPERIOD + 1)*(WTSCALE + 1)}
тактов
работы CPU), таймер перейдет в состояние Timeout и генерирует NMI. В состоянии Timeout таймер
формирует признак INT и останавливается, а запись в какой- либо из его регистров блокируется.
Для вывода WDT из состояния Timeout необходимо его переключить в режим ITM посредством последовательной записи в поле KEY регистра WTCSR кодов А0 и F5. При переключении таймера из неактивного состояния в режиме ITM в режим WDM путем записи 0 в поле MODE регистра WTCSR происходит обнуление полей KEY и INT.
WDT в режиме интервального таймера
При работе таймера в режиме ITM при RLD=0 он однократно отрабатывает заданный временной интервал, устанавливает INT=1 и останавливается (когда оба счетчика SCOUNT и WTCOUNT достигают нулевого состояния). Если RLD=1, то каждый раз после достижения счетчиками нулевого состояния и установки INT=1, происходит пе резагрузка значений периода и коэффициента предделения частоты. То есть, таймер отрабатывает заданный временной интервал периодически до тех пор, пока он не будет остановлен.
Запрос на прерывание формируется каждые {(wtperiod + 1)*(wtscale + 1)}
тактов работы CPU, где
wtperiod
и wtscale
– содержимое регистров WTPERIOD
и WTSCALE
соответственно.
Применение сторожевого таймера в режимах WDT и интервального проиллюстрировано во встроенных в
MCStudio примерах WDTimer_handler_itimer_mode и WDTimer_handler_watchdog_mode. А также в примере
Start_nRST_NMI.
Немаскируемое прерывание
NMI (Non Maskable Interrupt) – немаскируемое прерывание. Возникает по положительному фронту входного сигнала NMI или при срабатывании сторожевого таймера WDT. Исключение NMI происходит только в пределах границ команды, поэтому оно не вызывает сброса или другую переинициализацию аппаратных средств. Состояние кэш, памяти, а также другие состояния процессора остаются неизменными. Значения регистров также сохраняются за исключением следующего:
- Поля BEV, TS, NMI и ERL регистра Status принимают заданные значения.
- В регистр ErrorEPC загружается значение PC - 4, если прерывание произошло на фоне команды в слоте задержки 1. перехода. В противном случае в регистр ErrorEPC загружается значение PC.
- В PC загружается значение 0xBFC0_0000.
Вектор исключения: Reset (0xBFC0_0000) Operation:
StatusBEV <= 1 StatusTS <= 0 StatusNMI <= 1
StatusERL <= 1if InstructionInBranCHDelaySlot then ErrorEPC <= PC - 4elseErrorEPC <= PC endif
PC <= 0xBFC0_0000
NMI срабатывает по переходу из нуля в единицу.
Стартовый код при работе с NMI
При работе с NMI при старте (всегда с 0xBFC0_0000) не требуется инициализация порта внешней памяти (она уже проинициализирована по аппаратному ресету nRST). Поэтому, алгоритм стартового кода будет следующим.
- Проверить, установлен ли бит 19 (NMI) в регистре Status CP0.
- Если не установлен, то произошел аппаратный сброс, и программа стартует по ресету, если нет - то в бит NMI необходимо программно записать ноль, чтобы очистить его, см. РП на процессор 1892ВМ10Я.
Пример обнаружения произошедшего NMI прерывания и возможные дальнейшие действия проиллюстрированы в примере Start_nRST_NMI.
Для данного случая значение регистра предделителя WTSCALE =0, значение периода
WTPERIOD = period-1
, period – период срабатывания таймера 5 сек.
Код его основной программы приведен ниже.
/*******************************************************************
* Start_nRST_NMI *
* Пример, работающий из флеш. *
* Программа стартует из адреса 0xBFC0_0000. *
* Проверяет бит 19 (NMI) регистра Status. *
* Если был аппаратный сброс, помигать одним диодом, *
* если прерывание по NMI - другим. *
* При включении питания происходит аппаратный сброс, *
*далее мигает соответствующий диод (VD4 на модуле NVCOM-02TEM-3U).*
* После прошествии некоторого времени включается WDT, *
*происходит NMI и мигает уже другой диод (VD3 на NVCOM-02TEM-3U) *
*******************************************************************/
#include "multicore/nvcom02t.h"
// Частота CPU в Гц
#define FREQ 240000000
// установка длительности паузы в тактах частоты CPU, 1 секунда
#define TIMEOUT 10000000
#define TIMING (1*FREQ) // 1 секунда
// длительность работы сторожевого таймера до генерирования прерывания - 5 секунд
#define WD_TIMING (20*FREQ) //
// режимы работы сторожевого таймера
#define WATCHDOG_MODE 0 // режим сторожевого таймера
#define ITIMER_MODE 1 // режим интервального таймера
unsigned int GetCP0_Count() {
unsigned int result;
asm volatile ("mfc0 %0, $9" :"=r"(result));
return result;
}
unsigned int GetCP0_Status() {
unsigned int result;
asm volatile ("mfc0 %0, $12" :"=r"(result));
return result;
}
void SetCP0_Count(unsigned int value) {
asm volatile ("mtc0 %0, $9" ::"r"(value));
}
void SetCP0_Status(unsigned int value) {
asm volatile ("mtc0 %0, $12" ::"r"(value));
}
// Инициализация таймера
// Параметр - период срабатывания таймера в тактах процессора
void WDTimer_Init(long long period, unsigned int mode) {
WDT.WTPERIOD.data = period -1; // в соответсвии с формулой периода //срабатывания таймера
// period = (WTPERIOD+1)*(WTSCALE+1)
WDT.WTSCALE.data = 0;// пример для значения коэффициента //предделения равного 0
if (mode==ITIMER_MODE) {
WDT.WTCSR.bits.MODE = 1; // itimer
WDT.WTCSR.bits.RLD = 1; // itimer
WDT.WTCSR.bits.INT_CTR = 1; // generate QSTR
}
if (mode==WATCHDOG_MODE) {
WDT.WTCSR.bits.MODE=0; // wdtimer
WDT.WTCSR.bits.INT_CTR=2; // generate NMI
}
}
void WDTimer_Start() {
WDT.WTCSR.data |= (1<<8);
}
int main() {
SYS_REG.CR_PLL.data = 0xb01230;//DSP240MHz,MPORT90MHz,RISC 240MHz
SYS_REG.CLK_EN.data = 0xFFFFFFFF; // запись единицы в системный //регистр CLK_EN
MFBSP1.DIR.data = 0xC0;//переключаем выводы LDAT[5:4] в режим выхода
if ((GetCP0_Status() & 0x80000) == 0) { // если не NMI (аппаратный //сброс), то запускаем WDT и
// мигаем диодом VD4 на отладочном модуле NVCOM-02TEM-3U
unsigned int CP0_Status = 0x401;
// Разрешаем прерывания вообще (CP0.Status[0]) и прерывания в QSTR0 в частности (CP0.Status[10])
SetCP0_Status(CP0_Status);
// Разрешаем прерывания от сторожевого таймера
SYS_REG.MASKR0.data |= 1<<20;
WDTimer_Init(WD_TIMING, WATCHDOG_MODE);
WDTimer_Start();
while (1) {
MFBSP1.GPIO_DR.data ^= 0x80; //GPIO_DR1[7:6]
SetCP0_Count(0); // CP0.Count
while (GetCP0_Count()<TIMEOUT) ; //
}
}
else {// если произошло NMI - мигаем диодом VD3
while (1) {
MFBSP1.GPIO_DR.data ^= 0x40; // GPIO_DR1[7:6]
SetCP0_Count(0); // CP0.Count
while (GetCP0_Count()<TIMEOUT) ; //
}
}
}