Перейти к содержанию
    

Доброго времени суток. Помогите начинающему. Долгое время писал на асме для разных микропроцессоров. В том числе большие проекты. На 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: [email protected]

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

2) Если необходимы прерывания достаточно ли такого объявления или необходимо что-то ещё (например переходы):

 

#pragma vector=USART_RXC_vect

__interrupt void rxint(void)

{

 

}

Достаточно, остальное компилятор сделает сам.

5) При использовании кольцевого буфера правильнее открыть буфер в виде массива или использовать указатели

 

Если открыть - имеется в виду объявить массив то его по любому необходимо объявлять как массив необходимой вам размерности. (Если конечно не использовать динамическое выделение памяти).

Вот доступ к элементам массива можно осуществлять индексами или через указатели.

Индексы более наглядные для понимания программы.

Указатели не так наглядны, но замечено что компилятор к указателям относится более лояльно и код получается, как правило на несколько (иногда на 10-20) меньше.

Поэтому где возможно лучше использовать указатели. Добавлю что это ИМХО.

Изменено пользователем Pat

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

1) Как определять какие библиотеки включать?

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

2) Если необходимы прерывания достаточно ли такого объявления или необходимо что-то ещё (например переходы):

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

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

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

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

не стоит привязываться к конкретному алгоритму в самом начале освоения языка. К тому же коментарии сделанные Бог весть кем могут быть и неправильными.

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

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

5) При использовании кольцевого буфера правильнее открыть буфер в виде массива или использовать указатели.

на любителя.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Спасибо всем за ответы и присланные примеры. Ещё буквально пару вопросов.

1) У меня комп. 3.20С/4.12А. В объявлении среды включена м/сх atmega640 необходимая мне, но в каталоге INC нет файла iom640.h. Где его взять? Самому написать конечно можно. На сайте IAR поиском не нашёл.

2) По поводу разбиения на файлы я, по видимому не правильно задал вопрос. Я в форуме где-то читал, что если п/п прерывания поместить в отдельный файл, то компилятор сохраняет/восст. все регистры в данном прерывании. Хотя может я и не правю понял. Также я читал что объявления надо обязательно вынести в отд. файл и включить его вначале. Из этого я сделал вывод (возможно неверный) что существует определённый порядок размещения процедур и объявлений. Хотя бы желательный. Если это так, то напишите чтобы я привыкал. :)

С уважением

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

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

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Скачал PDFы и сделал книжечки

Не поделитесь, кде качали книжечки? Тоже интересно освоить.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

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

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

 

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

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

SasaVitebsk!

Я тоже начал осваивать IAR Embedded Workbench.

Если есть возможность, поделитесь ссылками.

Может у кого есть Дока по IAR на русском?

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

По пункту 3 :

Предполагаю что setjmp запоминает регистры R0-R31, SREG ... в память а longjmp загружает из памяти указанной как аргмент к этим функциям . Тип jmp_buf используется для сохранения этих регистров наверху. Поддержка многозадачности .

 

jmp_buf main_task;

jmp_buf iprx_task;

jmp_buf iptx_task;

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

 

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

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

По пункту 3 :

Предполагаю что setjmp запоминает регистры R0-R31, SREG ... в память а longjmp загружает из памяти указанной как аргмент к этим функциям . Тип jmp_buf используется для сохранения этих регистров наверху. Поддержка многозадачности .

 

jmp_buf main_task;

jmp_buf iprx_task;

jmp_buf iptx_task;

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

 

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

 

Гм, да можно было и у меня спросить... Приятно, когда на твоих исходниках люди учатся ;)

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Гм, да можно было и у меня спросить... Приятно, когда на твоих исходниках люди учатся ;)

 

Тогда у меня к Вам, как к автору кода, есть пара вопросов:

1. почему нельзя было просто вызвать IPrec() в обработчике прерывания?

2. из-за чего весь сыр-бор с сохранением стеков?

 

Не все, а только используемые. Компилятор обязан генерить код, не нарушающий целостность работы программы, не более того.

....

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

 

Спасибо, что поправили меня.

Вопрос: а если вызываемая в обработчике функция определена в том же самом модуле, в котором определен и обработчик прерывания, потребуется ли определять ее как встраиваемую или компилятор и так будет "знать" какие регистры ей используются, и сгенерит код для сохранения только их?

Изменено пользователем defunct

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Вопрос: а если вызываемая в обработчике функция определена в том же самом модуле, в котором определен и обработчик прерывания, потребуется ли определять ее как встраиваемую или компилятор и так будет "знать" какие регистры ей используются, и сгенерит код для сохранения только их?

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

 

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

 

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

 

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

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Тогда у меня к Вам, как к автору кода, есть пара вопросов:

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.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

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

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

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

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

простейшая диспетчеризация, внутри функций-задач рассматриваются условия "запуска", которые устанавливаются обработчиками прерываний и сбрасываются после отработки самими "задачами":

 

   for(;;)
   {
      task1();
      task2();
      task3();
    }

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Ещё раз спасибо всем ответившим. :)

Перевариваю.

Dxp спасибо за полное разъяснение по поводу прерываний. В общем-то я так и предпологал когда задавал вопрос, но ты полностью расставил точки над и. Исчерпывающее объяснение простым и лаконичным языком. :)

RST по поводу использования твоего продукта (пока) в учебных целях, я тебе писал. Извини если письмо не дошло. Пока задавать тебе вопросы для меня бессмысленно. Мы разговариваем на разных языках. :)

Вообще данный проект для меня несколько сложноват. Это когда отдельные строчки понимаешь, а всё вместе в картину не складывается. :)

Мне прислали более простой и я сейчас заглядываю в оба.

По поводу книжечек и обучения. Книжечки, - это сопровождающие PDF которые находятся в комплекте IAR. Они на англ. языке. Не скажу, что я его хорошо знаю, но я привык работать с PDF доками для микросхем и меня это вполне устраивает. Вывел их на печать с помощью проги FinePrint в виде книжечки. Переплёл их в типографии. По Си пользуюсь русской книгой Г.Шилда. Думаю знания придут по мере преобретения опыта. Пишу проект. Накопятся вопросы, - напишу.

По iom640.h я знаю где искать его там нет. Пока пользуюсь iom2560.h Это практически одно и тоже по SFR и озу. В проекте выставил mega640. В любом случае эти ошибки вылезут только на этапе отладки

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Присоединяйтесь к обсуждению

Вы можете написать сейчас и зарегистрироваться позже. Если у вас есть аккаунт, авторизуйтесь, чтобы опубликовать от имени своего аккаунта.

Гость
К сожалению, ваш контент содержит запрещённые слова. Пожалуйста, отредактируйте контент, чтобы удалить выделенные ниже слова.
Ответить в этой теме...

×   Вставлено с форматированием.   Вставить как обычный текст

  Разрешено использовать не более 75 эмодзи.

×   Ваша ссылка была автоматически встроена.   Отображать как обычную ссылку

×   Ваш предыдущий контент был восстановлен.   Очистить редактор

×   Вы не можете вставлять изображения напрямую. Загружайте или вставляйте изображения по ссылке.

×
×
  • Создать...