Помощь - Поиск - Пользователи - Календарь
Полная версия этой страницы: Новичок на С
Форум разработчиков электроники ELECTRONIX.ru > Микроконтроллеры (MCs) > AVR
Страницы: 1, 2
SasaVitebsk
Доброго времени суток. Помогите начинающему. Долгое время писал на асме для разных микропроцессоров. В том числе большие проекты. На IBM приходилось писать вспомогательные програмки на Delfi. "C" использовать не приходилось. В данный момент приходится осваивать для реализации крупного и длительного проекта. "С" так, как есть вариант впоследствии перейти на др. МП. Почитал, - синтаксис языка вполне понятен. Хоть сейчас в бой. Но нек. моменты не понятны.
Во-первых, не хочу обидеть чьи-нибудь чувства, но более дебильной и запутанной IDE среды чем IAR Wb я не встречал. Но к этому привыкну. Скачал PDFы и сделал книжечки. По возможности изучил. Сделал маленькую прогу, но всё равно остался ряд вопросов по организации проги.
1) Как определять какие библиотеки включать?
2) Если необходимы прерывания достаточно ли такого объявления или необходимо что-то ещё (например переходы):

#pragma vector=USART_RXC_vect
__interrupt void rxint(void)
{

}

3) Как понимать следующие строки? (В смысле на языке высокого уровня)

jmp_buf main_task;
jmp_buf iprx_task;
jmp_buf iptx_task;
char iprx_rstack[8];

и вот эти пжлст

#pragma vector=USART_RXC_vect
__interrupt void rxint(void)
{
if (!setjmp(main_task)) //Запомнили контекст осн. задачи
{
longjmp(iprx_task,1); //Перешли в контекст IPrecive
}
}


4) Что необходимо описывать чтобы грамотно сделать проект в котором несколько файлов ".С"
5) При использовании кольцевого буфера правильнее открыть буфер в виде массива или использовать указатели.
6) Профи, если можно вышлите какой-нибудь файл(ы) исходников (можно не рабочих, урезанных или с ошибками) по которым я мог бы посмотреть структуру программы. Лучше с прерываниями. Обязуюсь не распространять и использовать в качестве учебных. Пожалуйста не присылайте слишком больших, а то я зароюсь. smile.gif

Заранее благодарен. e-mail: sasa@c32dvina.com
Pat
Цитата(SasaVitebsk @ Feb 7 2006, 23:56) *
2) Если необходимы прерывания достаточно ли такого объявления или необходимо что-то ещё (например переходы):

#pragma vector=USART_RXC_vect
__interrupt void rxint(void)
{

}

Достаточно, остальное компилятор сделает сам.
Цитата(SasaVitebsk @ Feb 7 2006, 23:56) *
5) При использовании кольцевого буфера правильнее открыть буфер в виде массива или использовать указатели


Если открыть - имеется в виду объявить массив то его по любому необходимо объявлять как массив необходимой вам размерности. (Если конечно не использовать динамическое выделение памяти).
Вот доступ к элементам массива можно осуществлять индексами или через указатели.
Индексы более наглядные для понимания программы.
Указатели не так наглядны, но замечено что компилятор к указателям относится более лояльно и код получается, как правило на несколько (иногда на 10-20) меньше.
Поэтому где возможно лучше использовать указатели. Добавлю что это ИМХО.
defunct
Цитата(SasaVitebsk @ Feb 7 2006, 23:56) *
1) Как определять какие библиотеки включать?

Если Вы знакомы с Delphi, то библиотеки C это нечто схожее с rtl Delphi. Компилятор определит какие библиотеки включать, от Вас же требуется только определиться с набором интересующих функций (аналогичтно секции uses в Delphi) - подключить с помощью директивы include требуемые вам модули.
Цитата
2) Если необходимы прерывания достаточно ли такого объявления или необходимо что-то ещё (например переходы):

кроме объявления обработчика прерывания потребуется еще запуск того или иного периферийного устройства в режиме прерываний, а также глобальное разрешение прерываний.
Цитата
3) Как понимать следующие строки? (В смысле на языке высокого уровня)

в C вначале пишется тип, а потом имя переменной. т.е. первые четыре записи это явное объявление переменных.
Цитата
и вот эти пжлст

не стоит привязываться к конкретному алгоритму в самом начале освоения языка. К тому же коментарии сделанные Бог весть кем могут быть и неправильными.
Цитата
4) Что необходимо описывать чтобы грамотно сделать проект в котором несколько файлов ".С"

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

на любителя.
SasaVitebsk
Спасибо всем за ответы и присланные примеры. Ещё буквально пару вопросов.
1) У меня комп. 3.20С/4.12А. В объявлении среды включена м/сх atmega640 необходимая мне, но в каталоге INC нет файла iom640.h. Где его взять? Самому написать конечно можно. На сайте IAR поиском не нашёл.
2) По поводу разбиения на файлы я, по видимому не правильно задал вопрос. Я в форуме где-то читал, что если п/п прерывания поместить в отдельный файл, то компилятор сохраняет/восст. все регистры в данном прерывании. Хотя может я и не правю понял. Также я читал что объявления надо обязательно вынести в отд. файл и включить его вначале. Из этого я сделал вывод (возможно неверный) что существует определённый порядок размещения процедур и объявлений. Хотя бы желательный. Если это так, то напишите чтобы я привыкал. smile.gif
С уважением
defunct
Цитата(SasaVitebsk @ Feb 8 2006, 03:40) *
1) У меня комп. 3.20С/4.12А. В объявлении среды включена м/сх atmega640 необходимая мне, но в каталоге INC нет файла iom640.h. Где его взять? Самому написать конечно можно. На сайте IAR поиском не нашёл.

Возможно вы плохо смотрели. iom640.h должен находиться в каталоге avr\inc\
в крайнем случае могу выслать почтой.
Цитата
2) По поводу разбиения на файлы я, по видимому не правильно задал вопрос. Я в форуме где-то читал, что если п/п прерывания поместить в отдельный файл, то компилятор сохраняет/восст. все регистры в данном прерывании. Хотя может я и не правю понял.

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

Выносятся только "глобальные" экспортируемые объявления с помощью одноименного .h файла, к таким объявлениям можно отнести: объявления констант, типов, переменных и функций которые будут экспортироваться из модуля .c.

Если провести аналогию с Delphi (unit), то считайте секция interface - в C это отдельный файл с расширением ".H", а секция implementation - это файл с расширением ".C". Разница в том, что в C этом механизм более гибок, и в .h файлах в отличие от секции interface (delphi unit) допустимо, но крайне нежелательно, размещение реализации.
Pyku_He_oTTyda
Цитата
Скачал PDFы и сделал книжечки

Не поделитесь, кде качали книжечки? Тоже интересно освоить.
dxp
Цитата(defunct @ Feb 8 2006, 08:50) *
Компилятор просто обязан сохранить все регистры на входе в обработчик прерывания и также обязан их восстановить на выходе из обработчика, независимо от того в отдельном модуле описан обработчик или в основном.

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

Исходя из этого, если нужен максимально быстрый обработчик прерываний, то вызов функций нецелесообразен. Если все-таки такая необходимость есть, то имеет смысл подумать, нельзя ли сделать эту функцию встраиваемой и разместить ее определение в заголовочном файле так, чтобы ее определение было доступно компилятору в точке компиляции кода обработчика прерывания.
Эрик
SasaVitebsk!
Я тоже начал осваивать IAR Embedded Workbench.
Если есть возможность, поделитесь ссылками.
Может у кого есть Дока по IAR на русском?
_artem_
По пункту 3 :
Предполагаю что setjmp запоминает регистры R0-R31, SREG ... в память а longjmp загружает из памяти указанной как аргмент к этим функциям . Тип jmp_buf используется для сохранения этих регистров наверху. Поддержка многозадачности .

jmp_buf main_task;
jmp_buf iprx_task;
jmp_buf iptx_task;
для каждой задачи по своему буферу для сохранения контекста (регистров и данных наверху)

char iprx_rstack[8]; - исползуется для сохранения RSTACK . У IAR два стека - CSTACK и RSTACK . В случае переключения от одной задачи к другой - оба надо сохранять . Посмотри документы по IAR для детального обяснения.
Rst7
Цитата(_artem_ @ Feb 8 2006, 14:47) *
По пункту 3 :
Предполагаю что setjmp запоминает регистры R0-R31, SREG ... в память а longjmp загружает из памяти указанной как аргмент к этим функциям . Тип jmp_buf используется для сохранения этих регистров наверху. Поддержка многозадачности .

jmp_buf main_task;
jmp_buf iprx_task;
jmp_buf iptx_task;
для каждой задачи по своему буферу для сохранения контекста (регистров и данных наверху)

char iprx_rstack[8]; - исползуется для сохранения RSTACK . У IAR два стека - CSTACK и RSTACK . В случае переключения от одной задачи к другой - оба надо сохранять . Посмотри документы по IAR для детального обяснения.


Гм, да можно было и у меня спросить... Приятно, когда на твоих исходниках люди учатся wink.gif
defunct
Цитата(Rst7 @ Feb 8 2006, 15:17) *
Гм, да можно было и у меня спросить... Приятно, когда на твоих исходниках люди учатся wink.gif


Тогда у меня к Вам, как к автору кода, есть пара вопросов:
1. почему нельзя было просто вызвать IPrec() в обработчике прерывания?
2. из-за чего весь сыр-бор с сохранением стеков?

Цитата(dxp @ Feb 8 2006, 09:07) *
Не все, а только используемые. Компилятор обязан генерить код, не нарушающий целостность работы программы, не более того.
....
Исходя из этого, если нужен максимально быстрый обработчик прерываний, то вызов функций нецелесообразен. Если все-таки такая необходимость есть, то имеет смысл подумать, нельзя ли сделать эту функцию встраиваемой и разместить ее определение в заголовочном файле так, чтобы ее определение было доступно компилятору в точке компиляции кода обработчика прерывания.


Спасибо, что поправили меня.
Вопрос: а если вызываемая в обработчике функция определена в том же самом модуле, в котором определен и обработчик прерывания, потребуется ли определять ее как встраиваемую или компилятор и так будет "знать" какие регистры ей используются, и сгенерит код для сохранения только их?
dxp
Цитата(defunct @ Feb 9 2006, 04:45) *
Вопрос: а если вызываемая в обработчике функция определена в том же самом модуле, в котором определен и обработчик прерывания, потребуется ли определять ее как встраиваемую или компилятор и так будет "знать" какие регистры ей используются, и сгенерит код для сохранения только их?

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

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

Кстати, квалификация функции словом inline совсем не гарантирует, что функция окажется встроенной. inline - это не более, чем подсказка компилятору, компилятор имеет полное право не встраивать функцию. Есть несколько объективный ситуаций, где встраивание не может быть осуществлено в принципе (например, рекурсивный вызов). Но в большинстве случаев у компилятора нет никаких причин отказывать во встраивании, если это явно указано. И кстати же, наш любимый IAR, как раз, замечен в нехорошем поведении - отказываться встраивать. Там, типа, как объяснили из саппорта, используется неслабый эвристический анализатор, который и определяет, стОит ли встраивать функцию или нет. По мне так все эти эвристики идут лесом, еще на версиях 2.хх столько крови они попили, сколько было сломано копий в дискуссиях с саппортом, доказывая, что эвристика - эвристикой, но лучше меня компилятор не может знать, встраивать функцию или нет, и если я явно написал inline (или определил тело функции-члена в классе), то пусть встраивает, не выпендривается. В конечном итоге они признали, что в этом есть смысл, но от эвристики не отказались, мотивируя, что если все отдать на откуп пользователю, то он (пользователь) может и дров наломать - навстраивает функций, а потом будет недоволен, что код у программы здоровенный. Поэтому для тех, кто хочет стопудово встроить фунцию и настаивает на этом, ввели специальную прагму #pragma inline=forced, при ее использовании гарантируется, что функция будет встроена, а если встраивание невозможно, то будет выдана ошибка. Этот подход, в принципе, устраивает всех.

Т.ч. как уже сказал, всегда лучше держать этот процесс под контролем, если надо, чтобы функция была встроена, то лучше позаботиться об этом: сделать ее определение доступной в точке компиляции, снабдить словом inline и, если компилятор поддерживает дополнительные средства типа упомянутой прагмы, то обязательно использовать их.
Rst7
Цитата(defunct @ Feb 9 2006, 00:45) *
Тогда у меня к Вам, как к автору кода, есть пара вопросов:
1. почему нельзя было просто вызвать IPrec() в обработчике прерывания?
2. из-за чего весь сыр-бор с сохранением стеков?


1. Возможно вы заметили, что IPreciver есть не конечный автомат, а обычный код, скажем так, поток (нить, thread) выполнения. Посему есть ответ и на вопрос 2 - весь сыр-бор - это организация двух (точнее трех) задач - main, IPreciver, IPtransmitter. Из них IPxxx - это задачи, которые передают управление в основную задачу только тогда, когда им надо ждать символ из входного потока (IPreciver) или ждать окончания передачи символа в выходной поток (IPtransmiter). И получают они управление по соответствующим прерываниям. Почему так - объясняю. Представте, что вам надо получить в буфер 20 байт. Как вы пишете это без использования прерываний:

char s[20];
char *p=s;
char i=20;
do
{
*p++=rxchar();
}while(--i);

Просто и понятно.

Теперь представте это как автомат. Необходимо хранить указатели, состояние, и т.д. Каждое прерывание их необходимо загрузить, потом выгрузить. Весьма накладно, когда обработчик большой и сложный - как например TCP/IP. Поэтому, в том проекте, который мы обсуждаем, сделана такая многозадачка с использованием функций исключительно библиотеки. Т.е. что получается с сохранением контекста:

1. Обработчик прерываний штатно сохраняет все scratch-регистры, т.к. есть вызов функции в обработчике (функция setjmp и longjmp). Также обработчик сохраняет SREG и если AVR большой, всяческие RAMPZ. Точнее сохраняет компилятор.
2. Функция setjmp сохраняет все localstore-регистры,оба указателя стека SP и Y, а также PC. В результате имеем полностью сохраненный контекст задачи в двух местах - часть регистров лежит в CSTACK задачи, остальное - в jmp_buf.

Назад - аналогично. Прелести метода в том, что ни одной асмовской комманды нет. Единственное колдовство - инициализация задачи прямо в jmp_buf. При анализе суммарного кода почти нет потерь на на переключение, если сравнивать чисто с асм-вариантом - буквально 1 лишний CALL.
defunct
Цитата(Rst7 @ Feb 9 2006, 10:22) *
Теперь представте это как автомат. Необходимо хранить указатели, состояние, и т.д. Каждое прерывание их необходимо загрузить, потом выгрузить. Весьма накладно, когда обработчик большой и сложный - как например TCP/IP. Поэтому, в том проекте, который мы обсуждаем, сделана такая многозадачка с использованием функций исключительно библиотеки. Т.е. что получается с сохранением контекста:

Хорошо, имеется некая структура данных (массив и индексы), по сути в этой структуре хранится весь контекст приемника. В чем заключается сложность обращения к этой структуре из обработчика прерывания? Ни в чем. Непонятно для чего может потребоваться еще что-то сохранять, когда просто попав в обработчик прерывания можно работать непосредственно с этой структурой данных (не затрагивая других структур данных).
Цитата
2. Функция setjmp сохраняет все localstore-регистры,оба указателя стека SP и Y, а также PC. В результате имеем полностью сохраненный контекст задачи в двух местах - часть регистров лежит в CSTACK задачи, остальное - в jmp_buf.

Т.е. вы сделали подобие полноценного диспетчера задач, что ж похвально. Однако, зачем усложнять себе жизнь, когда предполагается всего три задачи, причем четко разграниченные по назначению. Даже если задача относительно ресурсоемкая, она может выполняться непосредственно в контексте прерывания. Если же по каким-то причинам не может - тогда можно организовать диспетчеризацию непосредственно в основном потоке программы, что в любом случае будет проще и не потребует дополнительной памяти на сохранение контекстов задач.
простейшая диспетчеризация, внутри функций-задач рассматриваются условия "запуска", которые устанавливаются обработчиками прерываний и сбрасываются после отработки самими "задачами":

Код
   for(;;)
   {
      task1();
      task2();
      task3();
    }
SasaVitebsk
Ещё раз спасибо всем ответившим. smile.gif
Перевариваю.
Dxp спасибо за полное разъяснение по поводу прерываний. В общем-то я так и предпологал когда задавал вопрос, но ты полностью расставил точки над и. Исчерпывающее объяснение простым и лаконичным языком. smile.gif
RST по поводу использования твоего продукта (пока) в учебных целях, я тебе писал. Извини если письмо не дошло. Пока задавать тебе вопросы для меня бессмысленно. Мы разговариваем на разных языках. smile.gif
Вообще данный проект для меня несколько сложноват. Это когда отдельные строчки понимаешь, а всё вместе в картину не складывается. smile.gif
Мне прислали более простой и я сейчас заглядываю в оба.
По поводу книжечек и обучения. Книжечки, - это сопровождающие PDF которые находятся в комплекте IAR. Они на англ. языке. Не скажу, что я его хорошо знаю, но я привык работать с PDF доками для микросхем и меня это вполне устраивает. Вывел их на печать с помощью проги FinePrint в виде книжечки. Переплёл их в типографии. По Си пользуюсь русской книгой Г.Шилда. Думаю знания придут по мере преобретения опыта. Пишу проект. Накопятся вопросы, - напишу.
По iom640.h я знаю где искать его там нет. Пока пользуюсь iom2560.h Это практически одно и тоже по SFR и озу. В проекте выставил mega640. В любом случае эти ошибки вылезут только на этапе отладки
Rst7
Цитата(defunct @ Feb 9 2006, 13:55) *
Хорошо, имеется некая структура данных (массив и индексы), по сути в этой структуре хранится весь контекст приемника. В чем заключается сложность обращения к этой структуре из обработчика прерывания? Ни в чем. Непонятно для чего может потребоваться еще что-то сохранять, когда просто попав в обработчик прерывания можно работать непосредственно с этой структурой данных (не затрагивая других структур данных).


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

Цитата
Код
   for(;;)
   {
      task1();
      task2();
      task3();
    }


Аналогично - все превращается в автомат и совершенно не читабельно. Тем более, что эта программа (это стек TCP/IP) как понимаете не самоцель, а лишь маленькая часть большой программы, а вы предлагаете весь софт писать вот так?

ЗЫ Наверное, надо другую тему организовать под названием "Какие у сапожников взгляды на искусство" wink.gif
defunct
Цитата(Rst7 @ Feb 9 2006, 14:56) *
Можно, однако это раздувает код, т.к. все локальные переменные превращаются в статические. А само сохранение регистров (и последующее восстановление) остается и никуда не девается. А так убиваем двух зайцев - код уменьшаем, улучшаем читаемость этого всего дела, т.к. весь алгоритм лежит последовательно, а не по кусочкам, выполнить то, выполнть это...

Не все переменные превращаются в статические. Статическими будут только переменные, которые необходимо хранить между входами в обработчик прерывания. По расходу памяти если сравнить с Вашим способом - "те же консервы, вид сбоку" smile.gif

Насчет улучшения читаемости - для кого? Углядеть алгоритм работы многозадачной системы - всегда сложнее чем однозадачной.

Цитата
Аналогично - все превращается в автомат и совершенно не читабельно. Тем более, что эта программа (это стек TCP/IP) как понимаете не самоцель, а лишь маленькая часть большой программы, а вы предлагаете весь софт писать вот так?

я не предлагаю так писать софт, я его так пишу.

for{;;}
{
DispatchLalala();
KernelDispatchblablabla();
DoSomethingElse();
sleep();
}


Цитата
ЗЫ Наверное, надо другую тему организовать под названием "Какие у сапожников взгляды на искусство" wink.gif

smile.gif
SasaVitebsk
По ходу написания возникло ещё несколько вопросов, Вы уж извините.
1) Предположим у меня из rs232 в буффер грузятся различные структуры данных, их размер и тип определяются в момент загрузки. В процессе работы, обработанные структуры удаляются из буффера (удаляемая может находится внутри буфера).
Я вижу два варианта работы с такими данными:
а) Динамически размещаю каждую новую структуру и работаю с объявленными полями. Динамически удаляю.
б) Один раз с пом. malloc выделить всю свободную память и работать с данными, как с байтами, производя преобразования на ходу вручную.
Я как конченный ассемблерщик smile.gif склоняюсь конечно ко второму. Преимущество в простоте загрузки и очистки "мусора". Но прога в первом варианте будет выглядеть, по-моему, красивей. smile.gif

Вопрос 1: Как много ресурсов будет зажирать постоянно дёргающиеся malloc/free. Будет ли эффективней работа со структурой.

Вопрос 2: если я получаю адрес расположения структуры в указатель
byte *Addr;

и имею структуру типа описанной ниже. При этом я не хочу под неё выделять память. Я хочу иметь одну переменную-структуру и присвоить значение адреса чтобы иметь возможность работать с полями по законам соответствующей арифметики. Будет ли правильным такое объявление ...

struct x1
{
word TimeStart; // Время начала исполнения команды
byte TimeMashtabTek, // Текущее значение масштаба для времени исполнения
TimeMashtab; // Масштаб для времени исполнения
word TimeLife; // Время исполнения команды с учётом масштаба
int BegX,BegY, // Начало объекта (X,Y)
SizeX,SizeY; // Размеры объекта (X,Y)
signed char VecX,VecY; // вектор перемещения объекта (X,Y)
} *tip1;

... и такая работа с ним. Если нет, то как сделать правильно

tip1 = Addr; /* или типа ... "tip1 = (struct x1*) Addr; */
*tip1.TimeLife--; /* слово уменьшится на 1 */

Прошу не смейтесь, так как я плаваю и пока не "чуствую языка".

2) Что за всеобщая истерика с printf/scanf. Почему все так стремятся использовать эти процедуры? Что это даёт программисту (в смысле на микроконтроллере)?
Если это действительно так важно, то как их использовать если поток I/O у тебя совершенно свой? В смысле есть ли какой нибудь Assign? Переписать Putc/Getc?
Если да, то как вводить/выводить на разные устройства в одном проекте? Короче чуствую что я не знаю целую глобальную область и это удручает.
vet
1) Не рекомендуется использовать динамическое выделение памяти в программе для м/к. Соответственно, и реализовано оно в известных мне компиляторах AVR весьма примитивно.
Времени выделение не занимает.
2) Это пишется так: tip1->TimeLife--;
3) Стандартный сишный ввод-вывод, незачем изобретать велосипед. И возможностей у него более чем достаточно.
Переписать нужно putchar/getchar.
IgorKossak
SasaVitebsk, если Вы склоняетесь к п. 1).б), то Вам и вовсе незачем пользоваться динамической памятью, используйте статический массив.
По поводу printf/scanf, кроме того, что Вам посоветовал vet, можно ещё воспользоваться вариантом sprintf/sscanf и работать со стрингами в ОЗУ если не хотите переписывать putchar/getchar (т. к. устройства ввода\вывода могут быть разные).
SasaVitebsk
Цитата(vet @ Feb 14 2006, 10:28) *
1) Не рекомендуется использовать динамическое выделение памяти в программе для м/к. Соответственно, и реализовано оно в известных мне компиляторах AVR весьма примитивно.
Времени выделение не занимает.


Это понял. Спасибо.

Цитата(vet @ Feb 14 2006, 10:28) *
2) Это пишется так: tip1->TimeLife--;


Это как раз понятно. А объявление я правильно сделал или правильнее как в коментарии?
Компилятор при таком объявлении займёт память или нет и как это посмотреть?


Цитата(vet @ Feb 14 2006, 10:28) *
3) Стандартный сишный ввод-вывод, незачем изобретать велосипед. И возможностей у него более чем достаточно.
Переписать нужно putchar/getchar.


Это понял. Спасибо.
IgorKossak
Цитата(SasaVitebsk @ Feb 14 2006, 21:40) *
Цитата(vet @ Feb 14 2006, 10:28) *

2) Это пишется так: tip1->TimeLife--;

Это как раз понятно. А объявление я правильно сделал или правильнее как в коментарии?
Компилятор при таком объявлении займёт память или нет и как это посмотреть?

Как в комментарии лучше.
Когда делаете явное приведение, это больше для себя, чтобы в будущем не забыть что имелось в виду.
Компилятору всё равно (посмотреть можно в листинге или при отладке).
SasaVitebsk
Спасибо, попробую разные варианты и доложу как получилось лучше. smile.gif Ничто не помогает узнать язык лучше, чем опыт и испытание разных вариантов. biggrin.gif
SasaVitebsk
1) Насколько я понял запись типа " *str++ " говорит что "надо извлечь символ и сдвинуть указатель", а не "инкрементировать значение по адресу". Поправте если я ошибся.
2) Нашёл как передаются параметры, но пока не нашёл как они хранятся в памяти. Меня интересует short int хранится младшим байтом вперёд?
3) Если я планирую использовать крупные таблицы данных. 4 таблицы по 2-3 кб. Не хотелось бы их подключать в виде текстового массива-констант. Есть ли возможность подключить бинарный файл на этапе линковки? Как в данном случае определить и использовать адрес который получится на этапе линковки? Или может быть существует другой способ? Подскажите.

Заранее благодарен.
dxp
Цитата(SasaVitebsk @ Feb 21 2006, 04:39) *
1) Насколько я понял запись типа " *str++ " говорит что "надо извлечь символ и сдвинуть указатель", а не "инкрементировать значение по адресу". Поправте если я ошибся.

Да.

Цитата(SasaVitebsk @ Feb 21 2006, 04:39) *
2) Нашёл как передаются параметры, но пока не нашёл как они хранятся в памяти. Меня интересует short int хранится младшим байтом вперёд?

Вопрос непонятен. Параметры (аргументы функции) именно передаются, а не хранятся. Схемы есть разные, в версиях 1.хх была одна схема, начиная с 2.хх - другая. В ней, насколько помню, все, что влазит в 8 регистров с 16 по 23, передается в них (с учетом выравнивания, ессно), остальное через стек. Все это хорошо документировано.

Цитата(SasaVitebsk @ Feb 21 2006, 04:39) *
3) Если я планирую использовать крупные таблицы данных. 4 таблицы по 2-3 кб. Не хотелось бы их подключать в виде текстового массива-констант. Есть ли возможность подключить бинарный файл на этапе линковки? Как в данном случае определить и использовать адрес который получится на этапе линковки? Или может быть существует другой способ? Подскажите.

Подключать бинарный файл можно, если он в формате UBROF. smile.gif Как сделать этот UBROF - пути все те же: скомпилить из сорца. smile.gif
IgorKossak
Цитата(SasaVitebsk @ Feb 21 2006, 00:39) *
3) Если я планирую использовать крупные таблицы данных. 4 таблицы по 2-3 кб. Не хотелось бы их подключать в виде текстового массива-констант. Есть ли возможность подключить бинарный файл на этапе линковки? Как в данном случае определить и использовать адрес который получится на этапе линковки? Или может быть существует другой способ? Подскажите.

Бинарный файл на этапе линковки можно подключить.
См. опции линкера Config\Raw binary image
SasaVitebsk
Цитата(dxp @ Feb 21 2006, 15:36) *
Вопрос непонятен. Параметры (аргументы функции) именно передаются, а не хранятся. Схемы есть разные, в версиях 1.хх была одна схема, начиная с 2.хх - другая. В ней, насколько помню, все, что влазит в 8 регистров с 16 по 23, передается в них (с учетом выравнивания, ессно), остальное через стек. Все это хорошо документировано.


Я имел ввиду не передачу функции данных в виде параметра, а хранение этих данных в озу. например если я использую адрес на int, то он укажет на младший или на старший байт?
В одном месте нашёл вариант сохранения структуры. Похоже младшим байтом вперёд. Ладно это проверим.
dxp
Цитата(SasaVitebsk @ Feb 21 2006, 23:16) *
Я имел ввиду не передачу функции данных в виде параметра, а хранение этих данных в озу. например если я использую адрес на int, то он укажет на младший или на старший байт?
В одном месте нашёл вариант сохранения структуры. Похоже младшим байтом вперёд. Ладно это проверим.

Указатель содержит адрес. Адрес - это просто местоположение в памяти. Обычно измеряется в байтах (8-битных). К размеру объекта, расположенного по адресу, сам адрес никакого отношения не имеет. К размеру имеет отношение тип указателя. Собственно, он (тип) для того и задается, чтобы компилятор мог правильно работать с объектом, адресуемым указателем. Для писания на С/С++, если не использовать хаки и всякие низкоуровневые финты ушами, а также если опустить вопросы отладки, ровно пофигу, как объекты располагаются в памяти физически. Комплятор их положил, пусть с ними разбирается. Конкретно EWAVR - да кладет объекты младшими байтами по младшим адресам.
SasaVitebsk
DXP ты всё правильно понимаешь, но я писал на ASMе даже для IBM, поэтому писать "без финтов ушами" ещё надо научиться. smile.gif А надо. Очень надо. Я хочу чтобы прога получилась минимально зависима от кристала.

Теперь новый вопрос. Если Вы не возражаете.
Я хочу во флеш разместить несколько массивов. И масив указателей на эти массивы. Вот такое объявление будет правильным? Если нет, то как?

const byte __flash fnt0_6x8[224] = { .... };
const byte __flash fnt1_8x8[224] = { .... };
const word __flash fnt3_10x10[224] = { .... };
const word __flash fnt3_11x13[224] = { .... };

const void __flash *symbol[3] = {&fnt0_6x8,&fnt1_8x8,&fnt2_10x10,&fnt3_11x13};
vet
Нет, идентификатор массива сам является указателем. Правильно будет {fnt0_6x8, fnt1_8x8, fnt2_10x10, fnt3_11x13};
dxp
Цитата(SasaVitebsk @ Mar 1 2006, 17:07) *
Теперь новый вопрос. Если Вы не возражаете.
Я хочу во флеш разместить несколько массивов. И масив указателей на эти массивы. Вот такое объявление будет правильным? Если нет, то как?

const byte __flash fnt0_6x8[224] = { .... };
const byte __flash fnt1_8x8[224] = { .... };
const word __flash fnt3_10x10[224] = { .... };
const word __flash fnt3_11x13[224] = { .... };

const void __flash *symbol[3] = {&fnt0_6x8,&fnt1_8x8,&fnt2_10x10,&fnt3_11x13};

Во-первых, const не нужен - __flash и так константнее некуда. smile.gif Я обычно использовал вид

__flash byte Array[] = { .... };

Оператор разименовывания ('*') при объявлении массива писать не надо, если только не хотите объявить массив указателей.

Вообще, я понял, что Вам надо типа двумерного массива - ну так и объявите явно массив массивов, зачем эти финты с адресами? И памяти меньше скушает.
defunct
Цитата(dxp @ Mar 1 2006, 16:04) *
Во-первых, const не нужен - __flash и так константнее некуда. smile.gif Я обычно использовал вид


А почему в таком случае нельзя просто const использовать, все-таки const это из стандарта ansi-c, а __flesh всего-лишь приблуда от IAR.
IgorKossak
Цитата(defunct @ Mar 1 2006, 20:26) *
Цитата(dxp @ Mar 1 2006, 16:04) *

Во-первых, const не нужен - __flash и так константнее некуда. smile.gif Я обычно использовал вид


А почему в таком случае нельзя просто const использовать, все-таки const это из стандарта ansi-c, а __flesh всего-лишь приблуда от IAR.

В этом случае компилятор\линкер могут потребовать внешней ROM, где и будут храниться Ваши константы.
Что касается стандартов, то они хороши для PC, а для embedded приложений с невероятным количеством особенностей целевых архитектур "приблуды" зачастую необходимы.
SasaVitebsk
Цитата(dxp @ Mar 1 2006, 18:04) *
Во-первых, const не нужен - __flash и так константнее некуда. smile.gif Я обычно использовал вид

__flash byte Array[] = { .... };

Оператор разименовывания ('*') при объявлении массива писать не надо, если только не хотите объявить массив указателей.

Вообще, я понял, что Вам надо типа двумерного массива - ну так и объявите явно массив массивов, зачем эти финты с адресами? И памяти меньше скушает.


Спецы как всегда зрят в корень! biggrin.gif Их не обманешь. Уточню картину. smile.gif С учётом Ваших поправок.

byte __flash fnt0_6x8[224][8] = { .... };
byte __flash fnt1_8x8[224][8] = { .... };
word __flash fnt3_10x10[224][10] = { .... };
word __flash fnt3_11x13[224][13] = { .... };

void __flash *symbol[3] = {&fnt0_6x8,&fnt1_8x8,&fnt2_10x10,&fnt3_11x13};

Я действительно хотел сделать массив ссылок, потому как масивы во первых два байтовых и два словных, а во вторых имеют разные размеры. Я не знаю как их объединить в один массив обычный.
Во вторых если я хотел сделать массив указателей правильно ли я объявил их ( &...) . Компилятор не выдал мне ошибки

И ещё отдельный вопрос. При запуске мой компилятор указывает мне на отсутствие одного из файлов DLL. И ещё ряд вопросов у меня к нему smile.gif
Где скачать?
vet
Цитата
Во вторых если я хотел сделать массив указателей правильно ли я объявил их ( &...) . Компилятор не выдал мне ошибки

Уже писал про это на прошлой странице.
Balaganov
Цитата(SasaVitebsk @ Mar 2 2006, 20:13) *
void __flash *symbol[3] = {&fnt0_6x8,&fnt1_8x8,&fnt2_10x10,&fnt3_11x13};



в левой части надобы ставиить 4, т.к. кол-во элементов массива = 4.
dxp
Цитата(IgorKossak @ Mar 2 2006, 00:33) *
Цитата(defunct @ Mar 1 2006, 20:26) *

Цитата(dxp @ Mar 1 2006, 16:04) *

Во-первых, const не нужен - __flash и так константнее некуда. smile.gif Я обычно использовал вид


А почему в таком случае нельзя просто const использовать, все-таки const это из стандарта ansi-c, а __flesh всего-лишь приблуда от IAR.

В этом случае компилятор\линкер могут потребовать внешней ROM, где и будут храниться Ваши константы.

Добавлю: либо разместит объекты, объявленные как const, в ОЗУ. А его мало и жалко его тратить под неизменяемые данные.
dxp
Цитата(SasaVitebsk @ Mar 3 2006, 00:13) *
byte __flash fnt0_6x8[224][8] = { .... };
byte __flash fnt1_8x8[224][8] = { .... };
word __flash fnt3_10x10[224][10] = { .... };
word __flash fnt3_11x13[224][13] = { .... };

void __flash *symbol[3] = {&fnt0_6x8,&fnt1_8x8,&fnt2_10x10,&fnt3_11x13};

Я действительно хотел сделать массив ссылок, потому как масивы во первых два байтовых и два словных, а во вторых имеют разные размеры. Я не знаю как их объединить в один массив обычный.
Во вторых если я хотел сделать массив указателей правильно ли я объявил их ( &...) . Компилятор не выдал мне ошибки

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

Сделать массив указателей на разные типы тоже нельзя, указатели на разные типы - сами по себе являются разными типами и не могут быть элементами массива. То, что Вы сделали - завели массив указателй void* - это Вы обманули компилятор (и себя тоже smile.gif ). Этим самым Вы отключили контроль типов со стороны компилятора и взяли всю ответственность за правильность работы на себя. Компилятор тут "умыл руки". Не советую так делать. Вообще, найдется очень немного случаев, когда без void* действительно сложно обойтись и он оправдан. Но рассматриваемый случай совсем не этот.

Я не очень понимаю, что Вы хотите иметь в конечном итоге, но учитывая, что эти массивы как-то используются в каких-то функциях, и принимая во внимание тот факт, что раз массивы разные, то и функции с ними работают разные, - реализовать работу с ними через массив указателей на функции.

Цитата(SasaVitebsk @ Mar 3 2006, 00:13) *
И ещё отдельный вопрос. При запуске мой компилятор указывает мне на отсутствие одного из файлов DLL. И ещё ряд вопросов у меня к нему smile.gif
Где скачать?

На какую DLL? Никогда ничего подобного не встречал.
SasaVitebsk
отсутствует ...\avr\bin\nwtdavr.dll smile.gif
при этом проект компилится раза три-четыре. После чего указ. что-то о лицензии smile.gif
Ну я выхожу/вхожу и по новый. smile.gif сам компилятор мне с таблеткой пришёл, но я и новую пробовал. Похоже в нём чего-то не хватает.
Остановиться не могу, - к середине мая проект должен быть завершён. Пробую перейти на GCC. Но потом хочу вернуться и перекомпилить на IAR. А пока попробую найти где-нибудь ну и условную компиляцию чтоб на обоих, - не помешает. smile.gif Потом сверю скорость и объём.

По поводу ссылок. Несколько процедур будут однотипно обрабатывать эти массивы. Перед обработкой я явно указываю (определяю по размерности) тип массива и обрабатываю их. Если я сделаю так, то потом могу менять/добавлять новые массивы. (это массивы шрифтов).
dxp
Цитата(SasaVitebsk @ Mar 3 2006, 15:53) *
отсутствует ...\avr\bin\nwtdavr.dll smile.gif

Нету у меня такой дллки. Версия 4.12. И все без проблем работает.
appsoft
Сообщение об отсутствии nwtdavr.dll наблюдал в результате "лечения" IAR версии 3.20 под XP. Появляется при каждый раз при старте компилятора. По моим наблюдениям на работоспособность не влияет.
SasaVitebsk
Цитата(SasaVitebsk @ Feb 14 2006, 23:40) *
Цитата(vet @ Feb 14 2006, 10:28) *

2) Это пишется так: tip1->TimeLife--;


Это как раз понятно. А объявление я правильно сделал или правильнее как в коментарии?
Компилятор при таком объявлении займёт память или нет и как это посмотреть?


Передаю привет "vet". Даже если он этот топик и не увидит. smile.gif

Вот теперь не то чтобы понятно, но чуть-чуть приблизился к пониманию. А ведь знающие люди месяц назад писали. Вот баран самоуверенный. Теперь более 500 строк текста перелопачиваю! smile.gif

Ещё вопрос. Хочу в подпрограмму передать ссылку. Это массив расположенный в структуре. На неё имеется указатель. Т.е. к самому массиву я обращаюсь KomG->Pict, а адрес пытаюсь передать типа
&(KomG->Pict). Объявление следующее:

struct AddrKomG
{
byte Name; // Èìÿ êîìàíäû
word TimeStart; // Âðåìÿ íà÷àëà èñïîëíåíèÿ êîìàíäû
byte TimeMashtabTek, // Òåêóùåå çíà÷åíèå ìàñøòàáà äëÿ âðåìåíè èñïîëíåíèÿ
TimeMashtab; // Ìàñøòàá äëÿ âðåìåíè èñïîëíåíèÿ
word TimeLife; // Âðåìÿ èñïîëíåíèÿ êîìàíäû ñ ó÷¸òîì ìàñøòàáà
int BegX,BegY; // Íà÷àëî îáúåêòà (X,Y)
signed char VecX,VecY; // âåêòîð ïåðåìåùåíèÿ îáúåêòà (X,Y)
word SizeX,SizeY; // Ðàçìåð êàðòèíêè (X,Y)
byte Pict[10]; // Êàðòèíêà
} *KomG;

а объявление функции следующее

void OutPict(int X,int Y,int sx,int sy,byte *Pict)

Компилятор ругается типа

Error[Pe167]: argument of type "byte (*)[10]" is incompatible with parameter of type "byte *" E:\work\AVR IAR C\Panno\office.c 232
beer_warrior
KomG->Pict это уже сам по себе указатель т.е. значение KomG инкрементированнае на смещение Pict в структуре.
Чтобы доступиться к элементу Pict[N] указатель смещается еще на N байт.
А вы даете как аргумент адрес указателя.
SasaVitebsk
Пользуюсь книгой Шилдт"Полный справочник по С" 4-е изд. Задаю вопрос когда уже совсем ....
Там указано: Чтобы с помощью указателя на структуру получить доступ к её членам, необходимо использовать оператор стрелка ->.

После этого приводится пример. Где описана процедура

void update(struct my_time *t) { // в принципе как у меня
t->seconds++; // в данном случае инкрементируется значение а не адрес
if (t->seconnds==60) { t->seconnds=0; // опять таки значение.

Вероятнее всего я чего-то не понимаю.

Давайте сначала.
Итак объявлен указатель t на структуру my_time (предположем что сама структура тоже есть):

struct my_time {
int hours;
int minutes;
int seconds;
} *t;

То по книге чтобы инкрементировать значение секунд мне необходимо выполнить оператор

t->seconds++;

Т.е. чтобы передать секунды в подпрограмму "по значению" мне также необходимо передать
"t->seconds" в списке параметров.
А если я хочу передать адрес секунд т.е передать "по ссылке"? Я пытаюсь сделать это так &(t->seconds).

Где в моих рассуждениях ошибка?
Заранее благодарю.
vet
Так и нужно. Можно без скобок.
SasaVitebsk
"vet" твой ответ добавил мне уверенности и синтаксическую ошибку я нашёл. Пасибо тебе в очередной раз. Правда синтаксически верная программа не всегда работает. smile.gif

У меня в структуре стоял массив. Поэтому синтаксически правильно было передавать &KomG->Pict[0].

Подскажите если есть массив указателей на многомерные массивы.
const void __flash *symbol[maxfonts] = {&fnt0_6x8,&fnt1_8x8,&fnt2_10x10,&fnt3_11x13};

Приведу для примера объявление одного из массивов.
const byte __flash fnt0_6x8[224][8] = {

Я при работе с элементами использую арифметику. Т.е. высчитываю адрес. Ну например:
id = *(((byte*)symbol[tekfonts])+(Znak*Fonts[tekfonts][2])+iy);

А может можно компилятору просто указать элемент типа [][]
Наверное тогда указатель нужно привести к типу "конкретный масив".

И ещё один вопрос:
Как мне найти начало свободной памяти для динамических данных? Я по аналогии с Паскалем пробовал сделать так

TekAdrActive = malloc(SizeFree); //Выделить память

пишет что malloc типа void
defunct
Цитата(SasaVitebsk @ Mar 6 2006, 00:20) *
И ещё один вопрос:
Как мне найти начало свободной памяти для динамических данных? Я по аналогии с Паскалем пробовал сделать так

А зачем находить начало? Просто выделяйте память под структуру..
Код
  MYSTRUCT *mystruct;
  mystruct = malloc( sizeof( MYSTRUCT) );


если вас интересует численное значение адреса, его можно получить например так:
Код
  int i;
  i = (int)malloc( 0 );
SasaVitebsk
Чувствую, что я скоро всем надоем, и отвечать мне никто не будет. smile.gif
Но у меня уже есть прогресс! Я написал большую часть проги и откомпиллил её без ошибок!!! smile.gif

Вот такой вот вопрос:
Есть объявление функции. В качестве параметра 32 битное число. Я хочу использовать его побайтно.
Могу я сделать так?

union uint32_u {
uint32_t dw;
uint8_t bt[4];
};

в вызове использовать типа: uint32_u addr;

а при обращении использовать unsigned long int addr.

Или как поступить в данном случае. Как обратится к байтам длинного числа не пребегая к операциям типа ">> 8 ...."
defunct
Цитата(SasaVitebsk @ Mar 7 2006, 03:26) *
Могу я сделать так?

union uint32_u {
uint32_t dw;
uint8_t bt[4];
};

можете, но для задания размера массива лучше использовать sizeof напр. так:

Код
union uint32_u{
   long Num;
   char Arr[sizeof(long)];  
};


Цитата
Или как поступить в данном случае. Как обратится к байтам длинного числа не пребегая к операциям типа ">> 8 ...."

Код
uint32_u Addr;
Addr.Num = <some long number>;
for ( i = 0; i < sizeof(long); ++i)
     Addr.Arr[i] = ...;
dxp
Цитата(SasaVitebsk @ Mar 7 2006, 07:26) *
Есть объявление функции. В качестве параметра 32 битное число. Я хочу использовать его побайтно.
Могу я сделать так?

union uint32_u {
uint32_t dw;
uint8_t bt[4];
};

в вызове использовать типа: uint32_u addr;

а при обращении использовать unsigned long int addr.

Или как поступить в данном случае. Как обратится к байтам длинного числа не пребегая к операциям типа ">> 8 ...."

Можно, все будет работать. На AVR. Но способ этот непереносимый, т.е. нет гарантий, что этот код будет работать на другой платформе. Об этом всегда надо помнить.
Для просмотра полной версии этой страницы, пожалуйста, пройдите по ссылке.
Invision Power Board © 2001-2025 Invision Power Services, Inc.