Jump to content

    
Sign in to follow this  
abi

XMEGA прерывания

Recommended Posts

Здравствуйте. Помогите пожалуйста разобраться. Собрал для примера тестовый проект, МК ATxmega256A3U, scmRTOS 5.1.0. Во время выполнения процесса перестают работать прерывания, как только процесс уходит в sleep прерывания начинают работать. В чем может быть причина, что прерывания не работают во время выполнения кода процесса? Причем прерываний нет и от системного таймера OS TCDO, когда работает процесс.

int main()
{
    BspClockInit();
    BspPin2Init();
    BspComUartConnect();
    ComSetFrameFormat();
    ComSetBaudRate(Baud_19200BPS);
    
    TCD0.CTRLA    = TC_CLKSEL_DIV1_gc;
    TCD0.CTRLB    = TC_WGMODE_NORMAL_gc;
    TCD0.CTRLC    = 0x00;
    TCD0.CTRLD    = 0x00;
    TCD0.PER      = 24000 - 1;
    TCD0.INTCTRLA = TC_OVFINTLVL_LO_gc;
    TCD0.CTRLFSET = TC_CMD_UPDATE_gc;
    TCD0.CTRLFCLR = TC_CMD_UPDATE_gc;
    TCD0.CTRLFSET = TC_CMD_RESTART_gc;
    TCD0.CTRLFCLR = TC_CMD_RESTART_gc;
    
    TCF0.CTRLA    = TC_CLKSEL_DIV1024_gc;
    TCF0.CTRLB    = TC_WGMODE_NORMAL_gc;
    TCF0.CTRLC    = 0x00;
    TCF0.CTRLD    = 0x00;
    TCF0.PER      = 234 - 1;
    TCF0.INTCTRLA = TC_OVFINTLVL_LO_gc;
    TCF0.CTRLFSET = TC_CMD_UPDATE_gc;
    TCF0.CTRLFCLR = TC_CMD_UPDATE_gc;
    TCF0.CTRLFSET = TC_CMD_RESTART_gc;
    TCF0.CTRLFCLR = TC_CMD_RESTART_gc;
    
    PMIC.CTRL = PMIC_LOLVLEN_bm | PMIC_RREN_bm;

    OS::run();
}

template<> void TProc1::exec()
{
    unsigned char temp;
    
    for(;;)
    {
        sleep(1000);
        for (unsigned char i = 0; i < 50; i++)
        {
            unsigned int cnt = TCF0.CNT;
            ComPutchar(cnt >> 8);
            ComPutchar(cnt & 0xFF);           
            temp = TCF0.INTFLAGS;
            ComPutchar(temp);
            temp = SREG;
            ComPutchar(temp);
        }
    }
}

#pragma vector=TCF0_OVF_vect
OS_INTERRUPT void TimerF0_period_ISR()
{    
    BspPinTgl2();
}


Во время выполнения процесса в цикле в UART вывожу состояния регистров таймера TCF0 и SREG

CNT FLAGS SREG
0058  f0   82  В TCF0.INTFLAGS флаг переполнения сброшен, прерывания работали.
0065  f0   80 
0095  f0   80 
00c6  f0   80 
000d  f1   80  Произошло переполнение таймера, выставлен флаг переполнения в TCF0.INTFLAGS, 
003e  f1   80  в SREG I=1 прерывания разрешены
006e  f1   80 
009f  f1   80 
00d0  f1   80  Произошло повторное переполнение таймера, прерывания нет, флаг не сброшен.
0017  f1   80 
0047  f1   80 
0078  f1   80 
 


Заранее спасибо за помощь.

 

Share this post


Link to post
Share on other sites
25 minutes ago, abi said:

В чем может быть причина, что прерывания не работают во время выполнения кода процесса?

Может быть вы их выключаете в функциях работы с последовательным портом? Ну или не вы, а либа, если используете таковую...

 

Share this post


Link to post
Share on other sites
6 minutes ago, haker_fox said:

Может быть вы их выключаете в функциях работы с последовательным портом? Ну или не вы, а либа, если используете таковую...

 

В цикле только одна функция используется, просто в UART передает данные

void ComPutchar(char ch)
{
    while ((USARTD1.STATUS & USART_DREIF_bm) == 0);
    USARTD1.DATA = ch;
}

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

#define delay_ms(ms) ( __delay_cycles((F_CPU / 1000UL) * (ms)) )
  
  
for(;;)
{
	sleep(1000);
	delay_ms(10);
}

В логе видно, что флаги устанавливаются, прерывания разрешены, вроде все условия для выполнения прерывания есть, но их нет до тех пор пока процесс не уходит в sleep

Share this post


Link to post
Share on other sites
В 17.02.2020 в 21:06, abi сказал:

В логе видно, что флаги устанавливаются, прерывания разрешены, вроде все условия для выполнения прерывания есть, но их нет до тех пор пока процесс не уходит в sleep

Процесс уходит в слип - означает, что процесс просто отдал управление ядру, а оно, соответственно, передало его (управление) другому процессу. Когда работает ядро, прерывания заблокированы (это короткое время), и если они начинают работать "во время слип", это значит, что они работают, когда активен другой процесс. У другого процесса другой контекст и значения регистров тоже другие. Похоже, что у интересующего вас процесса почему-то залочены прерывания. А у другого не залочены.

 

Что блокирует прерывания во время работы процесса, с ходу сказать сложно. Попробуйте в коде процесса сграбить значение регистров, которые рулят прерываниями - статусный  регистр, где глобальный флаг I (если правильно помню, с AVR работал в позапрошлой жизни), например, скопировать его в глобальную переменную и потом вывести её. Если есть эмулятор, то можно остановиться по брейкпоинту и посмотреть просто значения всех интересующих регистров.

Share this post


Link to post
Share on other sites
6 hours ago, AHTOXA said:

А разве scmRTOS поддерживает XMEGA?

Сделал несколько небольших изменений для работы с xmega, если кому интересно позже открою проект. Все прерывания установил на один уровень. Перепланирование процессов производится прямой передачи управления #define scmRTOS_CONTEXT_SWITCH_SCHEME  0. Изначально основной проект был сделан под mega128, затем решил адаптировать его под xmega. Проект заработал, но заметил при приеме данных по UART, временами пакеты принимаются с ошибкой. Позже выяснил причину, оказалось, что когда какой-либо процесс активный, прерывания процессор не выполняет. Прерывание откладывается на время выполнения кода процесса, т.е. до тех пор пока не произойдет перепланировка. Если процесс занимает короткое время данные по UART успевают приниматься, иначе несколько байт теряются. Для поиска причины сделал тестовый проект, в нем 3 процесса, в каждом процессе через UART вывожу состояния нескольких регистров. Для примера код одного процесса. 

template<> void TProc1::exec()
{    
    unsigned char temp;
    unsigned int val;
    
    for(;;)
    {
        sleep(2500);   
        for (unsigned char i = 0; i < 10; i++)
        {
            temp = TCF0.INTFLAGS;
            ComPutchar(temp);
            val = SP;
            ComPutchar(val >> 8);
            ComPutchar(val & 0xFF);
            temp = PMIC.INTPRI;
            ComPutchar(temp);        
            temp = PMIC.STATUS;
            ComPutchar(temp);
            temp = SREG;
            ComPutchar(temp);
        }
    }
}

Полученные данные

Proc1
INTFLAGS  SP  INTPRI STATUS SREG
  f0     21b1   00     01    82 
  f0     21b1   00     01    80 
  f0     21b1   00     01    80 
  f1     21b1   00     01    80 
  f1     21b1   00     01    80
  f1     21b1   00     01    80 
  f1     21b1   00     01    80 
  f1     21b1   00     01    80 
  f1     21b1   00     01    80 
  f1     21b1   00     01    80 

Proc3
INTFLAGS  SP  INTPRI STATUS SREG
  f0     2337   00    01     82 
  f0     2337   00    01     80 
  f0     2337   00    01     80 
  f1     2337   00    01     80 
  f1     2337   00    01     80
  f1     2337   00    01     80 
  f1     2337   00    01     80 
  f1     2337   00    01     80 
  f1     2337   00    01     80 
  f1     2337   00    01     80 

Proc2
INTFLAGS  SP  INTPRI STATUS SREG
  f0     2274   00    01     82 
  f0     2274   00    01     80 
  f0     2274   00    01     80 
  f1     2274   00    01     80 
  f1     2274   00    01     80
  f1     2274   00    01     80 
  f1     2274   00    01     80 
  f1     2274   00    01     80 
  f1     2274   00    01     80 
  f1     2274   00    01     80 

Из лога видно, что прерывания всегда разрешены SREG = 0x80, I = 1. Таймер TCF0 после переполнения выставляет флаг (0x01), INTFLAGS = 0xf1. Непонятно почему в регистре PMIC.STATUS всегда 1, насколько я понял статус показывает какого уровня прерывание на данный момент выполняется и этот флаг должен быть сброшен после выполнения. В проекте всего два разрешенных прерывания, системный таймер OS - TCD0 (1 ms) и от таймера TCF0 (2 ms) который просто переключает состояние пина.

Share this post


Link to post
Share on other sites
45 минут назад, abi сказал:

когда какой-либо процесс активный, прерывания процессор не выполняет.

Вот это не очень понятно. Ведь всегда есть какой-то активный процесс. Если это не рабочие процессы, то процесс Idle. С точки зрения оси нет разницы.

А вообще эти прерывания когда-нибудь выполняются? Может, просто вектор неверно задан? Или нужен ещё какой-то флажок разрешения этого прерывания?

Share this post


Link to post
Share on other sites
8 minutes ago, AHTOXA said:

Вот это не очень понятно.

Для примера запущен таймер TCF0, который каждые 2 ms генерирует прерывания, в обработчике переключаю состояние пина, осциллографом видно что каждые 2 ms пин меняет состояние, т.е. работает правильно. Но когда работают процессы, прерывания от таймера не происходят, например

#define delay_ms(ms) ( __delay_cycles((F_CPU / 1000UL) * (ms)) )

template<> void TProc1::exec()
{    
    unsigned char temp;
    unsigned int val;
    
    for(;;)
    {
        sleep(500);
        delay_ms(50);
    }
}

В течении 500 ms (когда процесс в sleep(500)) прерывания есть, как только процесс становится активным попадает в delay_ms(50) прерываний нет, и по осциллографу видно, что в течении 50 ms, пин не меняет состояние. Далее снова в течении 500 ms прерывания есть и т.д. В посту выше приводил пример с выводом данных через UART, пока в цикле выводятся данные прерываний нет, как только в процессе вызывается любой сервис OS в котором происходит перепланировка прерывания начинают работать.

Share this post


Link to post
Share on other sites

То есть, если в проекте оставить один процесс TProc1, то тестовый пин будет дёргаться 500 мс, потом 50 мс не дёргаться, потом снова 500 дёргаться и так далее?

Share this post


Link to post
Share on other sites

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

Share this post


Link to post
Share on other sites

Очень интересно. Не могу представить, как такое может быть.

Попробуйте задать scmRTOS_IDLE_HOOK_ENABLE 1

и определить функцию idle_process_user_hook(), в которой тоже выдавайте на печать всю эту информацию (STATUS, SREG и проч).

Share this post


Link to post
Share on other sites
13 hours ago, AHTOXA said:

Попробуйте задать scmRTOS_IDLE_HOOK_ENABLE 1

Добавил в idle_process_user_hook() вывод состояния регистров. При активном процессе idle прерывания работают, это видно осциллографом и по состоянию регистров. Флаг регистра TCF0.INTFLAGS сброшен, состояние PMIC.STATUS всегда = 0.

INTFLAGS  SP   INTPRI STATUS SREG
  f0     23a7   00     00     82 
  f0     23a7   00     00     80 
  f0 	 23a7   00     00     80 
  f0     23a7   00     00     80
  f0     23a7   00     00     80 

Не могу понять, почему в рабочих процессах всегда PMIC.STATUS = 1, а в процессе idle PMIC.STATUS = 0 и прерывания работают.

Share this post


Link to post
Share on other sites

Сдаётся мне, что вы неверно заполняете начальную структуру контекста проекта.

Смотрите реализацию функции init_stack_frame(). Возможно, что для xmega в контекст нужно включить PMIC?

Share this post


Link to post
Share on other sites
16 minutes ago, AHTOXA said:

Возможно, что для xmega в контекст нужно включить PMIC?

У PMIC 3 регистра. PMIC.STATUS только для чтения. PMIC.INTPRI сохраняет вектор последнего подтвержденного низкоуровневого прерывания, если разрешен режим Round-Robin, но он отключен. PMIC.CTRL для настройки уровня разрешенных прерываний, т.к. у всех прерываний установлен Low Level, регистр настроен на LOLVLEN и всегда равен 1. Выводил состояние этого регистра в процессах, он не меняется и всегда равен 1. Поэтому думаю сохранять регистры PMIC в контекст смысла нет. А вот интересно при создании объекта IdleProc используется функция init_stack_frame () так же как и при создании рабочих процессов, т.е. все одинаково, а результат разный. Позже попробую добавить в IdleProc функцию sleep и скорее всего и IdleProc начнет блокировать прерывания.

Share this post


Link to post
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

Sign in to follow this