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

Более сложное - если ваш файл компилится в режиме С++, то надо писать extern "C" void __start_firmware(); Если файл может компилиться как в С так и в С++ варианте, то
#include <yvals.h>
_EXTERN_C
void __start_firmware();
_END_EXTERN_C

 

Спасибо!!! Второй вариант заработал.

:a14:

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


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

Потихоньку разбираюсь с C++. Не пойму один момент.

Как известно, в ARM надо прописать адрес обработчика прерывания в VIC;

Некоторый класс:

typedef class _TIMER
{
   public:
        void Init();
        __arm void Isr();

}TIMER,*pTIMER;

void _TIMER::Init()
{
  -------------------
  OpenIsr(TIMER1,(DWORD*)Isr,HIGH_PR);                       // определяем обработчик прерывания
}

__arm void _TIMER::Isr()
{
----------------------
}

Компилятор ругается, что нельзя сконвертировать тип.

Пробовал и так конвертировать:

  OpenIsr(TIMER1,static_cast<DWORD*>(Isr),HIGH_PR);     // определяем обработчик прерывания

 

Ошибка таже:

Error[Pa073]: invalid type conversion (from "__code __interwork void ()" to "DWORD*")

 

Это вообще возможно или я в трех соснах блуждаю?

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


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

Ошибка таже:

Error[Pa073]: invalid type conversion (from "__code __interwork void ()" to "DWORD*")

Странная ошибка. он должен был ругаться на конвертацию адреса функции-члена в указатель, а он ругается на нее как на обычную функцию. Дело в том, что при вызове функции-члена первым параметром неявно передается указатель на объект. Прерывание не может передать этот указатель. Поэтому, или обработчик должен быть статической функцией-членом (которой не нужен this) или надо написать обычную функцию как обертку:
__arm void TIMER1_Isr_wrapper()
{
   TIMER1.Isr();   // Тут мы явно указываем передать адрес TIMER1 как this
}


  OpenIsr(TIMER1,(DWORD*)TIMER1_Isr_wrapper,HIGH_PR);                       // определяем обработчик прерывания

при этом функцию-член-обработчик лучше сделать встраиваимой.

 

Чтобы было легче понять: Вы объявили 5 экземпляров этого класса (TIMER1, TIMER2, ... TIMER5). Попав в ваш _TIMER::Isr(), откуда он будет знать, какой конкретно из этих 5 таймеров надо обрабатывать? И при инициализации ваш _TIMER:Init() настраивает только первый таймер, а как настраивать остальные? Если вы пишете этот класс под один конкретный таймер, то можно все функции-члены и данные-члены сделать статическими, но тогда появится неудобство - придется явно заводить каждый статический член-данное.

 

Ой... Вы же передаете TIMER1 в OpenIsr. Видимо где-то дальше оно у вас используется для вызова обработчика. Тогда про статическое не читайте, почитайте это: указатель на функцию-член. И покажите объявление (а лучше и текст) OpenIsr()

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


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

Ой... Вы же передаете TIMER1 в OpenIsr. Видимо где-то дальше оно у вас используется для вызова обработчика. Тогда про статическое не читайте, почитайте это: указатель на функцию-член. И покажите объявление (а лучше и текст) OpenIsr()

 

Хорошая ссылка. Привожу код для OpenIsr():

void OpenIsr(DWORD IntNum,DWORD Handler, DWORD Priority)
{
    DWORD *vect_addr;
    DWORD *vect_cntl;
    VICIntEnClr = 1 << IntNum;
    if (IntNum < VIC_SIZE )
    {
        vect_addr = (DWORD *)(VIC_BASE_ADDR + VECT_ADDR_INDEX + IntNum*4);
        vect_cntl = (DWORD *)(VIC_BASE_ADDR + VECT_CNTL_INDEX + IntNum*4);
        *vect_addr =  Handler;    
        *vect_cntl = Priority;
        VICIntEnable = 1 << IntNum;    
    }
}

Немного конкретизирую идею

Думал сделать отдельный класс TARGET, в котором будут находиться функции , а потом все классы сделать наследниками его. Класс, который написал - это просто пример. Наверное удобнее номер таймера передавать в конструкторе, а потом от этого прописывать прерывания в OpenIsr:

typedef class _TIMER
{   
   public:
    _TIMER(DWORD TimerNum);
    void Init();
    void Isr();
}

Собственно на пути всего этого стоит то, что не могу передать функции OpenIsr адрес обработчика превывания (метод этого же класса), который она должна записать в регистр микроконтроллера (LPC2468). По ссылке, указанной вами, попытался создать в классе такой указатель: void (_TIMER::*pFunction) ();

Потом в конструкторе попытался сделать так:

_TIMER::_TIMER(void)
{
  DWORD *p;
  pFunction = &_TIMER::Isr;    
  p = static_cast<DWORD*>(pFunction); 
}

На третью строчку в фигурных скобках таже ошибка, не может сконвертировать тип.

 

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

Изменено пользователем Vitaliy_ARM

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


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

Ах вот оно как! Я чего-то подумал, что TIMER1 это у вас объект класса _TIMER, а это вы так VIC_TIMER1 переобозвали... Маленькое замечание - не рекомендуется использовать подчеркивание в качестве первого символа в именах, такие имена зарезервированы за компилятором, можете напороться на интересные ошибки. И имена из одних заглавных обычно используются для мароопределений (#define).

На третью строчку в фигурных скобках таже ошибка, не может сконвертировать тип.
Функция, вызываемая по этому указателю, требует this в качестве неявного параметра. По этому указателю определяется, с каким именно из объектов данного класса идет работа. Как ваш обработчик передаст в функцию этот this? Если бы компилятор разрешил вам это преобразование, то у вас для нескольких прерваний VICVectAddr указывал бы на одну и ту же функцию. И как бы эта функция разгребала, какой из таймеров надо обрабатывать? VIC на каждый источник прерывания хочет вызвать отдельную функцию. И никак вы это не обойдете. Или вам придется сделать на N таймеров N функций-"оберток", или в классе объявить N статических обработчиков, которые отличаются от "оберток" только областью видимости.

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

 

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

 

Что-то вроде вот такого:

class interrupt_t
{
    friend __irq void IRQ_Handler();
protected:
    interrupt_t(uint_fast8_t const vector_num, uint32_t const irq_ID);
private:
    virtual void Handler() = 0;
};

class timer_t : private interrupt_t
{
public:
    timer_t(uint_fast8_t const vector_num, uint32_t const irq_ID);
private:
    virtual void Handler();
    uint32_t Time;
};

inline interrupt_t::interrupt_t(uint_fast8_t const vector_num, uint32_t const irq_ID)
{
    uint32_t *Vector = &VICVectAddr0;
    Vector[vector_num] = (uint32_t)this;
    uint32_t *Control = &VICVectCtnl0;
    Control[vector_num] = (1 << 5) | irq_ID;
    VICVectEnable = (1 << irq_ID);
}

inline timer_t::timer_t(uint32_t const vector_num, uint32_t const irq_ID)
    : interrupt_t(vector_num, irq_ID)
{
    // настройка таймера
    .........    
}

timer_t::Handler()
{
    ++Time;
}

timer_t Timer1 (15, VIC_TIMER1);

#pragma vector = 0x18
__irq void IRQ_Handler()
{
    interrupt_t * pSource = (interrupt_t *)VICVectAddr;
    pSource->Handler();
    VICVectAddr = 0;
}

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

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


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

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

Сложно. Получается, что с оберткой по-проще. Уже делал что-то подобное (прикрепил один из вариантов оберток).

 

Еще пока не использовал наследование, пользовался подобными конструкциями:

while(1)
{
     if(Emac.CheckRx())
    {
      // определяем указатель на приемный пакет
      Emac.RxData(&Buf,&Len);
      // обрабатываем ответ
      StUdp.EthDataProcess(&Buf,&Len);
      // отправляем ответ
      Emac.TxData(Buf,Len);
     }
}

Хотя, если использовать наследование, то можно это все превратить в одну функцию. Занялся формированием концепции программирования.

 

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

isr.rar

Изменено пользователем Vitaliy_ARM

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


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

Вы сказали для неторопливых, оценивали ли вы размер кода, когда несколько объектов наследуют один и тот же базовый класс? Непонятен момент, как компилируются функции этого базового класса. Для каждого наследника они дублируются или компилятор это как-то оптимизирует?
Объект состоит только из данных. Когда вы обращаетесть к функции-члену, то компилятор по имени объекта определяет, какую именно из функций надо вызвать. Поэтому каждая (невстраиваимая) функция-член встречается в коде ровно один раз. Если это унаследованная функция для данного класса, то будет вызвана функция базового класса. Поэтому накладных расходов по коду при наследовании нет никаких. пример:
class a_t:
{
public:
    void func_1() { data_a ++; }
    void func_2() { data_a --; }
    int data_a;

};

class b_t
{
public:
    void func_2() { data_b += 2; }
    int data_b;
};

a_t a;
/*
    в памяти имеем:
    a = struct a_t
    {
        int data_a;
    };
*/

b_t b;
/*
    в памяти имеем:
    b = struct
    {
        struct a_t 
        {
            int data_a;
        };
        int data_b;
    };
*/

void test()
{
     a.func_1();// вызывается a_t::func_1(this = &a), происходит a.data_a++;
     a.func_2();// вызывается a_t::func_2(this = &a), происходит a.data_a--;
     b.func_1();// вызывается a_t::func_1(this = &b), происходит b.data_a++;
     b.func_2();// вызывается b_t::func_2(this = &b), происходит b.data_b+=2;
}

Единственные накладные расходы, которые могут возникнуть - по памяти данных на архитектурах с выравниванием. Размер struct a_t будет округлен в бОльую сторону до параметра выравнивания (чтобы если из них сделать массив, следующий элемент оказался выровненным), и это округление может оставить пустое место между data_a и data_b в b.

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


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

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

 

Единственные накладные расходы, которые могут возникнуть - по памяти данных на архитектурах с выравниванием. Размер struct a_t будет округлен в бОльую сторону до параметра выравнивания (чтобы если из них сделать массив, следующий элемент оказался выровненным), и это округление может оставить пустое место между data_a и data_b в b.

 

Отлично. Получается можно использовать преимущества ОПП и при этом размер кода практически не увеличивается.

Задам последний вопрос на отвлеченную тему:

Если делать класс например для всех COM портов на контроллере LPC2468. При создании в конструкторе прописывать номер порта. При этом есть проблема, у каждого порта свои регистры. Думал как вариант: создать поля указателей на эти регистры. В конструкторе поставить switch и присваивать здесь этим указателям адреса соответствующих регистров, а в остальных функциях обращаться непосредственно к указателям. Достоинсва: быстродействие, недостатки: лишняя память на поля указателей.

 

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

 

Можно ли обойтись (а нужно ли?) без операторов switch() при обращении к регистрам ввода/вывода, которые для каждого COM порта могут находиться в любом месте области памяти?

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


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

Вариант два: сделать в классе поле номера порта, так же инициализировать его в конструкторе. Потом в каждой функции ставить siwtch() и выбирать соответствующие регистры. Достоинсва: меньше памяти расходуется на поля, недостатки: меньше быстродействие.
Насколько я помню, там все порты примерно одинаоквы. Все их регистры можно описать одной структурой. Так почему бы указатель на эту структуру и не передавать конструктору?

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


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

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

 

:a14: Точно! Как я сразу не догадался. Спасибо за ответы

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


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

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

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

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

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

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

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

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

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

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