У меня в программе 2 таймера, которые генерируют прерывания:
1) вызывает прерывание с частотой 10КГц. Включается при старте BlackFin и не отключается. Используется для счёта системного времени и асинхронного вывода на DO (если состояние DO требуется изменить в конкретном такте).
2) вызывает прерывание с частотой 10Кгц либо 44КГц в разные промежутки времени. Обработчик короткий, но использует асинхронный вывод на DO (если состояние DO требуется изменить в конкретном такте).
Проблема в том что счёт времени сильно "пляшет". Т.е. я отсчитываю с помощью системного счётчика времени интервал 100 мс, а фактически на DO осциллографом вижу скачущий интервал 95-105 мс. При этом кол-во отсчётов таймера0 верное, т.е. = 1000.
Пробовал инициализировать прерывания от таймеров на разные IVG (из свободных 8, 12, 13, 14, 15), так и на одном IVG. Иногда разброс интервалов увеличивается, но лучше чем 5 % отклонения стабильно я не получил.
Подозреваю, что ошибка у меня в работе с таймерами или при обработке прерываний. Код прилагаю. Прошу подсказать хотя бы в каком направлении копать. Отладчика, к сожалению, нет.:(
Инициализация таймеров (на примере таймера 0:
uint32_t Timer_Get_SICmask(uint8_t TimerNum)
{
uint32_t SIC_IAR_Mask = 0xFFFFFFFF;
SIC_IAR_Mask &= ~(0xF << (TimerNum << 2));
return SIC_IAR_Mask;
}
int8_t Timer0_Start(uint32_t FreqHz)
{
if (!FreqHz)
return -1;
// Регистрируем обработчик для ivg13
REGISTER_ISR(13, ivg13_isr);
// Выключаем таймер
*pTIMER_DISABLE = TIMDIS0;
// Настраиваем таймер
*pTIMER0_CONFIG = OUT_DIS | PWM_OUT | PERIOD_CNT | IRQ_ENA; // Выход ШИМ отключен, режим PWM_OUT, счёт до периода, прерывание включено, сброс таймера в 1 (не в 0)
// Период счёта таймера до обнуления с формированием запроса на прерывание
*pTIMER0_PERIOD = (TMR_SCLK / FreqHz);// - 1;
// Приоритет оставляем по-умолчанию (IVG13)
*pSIC_IAR4 = (*pSIC_IAR4 & Timer_Get_SICmask(0)) | P32_IVG(13);
// Включаем у SIC вход от периферии
*pSIC_IMASK1 |= IRQ_TIMER0;
// Обнуляем регистр-счётчик
*pTIMER0_COUNTER = 0;
// Снимаем флаг сработавшего таймера
TimerState &= ~(1 << 0);
// Запускаем таймер
*pTIMER_ENABLE = TIMEN0;
return 0;
}
Обработчик:
#define TIMER_STATUS_FLAGS_MASK_T0 0x00000001 // Маска флагов TIMILx и TOVF_ERRx в регистре TIMER_STATUS для таймера 0
#define TIMER_STATUS_FLAGS_MASK_T1 0x00000002 // Маска флагов TIMILx и TOVF_ERRx в регистре TIMER_STATUS для таймера 1
ISR(ivg13_isr)
{
ssync();
if (*pTIMER_STATUS & TIMIL0) // Если timer0 удерживает линию прерывания SIC
{
ssync();
*pTIMER_STATUS = TIMER_STATUS_FLAGS_MASK_T0;
TimerState |= (1 << 0); // Ставим флаг таймера
Timer0_Routine();
}
if (*pTIMER_STATUS & TIMIL1) // Если timer1 удерживает линию прерывания SIC
{
ssync();
*pTIMER_STATUS = TIMER_STATUS_FLAGS_MASK_T1;
TimerState |= (1 << 1); // Ставим флаг таймера
Timer1_Routine();
ssync();
}
}
Асинхронный вывод на DO (используется в обработчике обоих прерываний таймеров):
volatile uint16_t DO_Value; // Текущее значение дискретных выходов
volatile uint32_t DO_Value_Old = 0; // Предыдущее значение дискретных выходов
// Запись заранее подготовленного значения DOUT_Value в DOUT
void DO_Write(void)
{
if (DO_Value == DO_Value_Old)
return;
// Есть свободные дескрипторы для передачи по SPORT
if (sport_tx_req_rdy()) // Проверял - false не возвращает
{
DO_Value_Old = DO_Value;
sport_tx_start_req((uint32_t *)&DO_Value_Old, 1);
}
}