Помощь - Поиск - Пользователи - Календарь
Полная версия этой страницы: Начало работы with scmRTOS
Форум разработчиков электроники ELECTRONIX.ru > Cистемный уровень проектирования > Операционные системы > scmRTOS
Страницы: 1, 2, 3, 4, 5, 6
dxp
Цитата(jorikdima @ Mar 28 2010, 17:45) *
Собственно это конечно не есть пример грамотного программирования, быть может smile.gif, но пример наследования, где private модификатор мне пришлось поменять на protected.

Ну, тут, имхо, вообще тогда не стоит с этим шаблоном связываться. Какие преимущества он дает? Инкапсуляция грубо попрана, smile.gif абстракции никакой не осталось - по сути работаете с потрохами объекта напрямую, минуя его интерфейс. Ну, и зачем тогда вообще объект? Заводите себе буфер в памяти и спокойно работаете с ним. А может удобнее было бы завести пару буферов - в один пишете, другой по DMA отдаете (если условия задачи позволяют обойтись двумя буферами). Или, например, если буфера небольшие, то завести некий пул буферов - один, который отдает данные, передать DMA, остальные в работе с источниками данных, буфера переключать... И т.д. Подход, конечно, низкоуровневый, и имеет хорошее только частное решение, но он, имхо, ничем не хуже, чем потрошить объект кольцевого буфера, сам смысл которого - скрыть от пользователя реализацию и только предоставить удобный и безопасный интерфейс.
jorikdima
Цитата(dxp @ Mar 28 2010, 15:42) *
Ну, и зачем тогда вообще объект?

Затем, что в нем уже реализовано 90% того, что мне надо - работа с кольцевым буфером, запись внего... Я лишь добавил один метод, который возвращает мне адрес для DMA.
Ну собственно говоря ладно тогда, коль не убедил smile.gif Придумаю более удачный пример расширения наследованием, надеюсь, убедю.
Спасибо.
AHTOXA
Цитата(dxp @ Mar 26 2010, 20:48) *
Кстати, ring_buffer - это не сервис, а просто реализация кольцевого буфера. Что вам не хватает в его реализации, что вы хотите отнаследоваться и написать свой вариант?

Ну, например, я хочу сделать отладочный вариант кольцевого буфера. С выдачей дампа его содержимого. Да мало ли чего ещёsmile.gif
Тут, имхо, вопрос надо ставить так: а чем может повредиить применение protected вместо private? Моё мнение здесь такое - ничем. Если человек наследует какой-то объект, то он либо знает что делает, либо сам себе злобный буратино. Запрещать всем наследоваться из-за нескольких потенциальных буратин? Имхо, это неправильноsmile.gif
ЗЫ. Хочу витруальный Exec()! smile.gif
dxp
Цитата(jorikdima @ Mar 28 2010, 21:24) *
Затем, что в нем уже реализовано 90% того, что мне надо - работа с кольцевым буфером, запись внего... Я лишь добавил один метод, который возвращает мне адрес для DMA.

Есть в вашем предложении интересное зерно. Будем подумать.

Цитата(AHTOXA @ Mar 29 2010, 11:14) *
Ну, например, я хочу сделать отладочный вариант кольцевого буфера. С выдачей дампа его содержимого. Да мало ли чего ещёsmile.gif
Тут, имхо, вопрос надо ставить так: а чем может повредиить применение protected вместо private? Моё мнение здесь такое - ничем. Если человек наследует какой-то объект, то он либо знает что делает, либо сам себе злобный буратино. Запрещать всем наследоваться из-за нескольких потенциальных буратин? Имхо, это неправильноsmile.gif

Оно исходно шло (и идет) как библиотека поддержки. Никакого расширения тут просто не предполагалось. А если так рассуждать, что вообще долой private, даешь везде protected - мало ли кому что может захотеться.

Цитата(AHTOXA @ Mar 29 2010, 11:14) *
ЗЫ. Хочу витруальный Exec()! smile.gif

Это ты про процесс, что-ли? smile.gif И как ты его собрался вызывать?
AHTOXA
Цитата(dxp @ Mar 29 2010, 13:35) *
Оно исходно шло (и идет) как библиотека поддержки. Никакого расширения тут просто не предполагалось. А если так рассуждать, что вообще долой private, даешь везде protected - мало ли кому что может захотеться.

Ну а почему нет? smile.gif Зачем заранее рубить возможные расширения? Это ж не виндаsmile.gif Да и винду расковыряли, несмотря ни на что.

Цитата
Это ты про процесс, что-ли? smile.gif И как ты его собрался вызывать?


Дык, элементарно:
  1. Делаем TBaseProcess.Exec() виртуальной (пустой или чисто виртуальной - неважно);
  2. Добавляем в OS_Kernel.cpp новую функцию - трамплин:
    Код
    void OS::start_process(void)
    {
        TBaseProcess* proc = Kernel.ProcessTable[Kernel.CurProcPriority];
        proc->Exec();
    }
  3. Добавляем эту функцию в друзья классам TKernel и TBaseProcess (если Exec - protected);
  4. Убираем третий параметр у конструктора TBaseProcess:
    Код
    TBaseProcess::TBaseProcess(TStackItem* Stack, TPriority pr)
    {
        Kernel.RegisterProcess(this);
    ...
        *(--StackPointer)  = reinterpret_cast<dword>(start_process);
  5. И телемаркет! © smile.gif


Имхо, весьма элегантно. Может даже и выигрыш есть... Завтра попробую в работеsmile.gif
dxp
Цитата(AHTOXA @ Mar 29 2010, 22:58) *
Ну а почему нет? smile.gif Зачем заранее рубить возможные расширения? Это ж не виндаsmile.gif Да и винду расковыряли, несмотря ни на что.

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

Цитата(AHTOXA @ Mar 29 2010, 22:58) *
Дык, элементарно:

Э-э... А запускать ты их откуда собрался? А как тут все это вяжется со стековым кадром? И самое главное - зачем все это? Какие преимущества это дает? Зачему тут динамический полиморфизм, когда все механизмы исходно статические?

Цитата(AHTOXA @ Mar 29 2010, 22:58) *
Имхо, весьма элегантно. Может даже и выигрыш есть... Завтра попробую в работеsmile.gif

Давай. Расскажешь потом. smile.gif
AHTOXA
Цитата(dxp @ Mar 30 2010, 08:58) *
Видимо, пожелание надо учесть, тем более, что это не тянет за собой проблем с производительностью и совместимостью.

Ура smile.gif

Цитата
Э-э... А запускать ты их откуда собрался? А как тут все это вяжется со стековым кадром?

Вроде я всё написал. В конструкторе TBaseProcess вместо адреса статического Exec() используется адрес глобальной функции start_process(). А она уже определяет текущий процесс и запускает его функцию Exec(). Вся эта возня происходит всего один раз при старте процесса, то есть, на скорость работы не влияет абсолютно.
Цитата
И самое главное - зачем все это? Какие преимущества это дает? Зачему тут динамический полиморфизм, когда все механизмы исходно статические?

А всё это ради того, чтоб функция Exec была не статической. Тогда она сможет обращаться с членами класса-процесса. И можно будет сделать нормальную инкапсуляцию. Например, процесс LcdProcess будет иметь приватный член Lcd, и все остальные процессы будут писать в Lcd только посредством вызова методов класса LcdProcess.
Сейчас для этого приходится заводить класс-посредник, типа LcdProxy. У меня практически все методы Exec выглядят как
Код
    template <>
    OS_PROCESS void TTerminalProcess::Exec()
    {
        for (;;)
            terminal.loop();
    }

Я понимаю, что это мелочи, но напрягает. Наверняка есть ещё плюсы моего подхода, но я их пока не придумалsmile.gif

Цитата
Давай. Расскажешь потом. smile.gif


Проверил, всё замечательно работаетsmile.gif Причём даже без изменения проектов.
jorikdima
Цитата(AHTOXA @ Mar 30 2010, 07:46) *
А всё это ради того, чтоб функция Exec была не статической. Тогда она сможет обращаться с членами класса-процесса. И можно будет сделать нормальную инкапсуляцию. Например, процесс LcdProcess будет иметь приватный член Lcd, и все остальные процессы будут писать в Lcd только посредством вызова методов класса LcdProcess.

Вот мне в свое время тоже этого не хватало. Я хотел создать свой тип процесса, который работал бы с портом. То есть все остальные процессы лишь вызывают функцию передачи сообщения, а упаковкой данных в мой протокол, отправкой и приемом данных по UART занимается процесс. Прелесть в том, что я могу создать несколько таких однотиповых процессов, которые будут работать принципиально одинаково с разными портами, например. Это невозможно из-за статичности Exec() (из нее нельзя обращаться к нестатическим членам класса inher_process - наследника process).
dxp
Цитата(AHTOXA @ Mar 30 2010, 11:46) *
Вроде я всё написал. В конструкторе TBaseProcess вместо адреса статического Exec() используется адрес глобальной функции start_process(). А она уже определяет текущий процесс и запускает его функцию Exec(). Вся эта возня происходит всего один раз при старте процесса, то есть, на скорость работы не влияет абсолютно.

Во-первых, зачем функцию делать виртуальной? smile.gif Обычная (просто в шаблоне объявить функцию-член, не в предке) не сойдет? Виртуальная функция потащит накладняки в виде vptr в каждом объекте + vtbl на каждый тип (т.е. тоже на каждый объект). Это мелочи, конечно. Но зачем?

Во-вторых, вызов функции из start_process будет сохранять в стеке адрес возврата (и еще всякую служебную инфу в зависимости от платформы). С этим тоже можно с разным успехом побороться с помощью расширений типа __task (у IAR), но зачем?

Ну, и в-третьих (см ниже)...

Цитата(AHTOXA @ Mar 30 2010, 11:46) *
А всё это ради того, чтоб функция Exec была не статической. Тогда она сможет обращаться с членами класса-процесса. И можно будет сделать нормальную инкапсуляцию. Например, процесс LcdProcess будет иметь приватный член Lcd, и все остальные процессы будут писать в Lcd только посредством вызова методов класса LcdProcess.
Сейчас для этого приходится заводить класс-посредник, типа LcdProxy. У меня практически все методы Exec выглядят как
Код
    template <>
    OS_PROCESS void TTerminalProcess::Exec()
    {
        for (;;)
            terminal.loop();
    }

Я понимаю, что это мелочи, но напрягает. Наверняка есть ещё плюсы моего подхода, но я их пока не придумалsmile.gif

Тот вариант (который ты предложил) по сути является эквивалентным этому. Только там вызов terminal.loop() упрятан в недра. Это лучше? Сомневаюсь.

Делать фукнцию Exec нестатической имеет смысл, если надо обращаться к представлению своего класса-процесса - т.е. к переменным priority, timeout, пулу памяти, в котором лежит стек процесса. На пользовательском уровне все это не нужно и даже наоборот вредно. Поэтому это и изолировано от юзера. Если уж ему очень надо, пусть пишет явный адрес объекта. Но это будет уже хак, и он сам явно нарушает правила, тем самым беря на себя ответственность. Его право. Но помогать ему в этом не следует.

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

И следует четко осознать - функция-поток не является никаким пользовательским кодом. Это код ОС, которая предоставляет пользователю организовать свой код в псевдопараллельном виде и управляемый по событиям (event-driven). И поэтому надо четко отделять представление объекта-процесса от пользовательского кода, запускаемого из этого процесса. Т.ч. все логично.

Цитата(jorikdima @ Mar 30 2010, 13:48) *
Вот мне в свое время тоже этого не хватало. Я хотел создать свой тип процесса, который работал бы с портом. То есть все остальные процессы лишь вызывают функцию передачи сообщения, а упаковкой данных в мой протокол, отправкой и приемом данных по UART занимается процесс. Прелесть в том, что я могу создать несколько таких однотиповых процессов, которые будут работать принципиально одинаково с разными портами, например. Это невозможно из-за статичности Exec() (из нее нельзя обращаться к нестатическим членам класса inher_process - наследника process).

Не понял, чего вам не хватило. Не понял, зачем городить несколько однотипных процессов, ведь процесс - довольно ресурсоемкая штука, впору наоборот задуматься, как бы в один процесс упаковать несколько задач (если они выполняются в одном темпе). И самое главное, не понял, что бы вам дало, если бы Exec была бы нестатической? Ну, получили бы вы непосредственный доступ к преставлению объекта-процесса:

Код
    protected:
        TStackItem* StackPointer;
        TTimeout Timeout;
        TPriority Priority;


И что вам это дает?
AHTOXA
Цитата(dxp @ Mar 30 2010, 15:19) *
Во-первых, зачем функцию делать виртуальной? smile.gif


Я бы и на обычную согласился, но не смог придумать, как её вызвать при старте процессsmile.gif

Цитата
Во-вторых, вызов функции из start_process будет сохранять в стеке адрес возврата (и еще всякую служебную инфу в зависимости от платформы). С этим тоже можно с разным успехом побороться с помощью расширений типа __task (у IAR), но зачем?


Это да, накладных расходов довольно много. Это меня немножко смущает. Но тут надо ещё поковыряться, я ведь просто демонстрировал концепцию, наверное можно проще.

Цитата
Ну, и в-третьих (см ниже)...
Тот вариант (который ты предложил) по сути является эквивалентным этому. Только там вызов terminal.loop() упрятан в недра. Это лучше? Сомневаюсь.


Ну, мне кажется, что так - стройнее, что ли. Есть процесс, есть его поля и методы. А не так, что есть процесс, а всё остальное (поля и методы) - в отдельном, другом объекте.

Цитата
Делать фукнцию Exec нестатической имеет смысл, если надо обращаться к представлению своего класса-процесса - т.е. к переменным priority, timeout, пулу памяти, в котором лежит стек процесса.


Так с этого и началосьsmile.gif Хотел добавить отладочные функции (размер стека, плюс (может быть) время активного состояния процесса, то, сё...). Без правки кода ОС. Вот и возникла мысль.
Хотя конечно, при более внимательном рассмотрении видно, что это несколько противоречит идеологии ОСsmile.gif

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


Как это? А если она вызовет другую? smile.gif Ну и вообще, что статическая, что обычная функция при условии единственного экземпляра объекта - разницы практически никакой. (кроме возможности обращения к полям объекта)

Цитата
И следует четко осознать - функция-поток не является никаким пользовательским кодом. Это код ОС, которая предоставляет пользователю организовать свой код в псевдопараллельном виде и управляемый по событиям (event-driven). И поэтому надо четко отделять представление объекта-процесса от пользовательского кода, запускаемого из этого процесса. Т.ч. все логично.


А вот тут - ни слова не понялsmile.gif
dxp
Цитата(AHTOXA @ Mar 30 2010, 17:03) *
Ну, мне кажется, что так - стройнее, что ли. Есть процесс, есть его поля и методы. А не так, что есть процесс, а всё остальное (поля и методы) - в отдельном, другом объекте.

Стройнее оно было бы, если бы пользовательский код реально имел бы потребность в доступе к представлению (закрытой части) объекта-процесса. А такой потребности нет.


Цитата(AHTOXA @ Mar 30 2010, 17:03) *
Так с этого и началосьsmile.gif Хотел добавить отладочные функции (размер стека, плюс (может быть) время активного состояния процесса, то, сё...). Без правки кода ОС. Вот и возникла мысль.

Отладочный функционал надо разрабатывать и включать отдельно. Кстати, можно попробовать отнаследоваться от OS::process<> и там уже добавлять все, что душе угодно.


Цитата(AHTOXA @ Mar 30 2010, 17:03) *
Как это? А если она вызовет другую? smile.gif Ну и вообще, что статическая, что обычная функция при условии единственного экземпляра объекта - разницы практически никакой. (кроме возможности обращения к полям объекта)

Идеологически уже неверно. Рутовая функция должна быть одна. И статическая функция это обеспечивает в силу правил языка. В отличие от.

Цитата(AHTOXA @ Mar 30 2010, 17:03) *
А вот тут - ни слова не понялsmile.gif

template<> TTerminalProcess::Exec() - это часть ОС, это код, обеспечиваемый осью особой функциональностью. Это не пользовательский код.

Terminal.Loop() - это пользовательский код, написанный в соответствии с потребностями задачи.

Не нужно смешивать эти две вещи - они хотя и "живут" вместе, дополняя друг друга, но тем не менее являются сущностями разного рода, что, в частности, проявляется в том, что у них нет у них потребности лазить к представлению друг друга.

Такая вот идеология исходно заложена. Если хочется сделать процесс частью пользовательского кода, то как вариант попробовать, как уже сказано выше, отнаследоваться от process<> и родить свой класс. Но это уже другая идеология. И я пока не вижу каких-либо преимуществ у нее перед существующей.
AHTOXA
Цитата(dxp @ Mar 30 2010, 18:01) *
Стройнее оно было бы, если бы пользовательский код реально имел бы потребность в доступе к представлению (закрытой части) объекта-процесса. А такой потребности нет.


А отладка? smile.gif

Цитата
Отладочный функционал надо разрабатывать и включать отдельно. Кстати, можно попробовать отнаследоваться от OS::process<> и там уже добавлять все, что душе угодно.


Не выйдет. Ибо Exec() - не виртуальная. А именно её конструктор OS::process<> передаёт в конструктор TBaseProcess.
Вернее, у меня получилось, но лишь путём полной замены OS::process<> на OS::debugprocess<> с переписыванием всего функционалаsmile.gif

Цитата
Идеологически уже неверно. Рутовая функция должна быть одна. И статическая функция это обеспечивает в силу правил языка. В отличие от.


Объект всё равно тоже должен быть один, это всяко нужно контролировать. Так что не суть.

Цитата
template<> TTerminalProcess::Exec() - это часть ОС, это код, обеспечиваемый осью особой функциональностью. Это не пользовательский код.

Terminal.Loop() - это пользовательский код, написанный в соответствии с потребностями задачи.

Не нужно смешивать эти две вещи - они хотя и "живут" вместе, дополняя друг друга, но тем не менее являются сущностями разного рода, что, в частности, проявляется в том, что у них нет у них потребности лазить к представлению друг друга.


Не согласен. И то и то - пользовательский код. Я - пользователь, я завёл процесс TTerminalProcess, это мой, пользовательский процесс. И моё, пользователя, дело - писать весь код прямо в TTerminalProcess::Exec() или вынести его в отдельную функцию. От этого он не становится системным/пользовательским. А функция ОС - вызвать этот код и обеспечивать ему процессорные ресурсы.

Цитата
Такая вот идеология исходно заложена.


Это-то я прекрасно понялsmile.gif Но ведь не вредно иногда порассуждать в ту или иную сторону от исходной идеологии? Может что-то придумается дельноеsmile.gif
dxp
Цитата(AHTOXA @ Mar 30 2010, 20:28) *
А отладка? smile.gif

Про отладку писал выше - это надо функционал закладывать сразу в сорцы оси. И формализовать интерфейс управления этим делом. Остальное - костыли.

Цитата(AHTOXA @ Mar 30 2010, 20:28) *
Не выйдет. Ибо Exec() - не виртуальная. А именно её конструктор OS::process<> передаёт в конструктор TBaseProcess.
Вернее, у меня получилось, но лишь путём полной замены OS::process<> на OS::debugprocess<> с переписыванием всего функционалаsmile.gif

Да, точно. И дело не в виртуальности, а в статичности.

Цитата(AHTOXA @ Mar 30 2010, 20:28) *
Не согласен. И то и то - пользовательский код. Я - пользователь, я завёл процесс TTerminalProcess, это мой, пользовательский процесс. И моё, пользователя, дело - писать весь код прямо в TTerminalProcess::Exec() или вынести его в отдельную функцию. От этого он не становится системным/пользовательским. А функция ОС - вызвать этот код и обеспечивать ему процессорные ресурсы.

Ну, тогда вся ос - тоже пользовательский код. Критерий простой - Exec вызывается осью, значит это ее ресурс. А пользовательская функция вызывается пользователем - это его ресурс. И связи смысловой (чтобы нужно было обмениваться данными) меж ними нет.
ReAl
Цитата(dxp @ Mar 30 2010, 15:01) *
Стройнее оно было бы, если бы пользовательский код реально имел бы потребность в доступе к представлению (закрытой части) объекта-процесса. А такой потребности нет.
...
Такая вот идеология исходно заложена. Если хочется сделать процесс частью пользовательского кода, то как вариант попробовать, как уже сказано выше, отнаследоваться от process<> и родить свой класс. Но это уже другая идеология. И я пока не вижу каких-либо преимуществ у нее перед существующей.
У меня мысль "процесс как часть пользовательского кода" вытекает приблизительно из следующего:
Ну вот есть "драйверный" класс для работы с аппаратурой. Есть у него функция-оработчик ISR, есть запрятанные внутрь мьютексы и, возможно, события, клиенты дргают за открытые функции. При этом они могут уснуть по мьютксу или по ожидаию события завершения работы, могут просто в очередь что-то поставить - не важно, реализация спрятана внутри. За .do() дёрнули и всё.
Теперь что-то посложнее, или сам этот класс разросся, добавил функционала и теперь в модель "несколько функций + ISR" не укладывается, хочет процесс. Зачем менять модель работы других с этим делом? Ну стал тот же упомянутый lcd обслуживаться процессом - какое кому дело?
Конечно, можно этому классу lcd в друзья прописать класс "его" процеса, чтобы тот мог использовать недоступные остальным поля да функции, скажем, все могут писать в очередь, а читать - только этот процесс. Или вообще использовать процесс всего лишь как обёртку над функцией выполнения реальной работы из класса, как у Антона. Но какой смысл в двух сущностях, когда она, вообще говоря, одна?
Есть процесс, у него есть "системная" часть, есть связанная с его реальной работой. Всем, кроме системы, видна его "профессиональная деятельность", они к ней обращаются.

Цитата(AHTOXA @ Mar 30 2010, 13:03) *
Ну, мне кажется, что так - стройнее, что ли. Есть процесс, есть его поля и методы. А не так, что есть процесс, а всё остальное (поля и методы) - в отдельном, другом объекте.
Ну вот что-то такое (процессы как наследники базового процесса и нестатическая функция процесса) и я пытался обсуждать.
Хотел причесать думалки и что-то родить для более предметного обсуждения, но заглох - сейчас сам не могу понять, чем занимаюсь, а о сферических конях не хотелось говорить.
Можно ведь и конструктор процесса так организовать, чтобы он в подготавливаемый для первого "вызова" стековый кадр затолкал и саму функцию Exec(), и необходимый для неё this.
В результате, как минимум, обращения к "системным" и "пользовательским" полям процесса смогут идти относительно одного указателя. Что где ещё вылезет - надо смотреть.
Я хотел сначала вникнуть в ту зависшую переделку с TService, потом об этом подумать предметнее, но так и завис :-(

Цитата(dxp @ Mar 30 2010, 17:16) *
Про отладку писал выше - это надо функционал закладывать сразу в сорцы оси. И формализовать интерфейс управления этим делом. Остальное - костыли.
+1
И, возможно, начать с ( {от|в}ключаемого defin-ом или параметром шаблона ) контроля стека - опять таки, пришлось скоприровать шаблон процесса под другим именем и добавить в него такой контроль.

Цитата(dxp @ Mar 30 2010, 17:16) *
Да, точно. И дело не в виртуальности, а в статичности.
Ага. Не помню уже - с тобой это в аське пару лет назад обсуждал, или на ком-то другом потренировалася, а потом решил не трясти воздух недодуманными мыслями.

Цитата(dxp @ Mar 30 2010, 17:16) *
Критерий простой - Exec вызывается осью, значит это ее ресурс.
Похоже, таки с тобой :-)
Да, взывается осью, и эта Exec() как-то очень похожа на isr() из "класса-драйвера", вызывающуюся аппаратурой.
Но ведь остальные функции класса-драйвера находятся в нём же.
И мне эти вещи (класс-"драйвер" и процесс) кажутся подобными, хоть и на разнх уровнях.
ReAl
---------------
Тьху, вспомнил - с заталкиванием в образ стекового кадра this и указателя на нестатическую функцию проблема - указатель на функцию-член это не просто указатель на функцию, там и размер другой.
Ох уж этот склероз...

Цитата(AHTOXA @ Mar 29 2010, 18:58) *
...

Код
class TBaseProcess {
    ...
   static void start_process(TBaseProcess* proc)
};

void TBaseProcess::start_process(TBaseProcess* proc)
{
    proc->Exec();
}

Код
TBaseProcess::TBaseProcess(TStackItem* Stack, TPriority pr)
{
    Kernel.RegisterProcess(this);
...
    // на место нужного регистра затолкать reinterpret_cast<dword>(this)
    *(--StackPointer)  = reinterpret_cast<dword>(start_process);
Но всё равно лишний расход стека будет. А вот сразу Exec сюда - увы...
dxp
Цитата(ReAl @ Mar 30 2010, 21:24) *
У меня мысль "процесс как часть пользовательского кода" вытекает приблизительно из следующего:
<...>
Похоже, таки с тобой :-)
Да, взывается осью, и эта Exec() как-то очень похожа на isr() из "класса-драйвера", вызывающуюся аппаратурой.
Но ведь остальные функции класса-драйвера находятся в нём же.
И мне эти вещи (класс-"драйвер" и процесс) кажутся подобными, хоть и на разнх уровнях.

Аналогия, все же, другая. ISR - сам по себе просто функция, которая целиком отдана на откуп пользователю. У нее нет никакого внутреннего представления. Сделать ее частью пользовательского класса имеет смысл потому, что она может "хотеть" иметь доступ к представлению (закрытой части класса) своего класса. Это и является критерием, почему ISR - это часть пользовательского кода. А процесс - это часть ОС, в доступе к его представлению пользовательский код нужды не имеет. И сам процесс не имеет никакой нужды в доступе к потрохам пользовательского кода (классам). Поэтому это сущности ортогональные.

Другая аналогия. Системная функция main - она системный код или пользовательский? Именно сама функция, а не ее потроха. Функция - системная, ее система вызывает, и пользовательский код никак не управляет ее вызовом. Хотя использует ее в своих нуждах. Ровно так же и наши процессы.

Если уж хочется держать процесс вместе с кодом, который его использует, то можно попробовать объект-процесс заводить не в глобальной области видимости, а как член пользовательского класса. Главное, чтобы этот класс был сам в global scope и имел глобальное же время жизни. Ну, и не забыть конструктор процесса вызвать при вызове конструктора объемлющего класса. Я так не делал еще, но на вскидку не вижу препятствий. Это, имхо, будет примерно то, что ты хочешь. smile.gif
AHTOXA
Цитата(dxp @ Mar 30 2010, 20:16) *
Про отладку писал выше - это надо функционал закладывать сразу в сорцы оси. И формализовать интерфейс управления этим делом. Остальное - костыли.


Дык! Давайте займёмся. Я так понимаю, почти у каждого уже есть свой вариант. Осталось только выбрать самый-самыйsmile.gif

Цитата
Ну, тогда вся ос - тоже пользовательский код. Критерий простой - Exec вызывается осью, значит это ее ресурс. А пользовательская функция вызывается пользователем - это его ресурс. И связи смысловой (чтобы нужно было обмениваться данными) меж ними нет.


Опять не согласен. Все функции в конечном счёте вызываются осью. Критерий - кто написал функцию. Пользователь написал - пользовательская функция.
А к чему мы это вообще? smile.gif

Цитата(ReAl @ Mar 31 2010, 01:41) *
Код
TBaseProcess::TBaseProcess(TStackItem* Stack, TPriority pr)
{
    Kernel.RegisterProcess(this);
...
    // на место нужного регистра затолкать reinterpret_cast<dword>(this)
    *(--StackPointer)  = reinterpret_cast<dword>(start_process);
Но всё равно лишний расход стека будет. А вот сразу Exec сюда - увы...


Да, так красившеsmile.gif И - да, дополнительный расход стека всё равно есть.
dxp
Цитата(AHTOXA @ Mar 31 2010, 12:08) *
Дык! Давайте займёмся. Я так понимаю, почти у каждого уже есть свой вариант. Осталось только выбрать самый-самыйsmile.gif

Постепенно сделаем. Для начала надо определить, что именно нужно мониторить при отладке. Перечень. Второе - канал для мониторинга (терминалка или что еще). Можно в группе тему поднять.
Сергей Борщ
Цитата(dxp @ Mar 31 2010, 08:18) *
Постепенно сделаем. Для начала надо определить, что именно нужно мониторить при отладке. Перечень. Второе - канал для мониторинга (терминалка или что еще).
Канал может быть абстрактным классом. Пользователь сам допишет.
А вот мониторить нужно, мне кажется:
а) расход процессорного времени по процессам
б) в ожидании какого сервиса уснул процесс
в) расход стека.
Кому надо что-то еще - дописываем.
Причем надо иметь возможность подключать/отключать каждый из пунктов на этапе компиляции.
пункт б) я использую (правда без вывода, смотрю отладчиком), очень помогает при выяснении "какого черта этот процесс повис".
пункта а) очень не хватает.

Цитата(dxp @ Mar 31 2010, 08:18) *
Можно в группе тему поднять.
Да как-то кисло там. Сообщения то доходят на почту, то теряются. И тут людей больше.
AHTOXA
Цитата(Сергей Борщ @ Mar 31 2010, 15:27) *
Канал может быть абстрактным классом. Пользователь сам допишет.


Канал вообще не нужен, имхо. Нужны функции для получения требуемых данных, типа
TBaseProcess.GetFreeStack(), TBaseProcess.GetTimes()... етц. А уж как их вызовет пользователь - его дело.

Цитата
Да как-то кисло там. Сообщения то доходят на почту, то теряются. И тут людей больше.


+1. Ещё и с русскими сообщениями стала какая-то ерунда происходить.
dxp
Цитата(Сергей Борщ @ Mar 31 2010, 16:27) *
а) расход процессорного времени по процессам

В чем измерять? В тиках системного таймера?

Цитата(AHTOXA @ Mar 31 2010, 16:40) *
Канал вообще не нужен, имхо. Нужны функции для получения требуемых данных, типа
TBaseProcess.GetFreeStack(), TBaseProcess.GetTimes()... етц. А уж как их вызовет пользователь - его дело.

Да, пожалуй надо только сами внутренние средства приделать и интерфейс к ним, а там уж видно будет, как лучше. Можно хоть по отдельности юзать, хоть какой-нить класс монитора написать и подключать к любому доступному каналу.
Сергей Борщ
Цитата(dxp @ Mar 31 2010, 13:37) *
В чем измерять? В тиках системного таймера?
Возможно. Как вариант - что-то поточнее. Вплоть до тиков таймера, на прерывании которого висит системный таймер. Ибо частенько процесс работает меньше тика системного таймера. Пользователя чаще интересует отношение загрузки между процессами, поэтому размерность не важна.
Самый простой вариант (применялся на моей предыдущей работе в другой операционке) - в прерывании системного таймера инкрементируется счетчик в активном процессе. Раз в несколько сотен прерываний накопленные значения переносятся в результат, счетчики обнуляются. Такой подход не учитывает время, потраченное на прерывания. Для его учета надо вклиниваться в точку переключения процессов и обертку прерываний.
dxp
Цитата(Сергей Борщ @ Mar 31 2010, 18:54) *
Самый простой вариант (применялся на моей предыдущей работе в другой операционке) - в прерывании системного таймера инкрементируется счетчик в активном процессе.

Если какой-то процесс работает малое количество времени, то он вообще может не пересекаться с прерыванием системного таймера, и тогда он будет вообще казаться полностью неактивным. Наверное правильнее всего делать отсечки при переключении контекста - прямо в тактах, которыми тактируется системный таймер (реализация должна быть простой - через считывание значения таймера. Хотя тут есть трудности на некоторых платформах - например, у MSP430 системным таймером удобно взять сторожевого барбоса, а из него значения взять, насколько помню, нельзя sad.gif ). Для каждой платформы тут должна быть своя функция (в порте), которая возвращает количество тактов (не тиков) частоты, тактирующей системный таймер. Иначе, имхо, смысла мало в таком сервисе.

Еще, наверное, имеет смысл ввести счетчик переключения контекста в каждом процессе - тоже интересно посмотреть, с какой частотой щелкает процесс. Иногда такое может помочь выловить нефатальные косяки в программе (например, если процесс переключается слишком часто по сравнению с тем, как должно быть по замыслу).
AHTOXA
Цитата(dxp @ Mar 31 2010, 19:41) *
Если какой-то процесс работает малое количество времени, то он вообще может не пересекаться с прерыванием системного таймера, и тогда он будет вообще казаться полностью неактивным.

Ну и пусть себе кажется неактивным, счётчик переключений контекста процесса покажет, что это не так. Тут, имхо, важно не переборщить. Размер стека нужно вычислять точно. А количество процессорного времени - всё равно получится приблизительным. Хотя, если в порте доставать тики таймера не накладно, то можно и их конечноsmile.gif
a9d
А можно как нибудь код процессов разнести по разным файлам?
У меня файл main.cpp на APM раздулся нешуточно. Уже надоедает его по полчаса листать в поисках процесса.
AHTOXA
А что мешает? sm.gif
Например, выносим процесс TProc1:
Создаём файл proc1.h:
Код
#ifndef PROC1_H_INCLUDED
#define PROC1_H_INCLUDED
#include <scmRTOS.h>

typedef OS::process<OS::pr3, 1200> TProc1;

extern TProc1 Proc1;

#endif // PROC1_H_INCLUDED


И затем proc1.cpp:
Код
#include "proc1.h"

namespace OS
{
    template <>
    OS_PROCESS void TProc1::Exec()
    {
        for (;;)
        {
            Sleep(100)
        }
    }
}
TProc1 Proc1;

Везде, где надо обращаться к Proc1 - пишем сначала
#include "proc1.h"

Или же, чтобы не запутаться в приоритетах, вынести все объявления процессов в один файл, типа procs.h:
Код
#ifndef PROCS_H_INCLUDED
#define PROCS_H_INCLUDED
#include <scmRTOS.h>

typedef OS::process<OS::pr0, 1200> TProc0;
typedef OS::process<OS::pr1, 1200> TProc1;
typedef OS::process<OS::pr2, 1200> TProc2;

extern TProc0 Proc0;
extern TProc1 Proc1;
extern TProc2 Proc2;

#endif // PROCS_H_INCLUDED


Как-то так.
a9d
Спасибо. То, что доктор прописал.

Я пробовал разнести по разным файлам, но у меня компилятор вечно матерился.

ЗЫ: Закоментил в main.cpp namespace OS и все процессы. Вместо этого вставил инклуд #include "process/process.h".

В #include "process/process.h" описания процессов. И дальше в каждом отдельном файле код процесса.
Все компилится но компилятор никак не реагирует на ошибки в коде процесса и не включает этот код в прошивку. Т.е он собирает прошивку без процессов.
AHTOXA
Ну, эта проблема уже не связана scmRTOS. Нужно добавить новые *.cpp файлы к проекту, то есть указать компилятору, что их надо тоже компилировать. Способ зависит от компилятора (и IDE).
yuragv
Подскажите пожалуйста:

Исходные данные. Ядро CortexM3.
scmRTOS_CONTEXT_SWITCH_SCHEME 1

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

Вставка SchedISR() в обработчик сис. таймера не помогает.
Сергей Борщ
QUOTE (yuragv @ Mar 12 2011, 12:48) *
Получается что процесс с высоким приоритетом не отдает управление
процессу с низким приоритетом.
И правильно. На то у него и более высокий приоритет.
yuragv
ну я в принципе разобрался.
можно в самом низкоприоритетном процессе не отдавать управление.
высокоприоритетные заберут сами. только в них надо ставить время ожидание не меньше 2 тиков.
а то не перепадет низкоприоритетному ничего.

жалко что нельзя два и более процесса сделать с одинаковым приоритетом.
Хотелось бы писать допустим чисто интерфейсные процессы, не думая где отдавать управление
dxp
Цитата(yuragv @ Mar 12 2011, 19:31) *
жалко что нельзя два и более процесса сделать с одинаковым приоритетом.
Хотелось бы писать допустим чисто интерфейсные процессы, не думая где отдавать управление

При использовании вытесняющей ОС нужно программу строить как управляемую по событиям (event-driven). И интерфейсные процессы отлично ложатся под эту идеологию.
IgorKossak
Цитата(yuragv @ Mar 12 2011, 15:31) *
... не думая где отдавать управление

В привычке думать, где отдавать управление, чувствуется опыт работы с кооперативными ОС.
Ломайте привычки, меняйте идеологию.
a9d
У меня состоит задача в том, чтобы по запросу возвращать самые актуальные данные. У меня два процесса один постоянно опрашивает датчики, обрабатывает показания записывает их в структуру и отсылает меседж. Второй процесс опрашивает трансивер и если пришел запрос, то проверяет is_non_empty() меседжа, и если там что-то есть, то отправляет данные.

1) Это нормально многократно перезаписывать сообщение не получая его?
2) Процесс может отправить сам себе сообщение? Иногда такое, тоже требуется.
3) Используя is_non_empty(), нужно ли потом вручную вызывать reset()?
4) При использовании wait() флаг наличия сообщения сбрасывается сам?
dxp
Цитата(a9d @ Mar 18 2011, 14:44) *
1) Это нормально многократно перезаписывать сообщение не получая его?

Можно, только прежние тела сообщения будут затёрты. Насколько это вас устраивает, я не знаю.

Цитата(a9d @ Mar 18 2011, 14:44) *
2) Процесс может отправить сам себе сообщение? Иногда такое, тоже требуется.

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

Цитата(a9d @ Mar 18 2011, 14:44) *
3) Используя is_non_empty(), нужно ли потом вручную вызывать reset()?

Можно. Почему нельзя?

Цитата(a9d @ Mar 18 2011, 14:44) *
4) При использовании wait() флаг наличия сообщения сбрасывается сам?

А в исходник посмотреть? sm.gif
Если на момент посылки уже были ожидающие процессы, то флаг наличия и не будет установлен, ибо незачем.
a9d
Мне не нужны старые данные. Поэтому вариант с затиранием подходит. Интересовало насколько это корректно.

Отсылать самому себе сигнал очень удобно в следующей ситуации.
Есть два процесса. Процесс 1 производит опрос, Процесс 2 управляющий.
При старте системы Процесс 1 в начале цикла ожидает флаг, Процесс 2 устанавливает флаг тем самым разрешая опрос. После успешного опроса Процесс 1 выставляет сам для себя флаг, если опрос был не успешен то он отправляет месседж с ошибкой и не выставляет флаг. Тем самым Процесс 1 в случае ошибке уснет, пока Процесс 2 не решит, что с ним делать.
Интересовало насколько это корректно.

Насчет месседжей я спрашивал другое. Если я использую is_non_empty(), то при получении сообщения я не нашел места где флаг сбрасывается. А значит нужно вызывать сброс вручную?

А в случае с wait() как я понимаю флаг будет сброшен сам и вызове reset() нет необходимости? В документации не написано в каких ситуациях флаг сбрасывается. На этот вопрос уже нашел ответ. Флаг сбрасывается.
dxp
Цитата(a9d @ Mar 18 2011, 21:40) *
Отсылать самому себе сигнал очень удобно в следующей ситуации.
Есть два процесса. Процесс 1 производит опрос, Процесс 2 управляющий.
При старте системы Процесс 1 в начале цикла ожидает флаг, Процесс 2 устанавливает флаг тем самым разрешая опрос. После успешного опроса Процесс 1 выставляет сам для себя флаг, если опрос был не успешен то он отправляет месседж с ошибкой и не выставляет флаг. Тем самым Процесс 1 в случае ошибке уснет, пока Процесс 2 не решит, что с ним делать.
Интересовало насколько это корректно.

Не очень понял, какой флаг имеется в виду. Флаг события (TEventFlag) или внутренняя переменная message NonEmpty? Лучше такие вещи пояснять на примере псевдокода.

Цитата(a9d @ Mar 18 2011, 21:40) *
Насчет месседжей я спрашивал другое. Если я использую is_non_empty(), то при получении сообщения я не нашел места где флаг сбрасывается. А значит нужно вызывать сброс вручную?

Получение сообщения производится в функции wait(). Там же и обрабатывается внутренняя переменная (NonEmpty). Эта переменная устанавливается при передаче только если в этот момент никто не ждёт сообщения (тогда при первой попытке встать на ожидание процесс "увидит", что сообщение уже лежит, и его можно использовать). Если же есть ожидающие процессы, то NonEmpty не устанавливается, т.к. в этом нет необходимости - просто процесс, который находился в ожидании, будет переведён в готовые к выполнению.

Следует иметь в виду, что внутренняя переменная NonEmpty - это не флаг-признак наличия сообщения. Это именно внутренняя переменная, используемая потрохами сервиса для своих служебных целей, поэтому ориентироваться на неё не следует. Работать нужно с интерфейсом - открытыми функциями-членами. Если приём сообщения осуществляется с помощью is_non_empty() (что правильнее назвать не приёмом, а проверкой наличия), то, конечно, и сброс состояния тоже придётся делать вручную.

Цитата(a9d @ Mar 18 2011, 21:40) *
А в случае с wait() как я понимаю флаг будет сброшен сам и вызове reset() нет необходимости? В документации не написано в каких ситуациях флаг сбрасывается. На этот вопрос уже нашел ответ. Флаг сбрасывается.

В документации описано функционирование с точки зрения интерфейса сервиса. А работа с NonEmpty - это уже детали реализации, они, конечно, как обычно, не являются предметом документирования. Детали реализации вообще могут меняться вполне произвольно, при сохранении интерфейса класса и логики его работы работы, поэтому ориентироваться на них не надо.
a9d
Код
OS::TEventFlag flg;
OS::message<char> err;
....................
...................

Proc1              //управляющий
{

   flg.Signal();
  
   while(1)
   {
      err.wait();  //ждем сообщение об ошибке
      ............
      ............
   }
}


Proc2           //рабочий
{
    
    while(1)
    {
       flg.wait();
      
       if(Opros)
       {
          ..........
          flg.Signal();
          ..........
          Sleep(1);  
        }
        else
        {
          err=1;          //сообщаем номер ошибки
          err.send();    //отправляем месседж
                              //флаг не взведен и при следующем проходе процесс уснет
        }
                
    }
}


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


Теперь про месседжи.
У меня один процесс главный он через сообщения рассылает другим процессам команды. Процессы которые принимают команды не должны работать постоянно.

Это получается более корректно и правильно написать так
Код
while(1)
{
    if(msg.wait(1))
    {
       buf=msg;
       .....................
    }
    
    ..........................
}


Чем так

Код
while(1)
{
    if(msg.is_non_empty())
    {
         buf=msg;
         msg.reset();
         ................
     }
  
   .................................
   Sleep(1);
}


dxp
Я бы сделал так.
Код
OS::TEventFlag flg;
OS::message<char> err;

Proc1              //управляющий
{

   flg.Signal();
  
   while(1)
   {
      err.wait();  //ждем сообщение об ошибке
      ............
      ............
   }
}


Proc2           //рабочий
{
    while(1)
    {
        flg.wait();
      
        while(1)
        {
            if(Opros)
            {
                ..........
                Sleep(1);  
            }
            else
            {
                err=1;        //сообщаем номер ошибки
                err.send();   //отправляем месседж
                break;
            }
        }
    }
}



Цитата(a9d @ Mar 21 2011, 20:02) *
Это получается более корректно и правильно написать так
Код
while(1)
{
    if(msg.wait(1))
    {
       buf=msg;
       .....................
    }
    
    ..........................
}

Конечно. Это и есть штатный и основной способ использования. Ждём сообщения, дождались - обрабатываем. А is_non_empty(), reset() - это вспомогательные фукнции
a9d
Код
Proc2           //рабочий
{
    while(1)
    {
        flg.wait();
      
        while(1)
        {
            if(Opros)
            {
                ..........
                Sleep(1);  
            }
            else
            {
                err=1;        //сообщаем номер ошибки
                err.send();   //отправляем месседж
                break;
            }
        }
    }
}


Этот код получше. До такого я не додумался))
a9d
Через неделю планирую начать проект на STM32W
Под STM32 есть порт версии 3.10. А как обстоят дела с версией 4.0 ? Порт по идее есть, но он стабилен? Имеет смысл его использовать в проекте?
shreck
Цитата(a9d @ Sep 6 2011, 01:20) *
Через неделю планирую начать проект на STM32W
Под STM32 есть порт версии 3.10. А как обстоят дела с версией 4.0 ? Порт по идее есть, но он стабилен? Имеет смысл его использовать в проекте?

scmRTOS версии 4 и порт для Cortex-M3 (без разницы какой конкретно контроллер) под нее вполне стабильны.
Здесь можно скачать scmRTOS и порт версии 4.
Здесь и здесь документация.
a9d
За доку на кортексы спасибо. О ее существовании даже не знал.
a9d
Заметил, что в 4 версии для cortex-m3 есть файлик startup.c. Хотя в тоже время имеется "\STM32\STM32F10x_StdPeriph_Lib_V3.5.0\Libraries\CMSIS\CM3\DeviceSupport\ST\STM32F10x\startup\arm" тоже самое только на ассемблере.
Почему не используется ассемблерная версия?

Также в примерах содержаться файлы core_cm3.h и stm32f10x.h которые тоже содержаться STM32F10x_StdPeriph_Lib_V3.5.0.
Зачем их добавлять в примеры?

Настроил отладчик ST-Link (использую дисковери) как написано в статье http://we.easyelectronics.ru/STM32/otladka...eclipsegcc.html .
В проектах без ОС отладка работает.
А вот с ОС неработает. Зависает здесь
Код
void Reset_Handler(void)
{
    __Init_Data();

    main();
}


или

Код
void Default_Handler(void)
{
    for (;;);
}


Хотя проект рабочий и я вижу как плата мигает диодами.
AHTOXA
Цитата(a9d @ Sep 12 2011, 01:13) *
Заметил, что в 4 версии для cortex-m3 есть файлик startup.c. Хотя в тоже время имеется "\STM32\STM32F10x_StdPeriph_Lib_V3.5.0\Libraries\CMSIS\CM3\DeviceSupport\ST\STM32F10x\startup\arm" тоже самое только на ассемблере.

Где "имеется"? sm.gif
Дело в том, что в то время, когда создавался порт, в составе CMSIS не было ассемблерных стартапов. Да и нынешние - не будут работать с C++.
К тому же, версии библиотек от ST имеют нехорошую тенденцию меняться в самый неподходящий момент. Поэтому в состав примеров были включены нужные для работы примеров файлы. Обратите внимание, для собственно ОС эти файлы не нужны, порт - универсален для любых Cortex-M3.

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

С вашей версией выходит
Код
Invoking: ARM Sourcery Windows GNU Print Size
arm-none-eabi-size  --format=berkeley scmRTOS_stm32v3.elf
   text       data        bss        dec        hex    filename
   1872          4       1680       3556        de4    scmRTOS_stm32v3.elf
Finished building: scmRTOS_stm32v3.siz


с версией от STM
Код
Invoking: ARM Sourcery Windows GNU Print Size
arm-none-eabi-size  --format=berkeley scmRTOS_stm32v3.elf
   text       data        bss        dec        hex    filename
   1912          4       1680       3596        e0c    scmRTOS_stm32v3.elf
Finished building: scmRTOS_stm32v3.siz


Но один фиг отладчик не работает.
AHTOXA
Компилируется != работаетsm.gif
В том варианте startup_stm32f10x_xx.s, что есть у меня (V3.3.0), не вызываются конструкторы глобальных объектов. Хотя может сейчас уже поправили...
a9d
Уже вышла версия 3.5.

Также нашел объяснение с отладкой
Цитата
Brain13
Aug 12 2011, 08:23
StAlexy, у Вас в коде полная мешанина: разрешение прерывания SPI, инициализация TIM, разрешение прерывания TIM, инициализация SPI. Так делать нехорошо, когда проект вырастет сложно будет разобраться.

Если процессор зависает, но в обработчик не заходит, то скорее всего прерывание вызывается, но обработчика просто нет, поэтому он виснет в Default_Handler, которы по стандарту представляет из себя бесконечный цикл. Если у вас смесь С и С++. То необходимо добавить extern "C" при обьявлении обработчика, то есть:
Код
extern "C" void TIM2_IRQHandler(void);
Я на такое тоже натыкался, теперь все обработчики так описываю.

Посмотрите регистр статуса SPI, биты запроса прерывания выставлены? У Вас есть возможность узнать где зависла программа?

PS: уход от проблемы - не решение, разберитесь с прерываниями, без них - тяжко.


Сделал так но результата не дало.
#define OS_INTERRUPT extern "C"
a9d
Начал копаться в sysinit.cpp. Читабельность оставляет желать лучшего.

Код
RCC->CFGR = (RCC->CFGR & ~(RCC_CFGR_HPRE | RCC_CFGR_PPRE2 | RCC_CFGR_PPRE1 | RCC_CFGR_ADCPRE)) |
            RCC_CFGR_HPRE_DIV1 | RCC_CFGR_PPRE2_DIV1 | RCC_CFGR_PPRE1_DIV2 | RCC_CFGR_ADCPRE_DIV6;


От таких конструкций мозг закипает. Такое намного проще и удобней разбить на несколько команд. При этом возрастет читабельность.

Также в этом файле нашел следующее
Код
#if (!defined STM32F10X_LD_VL) && (!defined STM32F10X_MD_VL)
    // Enable Prefetch Buffer
    FLASH->ACR |= FLASH_ACR_PRFTBE;

    // Flash 2 wait state (if freq in 24..48 MHz range - 1WS.)
    FLASH->ACR = (FLASH->ACR & ~FLASH_ACR_LATENCY) | FLASH_ACR_LATENCY_2;

#endif


Но в ACR есть всего один бит и эта конструкция его никак не трогает.
Нажмите для просмотра прикрепленного файла

Также у value line потолок 24 Mhz. Отсюда вопрос. Что это такое?
AHTOXA
Цитата(a9d @ Sep 13 2011, 00:54) *
Уже вышла версия 3.5.

И? Есть там вызов конструкторов?
Цитата(a9d @ Sep 13 2011, 00:54) *
#define OS_INTERRUPT extern "C"

Попробуйте просто написать extern "C" перед функцией обработчика прерывания.
Цитата(a9d @ Sep 13 2011, 02:05) *
От таких конструкций мозг закипает. Такое намного проще и удобней разбить на несколько команд. При этом возрастет читабельность.

Так разбейте, какие проблемы? Примеры - это же не догма, а лишь руководство к действию sm.gif
Цитата(a9d @ Sep 13 2011, 02:05) *
Также в этом файле нашел следующее
Код
#if (!defined STM32F10X_LD_VL) && (!defined STM32F10X_MD_VL)

...
Также у value line потолок 24 Mhz. Отсюда вопрос. Что это такое?

Видите первую строчку? Эта часть - не для value line.
Для просмотра полной версии этой страницы, пожалуйста, пройдите по ссылке.
Invision Power Board © 2001-2025 Invision Power Services, Inc.