Помощь - Поиск - Пользователи - Календарь
Полная версия этой страницы: вопрос по работе с тремя прерываниями одинакового приоритета,
Форум разработчиков электроники ELECTRONIX.ru > Сайт и форум > В помощь начинающему > ARM, 32bit
Bulat
по прерыванию от порта PIO считываются последовательные данные, то есть при каждом прерывании считывается один бит:
Код
__ramfunc void irq_Receive()
{
  PauseTimerBase->TC_CCR = AT91C_TC_CLKEN;  
  PauseTimerBase->TC_CCR = AT91C_TC_SWTRG;                                    

  source = regs->PIOA_PDSR&0x600;
    
  if(source==0x200) {recA = recA>>1;  i_shA++; recA = recA|0x80000000;} //A1
  if(source==0x400) {recA = recA>>1;  i_shA++; recA = recA&0x7FFFFFFF;}  //B1
  
   dummy = AT91C_BASE_PIOA->PIO_ISR;    
}

при этом перезапускается таймер, который остчитывает паузу между принимаемыми словами. как только пришла пауза, таймер срабатывает и по прерыванию:
Код
__ramfunc void pause_timer_irq()
{
  if(i_shA==32)
  {
    recDA[kA] = recA; kA++;    
  }  
  if(kA==12)  send_to_host();  
  i_shA=0;
  dummy = PauseTimerBase->TC_SR;  
}

записывает данные в некий буфер.
но помимо этого одновременно может идти передача данных по прерываниюот другого таймера, работающего в режиме генератора:
Код
__ramfunc void timer0_irq_handler()
{  
  ii--;                                                                         //счетчик переданных бит
  if(ii==1) {MipsTimerBase->TC_CCR = AT91C_TC_CLKDIS;}
  MipsTimerBase->TC_RA = 30; MipsTimerBase->TC_RB = MipsTimerBase->TC_RC = 60;
  if(datA&0x80000000) regA = 0x20000;
  else regA = 0x0;
  regs->PIOA_ODSR = regA|front;      
  datA = datA<<1;
  dummy = MipsTimerBase->TC_SR;
  
}

Передача и прием идут с частотой 100 кГц. Приоритеты всех прерываний 7, то есть по идее они должны выполняться по очереди. Но при этом обработчик таймера паузы должен успеть все сделать до прихода следующего слова.
Передача и прием по отдельности работают нормально, без сбоев. Но при их одновременной работе прием не нарушается, а передача происходит с ошибками и в дальнейшем вовсе останавливается. Поэтому я и решил оптимизировать обработчики прерываний.
Для начала я решил все обработчики поместить в RAM, но тут столкнулся с интересной особенностью, если обработчик приемника void irq_Receive() не помещать в ОЗУ, то прием идет очень медленно, не успевает. Но, если обработчик передатчика void timer0_irq_handler() поместить в ОЗУ, то при одновременной работе приемника и передатчика (замыкание выхода передатчика на вход приемника) данные вообще не передаются, а если не помещать в ОЗУ то немного данных передаются. Как правильно размещать обработчики прерываний в ОЗУ и значительно ли это ускоряет работу?
GetSmart
А где код, который вызывает все эти обработчики? У них общий хэндлер?
Bulat
Цитата(GetSmart @ Nov 23 2009, 20:23) *
А где код, который вызывает все эти обработчики? У них общий хэндлер?

Структура программы следующая:
Код
//Обработчик прерывания таймера 1 - контроллер паузы между принимаемыми словами
__ramfunc void pause_timer_irq()
{
  - запись принятого 32-битного слова в буфер;
}

//Прием последовательности бит по прерыванию от порта PIO
__ramfunc void irq_Receive()
{
   - запуск таймера 1, контролирующего паузу;
   - прием последовательности бит по прерыванию от порта PIO
}

//Передача последовательности бит по прерыванию от таймера 0
__ramfunc void timer0_irq_handler()
{  
  ii--;  //счетчик переданных бит
  ...
}

//Команда на передачу данных
void Write_TRA()
{
   - подготовка данных для передачи по послед каналу;
   - запуск таймера-генератора 0, с пом. которого передаются данные;
   while(ii!=0); //ожидание обнуления счетчика переданных бит (32)
}

void main()
{
   - конфигурирование обработчиков прерывания;
   while(1)  { чтение принятых команд от хоста}
}

Прием и передача идут одновременно с частотой 100 кГц. То есть за 5 мкс необходимо подготовить данные для передачи и выставить их в порт, а за следующие 5 мкс считать данные, которые к тому времени находятся в порту. А во время паузы (40 мкс) надо успеть записать принятое слово в буфер. Для этого я все обработчики помещаю в ОЗУ с помощью __ramfunc. Но при этом какие то сбои происходят при приеме, но когда я убираю из ОЗУ обработчик передатчика timer0_irq_handler, то данные начинают идти, хотя не все... Почему так происходит и как правильно размещать в ОЗУ обработчики прерываний? Все 3 прерывания имеют одинаковый приоритет 7.
GetSmart
Ну понятно. Значит ни входа/выхода в прерывание не организовано (__arm __irq), ни сброса VIC/AIC на выходе из прерывания. Удивительно, что что-то вообще работает.
Bulat
Цитата(GetSmart @ Nov 24 2009, 12:15) *
Ну понятно. Значит ни входа/выхода в прерывание не организовано (__arm __irq), ни сброса VIC/AIC на выходе из прерывания. Удивительно, что что-то вообще работает.

так выход же у меня происходит путем считывания статусного регистра dummy = AT91C_BASE_PIOA->PIO_ISR или еще нужно в конце обработчика делать запись AT91C_BASE_AIC->AIC_EOICR = 1 ?
по поводу (__arm __irq), я так делал, но у меня возникала ошибка: Error[Pe167]: argument of type "void (__arm __irq __atpcs *)()" is incompatible with parameter of type "void и компилятор ссылался на функцию конфигурации прерывания: AT91F_AIC_ConfigureIt ( AT91C_BASE_AIC, MipsTimer.Id, TIMER_INTERRUPT_LEVEL,AT91C_AIC_SRCTYPE_EXT_NEGATIVE_EDGE, timer0_irq_handler);
GetSmart
Цитата(Bulat @ Nov 24 2009, 14:10) *
или еще нужно в конце обработчика делать запись AT91C_BASE_AIC->AIC_EOICR = 1 ?

Именно это должно быть в конце каждого обработчика.
Цитата(Bulat @ Nov 24 2009, 14:10) *
по поводу (__arm __irq), я так делал, но у меня возникала ошибка: Error[Pe167]:

Не знаю что там за проблема, но без __arm __irq (когда нет одного общего на всех хэндлера) нормально прога работать не будет.
aaarrr
Тип преобразуйте:
Код
AT91F_AIC_ConfigureIt ( AT91C_BASE_AIC, MipsTimer.Id, TIMER_INTERRUPT_LEVEL,AT91C_AIC_SRCTYPE_INT_HIGH_LEVEL, (void*)timer0_irq_handler);


Вообще замечательный подход - вместо того, чтобы разобраться в проблеме, боремся с сообщениями об ошибках sad.gif

AT91C_AIC_SRCTYPE_EXT_NEGATIVE_EDGE - почему EXT, на кой хрен, простите, EDGE????
Bulat
Цитата(GetSmart @ Nov 24 2009, 14:39) *
Именно это должно быть в конце каждого обработчика.

Не знаю что там за проблема, но без __arm __irq (когда нет одного общего на всех хэндлера) нормально прога работать не будет.
дело в том, что все это уже прописано в моем стартапе:
-вход в прерывание B IRQ_Handler_Entry ; 0x18 IRQ
- и конец прерывания str r14, [r14, #AIC_EOICR]
Прикрепляю Cstartup.

Цитата(aaarrr @ Nov 24 2009, 14:43) *
Тип преобразуйте:
Код
AT91F_AIC_ConfigureIt ( AT91C_BASE_AIC, MipsTimer.Id, TIMER_INTERRUPT_LEVEL,AT91C_AIC_SRCTYPE_INT_HIGH_LEVEL, (void*)timer0_irq_handler);


Вообще замечательный подход - вместо того, чтобы разобраться в проблеме, боремся с сообщениями об ошибках sad.gif

AT91C_AIC_SRCTYPE_EXT_NEGATIVE_EDGE - почему EXT, на кой хрен, простите, EDGE????

Да, извините, тут я упустил. Сейчас во всех 3-х прерываниях поставил AT91C_AIC_SRCTYPE_INT_HIGH_LEVEL.
Только проблема не решилась. А почему у этого контроллера прерывания от порта срабатывают 2 раза - по фронту и по спаду? Для чего тогда ставить AT91C_AIC_SRCTYPE_INT_HIGH_LEVEL или AT91C_AIC_SRCTYPE_INT_POSITIVE_EDGE? Может из-за этого контроллер не успевает обработать все прерывания друг за другом. У всех жи одинаковый приоритет.
aaarrr
Цитата(Bulat @ Nov 24 2009, 14:20) *
А почему у этого контроллера прерывания от порта срабатывают 2 раза - по фронту и по спаду?

Потому что так устроен контроллер PIO - генерирует прерывание по любому фронту.

Цитата(Bulat @ Nov 24 2009, 14:20) *
Для чего тогда ставить AT91C_AIC_SRCTYPE_INT_HIGH_LEVEL или AT91C_AIC_SRCTYPE_INT_POSITIVE_EDGE?

А для чего вообще работать с внутренними источниками прерываний по фронту?
Bulat
Цитата(aaarrr @ Nov 24 2009, 17:01) *
Потому что так устроен контроллер PIO - генерирует прерывание по любому фронту.


А для чего вообще работать с внутренними источниками прерываний по фронту?

Значит я правильно пользуюсь прерываниями с моим стартапом? Конфликтов между прерываниями никаких не может возникнуть в случае одновременного вызова, учитывая, что приоритеты однинаковы?
aaarrr
Цитата(Bulat @ Nov 24 2009, 15:18) *
Значит я правильно пользуюсь прерываниями с моим стартапом? Конфликтов между прерываниями никаких не может возникнуть в случае одновременного вызова, учитывая, что приоритеты однинаковы?

Правильно - понятие относительное smile.gif Да, пользуетесь правильно: для прерываний не нужны модификаторы __irq и запись EOICR, конфликтов не будет.
Но само по себе использование такого кащенитского стартапа является большой ошибкой.
Bulat
Цитата(aaarrr @ Nov 24 2009, 17:30) *
Правильно - понятие относительное smile.gif Да, пользуетесь правильно: для прерываний не нужны модификаторы __irq и запись EOICR, конфликтов не будет.
Но само по себе использование такого кащенитского стартапа является большой ошибкой.

то есть из-за этого стартапа могут проблемы возникнуть?
а могут проблемы быть из-за того, что прерывания от порта 2 раза возникают при приходе одного бита? это же дополнительное аремя на вызов и обработку прерывания.
aaarrr
Цитата(Bulat @ Nov 24 2009, 15:44) *
то есть из-за этого стартапа могут проблемы возникнуть?

Главная его проблема в том, что он тупо сохраняет контекст и переключает режим для всех подряд прерываний, в том числе и для прерываний с максимальным приоритетом.
В подавляющем большинстве случаев работать будет, просто подобное построение выдает совершенную бездумность подхода.

Цитата(Bulat @ Nov 24 2009, 15:44) *
а могут проблемы быть из-за того, что прерывания от порта 2 раза возникают при приходе одного бита? это же дополнительное аремя на вызов и обработку прерывания.

Вы с оптимизацией разобрались? Пока компилятор генерирует код вроде того, что вчера публиковался, о прерываниях с частотой 100кГц лучше забыть.
Bulat
Цитата(aaarrr @ Nov 24 2009, 18:02) *
Главная его проблема в том, что он тупо сохраняет контекст и переключает режим для всех подряд прерываний, в том числе и для прерываний с максимальным приоритетом.
В подавляющем большинстве случаев работать будет, просто подобное построение выдает совершенную бездумность подхода.


Вы с оптимизацией разобрались? Пока компилятор генерирует код вроде того, что вчера публиковался, о прерываниях с частотой 100кГц лучше забыть.

я пытался применить локальную оптимизацию для обработчиков прерываний таким образом:
Код
#pragma optimize=s 9
void timer0_irq_handler()
{  
  
  if(ii==1) {MipsTimerBase->TC_CCR = AT91C_TC_CLKDIS;}
  MipsTimerBase->TC_RA = 30; MipsTimerBase->TC_RB = MipsTimerBase->TC_RC = 60;
    
    crc = crc^regA;                                                               //подсчет контрольной суммы
    if(ii==2)                                                                         //вычисление бита паритета
    { if(crc == 0) regA = 0x0;
      else regA = 0x20000;
    }
    else
      {  
        if(datA&0x80000000) regA = 0x20000;
        else regA = 0x0;
      }
    regs->PIOA_ODSR = regA|front;      
    datA = datA<<1;
  ii--;                                                                                                  
  
  dummy = MipsTimerBase->TC_SR;
  
}

но напротив #pragma optimize=s 9 вылазит ворнинг следующего содержания: Warning[Go003]: Optimization can only be lowered from the default, not raised
Но ведь в настройках я вообще отключил оптимизацию. Почему возникает такое предупреждение?
aaarrr
Цитата(Bulat @ Nov 24 2009, 16:11) *
Но ведь в настройках я вообще отключил оптимизацию.

И зачем?

Цитата(Bulat @ Nov 24 2009, 16:11) *
я пытался применить локальную оптимизацию для обработчиков прерываний таким образом:

Верните глобальную оптимизацию на место и перепешите обработчики.

Где локальные переменные?
Зачем с периферией работать через левые указатели (regs, MipsTimerBase)?
Это вот -
Код
MipsTimerBase->TC_RA = 30; MipsTimerBase->TC_RB = MipsTimerBase->TC_RC = 60;

- зачем повторять в каждом прерывании?
Bulat
Цитата(aaarrr @ Nov 24 2009, 18:27) *
И зачем?


Верните глобальную оптимизацию на место и перепешите обработчики.

Где локальные переменные?
Зачем с периферией работать через левые указатели (regs, MipsTimerBase)?
Это вот -
Код
MipsTimerBase->TC_RA = 30; MipsTimerBase->TC_RB = MipsTimerBase->TC_RC = 60;

- зачем повторять в каждом прерывании?

-Когда я глобальную оптимизацию включаю, у меня работать перестает) ну не конкретно эти обработчики, а что-то другое.
-Если использовать локальные переменные внутриобработчика, тогда всеравно их значения нужно передавать через функцию основной программе (ii-счетчик, datA-сдвиговый буфер).
-А как лучше обращаться к регистрам таймера из обработчика прерывания?
aaarrr
Цитата(Bulat @ Nov 24 2009, 17:00) *
-Когда я глобальную оптимизацию включаю, у меня работать перестает) ну не конкретно эти обработчики, а что-то другое.

Вот с исправления этой проблемы и надо начинать. Все остальное пока забудьте.

Цитата(Bulat @ Nov 24 2009, 17:00) *
-Если использовать локальные переменные внутриобработчика, тогда всеравно их значения нужно передавать через функцию основной программе (ii-счетчик, datA-сдвиговый буфер).

regA, dummy

Цитата(Bulat @ Nov 24 2009, 17:00) *
-А как лучше обращаться к регистрам таймера из обработчика прерывания?

А зачем это делать вообще? Во-первых, зачем нужно использовать все три регистра (RA-RC)? Во-вторых, откуда следует необходимость их перезаписи?
Bulat
Цитата(aaarrr @ Nov 24 2009, 20:02) *
Вот с исправления этой проблемы и надо начинать. Все остальное пока забудьте.


regA, dummy


А зачем это делать вообще? Во-первых, зачем нужно использовать все три регистра (RA-RC)? Во-вторых, откуда следует необходимость их перезаписи?


Во-первых, я настраиваю таймер в соответствии с документацией. Таймер работает в таком режиме, что при равенстве RA бит сигнал устанавливается в 1, а при равенстве RC сбрасывается, при этом RB=RC.

Во-вторых, мне необходимо генерировать 32-битную последовательность импульсов с частотой 100кГц и с паузой между 32-битными словами 40мкс, то есть 4Т. Генерацию импульсов и паузу я осуществляю с помощью одного таймера, благодаря изменению регистров RA-RC в процессе его работы.

Ниже в комментариях к коду я объясняю работу:
Код
//обработчик прерываний таймера, который осуществляет подготовку каждого бита передаваемого слова
void timer0_irq_handler()
{
unsigned int regA;
  int dummy;
  int  crc;
  
  if(ii==1) {MipsTimerBase->TC_CCR = AT91C_TC_CLKDIS;}      //останов таймера после передачи всего слова
  MipsTimerBase->TC_RA = 30; MipsTimerBase->TC_RB = MipsTimerBase->TC_RC = 60;//настройка таймера на частоту передачи 100кГц
    
    crc = crc^regs->PIOA_ODSR;                                                  //подсчет контрольной суммы
    if(ii==2)                                                                   //вычисление бита паритета
    { if(crc == 0) regA = 0x0;
      else regA = 0x20000;
    }
    else
      {  
        if(datA&0x80000000) regA = 0x20000;
        else regA = 0x0;
      }
  
  regs->PIOA_ODSR = regA|front;                            //Вывод в порт следующего бита
  datA = datA<<1;                                                  //Смещение буфера с передаваемым 32-битным словом
  ii--;                                                                         //счетчик переданных бит
  dummy = MipsTimerBase->TC_SR;
}

//Функция подготавливающая 32-битные слова для передачи и запускающая таймер
void Write_TRA()
{
  unsigned int regA;  
//подготовка слова datA для последовательной передачи
  
  //Вывод первого бита в порт, чтобы при возникновении прерывания он сразу был передан
  if(datA&0x80000000) regA = 0x20000;
  else regA = 0x0;
  regs->PIOA_ODSR = regA|front;
  datA = datA<<1; //сдвигаем слово

  ii=32;   //загружаем счетчик передаваемых слов
  //запускаем таймер  
  MipsTimerBase->TC_CCR = AT91C_TC_CLKEN;                                    
  MipsTimerBase->TC_CCR = AT91C_TC_SWTRG;  

  while(ii!=0);  //ожидаем, пока передадуться все 32 бита
  
  //устанавливаем значения регистров таким образом, чтобы он начал работать с паузы 40мкс (чтобы выдержать необходимую по протоколу паузу), а затем был короткий импульс 5мкс, по которому ужебудет передаваться первый бит нового слова.  
  MipsTimerBase->TC_RA = 178; MipsTimerBase->TC_RB = MipsTimerBase->TC_RC = 200;
    
}


поэтому я вынужден уже в обработчике прерывания устанавливать новые значения регистров, соответствующие 100кГц, чтобы оставшиеся 31 бит передавать с этой частотой.
aaarrr
Цитата(Bulat @ Nov 25 2009, 15:02) *
Таймер работает в таком режиме, что при равенстве RA бит сигнал устанавливается в 1, а при равенстве RC сбрасывается, при этом RB=RC.

И что получается - тактовый сигнал? Тогда зачем городить синхронный последовательный интерфейс программно, когда есть SPI и SSC?
Bulat
Цитата(aaarrr @ Nov 25 2009, 17:12) *
И что получается - тактовый сигнал? Тогда зачем городить синхронный последовательный интерфейс программно, когда есть SPI и SSC?

потому что мне нужно передавать 32 битный код и еще параллельно получать от контроллера тактовую частоту на выходе, особенность протокола и аппаратной реализации.
При средней глобальной оптимизации мне удалось добиться стабильной, одновременной передачи и приема данных, но при передачи периодически проскакивают ошибочные слова, но посли оптимизации их стало значительно меньше. Когда я включаю максимальную оптимизацию по скорости, то передача не работает, только прием. Судя по всему оптимизатор что-то делает с MipsTimerBase->TC_RA = 30; MipsTimerBase->TC_RB = MipsTimerBase->TC_RC = 60; в обработчике, так как эта операция повторяется при каждом прерывании.
aaarrr
Цитата(Bulat @ Nov 25 2009, 15:45) *
потому что мне нужно передавать 32 битный код и еще параллельно получать от контроллера тактовую частоту на выходе, особенность протокола и аппаратной реализации.

Это можно сделать при помощи SSC.

Цитата(Bulat @ Nov 25 2009, 15:45) *
Судя по всему оптимизатор что-то делает с MipsTimerBase->TC_RA = 30; MipsTimerBase->TC_RB = MipsTimerBase->TC_RC = 60; в обработчике, так как эта операция повторяется при каждом прерывании.

Уж с ними он точно не может ничего сделать.
Для просмотра полной версии этой страницы, пожалуйста, пройдите по ссылке.
Invision Power Board © 2001-2025 Invision Power Services, Inc.