Перейти к содержанию
    

Обработчик прерываний в HI-TECH

Здравствуйте.

 

Наконец-таки начал переползать с ассемблера на Си и возникла следующая проблем. Имеется код (приведены только отрывки на которых спотыкается компилятор):

 

//изменение скорости обмена
void SetBRate(unsigned brate)
{
....
}

//отправка данных
void SendByte(unsigned brate,unsigned char data)
{    
     SetBRate(brate);
....
}

//отправка данных на индикатор
static void _send_byte(unsigned char data)
{
....
    SendByte(br_19200,data);    
....
    SetBRate(br_1200);
}    

//вывод символа на индикатор
void WriteChar(unsigned char chr)
{
     //корректировка кода для русских букв
     if (chr >= 0xC0 && chr <= 0xEF)
         chr -= 0x40;
     else
         if (chr >= 0xF0)
            chr -= 0x10;
    
     _send_byte(chr);
}

//обработчик прерываний от Timer 0    
void INT_TMR0(void)
{    
     static int cnt = 0;
    
      if (++cnt >= 10)
      {
           WriteChar('1');
           cnt = 0;
       }
    
       T0IF = 0;    
}

//обработчик прерываний
void interrupt INTHandler(void)
{
      if (T0IF) INT_TMR0();    //прерывание от Timer 0
      if (RCIF) INT_RxD();    //прерывание от приемника EUSART
}

 

При комплияции выдаются следующие сообщения об ошибках:

 

Error   [472]; . non-reentrant function "__send_byte" appears in multiple call graphs: rooted at "_INTHandler" and "_main"

Error   [472]; . non-reentrant function "_SendByte" appears in multiple call graphs: rooted at "_INTHandler" and "_main"

Error   [472]; . non-reentrant function "_SetBRate" appears in multiple call graphs: rooted at "_INTHandler" and "_main"

 

В документации на HI-TECH написано, что:

 

(472) non-reentrant function "*" appears in multiple call graphs: rooted at "*" and "*" (Linker)

This function can be called from both main-line code and interrupt code. Use the reentrant keyword, if this compiler supports it, or recode to avoid using local variables or parameters, or duplicate the function, e.g.:

 

Однако из описания ошибки я так и не понял, как ее исправить. Подскажить, на какие грабли я наступил?

 

P.S. Используется MPLAB 8.00 + HI-TECH 9.60 LITE (который идет вместе с MPLAB 8.00). Контроллер PIC16F690.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

ты используешь одну функцию и в основном теле программы и в прерывании. Нельзя так. И вообще лучше не использовать функции в прерываниях. У меня использовалась, так при нагреве устройства до 54 град. прога глючить начинала. заменил самописным кодом - все нормально стало.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

...

(472) non-reentrant function "*" appears in multiple call graphs: rooted at "*" and "*" (Linker)

This function can be called from both main-line code and interrupt code. Use the reentrant keyword, if this compiler supports it, or recode to avoid using local variables or parameters, or duplicate the function, e.g.:

 

Однако из описания ошибки я так и не понял, как ее исправить. Подскажить, на какие грабли я наступил?

 

P.S. Используется MPLAB 8.00 + HI-TECH 9.60 LITE (который идет вместе с MPLAB 8.00). Контроллер PIC16F690.

 

Компилер вам говорит: "Это НЕ функция которая позволяет многократный вход в нее, а она вызываемая в нескольких мест "*" и "*". Эту функцию возможно вызывать из обеих "main" и "interrupt". Пользуйте ключевое слово "reentrant", если этот компилер его поддерживает, или перепишите ее, чтобы она не использовала локальные переменные или продублируйте ее."

 

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

В прерывании лучше внешние функции вообще не применять.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Гость DL36
Компилер вам говорит: "Это НЕ функция которая позволяет многократный вход в нее, а она вызываемая в нескольких мест "*" и "*". Эту функцию возможно вызывать из обеих "main" и "interrupt". Пользуйте ключевое слово "reentrant", если этот компилер его поддерживает, или перепишите ее, чтобы она не использовала локальные переменные или продублируйте ее."

Ключевое слово #pragma interrupt_level 0

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

В прерывании лучше внешние функции вообще не применять.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

or recode to avoid using local variables or parameters
Нет у PIC16 стека, на котором компилятор может сохранить временные переменные. И нет развитой косвенной адресации.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Ключевое слово #pragma interrupt_level 0

 

это слово указывает линкеру, что потребитель гарантирует и береть на себя ответственость, что эта функция не будет вызвана обработчиком прерывании и основной программы одновременно, что бы тот (линкер) не выдавал сообщении об ошибке. В некоторых компиляторах (поддерживающие рекурсию, например) есть возможность объявить функцию как "reentrant", т.е. она может быть вызвана еще раз не завершив свою работу.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Гость DL36
это слово указывает линкеру, что потребитель гарантирует и береть на себя ответственость, что эта функция не будет вызвана обработчиком прерывании и основной программы одновременно, что бы тот (линкер) не выдавал сообщении об ошибке. В некоторых компиляторах (поддерживающие рекурсию, например) есть возможность объявить функцию как "reentrant", т.е. она может быть вызвана еще раз не завершив свою работу.
Все правильно, также правильно не использовать функции в прерываниях.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

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

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Если надо сделать однотипное логически завершенное действие несколько раз,

 

для этого можно и макросы использовать.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

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

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

 

Касательно выделения логических блоков в функцию совершенно согласен.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

это слово указывает линкеру, что потребитель гарантирует и береть на себя ответственость, что эта функция не будет вызвана обработчиком прерывании и основной программы одновременно, что бы тот (линкер) не выдавал сообщении об ошибке. В некоторых компиляторах (поддерживающие рекурсию, например) есть возможность объявить функцию как "reentrant", т.е. она может быть вызвана еще раз не завершив свою работу.

 

К сожалению ключевое слово #pragma interrupt_level 0 не помогло. Если я объявляю ф-цию следующим образом:

 

#pragma interrupt_level 0
void SendByte(unsigned brate,unsigned char data)
{    
     SetBRate(brate);
     TXREG = data;
     while (!TRMT);
}

 

То получаю от компилятор сообщение:

 

Error   [473]; . function "_SendByte" is not called from specified interrupt_level

 

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

 

void main(void)
{        
    Init();
    
    while (1)
    {
         if (T0IF) INT_TMR0();
         if (RCIF) INT_RxD();
    }
}

 

Решение, наверное, не слишком красивое, но пока сойдет. Как вариант, думаю продублировать некоторые ф-ции, которые используются как в обработчике прерываний, так и в основной программе, т.е. сделать что-то на подобии:

 

void intr_SetBRate(unsigned brate)
{
....
}

void intr_SendByte(unsigned brate,unsigned char data)
{    
     intr_SetBRate(brate);
     TXREG = data;
     while (!TRMT);
}

 

Но сдерживат тот факт, что произойдет увеличит объем кода, а это не есть хорошо. Тоже каксается и использования макросов.

 

Большое спасибо всем за помощь.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Гость DL36
К сожалению ключевое слово #pragma interrupt_level 0 не помогло. Если я объявляю ф-цию следующим образом:

 

Надо добавить такую же строчку перед обработчиком прерывания. Хотя в Вашем случае видимо оптимальнее сделать две одинаковые функции с разными именами.

 

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

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Здравствуйте.

 

Наконец-таки начал переползать с ассемблера на Си и возникла следующая проблем. Имеется код

При комплияции выдаются следующие сообщения об ошибках:

 

Error   [472]; . non-reentrant function "__send_byte" appears in multiple call graphs: rooted at "_INTHandler" and "_main"

Error   [472]; . non-reentrant function "_SendByte" appears in multiple call graphs: rooted at "_INTHandler" and "_main"

Error   [472]; . non-reentrant function "_SetBRate" appears in multiple call graphs: rooted at "_INTHandler" and "_main"

Может я ошибаюсь, но по сообщениям об ошибках у меня сложилось впечатление, что вы пытаетесь вызвать функции SendByte и SetBRate из прерывания и из основной программы. Так же, судя по именам функций, они напрямую работают с UART'ом.

 

Если я прав, то это все будет работать до тех пор, пока основная программа и прерывание не попытаются ОДНОВРЕМЕННО отправить байт в UART. А они попытаются, т.к. передача байта (если он передается не на 10 MHz :) ) вещь довольно длинная, вероятность попасть в нее относительно велика.

 

 

 

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

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Надо добавить такую же строчку перед обработчиком прерывания. Хотя в Вашем случае видимо оптимальнее сделать две одинаковые функции с разными именами.

Спасибо, действительно помогло добавление #pragma interrupt_level 0 перед обработчиком прерываний. Хотя я думаю в обработчик прерываний вынести обработку только наиболее значимых прерываний, а все остальные обработать так, как я писал выше (т.е. в основном цикле программы). Думаю должно получиться неплохо.

 

Хотя вариант с использованием двух одинаковых функции с разными именами я пока еще не отбросил.

 

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

Знаю, что основа. Поэтому и задал вопрос, чтобы понять как пользоваться прерываниями в HI-TECH, т.к. в asm-е у меня вопросов с ними вроде не возникало.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Гость DL36
Спасибо, действительно помогло добавление #pragma interrupt_level 0 перед обработчиком прерываний. Хотя я думаю в обработчик прерываний вынести обработку только наиболее значимых прерываний, а все остальные обработать так, как я писал выше (т.е. в основном цикле программы). Думаю должно получиться неплохо.

 

Хотя вариант с использованием двух одинаковых функции с разными именами я пока еще не отбросил.

Знаю, что основа. Поэтому и задал вопрос, чтобы понять как пользоваться прерываниями в HI-TECH, т.к. в asm-е у меня вопросов с ними вроде не возникало.

Старайтесь в прерывании производить только самые необходимые действия. Например принять байт по уарт, положить его в буфер, поднять флаг что надо что то сделать и уйти из прерывания, дабы не потерять другие вызовы.

 

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

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Присоединяйтесь к обсуждению

Вы можете написать сейчас и зарегистрироваться позже. Если у вас есть аккаунт, авторизуйтесь, чтобы опубликовать от имени своего аккаунта.

Гость
К сожалению, ваш контент содержит запрещённые слова. Пожалуйста, отредактируйте контент, чтобы удалить выделенные ниже слова.
Ответить в этой теме...

×   Вставлено с форматированием.   Вставить как обычный текст

  Разрешено использовать не более 75 эмодзи.

×   Ваша ссылка была автоматически встроена.   Отображать как обычную ссылку

×   Ваш предыдущий контент был восстановлен.   Очистить редактор

×   Вы не можете вставлять изображения напрямую. Загружайте или вставляйте изображения по ссылке.

×
×
  • Создать...