Помощь - Поиск - Пользователи - Календарь
Полная версия этой страницы: IAR AVR C++ class и прерывание
Форум разработчиков электроники ELECTRONIX.ru > Сайт и форум > В помощь начинающему > Программирование
west329_
Подскажите как правильно описать прерывание от uart0 для функции которая определяется в класе на уровень выше, так сказать пртоколе верхнего уровня cHuProt.

Сам клас
Код
#include "com0.h"

class cHuProt:public cUsart0 {
  protected:  
              void    HU_Rx_Byte(void);
  public:
              ui8     Recive_Pack_HU(void);
              ui8     Recive_Pack_HU_End(void);
              ui8     Init_HU(void);
              ui8     Reset_Recive_HU(void);
              ui8     Expect_new_HU(void);
              ui8     ECM_HU_len(void);
              ui8*    Get_Point_to_ECM_HU_data(void);
              ui8*    Get_Point_to_HU_S(void);
              ui8*    Get_Point_to_HU_C(void);
              ui8*    Get_Point_to_HU_I(void);
              ui8     DW_HU_Transmit(ui8 *pData);
              
  //cHumaxProt();
  //~cHumaxProt();
};


В клас cUsart0 я записал все низкоуровневые функции работы с железом uart
Код
class cUsart0 {
  protected:
              ui8 Usart0_Tx_Raw(ui8 tx_char);
              ui8 Usart0_RX_Raw(void);
  public:
              ui8 Usart0_Init(ui16 ubrr);
              ui8 Usart0_Tx_Data(ui8 *pData, ui8 len_data);
              ui8 Usart0_Tx_Char(ui8 tx_char);
              
  //Usart0();
  //~Usart0();
};

/////////////////////////////////////////////////////////////////////////////
// Реализация класса cUsart0
/////////////////////////////////////////////////////////////////////////////
....
....
....



Сама функция Usart0_Init использует прерывание при получении символа с uart.

Очень хочется вызывать функцию Usart0_RX_Raw(void); когда приходит символ, тоесть както так описать метод в протоколе
Код
#pragma vector = USART0_RXC_vect
__interrupt void cHuProt::HU_Rx_Byte(void) {
      temp=ui8 Usart0_RX_Raw();
      ......
      ......
}


но увы получаю ошибки
Цитата
Error[Ta022]: Interrupt functions cannot take any parameters. D:\Main_prj\tutor\MainTestCPP\hu.h 19
AHTOXA
Функции класса передаётся неявный параметр this. Объявите функцию static. Хотя это практически ничем не лучше, чем отдельный обработчик прерывания с вызовом в нём uart.HUMAX_Rx_Byte();

И вот тут почитайте.
dxp
Цитата(AHTOXA @ Oct 3 2009, 03:33) *
Функции класса передаётся неявный параметр this. Объявите функцию static. Хотя это практически ничем не лучше, чем отдельный обработчик прерывания с вызовом в нём uart.HUMAX_Rx_Byte();

Оно все же получше тем, что имеет доступ к представлению своего класса. Ну, и тем, что не загромождает глобальное пространство имен. smile.gif
AHTOXA
Что значит "к представлению"? К приватным членам? Если так, то вызываемая uart.HUMAX_Rx_Byte(), как функция класса, тоже имеет доступ к представлению классаsmile.gif

Так что, имхо, единственное преимущество -
Цитата(dxp @ Oct 3 2009, 07:54) *
что не загромождает глобальное пространство имен. smile.gif
west329_
Вобщем как я не пытался так и не получилось у меня повесит напрямую прерывание на метод класса.
Пошол по другому пути, создал обычную процедуру повесил на неё прерывание а из него уже вызываю метод класса, незнаю можна так делать или нельзя, код проверял работает нормально.
Код
#pragma vector = USART0_RXC_vect
__interrupt void Recive_Byte_from_tu(void) {
   ui8 Rx_Ch = Tu.HU_Rx_Byte();
....
....
....}
dxp
Цитата(AHTOXA @ Oct 3 2009, 14:10) *
Что значит "к представлению"? К приватным членам?

Да, к закрытой части класса.

Цитата(AHTOXA @ Oct 3 2009, 14:10) *
Если так, то вызываемая uart.HUMAX_Rx_Byte(), как функция класса, тоже имеет доступ к представлению классаsmile.gif

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

Цитата(west329_ @ Oct 3 2009, 18:22) *
Вобщем как я не пытался так и не получилось у меня повесит напрямую прерывание на метод класса.
Пошол по другому пути, создал обычную процедуру повесил на неё прерывание а из него уже вызываю метод класса, незнаю можна так делать или нельзя, код проверял работает нормально.
Код
#pragma vector = USART0_RXC_vect
__interrupt void Recive_Byte_from_tu(void) {
   ui8 Rx_Ch = Tu.HU_Rx_Byte();
....
....
....}

А если в классе объявить:

__interrupt static void Receive_Byte_from_tu();

то не работает, что-ли? Что не работает в этом случае? Не собирается проект? Или в железе не работает?
AHTOXA
Цитата(dxp @ Oct 3 2009, 19:30) *
А так и функцию дополнительную городить не надо - сразу в этой все и пишешь. В общем, как делать - вопрос вкусовых предпочтений, по большей части. Но если есть возможность не плодить лишние сущности, то это есть гуд.


Зато в этой функции придётся обращаться к членам класса с указанием экземпляра класса.
Типа,
Код
  uart.rxbuf[uart.rxpos++] = U0RXBUF;
  uart.rxpos &= uart.rxsize;


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

А вообще - да, согласен, дело вкусаsmile.gif
dxp
Цитата(AHTOXA @ Oct 3 2009, 21:42) *
Зато в этой функции придётся обращаться к членам класса с указанием экземпляра класса.
Типа,
Код
  uart.rxbuf[uart.rxpos++] = U0RXBUF;
  uart.rxpos &= uart.rxsize;

Это всяко. smile.gif Но по сути там и не может быть экземпляров - ведь работа же идет с конкретным аппаратным ресурсом, который в системе один, и регистры управления которым, как и вектор прерывания, жестко заданы. Поэтому и остальные ресурсы (типа буферов, индексов и т.п.) тоже существуют в единственном экземпляре и их тоже бы надо бы объявить как static.

Экземпляры нужны, например, если на один вектор прерывания забиндено несколько (хотя бы два) UART'ов. Тогда да - тут появляются отдельные объекты класса. Но и в этом случае статическая функция класса рулит - она одна на все объекты класса, и является корневой для процесса обработки прерывания - прерывание вызывает ее, а уж внутри нее и выясняется (например, по соответствующим битам в регистре статуса/управления, от какого именно UART'а возникло прерывание), какому объекту (экземпляру) передать управление. Хотя я не сталкивался на практике с платформами, где на одном прерывании сидело два UART'а. smile.gifТочнее, такое у нас есть, но UART'ы живут в ПЛИСке, и на этом прерывании много чего еще сидит. Т.е. это не готовая аппаратная платформа.
singlskv
Цитата(dxp @ Oct 5 2009, 11:47) *
Но и в этом случае статическая функция класса рулит - она одна на все объекты класса, и является корневой для процесса обработки прерывания - прерывание вызывает ее, а уж внутри нее и выясняется (например, по соответствующим битам в регистре статуса/управления, от какого именно UART'а возникло прерывание), какому объекту (экземпляру) передать управление. Хотя я не сталкивался на практике с платформами, где на одном прерывании сидело два UART'а. smile.gif

На ARM это как раз делаеться легко, просто один и тот же адрес обработчика подсовываете при инициализации
а дальше разбор в обработчике по номеру переферийного блока в котором возникло прерывание.
Alechin
а если я хочу созать шаблон класса с обработчиком внутри - компилятор не вставляет код обработчика. Мелькало где-то в обсуждениях, что для шаблонов именно так. Я обойти эту ситуацию не смог. Вот пример:
хочу создать шаблон класса таймеров. Аргумент шаблона - используемый таймер А или B (это MSP430, но принцип должен быть один)
Код
#define MODULES_IN_TIMER_A      2
#define MODULES_IN_TIMER_B      6
#define TIMER_A                'A'
#define TIMER_B                'B'
template <char Timer> class TTimers
{
  protected:
    static const BYTE nModules = (Timer == TIMER_A) ? (MODULES_IN_TIMER_A) : (MODULES_IN_TIMER_B);
    // Флаги состояния модулей захвата/сравнения.
    static volatile bool run[nModules];
    // Старший байт счетчика времени работы таймера.
    static volatile WORD hi_Word_of_Tick_Cntr;
  public:
    // Конструктор класса.
    inline TTimers()
      {
        // Обнулим счетчик времени работы таймера.
        hi_Word_of_Tick_Cntr = 0;
        // Выключим все модули захвата/сравнения.
        for(register IDX i = 0; i < nModules; i++) { Dis(i); Reset(i); }
        // Программируем таймер:
        // синхронизация от ACLK, счетчик 16 бит, режим непрерывного счета,
        // прерывание по переполнению запрещено, все таймеры независимы.
        (Timer == TIMER_A) ? (TACTL = TASSEL0 + MC1 + TACLR) : (TBCTL = TBSSEL0 + MC1 + TBCLR);
      }

    // Методы управления прерываниями от модулей захвата/сравнения.
    static inline void Ena(IDX i) { *(&((Timer == TIMER_A) ? (TACCTL1) : (TBCCTL1)) + i) = CCIE; }
    static inline void Dis(IDX i) { *(&((Timer == TIMER_A) ? (TACCTL1) : (TBCCTL1)) + i) = 0x00; }

    // Методы загрузки значения в регистр сравнения.
    static inline WORD Remained(IDX i)          { return *(&((Timer == TIMER_A) ? (TACCR1) : (TBCCR1)) + i) - ((Timer == TIMER_A) ? (TAR) : (TBR)); }
    static inline void Load(IDX i, WORD val)    { *(&((Timer == TIMER_A) ? (TACCR1) : (TBCCR1)) + i) = ((Timer == TIMER_A) ? (TAR) : (TBR)) + val;  }

    // Методы проверки/сброса/установки флага срабатывания таймера.
    static inline bool is_Setting(IDX i)       { return run[i] == true; }
    static inline void Reset(IDX i)            { run[i] = false;        }
    static inline void Set(IDX i)              { run[i] = true;         }

    // Обработчик прерывания.
    #pragma vector = (Timer == TIMER_A) ? (TIMERA1_VECTOR) : (TIMERB1_VECTOR)
    static __interrupt void Timer_IRQ(void)
      {
        IDX i = (Timer == TIMER_A) ? (TAIV) : (TBIV);
        if(i == ((Timer == TIMER_A) ? (0x0a) : (0x0e)))
          {
            // Прерывание по переполнению.
            // Инкрементируем старший байт счетчика работы таймера.
            hi_Word_of_Tick_Cntr++;
          }
        else
          {
            // Прерывание от модулей захвата/сравнения.
            i = (i >> 2) - 1;
            Dis(i);                           // Запрещаем прерывания от модуля захвата/сравнения.
            Set(i);                           // Выставляем признак завершения счета.
            __low_power_mode_off_on_exit();   // Выходим из режима пониженного потребления.
          }
      }
};


в итоге в коде нет обработчиков. Как в этой ситуации поступить?
и еще: если создать два экземпляра класса (ну конечно для А и B таймеров) - переменные run у каждого экземпляра свои, а hi_Word_of_Tick_Cntr одна на оба экземпляра! В чем тут причина я так-же не смог понять.
Сергей Борщ
Цитата(Alechin @ Dec 4 2009, 09:18) *
в итоге в коде нет обработчиков. Как в этой ситуации поступить?
Инстанцировать их явно.

Цитата(Alechin @ Dec 4 2009, 09:18) *
а hi_Word_of_Tick_Cntr одна на оба экземпляра! В чем тут причина я так-же не смог понять.
Простите, а зачем вы пишете static в объявлении переменной, если не понимаете, зачем он нужен?
Alechin
Цитата(Сергей Борщ @ Dec 4 2009, 11:14) *

вот и не понимаю как. Для начала - как задать компилятору вектор прерывания? В нутри класса есть аргумент, определяющий A или B таймер. Вне класса - как определить, с каким таймером работаем?
Вернее понимаю как на этапе создания класса, когда уже известно какой таймер будем использовать. Я же хочу сделать универсальный класс (ну вернее шаблон) - что бы подключил h файл и вообще не открывать его (для правки).

Цитата
Простите, а зачем вы пишете static в объявлении переменной, если не понимаете, зачем он нужен?

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

насчет одной переменной на оба класса: я все-таки недопонимаю тогда шаблоны: ведь компилятор создает ДВА никак не связанных между собой класса, и поэтому как может получиться одна переменная на оба класса.
XVR
У меня есть БОЛЬШИЕ сомнения по поводу допустимости такой записи
Код
#pragma vector = (Timer == TIMER_A) ? (TIMERA1_VECTOR) : (TIMERB1_VECTOR)

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

Еще замечание - использование огмоного количества констант, выбираемых по параметру шаблона, не есть гуд sad.gif
Лучше сделать так:
Код
struct TIMER_A {
enum Consts {
   nModules = 2,
   TxCTLImg = TASSEL0 + MC1 + TACLR,
   ...
};
BYTE& TxCTL() {return TACTL;}
...
};

// Для TIMER_B аналогично

template <class Timer> class TTimers : private Timer
{
  protected:
    static volatile bool run[nModules];
    // Старший байт счетчика времени работы таймера.
    static volatile WORD hi_Word_of_Tick_Cntr;
  public:
    // Конструктор класса.
    inline TTimers()
      {
        // Обнулим счетчик времени работы таймера.
        hi_Word_of_Tick_Cntr = 0;
        // Выключим все модули захвата/сравнения.
        for(register IDX i = 0; i < nModules; i++) { Dis(i); Reset(i); }
        // Программируем таймер:
        // синхронизация от ACLK, счетчик 16 бит, режим непрерывного счета,
        // прерывание по переполнению запрещено, все таймеры независимы.
        TxCTL() = TxCTLImg;
      }
...
};
Alechin
Цитата
Прерывания придется делать вне класса, в виде обычных функций (в количестве 2х штук), и вызывать уже внутри них метод Timer_IRQ из конкретного класса

Делал - говорю-ж - криво выходит. Во первых обработчики всегда будут существовать - т.е. при попытке задействовать другой обработчик линковщик ругается на два определения обработчика.
Во вторых - изначально собирался ввести еще один аргумент шаблона: "функцию" таймера - выставление флага (SIGNAL_TIMER) или вызов процедуры (PROC_TIMER). А это уже другие действия в обработчике. Т.е. уже четыре обработчика. А тут уже никак. Через один универсальный шаблон все очень компактно и красиво получилось. Кроме того, что обработчиков прерываний в коде нет sad.gif

Цитата
Прерывания придется делать вне класса, в виде обычных функций (в количестве 2х штук), и вызывать уже внутри них метод Timer_IRQ из конкретного класса

А, я несколько не так понял. Так, должно получиться. И наверное даже компилятор подставновку сделает (т.е. не будет лишнего вызова). Проблема тогда только в том, что бы как-нибудь "красиво" задать обработчик - т.е. как бы дистанцируясь от аппаратуры (не могу объяснить sad.gif в общем есть файл hardware.h, в котором определена вся аппаратура, и далее в программе никаких прямых упоминаний аппаратуры нет. Тут так не выйдет - надо будет где-то задать конкретный обработчик конкретного прерывания, что сразу свяжет программу с аппаратурой, чего хочется избежать.

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

А почему? Компилироваться долго будет?
XVR
Цитата(Alechin @ Dec 4 2009, 12:44) *
А, я несколько не так понял. Так, должно получиться. И наверное даже компилятор подставновку сделает (т.е. не будет лишнего вызова). Проблема тогда только в том, что бы как-нибудь "красиво" задать обработчик - т.е. как бы дистанцируясь от аппаратуры (не могу объяснить sad.gif в общем есть файл hardware.h, в котором определена вся аппаратура, и далее в программе никаких прямых упоминаний аппаратуры нет. Тут так не выйдет - надо будет где-то задать конкретный обработчик конкретного прерывания, что сразу свяжет программу с аппаратурой, чего хочется избежать.
Ну и задать эту связку где нибудь в том же hardware.cpp
Проблема в том, что обработчик привязывается с помощью #pragma, а это не языковая конструкция. Т.е. никакие template'ы к ней привязать нельзя.

Цитата
А почему? Компилироваться долго будет?
Некрасиво. Масса констант для обоих таймеров вместе с if'ами равномерно размазанных по всему телу шаблонного класса. cranky.gif Лучше эти константы собрать в одном месте (и отдельно для каждого таймера) с помощью классов TIMER_?, а в самом TTimer<> уже использовать имена, независящие от конкретного типа таймера
Alechin
Хорошо, допустми каким-либо образом мне удалось создать экземпляры класса таймеров.
Теперь хотелось бы создать класс индивидуального таймера.
Если создать наследника каласса таймеров - получим вызов конструктора базового класса (класса таймеров) при создании каждого экземпляра таймера. Что не нужно (и не правильно). Убрать вызов конструктора базового класса невозможно?
Хорошо, создаем самостоятельный класс. Тогда будем передавать в конструктор класса таймера ссылки/адреса на базовый класс. При этом подстановки компилятор уже не будет делать и получим овехед?
Как тут "выкрутиться"?
XVR
Классы таймеров состоят исключительно из констант (в виде enum'ов) и статических функций. У них нет конструкторов и деструкторов. Так что их можно безопасно создавать в любых количествах.

И вообще они реализуют так называемый 'compile-time полиморфизм'. Вместо таблиц виртуальных методов (для настройки на конкретный таймер) используются шаблоны, которые ориентируются на ИМЕНА констант и функций. Имея несколько типов классов TIMER с одноименными member'ами с разным содержимым, можно настроить TTimer на любой физический таймер
Alechin
Цитата
Классы таймеров состоят исключительно из констант (в виде enum'ов) и статических функций. У них нет конструкторов и деструкторов. Так что их можно безопасно создавать в любых количествах.

ну то, что нет деструктора это понятно. А вот конструктор должен быть - надо ведь проинициализировать и "запустить" таймер.
Иначе в начале программы придется все делать руками (типа методы Init() вызывать). Моя задача полность оградить программиста от работы с какой-либо аппаратурой. Т.е. он должен знать, что есть класс "таймер" с известным тиком, экземпляры корого он создает и пользует, есть класс "цифровой потенциометр" в котором он устанавливает значения. А уж какие-там ножки, прерывания задействованы, программный или аппаратный SPI там - это его не должно касаться. Все это прописано в моем хидере, который он просто подключает к проекту. Что-либо править в данном хидере ему нельзя.

Цитата
И вообще они реализуют так называемый 'compile-time полиморфизм'. Вместо таблиц виртуальных методов (для настройки на конкретный таймер) используются шаблоны, которые ориентируются на ИМЕНА констант и функций. Имея несколько типов классов TIMER с одноименными member'ами с разным содержимым, можно настроить TTimer на любой физический таймер

где на это посмотреть?
В принципе я сейчас создал класс таймера (программного), при создании которого конструктору передаю адрес класса аппаратных таймеров. Компилятор молодец - никакого оверхеда! Ну в принципе адреса классов аппаратных таймеров известны на этапе компиляции, так наверное он так и должен был поступить (просто подставить адреса, а не использовать указатель). Плохо то, что неизвестно, можно ли это гарантировать при других уровнях оптимизации например, или смене версии компилятора.
XVR
Цитата(Alechin @ Dec 4 2009, 15:02) *
ну то, что нет деструктора это понятно. А вот конструктор должен быть - надо ведь проинициализировать и "запустить" таймер.
Это другое дело. Вопрос в том, как именно 'таймер' передается програмисту? Если передается тип (класс), многочисленные экземпляры которого может создавать програмист для доступа к одному физическому таймеру - то эти экземпляры должны быть 'пустыми'. Т.е. они служат лишь для настройки на конкретный таймер. В этом случае саму аппаратуру нужно инициализировать отдельно: либо руками, либо использовать singleton'ы, которые будут инициализировать конкретные аппаратные таймеры.
Второй вариант - когда програмисту отдаются конкретные ИНСТАНСЫ объекта 'таймер'. В таком случае инициализацию инстанса можно производить в конструкторе

Цитата
Моя задача полность оградить программиста от работы с какой-либо аппаратурой. Т.е. он должен знать, что есть класс "таймер" с известным тиком, экземпляры корого он создает и пользует,
Один экземпляр на таймер или много экземпляров на один и тот же таймер?

Цитата
где на это посмотреть?
Я же писал пример несколько сообщений назад

Цитата
Ну в принципе адреса классов аппаратных таймеров известны на этапе компиляции, так наверное он так и должен был поступить (просто подставить адреса, а не использовать указатель).
Угу, с шаблоном так и будет
Цитата
Плохо то, что неизвестно, можно ли это гарантировать при других уровнях оптимизации например, или смене версии компилятора.
Можно, это врожденное свойство шаблона и от уровня оптимизации это не зависит
Для просмотра полной версии этой страницы, пожалуйста, пройдите по ссылке.
Invision Power Board © 2001-2025 Invision Power Services, Inc.