Помощь - Поиск - Пользователи - Календарь
Полная версия этой страницы: GCC и C++ для Cortex-M3
Форум разработчиков электроники ELECTRONIX.ru > Микроконтроллеры (MCs) > Cредства разработки для МК > GNU/OpenSource средства разработки
Pavel V.
Пытаюсь собрать следующий код:

CODE
#include "LPC17xx.h"

typedef enum
{
ePort0,
ePort1,
ePort2,
ePort3,
ePort4
} LPCPort;

typedef enum
{
eHigh,
eLow
} LPCActive;

typedef enum
{
eInput,
eOutput
} LPCDirection;

template<LPCPort port, uint8_t pin, LPCActive activestate = eHigh> struct Pin;

template<LPCPort port, uint8_t pin, LPCActive activestate>
struct Pin
{
static const uint32_t mask = 1UL << pin;
LPC_GPIO_TypeDef* GetPointer()
{
return (port == ePort0 ? LPC_GPIO0 :
port == ePort1 ? LPC_GPIO1 :
port == ePort2 ? LPC_GPIO2 :
port == ePort3 ? LPC_GPIO3 :
LPC_GPIO4);
}
void On()
{
activestate == eHigh ?
GetPointer()->FIOSET = mask :
GetPointer()->FIOCLR = mask;
}
void Off()
{
activestate == eHigh ?
GetPointer()->FIOCLR = mask :
GetPointer()->FIOSET = mask;
}
void Mode(LPCDirection dir)
{
dir == eOutput ?
GetPointer()->FIODIR |= mask :
GetPointer()->FIODIR &= ~mask;
}
uint32_t IsActive()
{
uint32_t ret = GetPointer()->FIOPIN & mask;
if (activestate == eHigh)
{
return ret > 0 ? 1 : 0;
}
else
{
return ret > 0 ? 0 : 1;
}
}
};


CODE
class IOutput
{
public:
IOutput()
{
}
virtual ~IOutput()
{
}

virtual void Set(uint32_t state) = 0;
virtual uint32_t Get() = 0;
};

template<LPCPort port, uint8_t pin, LPCActive activestate>
class Output: public Pin<port, pin, activestate> , IOutput
{
public:

Output()
{
_pin.Off();
_pin.Mode(eOutput);
}

virtual ~Output()
{
}

virtual void Set(uint32_t state)
{
state ? _pin.On() : _pin.Off();
}

virtual uint32_t Get()
{
return _pin.IsActive();
}
private:
Pin<port, pin, activestate> _pin;
};


Если собирать вот такой main.cpp:

Код
Pin<ePort4, 29, eHigh> g_testPin;

int main(void)
{
    while (1) {
        g_testPin.On();
        g_testPin.Off();
    }
}


Все ОК, выходной файл 800 байт.

Но если попробовать вот такой main.cpp:

Код
Output<ePort4, 29, eHigh> g_testPin;

int main(void)
{
    while (1) {
        g_testPin.Set(1);
        g_testPin.Set(0);
    }
}


Линкер начинает ругаться:

Код
/opt/arm-kgp-eabi/lib/gcc/arm-kgp-eabi/4.7.0/../../../../arm-kgp-eabi/lib/thumb/cortex-m3/libc.a(lib_a-abort.o): In function `abort':
abort.c:(.text.abort+0xa): undefined reference to `_exit'
/opt/arm-kgp-eabi/lib/gcc/arm-kgp-eabi/4.7.0/../../../../arm-kgp-eabi/lib/thumb/cortex-m3/libc.a(lib_a-sbrkr.o): In function `_sbrk_r':
sbrkr.c:(.text._sbrk_r+0xc): undefined reference to `_sbrk'
/opt/arm-kgp-eabi/lib/gcc/arm-kgp-eabi/4.7.0/../../../../arm-kgp-eabi/lib/thumb/cortex-m3/libc.a(lib_a-signalr.o): In function `_kill_r':
signalr.c:(.text._kill_r+0xe): undefined reference to `_kill'
/opt/arm-kgp-eabi/lib/gcc/arm-kgp-eabi/4.7.0/../../../../arm-kgp-eabi/lib/thumb/cortex-m3/libc.a(lib_a-signalr.o): In function `_getpid_r':
signalr.c:(.text._getpid_r+0x0): undefined reference to `_getpid'
/opt/arm-kgp-eabi/lib/gcc/arm-kgp-eabi/4.7.0/../../../../arm-kgp-eabi/lib/thumb/cortex-m3/libc.a(lib_a-writer.o): In function `_write_r':
writer.c:(.text._write_r+0x10): undefined reference to `_write'
/opt/arm-kgp-eabi/lib/gcc/arm-kgp-eabi/4.7.0/../../../../arm-kgp-eabi/lib/thumb/cortex-m3/libc.a(lib_a-closer.o): In function `_close_r':
closer.c:(.text._close_r+0xc): undefined reference to `_close'
/opt/arm-kgp-eabi/lib/gcc/arm-kgp-eabi/4.7.0/../../../../arm-kgp-eabi/lib/thumb/cortex-m3/libc.a(lib_a-fstatr.o): In function `_fstat_r':
fstatr.c:(.text._fstat_r+0xe): undefined reference to `_fstat'
/opt/arm-kgp-eabi/lib/gcc/arm-kgp-eabi/4.7.0/../../../../arm-kgp-eabi/lib/thumb/cortex-m3/libc.a(lib_a-isattyr.o): In function `_isatty_r':
isattyr.c:(.text._isatty_r+0xc): undefined reference to `_isatty'
/opt/arm-kgp-eabi/lib/gcc/arm-kgp-eabi/4.7.0/../../../../arm-kgp-eabi/lib/thumb/cortex-m3/libc.a(lib_a-lseekr.o): In function `_lseek_r':
lseekr.c:(.text._lseek_r+0x10): undefined reference to `_lseek'
/opt/arm-kgp-eabi/lib/gcc/arm-kgp-eabi/4.7.0/../../../../arm-kgp-eabi/lib/thumb/cortex-m3/libc.a(lib_a-readr.o): In function `_read_r':
readr.c:(.text._read_r+0x10): undefined reference to `_read'
collect2: error: ld returned 1 exit status
make: *** [Test2.elf] Error 1


Пытался подсунуть заглушки системных функций, тогда собирается, но размер выходного файла больше 40 Кб sm.gif

Если для класса Output убрать наследование от абстрактного класса IOutput, линкер требует только функцию _sbrk, хотя тоже непонятно почему, ведь в программе нигде не используется динамическое выделение памяти..

Тулчейны пробовал разные - эффект сохраняется.

Ключи компиляции и сборки:

Код
Оптимизация -O2

# GCC
GCFLAGS = -O$(OPTIMIZATION) -gdwarf-2 -mcpu=cortex-m3 -mthumb -mthumb-interwork -mlong-calls -ffunction-sections -fdata-sections -Wall -Wextra --std=gnu99 -DTNKERNEL_PORT_CORTEXM3
GCFLAGS += $(patsubst %,-I%,$(INCDIRS)) -I.

# G++
GPFLAGS = -O$(OPTIMIZATION) -gdwarf-2 -mcpu=cortex-m3 -mthumb -mthumb-interwork -mlong-calls -ffunction-sections -fdata-sections -Wall -Wextra -fno-rtti -fno-exceptions -DTNKERNEL_PORT_CORTEXM3
GPFLAGS += $(patsubst %,-I%,$(INCDIRS)) -I.

# Assembler
ASFLAGS = $(LISTING) -mcpu=cortex-m3 -mthumb -x assembler-with-cpp
ASFLAGS += $(patsubst %,-I%,$(INCDIRS)) -I.

# Linker
LDFLAGS = -T$(LSCRIPT) -mcpu=cortex-m3 -mthumb -O$(OPTIMIZATION) -Wl,-Map=$(PROJECT).map,--cref -nostartfiles -fno-exceptions -fno-rtti -Wl,--gc-sections


Что я неправильно делаю? smile3046.gif
neiver
Деструкторы надо выкинуть, они всё равно никогда не будут вызваны. Это инфраструктура поддержки деструкторов для глобальных объектов с виртуальными функциями такая. Эти деструкторы регестрируются с помощью функции atexit, которая тянет за собой кучу всего остального.
Pavel V.
Цитата(neiver @ Feb 6 2012, 18:33) *
Деструкторы надо выкинуть, они всё равно никогда не будут вызваны. Это инфраструктура поддержки деструкторов для глобальных объектов с виртуальными функциями такая. Эти деструкторы регестрируются с помощью функции atexit, которая тянет за собой кучу всего остального.

Спасибо! Действительно, удаление деструкторов помогло sm.gif Как все оказалось просто.
AHTOXA
Это только до тех пор, пока вы не используете Get() wink.gif
Правильное решение таково: при использовании в проекте чисто виртуальных функций надо добавить к проекту следующий файл: тынц!
alx2
Цитата(Pavel V. @ Feb 6 2012, 17:20) *
Если для класса Output убрать наследование от абстрактного класса IOutput, линкер требует только функцию _sbrk, хотя тоже непонятно почему, ведь в программе нигде не используется динамическое выделение памяти..
Что я неправильно делаю? smile3046.gif

Это Вы думаете, что не используется. sm.gif А линкер думает иначе.
Почему линкер требует тот или иной вимвол, можно узнать из map-файла. Там линер подробно указывает, какой символ из какого модуля берется и кто его затребовал.
Pavel V.
Цитата(AHTOXA @ Feb 6 2012, 22:01) *
Это только до тех пор, пока вы не используете Get() wink.gif
Правильное решение таково: при использовании в проекте чисто виртуальных функций надо добавить к проекту следующий файл: тынц!

Убрал только деструкторы и проблема исчезла. В том числе при использовании чисто виртуальных функций. Может поправили уже?

Пробовал даже такой пример:
Код
int main(void)
{
    while (1) {
        IOutput *pa = (IOutput *)&g_testPin;
        uint32_t a = pa->Get();
        if(a)
            pa->Set(0);
        else
            pa->Set(1);
    }
}


Компилится без проблем:

Код
arm-kgp-eabi-size Test2.elf
   text       data        bss        dec        hex    filename
    956          4        264       1224        4c8    Test2.elf


Цитата(alx2 @ Feb 7 2012, 09:25) *
Это Вы думаете, что не используется. sm.gif А линкер думает иначе.
Почему линкер требует тот или иной вимвол, можно узнать из map-файла. Там линер подробно указывает, какой символ из какого модуля берется и кто его затребовал.

Я, к сожалению, пока не очень в нем ориентируюсь, но невооруженным взглядом видно, что использование деструкторов в абстрактных классах тянет за собой exceptions и другую требуху, несмотря на запрещающие флаги компилятора.
AHTOXA
Цитата(Pavel V. @ Feb 7 2012, 11:51) *
Убрал только деструкторы и проблема исчезла. В том числе при использовании чисто виртуальных функций. Может поправили уже?

Ух ты, точно! Попробовал arm-kgp-eabi-gcc 4.7.0 20110328 (experimental) - работает без sys.c. Это радуетsm.gif
Pavel V.
Цитата(AHTOXA @ Feb 7 2012, 12:10) *
Ух ты, точно! Попробовал arm-kgp-eabi-gcc 4.7.0 20110328 (experimental) - работает без sys.c. Это радуетsm.gif

Тулчейн от CodeSourcery (последняя версия) тоже корректно компилирует.
neiver
Да, кстати, а зачем делать абстрактные интерфейсы типа IOutput на таком низком уровне как линии ввода вывода?
Моя скромная практика показывает, что много эффективнее по размеру и скорости кода сделать класс Pin полнностью "статическим" и передавать в использующий его код в виде шаблонных параметров. А абстрактные интерфейсы с виртуальными функциями городить уже на уровне повыше.
Ну, а я по случаю порекламирую свою библиотеку по работе с портами. Помимо работы с отдельными линиями там эффективно реализованна работа с произвольными группами линий.
Вот так определелен класс линии ввода-вывода:
https://github.com/KonstantinChizhov/Mcucpp.../mcucpp/iopin.h
Порт ввода-вывода для STM32:
https://github.com/KonstantinChizhov/Mcucpp...M/Stm32/ports.h
Приме использования для STM32:
https://github.com/KonstantinChizhov/Mcucpp..._STM32/main.cpp
Сйечас у меня есть более-менее протестированные порты для AVR, MSP430 и STM32. Еще мне люди присылали свои реализации портов для LPC17xx и STM8, но я их не тестировал и в репозиторий не выкладывал.
ReAl
Цитата(neiver @ Feb 7 2012, 11:45) *
Еще мне люди присылали свои реализации портов для LPC17xx и STM8, но я их не тестировал и в репозиторий не выкладывал.
А можно их оба мне на real at real.kiev.ua? Я потестирую :-)
Pavel V.
neiver
Спасибо за ссылки, буду изучать sm.gif По коду не сразу получается воткнуть, надо будет для себя диаграммки нарисовать для наглядности.

У меня GPIO в проектах в основном для "медленных" операций используются, типа управления питанием внешних модулей, поэтому накладными расходами на вызов функций работы с портами особо не заморачивался. Зато очень удобно передавать куда угодно по ссылке.

Будьте добры мне тоже на почту реализацию для LPC17хх, как раз сейчас с этим процессором работаю (павел AT posten.ru).
Pavel V.
Кстати, заметил еще одну особенность - при уровне оптимизации -O0 проблема сохраняется, тянутся системные вызовы. Начиная с -O1 перестают.
Brain13
Чтобы не плодить новые темы пишу здесь.

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

Решил попробовать использовать полиморфизм в МК.
написал такой код:
CODE

// IOutDevice.h
class IOutDevice
{
public:
virtual void On(void) = 0;
virtual void Off(void) = 0;
};

// PC0LED.h
class PC0_LED : public IOutDevice
{
public:
PC0_LED();

virtual void On(void);
virtual void Off(void);
};

// PC0LED.cpp
PC0_LED::PC0_LED()
{
}

void PC0_LED::On()
{
GPIO_PinOutSet(gpioPortC, 0);
}

void PC0_LED::Off()
{
GPIO_PinOutClear(gpioPortC, 0);
}

// PC1LED.h
class PC1_LED : public IOutDevice
{
public:
PC1_LED();

virtual void On(void);
virtual void Off(void);
};

// PC1LED.cpp
PC1_LED::PC1_LED()
{
}

void PC1_LED::On()
{
GPIO_PinOutSet(gpioPortC, 1);
}

void PC1_LED::Off()
{
GPIO_PinOutClear(gpioPortC, 1);
}


// main.cpp
int main(void)
{
init();

PC0_LED pc0;
PC1_LED pc1;

IOutDevice*dev[2];
dev[0] = &pc0;
dev[1] = &pc1;


while (1)
{
dev[0]->On();
dev[1]->On();
delay(2000000);

dev[0]->Off();
dev[1]->Off();
delay(2000000);
}

}

// Интерфейс IOutDevice, и 2 класса его реализующих для PC0 и PC1.

Все время думал, что для абстрактных методов необходим rtti. И компилировал без опции -fno-rtti (размер программы 10456). В этом топике увидел, что работает и с опцией -fno-rtti, попробовал - раотает (размер программы 2228).

Вопрос: почему код успешно компилируется с опцией -fno-rtti без ошибок? Разве для использования абстрактных методов не нужен rtti?
Сергей Борщ
QUOTE (Brain13 @ Jan 16 2013, 08:50) *
Разве для использования абстрактных методов не нужен rtti?
Нет, не нужен. Полиморфизм реализуется таблицами указателей на виртуальные функции (vtbl) для каждого класса и указателем на таблицу своего класса в каждом объекте. Имея указатель на объект вы можете найти его таблицу указателей и в ней найти указатель на нужную реализацию метода. И все, никакого мошенничества.
dxp
QUOTE (Brain13 @ Jan 16 2013, 13:50) *
Все время думал, что для абстрактных методов необходим rtti. И компилировал без опции -fno-rtti (размер программы 10456). В этом топике увидел, что работает и с опцией -fno-rtti, попробовал - раотает (размер программы 2228).

Вопрос: почему код успешно компилируется с опцией -fno-rtti без ошибок? Разве для использования абстрактных методов не нужен rtti?

1. Что такое абстрактные методы?

2. RTTI - Run-Time Type Identification - определение типа на этапе выполнения. Причём тут полиморфизм, который требует просто наследования и виртуальных функций, механизм реализации которых - это таблицы указателей на функции? Для полиморфизма не требуется никакая информация о типах на этапе выполнения.

RTTI потребуется, если будете использовать dynamic_cast<>.
Brain13
Цитата(dxp @ Jan 16 2013, 11:37) *
1. Что такое абстрактные методы?

Абстрактный метод = виртуальный метод без реализации.

Цитата(dxp @ Jan 16 2013, 11:37) *
2. RTTI - Run-Time Type Identification - определение типа на этапе выполнения. Причём тут полиморфизм, который требует просто наследования и виртуальных функций, механизм реализации которых - это таблицы указателей на функции? Для полиморфизма не требуется никакая информация о типах на этапе выполнения.

RTTI потребуется, если будете использовать dynamic_cast<>.


Хммм... а я думал что, для вызова виртуальных методов необходимо вначале узнать реальный тип объекта, а лишь затем вызвать его переопределенный метод. Я считал, что определением реального типа объекта и занимается rtti. Походу я чего-то не понимаю.

Вот в вике написано о виртуальных методах:
Цитата(http://ru.wikipedia.org/wiki/%D0%92%D0%B8%D1%80%D1%82%D1%83%D0%B0%D0%BB%D1%8C%D0%BD%D1%8B%D0%B9_%D0%BC%D0%B5%D1%82%D0%BE%D0%B4)
То есть, в случае виртуальной функции, для определения реализации функции используется информация о типе объекта и вызывается «правильная» реализация, независимо от типа указателя. При вызове невиртуальной функции, компилятор руководствуется типом указателя или ссылки, поэтому вызываются две разные реализации function2(), несмотря на то, что используется один и тот же объект.

Так получается что инфа о типе не используется, а используется таблица указателей на виртуальные функции (vtbl)?

А что тогда делает rtti, и зачем нужен dynamic_cast<>? Можно примерчик?
dxp
QUOTE (Brain13 @ Jan 18 2013, 12:48) *
Абстрактный метод = виртуальный метод без реализации.

Это называется "чистая виртуальная" - pure virtual - функция. Никаких абстрактных методов в С++ нет, есть только абстрактные базовые классы. Кстати, "виртуальный метод" - масло масляное. В терминах С++ виртуальная функция - это и есть метод. Т.е. функции-члены могут быть обычными и виртуальными, последние и являются методами, а обычные функции-члены остаются обычными функциями-членами, никакими методами они не являются.


QUOTE (Brain13 @ Jan 18 2013, 12:48) *
Хммм... а я думал что, для вызова виртуальных методов необходимо вначале узнать реальный тип объекта, а лишь затем вызвать его переопределенный метод. Я считал, что определением реального типа объекта и занимается rtti. Походу я чего-то не понимаю.

Реальный тип объекта и узнаётся... на этапе компиляции. Компилятор смотрит тип объекта и проверяет, можно ли для этого типа вызвать виртуальную функцию. Если можно, то организует вызов, но не прямой, как в случае с обычной функцией, а через указатель vtbl, откуда извлекается указатель на вызываемую функцию. Всё организуется на этапе компиляции.

QUOTE (Brain13 @ Jan 18 2013, 12:48) *
Вот в вике написано о виртуальных методах:

Мутно написано. Правильный вариант - см выше.

QUOTE (Brain13 @ Jan 18 2013, 12:48) *
А что тогда делает rtti, и зачем нужен dynamic_cast<>? Можно примерчик?

RTTI нужно, когда действительно надо на рантайме решить, что делать с объектом. Например, есть некий приёмник сообщений, сообщения могут быть разного типа, и нужно каждый тип обрабатывать по-своему. Вот тут информация о типе и нужна реально. dynamic_cast<> позволяет проверить соответствие объекта ожидаемому типу. Накладные расходы, зато можно строить систему, которую не нужно каждый раз пересобирать при добавлении/изменении типов сообщений. Кстати, стандартный механизм RTTI универсален и довольно тяжёл, поэтому в embedded частенько имеет смысл реализовывать что-то своё (внедрение информации о типе в объекты).
Для просмотра полной версии этой страницы, пожалуйста, пройдите по ссылке.
Invision Power Board © 2001-2025 Invision Power Services, Inc.