Перейти к основному содержимому

Работа с таймером

Документ применим к микросхемам 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 происходит только в пределах границ команды, поэтому оно не вызывает сброса или другую переинициализацию аппаратных средств. Состояние кэш, памяти, а также другие состояния процессора остаются неизменными. Значения регистров также сохраняются за исключением следующего:

  1. Поля BEV, TS, NMI и ERL регистра Status принимают заданные значения.
  2. В регистр ErrorEPC загружается значение PC - 4, если прерывание произошло на фоне команды в слоте задержки 1. перехода. В противном случае в регистр ErrorEPC загружается значение PC.
  3. В 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) ; //
}
}
}