Jump to content

    
Sign in to follow this  
Pavel V.

GCC и C++ для Cortex-M3

Recommended Posts

Пытаюсь собрать следующий код:

 

#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;
	}
}
};

 

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 Кб :)

 

Если для класса 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:

Edited by IgorKossak
[codebox]

Share this post


Link to post
Share on other sites

Деструкторы надо выкинуть, они всё равно никогда не будут вызваны. Это инфраструктура поддержки деструкторов для глобальных объектов с виртуальными функциями такая. Эти деструкторы регестрируются с помощью функции atexit, которая тянет за собой кучу всего остального.

Share this post


Link to post
Share on other sites
Деструкторы надо выкинуть, они всё равно никогда не будут вызваны. Это инфраструктура поддержки деструкторов для глобальных объектов с виртуальными функциями такая. Эти деструкторы регестрируются с помощью функции atexit, которая тянет за собой кучу всего остального.

Спасибо! Действительно, удаление деструкторов помогло :) Как все оказалось просто.

Share this post


Link to post
Share on other sites

Это только до тех пор, пока вы не используете Get() ;)

Правильное решение таково: при использовании в проекте чисто виртуальных функций надо добавить к проекту следующий файл: тынц!

Share this post


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

Что я неправильно делаю? :smile3046:

Это Вы думаете, что не используется. :) А линкер думает иначе.

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

 

Share this post


Link to post
Share on other sites
Это только до тех пор, пока вы не используете Get() ;)

Правильное решение таково: при использовании в проекте чисто виртуальных функций надо добавить к проекту следующий файл: тынц!

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

 

Пробовал даже такой пример:

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

 

Это Вы думаете, что не используется. :) А линкер думает иначе.

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

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

Share this post


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

Ух ты, точно! Попробовал arm-kgp-eabi-gcc 4.7.0 20110328 (experimental) - работает без sys.c. Это радует:)

 

Share this post


Link to post
Share on other sites
Ух ты, точно! Попробовал arm-kgp-eabi-gcc 4.7.0 20110328 (experimental) - работает без sys.c. Это радует:)

Тулчейн от CodeSourcery (последняя версия) тоже корректно компилирует.

Share this post


Link to post
Share on other sites

Да, кстати, а зачем делать абстрактные интерфейсы типа 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, но я их не тестировал и в репозиторий не выкладывал.

 

Share this post


Link to post
Share on other sites
Еще мне люди присылали свои реализации портов для LPC17xx и STM8, но я их не тестировал и в репозиторий не выкладывал.
А можно их оба мне на real at real.kiev.ua? Я потестирую :-)

Share this post


Link to post
Share on other sites

neiver

Спасибо за ссылки, буду изучать :) По коду не сразу получается воткнуть, надо будет для себя диаграммки нарисовать для наглядности.

 

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

 

Будьте добры мне тоже на почту реализацию для LPC17хх, как раз сейчас с этим процессором работаю (павел AT posten.ru).

Edited by IgorKossak
Бездумное цитирование

Share this post


Link to post
Share on other sites

Чтобы не плодить новые темы пишу здесь.

 

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

 

Решил попробовать использовать полиморфизм в МК.

написал такой код:

// 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?

Share this post


Link to post
Share on other sites
Разве для использования абстрактных методов не нужен rtti?
Нет, не нужен. Полиморфизм реализуется таблицами указателей на виртуальные функции (vtbl) для каждого класса и указателем на таблицу своего класса в каждом объекте. Имея указатель на объект вы можете найти его таблицу указателей и в ней найти указатель на нужную реализацию метода. И все, никакого мошенничества.

 

Share this post


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

 

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

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

 

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

 

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

 

Share this post


Link to post
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

Sign in to follow this