реклама на сайте
подробности

 
 
> Как правильно создать многофайловый проект, Ошибка при линковке
alux
сообщение May 6 2007, 14:07
Сообщение #1


Знающий
****

Группа: Свой
Сообщений: 589
Регистрация: 24-04-05
Пользователь №: 4 447



Привет всем.
Создаю проект на IAR v.4.12A, состоящий из многих С-файлов. Проблема возникает при линковке в один файл. Если быть точнее, то при попытке вызова battery_charge(); из main возникает ошибка Error[e46]: Undefined external "FAST_charge" referred in bc ( IAR projects\pribor\Release\Obj\bc.r90 ). В main подключены хидеры всех используемых модулей, в том числе и bc.h :

///// main.c ////////
#include "bc.h"
...........
void main (void)
{
battery_charge();
}

//// bc.c /////
#include "bc.h"

void battery_charge(void)
{
FAST_charge();
}

///// bc.h ////////
extern void FAST_charge(void);
extern void TRICKLE_charge(void);
extern void battery_charge(void);

//// NiMh.c ////////
#include "NiMH.h"
void FAST_charge (void)
{
}


//// NiMh.h //////
void FAST_charge(void);

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

Сообщение отредактировал alux - May 6 2007, 14:40
Go to the top of the page
 
+Quote Post
3 страниц V   1 2 3 >  
Start new topic
Ответов (1 - 14)
jorikdima
сообщение May 6 2007, 14:20
Сообщение #2


тут может быть ваша реклама
*****

Группа: Свой
Сообщений: 1 164
Регистрация: 15-03-06
Из: Санкт-Петербург/CA
Пользователь №: 15 280



хидеры то подключены, а в опциях проекта пути к ним прописаны? (если они по поддиректориям распределены)
Go to the top of the page
 
+Quote Post
zltigo
сообщение May 6 2007, 14:24
Сообщение #3


Гуру
******

Группа: Свой
Сообщений: 13 372
Регистрация: 27-11-04
Из: Riga, Latvia
Пользователь №: 1 244



Цитата(alux @ May 6 2007, 17:07) *
void battery_charge(void)
{
FAST_charge();
}

Описка. Найдите разницу в одной букве:
Цитата
void FAST_carge (void)
{
}


И пожалуй не стоит писать в AVR->IAR, ибо сие ни к AVR ни к IAR отнрошения не имеет.

P.S.
И дублировать прототипы функций в headers это лишнее.


--------------------
Feci, quod potui, faciant meliora potentes
Go to the top of the page
 
+Quote Post
alux
сообщение May 6 2007, 14:46
Сообщение #4


Знающий
****

Группа: Свой
Сообщений: 589
Регистрация: 24-04-05
Пользователь №: 4 447



Цитата(jorikdima @ May 6 2007, 17:20) *
хидеры то подключены, а в опциях проекта пути к ним прописаны? (если они по поддиректориям распределены)

Все файлы (*.с, *.h) лежат в одной рабочей папке.


Цитата(zltigo @ May 6 2007, 17:24) *
И дублировать прототипы функций в headers это лишнее.

Ошибся при наборе сообщения, уже исправил...
Go to the top of the page
 
+Quote Post
rezident
сообщение May 6 2007, 18:25
Сообщение #5


Гуру
******

Группа: Свой
Сообщений: 10 920
Регистрация: 5-04-05
Пользователь №: 3 882



Цитата(alux @ May 6 2007, 20:46) *
Все файлы (*.с, *.h) лежат в одной рабочей папке.

Дык мало положить их в одну папку, нужно их еще и в проект включить. См. пример на скриншоте.
P.S. кстати, не обязательно все файлы валить в одну кучу. Я для хидеров каждого проекта использую каталог _INC, а для Си-ных исходников каталог _LIB. А в самих файлах пишу относительные пути (на скриншоте выделено красным). Таким образом я могу весь проект легко перенести на другой диск или в другой каталог без потери связей между файлами и перенастройки проекта.
Эскизы прикрепленных изображений
Прикрепленное изображение
 
Go to the top of the page
 
+Quote Post
SasaVitebsk
сообщение Dec 9 2007, 19:06
Сообщение #6


Гуру
******

Группа: Свой
Сообщений: 2 712
Регистрация: 28-11-05
Из: Беларусь, Витебск, Строителей 18-4-220
Пользователь №: 11 521



Простите, решил сюда добавить, чтобы темы не плодить.
Не так давно пишу на Си. За этот период сильно изменил свои подходы. Теперь проект разбиваю на файлы, которые логически закончены. Это даёт возможность многократного использования с минимальными изменениями. Но вот столкнулся с некоторыми проблемами в таком подходе. В принципе я о них уже читал в разных темах и общие указания слышал/запомнил.

Сама проблема возникает когда п/п из другого файла используется в прерывании, которое в main описывается. Как выходить из неё - вроде понятно. 1) Вообще убрать вызовы п/п. 2) перенести п/п в файл, где пишется прерывание.

Собственно меня интересует более общая тема. Как всётаки сделать так, чтобы и овцы целы и волки сыты. То есть вопросы следующие.

1) Правильным ли является подход с выделением логически законченного блока в отдельный файл со своим хедером. И если "нет" или "не совсем", то хотелось бы услышать как поступают проффессионалы.
2) Как использовать такие "почти библиотеки", чтобы уменьшить накладные рассходы и при этом сохранить красоту написания проги.
3) Является ли выигрышным в этом смысле вариант с С++ и созданием классов/объектов.
Go to the top of the page
 
+Quote Post
singlskv
сообщение Dec 9 2007, 20:22
Сообщение #7


дятел
*****

Группа: Свой
Сообщений: 1 681
Регистрация: 13-05-06
Из: Питер
Пользователь №: 17 065



Цитата(SasaVitebsk @ Dec 9 2007, 22:06) *
Сама проблема возникает когда п/п из другого файла используется в прерывании, которое в main описывается. Как выходить из неё - вроде понятно. 1) Вообще убрать вызовы п/п. 2) перенести п/п в файл, где пишется прерывание.
3) объявить п/п через extern в соответствующем хидере.
Цитата
1) Правильным ли является подход с выделением логически законченного блока в отдельный файл со своим хедером. И если "нет" или "не совсем"
Правильным, тока для разрешения всех противоречий нужно еще иметь "главный" хидер
в котором и объявляются основные настройки проекта и инклудятся все необходимые хидеры, ИМХО.
Go to the top of the page
 
+Quote Post
Сергей Борщ
сообщение Dec 9 2007, 21:56
Сообщение #8


Гуру
******

Группа: Модераторы
Сообщений: 8 455
Регистрация: 15-05-06
Из: Рига, Латвия
Пользователь №: 17 095



Цитата(SasaVitebsk @ Dec 9 2007, 21:06) *
п/п из другого файла используется в прерывании, которое в main описывается.
Это как? Обычно обработчик прерывания описываю не в main, а в том же файле, что и остальные п/п соответстующего модуля. А зачем обработчик уносить в тот же файл, где и main? Или это архитектера вроде pic, когда одно прервание на все случаи? Плюсы помогут, ибо в них можно в заголовочном файле описать обработчик как inline.


--------------------
На любой вопрос даю любой ответ
"Write code that is guaranteed to work, not code that doesn’t seem to break" (C++ FAQ)
Go to the top of the page
 
+Quote Post
Непомнящий Евген...
сообщение Dec 10 2007, 06:26
Сообщение #9


Знающий
****

Группа: Свой
Сообщений: 771
Регистрация: 16-07-07
Из: Волгодонск
Пользователь №: 29 153



Просветите плиз - что такое "п/п"?
Go to the top of the page
 
+Quote Post
Николай Z
сообщение Dec 10 2007, 07:21
Сообщение #10


Местный
***

Группа: Участник*
Сообщений: 418
Регистрация: 20-08-07
Пользователь №: 29 930



Цитата(Непомнящий Евгений @ Dec 10 2007, 09:26) *
Просветите плиз - что такое "п/п"?


Видимо подпрограмма.... Она же - функция...
Немного не С-шная терминология, но суть практически та же...
В Фортране к примеру - были функции, кторые возврвщали значение и void-функции, которые не возвращали значение или что почти то же самое обычно - возвращали пустое значение...

Еще в некоторых других языках использовалась такая терминология.

Сообщение отредактировал Николай Z - Dec 10 2007, 07:24
Go to the top of the page
 
+Quote Post
Непомнящий Евген...
сообщение Dec 10 2007, 07:34
Сообщение #11


Знающий
****

Группа: Свой
Сообщений: 771
Регистрация: 16-07-07
Из: Волгодонск
Пользователь №: 29 153



Что такое подпрограмма - я в курсе smile.gif Просто не сообразил, что п\п - это ее сокращение. Спасибо.

Цитата(Сергей Борщ @ Dec 10 2007, 00:56) *
Это как? Обычно обработчик прерывания описываю не в main, а в том же файле, что и остальные п/п соответстующего модуля. А зачем обработчик уносить в тот же файл, где и main?

Ну например, чтобы не зависеть от конкретного прерывания. Тогда в модуле (а точнее в хидере) я пишу inline-функцию, а само прерывание делаю в основной программе, дергая из его обработчика эту функцию. Вместо inline-функции в С можно использовать макрос.
Go to the top of the page
 
+Quote Post
alexander55
сообщение Dec 11 2007, 06:17
Сообщение #12


Бывалый
*****

Группа: Свой
Сообщений: 1 584
Регистрация: 7-08-07
Пользователь №: 29 615



Цитата(Сергей Борщ @ Dec 10 2007, 00:56) *
А зачем обработчик уносить в тот же файл, где и main?

Это дело вкуса.
Я вижу это так.
Если железно ясно, что
- используется прерывание
- и это универсальное решение,
то прерывание имеет смысл иметь в модуле, где осуществляется реализация.
Если :
- хочется иметь main как диспетчер, в котором видна вся логика функционирования
- main небольшой по размеру
- возможны изменения (без прерывания или с прерыванием),
то лучше обработчик прерывания в main.
В дальнейшем, когда все устаканится или размер main станет неприличным по размеру, можно его перенести. biggrin.gif
Go to the top of the page
 
+Quote Post
Сергей Борщ
сообщение Dec 11 2007, 09:51
Сообщение #13


Гуру
******

Группа: Модераторы
Сообщений: 8 455
Регистрация: 15-05-06
Из: Рига, Латвия
Пользователь №: 17 095



Цитата(alexander55 @ Dec 11 2007, 08:17) *
Если :
- хочется иметь main как диспетчер, в котором видна вся логика функционирования
Если это ARM, то в main достаточно заносить адреса обработчиков (объявленных как extern) в нужные регистры контроллера прерываний. Тела самих обработчиков в main() не нужны. Если это проц без контроллера, то у него скорее всего вектора жестко распределены за периферией, и выбор вектора просто однозначен - в main нечего диспетчерить. Если хочется иметь выбор из нескольких одинаковых периферийных модулей (UART, например), то можно попробовать так:
Код
в Hardware.h указываем
#define RS232_MODULE   USART1
#define RS485_MODULE   USART0

в RS232.c:
#pragma RS232_MODULE##_UDRE_vect
interrupt void RS232_transmit_handler()
{

} //опять main.c не у дел
Наконец, если используются плюсы и обработчик - метод класса, то код этого метода с атрибутом inline размещается в заголовочном файле, а в main.cpp пишется "обертка":
Код
__irq void RS232_Handler()
{
      RS232.Handler();
}
или
#pragma USART1_UDRE_vect
interrupt void RS232_transmit_handler()
{
      RS232.Handler();
}
при этом обработчик делается private, а функция-обертка объявляется другом класса. Но при желании эти обертки уносятся в файл соответствующего модуля и можно снова применить первый вариант.
Цитата(alexander55 @ Dec 11 2007, 08:17) *
В дальнейшем, когда все устаканится или размер main станет неприличным по размеру, можно его перенести.
Мне кажется, что всякие методики проектирования создают именно для того, чтобы по возможности исключить такую непроизводительную работу. Хотя, конечно, идеала не существует и процесс был и будет итеративным. Но количество итераций можно попытаться уменьшить.


--------------------
На любой вопрос даю любой ответ
"Write code that is guaranteed to work, not code that doesn’t seem to break" (C++ FAQ)
Go to the top of the page
 
+Quote Post
alexander55
сообщение Dec 11 2007, 10:29
Сообщение #14


Бывалый
*****

Группа: Свой
Сообщений: 1 584
Регистрация: 7-08-07
Пользователь №: 29 615



Цитата(Сергей Борщ @ Dec 11 2007, 12:51) *
Наконец, если используются плюсы и обработчик - метод класса, то код этого метода с атрибутом inline размещается в заголовочном файле, а в main.cpp пишется "обертка":
Код
__irq void RS232_Handler()
{
      RS232.Handler();
}
или
#pragma USART1_UDRE_vect
interrupt void RS232_transmit_handler()
{
      RS232.Handler();
}

Я это и имел ввиду.

Цитата(Сергей Борщ @ Dec 11 2007, 12:51) *
в main нечего диспетчерить.

Это не совсем диспетчеризация и даже может совсем не диспетчерезация.
Приятно сразу понять логику работы всей системы глядя на один модуль.
Детали находятся за рамками и если требуются, то понятно, где это можно посмотреть (глядя на модуль).
Второй смысл - это скажем так "просмотр синхронизации потоков и распределенных приложений".
Во выдал. biggrin.gif
Go to the top of the page
 
+Quote Post
SasaVitebsk
сообщение Dec 11 2007, 10:58
Сообщение #15


Гуру
******

Группа: Свой
Сообщений: 2 712
Регистрация: 28-11-05
Из: Беларусь, Витебск, Строителей 18-4-220
Пользователь №: 11 521



Я, собственно так и работаю. И думал так все. smile.gif В смысле "main" главная или основная.

В main.h описываю основные переменные проекта. Например частоту проца и прочее. Этот хидер включаю во все хидера составных частей проекта (В данном последнем это Kls3x4.h, timerhw.h, lcd44780.h, wake.h). В каждом из составляющих хидеров определяются внутренние константы и объявления данного модуля и прототипы функций.

В самой main.c описывается вся программа опирающаяся на составные части. Прерывание описывается в main.c

Прерывание - метки времени. То есть там целый набор. smile.gif в том числе вызовы опроса клавиатуры. Вот и получается - засунуть её в Kls3x4.с некрасиво (там логически законченная библиотека) а при применении в main - есть накладные.


Собственно само по себе, естественно, это проблему не вызывает. Данное прерывание обрабатывается раз в 10мс и, честно говоря мне до лампочки что там весь контекст сохраняется. Я просто на будущее. Чтобы оценить кто как работает с оформлением и структурой программы. Ведь не маловажная причина перехода на Си - это читаемость программы её структурированность и возможность неоднократного применения модулей. По крайней мере я так рассматриваю. А опыта пока не хватает. Это я вижу хотябы по тому, что у меня от проекта к проекту пока меняются некоторые моменты оформления.

Думаю конечно скоро всё устаканится.

Спасибо за советы. beer.gif
Go to the top of the page
 
+Quote Post

3 страниц V   1 2 3 >
Reply to this topicStart new topic
1 чел. читают эту тему (гостей: 1, скрытых пользователей: 0)
Пользователей: 0

 


RSS Текстовая версия Сейчас: 20th July 2025 - 18:04
Рейтинг@Mail.ru


Страница сгенерированна за 0.0148 секунд с 7
ELECTRONIX ©2004-2016