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

|
Доброго времени суток. Помогите начинающему. Долгое время писал на асме для разных микропроцессоров. В том числе большие проекты. На 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) Профи, если можно вышлите какой-нибудь файл(ы) исходников (можно не рабочих, урезанных или с ошибками) по которым я мог бы посмотреть структуру программы. Лучше с прерываниями. Обязуюсь не распространять и использовать в качестве учебных. Пожалуйста не присылайте слишком больших, а то я зароюсь.  Заранее благодарен. e-mail: sasa@c32dvina.com
|
|
|
|
|
Feb 7 2006, 22:48
|

Местный
  
Группа: Свой
Сообщений: 421
Регистрация: 27-05-05
Из: Энергодар
Пользователь №: 5 480

|
Цитата(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) меньше. Поэтому где возможно лучше использовать указатели. Добавлю что это ИМХО.
Сообщение отредактировал Pat - Feb 7 2006, 22:49
|
|
|
|
|
Feb 7 2006, 23:04
|

кекс
     
Группа: Свой
Сообщений: 3 825
Регистрация: 17-12-05
Из: Киев
Пользователь №: 12 326

|
Цитата(SasaVitebsk @ Feb 7 2006, 23:56)  1) Как определять какие библиотеки включать? Если Вы знакомы с Delphi, то библиотеки C это нечто схожее с rtl Delphi. Компилятор определит какие библиотеки включать, от Вас же требуется только определиться с набором интересующих функций (аналогичтно секции uses в Delphi) - подключить с помощью директивы include требуемые вам модули. Цитата 2) Если необходимы прерывания достаточно ли такого объявления или необходимо что-то ещё (например переходы): кроме объявления обработчика прерывания потребуется еще запуск того или иного периферийного устройства в режиме прерываний, а также глобальное разрешение прерываний. Цитата 3) Как понимать следующие строки? (В смысле на языке высокого уровня) в C вначале пишется тип, а потом имя переменной. т.е. первые четыре записи это явное объявление переменных. Цитата и вот эти пжлст не стоит привязываться к конкретному алгоритму в самом начале освоения языка. К тому же коментарии сделанные Бог весть кем могут быть и неправильными. Цитата 4) Что необходимо описывать чтобы грамотно сделать проект в котором несколько файлов ".С" так же как и в других языках, чтобы грамотно построить проект, требуется разбить проект на функциональные узлы, и оформить их в виде модулей. Цитата 5) При использовании кольцевого буфера правильнее открыть буфер в виде массива или использовать указатели. на любителя.
|
|
|
|
|
Feb 8 2006, 02:50
|

кекс
     
Группа: Свой
Сообщений: 3 825
Регистрация: 17-12-05
Из: Киев
Пользователь №: 12 326

|
Цитата(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) допустимо, но крайне нежелательно, размещение реализации.
Сообщение отредактировал defunct - Feb 8 2006, 03:27
|
|
|
|
|
Feb 8 2006, 07:07
|

Adept
     
Группа: Свой
Сообщений: 3 469
Регистрация: 6-12-04
Из: Novosibirsk
Пользователь №: 1 343

|
Цитата(defunct @ Feb 8 2006, 08:50)  Компилятор просто обязан сохранить все регистры на входе в обработчик прерывания и также обязан их восстановить на выходе из обработчика, независимо от того в отдельном модуле описан обработчик или в основном. Не все, а только используемые. Компилятор обязан генерить код, не нарушающий целостность работы программы, не более того. Исходя из этого, им сохраняются только регистры, используемые асинхронно (по отношению к основному потоку выполнения прогрммы). Если весь код обработчика прерывания доступен компилятору на этапе компиляции, то компилятор может легко определить, какие именно регистры ему нужны, и именно их и сохранить. Если в обработчике прерывания используется вызов функции, чей код недоступен компилятору (т.е. если функция невстраиваемая), то компилятор не знает, какие регистры эта функия использует и сохраняет все scratch регистры (preserved регистры ему тут сохранять не надо, их сохранит при необходимости вызываемая функция). Исходя из этого, если нужен максимально быстрый обработчик прерываний, то вызов функций нецелесообразен. Если все-таки такая необходимость есть, то имеет смысл подумать, нельзя ли сделать эту функцию встраиваемой и разместить ее определение в заголовочном файле так, чтобы ее определение было доступно компилятору в точке компиляции кода обработчика прерывания.
--------------------
«Отыщи всему начало, и ты многое поймёшь» К. Прутков
|
|
|
|
|
Feb 8 2006, 10:02
|
Группа: Новичок
Сообщений: 5
Регистрация: 15-12-05
Из: Санкт-Петербург
Пользователь №: 12 239

|
SasaVitebsk! Я тоже начал осваивать IAR Embedded Workbench. Если есть возможность, поделитесь ссылками. Может у кого есть Дока по IAR на русском?
--------------------
Uz
|
|
|
|
|
Feb 8 2006, 22:45
|

кекс
     
Группа: Свой
Сообщений: 3 825
Регистрация: 17-12-05
Из: Киев
Пользователь №: 12 326

|
Цитата(Rst7 @ Feb 8 2006, 15:17)  Гм, да можно было и у меня спросить... Приятно, когда на твоих исходниках люди учатся  Тогда у меня к Вам, как к автору кода, есть пара вопросов: 1. почему нельзя было просто вызвать IPrec() в обработчике прерывания? 2. из-за чего весь сыр-бор с сохранением стеков? Цитата(dxp @ Feb 8 2006, 09:07)  Не все, а только используемые. Компилятор обязан генерить код, не нарушающий целостность работы программы, не более того. .... Исходя из этого, если нужен максимально быстрый обработчик прерываний, то вызов функций нецелесообразен. Если все-таки такая необходимость есть, то имеет смысл подумать, нельзя ли сделать эту функцию встраиваемой и разместить ее определение в заголовочном файле так, чтобы ее определение было доступно компилятору в точке компиляции кода обработчика прерывания. Спасибо, что поправили меня. Вопрос: а если вызываемая в обработчике функция определена в том же самом модуле, в котором определен и обработчик прерывания, потребуется ли определять ее как встраиваемую или компилятор и так будет "знать" какие регистры ей используются, и сгенерит код для сохранения только их?
Сообщение отредактировал defunct - Feb 9 2006, 03:56
|
|
|
|
|
Feb 9 2006, 06:10
|

Adept
     
Группа: Свой
Сообщений: 3 469
Регистрация: 6-12-04
Из: Novosibirsk
Пользователь №: 1 343

|
Цитата(defunct @ Feb 9 2006, 04:45)  Вопрос: а если вызываемая в обработчике функция определена в том же самом модуле, в котором определен и обработчик прерывания, потребуется ли определять ее как встраиваемую или компилятор и так будет "знать" какие регистры ей используются, и сгенерит код для сохранения только их? Если функция встроена, т.е. ее код явно помещен в точку вызова, то все испольуемое видно. Если организован вызов, то все, никих потрохов функции компилятор рассматривать не будет, поведение будет всегда таким, каким оно должно быть при вызове функции. Так ведь можно далеко зайти - а если та функция тоже содержит вызов и т.д. Обычно такое поведение присутствует на самом деле. Хотя, конечно тут поведение компилятора может быть неоднозначно - он может делать различные оптимизации. Сегодня уже есть варианты компиляторов, которые делают post-link оптимизацию, т.е. проходятся уже по слинкованному коду и оптимизируют его. Т.ч. делать просмотр всего файла и организовывать оптимизации в пределах него вообще не очень сложная задача. В общем, все зависит от самого компилятора, указанных оптимизаций и их стратегий. Кое-что, между прочим, из таких оптимизаций и делается. Например, при оптимизации по размеру компилятор ищеть одинаковые куски во всем файле, выносит их в отдельные функии и организовывает вызовы. Т.ч. возможно скоро доживем и до более крутых оптимизаций. Но в настоящее время этого, насколько знаю, нет и лучше контролировать этот процесс, явно указывая, встраивать функцию или нет. Кстати, квалификация функции словом inline совсем не гарантирует, что функция окажется встроенной. inline - это не более, чем подсказка компилятору, компилятор имеет полное право не встраивать функцию. Есть несколько объективный ситуаций, где встраивание не может быть осуществлено в принципе (например, рекурсивный вызов). Но в большинстве случаев у компилятора нет никаких причин отказывать во встраивании, если это явно указано. И кстати же, наш любимый IAR, как раз, замечен в нехорошем поведении - отказываться встраивать. Там, типа, как объяснили из саппорта, используется неслабый эвристический анализатор, который и определяет, стОит ли встраивать функцию или нет. По мне так все эти эвристики идут лесом, еще на версиях 2.хх столько крови они попили, сколько было сломано копий в дискуссиях с саппортом, доказывая, что эвристика - эвристикой, но лучше меня компилятор не может знать, встраивать функцию или нет, и если я явно написал inline (или определил тело функции-члена в классе), то пусть встраивает, не выпендривается. В конечном итоге они признали, что в этом есть смысл, но от эвристики не отказались, мотивируя, что если все отдать на откуп пользователю, то он (пользователь) может и дров наломать - навстраивает функций, а потом будет недоволен, что код у программы здоровенный. Поэтому для тех, кто хочет стопудово встроить фунцию и настаивает на этом, ввели специальную прагму #pragma inline=forced, при ее использовании гарантируется, что функция будет встроена, а если встраивание невозможно, то будет выдана ошибка. Этот подход, в принципе, устраивает всех. Т.ч. как уже сказал, всегда лучше держать этот процесс под контролем, если надо, чтобы функция была встроена, то лучше позаботиться об этом: сделать ее определение доступной в точке компиляции, снабдить словом inline и, если компилятор поддерживает дополнительные средства типа упомянутой прагмы, то обязательно использовать их.
--------------------
«Отыщи всему начало, и ты многое поймёшь» К. Прутков
|
|
|
|
|
Feb 9 2006, 08:22
|

Йа моск ;)
     
Группа: Модераторы
Сообщений: 4 345
Регистрация: 7-07-05
Из: Kharkiv-city
Пользователь №: 6 610

|
Цитата(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.
--------------------
"Практика выше (теоретического) познания, ибо она имеет не только достоинство всеобщности, но и непосредственной действительности." - В.И. Ленин
|
|
|
|
|
Feb 9 2006, 11:55
|

кекс
     
Группа: Свой
Сообщений: 3 825
Регистрация: 17-12-05
Из: Киев
Пользователь №: 12 326

|
Цитата(Rst7 @ Feb 9 2006, 10:22)  Теперь представте это как автомат. Необходимо хранить указатели, состояние, и т.д. Каждое прерывание их необходимо загрузить, потом выгрузить. Весьма накладно, когда обработчик большой и сложный - как например TCP/IP. Поэтому, в том проекте, который мы обсуждаем, сделана такая многозадачка с использованием функций исключительно библиотеки. Т.е. что получается с сохранением контекста: Хорошо, имеется некая структура данных (массив и индексы), по сути в этой структуре хранится весь контекст приемника. В чем заключается сложность обращения к этой структуре из обработчика прерывания? Ни в чем. Непонятно для чего может потребоваться еще что-то сохранять, когда просто попав в обработчик прерывания можно работать непосредственно с этой структурой данных (не затрагивая других структур данных). Цитата 2. Функция setjmp сохраняет все localstore-регистры,оба указателя стека SP и Y, а также PC. В результате имеем полностью сохраненный контекст задачи в двух местах - часть регистров лежит в CSTACK задачи, остальное - в jmp_buf. Т.е. вы сделали подобие полноценного диспетчера задач, что ж похвально. Однако, зачем усложнять себе жизнь, когда предполагается всего три задачи, причем четко разграниченные по назначению. Даже если задача относительно ресурсоемкая, она может выполняться непосредственно в контексте прерывания. Если же по каким-то причинам не может - тогда можно организовать диспетчеризацию непосредственно в основном потоке программы, что в любом случае будет проще и не потребует дополнительной памяти на сохранение контекстов задач. простейшая диспетчеризация, внутри функций-задач рассматриваются условия "запуска", которые устанавливаются обработчиками прерываний и сбрасываются после отработки самими "задачами": Код for(;;) { task1(); task2(); task3(); }
|
|
|
|
|
Feb 9 2006, 11:56
|
Гуру
     
Группа: Свой
Сообщений: 2 712
Регистрация: 28-11-05
Из: Беларусь, Витебск, Строителей 18-4-220
Пользователь №: 11 521

|
Ещё раз спасибо всем ответившим. Перевариваю. Dxp спасибо за полное разъяснение по поводу прерываний. В общем-то я так и предпологал когда задавал вопрос, но ты полностью расставил точки над и. Исчерпывающее объяснение простым и лаконичным языком. RST по поводу использования твоего продукта (пока) в учебных целях, я тебе писал. Извини если письмо не дошло. Пока задавать тебе вопросы для меня бессмысленно. Мы разговариваем на разных языках. Вообще данный проект для меня несколько сложноват. Это когда отдельные строчки понимаешь, а всё вместе в картину не складывается. Мне прислали более простой и я сейчас заглядываю в оба. По поводу книжечек и обучения. Книжечки, - это сопровождающие PDF которые находятся в комплекте IAR. Они на англ. языке. Не скажу, что я его хорошо знаю, но я привык работать с PDF доками для микросхем и меня это вполне устраивает. Вывел их на печать с помощью проги FinePrint в виде книжечки. Переплёл их в типографии. По Си пользуюсь русской книгой Г.Шилда. Думаю знания придут по мере преобретения опыта. Пишу проект. Накопятся вопросы, - напишу. По iom640.h я знаю где искать его там нет. Пока пользуюсь iom2560.h Это практически одно и тоже по SFR и озу. В проекте выставил mega640. В любом случае эти ошибки вылезут только на этапе отладки
|
|
|
|
|
Feb 9 2006, 12:56
|

Йа моск ;)
     
Группа: Модераторы
Сообщений: 4 345
Регистрация: 7-07-05
Из: Kharkiv-city
Пользователь №: 6 610

|
Цитата(defunct @ Feb 9 2006, 13:55)  Хорошо, имеется некая структура данных (массив и индексы), по сути в этой структуре хранится весь контекст приемника. В чем заключается сложность обращения к этой структуре из обработчика прерывания? Ни в чем. Непонятно для чего может потребоваться еще что-то сохранять, когда просто попав в обработчик прерывания можно работать непосредственно с этой структурой данных (не затрагивая других структур данных). Можно, однако это раздувает код, т.к. все локальные переменные превращаются в статические. А само сохранение регистров (и последующее восстановление) остается и никуда не девается. А так убиваем двух зайцев - код уменьшаем, улучшаем читаемость этого всего дела, т.к. весь алгоритм лежит последовательно, а не по кусочкам, выполнить то, выполнть это... Цитата Код for(;;) { task1(); task2(); task3(); } Аналогично - все превращается в автомат и совершенно не читабельно. Тем более, что эта программа (это стек TCP/IP) как понимаете не самоцель, а лишь маленькая часть большой программы, а вы предлагаете весь софт писать вот так? ЗЫ Наверное, надо другую тему организовать под названием "Какие у сапожников взгляды на искусство"
--------------------
"Практика выше (теоретического) познания, ибо она имеет не только достоинство всеобщности, но и непосредственной действительности." - В.И. Ленин
|
|
|
|
|
Feb 9 2006, 13:42
|

кекс
     
Группа: Свой
Сообщений: 3 825
Регистрация: 17-12-05
Из: Киев
Пользователь №: 12 326

|
Цитата(Rst7 @ Feb 9 2006, 14:56)  Можно, однако это раздувает код, т.к. все локальные переменные превращаются в статические. А само сохранение регистров (и последующее восстановление) остается и никуда не девается. А так убиваем двух зайцев - код уменьшаем, улучшаем читаемость этого всего дела, т.к. весь алгоритм лежит последовательно, а не по кусочкам, выполнить то, выполнть это... Не все переменные превращаются в статические. Статическими будут только переменные, которые необходимо хранить между входами в обработчик прерывания. По расходу памяти если сравнить с Вашим способом - "те же консервы, вид сбоку"  Насчет улучшения читаемости - для кого? Углядеть алгоритм работы многозадачной системы - всегда сложнее чем однозадачной. Цитата Аналогично - все превращается в автомат и совершенно не читабельно. Тем более, что эта программа (это стек TCP/IP) как понимаете не самоцель, а лишь маленькая часть большой программы, а вы предлагаете весь софт писать вот так? я не предлагаю так писать софт, я его так пишу. for{;;} { DispatchLalala(); KernelDispatchblablabla(); DoSomethingElse(); sleep(); } Цитата ЗЫ Наверное, надо другую тему организовать под названием "Какие у сапожников взгляды на искусство" 
|
|
|
|
|
Feb 14 2006, 00:12
|
Гуру
     
Группа: Свой
Сообщений: 2 712
Регистрация: 28-11-05
Из: Беларусь, Витебск, Строителей 18-4-220
Пользователь №: 11 521

|
По ходу написания возникло ещё несколько вопросов, Вы уж извините. 1) Предположим у меня из rs232 в буффер грузятся различные структуры данных, их размер и тип определяются в момент загрузки. В процессе работы, обработанные структуры удаляются из буффера (удаляемая может находится внутри буфера). Я вижу два варианта работы с такими данными: а) Динамически размещаю каждую новую структуру и работаю с объявленными полями. Динамически удаляю. б) Один раз с пом. malloc выделить всю свободную память и работать с данными, как с байтами, производя преобразования на ходу вручную. Я как конченный ассемблерщик  склоняюсь конечно ко второму. Преимущество в простоте загрузки и очистки "мусора". Но прога в первом варианте будет выглядеть, по-моему, красивей.  Вопрос 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? Если да, то как вводить/выводить на разные устройства в одном проекте? Короче чуствую что я не знаю целую глобальную область и это удручает.
|
|
|
|
|
Feb 14 2006, 19:40
|
Гуру
     
Группа: Свой
Сообщений: 2 712
Регистрация: 28-11-05
Из: Беларусь, Витебск, Строителей 18-4-220
Пользователь №: 11 521

|
Цитата(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. Это понял. Спасибо.
|
|
|
|
|
Feb 16 2006, 09:30
|

Шаман
     
Группа: Модераторы
Сообщений: 3 064
Регистрация: 30-06-04
Из: Киев, Украина
Пользователь №: 221

|
Цитата(SasaVitebsk @ Feb 14 2006, 21:40)  Цитата(vet @ Feb 14 2006, 10:28)  2) Это пишется так: tip1->TimeLife--;
Это как раз понятно. А объявление я правильно сделал или правильнее как в коментарии? Компилятор при таком объявлении займёт память или нет и как это посмотреть? Как в комментарии лучше. Когда делаете явное приведение, это больше для себя, чтобы в будущем не забыть что имелось в виду. Компилятору всё равно (посмотреть можно в листинге или при отладке).
|
|
|
|
|
Feb 21 2006, 11:36
|

Adept
     
Группа: Свой
Сообщений: 3 469
Регистрация: 6-12-04
Из: Novosibirsk
Пользователь №: 1 343

|
Цитата(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.  Как сделать этот UBROF - пути все те же: скомпилить из сорца.
--------------------
«Отыщи всему начало, и ты многое поймёшь» К. Прутков
|
|
|
|
|
Feb 21 2006, 17:16
|
Гуру
     
Группа: Свой
Сообщений: 2 712
Регистрация: 28-11-05
Из: Беларусь, Витебск, Строителей 18-4-220
Пользователь №: 11 521

|
Цитата(dxp @ Feb 21 2006, 15:36)  Вопрос непонятен. Параметры (аргументы функции) именно передаются, а не хранятся. Схемы есть разные, в версиях 1.хх была одна схема, начиная с 2.хх - другая. В ней, насколько помню, все, что влазит в 8 регистров с 16 по 23, передается в них (с учетом выравнивания, ессно), остальное через стек. Все это хорошо документировано. Я имел ввиду не передачу функции данных в виде параметра, а хранение этих данных в озу. например если я использую адрес на int, то он укажет на младший или на старший байт? В одном месте нашёл вариант сохранения структуры. Похоже младшим байтом вперёд. Ладно это проверим.
|
|
|
|
|
Feb 22 2006, 08:13
|

Adept
     
Группа: Свой
Сообщений: 3 469
Регистрация: 6-12-04
Из: Novosibirsk
Пользователь №: 1 343

|
Цитата(SasaVitebsk @ Feb 21 2006, 23:16)  Я имел ввиду не передачу функции данных в виде параметра, а хранение этих данных в озу. например если я использую адрес на int, то он укажет на младший или на старший байт? В одном месте нашёл вариант сохранения структуры. Похоже младшим байтом вперёд. Ладно это проверим. Указатель содержит адрес. Адрес - это просто местоположение в памяти. Обычно измеряется в байтах (8-битных). К размеру объекта, расположенного по адресу, сам адрес никакого отношения не имеет. К размеру имеет отношение тип указателя. Собственно, он (тип) для того и задается, чтобы компилятор мог правильно работать с объектом, адресуемым указателем. Для писания на С/С++, если не использовать хаки и всякие низкоуровневые финты ушами, а также если опустить вопросы отладки, ровно пофигу, как объекты располагаются в памяти физически. Комплятор их положил, пусть с ними разбирается. Конкретно EWAVR - да кладет объекты младшими байтами по младшим адресам.
--------------------
«Отыщи всему начало, и ты многое поймёшь» К. Прутков
|
|
|
|
|
Mar 1 2006, 11:07
|
Гуру
     
Группа: Свой
Сообщений: 2 712
Регистрация: 28-11-05
Из: Беларусь, Витебск, Строителей 18-4-220
Пользователь №: 11 521

|
DXP ты всё правильно понимаешь, но я писал на ASMе даже для IBM, поэтому писать "без финтов ушами" ещё надо научиться.  А надо. Очень надо. Я хочу чтобы прога получилась минимально зависима от кристала. Теперь новый вопрос. Если Вы не возражаете. Я хочу во флеш разместить несколько массивов. И масив указателей на эти массивы. Вот такое объявление будет правильным? Если нет, то как? 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};
|
|
|
|
|
Mar 1 2006, 14:04
|

Adept
     
Группа: Свой
Сообщений: 3 469
Регистрация: 6-12-04
Из: Novosibirsk
Пользователь №: 1 343

|
Цитата(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 и так константнее некуда.  Я обычно использовал вид __flash byte Array[] = { .... }; Оператор разименовывания ('*') при объявлении массива писать не надо, если только не хотите объявить массив указателей. Вообще, я понял, что Вам надо типа двумерного массива - ну так и объявите явно массив массивов, зачем эти финты с адресами? И памяти меньше скушает.
--------------------
«Отыщи всему начало, и ты многое поймёшь» К. Прутков
|
|
|
|
|
Mar 1 2006, 18:33
|

Шаман
     
Группа: Модераторы
Сообщений: 3 064
Регистрация: 30-06-04
Из: Киев, Украина
Пользователь №: 221

|
Цитата(defunct @ Mar 1 2006, 20:26)  Цитата(dxp @ Mar 1 2006, 16:04)  Во-первых, const не нужен - __flash и так константнее некуда.  Я обычно использовал вид А почему в таком случае нельзя просто const использовать, все-таки const это из стандарта ansi-c, а __flesh всего-лишь приблуда от IAR. В этом случае компилятор\линкер могут потребовать внешней ROM, где и будут храниться Ваши константы. Что касается стандартов, то они хороши для PC, а для embedded приложений с невероятным количеством особенностей целевых архитектур "приблуды" зачастую необходимы.
|
|
|
|
|
Mar 2 2006, 18:13
|
Гуру
     
Группа: Свой
Сообщений: 2 712
Регистрация: 28-11-05
Из: Беларусь, Витебск, Строителей 18-4-220
Пользователь №: 11 521

|
Цитата(dxp @ Mar 1 2006, 18:04)  Во-первых, const не нужен - __flash и так константнее некуда.  Я обычно использовал вид __flash byte Array[] = { .... }; Оператор разименовывания ('*') при объявлении массива писать не надо, если только не хотите объявить массив указателей. Вообще, я понял, что Вам надо типа двумерного массива - ну так и объявите явно массив массивов, зачем эти финты с адресами? И памяти меньше скушает. Спецы как всегда зрят в корень!  Их не обманешь. Уточню картину.  С учётом Ваших поправок. 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. И ещё ряд вопросов у меня к нему  Где скачать?
|
|
|
|
|
Mar 2 2006, 18:19
|
Знающий
   
Группа: Свой
Сообщений: 550
Регистрация: 16-06-04
Из: Казань
Пользователь №: 32

|
Цитата Во вторых если я хотел сделать массив указателей правильно ли я объявил их ( &...) . Компилятор не выдал мне ошибки Уже писал про это на прошлой странице.
--------------------
Главная линия этого опуса ясна мне насквозь!
|
|
|
|
|
Mar 2 2006, 20:48
|
Участник

Группа: Свой
Сообщений: 41
Регистрация: 16-02-05
Пользователь №: 2 688

|
Цитата(SasaVitebsk @ Mar 2 2006, 20:13)  void __flash *symbol[3] = {&fnt0_6x8,&fnt1_8x8,&fnt2_10x10,&fnt3_11x13}; в левой части надобы ставиить 4, т.к. кол-во элементов массива = 4.
|
|
|
|
|
Mar 3 2006, 05:30
|

Adept
     
Группа: Свой
Сообщений: 3 469
Регистрация: 6-12-04
Из: Novosibirsk
Пользователь №: 1 343

|
Цитата(IgorKossak @ Mar 2 2006, 00:33)  Цитата(defunct @ Mar 1 2006, 20:26)  Цитата(dxp @ Mar 1 2006, 16:04)  Во-первых, const не нужен - __flash и так константнее некуда.  Я обычно использовал вид А почему в таком случае нельзя просто const использовать, все-таки const это из стандарта ansi-c, а __flesh всего-лишь приблуда от IAR. В этом случае компилятор\линкер могут потребовать внешней ROM, где и будут храниться Ваши константы. Добавлю: либо разместит объекты, объявленные как const, в ОЗУ. А его мало и жалко его тратить под неизменяемые данные.
--------------------
«Отыщи всему начало, и ты многое поймёшь» К. Прутков
|
|
|
|
|
Mar 3 2006, 05:43
|

Adept
     
Группа: Свой
Сообщений: 3 469
Регистрация: 6-12-04
Из: Novosibirsk
Пользователь №: 1 343

|
Цитата(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* - это Вы обманули компилятор (и себя тоже  ). Этим самым Вы отключили контроль типов со стороны компилятора и взяли всю ответственность за правильность работы на себя. Компилятор тут "умыл руки". Не советую так делать. Вообще, найдется очень немного случаев, когда без void* действительно сложно обойтись и он оправдан. Но рассматриваемый случай совсем не этот. Я не очень понимаю, что Вы хотите иметь в конечном итоге, но учитывая, что эти массивы как-то используются в каких-то функциях, и принимая во внимание тот факт, что раз массивы разные, то и функции с ними работают разные, - реализовать работу с ними через массив указателей на функции. Цитата(SasaVitebsk @ Mar 3 2006, 00:13)  И ещё отдельный вопрос. При запуске мой компилятор указывает мне на отсутствие одного из файлов DLL. И ещё ряд вопросов у меня к нему  Где скачать? На какую DLL? Никогда ничего подобного не встречал.
--------------------
«Отыщи всему начало, и ты многое поймёшь» К. Прутков
|
|
|
|
|
Mar 3 2006, 11:10
|
Участник

Группа: Свой
Сообщений: 22
Регистрация: 9-12-04
Пользователь №: 1 425

|
Сообщение об отсутствии nwtdavr.dll наблюдал в результате "лечения" IAR версии 3.20 под XP. Появляется при каждый раз при старте компилятора. По моим наблюдениям на работоспособность не влияет.
|
|
|
|
|
Mar 5 2006, 16:22
|
Гуру
     
Группа: Свой
Сообщений: 2 712
Регистрация: 28-11-05
Из: Беларусь, Витебск, Строителей 18-4-220
Пользователь №: 11 521

|
Цитата(SasaVitebsk @ Feb 14 2006, 23:40)  Цитата(vet @ Feb 14 2006, 10:28)  2) Это пишется так: tip1->TimeLife--;
Это как раз понятно. А объявление я правильно сделал или правильнее как в коментарии? Компилятор при таком объявлении займёт память или нет и как это посмотреть? Передаю привет "vet". Даже если он этот топик и не увидит. Вот теперь не то чтобы понятно, но чуть-чуть приблизился к пониманию. А ведь знающие люди месяц назад писали. Вот баран самоуверенный. Теперь более 500 строк текста перелопачиваю!  Ещё вопрос. Хочу в подпрограмму передать ссылку. Это массив расположенный в структуре. На неё имеется указатель. Т.е. к самому массиву я обращаюсь 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
|
|
|
|
|
Mar 5 2006, 22:20
|
Гуру
     
Группа: Свой
Сообщений: 2 712
Регистрация: 28-11-05
Из: Беларусь, Витебск, Строителей 18-4-220
Пользователь №: 11 521

|
"vet" твой ответ добавил мне уверенности и синтаксическую ошибку я нашёл. Пасибо тебе в очередной раз. Правда синтаксически верная программа не всегда работает.  У меня в структуре стоял массив. Поэтому синтаксически правильно было передавать &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
Сообщение отредактировал SasaVitebsk - Mar 5 2006, 22:30
|
|
|
|
|
Mar 6 2006, 00:36
|

кекс
     
Группа: Свой
Сообщений: 3 825
Регистрация: 17-12-05
Из: Киев
Пользователь №: 12 326

|
Цитата(SasaVitebsk @ Mar 6 2006, 00:20)  И ещё один вопрос: Как мне найти начало свободной памяти для динамических данных? Я по аналогии с Паскалем пробовал сделать так А зачем находить начало? Просто выделяйте память под структуру.. Код MYSTRUCT *mystruct; mystruct = malloc( sizeof( MYSTRUCT) ); если вас интересует численное значение адреса, его можно получить например так: Код int i; i = (int)malloc( 0 );
|
|
|
|
|
Mar 7 2006, 01:26
|
Гуру
     
Группа: Свой
Сообщений: 2 712
Регистрация: 28-11-05
Из: Беларусь, Витебск, Строителей 18-4-220
Пользователь №: 11 521

|
Чувствую, что я скоро всем надоем, и отвечать мне никто не будет. Но у меня уже есть прогресс! Я написал большую часть проги и откомпиллил её без ошибок!!! Вот такой вот вопрос: Есть объявление функции. В качестве параметра 32 битное число. Я хочу использовать его побайтно. Могу я сделать так? union uint32_u { uint32_t dw; uint8_t bt[4]; }; в вызове использовать типа: uint32_u addr; а при обращении использовать unsigned long int addr. Или как поступить в данном случае. Как обратится к байтам длинного числа не пребегая к операциям типа ">> 8 ...."
|
|
|
|
|
Mar 7 2006, 02:18
|

кекс
     
Группа: Свой
Сообщений: 3 825
Регистрация: 17-12-05
Из: Киев
Пользователь №: 12 326

|
Цитата(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] = ...;
Сообщение отредактировал defunct - Mar 7 2006, 02:22
|
|
|
|
|
Mar 7 2006, 20:56
|

Частый гость
 
Группа: Свой
Сообщений: 175
Регистрация: 26-01-06
Из: Sevastopol
Пользователь №: 13 664

|
Цитата(SasaVitebsk @ Mar 7 2006, 03:26)  Или как поступить в данном случае. Как обратится к байтам длинного числа не пребегая к операциям типа ">> 8 ...." Кстати говоря, с большой долей вероятности при достаточном уровне оптимизации компилятор сам не будет использовать сдвиги, а просто возьмет нужный байт из регистра или из памяти. Во всяком случае, IAR так делает. Вообще, очень полезно бывает смотреть на сгенерированный компилятором код, чтобы что-то там оптимизировать (если это хочется сделать). Пример: Код unsigned char CRC(...) { int crc; ... return crc ? 1 : 0; // 0 - все в порядке, не ноль - ошибка } Дает совершенно правильный, но не слишком оптимальный для AVR код (на IAR). Вычисление кода возврата по return компилируется в 10 байтов. Можно улучшить ситуацию, записав вот так: Код return (crc | (crc >> 8)); // 0 - все в порядке, не ноль - ошибка При видимом усложнении кода получаем вместо 10-ти уже 6 байтов. Неплохо. Но самое интересное будет, если переставить местами операнды и добавить лишние 0xff: Код return ((crc >> 8) | (crc & 0xff)); // 0 - все в порядке, не ноль - ошибка Это компилируется всего в 4 байта, несмотря на увеличение количества операций. Поэтому крайне рекомендуется при необходимости писать оптимально для конкретного процессора изучать генерируемый компилятором код. Иногда можно обнаружить совершенно неочевидные вещи.
|
|
|
|
|
Mar 7 2006, 22:22
|

кекс
     
Группа: Свой
Сообщений: 3 825
Регистрация: 17-12-05
Из: Киев
Пользователь №: 12 326

|
Цитата(osnwt @ Mar 7 2006, 22:56)  Код return ((crc >> 8) | (crc & 0xff)); // 0 - все в порядке, не ноль - ошибка Это компилируется всего в 4 байта, несмотря на увеличение количества операций. Поэтому крайне рекомендуется при необходимости писать оптимально для конкретного процессора изучать генерируемый компилятором код. Иногда можно обнаружить совершенно неочевидные вещи. на ассемблере для этой задачи (0- все в порядке, не 0 - ошибка crc16) требуется всего одна команда or al, ah (2 байта), поэтому при необходимости получить оптимальный код, неплохо бы изучить ассемблер конкретного процессора.. Вопрос, лишь в том оправдается ли затраченное время (немалое) на получение оптимального кода для конкретного процессора?
|
|
|
|
|
Mar 7 2006, 22:41
|

Частый гость
 
Группа: Свой
Сообщений: 175
Регистрация: 26-01-06
Из: Sevastopol
Пользователь №: 13 664

|
Цитата(defunct @ Mar 8 2006, 00:22)  Цитата(osnwt @ Mar 7 2006, 22:56)  Код return ((crc >> 8) | (crc & 0xff)); // 0 - все в порядке, не ноль - ошибка Это компилируется всего в 4 байта, несмотря на увеличение количества операций. на ассемблере для этой задачи (0- все в порядке, не 0 - ошибка crc16) требуется всего одна команда or al, ah (2 байта), поэтому при необходимости получить оптимальный код, неплохо бы изучить ассемблер конкретного процессора.. Вопрос, лишь в том оправдается ли затраченное время (немалое) на получение оптимального кода для конкретного процессора? Одна команда - это два байта. Вторые два байта - это, собственно, ret. Фрагмент листинга компилятора: // 159 // 160 // returned 0 means CRC is OK // 161 return ((crc >> 8) | (crc & 0xff)); OR R16, R17 Однозначно ассемблер надо знать, иначе смысла смотреть на генерируемый код нет никакого. А насчет оправдания затрат - во первых, это надо сделать примерно раз и дальше использовать везде. Во вторых, это дает результат, и не такой малый. Скажем, компилированная GCC ассемблерная часть USB драйвера от Objective Development получается на 166 байтов короче, чем она же, но скомпилированная IAR. А на написанном мной куске того же объема результат прямо противоположный, и при этом я не оптимизировал тот свой код вручную. Это - результат вполне определенной оптимизации исходника USB под GCC, о чем вполне определенно пишет автор в readme IAR порта (не уверен, появился ли тот порт на их сайте или еще нет, так как последняя версия лежит у меня, всё ход не дойдет ее дотестировать). В третьих, всё подряд оптимизировать не имеет смысла. Но в ряде случаев это просто необходимо. Когда мой бут вылез за пределы 4-х кило на 200 байтов, мне пришлось приложить ряд усилий, чтобы вернуть его обратно. При этом ничем не было пожертвовано, и код не потерял читабельности. Правда, пришлось переписать часть кода Atmel, да и свой немного поправить. Зато всё осталось на C, и поддерживать его будет проще. Я сам был поборником ассемблера, ибо в свое время общался больше чем с десятком-полутора разных вариантов. Но с некоторых пор мнение изменил. Проще поддерживать все же C код. А на ассемблер надо переносить только критически важные участки: ISR и т.п. Вот это реально будет экономить время на поддержке.
|
|
|
|
|
Mar 8 2006, 11:27
|

Частый гость
 
Группа: Свой
Сообщений: 175
Регистрация: 26-01-06
Из: Sevastopol
Пользователь №: 13 664

|
Цитата Я вот тут подумал, чем надеяться на оптимизация компилятора не проще ли просто писать, то что требуется: Код return crc != 0 Увы, нет. Сравните с предыдущим листингом: Код // 160 // returned 0 means CRC is OK // 161 // return ((crc >> 8) | (crc & 0xff)); // 162 return crc != 0; OR R16, R17 BREQ ??crcApplicationCheck_1 LDI R16, 1 RJMP ??crcApplicationCheck_2 ??crcApplicationCheck_1: LDI R16, 0 ??crcApplicationCheck_2: Логическая операция должна вернуть 0 или 1. А побитовая - что угодно, лишь бы не ноль. Опять же, если не стоит задачи оптимизации, то писать надо то, что надо. Но иногда приходится извернуться, и это верно для любых компиляторов. ЗЫ. В приведенном примере я сам поражен: и это - максимальная оптимизация в IAR??? Ведь LDI R16, 0 - совершенно излишняя команда в данном случае.
Сообщение отредактировал osnwt - Mar 8 2006, 11:29
|
|
|
|
|
Mar 8 2006, 12:31
|

кекс
     
Группа: Свой
Сообщений: 3 825
Регистрация: 17-12-05
Из: Киев
Пользователь №: 12 326

|
Цитата(osnwt @ Mar 8 2006, 13:27)  Увы, нет. Сравните с предыдущим листингом: После того как написал пост - проверил, заметил, потому и удалил пост... Цитата ЗЫ. В приведенном примере я сам поражен: и это - максимальная оптимизация в IAR??? Ведь LDI R16, 0 - совершенно излишняя команда в данном случае. Да поразительно работает оптимизация..
|
|
|
|
|
Mar 8 2006, 13:59
|

Частый гость
 
Группа: Свой
Сообщений: 175
Регистрация: 26-01-06
Из: Sevastopol
Пользователь №: 13 664

|
Цитата(defunct @ Mar 8 2006, 14:31)  После того как написал пост - проверил, заметил, потому и удалил пост... А я думаю: что за глюки форума? В почте - вот оно, на сайте - нет ничего  Потом уж догадался о природе вещей, но ответить Чемберлену-то нужно! (без обид - шутка - после бессонной ночи сборки очередного девайса сижу вот такой:  ).
|
|
|
|
|
Mar 11 2006, 17:24
|
Гуру
     
Группа: Свой
Сообщений: 2 712
Регистрация: 28-11-05
Из: Беларусь, Витебск, Строителей 18-4-220
Пользователь №: 11 521

|
Беседа была интересная.  И я планирую проверить некоторые вещи в плане генерации. Пока я ещё не завершил написание проекта. Обычно я сначала пишу всё, и только потом вылизываю. И всётаки вопрос: Если применяешь с компиллер, то оптимизация описанным путём, - это же явная привязка к конкретному компилятору. И даже, вероятней всего, к его конкретной версии.  Второй вопрос. Зачем, конкретно Вы, используете два разных компилятора. Хотя бы коротко. Третий. Насколько сложно программу написанную на IAR "заставить" компилится на GCC. Может у кого есть спец. ".h" файлы.
|
|
|
|
|
Mar 11 2006, 19:47
|

Частый гость
 
Группа: Свой
Сообщений: 175
Регистрация: 26-01-06
Из: Sevastopol
Пользователь №: 13 664

|
Если вопросы ко мне - попробую ответить. Цитата(SasaVitebsk @ Mar 11 2006, 19:24)  Если применяешь с компиллер, то оптимизация описанным путём, - это же явная привязка к конкретному компилятору. И даже, вероятней всего, к его конкретной версии.  Верно, хотя это не привязка, так как таким образом оптимизированный код скомпилируется правильно и другим компилятором. Может, не столь же эффективно - но правильно. С другой стороны, то, что пишется под AVR, едва ли будет компилироваться десятками разных компиляторов под десятком платформ. С вероятностью процентов 95% этот код вообще не выйдет за пределы компьютера автора (авторов). Потому при сохранении общей наглядности кода на C такая оптимизация имеет право на жизнь, когда есть проблемы по быстродействию или объему. Мне для впихивания навороченного USB крипто-загрузчика в 4 килобайта бут-блока меги32 пришлось соптимизировать примерно 200 лишних байтов. Ясное дело, что не только таким способом - многое пришлось переписать по другому. Но результат был достигнут. Причем, переписать неочевидным путем. Скажем, алгоритм дешифровки использует сравнение 4-х байтов, загружаемых через последовательные *p++ и сравниваемые с 4-мя байтами сигнатуры побайтно. Казалось бы - самое оптимальное, раз байты так извлекаются. НЕТ. Более оптимально оказалось сделать так (непортабельно из за выравнивания на других платформах, но это и не нужно): v = *(unsigned long *)p; p += 4; а потом сравнить v с unsigned long сигнатурой. Это дало приличную экономию, учитывая, что сигнатура в данном случае - константа. Цитата Второй вопрос. Зачем, конкретно Вы, используете два разных компилятора. Хотя бы коротко.  Лично я не использую, я использую IAR. Однако, версия USB драйвера для AVR написана под GCC компилятор и ассемблер, и под них оптимизирована автором. Потому мне пришлось сравнить эти два компилятора, чтобы портировать все на IAR. Это оказалось весьма несложно - C код вообще не менялся, практически. Ассемблерный - тоже. Были символические нюансы по особенностям типа "PROGMEM меняется на __flash", и т.п. А также соглашения по вызову ассемблерных функций из C и оформлению процедур обработки прерываний. И вот при всём этом при максимальных оптимизациях C-часть драйвера компилируется под IAR примерно в 750 байтов, а под GCC - 550. 200 байтов проигрывает IAR. Однако, алгоритм дешифровки, никак не оптимизированный, скомпилированный под IAR получился около 1000 байтов, а под GCC - 1400. Вот мы уже впереди. Так что особенности того компилятора, под который пишем, знать всё же полезно. Однако, в худшем случае переносимый код скомпилируется и другим компилятором. А вот всякие ассемблерные вставки с разным их синтаксисом и особенностями распределения регистров уже совсем непереносимы. Для USB драйвера пришлось переписать пару процедур, вызываемых из C, именно из за другого соглашения по регистрам. Цитата Третий. Насколько сложно программу написанную на IAR "заставить" компилится на GCC. Может у кого есть спец. ".h" файлы. Если программа написана "правильно", то очень просто. Особенности относятся, как правило, к расширениям языка для поддержки FLASH, SFR, и т.п. Для примера приведу файл iarcompat.h из неопубликованной пока версии USB драйвера, который включается сишной и ассемблерной частями вместо "avr/*.h" файлов, используемых GCC. Остальные изменения не сильно принципиальны. Главное - это по всем спорным вопросам расширений писать макро, которые можно потом просто переопределить. Код /* Name: iarcompat.h * Project: AVR USB driver * Author: Christian Starkjohann * Creation Date: 2006-03-01 * Tabsize: 4 * Copyright: (c) 2006 by OBJECTIVE DEVELOPMENT Software GmbH * License: Proprietary, free under certain conditions. See Documentation. * This Revision: $Id$ */
/* General Description: This header is included when we compile with the IAR C-compiler and assembler. It defines macros for cross compatibility between gcc and IAR-cc.
Thanks to Oleg Semyonov for his help with the IAR tools port! */
#ifndef __iarcompat_h_INCLUDED__ #define __iarcompat_h_INCLUDED__
#if defined __IAR_SYSTEMS_ICC__ || defined __IAR_SYSTEMS_ASM__
/* Enable bit definitions */ #ifndef ENABLE_BIT_DEFINITIONS # define ENABLE_BIT_DEFINITIONS 1 #endif
/* Include IAR headers */ #include <ioavr.h> #ifndef __IAR_SYSTEMS_ASM__ # include <inavr.h> #endif
#define __attribute__(arg) #define IAR_SECTION(section) @ section
#ifndef USB_BUFFER_SECTION # define USB_BUFFER_SECTION "TINY_Z" /* if user has not selected a named section */ #endif
#ifdef __IAR_SYSTEMS_ASM__ # define __ASSEMBLER__ #endif
#ifdef __HAS_ELPM__ # define PROGMEM __farflash #else # define PROGMEM __flash #endif
#define PRG_RDB(addr) (*(PROGMEM char *)(addr))
/* The following definitions are not needed by the driver, but may be of some * help if you port a gcc based project to IAR. */ #define cli() __disable_interrupt() #define sei() __enable_interrupt() #define wdt_reset() __watchdog_reset()
#endif /* defined __IAR_SYSTEMS_ICC__ || defined __IAR_SYSTEMS_ASM__ */ #endif /* __iarcompat_h_INCLUDED__ */
|
|
|
|
|
Mar 11 2006, 21:51
|

Профессионал
    
Группа: Свой
Сообщений: 1 065
Регистрация: 8-10-05
Из: Kiev, UA
Пользователь №: 9 380

|
Цитата Третий. Насколько сложно программу написанную на IAR "заставить" компилится на GCC. Может у кого есть спец. ".h" файлы. Немножко дополню. Кроме указанных osnwt макросов, по другому объявляются прерывания, что легко обходиться с помощью #ifdef #else #endif. С флэшью gcc обходиться не так непринужденно как IAR, т.е. хитрые фокусы со всякими связанными списками во флэши, требуют некоторых ухищрений. Вообще желательно писать с Шилдтом на столе, не привязываясь к соблазнительным фичам конкретного компилятора, и тогда переносимость будет вполне приличная.
--------------------
Вони шукають те, чого нема, Щоб довести, що його не існує.
|
|
|
|
|
Mar 11 2006, 22:05
|

Частый гость
 
Группа: Свой
Сообщений: 175
Регистрация: 26-01-06
Из: Sevastopol
Пользователь №: 13 664

|
Цитата(beer_warrior @ Mar 11 2006, 23:51)  Кроме указанных osnwt макросов, по другому объявляются прерывания, что легко обходиться с помощью #ifdef #else #endif. Да. Я просто привел то, что реально использовалось в драйвере. А прерывание там находится в ассемблерной части, что тоже потребовало определенных #if-ов для корректного его описания. Цитата С флэшью gcc обходиться не так непринужденно как IAR Как пояснил Кристиан, gcc не рассчитывался на гарвардскую архитектуру процессора. Потому чтобы поддержать в нем AVR, пришлось сделать немалые хаки. А отсутствие возможности прямой работы с переменными во флэше (см. выше макро для чтения байта) было причиной того, что автор драйвера не согласился поддержать работу с переменными дескрипторами уникального серийного номера устройства вместо константных.
|
|
|
|
|
Mar 11 2006, 23:51
|

кекс
     
Группа: Свой
Сообщений: 3 825
Регистрация: 17-12-05
Из: Киев
Пользователь №: 12 326

|
Цитата(SasaVitebsk @ Mar 11 2006, 19:24)  И всётаки вопрос: Если применяешь с компиллер, то оптимизация описанным путём, - это же явная привязка к конкретному компилятору. И даже, вероятней всего, к его конкретной версии.  вопроса-то вроде и нет, Вы сами все верно сказали.  Еще дополню относительно третьего вопроса.. Цитата Третий. Насколько сложно программу написанную на IAR "заставить" компилится на GCC. Может у кого есть спец. ".h" файлы. Зависит от привязки к компилятору. Чем больше текста написано на plain C, без использования прагм, ключевых слов характерных для конкретного компилятора, тем проще будет "заставить" код компилироваться другим компилятором. Хотя с ключевыми словами просто - можно создать хедер с соответствеющими макросами и все (пример osnwt).. А прагм не так уж много.. ;> Существенное отличие между IAR и GCC на мой взгляд заключается наверное в том, что GCC основан на стандарте C99 и позволяет определять переменные только в начале функций, т.о. выражения вида for(int i; i=0;....}, прекрасно воспринимаемые компилятором IAR, в gcc компилироваться не будут. Соответственно лучше сразу ими не пользоваться и в IAR... Цитата Мне для впихивания навороченного USB крипто-загрузчика в 4 килобайта бут-блока меги32 пришлось соптимизировать примерно 200 лишних байтов. Ясное дело, что не только таким способом - многое пришлось переписать по другому. Но результат был достигнут. Я бы "навороченный" криптозагрузчик писал бы полностью на ассемблере, если бы мне потребовался действительно небольшой объем. А так (с упованием на оптимизацию компилятора C) могу только сказать "овчинка выделки не стоит". Появится новая версия компилятора и код, вполне возможно, уже не втиснется в 4k.
Сообщение отредактировал defunct - Mar 11 2006, 23:58
|
|
|
|
|
Mar 12 2006, 01:38
|

Частый гость
 
Группа: Свой
Сообщений: 175
Регистрация: 26-01-06
Из: Sevastopol
Пользователь №: 13 664

|
Цитата(defunct @ Mar 12 2006, 01:51)  Я бы "навороченный" криптозагрузчик писал бы полностью на ассемблере, если бы мне потребовался действительно небольшой объем. А так (с упованием на оптимизацию компилятора C) могу только сказать "овчинка выделки не стоит". Появится новая версия компилятора и код, вполне возможно, уже не втиснется в 4k. Дело в том, что 2 килобайта - это USB драйвер, еще килобайт - это дешифровщик. Тем самым мы уже вылезли за пределы 2Кбайт. Остальное - это протокольная часть со всякими штуками. Естественно, что можно все написать на асме, если писать с нуля. Но переписывать 3/4 кода просто из спортивного интереса я не могу себе позволить, поскольку это не моя работа, а всего лишь хобби. Я предпочту уделить это время написанию собственно того, что можно загружать этим загрузчиком, к примеру. Кроме того, бут - это такая штука, которая едва ли будет часто меняться или развиваться. Обычно базовых функций хватает. Скорее, снимут с производства AVR в пользу чего-то другого, чем потребуется что-то менять. Новый компилятор едва ли будет давать бОльший код, чем старый. При этом нормальная практика в серьезных проектах - хранить средства разработки под версионным контролем наряду с собственно разработкой. Так что всегда есть возможность откомпилироваться старым. Иными словами, все определяется потребностями и возможностями. В рамках имеющихся потребностей мне хватило имеющихся возможностей реализовать желаемое с минимальными затратами времени (а переписывание загрузчика или USB драйвера займет на порядок-два больше усилий на отладку и поиск трудноуловимых глюков при малейшей ошибке). В другой ситуации будет лучше все писать с нуля и на асме, но не в этой. Естественно, это всё IMHO.
|
|
|
|
|
Mar 12 2006, 01:53
|

кекс
     
Группа: Свой
Сообщений: 3 825
Регистрация: 17-12-05
Из: Киев
Пользователь №: 12 326

|
Цитата(osnwt @ Mar 12 2006, 03:38)  Кроме того, бут - это такая штука, которая едва ли будет часто меняться или развиваться. Обычно базовых функций хватает. Скорее, снимут с производства AVR в пользу чего-то другого, чем потребуется что-то менять. Новый компилятор едва ли будет давать бОльший код, чем старый. При этом нормальная практика в серьезных проектах - хранить средства разработки под версионным контролем наряду с собственно разработкой. Так что всегда есть возможность откомпилироваться старым. Вот именно, и это как раз тот случай когда применение ассемблера действительно оправдано! Цитата(osnwt @ Mar 12 2006, 03:38)  Дело в том, что 2 килобайта - это USB драйвер .... В другой ситуации будет лучше все писать с нуля и на асме, но не в этой. Естественно, это всё IMHO. Возможно, а возможно было бы целесообразно применить код Igor Cesko о котором Вы упоминали...
|
|
|
|
|
Mar 12 2006, 11:23
|

Профессионал
    
Группа: Свой
Сообщений: 1 065
Регистрация: 8-10-05
Из: Kiev, UA
Пользователь №: 9 380

|
2 osnwt Цитата Как пояснил Кристиан, gcc не рассчитывался на гарвардскую архитектуру процессора. Потому чтобы поддержать в нем AVR, пришлось сделать немалые хаки. Вообще-то достаточно странно.Ведь по идее компилеру должно быть наплевать задан сегмент кода программно или аппаратно. Тем более гарвардских процессоров добрая половина. Интересно сравнить например с возможностями SDCC. 2defunct Цитата Существенное отличие между IAR и GCC на мой взгляд заключается наверное в том, что GCC основан на стандарте C99 и позволяет определять переменные только в начале функций, т.о. выражения вида for(int i; i=0;....}, прекрасно воспринимаемые компилятором IAR, в gcc компилироваться не будут. Соответственно лучше сразу ими не пользоваться и в IAR... Ну во-первых обьявления в операторе for это как раз С99, а во-вторых gcc поддерживает 4 стандарта С89-С99 и их расширения для GNU, тут как раз проблемы нет - С99 и вперед. 2SasaVitebsk Конечная цель любой отпимизации свести код либо к минимальному размеру, либо к максимальной скорости, как правило несложные неоптимальности - использование промежуточных переменных, изменнение обычной переменной в цикле, чрезмерно сложные ветвления соптимизируються одинаково ибо приемы решения этих проблем общие. По ссылке ниже - анализ компиляторов с примерами - примените их к AVRу и получите представление о сходстве и различии в их поведении. http://www.rsdn.ru/article/devtools/devtools.xml
--------------------
Вони шукають те, чого нема, Щоб довести, що його не існує.
|
|
|
|
|
Mar 12 2006, 14:23
|

Частый гость
 
Группа: Свой
Сообщений: 175
Регистрация: 26-01-06
Из: Sevastopol
Пользователь №: 13 664

|
Цитата(defunct @ Mar 12 2006, 03:53)  Цитата(osnwt @ Mar 12 2006, 03:38)  Кроме того, бут - это такая штука, которая едва ли будет часто меняться или развиваться.
Вот именно, и это как раз тот случай когда применение ассемблера действительно оправдано! Если бы у меня стояла задача минимизировать цену в большой партии устройств, то я бы, возможно, и попытался. Однако, код USB занимает больше 1.5 килобайт. Втиснуть в оставшееся дешифровальщик и протокольную часть было бы проблематично, хотя шанс и есть. Но в моей ситуации это была бы неоправданная трата времени. Цитата Цитата(osnwt @ Mar 12 2006, 03:38)  Дело в том, что 2 килобайта - это USB драйвер .... В другой ситуации будет лучше все писать с нуля и на асме, но не в этой. Естественно, это всё IMHO.
Возможно, а возможно было бы целесообразно применить код Igor Cesko о котором Вы упоминали... Цитата с сайта автора avr-usb (выделено мною): Цитата Advantages over other Firmware-Only Implementations
A similar driver for the AVR series of microcontrollers is available from Igor Češko. Our USB driver has the following advantages over Igor's driver:
* All customizable code written in ANSI-C and thus easier to maintain. * Modular concept: easier to integrate into existing designs. * Slightly smaller code size in spite of high level language modules. * Faster: All encoding/decoding (USB requires NRZI coding and bit stuffing) is done in real-time, not in the main loop after storing away the raw data stream. * AVR-USB comes with a free shared Vendor- / Product-ID pair. * The level of standards conformance is documented (description of limitations and potential problems). * Available for free with Open Source license. (See the file License.txt in the distribution.) И, что интересно, что это правда - ассемблерный вариант занимал больший объем, не столь стабилен и совершенно недокументирован. А в этом варианте вчера была вычищена одна логическая бага, которая мешала при очень интенсивном обмене данными с хостом при наличии других устройств на том же контроллере. И теперь проблем я пока не вижу. Цитата(beer_warrior @ Mar 12 2006, 13:23)  Вообще-то достаточно странно.Ведь по идее компилеру должно быть наплевать задан сегмент кода программно или аппаратно. Не вижу ничего странного. При различных методах адресации кода и данных нужно помнить разные типы указателей или использовать нечто вроде generic типа указателя в IAR, который крайне неэффективен. Доступ к данным тоже осуществляется совершенно по разному. Вроде, мелочь - но, видимо, существенная.
|
|
|
|
|
Mar 16 2006, 21:19
|
Гуру
     
Группа: Свой
Сообщений: 2 712
Регистрация: 28-11-05
Из: Беларусь, Витебск, Строителей 18-4-220
Пользователь №: 11 521

|
Похоже даже максимального уровня оптимизации мне будет не достаточно. То что я увидел в обработчике самого быстрого прерывания (245мкс), трудно назвать программой в здравом уме. Читал некоторые посты по оптимизации. Применил всё что я вычитал оттуда. Также захотел "облегчить работу компилятора" заменив ряд логических операций, - таблицей перекодировки. Теперь даже не знаю что былобы лучше. И ещё скажите мне почему компилятор так упорно применяет команды типа "LDD R18, Z+61" вместо "lds r18,имя". Явно нагляднее последнее. (В Z компилятор заносит имя переменной вообще не имеющей отношения к процедуре прерывания). Учитывая использование данной инструкции, похоже необходимо переменные используемые в одном месте группировать? Может есть у кого рекомендации по оптимизации программы на С? Я пока чётко понял одну касающуюся необходимости правильного размещения или объявления процедуры прерывания. Вопросов у меня несколько. 1) Как правильно объявить процедуру прерывания если я её полностью напишу на ASMе? Достаточно ли просто extern? 2) Где почитать по макросам для IAR ASM? 3) На объявление переменной типа: struct { uint8_t RXOFF : 1, // Буфер переполнен, загрузка приостановлена RMaster: 1, // Контроллер в режиме "Мастер" RLoadActKom : 1, // Разрешена Загрузка команд в "активную зону" cursiv: 1, // вывод текста курсивом Color: 1, // цвет Load485: 1, // Загрузка команд идёт с RS485 ErrLoadKom: 1; // Ошибка загрузки команды // RActivPass: 1; // Прерывание завершено; } Flag; Компилятор выделил 11 (!) байт данных. Где у меня ошибка? Или я не правильно понял "возможность работы с битами" и эта "возможность" такая же как "boolean" под Паскалем?
|
|
|
|
|
Mar 16 2006, 21:55
|

Профессионал
    
Группа: Свой
Сообщений: 1 065
Регистрация: 8-10-05
Из: Kiev, UA
Пользователь №: 9 380

|
Цитата почему компилятор так упорно применяет команды типа "LDD R18, Z+61" вместо "lds r18,имя". Обычно такое случаеться при работе со сложными типами данных. В Z - адрес, а потом ездит по смещению. Цитата Компилятор выделил 11 (!) байт данных. Где у меня ошибка? Или я не правильно понял "возможность работы с битами" Есть две важных установки компилятора 1.Упаковка структур - т.е без выравнивания по адресам. 2.Разрешение работы с битовыми полями. Устанавливаеться прагмами или в свойствах проекта. Если такие проблемы - м.б. взять камень побыстрее?
--------------------
Вони шукають те, чого нема, Щоб довести, що його не існує.
|
|
|
|
|
Mar 16 2006, 22:21
|
Знающий
   
Группа: Свой
Сообщений: 550
Регистрация: 16-06-04
Из: Казань
Пользователь №: 32

|
Цитата(SasaVitebsk @ Mar 17 2006, 00:19)  И ещё скажите мне почему компилятор так упорно применяет команды типа "LDD R18, Z+61" вместо "lds r18,имя". Явно нагляднее последнее. (В Z компилятор заносит имя переменной вообще не имеющей отношения к процедуре прерывания). LDS занимает 2 слова, а LDD - одно, при одинаковом времени выполнения. Очевидно, второй вариант предпочтительнее.
--------------------
Главная линия этого опуса ясна мне насквозь!
|
|
|
|
|
Mar 17 2006, 10:53
|

Частый гость
 
Группа: Свой
Сообщений: 175
Регистрация: 26-01-06
Из: Sevastopol
Пользователь №: 13 664

|
Цитата(SasaVitebsk @ Mar 16 2006, 23:19)  То что я увидел в обработчике самого быстрого прерывания (245мкс), трудно назвать программой в здравом уме. Читал некоторые посты по оптимизации. Основной совет: минимизировать количество операций в прерывании, ограничившись, например, выставлением флага или записью байта в регистр. Битовые флаги удобно делать регистровыми типа __regvar. Экономит сохранение регистров, да и все остальное. Не менее важный совет: избегать вызова функций из процедуры обработки прерывания. Если последняя работает автономно со своими локальными или глобальными переменными, то код будет самым эффективным. Если она вызывает хоть одну другую функцию, определенную в этом же файле - мы нарываемся на сохранение и восстановление некоторых регистров, используемых той функцией. Если процедура обработки прерывания вызовет хоть одну функцию типа extern, описанную в другом файле или непродекларированную - будьте готовы к полному сохранению и восстановлению половины регистров (все scratch регистры, которые любая C функция имеет право портить). Вызвано это тем, что при вызове функции из этого же файла компилятор знает, какие регистры та функция использует. Для внешних вызовов компилятор не может предполагать ничего и исходит из худшего случая. Кстати, именно поэтому и рекомендую изучать генерируемый компилятором код. Казалось бы, всего один вызов функции - а какие накладные расходы? И ведь иначе и не сделать с доступной компилятору информацией. Он же не знает, что extern функция всего лишь выставляет бит в регистре... Цитата 1) Как правильно объявить процедуру прерывания если я её полностью напишу на ASMе? Достаточно ли просто extern? Вероятно, не extern, а public. Но ее вообще не нужно нигде объявлять, если это процедура обработки прерывания. C-компилятору вообще нет дела до этой процедуры, он ее не вызывает. Но при этом придется самому позаботиться о том, чтобы правильно разместить команду перехода на начало функции в таблице векторов прерываний. Пример можно посмотреть в исходных текстах USB драйвера. Там именно так и сделано. Цитата 2) Где почитать по макросам для IAR ASM? Вероятно, в прилагаемой к нему документации? Дополнительно можно посмотреть в его *.h файлы. Можно найти кое-что полезное. Цитата 3) На объявление переменной типа: ... Компилятор выделил 11 (!) байт данных. Как это было определено? С трудом себе это представляю. У меня, как и ожидается, 1 байт. Листинг ниже. А что у Вас в листинге? Код // 87 __root struct // 88 { // 89 uint8_t RXOFF : 1, // Буфер переполнен, загрузка приостановлена // 90 RMaster: 1, // Контроллер в режиме "Мастер" // 91 RLoadActKom : 1, // Разрешена Загрузка команд в "активную зону" // 92 cursiv: 1, // вывод текста курсивом // 93 Color: 1, // цвет // 94 Load485: 1, // Загрузка команд идёт с RS485 // 95 ErrLoadKom: 1; // Ошибка загрузки команды // 96 // RActivPass: 1; // Прерывание завершено;
RSEG NEAR_Z:DATA:ROOT(0) REQUIRE `?<Segment init: NEAR_Z>` // 97 } Flag; Flag: DS 1 (__root - это чтобы компилятор не выкинул переменную, на которую нет ссылок в коде).
|
|
|
|
|
Mar 17 2006, 14:19
|
Участник

Группа: Свой
Сообщений: 60
Регистрация: 8-11-05
Пользователь №: 10 602

|
У IAR синтаксис обычный для языка С. Т.е описание библиотек ты можешь найти в любой книжке по С. Приведенные Вами примеры функций - это обработчики прерываний. Они бодробно описаны в описании IAR. Как строить программу на C можно прочитать в любом самоучителе по С
|
|
|
|
|
Mar 17 2006, 14:41
|

Частый гость
 
Группа: Свой
Сообщений: 175
Регистрация: 26-01-06
Из: Sevastopol
Пользователь №: 13 664

|
Цитата(alekseykoj @ Mar 17 2006, 16:19)  У IAR синтаксис обычный для языка С. Т.е описание библиотек ты можешь найти в любой книжке по С. Приведенные Вами примеры функций - это обработчики прерываний. Они бодробно описаны в описании IAR. Как строить программу на C можно прочитать в любом самоучителе по С Вопросы были не в том, как строить программу на C, а специфических особенностях IAR. В том числе: Цитата - как описать процедуру обработки прерывания на ассемблере (а не как построить программу на C). Ответ: стандартно, так как C до нее нет никакого дела. Читать документацию по ассемблеру (именно по IAR ассемблеру - важно, как разместить rjmp по адресу вектора прерывания с использованием команд управления сегментами *SEG и счетчиком адреса ORG, которые частично отличаются по синтаксису от других ассемблеров). Цитата - где почитать по макросам IAR ассемблера (а не про стандартные C библиотеки). Ответ: после чтения документации полезно посмотреть не стандартные, а именно специфические для IAR *.h файлы, поскольку они используются и для C-программ, и для ассемблерных программ. Ассемблер IAR отличается от ассемблера Atmel синтаксисом большинства директив, а также использованием препроцессора C по умолчанию. Стандартные библиотеки C тут не причем. Цитата - почему стандартное битовое поле из 8-ми битов превращается в 11 байтов вместо 1. Ответ: и на самом деле, почему? Видимо, не так смотрелось. Нужно смотреть на листинг, во что превращается объявление. Этого ни при каком выравнивании не может быть, так как 8 байтов из 8 битов - еще понять можно, но 11... То есть, даны ответы, в общем-то, правильные, но совсем не на заданные вопросы...
|
|
|
|
|
Mar 17 2006, 17:59
|
Гуру
     
Группа: Свой
Сообщений: 2 712
Регистрация: 28-11-05
Из: Беларусь, Витебск, Строителей 18-4-220
Пользователь №: 11 521

|
Я извиняюсь перед всеми. Надо было более детально разобраться в произошедшем, а потом писать. Спасибо всем высказавшимся. Скажу что изучал когда-то очень давно теорию компиляторов и ОС.  Но тогда мне это было не нужно и безинтересно. Тогда безгранично годсподствовала ОС ЕС ЭВМ и только0-только появилась СВМ! С каким бы я интересом послушил бы сейчас того препода! Такой класный был преподаватель ... Теперь по сути. Компилятор, очевидно, очень хороший. Местами великолепно "угадывает" как надо делать и прекрассно компилирует. Но некоторые фичи я пока не понимаю. 1) В процедуре обработки прерывания я не вызываю функций. (Прочитал в других ветках форума). Но если я пользуюсь переменными, объявленными как статические глобальные (вверху файла), то код получается значительно больший. Похоже компилятор пытается выделять локальные экземпляры переменных или что-то в этом духе. Обращение к этим переменным осуществляется ч/з указатель. Не совсем понимаю зачем это? Может чтобы дважды можно было вызвать? Если объявляешь статическими внутри функции, то код намного понятнее. А как надо объявлять? Я хочу чтобы от вызова к вызову переменные сохраняли своё значение. 2) Наблюдаю картину, что если арифметическое выражение сделать сложным, то компилятор начинает генерить сложный код. Получается что лучше разбивать операторы на более простые? 3) Мне необходимо в прерывании выполнить определённую операцию 18 раз. Можно сделать её путём логических операций. Но что бы ускорить процесс я решил воспользоваться табличным методом. Для этого сгенерировал соответсвующий массив. Вне данного прерывания этот массив использоваться не будет. Я объявил его в другом файле вместе с другими таблицами а в перывании объявил его как внешний. (Пробовал объявить его внутри прерывания, - компилятор ругается). Так вот обращение к этому массиву какое-то мудрёное. Может кто подскажет в чём дело? 4) На счёт одинадцати байт.  И правда и не совсем.  С одной стороны при обращении к битам происходит обращение только к одному байту! Причём обращение красивое!!! Но в распечатке листинга (релиз) я сейчас вижу следующее: // 70 } Flag; Flag: DS 1 DS 1 А раньше выделялось 11 байт. Вроде ничего не менял. Пробовал воткнуть STATIC, - не получилось. Кстати ко многим переменным добавляется ещё один байт. Это зачем? Пятое и пока последнее. Спасибо всем откликающимся. Я конечно постепенно приду к определённым результатам и самостоятельно, не отрывая Вас от Ваших дел, но мне кажется что данный форум читаю не только я, но и другие начинающие программисты С. И, мне хочется надеятся, что он будет полезен для многих, поскольку я пытаюсь ставить вопросы, так сказать, из жизни.
|
|
|
|
|
Mar 17 2006, 19:52
|

Частый гость
 
Группа: Свой
Сообщений: 175
Регистрация: 26-01-06
Из: Sevastopol
Пользователь №: 13 664

|
Цитата(Old1 @ Mar 17 2006, 20:53)  Компилятор глобальные переменные по умолчанию помещает в SRAM, поэтому и обращается к ним через указатель, времени это занимает больше, если сравнивать с обращениями к регистровому файлу или к io-регистрам (поэтому обычно рекомендуют стараться меньше использовать глобальные переменные). Для того чтобы уменьшить время выполнения обработчика прерывания можно посоветовать объявить глобальную переменную как регистровую или разместить ее в неиспользуемых io-регистрах. Ответ большей частью верный, но непонятный для начинающих. Попробую пояснить. Имеем три вида переменных в языке C: 1) Глобальные, объявленные на внешнем уровне. 2) Локальные статические, объявленные внутри функции и сохраняющие свои значения между вызовами. 3) Локальные автоматические, объявленные внутри функции и не сохраняющие своих значений. Переменные (1) компилятор размещает в SRAM. Слово register конкретным компилятором для глобальных переменных не будет удовлетворено, так как неизвестно, что с этими регистрами сделают другие функции. Для размещения глобальной переменной в регистре есть опция конкретного компилятора lock registers и ключевое слово __regvar (см. документацию). Залочив несколько регистров (то есть, зарезервировав их под статические переменные), вся программа и используемые библиотеки должна быть откомпилирована с такой же опцией, иначе на стадии линковки будет ошибка. Есть еще вариант размещения констант в памяти программ - const __flash, но это не относится к изменяемым переменным. Кстати, зарезервировать регистры под переменные мало: надо еще и явно разместить их там (см. документацию). Иначе будет только хуже или, как минимум, не лучше. А хуже потому, что компилятор ограничен в использовании всех свободных регистров, а если там ничего не разместили, то выигрыша не будет. Не все переменные следует делать регистровыми - это зависит от их использования, надо пробовать. Иногда на одной переменной выигрыш десятки байтов, на другой - почти ничего, а на третьей получаем даже проигрыш. Локальные статические переменные (2) компилятор также размещает в SRAM (из ответа можно понять, что нет). Отличие от (1) - только в области видимости (конкретная функция). Все справедливо и в отношении игнорирования слова register. __regvar использовать, скорее всего, тоже не удастся: насколько помню, компилятор допускает его применение только для глобальных переменных. Почему обращение к локальным static менее эффективно, чем к глобальным - не скажу, не видя фрагмента кода. Локальные автоматические переменные (3) - вот тут возможны варианты. Классически эти переменные размещаются в стеке, расположенном тоже в SRAM. Однако в данном случае компилятор и без всяких слов register будет пытаться разместить максимум автоматических переменных в регистрах. Сохранять их между вызовами не нужно, а есть достаточное количество регистров, которые функция может использовать и не обязана сохранять (см. документацию). Работа с регистрами обычно эффективнее, это верно. Но использовать минимум глобальных переменных рекомендуют вовсе не по этой причине, а из соображений стиля программирования и читабельности программ. Остальное более-менее вторично и зависит от конкретных особенностей компилятора. Тут есть много нюансов. Например, единственный вызов функции без параметров, занимающий слово (2 байта) может очень неслабо увеличить размер некоей функции. Почему? А потому что вызванная функция может использовать половину регистров, и весь выигрыш от размещения локальных auto переменных в регистрах будет съеден необходимостью их сохранить перед этим вызовом, а потом восстановить. Почему компилятор использует обращение через "указатель"? Ряд команд специально сделан для работы с индексированными данными. Например, в регистровую пару Z может быть загружен начальный адрес структуры, а потом к ее членам можно обращаться одной командой через Z+смещение. Компактно и удобно. Этот прием компилятор может использовать и для доступа к локальным переменным, и для глобальных с целью экономии кода. Насчет сложного кода в выражениях: заочно ответить нельзя. В таких случаях надо писать иначе: вот фрагмент 1 (кусок кода), вот фрагмент 2 (кусок кода). Первый занимает на 20 байтов больше. В чем причина? Иногда ответить на этот вопрос вообще нельзя, не видя описания переменных и т.п. Часто просто удобнее писать по частям, заводя по ходу дела промежуточные переменные. Не следует беспокоиться: при высоких уровнях оптимизации компилятор так или иначе не будет хранить то, что дальше по тексту не требуется. А читабельность это может поднять. Про массивы в прерывании. То же самое. Не видя кода, нельзя сказать, почему компилятор "ругается". Как минимум, следует указывать сообщение об ошибке. А сэкономить можно много как. Например, объявить таблицу как const __flash, разместив ее тем самым не в SRAM (которое заполняется кодом инициализации из того же flash при запуске программы), а непосредственно во flash. Правда, доступ будет чуть сложнее, но зато таблица не займет оперативной памяти, что иногда важнее. А, кстати, а как на самом деле объявлялась таблица внутри? Надеюсь, что static? Иначе бедному компилятору пришлось бы ее инициализировать при каждом входе в функцию... Может, это и не понравилось? В общем, код в студию. 11 байт: то же самое. Бесцельно компилятор не выделяет лишние байты в конце. Причина обычно всегда есть. Но без фрагмента текста понять ее сложно. Мало кто на этом форуме умеет гадать на кофейной гуще Цитата(SasaVitebsk @ Mar 17 2006, 21:38)  Цитата(Old1 @ Mar 17 2006, 22:53)  Компилятор глобальные переменные по умолчанию помещает в SRAM
Компилятор в любом случае помещает эти переменные в SRAM. Но в первом описанном случае помещает ещё и указатели на эти переменные. Ну и обращается к ним ... соответственно. Компилятор не помещает их в SRAM, если явно указано, например, char __regvar __noinit c @ 15; При этом глобальная переменная c будет размещена не в SRAM, а в регистре, который мы обязаны залочить на уровне всего проекта опцией компилятора или IDE. Тут ответ был совершенно правильный. А размещать для статических переменных еще и указатели на них, помимо самих переменных... помилуйте, ради чего? Повторю, что не видя конкретного кода в конкретном контексте, сказать на 100% просто невозможно, можно лишь гадать.
|
|
|
|
|
Mar 19 2006, 13:05
|
Гуру
     
Группа: Свой
Сообщений: 2 712
Регистрация: 28-11-05
Из: Беларусь, Витебск, Строителей 18-4-220
Пользователь №: 11 521

|
osnwt спасибо за расширенный ответ. Детали и нюансы не почерпнёшь в мануалах. Там всё лаконично. Массив пересчёта я поместил в const __flash, бузусловно. Использовать 256 байт озу под это для меня слишком расточительно. Помещать Вам на рассмотрение ещё и фрагменты текста, по-моему это уж слишком.  Это уж самом-самом крайнем случае. Буду разбираться сам. Ваши советы мне помогли обрести уверенность. Я теперь больше доверяю компилятору и меньше себе.  Если что-то не так, - более детально разбираю программу. На счёт "легендарных" 11 байт. Ещё раз внимательно всё просмотрел (в который уже раз). У меня в программе очень много различных данных находится в sram. И разнотипных к тому же. Чё делает компилятор? - Он выбирает группу переменных (возможно использующихся в одном месте) и для обращения к переменным использует одно имя. А к остальным переменным обращается LDD Rx, Z+ (смещение в группе). Вот так и получилось 11 байт.  На самом деле это определённое количество переменных начинающихся с переменной "flag". В моём случае вычисление типа: Oport0 &= ((EkrToPort[*AdrShowEkr++ ^ Cycl_Ekr] & 0xf) | (EkrToPort[*AdrShowEkr++ ^ Cycl_Ekr] & 0xf0)); Заняло больше места чем l = (EkrToPort[*AdrShowEkr++ ^ Cycl_Ekr] & 0xf); Oport0 &= (l | (EkrToPort[*AdrShowEkr++ ^ Cycl_Ekr] & 0xf0)); (l - переменная локальная)
|
|
|
|
|
Mar 19 2006, 14:32
|

Частый гость
 
Группа: Свой
Сообщений: 175
Регистрация: 26-01-06
Из: Sevastopol
Пользователь №: 13 664

|
Цитата(SasaVitebsk @ Mar 19 2006, 15:05)  В моём случае вычисление типа: Oport0 &= ((EkrToPort[*AdrShowEkr++ ^ Cycl_Ekr] & 0xf) | (EkrToPort[*AdrShowEkr++ ^ Cycl_Ekr] & 0xf0)); Заняло больше места чем l = (EkrToPort[*AdrShowEkr++ ^ Cycl_Ekr] & 0xf); Oport0 &= (l | (EkrToPort[*AdrShowEkr++ ^ Cycl_Ekr] & 0xf0)); (l - переменная локальная) Хочу заметить, что первое (сложное) выражение, помимо того, что не очень наглядно, еще и неоднозначно с точки зрения порядка вычислений (иногда при этом можно получить и разную длину кода). Использованное там выражение *p++ имеет побочный эффект в виде инкремента указателя. В выражении e1 | e2 порядок вычисления выражений e1 и e2 вообще не определен (в отличие от e1 || e2, где вычисление идет слева направо - смысл операций, разумеется, разный). Потому в общем случае компилятор может сначала посчитать правое выражение, а потом левое. Очевидно, что результаты будут различные, так как *p++ (где p - это EkrToPort) даст разные результаты в этих двух случаях. Потому в таких случаях особенно важно писать однозначно и наглядно.
|
|
|
|
|
Mar 19 2006, 18:38
|

Частый гость
 
Группа: Свой
Сообщений: 175
Регистрация: 26-01-06
Из: Sevastopol
Пользователь №: 13 664

|
Цитата(osnwt @ Mar 19 2006, 16:32)  *p++ (где p - это EkrToPort) Ошибка: имелось в виду AdrShowEkr в выражении Код Oport0 &= ((EkrToPort[*AdrShowEkr++ ^ Cycl_Ekr] & 0xf) | (EkrToPort[*AdrShowEkr++ ^ Cycl_Ekr] & 0xf0));
|
|
|
|
|
Mar 19 2006, 20:27
|
Гуру
     
Группа: Свой
Сообщений: 2 712
Регистрация: 28-11-05
Из: Беларусь, Витебск, Строителей 18-4-220
Пользователь №: 11 521

|
Я это обнаружил с самого начала, но решил посмотреть как это сделает компилятор. Потом убрал чтобы не было неоднозначности. Написанная мной подпрограмма прерывания на ASMе оказалась быстрее примерно в четыре раза. Я конечно ещё поэкспериментирую с СИ (программой прерывания), но вероятней всего в конечном варианте будет программа на асемблере. (Самый быстрый вариант на СИ тоже оставлю для возможного перехода на другой МП) Я приступил к отладке, поскольку в настоящий момент начинаю "терять из под контроля программу".  (Не знаю как у кого, а у меня при большой длине проги наступает такой момент, когда она живёт своей жизнью) После того как я отлажу (в симуляторе) некоторые независимые части, то начну её дописывать. Осталось процентов 20-25. И вот сейчас у меня непонятная ошибка. TekAdrActive = LastAdrActive = malloc(SIZE_FREE); // Âûäåëèòü ïàìÿòü При отладке в указатели заносится значение при SIZE_FREE менее 10. Фишка в том, что я пишу под atmega2560 (sram 8k). Компилятор выдаёт следующие значения занятой программы. // // 43 bytes in segment ABSOLUTE // 2 668 bytes in segment CODE // 3 002 bytes in segment FARCODE // 7 bytes in segment INITTAB // 12 bytes in segment INTVEC // 855 bytes in segment NEAR_Z // // 5 670 bytes of CODE memory (+ 19 bytes shared) // 855 bytes of DATA memory (+ 43 bytes shared) И ещё указатель на свободный адрес она выдаёт типа 229h. Может быть там и есть дырка байт 10.  Но потом идут массивы и прочее. Короче менее 5bfh этот адрес не должен быть. Где-то ошибка. Может быть это связано с сегментами? Дело в том что я с ними не работал и пока убей-непонимаю зачем они нужны (применительно к AVR. На процессоре 80x8х когда-то сталкивался под ассемблером. Там это очевидно). Помогите разобраться.
|
|
|
|
|
Mar 19 2006, 23:04
|

Частый гость
 
Группа: Свой
Сообщений: 175
Регистрация: 26-01-06
Из: Sevastopol
Пользователь №: 13 664

|
Цитата(SasaVitebsk @ Mar 19 2006, 22:27)  TekAdrActive = LastAdrActive = malloc(SIZE_FREE); // Âûäåëèòü ïàìÿòü
При отладке в указатели заносится значение при SIZE_FREE менее 10. Фишка в том, что я пишу под atmega2560 (sram 8k). Компилятор выдаёт следующие значения занятой программы. ... Может быть это связано с сегментами? Дело в том что я с ними не работал и пока убей-непонимаю зачем они нужны (применительно к AVR. На процессоре 80x8х когда-то сталкивался под ассемблером. Там это очевидно). Помогите разобраться. Каждый байт кода или данных помечается, как располагаемый в определенном именованном сегменте. Имеется некоторое количество предопределенных сегментов, используемых компилятором. Можно также создавать свои собственные. Раскладыванием сегментов по адресам занимается линкер под управлением файла конфигурации *.lnk. По умолчанию в IAR среда определяет основные размеры областей, а дальше используется файл конфигурации для соответствующего контроллера. Там указано, что сегмент такой-то занимает адреса такие-то. Это делается (по умолчанию) с учетом заданных в конфигурации проекта размеров стеков, кучи и т.п. При работе с *alloc функциями надо выделить достаточный размер "кучи" (heap) в конфигурации проекта. А потом посмотреть, где линкер разместит сегменты HEAP и NEAR_HEAP. Если выделяемый адрес не попадает в эти области, то что-то не так с программой. Подробнее RTFM по линкеру, там все хорошо расписано. В данном случае сегменты не имеют ничего общего с сегментами x86, это просто средство управления распределением адресов под код и данные при окончательной компоновке.
|
|
|
|
|
Mar 22 2006, 12:29
|
Гуру
     
Группа: Свой
Сообщений: 2 712
Регистрация: 28-11-05
Из: Беларусь, Витебск, Строителей 18-4-220
Пользователь №: 11 521

|
Спасибо. Сегодня ине глючит у меня. Поэтому топик попал дважды да не весь.  Передаю вторую часть Подскажите спецы (IAR). Более сложный вопрос.  Переменные объявлены следующим образом: void static *AdrActiveKom[MAX_ACTIVE_KOM+1]; // Адреса указателей на начало активных команд uint8_t i, j, c, *adrnpict; Имею такой цикл (или он меня  ) j= (uint8_t *)AdrActiveKom[i+1]-(uint8_t *)AdrActiveKom[i]; // Высчитать длину команды for(adrnpict=(AdrActiveKom[i]);adrnpict<(AdrActiveKom[NumbActiveKom+1]-j);adrnpict++) *adrnpict = *(adrnpict+j);// перенесли команду Если цикл описать так, то синтаксич. ошибка несовместимости типов, а если так как ниже то всё OK j= (uint8_t *)AdrActiveKom[i+1]-(uint8_t *)AdrActiveKom[i]; // Высчитать длину команды for(adrnpict=((uint8_t *)AdrActiveKom[i]);adrnpict<((uint8_t *)AdrActiveKom[NumbActiveKom+1]-j);adrnpict++) *adrnpict = *(adrnpict+j);// перенесли команду Но при вычислениях (не в теле цикла, а при выч. переменных цикла) компилятор использует адреса а не содержимое массива. Можно конечно поизголятся используя адресную арифметику, но я хочу выяснить где у меня ошибка? Что я не правильно понимаю? С уважением.
|
|
|
|
|
Mar 22 2006, 13:09
|

кекс
     
Группа: Свой
Сообщений: 3 825
Регистрация: 17-12-05
Из: Киев
Пользователь №: 12 326

|
Цитата(SasaVitebsk @ Mar 22 2006, 14:29)  Можно конечно поизголятся используя адресную арифметику, но я хочу выяснить где у меня ошибка? Ошибка на стадии проектирования.. Я извиняюсь, но алгоритм у Вас если мягко сказать - очень замороченный. Что приводит к просто ужасной реализации в коде. Задача вроде бы простая - скопировать тело команды в буфер команды, соответственно должна решаться простым способом даже без вычисления длины команды. Пока не поздно упрощайте алгоритм (если уже не поздно..)..
|
|
|
|
|
Mar 22 2006, 20:42
|
Гуру
     
Группа: Свой
Сообщений: 2 712
Регистрация: 28-11-05
Из: Беларусь, Витебск, Строителей 18-4-220
Пользователь №: 11 521

|
Всё ещё сложнее чем Вы думаете.  И вто же время достаточно симпатично в реализации. На самом деле команд много и поступают непрерывно. Команды имеют переменную длину и структуру. Заносятся последовательно и там проблем не возникает. Исполняются только те команды которым подошло время. Т.н. активные. Исполняются пока не завершится время исполнения. С исполнением тоже проблем нет. То что Вы видите, - это не занесение в буфер а "смерть" команды. Т.е. она отработала, и её надо выкинуть из буфера. Я конечно могу просто удалить указатель на данную команду, но тогда в памяти появится "дырка". Короче стандартная ситуация "сборки мусора", которая, как говорят, эффективно решена в UNIX, но плохо в Windows.  Известно и её решение в файловой системе диска начиная с FAT. По известной причине данные решения я применить не могу.  Поэтому я переписываю все команды следующие за удаляемой на место начиная с освобождённого. А также пересчитываю массив указателей на начало команд и уменьшаю их число. Всё это на PC работает и здесь ошибки нет, но компилятор почему-то не верно компилирует (точнее не так как я от него хочу).
|
|
|
|
|
Mar 23 2006, 23:30
|

кекс
     
Группа: Свой
Сообщений: 3 825
Регистрация: 17-12-05
Из: Киев
Пользователь №: 12 326

|
Цитата(SasaVitebsk @ Mar 23 2006, 23:39)  В массиве только указатели на "команды". Команда представляет собой структуру. Разные команды - разные структуры. Одна занимает 14 байт, - другая - 150 байт. Друг в друга ни как не скопируешь. Сходу посоветовал бы включить в тело команды поле "размер команды" 1 байт. Чтобы избавиться от вычисления размера. Цитата Насчёт мемcopy посмотрю. Не видел такой функции. ну начните с такого прототипа: int CopyArray(char *dest, char *source, int count);копирует count байт из массива source в массив dest, возвращается число реально скопированных байт. Реализацию думаю Вы легко сможете написать.. а потом перейдите к такому (чтобы копировать что угодно и куда угодно): int CopyMemory(void *dest, void *source, int count);копирует count байт c адреса source по адресу dest, возвращается число реально скопированных байт. Имея реализацию первой функции, написать реализацию второй - по времени займет не больше одной минуты. Цитата А всётаки что на счёт программы. Там 4 строчки. Описания переменных я дал. Смысл того что хочу сделать вроде объяснил. Где ошибка? ошибка всплывет, если упростить выражение вида for(adrnpict=(AdrActiveKom[i]);adrnpict<(AdrActiveKom[NumbActiveKom+1]-j);adrnpict++).... до такой записи: Код for(i=0; i<x; i++) { dest[i] = source[i]; } типы переменных (x, dest и source) и их значения задать Вам будет проще, чем мне гадать.. Цитата Алгоритм мой оценивать сложно так как вы видите очень малую часть общей картины. Да, оценить, а точнее - понять Ваш алгоритм по этой малой части общей картины, действительно нельзя.. Но вот, заметить что алгоритм через чур сложный - можно.
Сообщение отредактировал defunct - Mar 24 2006, 01:51
|
|
|
|
|
Mar 24 2006, 14:42
|
Гуру
     
Группа: Свой
Сообщений: 2 712
Регистрация: 28-11-05
Из: Беларусь, Витебск, Строителей 18-4-220
Пользователь №: 11 521

|
Нашёл функцию "memcpy" скорее всего это сокращение от memcopy. С ней всё пашет. Спасибо. Цитата(defunct @ Mar 24 2006, 03:30)  Да, оценить, а точнее - понять Ваш алгоритм по этой малой части общей картины, действительно нельзя.. Но вот, заметить что алгоритм через чур сложный - можно. Не спорю. Может даже ещё сложнее чем Вы подозреваете.  Но с одной стороны - это решение поставленной задачи. А с другой - это МОЁ решение.  Возможно пока я усложняю простые вещи. Или просто не вижу простого решения. Мой опыт программиста говорит, что ,как правило, находишь красивые решения, а потом их используешь многократно. Для этого, в первую очередь, требуется опыт. Я - оптимист, - опыт наработаем.  Было бы желание и работоспособность. За помощь премного благодарю. В последнее время задавать вопросы и даже просто посоветоваться было не с кем. Хорошо что есть инет!  Так что не обижайтесь, я Вас ещё побеспокою. Вопрос. Если я пишу п/п прерывания на asm, то необходимо как-то указывать компилятору С о наличии такой подпрограммы? Или это (что прерывание и расположено с такого вектора) указывается в модуле ASM и используется только линковщиком?
|
|
|
|
|
Mar 24 2006, 15:42
|

кекс
     
Группа: Свой
Сообщений: 3 825
Регистрация: 17-12-05
Из: Киев
Пользователь №: 12 326

|
/offtop Цитата А всётаки что на счёт программы. Там 4 строчки. Описания переменных я дал. Смысл того что хочу сделать вроде объяснил. Где ошибка? Цитата Нашёл функцию "memcpy" скорее всего это сокращение от memcopy. С ней всё пашет. Спасибо. Как видите не всегда рационально искать ошибку там где ее можно обойти ;> Цитата Вопрос. Если я пишу п/п прерывания на asm, то необходимо как-то указывать компилятору С о наличии такой подпрограммы? Или это (что прерывание и расположено с такого вектора) указывается в модуле ASM и используется только линковщиком? Если в модуле с п/п на ассемблере указан ORG с адресом вектора прерывания, то безусловно это будет учтено на стадии линковки. Компилятору C главное не указывать директив #pragma vector с номером вектора который уже "занят" модулем на ASM.
Сообщение отредактировал defunct - Mar 24 2006, 15:55
|
|
|
|
|
Mar 25 2006, 07:08
|

Частый гость
 
Группа: Свой
Сообщений: 175
Регистрация: 26-01-06
Из: Sevastopol
Пользователь №: 13 664

|
Цитата(SasaVitebsk @ Mar 24 2006, 16:42)  Вопрос. Если я пишу п/п прерывания на asm, то необходимо как-то указывать компилятору С о наличии такой подпрограммы? Или это (что прерывание и расположено с такого вектора) указывается в модуле ASM и используется только линковщиком? На C нужно разрешить указанное прерывание, установив соответствующие биты в регистрах, ответственных за прерывание, и глобально разрешив прерывания (__enable_interrupt() для IAR). А на ассемблере - вот так, например (для IAR): Код ; Переход с вектора прерывания на процедуру обработки ; ISR (interrupt service routine)
COMMON INTVEC ORG INT0_vect rjmp ISR_Int0
; Процедура обработки прерывания
RSEG CODE
ISR_Int0: ; сохранение статуса и регистров при необходимости ; ... ; ... ; восстановление регистров и статуса
reti
|
|
|
|
|
  |
12 чел. читают эту тему (гостей: 12, скрытых пользователей: 0)
Пользователей: 0
|
|
|