|
Начало работы with scmRTOS, Несколько вопросиков |
|
|
|
Feb 20 2008, 14:56
|
Частый гость
 
Группа: Новичок
Сообщений: 83
Регистрация: 2-02-06
Пользователь №: 13 912

|
Хочется научиться работать с этой штукой - scmRTOS & AVR(Atmega8) & IAR 4.30A ! Почитал темы которые есть на форуме, почитал User's Manual v2. Возникло некторое количество вопросов: 1. Какая последовательность создания проекта: мои предположения - создаем в IAR новый проект, тискаем добавить файлы в проект и добавляем OS_Kernel.cpp , OS_Services.cpp , OS_Target_asm.s90 , OS_Target_cpp.cpp , usrlib.cpp. В maim.cpp пишем Код #include <scmRTOS.h> . Затем каким то образом нужно создать самому как я понял scmRTOS_TARGET_CFG.h и scmRTOS_CONFIG.h, но как не ясно или их нужно тупо скопировать из примера автора и если что нада то менять. 2. Почему в примерах автор добавляет Код void OS::SystemTimerUserHook() { } void OS::IdleProcessUserHook() { } Так нужно делать всегда ? 3. Дальше >> понятно что для AVR передачу управления можно осуществить сгенерировав прерывание например от компоратора как описано в документации, но непонятно как нужно оформить функцию обработки этого прерывания, и чем она будет отличаться от функции обработки других прерываний. О взаимодействии между потоками пока вроде понятно. Может кто нить может описать последовательность начальных действий и привести пример кода или хотя бы шапку , где есть процессы и обработчики прерываний и передача управления. Мог написать что - нибудь глупое, потому как в круг моих понятий scmRTOS пока входит очень туманно или вообще не входит.
|
|
|
|
18 страниц
1 2 3 > »
|
 |
Ответов
(1 - 99)
|
Feb 21 2008, 04:11
|

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

|
Цитата(URANst @ Feb 20 2008, 20:56)  . Затем каким то образом нужно создать самому как я понял scmRTOS_TARGET_CFG.h и scmRTOS_CONFIG.h, но как не ясно или их нужно тупо скопировать из примера автора и если что нада то менять. Можно и так, и так. Как больше нравится. Чем плохо взять имеющиеся и откорретировать под свои потребности? Цитата(URANst @ Feb 20 2008, 20:56)  2. Почему в примерах автор добавляет Код void OS::SystemTimerUserHook() { } void OS::IdleProcessUserHook() { } В примерах для иллюстрации. Если они нужны, оставьте и используйте, не нужны, уберите. Все в ваших руках. Цитата(URANst @ Feb 20 2008, 20:56)  Так нужно делать всегда ? 3. Дальше >> понятно что для AVR передачу управления можно осуществить сгенерировав прерывание например от компоратора как описано в документации, но непонятно как нужно оформить функцию обработки этого прерывания, и чем она будет отличаться от функции обработки других прерываний.
О взаимодействии между потоками пока вроде понятно.
Может кто нить может описать последовательность начальных действий и привести пример кода или хотя бы шапку , где есть процессы и обработчики прерываний и передача управления. Есть полноценный рабочий пример. Под IAR. Возьмите его за основу. - Используйте в качестве "рыбы" для своего проекта.
- Используйте его в качестве референса, когда возникает вопрос, как сделать ту или иную базовую вещь. Пока не придет собственное устойчивое понимание (тогда будете сразу делать по-своему).
- Используйте его для исследования того, что реально происходит в процессоре, гоняя пример в отладчике.
--------------------
«Отыщи всему начало, и ты многое поймёшь» К. Прутков
|
|
|
|
|
Feb 21 2008, 06:50
|
Частый гость
 
Группа: Новичок
Сообщений: 83
Регистрация: 2-02-06
Пользователь №: 13 912

|
Вот взял из примера , это обработчик прерывания, кторый как я понял работает для передачи управления, но каким образо не понятно : Код #pragma vector=TIMER1_COMPA_vect OS_INTERRUPT void Timer1_period_ISR() { OS::TISRW_SS ISRW;
ENABLE_NESTED_INTERRUPTS();
//-------------------------------------------------- // // Message test // // Send data as message // TMamont m; // create message content
m.src = TMamont::ISR_SRC; m.data = 10; MamontMsg = m; // put the content to the OS::message object PORTB |= (1 << 4); MamontMsg.sendISR(); // send the message } В main разрешены 3 прерывания , а обработчик описан только один, че за ..... Код int main() { DDRB |= (1 << 4);
TCCR1B |= (1 << WGM12); // CTC mode OCR1A = 10005; // TCCR1B |= (1 << CS10); // Timer1 run with prescaling 1 TIMSK |= (1 << OCIE1A); // Timer1 OC interrupt enable--------------1 ое
TCCR0 = 0x03; // Start System Timer TIMSK |= (1 << TOIE0); //-----------------------------------------------2 ое
ACSR |= (1 << ACBG) | (1 << ACIE); /* Ref ON, IE ON */ -----------3 е DDRB |= (1 << 3); /* AIN1*/
OS::Run(); }
|
|
|
|
|
Feb 21 2008, 11:55
|
Частый гость
 
Группа: Новичок
Сообщений: 83
Регистрация: 2-02-06
Пользователь №: 13 912

|
С предыдущим вроде разобрался, все оказалось проще чем я думал, вопросы о следующем: Что зачит вот это Код ENABLE_NESTED_INTERRUPTS(); написанное в обработчике прерывания Почему в main нету глобального разрешения прерывания, как без него может произойти прерывание от таймера да и вообще любое, или где то в ядре прописывается (я понял что так и есть ) ?, Зачем нужны прерывания от системного таймера ? Все относится к примерам из папки самой операционки. И еще как в IAR можно изменить имя проекта ?
Сообщение отредактировал URANst - Feb 21 2008, 12:00
|
|
|
|
|
Mar 2 2008, 17:02
|
Знающий
   
Группа: Свой
Сообщений: 589
Регистрация: 24-04-05
Пользователь №: 4 447

|
Тоже решил попробовать эту ОС. Долго не мог решится из-за отсутствия опыта работы в С++. Хочу "прикрутить" эту ОС к своему проекту на Си. Кстати, как это правильней сделать : добавить мои исходники на Си к smcRTOS или можно еще как-то? Взял тестовый пример. Удачно скомпилился под Megs32. А под Megs324P выдает кучу ошибок (порядка 270). В чем засада, не пойму. Ведь Мега324 должна быть полностью совместима с Мегой32 ? PS. Может быть проблема в этом: Цитата I/O Mapping and SRAM The I/O memory space contains 64 addresses for CPU peripheral control registers. The ATmega164P/324P/644P I/O space and I/O range are changed and extended compared to ATmega16/32. The extended I/O space goes from 0x60 to 0xFF in data memory space where ST/STS/STD and LD/LDS/LDD instructions must be used. The memory map is slightly different between the ATmega16/32 and the ATmega164P/324P/644P due to extended I/O space. The ATmega164P/324P/644P internal data SRAM addressing starts at 0x100 as opposed to 0x60 in ATmega16/32. Это из документа Цитата AVR505: Migration between ATmega16/32 and ATmega164P/324P/644P PS2/// Вот так всегда. Ломаешь голову второй день... А как-только напишешь в форум, так сразу нашел в чем проблема. У Меги324 в файле iom324p.h есть определения: Цитата /* SREG */ #define I 7 #define T 6 #define H 5 #define S 4 #define V 3 #define N 2 #define Z 1 #define C 0 Выдает первые ошибки на эти строки: Код Error[Pe040]: expected an identifier
template<typename T, word size, class S> friend class channel; template<typename T> friend class message; Т.е. я так понял имеется переопределение идентификаторов T и S. Главное, что раньше я сталкивался с такой проблемой, когда присваивал переменным одну букву... И все-таки, как бы эту проблему "красиво" решить? PS3. Я могу ошибиться, но разве OS_Kernel не имеет собственного namespace дабы избегать подобные проблемы?
|
|
|
|
|
Mar 3 2008, 04:55
|

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

|
Цитата(alux @ Mar 2 2008, 23:02)  Выдает первые ошибки на эти строки: Код Error[Pe040]: expected an identifier
template<typename T, word size, class S> friend class channel; template<typename T> friend class message; Т.е. я так понял имеется переопределение идентификаторов T и S. Главное, что раньше я сталкивался с такой проблемой, когда присваивал переменным одну букву... И все-таки, как бы эту проблему "красиво" решить? PS3. Я могу ошибиться, но разве OS_Kernel не имеет собственного namespace дабы избегать подобные проблемы?  Тут дело не в пространствах имен, а в том, что препроцессор работает до компилятора и делает просто тупую текстовую подстановку, поэтому к моменту работы компилятора (анализа текста, разрешения имен и прочего) вместо T уже все подставлено. Яркий пример кривизны препроцессора и хорошая иллюстрация, почему его желательно по максимуму избегать. Ну, а в данном конкретном случае можно только "порадоваться" за разработчиков IAR, слепивших такую пакостную "бомбочку". Это ж надо догадаться задефайнить такие короткие имена! Как-то я на MSP430 тоже плотно присел, пока выяснял, что локальное имя N работает не так, как задумано - тоже иаровский дефайн был. Только там хитрее получилось - не так явно вылезало, ошибка была совсем не про конфликт имен. Препроцессор мастдай.
--------------------
«Отыщи всему начало, и ты многое поймёшь» К. Прутков
|
|
|
|
|
Mar 3 2008, 06:53
|
Знающий
   
Группа: Свой
Сообщений: 589
Регистрация: 24-04-05
Пользователь №: 4 447

|
Так что же мне делать в этом конкретном случае кроме как закоментировать проблемные дефайны в iom324p.h ? Как бы потом это не вылезло боком... Может не в тему(вопроса) будет сказано. Нашел еще одну интересную ОС - eXtreme Minimal Kernel XMK Почему-то на форуме о ней даже не упоминалось. Разработчик утверждает, что нет других мультизадачных preemtive free RTOS, которая будет работать в такой минимальной конфигурации (340 bytes of ROM and 18 bytes of RAM).
|
|
|
|
|
Mar 4 2008, 14:13
|
Знающий
   
Группа: Свой
Сообщений: 589
Регистрация: 24-04-05
Пользователь №: 4 447

|
При включении моих исходников проекта на Си в проект smcRTOS (пример 1-EventFlag)возникла проблема. Код Error[e18]: Range error, PC offset out of range. Valid range is -4096 (-0x1000) to 4094 (0x0FFE). File: D:\...\scmRTOS\AVR\OS_Target_asm.s90, Line: 234 Source: xjmp ContextSwitcher_ISR Where $ = #no label found# + 0x5C [0x5C] in module "scmRTOS_Asm" (D:\...\1-EventFlag\Release\Obj\OS_Target_asm.r90), offset 0x5C in segment part 1, segment INTVEC What: #no label found# - ($ + 2) [0x1388] Allowed range: 0xFFFFF000 - 0xFFF Operand: #no label found# [0x13e6] in module scmRTOS_Asm (D:\...\1-EventFlag\Release\Obj\OS_Target_asm.r90), Offset 0x0 in segment part 2, segment CODE Мой проект пока состоит из последовательного вызовов функций инициализации различной переферии. Ошибка возникает при подключении lcd_Init(); Отдельно мой проект отлажен под Мега324P. Занимал около 20кБ ROM и около 500 байт RAM. Пробовал изменить на Мега 644P и изменить размеры CSTACK, RSTACK. Не помогло. Как решить эту проблему?
|
|
|
|
|
Mar 4 2008, 16:02
|

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

|
Цитата(alux @ Mar 4 2008, 16:13)  Мой проект пока состоит из последовательного вызовов функций инициализации различной переферии. Каким-то образом обработчик прерывания переключения контекста оказывается в месте, куда не "дотягивается" xjmp из области векторов. Судя по ограничению +/- 4К вместо макроса xjmp подставляется RJMP, хотя нужен JMP. Глянул в исходники: Код #if (A90_PROC_OPTION == 0) || (A90_PROC_OPTION == 1) #define xcall rcall #define xjmp rjmp #else #define xcall call #define xjmp jmp #endif Где определяется A90_PROC_OPTION я не нашел - ни в исходниках, ни в описании ассемблера. Неопределенный символ считается равным нулю. Можно предположить, что перед этим должна быть (утерянная) строка Код #define A90_PROC_OPTION ((__TID__ >> 4) & 0x0F) dxp завтра разберется, откуда вылезла эта бага.
--------------------
На любой вопрос даю любой ответ"Write code that is guaranteed to work, not code that doesn’t seem to break" ( C++ FAQ)
|
|
|
|
|
Mar 5 2008, 04:55
|

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

|
Цитата(Сергей Борщ @ Mar 4 2008, 22:02)  Где определяется A90_PROC_OPTION я не нашел - ни в исходниках, ни в описании ассемблера. Неопределенный символ считается равным нулю. Можно предположить, что перед этим должна быть (утерянная) строка Код #define A90_PROC_OPTION ((__TID__ >> 4) & 0x0F) Действительно, косяк. Как он вкрался, не помню хоть убей. Пофиксено. P.S. Лишний раз "порадовался" "красоте" иаровского подхода - поменял имя МК и началось... Регистра TIMSK там уже нет, вместо него есть TIMSK0 и т.д. Я понимаю, что одного регистра не хватает, но зачем было имя-то менять? Пусть бы для совместимости осталось. То же самое касается TCCR0, которого уже тоже нет, а есть два с буквами на конце. Почему бы было не оставить для совместимости старое имя? Не понимаю. И уж зачем вообще было менять имя вектора ANA_COMP_vect на ANALOG_COMP_vect? А если заменили, то и вставили бы код для совместимости. И такие косяки у них с незапамятных времен. Такое впечатление, что о портировании кода даже внутри семейства они вообще не думают, а заголовки для разных процов лабают разные люди, которые никак свои действия друг с другом не координируют. Про упомянутое /* SREG */ #define I 7 #define T 6 #define H 5 #define S 4 #define V 3 #define N 2 #define Z 1 #define C 0 я вообще молчу - яркий образчик бездумного подхода и безответственности. Причем что интересно - это не у всех процов есть такое определение, а только у избранного круга: iom164.h iom324.h iom644.h iom644p.h iopwm2.h iopwm3.h Т.е. больше похоже на чью-то личную инициативу, нежели на общий подход. Бардак, короче.
--------------------
«Отыщи всему начало, и ты многое поймёшь» К. Прутков
|
|
|
|
|
Mar 5 2008, 07:36
|
Знающий
   
Группа: Свой
Сообщений: 589
Регистрация: 24-04-05
Пользователь №: 4 447

|
Цитата(dxp @ Mar 5 2008, 08:55)  Регистра TIMSK там уже нет, вместо него есть TIMSK0. Бардак, короче.  Вот только мне не понятно для чего добавили 0 в имена битов SPI ? Цитата /* SPSR */ #define SPIF0 7 #define WCOL0 6 #define SPI2X0 0
/* SPCR */ #define SPIE0 7 #define SPE0 6 #define DORD0 5 #define MSTR0 4 #define CPOL0 3 #define CPHA0 2 #define SPR01 1 #define SPR00 0 Не может же быть несколько SPI-интерфейсов на борту... Или может? Мне кажется, что это они скорее сделали по привычке... Хотя в даташите указаны "правильные" имена. По поводу регистра TIMSK0... Почему не исправлено? Добавить то всего три строчки. Цитата #ifndef TIMSK0 #define TIMSK TIMSK0 #endif Если гора не идет к Магомету, то Магомет идет к горе  Цитата(dxp @ Mar 5 2008, 08:55)  Действительно, косяк. Как он вкрался, не помню хоть убей. Еще не начал работать с OS, а уже отлавливаю косяки. То ли еще будет Цитата(IgorKossak @ Mar 5 2008, 00:09)  Сдаётся мне, что если откроете overview завтра, то там окажется завтрашнее число.  Точно Цитата(alux @ Mar 4 2008, 20:09)  Имеет ли значение уровень оптимизации компилятора для smcRTOS ? Дело в том, что у jacOS есть примечание для IAR C/EC++ for AVR 4.11A/W32 : Цитата Вероятны проблемы при установке опций оптимизации Code motion (?) Cross Call (?) И последнее. Рискну задать глупый вопрос. Если в проекте используется прерывание, но оно не является источником события для процессов, обязательно ли обработчик прерывания должен иметь тип OS_INTERRUPT и на входе ISR создавать объект OS::TISRW_SS ISRW ? И можно ли разрешать глобальные прерывания до OS::Run() ?
|
|
|
|
|
Mar 5 2008, 10:49
|

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

|
Цитата(alux @ Mar 5 2008, 09:36)  Вот только мне не понятно для чего добавили 0 в имена битов SPI ? Не может же быть несколько SPI-интерфейсов на борту... Или может? Я думаю что может. И скорее всего добавление 0 вызвано тем, что они начали таки движение в сторону унификации. Для новых кристаллов биты и регистры будут "пронумерованы" а старые потихоньку вымрут. Процесс перехода всегда сложный, но что поделать... Цитата(alux @ Mar 5 2008, 09:36)  Еще не начал работать с OS, а уже отлавливаю косяки. То ли еще будет  Вы же понимаете, что мы не можем проверить наши исходники на всех возможных процессорах  Мы можем только предполагать, что раз программа работает на ATmegaX (LPC2xxx, BFxxx и т.д.), то она будет работать и на ATmegaY (LPC2yyy, BFyyy и т.д.), потому что все AVR (LPC2, BF и т.д.) похожи. К сожалению, они похожи не во всем, отсюда и такие косяки. Чем больше их найдете вы - тем меньше их останется тем, кто будет использовать ее после вас. Кроме того, я, например, уже не работаю с ADuC70xx и STR71x, порты для которых написал. Для STR71x у меня не осталось железа, на котором я мог бы проверить вносимые изменения. Поэтому исправления найденных в других портах багов или улучшения переносятся в этот порт без проверки. Вы бы что предпочли - старую версию, в которой есть известные глюки или новую, в которой они исправлены, но, возможно, внесены новые?  Цитата(alux @ Mar 5 2008, 09:36)  Рискну задать глупый вопрос. Если в проекте используется прерывание, но оно не является источником события для процессов, обязательно ли обработчик прерывания должен иметь тип OS_INTERRUPT и на входе ISR создавать объект OS::TISRW_SS ISRW ? При отсуствиии в этом прерывании разрешения вложенных - нет, не обязательно. Но помните о размере стека - прерывание будет работать на стеке текущего процесса, значит стеки всех процессов надо будет увеличить на размер, требуемый прерыванию. Если использовать ISRW_SS, то для прерываний организуется отдельный стек, но за это надо платить увеличением времени обработки прерывания. Цитата(alux @ Mar 5 2008, 09:36)  И можно ли разрешать глобальные прерывания до OS::Run() ? Если в разрешаемых прерываниях не используются сервисы ОС, их обработчики не имеют OS::TISRW(__SS), то можно, но зачем?
--------------------
На любой вопрос даю любой ответ"Write code that is guaranteed to work, not code that doesn’t seem to break" ( C++ FAQ)
|
|
|
|
|
Mar 5 2008, 11:14
|

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

|
Цитата(alux @ Mar 5 2008, 13:36)  Почему не исправлено? Добавить то всего три строчки. Это должы добавлять те, кто модифицируют код. Чтобы для пользователя было прозрачно и портирование рабочего кода на новый кристалл проходило по максимуму безболезненно. Цитата(alux @ Mar 5 2008, 13:36)  Если гора не идет к Магомету, то Магомет идет к горе   Цитата(alux @ Mar 5 2008, 13:36)  Еще не начал работать с OS, а уже отлавливаю косяки. То ли еще будет  Уже начали.  Кстати, у меня эта ошибка не проявилась. Видимо, потому, что вектор достал до адреса. В любом случае этот не опасный баг - он отлавливается на этапе сборки. Вот те, которые при сборке не проявляются, а на рантайме вылезают - эти опасные. Цитата(alux @ Mar 5 2008, 13:36)  И можно ли разрешать глобальные прерывания до OS::Run() ? Можно. Но зачем? Ведь после OS::Run() управление будет передано сразу процессам, они загрузят свои стеки и свои значения регистра SREG, поэтому разрешение прерываний до запуска оси смысла имеет мало. Если только не планируете долго сидеть до запуска, чтобы успеть по прерываниям попрыгать.
--------------------
«Отыщи всему начало, и ты многое поймёшь» К. Прутков
|
|
|
|
|
Mar 6 2008, 06:53
|
Группа: Новичок
Сообщений: 9
Регистрация: 15-02-08
Пользователь №: 35 076

|
Цитата(Сергей Борщ @ Mar 5 2008, 14:49)  Но помните о размере стека - прерывание будет работать на стеке текущего процесса, значит стеки всех процессов надо будет увеличить на размер, требуемый прерыванию. Как узнать максимальное потребление стека задачами? Можно ли проинициализировать все стеки каким-либо значением вроде 0xA5, как в FreeRTOS, и главное, как сделать это проще всего в scmRTOS? Или например в бутлоадере забить всю память байтами 0xA5, а затем прыгать на начало основной программы?
|
|
|
|
|
Mar 6 2008, 13:26
|

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

|
Цитата(Сергей С. @ Mar 6 2008, 08:53)  Как узнать максимальное потребление стека задачами? Можно ли проинициализировать все стеки каким-либо значением вроде 0xA5, как в FreeRTOS, и главное, как сделать это проще всего в scmRTOS? Память стеков уже по умолчанию проинициализирована значением, только не 0xA5 а 0. В портах для ARM на верхушке контекста лежит содержимое CPSR, который не может быть равен нулю (конструкция у него такая). Поэтому я смотрю визуально (отладчиком или через терминал), где первый "не-ноль" - там было занято. Если есть желание забить каким-то своим значением - допишите memset в конструктор TProcess. Цитата(Сергей С. @ Mar 6 2008, 08:53)  Или например в бутлоадере забить всю память байтами 0xA5, а затем прыгать на начало основной программы? Не пройдет - стеки попадают в область глобальных неинициализированных переменных и cstartup обнуляет эту область.
--------------------
На любой вопрос даю любой ответ"Write code that is guaranteed to work, not code that doesn’t seem to break" ( C++ FAQ)
|
|
|
|
|
Mar 6 2008, 20:14
|
Группа: Новичок
Сообщений: 9
Регистрация: 15-02-08
Пользователь №: 35 076

|
Цитата(Сергей Борщ @ Mar 6 2008, 17:26)  Если есть желание забить каким-то своим значением - допишите memset в конструктор TProcess. Всего одна строчка кода - а сколько пользы! Вся память как на ладони. Спасибо.
|
|
|
|
|
Mar 9 2008, 21:15
|
Знающий
   
Группа: Свой
Сообщений: 589
Регистрация: 24-04-05
Пользователь №: 4 447

|
Мне не понятно, почему для генерации программного прерывания удобней использовать прерывание от аналогового компаратора? На мой взгляд лучше и удобней использовать внешнее прерывание. Во-первых, приоритет у него выше. Во-вторых, у большинства мк есть прерывния PCINT (pin change interrupt). Таким образом можно назначить практически любой вывод под это дело. В третьих, для переключения вывода в противоположное состояние достаточно записать PINy |= (1<<PINx); т.е. атомарную операцию SBI PINy, X. И было бы неплохо, чтобы в конфигурационном файле была возможность выбора источника программного прерывания: от компаратора или от внешнего прерывния. И также назначить символические имена выводам, которые используются для прерывания. В общем, максимально "загрузить" препроцессор, чтобы пользователь делал минимум телодвижений. И ошибок...
|
|
|
|
|
Mar 10 2008, 01:42
|

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

|
Цитата(alux @ Mar 9 2008, 23:15)  Мне не понятно, почему для генерации программного прерывания удобней использовать прерывание от аналогового компаратора? На мой взгляд лучше и удобней использовать внешнее прерывание. Во-первых, приоритет у него выше. Исторически сложилось, как самое ненужное. Приоритет ему как раз нужен в идеале самый низкий. ReAl в своем порте реализовал переключение по прерыванию SPM, в этом случае нога не нужна вообще. Цитата(alux @ Mar 9 2008, 23:15)  В третьих, для переключения вывода в противоположное состояние достаточно записать PINy |= (1<<PINx); Посмотрите порт ReAl, в нем (до SPM) было реализовано именно такое поведение - если процессор умеет перекидывать ногу одной командой, компаратор срабатывал от каждого перепада. Цитата(alux @ Mar 9 2008, 23:15)  И было бы неплохо, чтобы в конфигурационном файле была возможность выбора источника программного прерывания: от компаратора или от внешнего прерывния. Это и так реализовано - все функции, завязанные на прерывание переключения контекста находятся в файле scmROS_Target_cfg.h, который находится в папке приложения.
--------------------
На любой вопрос даю любой ответ"Write code that is guaranteed to work, not code that doesn’t seem to break" ( C++ FAQ)
|
|
|
|
|
Mar 10 2008, 04:40
|
Знающий
   
Группа: Свой
Сообщений: 589
Регистрация: 24-04-05
Пользователь №: 4 447

|
Цитата(Сергей Борщ @ Mar 10 2008, 05:42)  Приоритет ему как раз нужен в идеале самый низкий. Почему? Цитата(Сергей Борщ @ Mar 10 2008, 05:42)  ReAl в своем порте реализовал переключение по прерыванию SPM, в этом случае нога не нужна вообще. Так нужно как минимум три маш. цикла на переключение. Пытаюсь настроить переключение по прерыванию PCINT. Сделал следующее: Код #if scmRTOS_CONTEXT_SWITCH_SCHEME == 1
INLINE inline void RaiseContextSwitch() { PINC |= (1 << PINC7); // PORTB |= (1 << 3); PORTB &= ~(1 << 3); // set flag } INLINE inline void BlockContextSwitch() { PCMSK2 &= ~(1 << PCINT23); } // disable interrupt //{ ACSR &= ~(1 << ACIE); } // disable interrupt Как изменится в таком случае этот кусок? Код class TNestedISRW { public: INLINE TNestedISRW() : State(ACSR) { BlockContextSwitch(); __enable_interrupt(); } INLINE ~TNestedISRW() { __disable_interrupt(); ACSR = State; if(State & (1 << ACI)) RaiseContextSwitch(); } private: byte State; }; Не судите строго, я еще не совсем ориентируюсь в объектно-ориентированном программировании
|
|
|
|
|
Mar 10 2008, 09:08
|

Нечётный пользователь.
     
Группа: Свой
Сообщений: 2 033
Регистрация: 26-05-05
Из: Бровари, Україна
Пользователь №: 5 417

|
Цитата(alux @ Mar 10 2008, 06:40)  Почему? Так нужно как минимум три маш. цикла на переключение. Потому что если вдруг возникнет ещё какое-то прерывание от аппаратуры, оно должно быть обработано, а оно задержится на время переключения контекстов. У задачи, на которую идёт переключение, это прерывание от аппаратуры хоть так, хоть сяк (хоть до переключения, хоть сразу после) время заберёт, т.е. она всё равно реально начнёт работать позже. А само будет задержано переключателем контекстов. А "три цикла"... По сравнению с сотнями на собственно переключение - что три (in/ori/out для SPMCSR в IO), что два (sbi PIN для компаратора либо PCINT), что четыре (sbi PORT/cbi PORT либо in/ldi/eor/out для компаратора на старых кристаллах), что пять (lds/ori/sts для SPMCSR вне адресов IO) - на практике несущественны. Хотя можно в примеры вернуть (под условную компиляцию либо в разные примеры разный метод) и прерывание от компаратора (хотя у меня он имеет больше шансов быть занятым, чем PCINT), и даже PCINT, хоть я не хотел использовать именно потому, что оно слишком приоритетное. Кроме того, это блокирует использование PCINT от других ног этого порта. Цитата Пытаюсь настроить переключение по прерыванию PCINT. Сделал следующее: Код INLINE inline void BlockContextSwitch() { PCMSK2 &= ~(1 << PCINT23); } // disable interrupt //{ ACSR &= ~(1 << ACIE); } // disable interrupt Кстати, где как, но если уж считать такты, то у меги168 PCMSK* расположены в области памяти и работа с ними сожрёт весь выигрыш от sbi PIN по сравнению с in SPMCSR/ori/OUTSPMCSR. А в tiny85 у меня как-то рука не поднимается scmRTOS ставить, на 512 байтах ОЗУ только демонстрашку сделать можно. Да и там работа с SPMCSR и PCINT по сумме баллов мало отличаться будут. Я бы для PCINT сделал что-то в духе (для той же меги168), всё равно остальные PCINT из этого порта как входы прерываний использовать нельзя: Код #define PCINT_PIN C,7,H #define PCINT_BIT PCIE2
#if scmRTOS_CONTEXT_SWITCH_SCHEME == 1
INLINE inline void RaiseContextSwitch() { CPL(PCINT_PIN); }
INLINE inline void BlockContextSwitch() { PCICR &= ~(1 << PCINT_BIT); } // disable PCINT interrupt
class TNestedISRW { public: INLINE TNestedISRW() : State(PCICR) { BlockContextSwitch(); sei(); } INLINE ~TNestedISRW() { cli(); if( State & (1 << PCINT_BIT) ) PCICR |= (1 << PCINT_BIT); } private: byte State; }; Естественно, где-то в начале (до вызова OS::Run()) надо настроить разрешение для нужной ноги.
--------------------
Ну, я пошёл… Если что – звоните…
|
|
|
|
|
Mar 10 2008, 09:56
|
Знающий
   
Группа: Свой
Сообщений: 589
Регистрация: 24-04-05
Пользователь №: 4 447

|
Спасибо за разъяснение. Маленькое уточнение: sbi PINC, 0x01 занимает один такт. В моем проекте на Меге324P есть один лишний вывод на PC7. Вот я к PCINT и "привязался". Хотя после вышесказанного возможно настрою на SPM. Вывод дороже... Прокомментируйте, пожалуйста, этот код Цитата(ReAl @ Mar 10 2008, 13:08)  class TNestedISRW { public: INLINE TNestedISRW() : State(PCICR) { BlockContextSwitch(); sei(); } INLINE ~TNestedISRW() { cli(); if( State & (1 << PCINT_BIT) ) PCICR |= (1 << PCINT_BIT); } private: byte State; };[/code] Не понятно в строке конструктора : State(PCICR). PS. Кстати, только что обратил внимание, у Меги324P SPMCSR находится по адресу 0x37. Т.е можно применять команды IN / OUT. А PCMSK2 - 0x6D. Т.е. уже только ST/STS/STD и LD/LDS/LDD. Наверное в данном случае лучше применить прерывание от SPM . PS2. Решил попробовать вариант с SPM. Код #if defined(SPMCSR) #define SPM_CONTROL_REG SPMCSR #elif defined(SPMCR) #define SPM_CONTROL_REG SPMCR #else #error "SPM control register not defined" #endif Компилятор выдает ошибку Код Fatal Error[Pe035]: #error directive: "SPM control register not defined" Хотя файл <ioavr.h> подключен. Как правильно определить SPMCSR ? Неужели просто перед #if defined(SPMCSR) написать #define SPMCSR ?
|
|
|
|
|
Mar 10 2008, 15:36
|

Нечётный пользователь.
     
Группа: Свой
Сообщений: 2 033
Регистрация: 26-05-05
Из: Бровари, Україна
Пользователь №: 5 417

|
Цитата(alux @ Mar 10 2008, 11:56)  Маленькое уточнение: sbi PINC, 0x01 занимает один такт. В моем проекте на Меге324P есть один лишний вывод на PC7. Вот я к PCINT и "привязался". Хотя после вышесказанного возможно настрою на SPM. Вывод дороже... Я уже думал, что я серьёзно отстал от жизни и mega324P отличается от других AVR-ок. Глянул в описание - sbi/cbi выполняются за два такта. Под рукой нет для проверки. Цитата Прокомментируйте, пожалуйста, этот код Не понятно в строке конструктора : State(PCICR). Инициализация поля State значением PCICR (это не совсем одно и то же, что State = PCICR; в теле конструктора). Цитата PS2. Решил попробовать вариант с SPM. ... Компилятор выдает ошибку Код Fatal Error[Pe035]: #error directive: "SPM control register not defined" Хотя файл <ioavr.h> подключен. Как правильно определить SPMCSR ? Неужели просто перед #if defined(SPMCSR) написать #define SPMCSR ?  Кто такой ioavr.h ? Имеется ввиду <avr/io.h> ? Только что взял снапшот http://scmrtos.ginps.com/scmrtos-avr-gcc-snapshot.rarраспаковал, заменил в 1-EventFlag/makefile тип процессора: MCU := atmega324p всё скомпилировалось. Не знаю, в чём может быть проблема. -------- Тьху, это, наверно, IAR ? Ну тогда ничем не могу помочь, у меня его на компе нет.
--------------------
Ну, я пошёл… Если что – звоните…
|
|
|
|
|
Mar 10 2008, 16:00
|
Знающий
   
Группа: Свой
Сообщений: 589
Регистрация: 24-04-05
Пользователь №: 4 447

|
Цитата(ReAl @ Mar 10 2008, 19:36)  Глянул в описание - sbi/cbi выполняются за два такта. Прошу прощения. Заработался ..Не туда глянул  Действительно 2 такта. А вопрос с ошибкой компиляции я перенес в другой (IAR) форум. Не могу одолеть. Ну в смысле, если просто везде заменить TIMSK на TIMSK0 , то все OK. А вот для SPMCSR просто тупо вставил Код #include <ioavr.h> #define SPMCSR
#if defined(SPMCSR) #define SPM_CONTROL_REG SPMCSR #elif defined(SPMCR) #define SPM_CONTROL_REG SPMCR #else #error "SPM control register not defined" #endif Но это же за уши притянуто.. Хочется принципиально для себя решить этот вопрос.
|
|
|
|
|
Mar 10 2008, 16:52
|

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

|
Цитата(alux @ Mar 10 2008, 18:00)  Но это же за уши притянуто.. Хочется принципиально для себя решить этот вопрос. Увы, IAR и GCC несколько по-разному описывают регистры: Код GCC: #define _SFR_IO8(io_addr) ((io_addr) + __SFR_OFFSET) #define TIMSK0 _SFR_MEM8 (0x6E) IAR: #define SFR_B_N(_ADDR, _NAME, _B7, _B6, _B5, _B4, _B3, _B2, _B1, _B0) \ SFR_B_BITS_N(_NAME, _ADDR, \ Bit0,Bit1,Bit2,Bit3,Bit4,Bit5,Bit6,Bit7, \ _B0,_B1,_B2,_B3,_B4,_B5,_B6,_B7) #define SFR_B_BITS_N(_NAME, _ADDR, _A,_B,_C,_D,_E,_F,_G,_H, \ _A2,_B2,_C2,_D2,_E2,_F2,_G2,_H2) \ __io union { \ unsigned char _NAME; /* The sfrb as 1 byte */ \ struct { /* The sfrb as 8 bits */ \ __BYTEBITS(_NAME, _A,_B,_C,_D,_E,_F,_G,_H) \ }; \ struct { /* The sfrb as 8 bits */ \ __BYTEBITS(_NAME, _A2,_B2,_C2,_D2,_E2,_F2,_G2,_H2) \ }; \ } @ _ADDR;SFR_B_N(0x6E,TIMSK0,Dummy7,Dummy6,Dummy5,Dummy4,Dummy3,OCIE0B,OCIE0A,TOIE0) Поэтому в GCC можно использовать #ifdef к именам регистров, а в IAR - увы, нет. Поэтому вам проще всего выкинуть лишние строчки условий и оставить только Код #define SPM_CONTROL_REG SPMCSR #define TIMSK TIMSK0 или заменить SPM_CONTROL_REG на SPMCSR и TIMSK на TIMSK0 дальше в исходнике.
--------------------
На любой вопрос даю любой ответ"Write code that is guaranteed to work, not code that doesn’t seem to break" ( C++ FAQ)
|
|
|
|
|
Mar 11 2008, 12:37
|
Знающий
   
Группа: Свой
Сообщений: 589
Регистрация: 24-04-05
Пользователь №: 4 447

|
Возникло пару вопросов. Допустим, есть три процесса: KeyMenu, EditTime, Blink. Порядок работы должен быть такой : 1) KeyMenu - навигация по пунктам меню. На этом этапе остальные два процесса находятся в ожидании. 2) Заходим в пункт меню "Настройка -> Время". После нажатия ENTER запускается функция, которая считывает время с RTC в переменные и выводит эти значения на ЖКИ. После этого необходимо запустить процессы EditTime и Blink. При этом необходимо перевести KeyMenu в ожидание, пока работают эти процессы. Процесс Blink - это просто инвертирование текущего знакоместа с периодом 1 сек. 3) После окончания ввода времени, EditTime и Blink снова переводятся в ожидание, а KeyMenu переходит в состояние готовности.
Первый вопрос. Как это лучше организовать? И второй вопрос скорее по C++. Мой проект состоит из нескольких С-файлов. В tasks.c я вынес все процессы и объявления объектов. Как в другом С-файле использовать методы объекта?
PS. В другом С-файле, в котором необходимо вызвать ef.Signal(); просто объявил extern OS::TEventFlag ef; как это обычно делается с переменными и компилятора это устроило, хотя в учебнике С++ сказано, что данные-члены не могут определятся с модификаторами auto, extern, register
|
|
|
|
|
Mar 11 2008, 14:03
|

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

|
Цитата(alux @ Mar 11 2008, 18:37)  И второй вопрос скорее по C++. Мой проект состоит из нескольких С-файлов. В tasks.c я вынес все процессы и объявления объектов. Как в другом С-файле использовать методы объекта? В каком-нибудь загововке, который всем виден: Код typedef OS::process<OS::pr0, 768> TMainProc; typedef OS::process<OS::pr1, 768> TFPGAProc; typedef OS::process<OS::pr2, 1024> TControlsProc; typedef OS::process<OS::pr3, 1024> TGUIProc;
extern TMainProc MainProc; extern TFPGAProc FPGAProc; extern TControlsProc ControlsProc; extern TGUIProc GUIProc; В исходных файлах объявить сами объекты: Код TMainProc MainProc; ... TFPGAProc FPGAProc; ... Цитата(alux @ Mar 11 2008, 18:37)  PS. В другом С-файле, в котором необходимо вызвать ef.Signal(); просто объявил extern OS::TEventFlag ef; как это обычно делается с переменными и компилятора это устроило, хотя в учебнике С++ сказано, что данные-члены не могут определятся с модификаторами auto, extern, register extern OS::TEventFlag ef;где тут данное-член? И вообще, где тут член класса? Тут объект ef типа OS::TEventFlag объявлен как extern. Все в норме.
--------------------
«Отыщи всему начало, и ты многое поймёшь» К. Прутков
|
|
|
|
|
Mar 11 2008, 16:48
|

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

|
Цитата(alux @ Mar 11 2008, 14:37)  3) После окончания ввода времени, EditTime и Blink снова переводятся в ожидание, а KeyMenu переходит в состояние готовности. А смысл делать EditTime отдельным процессом? Мне кажется он просится функцией (возможно - функцией-членом класса TTimer), вызываемой из KeyMenu, но никак не отдельным процессом. Что касается Blink - он может ожидать некое сообщение (скажем, BLINK_ON) и получив его - мигать, пока не получит сообщение BLINK_OFF. Он может также получать сообщения SHORT_FLASH (одна короткая вспышка), LONG_FLASH (одна длинная вспышка) и другие, или сообщение может представлять из себя структуру, в которой записаны частота мигания, скважность, длительность.
--------------------
На любой вопрос даю любой ответ"Write code that is guaranteed to work, not code that doesn’t seem to break" ( C++ FAQ)
|
|
|
|
|
Mar 11 2008, 20:31
|
Знающий
   
Группа: Свой
Сообщений: 589
Регистрация: 24-04-05
Пользователь №: 4 447

|
Цитата(Сергей Борщ @ Mar 11 2008, 20:48)  А смысл делать EditTime отдельным процессом? Мне кажется он просится функцией (возможно - функцией-членом класса TTimer), вызываемой из KeyMenu, но никак не отдельным процессом. Так изначально у меня ф-ция EditTime вызывалась из функции пункта меню "Настройка -> Время" . В этой ф-ции в вечном цикле опрашивались кнопки и происходило редактирование времени. Естественно, контроллер больше ничего не делал, кроме прерываний. Только ждал нажатия ESC, которое вывело бы его из вечного цикла. Это было до использования ОС. Поэтому я решил сделать EditTime отдельным процессом. Scan_code нажатой кнопки получаю в прерывании таймера 2, который запускается в обработчике внешнего прерывания от нажатия кнопки. Код OS_PROCESS void TKeyMenu::Exec() { for(;;) { if(key_code.scan & KEY_PRESSED) { key_code.scan &= (0xff-KEY_PRESSED); // Clear MSB of scan_code (key_pressed) switch(key_code.scan) { case UP: SET_MENU(PREVIOUS); break; case DOWN: SET_MENU(NEXT); break; case LEFT: SET_MENU(PARENT); break; case RIGHT: SET_MENU(SIBLING); break; } } Sleep(10); } }
//----------------------------------------------------- OS_PROCESS void TEditTime::Exec() { for(;;) { ef.Wait(); if(key_code.scan & KEY_PRESSED) { key_code.scan &= (0xff-KEY_PRESSED); // Clear MSB of scan_code (key_pressed) switch(key_code.scan) { case LEFT: //k_esc // выйти из редактирования. Возврат к предыдущему пункту меню break; case RIGHT: //k_enter // переход к следующему параметру break; case DOWN: //k_left // перевод курсор на один символ влево break; case UP: //k_right // перевод курсора на один символ вправо break; default: // 0...9 //цифровые кнопки break; } } Sleep(3); } }
//--------------------------------------------------------------------------- OS_PROCESS void TBlink::Exec() { for(;;) { ef.Wait(); ks0108InvertRect(x, y, w, 9); Sleep(500); } } EditTime и Blink ждут сигнала от функции, которая вызывается при входе в пункт меню. Это мой первый опыт работы с ОС. Предложите что-нибудь лучше. А что такое функция-член класса TTimer ? Можно поподробней? Желательно с примером. Заранее спасибо.
|
|
|
|
|
Mar 11 2008, 22:31
|

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

|
Цитата(alux @ Mar 11 2008, 22:31)  Так изначально у меня ф-ция EditTime вызывалась из функции пункта меню "Настройка -> Время" . В этой ф-ции в вечном цикле опрашивались кнопки и происходило редактирование времени. Естественно, контроллер больше ничего не делал, кроме прерываний... Поэтому я решил сделать EditTime отдельным процессом. Так ведь и теперь у вас он во время EditTime не делает ничего в TKeyMenu. При этом у вас получилось, что EditTime работает изредка, а память под свой стек занимает всегда. Если бы вы сделали его функцией и вызывали из TKeyMenu - в остальное время этот стек мог бы быть использован другими частями TKeyMenu. Цитата(alux @ Mar 11 2008, 22:31)  А что такое функция-член класса TTimer ? Можно поподробней? Это я прочитал рекомендовавшуюся здесь книжку Гради Буча "Объектно-ориентированный анализ и проектирование" и пытаюсь применять полученные там знания. Он рекомендует вычленять в предметной области законченные абстракции и облекать их в вид классов. Вот у вас в системе судя по описанию есть некие часы, которые можно выделить в отдельную абстракцию. Они умеют считать время, их можно спросить "который час", их можно установить. Их можно реализовать в виде класса (обозвать его TTimer), который будет иметь только две открытые функции GetTime(), SetTime(). Внутрь этого класса спрятать (сделать private) счетчик времени и тем самым гарантировать, что никто его случайно не испортит - только вызвав явно SetTime(). Применяя в системе разные внешние микросхемы часов или программные часы, вам придется переписать только реализацию этого класса, вся остальная часть программы не будет зависеть от физической реализации часов. Вот изложил все это и понял, что функция EditTime имеет отношение скорее к интерфейсу пользователя, чем к часам, ее нет смысла делать членом класса TTimer, скорее она должна в конце своей работы вызвать SetTime().
--------------------
На любой вопрос даю любой ответ"Write code that is guaranteed to work, not code that doesn’t seem to break" ( C++ FAQ)
|
|
|
|
|
Mar 12 2008, 06:48
|
Знающий
   
Группа: Свой
Сообщений: 589
Регистрация: 24-04-05
Пользователь №: 4 447

|
Цитата(Сергей Борщ @ Mar 12 2008, 02:31)  Так ведь и теперь у вас он во время EditTime не делает ничего в TKeyMenu. При этом у вас получилось, что EditTime работает изредка, а память под свой стек занимает всегда. Если бы вы сделали его функцией и вызывали из TKeyMenu - в остальное время этот стек мог бы быть использован другими частями TKeyMenu. Согласен. Мой способ мне самому не нравится. Процессов не хватит на каждый пункт меню. В TEditTime и TKeyMenu используются одинаковые конструкции Код for(;;) { if(key_code.scan & KEY_PRESSED) { key_code.scan &= (0xff-KEY_PRESSED); // Clear MSB of scan_code (key_pressed) switch(key_code.scan) { case UP: ......... break; case DOWN: ......... break; case LEFT: ......... break; case RIGHT: ......... break; } } Sleep(...); } Как бы использовать один процесс TKey, только для редактирования времени выполнять одно действие, а для навигации меню - другое.? Подозреваю, что здесь напрашивается создать класс... Но пока не знаю, как это реализовать. Опыта с ++ маловато.
|
|
|
|
|
Mar 12 2008, 08:06
|

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

|
Цитата(alux @ Mar 12 2008, 08:48)  Как бы использовать один процесс TKey, только для редактирования времени выполнять одно действие, а для навигации меню - другое.? Подозреваю, что здесь напрашивается создать класс... Могу предложить сделать абстрактный базовый класс с виртуальными функциями (методами) ActionUp(), ActionDown(), ActionLeft() ....От него унаследовать TMainMenu, TEditTime и остальные, в которых переопределить эти методы. Посмотрите пример 3 из комплекта ОСи - там как раз такой подход реализован при кормлении слонов.
--------------------
На любой вопрос даю любой ответ"Write code that is guaranteed to work, not code that doesn’t seem to break" ( C++ FAQ)
|
|
|
|
|
Mar 12 2008, 09:06
|

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

|
Цитата(alux @ Mar 12 2008, 12:48)  Согласен. Мой способ мне самому не нравится. Процессов не хватит на каждый пункт меню. В TEditTime и TKeyMenu используются одинаковые конструкции ... Как бы использовать один процесс TKey, только для редактирования времени выполнять одно действие, а для навигации меню - другое.? Подозреваю, что здесь напрашивается создать класс... Но пока не знаю, как это реализовать. Опыта с ++ маловато. Вы тут углубились в вещи, которые к осям имеют очень опосредованное отношение - проектирование и разработка пользовательского интерфейса. Для пользовательского интерфейса идеально подходит та часть С++, которая реализует ООП. Вкратце: чтобы не городить мегатонны кода и запутаться в конце концов, применяется тот самый ОО подход - создается абстрактрый класс пункта меню, например, TMenuItem, в нем определяется интерфейс - набор чисто виртуальных функций, которые и будут реализовывать функциональность. Далее, от этого абстрактного класса наследуете уже конкретные классы пунктов меню, в которых определяете конкретное наполнение тех виртуальных функций. Код class TMenuItem // абстрактрый базовый класс пункта меню { public: TMenuItem(const char * str); virtual void draw() = 0; // чисто виртуальная фукнция virtual void change(int x) = 0; private: const char* caption; ... };
class TMenuItem1 : public TMenuItem { public: TMenuItem1(const char * str) : TMenuItem(str) { ... } ... };
... Таких классов надо родить столько, сколько у вас разных пунктов меню. В каждом из этих классов переопределяете фукнции draw и change (или какие там у вас будут функции). Код void TMenuItem1::draw() { ... } // конкретная реализация данной функции - отрисовка этого конкретного пункта меню.
void TMenuItem1::change(int x) { ... } // изменение данного конкретного пункта меню Для остальных классов тоже определить точно так же эти функции. Если есть одинаковые куски кода, то можно их использовать - например, у меня все пункты меню отрисовываются одинаково, поэтому у меня функция draw() невиртуальная и общая для всех классов потомков. А change() - виртуальная, т.к. содержимое пункта меню в каждом случае индивидуально и изменяется, соответственно, тоже индивидуально (к примеру, часть пунктов задает числовые параметры, а часть - строковые). Теперь организуете сами пункты меню в группы - например, создав массив указателей на объекты этих типов. Код TMenuItem1 MenuItem1; TMenuItem2 MenuItem2; ... TMenuItemN MenuItemN;
TMenuItem *Menu[] = { &MenuItem1, &MenuItem1, ... &MenuItemN, } Теперь в соответствии с событиями, получаемыми от кнопок, выполняете действия над объектами (тут для простоты использую частью псевдокод): Код byte index;
if( RIGHT ) { Menu[++index]->draw(); // проверка перехода границы массива для простоты опущена } else if( LEFT ) { Menu[--index]->draw(); // проверка перехода границы массива для простоты опущена } else if ( UP ) { Menu[index]->change(1); } else if ( DOWN ) { Menu[index]->change(-1); } Т.е. тут события LEFT/RIGHT задают перемещение по пунктам меню, UP/DOWN - изменение значения пункта. Конечно, это все кратко, для иллюстрации только. На практике, обычно, все сложнее - все упирается в конкретную организацию меню и требуемую функциональность. Но схема построения будет та же и она, как видно, достаточно проста. В каждом случае будут вызываться разные функции draw и change - для каждого объекта своя. Для всех пунктов меню общим является то, что они являются пунктами меню и то, что с ними можно делать - а именно: отображать и изменять. Но уже сами эти фукнции (отображение и изменение) для каждого пункта разные. Все это реализуется на основе полиморфизма (поведение, получаемое с помощью виртуальных фукнций), что вышеприведенный пример и иллюстрирует. Тут вы можете теперь добавлять сколько угодно пунктов меню, все они будут обрабатываться этим кодом. Если нужны еще какие-то функции, завязанные на другие события от кнопок (или другого источника входной информации), то добавляете их и пишете код, который их вызывает (по аналогии). Но уже не запутаетесь в этом разнообразии пунктов меню - все будет четко и единообразно. Вообще, наверное, GUI - одна из самых подходящих областей для применения ООП, где преимущества ООП перед процедурным программированием являются просто гиганскими.  Что касается конкретно ОС, то вот этот код обработки пунктов меню имеет смысл поместить в один процесс (как правило, низкоприоритетный), который получает сообщения от источников входной информации - кнопок и др. Процесс висит в саспенде, ждет сообщения. Получил сообщение, проснулся, обработал его, упал в ожидание следующего.
--------------------
«Отыщи всему начало, и ты многое поймёшь» К. Прутков
|
|
|
|
|
Mar 12 2008, 10:45
|
Знающий
   
Группа: Свой
Сообщений: 589
Регистрация: 24-04-05
Пользователь №: 4 447

|
Реализация меню - это отдельная тема для разговора. Если бы я сейчас начинал свой проект, то, возможно, и применил бы ОО подход в создании меню. Правда, пока не понятно, как это усложнится в случае иерархического меню. У меня меню создается макросом MAKE_MENU (Name, Next, Previous, Parent, Sibling, SelectFunc, EnterFunc, Text). Структура меню видна, как на ладони. И главное, что это уже отлажено и работает. Цитата(dxp @ Mar 12 2008, 13:06)  вот этот код обработки пунктов меню имеет смысл поместить в один процесс (как правило, низкоприоритетный) Этот процесс должен обрабатывать не только пункты меню, но и другие функции, в частности EditTime. По совету Сергея я создал от базового абстрактного класса TKey два новых класса: TMainMenu и TEditTime , которые имеют собственные реализации виртуальных методов Up, Down, Right, Left. Теперь мне надо, чтобы процесс, который получает сообщение в виде scan_code нажатой кнопки от ISR Timer2 отрабатывался по-разному, в зависимости от того, что в данный момент нужно: навигация по пунктам меню или редактирование времени. Как это сделать? PS. Ничего лучшего, кроме как при входе в меню "Настройка -> Время" установить обычный глобальный флаг (не OS::EventFlag), по которому в процессе TKey определять какие действия необходимо делать при нажатии на кнопки, не придумал. Спрашивается. Зачем было городить виртуальные функции с абстрактными классами?
|
|
|
|
|
Mar 12 2008, 12:30
|

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

|
Цитата(alux @ Mar 12 2008, 12:45)  PS. Ничего лучшего, кроме как при входе в меню "Настройка -> Время" установить обычный глобальный флаг (не OS::EventFlag), по которому в процессе TKey определять какие действия необходимо делать при нажатии на кнопки, не придумал. Спрашивается. Зачем было городить виртуальные функции с абстрактными классами?  Мимо. Заводите объекты ваших классов-потомков, заводите указатель на абстрактный класс TKey: Код TMainMenu MainMenu; TEditTime EditTime; TKey *CurrentMode = &MainMenu; // начинаем с меню. enum TKeyCode { KEY_LEFT, KEY_RIGHT, KEY_UP, KEY_DOWN }; OS::message<TKeyCode> Keyboard; Указателю вы можете присвоить адрес любого класса-потомка этого базового класса. Т.е. надо меню - CurrentMode = &MainMenu. Надо редактировать время - CurrentMode = &EditTime; Теперь в потоке, который собственно организует управление делаете вызов виртуальных функций: Код for(;;) { if(!Keyboard.Wait(timeout)) { CurrentMode = &MainMenu; // пользователь ушел, выход в основной режим по таймауту // или CurrentMode->Timeout(); } TKeyCode Key = Keyboard; switch (Key) { case LEY_LEFT: CurrentMode->Left(); break; case LEY_RIGHT: CurrentMode->Right(); break; ..................... } } Примерно так... Потом увидете, что все пункты меню можно построить точно так же как и TMainMenu, а красивую и понятную таблицу переходов между ними можно продолжать генерить макросом.
--------------------
На любой вопрос даю любой ответ"Write code that is guaranteed to work, not code that doesn’t seem to break" ( C++ FAQ)
|
|
|
|
|
Mar 12 2008, 13:24
|
Знающий
   
Группа: Свой
Сообщений: 589
Регистрация: 24-04-05
Пользователь №: 4 447

|
Спасибо, Сергей. Сам бы до этого не додумался. Я думаю достаточно будет EventFlag вместо message. Скан-код хранится в глобальной переменной. Еще один момент. В функции EditTime используются цифровые кнопки (0...9), а для навигации по пунктам меню - нет. В конструкции switch в ветке default при редактировании времени вводятся цифры. В базовом абстрактном классе необходимо создавать все виртуальные функции, которые используются потомками? Если да, то в TMainMenu нужно просто создать вирт. ф-цию Default {} с пустым телом? Цитата(Сергей Борщ @ Mar 12 2008, 16:30)  Потом увидете, что все пункты меню можно построить точно так же как и TMainMenu, а красивую и понятную таблицу переходов между ними можно продолжать генерить макросом. Это к разговору о создании иерархического меню? PS. Цитата(Сергей Борщ @ Mar 12 2008, 16:30)  if(!Keyboard.Wait(timeout)) wait нужно писать с маленькой буквы  PS2. А на счет функции Blink, которая нужна при редактировании времени, то на мой взгляд будет проще ее запускать отдельным таймером. К проекту подключен timer.c, в котором задан массив системных таймеров, с помощью которых можна запускать функции с заданным интервалом времени.
|
|
|
|
|
Mar 12 2008, 16:26
|

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

|
Цитата(alux @ Mar 12 2008, 15:24)  В конструкции switch в ветке default при редактировании времени вводятся цифры. В базовом абстрактном классе необходимо создавать все виртуальные функции, которые используются потомками? Если да, то в TMainMenu нужно просто создать вирт. ф-цию Default {} с пустым телом? Думаю, да. Обозвать ее как-то вроде Numeric(). Причем если система попискивает на нажатие кнопки, то можно в базовом классе определить пару невиртуальных функций, BeepOk() и BeepError() и в реализации Numeric() для TMainMenu вызывать BeepError(), а в "правильных" обработчиках - BeepOk(). Цитата(alux @ Mar 12 2008, 15:24)  Это к разговору о создании иерархического меню? Да.
--------------------
На любой вопрос даю любой ответ"Write code that is guaranteed to work, not code that doesn’t seem to break" ( C++ FAQ)
|
|
|
|
|
Mar 12 2008, 16:57
|
Знающий
   
Группа: Свой
Сообщений: 589
Регистрация: 24-04-05
Пользователь №: 4 447

|
Сделал так как Сергей сказал. За исключением того, что вместо message из ISR посылается TEventFlag::SignalISR(). Теперь наблюдаю следующее: При удержании кнопки при навигации по пунктам меню курсор постоянно "бегает". Никак не пойму из-за чего. Скан-код нажатой клавиши передается через глобальную переменную scan_code. В ней же старшим битом передается информация о нажатии кнопки. При обработке старший бит сбрасывается и таким образом избегаем повторной обработки нажатия кнопки. А на самом деле происходит повтор. :/ Если нет нажатия, то scan_code=0, и это состояние не обрабатывается. Код if(key_code.scan & KEY_PRESSED) { key_code.scan &= (0xff-KEY_PRESSED); // Clear MSB of scan_code (key_pressed) switch(key_code.scan) { ............. Коротко о том, как у меня устроена клавиатура. При нажатии попадаем в ISR PCINT, запрещаем PCINT. Там запускаем таймер2. Через 35 мс в ISR Timer2 останавливаю таймер2, вычисляю скан-код, разрешаю прерывание PCINT. До этого все работало замечательно  PS. Проверил этот же проект без ОС и с ОС(jacOS) - все работает нормально: отрабатывает одно нажатие. Значит проблема в этой системе. Возможно, я в упор не вижу слона, а со стороны вам будет виднее.  Так в чем же может быть проблема? Второй день копаю...
|
|
|
|
|
Mar 18 2008, 07:50
|
Знающий
   
Группа: Свой
Сообщений: 589
Регистрация: 24-04-05
Пользователь №: 4 447

|
Как узнать точное использование стека процессами? А то я все делаю вслепую: выставил 100 - работает. Добавил ф-цию - зависает... Использую AVR (Mega324P). JTAG исключен из-за отсутствия свободных выводов. В симуляторе тоже проблематично (как получить ответ от перефирии I2C, SPI, ?..). Было бы совсем неплохо, если бы средствами ОС была возможность в отладочном режиме посмотреть размер неиспользуемого стека каждой задачи через терминал. Такую возможность я увидел в ОС нашего соотечественника uOS. Там для этой цели используется ф-ция task_stack_avail(). И второе. По поводу ф-ции Blink в режиме редактирования времени. Я это реализовал с использованием очереди сообщений, как в примере 3- Channel. Создал от базового класса TMsg объект Blink с методом InvertRect(...); Вопрос, собственно, заключается в следующем : Как обеспечить периодический MsgQueue.push(&Blink) ? Конечно, можно для этой цели использовать отдельный таймер, но хотелось бы обойтись системным таймером. Но не хочу использовать SystemTimerUserHook() по понятным причинам. Может воспользоваться OS::GetTickCount(); которая, на сколько я понял, возвращает общее количество тиков с момента запуска ОС ? Тогда при входе в п. меню "Редактирование" сохранить T = OS::GetTickCount(), а в низкоприоритетном процессе LCDProc отсчитывать от значения T 500 тиков (500*2=1сек) и тогда класть сообщение Blink в очередь. Также нужно учитывать переполнение счетчика. Что скажете?
|
|
|
|
|
Mar 18 2008, 09:02
|

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

|
Цитата(alux @ Mar 18 2008, 13:50)  Как узнать точное использование стека процессами? Сергей Борщ советовал добавить в конструктор процесса memset, которая заполнит стековую память указанным значением. Стек для эксперимента надо взять с запасом. После этого делается прогон программы и анализируется глубина потребления стека. Цитата(alux @ Mar 18 2008, 13:50)  Вопрос, собственно, заключается в следующем : Как обеспечить периодический MsgQueue.push(&Blink) ? Конечно, можно для этой цели использовать отдельный таймер, но хотелось бы обойтись системным таймером. Но не хочу использовать SystemTimerUserHook() по понятным причинам. Может воспользоваться OS::GetTickCount(); которая, на сколько я понял, возвращает общее количество тиков с момента запуска ОС ? Тогда при входе в п. меню "Редактирование" сохранить T = OS::GetTickCount(), а в низкоприоритетном процессе LCDProc отсчитывать от значения T 500 тиков (500*2=1сек) и тогда класть сообщение Blink в очередь. Также нужно учитывать переполнение счетчика. Что скажете? А что является инициатором (источником события) этого блинка? Вот оно и должно пихать в очередь сообщение. Если надо просто периодически мигать, то запуск по счетчику тиков, имхо, не самое плохое решение (если в отдельный процесс не выносить, а запускать из имеющегося, выполняющиегося с заданной периодичностью).
--------------------
«Отыщи всему начало, и ты многое поймёшь» К. Прутков
|
|
|
|
|
Mar 18 2008, 09:38
|
Знающий
   
Группа: Свой
Сообщений: 589
Регистрация: 24-04-05
Пользователь №: 4 447

|
Цитата(dxp @ Mar 18 2008, 13:02)  Сергей Борщ советовал добавить в конструктор процесса memset, которая заполнит стековую память указанным значением. Стек для эксперимента надо взять с запасом. После этого делается прогон программы и анализируется глубина потребления стека. Что такое memset ? Можно поподробней? Цитата(dxp @ Mar 18 2008, 13:02)  А что является инициатором (источником события) этого блинка? Вот оно и должно пихать в очередь сообщение. Инициатором Blink является вход в п. меню "Настройка", в котором меняется режим работы клавиатуры (вернее процесса ScanKey): .............................. CurrentMode = &EditTime; } Цитата(dxp @ Mar 18 2008, 13:02)  если в отдельный процесс не выносить, а запускать из имеющегося, выполняющееся с заданной периодичностью). Т.е. есть необходимо, чтобы был отдельный процесс, который запускается с заданной периодичностью? Я думал для этой цели использовать низкоприоритетный процесс LCDProc, а заданную периодичность в нем же осуществлять проверкой OS::GetTickCount()? и каждые 500 тиков в этом же процессе делать MsgQueue.push(&Blink). Но почему-то это не сработало: Код OS_PROCESS void TProcLCD::Exec() //TProc5 { for(;;) { CurrentTick = OS::GetTickCount(); if(((CurrentTick-T) > 500)&&(CurrentMode == &EditTime)) { MsgQueue.push(&Blink); T = CurrentTick; } TMsg* msg; MsgQueue.pop(msg); msg->run(); } }
|
|
|
|
|
Mar 18 2008, 13:13
|

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

|
Цитата(alux @ Mar 18 2008, 15:38)  Что такое memset ? Можно поподробней? Функция такая.  Цитата(alux @ Mar 18 2008, 15:38)  Инициатором Blink является вход в п. меню "Настройка", в котором меняется режим работы клавиатуры (вернее процесса ScanKey): Ну, вот из этого места и надо метать сообщение в очередь. Я имел в виду нечто иное. Когда запускается Blink, то это самостоятельный процесс (просто запускаемый в качестве job), который сам отмеряет все времена и управляет требуемой периферией. Т.е. метнули сообщение в очередь, на том конце очереди сообщение извлекается и вызывается соответствующая функция, которая весь процесс мигания и осуществляет. Отработала, закончилась. Обработчик очереди готов к обработке следующего сообщения. Т.е. вся низкоуровневая работа по формированию времен возложена на виртуальную функцию объекта Blink. А код входа в меню только лишь мечет команду сделать это (путем помещения указателя на объект: &Blink). Удобнее всего формировать времянки с помощью системных средств - той же функции Sleep(), например. Но для этого надо выделить целый процесс, что жалко, т.к. само по себе действие нечастое. Так вот делегирование выполнения операции позволяет совместить приятное с полезным - выполнять код в отдельном процессе (с использованием слипов и прочего) и в то же время не выделять индивидуально процесс под одну операцию, а зашарить его между всеми "желающими" - естественно, все их "желания" будут выполняться по очереди. Если все успевает, то все хорошо. Т.е. если, скажем, во время блинка срочно надо еще что-то сделать, то тогда ой. А если нет, то и пусть себе мигает отведенное ему время. Т.е. насколько этот вариант подходит под ваши требования, вам виднее.
--------------------
«Отыщи всему начало, и ты многое поймёшь» К. Прутков
|
|
|
|
|
Mar 19 2008, 09:42
|

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

|
Цитата(alux @ Mar 19 2008, 13:26)  Что значит "руками" ? Руками - значит, остановить эмулятором прогон программы и посмотреть до куда стек заполнился. Цитата(alux @ Mar 19 2008, 13:26)  Или приведите, пожалуйста, пример функции. Предлагаете написать мне для вас функцию?  Там все это совсем не сложно - адрес объекта известен, размер его полей до стека тоже известен, отсюда можно вычислить адрес, где начинается стек. Размер стека тоже известен. По сути надо просто от начала стека пройти до первого значения, которое отличается от значения по умолчанию (того, которым заполнен стек сначала). Это и будет размер свободного пространства в стеке. Далее, можно это опять же эмулятором смотреть, а можно вываливать хоть на терминал или куда удобно.
--------------------
«Отыщи всему начало, и ты многое поймёшь» К. Прутков
|
|
|
|
|
Mar 19 2008, 13:15
|
Знающий
   
Группа: Свой
Сообщений: 589
Регистрация: 24-04-05
Пользователь №: 4 447

|
Обнаружил такую проблему. Если два процесса ждут один и тот же Signal от третьего процесса, то почему-то программа виснет. Если ef.Wait() в одном из процессов убрать, то все работает. Размеры стеков увеличивал. Не помогло. Код OS_PROCESS void TMeasure::Exec() //TProc1 { for(;;) { monitoring.Wait(); while(Flags & MEASURE) { PCInt1.Wait(); // Ждем окончания преобразования АЦП (RDY => 0) measure(); } } }
OS_PROCESS void TProcFOSP::Exec() //TProc2 { for(;;) { monitoring.Wait(); // <<<<<<< Если этот убрать, то работает Sleep(); } } ........... При входе в п. меню: Flags |= MEASURE; ENABLE_PCINT1; // Разрешить прерывание от RDY monitoring.Signal(); } Второй процесс вообще ничего не делает, только ждет и спит. В чем может быть проблема?
|
|
|
|
|
Mar 21 2008, 09:07
|
Знающий
   
Группа: Свой
Сообщений: 589
Регистрация: 24-04-05
Пользователь №: 4 447

|
Цитата(dxp @ Mar 20 2008, 11:17)  Возможное решение есть тут. Файлы OS_Services.h и OS_Services.cpp. Заменил эти файлы. Проблема осталась. Плюс перестала работать очередь сообщений. А есть ли смысл пробовать /branches/b1/Common_b2 Rev138 ? PS. Прпробовал заменить файлы из /branches/b1/Common_b2 Rev138 . Обнаружил, что в файле scmRTOS_def.h отсутствует #include "scmRTOS_config.h" со всеми вытекающими ошибками. При подключении scmRTOS_config.h компилятор выдает ошибку: Error[Pe144]: a value of type "byte" cannot be used to initialize an entity of type "OS::TPriority" D:\...\device\scmRTOS\Common\OS_Kernel.h 355 .
|
|
|
|
|
Mar 21 2008, 09:27
|

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

|
Цитата(alux @ Mar 21 2008, 15:07)  Заменил эти файлы. Проблема осталась. Плюс перестала работать очередь сообщений. На данный момент ничего сказать не могу. У меня тоже есть проблемы с этим вариантом реализации в текущем проекте, поэтому я пока использую штатный вариант из trunk. Скоро доберусь до этого. Автор варианта правки бага в этой ветке Сергей Борщ, возможно, он сможет прокомментировать. Цитата(alux @ Mar 21 2008, 15:07)  А есть ли смысл пробовать /branches/b1/Common_b2 Rev138 ? Это тоже Сергей тренировался (Common_b2), но вся эта ветка (b1) - это полигон для отработки других вещей, никак не связанных с фиксом bug_1878045_fix. Т.ч. тут не ищите.
--------------------
«Отыщи всему начало, и ты многое поймёшь» К. Прутков
|
|
|
|
|
Mar 21 2008, 10:39
|
Знающий
   
Группа: Свой
Сообщений: 589
Регистрация: 24-04-05
Пользователь №: 4 447

|
Попробовал создать отдельный ef2 для TProc2 и Код ........... при входе в п. меню: Flags |= MEASURE; ENABLE_PCINT1; // Разрешить прерывание от RDY monitoring.Signal(); ef2.Signal(); Не помогло. Программа виснет и все тут...
|
|
|
|
|
Mar 21 2008, 13:58
|

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

|
Цитата(alux @ Mar 21 2008, 16:39)  Попробовал создать отдельный ef2 для TProc2 и Код ........... при входе в п. меню: Flags |= MEASURE; ENABLE_PCINT1; // Разрешить прерывание от RDY monitoring.Signal(); ef2.Signal(); Не помогло. Программа виснет и все тут...  Т.е. два разных флага для разных процессов? Ну, тогда не в них дело. А чем смотрите? В смысле, какие отладочные средства есть?
--------------------
«Отыщи всему начало, и ты многое поймёшь» К. Прутков
|
|
|
|
|
Mar 22 2008, 07:38
|

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

|
Цитата(alux @ Mar 21 2008, 20:46)  Отладчиком (JTAG) не пользуюсь по причине отсутствия свободных выводов. Наблюдаю визуально по программе: перестает реагировать на кнопки и не выводит результат на экран через очередь сообщений. Т.е. отладкой вообще не пользуетесь.  Это не есть гуд. Почти всегда необходимо иметь какой-то канал для отладки - не JTAG, так вывод на терминал (а еще лучше их оба, т.е. они вполне комплементарны), не вывод на терминал, так вывод еще куда-нибудь, но так, чтобы этот канал был усточивым к траблам в программе. Да хоть ножкой МК помахать. Если у вас есть экран, то, может, на него попробовать выводить отладочную инфу, если это не сложно. Цитата(alux @ Mar 21 2008, 20:46)  А в чем же дело? Два процесса ждут Signal, каждый от своего EventFlag. В процессах после Wait() идет Sleep(). Когда из третьего процесса (из п. меню) даю ef1.Signal() и ef2.Signal() для процессов, то все виснет. В смысле остальные процессы не работают. Что я делаю не так ?  Не могу сказать, "удаленно лечить не умею"  . Попробуйте на симуляторе прогнать, возможно, там можно будет увидеть проблему. Про OS::EventFlag мы знаем только один баг, и он проявляется в ситуации, когда два процесса ждут одновременно одного и того же флага. Общего утвержденного решения пока нет - есть частные, но они имеют проблемы с совместимостью поведения. Наиболее близким к желаемому вариантом является bug_1878045_fix, но он пока как следует не оттестирован, ждет своей очереди. Но два отдельных непересекающихся флага - это не сюда. В общем, попробуйте наладить какой-то канал отладки в железе и/или погонять в симуляторе - если ошибка логическая, то там ее несложно будет отловить.
--------------------
«Отыщи всему начало, и ты многое поймёшь» К. Прутков
|
|
|
|
|
Mar 22 2008, 11:28
|
Знающий
   
Группа: Свой
Сообщений: 589
Регистрация: 24-04-05
Пользователь №: 4 447

|
Вроде нашел еще один свой прокол : Скорость поступления данных из TMeasure в канал по крайней мере в 5 раз выше, чем скорость считывания из канала в TProcLCD. Возможно из-за этого были проблемы. Пока проверить не могу. Позже сообщу. По поводу симулятора. Как раз об этом думаю. Осталось разобраться с написанием макросов для симуляции прерываний и периферии. Заодно можно будет посмотреть использование стека каждым процессом. PS. Перед отправкой в канал сделал проверку свободного места: Код if(MsgQueue.get_count() < 3) MsgQueue.push(&ReDraw); Проверил. Проблема осталась. Т.е. когда два процесса ждут каждый свой EventFlag , то при подаче Сигналов из третьего процесса, программа виснет. То же самое и для одного общего флага. Кроме этого не могу понять, почему максимально возможное значение АЦП 0xffffff , которое я отправляю через очередь сообщений для TProcLCD, на индикатор выводится 0x4b7fffff. В данным момент в канал больше никто ничего не посылает. Проверял printf действительное значение АЦП перед запуском ОС (0xffffff) ?  PS2. Так, это уже слишком... При входе в пункт меню устанавливаю глобальный флаг (не EventFlag): Flags |= MEASURE; В процессе TMeasure в качестве отладки использую инвертирование знакоместа на ЖКИ и printf: Код OS_PROCESS void TMeasure::Exec() //TProc1 { for(;;) { monitoring.Wait(); // while(Flags & MEASURE) for(unsigned char i=0;i<20;i++) { PCInt1.Wait(); // Ждем окончания преобразования АЦП (RDY => 0) //measure(); MsgQueue.push(&Blink); printf_P("\n\rFlags: %d\n", Flags); Sleep(500); } } } Выяснилось, что после нулевой итерации Flags = 1, а после первой Flags=0 ! При этом в других процессах нет обращений к Flags. Что за...  Почему обнуляется глобальный флаг?
|
|
|
|
|
Mar 25 2008, 09:50
|

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

|
Цитата(alux @ Mar 23 2008, 17:25)  ... в реализации ReDraw используются функция-"пожиратель" стека: sprintf_P. А под процесс ProcLCD, в котором используется этот метод выделено 200 байт. Необходимо заменить sprintf на что-нибудь полегче. И к тому же это могло приводить к зависанию программы. Я прав? Более чем правы. Цитата(sevstels @ Mar 25 2008, 11:11)  Хочу поинтересоваться, это только у меня не запускается симулятор IAR в проекте с scmRTOS, или так и должно быть? У меня запускается и симулятор и эмулятор. А без scmRTOS запускается? Или что Вы понимаете под фразой "не запускается"?
|
|
|
|
|
Mar 25 2008, 10:43
|

Знающий
   
Группа: Участник
Сообщений: 626
Регистрация: 3-12-07
Пользователь №: 32 910

|
Тестировал на примерах scmRTOS v3 AVR. Изменил только настройки для работы с симулятором. Компилируется идеально, без ошибок или предупреждений. Но старт отладки не происходит. Стал искать наличие *.d90, его нигде нет .... хотя в окне workspace он присутствует. Открыл несколько своих старых проектов, там симулятор работает без проблем. Подумал, что в EWL версии ввели ограничение на размер кода. Установил полную версию IAR 5.10a Результат тот же - ни в одном примере c scmRTOS симулятор не работает и *.d90 не создаётся...
--------------------
|
|
|
|
|
Mar 26 2008, 05:27
|

Знающий
   
Группа: Участник
Сообщений: 626
Регистрация: 3-12-07
Пользователь №: 32 910

|
Пока пытаюсь найти причину сам. Вот нашёл вот тут: http://scmrtos.sourceforge.net/releases/avr/-DENABLE_BIT_DEFINITIONS -I$TOOLKIT_PATH$\AVR\inc -I$TOOLKIT_PATH$\AVR\inc\dlib TOOLKIT_PATH - отсутствует такое определение в IAR5.10 Есть TOOLKIT_DIR, см EWAVR_UserGuide.pdf стр 263(303) DENABLE_BIT_DEFINITIONS - тоже не нашёл такого нигде.. а вот ENABLE_BIT_DEFINITIONS есть
--------------------
|
|
|
|
|
Mar 26 2008, 10:33
|
Частый гость
 
Группа: Свой
Сообщений: 80
Регистрация: 3-08-06
Пользователь №: 19 287

|
Здравствуйте! Есть небольшой проектик под scmRTOS, ранее созданный для IAR EWARM 4.41, микроконтроллер LPC2148. Поставил EWARM 5.11 для пробы, но как-то не удается сделать так, чтобы, как и раньше, в векторах по адресам #00-#3F мирно сосуществовали как части из стандартного cstartup, так и части из OS_Target_asm. Поменял сегменты на секции, ORG на LTORG и т.д., но при компиляции в векторах присутствует либо только код из cstartup (т.е. нет частей ОС для программного и аппаратных IRQ/FIQ прерываний), либо только из OS_Target_asm (т.е. по 0-му вектору например находится какой-то мусор). Пытался менять :ROOT(x) в строках объявлений секций, что и приводило к вытеснению либо содержимого cstartup.s, либо OS_Target_asm.s. Что нужно сделать либо есть может у кого уже адаптированный OS_Target_asm.s для EWARM 5.11 ? Спасибо.
|
|
|
|
|
Mar 26 2008, 10:42
|

Странник
   
Группа: Свой
Сообщений: 766
Регистрация: 29-08-05
Из: Екатеринбург
Пользователь №: 8 051

|
Цитата(Ykidia @ Mar 26 2008, 15:33)  Что нужно сделать либо есть может у кого уже адаптированный OS_Target_asm.s для EWARM 5.11 ? Спасибо. Господа! Ну зачем все пихать в одну тему ?????
Почему нельзя создать новую, в которой и будет обсуждаться конкретный вопрос?
PS: А еще лучше задать вопрос в рассылке scmRTOS.
--------------------
"Как много есть на свете вещей, которые мне не нужны!" Сократ
|
|
|
|
|
Jun 28 2008, 08:38
|
Знающий
   
Группа: Свой
Сообщений: 589
Регистрация: 24-04-05
Пользователь №: 4 447

|
Цитата(dxp @ Mar 19 2008, 12:42)  Там все это совсем не сложно - адрес объекта известен, размер его полей до стека тоже известен, отсюда можно вычислить адрес, где начинается стек. Размер стека тоже известен. По сути надо просто от начала стека пройти до первого значения, которое отличается от значения по умолчанию (того, которым заполнен стек сначала). Это и будет размер свободного пространства в стеке. Далее, можно это опять же эмулятором смотреть, а можно вываливать хоть на терминал или куда удобно. Хочу вернуться к актуальному для меня сегодня вопросу определения размеров потребления стека процессами. scmRTOS порт IAR, AVR. Можно хотя бы пару строк кода примера привести? Допустим, создан объект Код TKeyScan KeyScan; // Процесс обработки клавиатуры с параметрами Код typedef OS::process<OS::pr0, 250, 32> TKeyScan; // Proc1; Задача: вычислить реальные CSTACK, RSTACK , использованные этим процессом в runtime. Ну, а вывести эти значения на ЖКИ в виде числа - это не проблема. И второй вопрос. Размер CSTACK, RSTACK в опциях компилятора имеет отношение только к функциям, которые вызываются до запуска OS::Run() ?
|
|
|
|
|
Jun 28 2008, 12:29
|

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

|
Цитата(alux @ Jun 28 2008, 11:38)  Можно хотя бы пару строк кода примера привести?
Задача: вычислить реальные CSTACK, RSTACK , использованные этим процессом в runtime. Примерно так: добавляем в конструктор процесса заполнение стека каким-то числом и добавляем функции сканирования стека: Код template<TPriority pr, word stack_size> class process : public TBaseProcess { public: INLINE process() : TBaseProcess(&Stack[stack_size/sizeof(TStackItem)] , pr , (void (*)())Exec) { TStackItem *pDst = Stack; word Size = StackPointer - Stack; while(Size) { *pDst++ = 0xAB; --Size; } } static int StackFree() { word Free = 0; for(;;) // stack always has non-0xAB items. { if( Stack[Free] != 0xAB ) return Free; ++Free; } } static int StackUsed() { return stack_size - StackFree(); } ................ Для двухстекового варианта переделайте сами. Для простоты функции возвращают результат в количестве элементов стека (а не байтах, но для AVR они совпадают). Не проверял, но думаю идея понятна. Посмотрите также еще один вариант реализации. Цитата(alux @ Jun 28 2008, 11:38)  И второй вопрос. Размер CSTACK, RSTACK в опциях компилятора имеет отношение только к функциям, которые вызываются до запуска OS::Run() ? Да. Эти же стеки используются для прерываний, если вы выбрали #define scmRTOS_ISRW_TYPE TISRW_SS
--------------------
На любой вопрос даю любой ответ"Write code that is guaranteed to work, not code that doesn’t seem to break" ( C++ FAQ)
|
|
|
|
|
Jun 28 2008, 18:11
|
Знающий
   
Группа: Свой
Сообщений: 589
Регистрация: 24-04-05
Пользователь №: 4 447

|
Цитата(Сергей Борщ @ Jun 28 2008, 15:29)  добавляем в конструктор процесса заполнение стека каким-то числом А разве стек в конструкторе (порт AVR) не инициализирован нулями? Прошу прощения за ламерский вопрос. В этом конструкторе просто добавить функции static int StackFree() и static int StackUsed(), в которой вычислять Stack[Free] == 0 или еще что-то нужно еще менять? Код OS::TBaseProcess::TBaseProcess(TStackItem* Stack, TStackItem* RStack, TPriority pr, void (*exec)()) : StackPointer(Stack) , Priority(pr) , Timeout(0) { Kernel.RegisterProcess(this);
//--------------------------------------------------------------- // // Prepare Process Stack Frame // *(--RStack) = reinterpret_cast<word>(exec); // return from interrupt address (low byte) *(--RStack) = reinterpret_cast<word>(exec) >> 8; // return from interrupt address (high byte)
--StackPointer; // emulate saving r31 *(--StackPointer) = 0x80; // SREG value: I-bit set, enable interrupts *(--StackPointer) = reinterpret_cast<word>(RStack-1) >> 8; // SP (high byte) *(--StackPointer) = reinterpret_cast<word>(RStack-1); // SP (low byte) StackPointer -= REGS_COUNT; // emulate saving regs (except r31) }
|
|
|
|
|
Jul 30 2008, 18:42
|

Частый гость
 
Группа: Свой
Сообщений: 105
Регистрация: 6-01-06
Пользователь №: 12 901

|
И снова "уровень оптимизации".. / см.следующий пост /Цитата(alux @ Mar 5 2008, 11:36)  Дело в том, что у jacOS есть примечание для IAR C/EC++ for AVR 4.11A/W32 : Цитата Вероятны проблемы при установке опций оптимизации Code motion (?) Cross Call (?)
Подтверждаю! / не верно! /Условия: - порт Сергея Борща (спасибо, Сергей!  ); - SAM7S (256, опции "проекта" и xcl соответственно "подправлены"); - IAR AR C/C++ for ARM 4.40A; - optimization - speed (high) , если флаг Code motion не снят, то "сложные" (имеющие вложенные) ф-ции, вызванные из Процесса НЕ выполняются: / причина в малом размере стека!!!! /Код OS_PROCESS void TProc2::Exec() { .... for(;;) { Timer_Ovf.Wait(); .... StDisp.Draw_Line(0,0,110,59,n);// А в "ней" вызыв."одноклассница" (TDisp) - Put_Pixel(x,x,x); ... } } Не знаю "чего и куда" компиллятор при оптимизации "motion" (видимо цикл обработки точек линии "выворачивает"), но только ОСь падает.. Интуитивно - что-то со стеком, НО его увеличение /не проводилось!!!/, даже "неразумное", ситуацию не меняет... (попытаюсь разобраться "через" Simulator) ... И еще "побочный" вывод: "Wiggleg + H-JTAG" - это, увы, скорее программатор, чем отладчик. Ни память "посмотреть", ни "останов" задать, ни "вообще"...
|
|
|
|
|
Jul 30 2008, 20:30
|

Частый гость
 
Группа: Свой
Сообщений: 105
Регистрация: 6-01-06
Пользователь №: 12 901

|
Прошу прощения за предыдущий пост! (стыдно, но рука не "поднялась" его "пофиксить") "Правды" в нем - только благодарность авторам! Я не стек увеличивал, а размер контекста.... Код #define scmRTOS_IDLE_PROCESS_STACK_SIZE 17 * sizeof(TStackItem) А как только увеличил размер стека Код typedef OS::process<OS::pr0, 200> TProc1; typedef OS::process<OS::pr1, 400> TProc2; typedef OS::process<OS::pr2, 200> TProc3; , так сразу Code motion перестал оказывать "влияние". .... Дизассембленый код, все же, посмотрел, разницы не увидел (опция Code motion ). Код функций неизмЕнен, функции на "своих" (одних и тех же) местах (две "верхние", во всяком случае). Глубина вложения "оказалась" - 4-ре уровня: линия->точка->формирование байта для записи->передача байта в LCD. .... Попутно возник вопрос.. Как лучше поступать? Делать функции встраиваемыми, "экономя" ОЗУ, или использовать вложенные, увеличивая размер стека? Понимаю, что ответ, скорее всего будет, - "Все зависит от конкретной задачи.." Но все же.. Нужно ли, допустим, объявлять встраиваемыми функции, зависимые от платформы ("нижние", так сказать)? Что подсказывает "опыт" и какой "стиль программирования" правильный? Спасибо.
|
|
|
|
|
Apr 14 2009, 03:03
|
Знающий
   
Группа: Свой
Сообщений: 589
Регистрация: 24-04-05
Пользователь №: 4 447

|
Как определить размеры потребления RSTACK процессами? Вариант Сергея Борща (сообщение #77) определения CSTACK работает, если функции-члены StackFree() и StackUsed() определить НЕ static, т.к. в них используется нестатические переменные Stack, RStack. Пробовал вставить в конструктор процесса: Код template<TPriority pr, word stack_size, word rstack_size> class process : public TBaseProcess { public: INLINE process() : TBaseProcess( &Stack[stack_size/sizeof(TStackItem)] , &RStack[rstack_size/sizeof(TStackItem)] , pr , (void (*)())Exec) { // Заполнить RSTACK процесса значением 0xAB TStackItem *pDst = RStack; word Size = GetReturnSP() - RStack; while(Size) { *pDst++ = 0xAB; --Size; } }
OS_PROCESS int RStackFree() { word Free = 0; for(;;) // stack always has non-0xAB items. { if( RStack[Free] != 0xAB ) return Free; ++Free; } } OS_PROCESS static void Exec(); private: TStackItem Stack [stack_size/sizeof(TStackItem)]; TStackItem RStack[rstack_size/sizeof(TStackItem)]; }; Безрезультатно. После этого программа даже не запускается
|
|
|
|
|
Apr 14 2009, 11:09
|
Знающий
   
Группа: Свой
Сообщений: 589
Регистрация: 24-04-05
Пользователь №: 4 447

|
Цитата(dxp @ Apr 14 2009, 09:04)  Иначе (если больше), то там Memory Overwrite со всеми вытекающими. Не работает, даже если уменьшить Size : Size = GetReturnSP() - RStack - 2; Странно, ведь при вычислении реального CSTACK эти функции работают...
|
|
|
|
|
Apr 15 2009, 06:41
|

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

|
Цитата(alux @ Apr 14 2009, 18:09)  Не работает, даже если уменьшить Size : Size = GetReturnSP() - RStack - 2;
Странно, ведь при вычислении реального CSTACK эти функции работают... Тут нужно не пытаться угадать, а просто посмотреть отладчиком (хотя бы на симуляторе) структуру stack frame и убедиться, что все делается правильно: заполнение, инициализация полей. Просто по шагам пройти спокойно и посмотреть. И заодно посмотреть, как происходит передача управления первому процессу (в OS_Start). Там будет видно, куда переход осуществляется, и видно причину, почему (если) не туда - значения в стеке.
--------------------
«Отыщи всему начало, и ты многое поймёшь» К. Прутков
|
|
|
|
|
Jul 28 2009, 11:16
|
Группа: Участник
Сообщений: 14
Регистрация: 14-04-09
Пользователь №: 47 644

|
При отладке демо-приложения в IAREW v 4.11b для MSP430 на точке останова вылетает следующее сообщение:
Tue Jul 28 15:06:49 2009: Breakpoint hit: Code @ main.cpp:123.9 Tue Jul 28 15:06:49 2009: The stack pointer for stack 'Stack' (currently Memory:0x402) is outside the stack range (Memory:0x9B0 to Memory:0xA00)
При этом программа работает. Так и должно быть или это ошибка? Объясните пожалуста.
|
|
|
|
|
Jul 28 2009, 12:36
|

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

|
Цитата(Copypaster @ Jul 28 2009, 14:16)  При отладке демо-приложения в IAREW v 4.11b для MSP430 на точке останова вылетает следующее сообщение:
Tue Jul 28 15:06:49 2009: Breakpoint hit: Code @ main.cpp:123.9 Tue Jul 28 15:06:49 2009: The stack pointer for stack 'Stack' (currently Memory:0x402) is outside the stack range (Memory:0x9B0 to Memory:0xA00)
При этом программа работает. Так и должно быть или это ошибка? Объясните пожалуста. Такое сообщение выскакивает когда stack plugin обнаруживает, что указатель стека указывает за пределы стека, обьявленного в среде по умолчанию. Но в контексте применения данной ОС (как и многих других) указатель стека указывают на стеки процессов, которые находятся вне стека по умолчанию. Единственный выход - отключить плагин stack. Как при этом контролировать переполнения стеков - не спрашивайте, тема избитая и на форуме много раз обсуждалась.
|
|
|
|
|
Mar 24 2010, 10:09
|
Группа: Участник
Сообщений: 10
Регистрация: 8-02-10
Пользователь №: 55 367

|
Начал разбираться с scmRTOS.3.10. Ответьте, плз. на несколько вопросов. 1) смотрю пример 1-EventFlag, описание процесса: typedef OS::process<OS::pr0, 120, 32> TProc1; Что означает 32? В доке на V2.0 такого параметра нет.
2) Существует ли проблема локальных переменных, объявленных в разных процессах? Когда происходит прерывание текущего процесса более приоритетным, тогда есть опасность порчи локальных переменных, ведь они формируются компилятором из кучи. Как быть? Объявлять их static? Чего-то я недопонимаю.
|
|
|
|
|
Mar 25 2010, 06:50
|
Группа: Участник
Сообщений: 10
Регистрация: 8-02-10
Пользователь №: 55 367

|
Цитата(dxp @ Mar 24 2010, 18:04)  Как это нет? Раздел 6.3 (порт для AVR), стр 85. Извиняюсь, виноват, до порта еще не дочитал...
|
|
|
|
|
Mar 25 2010, 09:03
|
Группа: Участник
Сообщений: 10
Регистрация: 8-02-10
Пользователь №: 55 367

|
Пользуясь случаем, хочу спросить у DXP. Когда примерно вы планируете выпуск мануала ОСи V3.1?
|
|
|
|
|
Mar 26 2010, 07:21
|

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

|
Цитата(Embedder74 @ Mar 25 2010, 15:03)  Пользуясь случаем, хочу спросить у DXP. Когда примерно вы планируете выпуск мануала ОСи V3.1? Определенно сказать не могу. Тот функционал, который существует сейчас, на 90% (если не больше) закрыт мануалом версии 2. Есть планы относительно новых возможностей, когда они будет реализованы, тогда и будет смысл выпускать новую документацию. По срокам ничего обещать не могу, планировал еще года полтора все это сделать, но текущая работа съедает все время. Удается только выкраивать какие-то куски на решение текущих вопросов по проекту.
--------------------
«Отыщи всему начало, и ты многое поймёшь» К. Прутков
|
|
|
|
|
Mar 26 2010, 08:46
|

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

|
Ну и я что ли спрошу. Почему в проекте, написанном на С++ вы используете модификаторы доступа private а не protected, например в классах описывающих сервисы? Я вот захотел малость дополнить/изменить функционал кольцевого буфера TCbuf, унаследовавшись от него, и не получил доступа к полям Код private: byte* buf; byte size; volatile byte count; byte first; byte last; без изменения исходника ОС.
|
|
|
|
|
Mar 26 2010, 12:54
|

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

|
Цитата(jorikdima @ Mar 26 2010, 14:46)  Почему в проекте, написанном на С++ вы используете модификаторы доступа private а не protected, например в классах описывающих сервисы? Я вот захотел малость дополнить/изменить функционал кольцевого буфера TCbuf, унаследовавшись от него, и не получил доступа к полям Потому, что этот класс не предназначался для того, чтобы его расширять наследованием, а только лишь как основа для сервиса TChannel. И оба класса уже давно obsolete, вместо них лучше использовать шаблоны ring_buffer и channel соответственно, которые универсальнее и функциональнее.
--------------------
«Отыщи всему начало, и ты многое поймёшь» К. Прутков
|
|
|
|
|
Mar 26 2010, 13:24
|

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

|
Цитата(dxp @ Mar 26 2010, 15:54)  Потому, что этот класс не предназначался для того, чтобы его расширять наследованием, а только лишь как основа для сервиса TChannel. И оба класса уже давно obsolete, вместо них лучше использовать шаблоны ring_buffer и channel соответственно, которые универсальнее и функциональнее. Сорри, я как раз ring_buffer и использовал, перепутал. Но это не имеет значение, я говорил вообще про все классы. TEventFlag например, или тот же channel. У всех есть поля private. Почему бы не предназначить эти классы в том числе для расширения наследованием? Ведь всего-то надо сделать поля не private, а protected
|
|
|
|
|
Mar 26 2010, 14:48
|

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

|
Цитата(jorikdima @ Mar 26 2010, 19:24)  Сорри, я как раз ring_buffer и использовал, перепутал. Но это не имеет значение, я говорил вообще про все классы. TEventFlag например, или тот же channel. У всех есть поля private. Почему бы не предназначить эти классы в том числе для расширения наследованием? Ведь всего-то надо сделать поля не private, а protected  А что это даст? Ну, отнаследуетесь вы от TEventFlag, и что дальше? Все равно весь осезависимый функционал закрыт (доступ к функциям ядра). Т.е. если хочется слепить свой собственный сервис, то этот вариант ничем не поможет. Как раз планируемые новшества и связаны с тем, чтобы дать возможность пользователю создавать свои собственные сервисы (как сказал уже Сергей, в репе есть экспериментальная ветка, но лично мне там не все нравится, надо еще помозговать). И всегда остается возможность строить свои типы на основе включения штатных сервисов. Кстати, ring_buffer - это не сервис, а просто реализация кольцевого буфера. Что вам не хватает в его реализации, что вы хотите отнаследоваться и написать свой вариант?
--------------------
«Отыщи всему начало, и ты многое поймёшь» К. Прутков
|
|
|
|
|
Mar 27 2010, 06:51
|

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

|
Цитата(dxp @ Mar 26 2010, 17:48)  Кстати, ring_buffer - это не сервис, а просто реализация кольцевого буфера. Что вам не хватает в его реализации, что вы хотите отнаследоваться и написать свой вариант? Приведу конкретный пример, удачный, неудачный - неважно. Я использую кольцевой буфер для хранения данных, которые я выдаю наружу через UART, используя DMA. Для зарядки DMA нужно знать адрес, по которому хранится первый элемент и размер. Можно сделать, например так: вычитываем все что есть в буфере в переменную (массив, лучше глобальный) и даем DMA адрес этого массива. Тут всего текущего функционала достаточно. Но это требует удвоенного RAM и постоянного перекладывания "из одного кармана в другой". Я бы предпочел указывать DMA адрес, где уже хранятся даные в ring_buffer, создав наследника и добавив функцию возвращения адреса. Для этого мне нужен доступ к его private полям, который я получил бы, будь они protected, наследованием. В данном примере мне доступ к ядру не нужен. Быть может это редкий случай, когда можно применять наследование?
|
|
|
|
|
Mar 27 2010, 13:31
|

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

|
Цитата(jorikdima @ Mar 27 2010, 12:51)  Приведу конкретный пример, удачный, неудачный - неважно. Я использую кольцевой буфер для хранения данных, которые я выдаю наружу через UART, используя DMA. А что, у вас DMA контроллер умеет читать по кольцу с произвольного адреса (делать wrap around при достижении границы пула памяти, выделенной под кольцевой буфер)? И кто будет модифицировать состояние объекта-буфера - head/tail указатели, count и т.д.? Просто так вычитать с "черного хода" данные недостаточно для корректной работы кольцевого буфера. Может я чего-то не понял, но такой вариант выглядит как грязный хак.
--------------------
«Отыщи всему начало, и ты многое поймёшь» К. Прутков
|
|
|
|
|
Mar 28 2010, 10:45
|

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

|
Цитата(dxp @ Mar 27 2010, 16:31)  А что, у вас DMA контроллер умеет читать по кольцу с произвольного адреса (делать wrap around при достижении границы пула памяти, выделенной под кольцевой буфер)? И кто будет модифицировать состояние объекта-буфера - head/tail указатели, count и т.д.? Просто так вычитать с "черного хода" данные недостаточно для корректной работы кольцевого буфера. Может я чего-то не понял, но такой вариант выглядит как грязный хак. Нет, DMA по кольцу не читает (про MSP430 речь). О wrap around забочусь я, если вижу, что не вычитанные данные "разрываются", то на вход DMA даю только те данные, которые идут от текущего места до конца линейного буфера, а остальное в следующий раз. Модифицирую состояние объекта-буфера - head/tail указатели, count и т.д я при таком вот чтении. Тут конечно кроется момент, связынный с тем, что сообщив на вход DMA адрес начала данных, запустив его на передачу и модифицировав count и head указатель, я по настоящему то буфер еще не освободил (пока DMA не отработал до конца). Но я сам гарантирую в своем софте, что не затру ту часть данных, которая с точки зрения кольцевого буфера свободна (а по факту еще не вычитана), новыми данными. Гарантирую это знанием о частоте прихода новых данных и размером кольцевого буфера, взятого с запасом. Собственно это конечно не есть пример грамотного программирования, быть может  , но пример наследования, где private модификатор мне пришлось поменять на protected.
|
|
|
|
2 чел. читают эту тему (гостей: 2, скрытых пользователей: 0)
Пользователей: 0
|
|
|