но даже не знаю какой стандарт поддерживает avr-gcc в atmelstudio ведь не так давно вышел очередной стандарт с++17
Про это можете забыть сразу.
Вообще про плюсы на контроллере, да еще и Гарвардской архитектуры, вспоминать грешно.
Чтобы использовать C++ с комфортом, необходимо как минимум динамическое выделение памяти, а для микроконтроллера это непозволительная роскошь - здесь нормальный менеджер памяти сам почти всю доступную память и скушает. Отдельный поворот делу придает то, что AVR, например, не может выполнять код из RAM (Гарвардская архитектура, да).
Конечно, отдельные эстеты умудряются писать на плюсах и под мелкие МК, но к этому нельзя относиться всерьез.
Так что только ANSI C, причем, желательно, с учетом правил MISRA.
_________________ Разница между теорией и практикой на практике гораздо больше, чем в теории.
char mask = 0; if (! (BTN_PIN & BTN_LINE_UP)) mask = BTN_SHRT_UP; if (! (BTN_PIN & BTN_LINE_DN)) mask = BTN_SHRT_DN; if (! (BTN_PIN & BTN_LINE_POWER)) mask = BTN_SHRT_POWER; if (! (BTN_PIN & BTN_LINE_SW)) mask = BTN_SHRT_SW;
if (mask){ //опрос состояния кнопки if (BtnLockCoun < (BTN_LOCK_TIME/10)){ //клавиша нажата BtnLockCoun++; return; //защелка еще не дощитала - возврат } BtnLastState = mask; BtnLockBit =1; //нажатие зафиксировано if (BtnLongCoun >= (BTN_LONG_TIME/10)) return; //возврат, т.к. счетчик длинн нажат досчитал до максимума еще раньше if (++BtnLongCoun >= (BTN_LONG_TIME/10)) BtnFlags |= (BtnLastState<<4); //счетчик досчитал до максимума - устанавливаем биты длинного нажатия } else{ //клавиша отжата if (BtnLockCoun){ BtnLockCoun --; return; //защелка еще не обнулилась - возврат } if (! BtnLockBit) //СТАТИЧЕСКИЙ ВОЗВРАТ return; BtnLockBit =0; //отжатие зафиксировано if (BtnLongCoun < (BTN_LONG_TIME/10)) BtnFlags |= BtnLastState; //установка бита короткого нажатия BtnLongCoun = 0; //сброс счетчика длительности нажатия } } //----------****7SEG****---------- #define SEGA 6 #define SEGB 5 #define SEGC 1 #define SEGD 2 #define SEGE 3 #define SEGF 4 #define SEGG 0
#define ANOD1 4 #define ANOD2 7 #define ANOD3 4 //---------- void segchar (unsigned char seg) { switch (seg) { case 0: PORTC=(0<<SEGA)|(0<<SEGB)|(0<<SEGC)|(0<<SEGD)|(0<<SEGE)|(0<<SEGF)|(1<<SEGG);break; case 1: PORTC=(1<<SEGA)|(0<<SEGB)|(0<<SEGC)|(1<<SEGD)|(1<<SEGE)|(1<<SEGF)|(1<<SEGG);break; case 2: PORTC=(0<<SEGA)|(0<<SEGB)|(1<<SEGC)|(0<<SEGD)|(0<<SEGE)|(1<<SEGF)|(0<<SEGG);break; case 3: PORTC=(0<<SEGA)|(0<<SEGB)|(0<<SEGC)|(0<<SEGD)|(1<<SEGE)|(1<<SEGF)|(0<<SEGG);break; case 4: PORTC=(1<<SEGA)|(0<<SEGB)|(0<<SEGC)|(1<<SEGD)|(1<<SEGE)|(0<<SEGF)|(0<<SEGG);break; case 5: PORTC=(0<<SEGA)|(1<<SEGB)|(0<<SEGC)|(0<<SEGD)|(1<<SEGE)|(0<<SEGF)|(0<<SEGG);break; case 6: PORTC=(0<<SEGA)|(1<<SEGB)|(0<<SEGC)|(0<<SEGD)|(0<<SEGE)|(0<<SEGF)|(0<<SEGG);break; case 7: PORTC=(0<<SEGA)|(0<<SEGB)|(0<<SEGC)|(1<<SEGD)|(1<<SEGE)|(1<<SEGF)|(1<<SEGG);break; case 8: PORTC=(0<<SEGA)|(0<<SEGB)|(0<<SEGC)|(0<<SEGD)|(0<<SEGE)|(0<<SEGF)|(0<<SEGG);break; case 9: PORTC=(0<<SEGA)|(0<<SEGB)|(0<<SEGC)|(0<<SEGD)|(1<<SEGE)|(0<<SEGF)|(0<<SEGG);break; case 99: //OFF Все сегменты PORTC=(1<<SEGA)|(1<<SEGB)|(1<<SEGC)|(1<<SEGD)|(1<<SEGE)|(1<<SEGF)|(1<<SEGG);break; } }
philosoraptor, я читал и эту статью, и ей подобные. И тут есть несколько моментов.
1. Автор рассматривает исключительно задачу работы с периферией, причем исключительно с портами. Выше же шла речь о более широком применении С++, в частности, и в основной логике - иначе зачем вообще заморачиваться?
2. Автор использует исключительно статические классы и ни словом не заикнулся (логично) про создание их экземпляров. И все равно, несмотря на все изгибание C++ для применимости в эмбеде, не обошлось без ограничений:
- "От динамической конфигурации линий ввода-вывода сразу отказываемся из-за необходимости доступа к портам через указатель со всеми вытекающими последствиями."
- "Мы не можем прочитать состояние выходных линий регистра – он всегда работает на выход, поэтому функцию чтения состояния не реализуем и не объявляем. Попытка прочитать состояние такого пора вызовет ошибку компиляции."
А динамическая конфигурация бывает нужна. Например, чтобы имитировать открытый коллектор на AVR.
Неизвестно, на какие компромиссы придется пойти при разработке API к другим специфичным блокам, которым периодически бывают нужны, например, особые последовательности доступа.
3. И несмотря на все ограничения код получился тяжеловесным и сложным для понимания. Даже сам автор в некоторый момент устал: "Обобщая доступ к отдельным битам в списке линий приходим к концепции среза. Не буду вдаваться в подробности их реализации".
4. В синтетических примерах автора результат дизассемблирования выглядит неплохо, но где гарантия, что в каких-то реальных условиях что-то не пойдет не так, и компилятор не сгенерирует код для того, что по задумке должен вычислять во время компиляции?
Родственный пример (для AVR-libc) - функции задержки, например, _delay_ms(). Если подставить в нее не константу, а переменную, будет сюрприз. Здесь может произойти нечто подобное.
struct MakePinList { private: // рекурсивно проходим все параметры // на следующей итерации Position увеличится на 1, // а T2 превратится в T1 и так далее typedef typename MakePinList < Position + 1,
...
5. В целом можно сказать, что фактически автор лишь использует механизм шаблонов C++ как продвинутый препроцессор. Впрочем, и без классического препроцессора не обходится:
Так в программирование неуместно употреблять один подход, и скорее даже не возможно написать код следуя всего лишь одной парадигме программирования т.к даже на языке Си, который не является объектно-ориентированным, можно работать в соответствии с принципами объектно-ориентированного программирования, хотя это и сопряжено с определёнными сложностями, а функциональное программирование можно применять при работе на любом императивном языке, в котором имеются функции, Я решил закончить данную прошивку в стиле функционального программирования, а уже следующие не мение интересную попробовать написать (не потеряв при этом быстродействие) сделав уклон на ООП.
Добавлено after 8 minutes 23 seconds: А насчет MISRA C который являеться как бы ''стандартом разработки программного обеспечения на Си'" . предполгаю что на данный момент времени он не являеться актуальным т.к насколько мне известно они не удосужились внести С99 в описание своего стандарта, не говоря о уже о том что процесса сертификации MISRA не существует в принцепе.
Добавлено after 1 hour 7 minutes 32 seconds: Я вот думаю как все таки более лаконично написать легко переносимую функцию? для работы с динамической индикацией семи сегментного индикатора. А то segchar()+WriteSeg() выглядят как то не комильфо.
предполгаю что на данный момент времени он не являеться актуальным
Это всего лишь ваше предположение. Впрочем, некоторые говорят, что и Си не является актуальным.
Процесса сертификации MISRA не существует по простой причине: MISRA - это, так сказать, набор хороших советов, которые применимы для создания кода, используемого в разных применениях в разных отраслях с разными требованиями и подходами к сертификации. Например, устройство в целом может быть сертифицировано на некоторый уровень SIL, при этом для достижения этой цели логично использовать правила MISRA при написании прошивки - это уберет много граблей. Можно, конечно, не заморачиваться и идти путем проб и ошибок, самостоятельно переоткрывая рекомендации MISRA.
Цитата:
Я вот думаю как все таки более лаконично написать легко переносимую функцию?
Я для динамической индикации использую что-то вроде фреймбуфера, в который складываю паттерны для символов на индикаторе. Этот буфер выводится в порт по прерыванию. Заполняет его отдельная функция, которая заодно преобразует числа/символы в паттерны.
_________________ Разница между теорией и практикой на практике гораздо больше, чем в теории.
_delay_ms(). Если подставить в нее не константу, а переменную, будет сюрприз.
На эти грабли, пожалуй, все новички наступают.
YS писал(а):
В синтетических примерах автора результат дизассемблирования выглядит неплохо, но где гарантия, что в каких-то реальных условиях что-то не пойдет не так, и компилятор не сгенерирует код для того, что по задумке должен вычислять во время компиляции?
Гарантии нет в любом случае, даже если строго следовать всем стандартам. Остается уповать лишь на здравый смысл кодера и на способность препроцессора отловить совсем уж явные несуразности.
YS писал(а):
речь о более широком применении С++, в частности, и в основной логике - иначе зачем вообще заморачиваться?
Например, если мы неплохо владеем С++, то легко сможем составить на нем и полностью обратно-совместимый с чистым Си код. В чем профит, спросите вы? Ну, к примеру, есть множество ардуинообразных библиотек на С++, которые можно использовать и без ардуины. Да, их можно легко портировать и на Си, но это лишние телодвижения.
Гарантии нет в любом случае, даже если строго следовать всем стандартам.
Просто не надо полагаться на оптимизации вроде вычисления выражений на этапе компиляции (если только они не состоят исключительно из констант-дефайнов) и, тем более, замену компилятором участка кода на результат его выполнения. Это очень рискованно само по себе, а заодно усложняет портирование кода.
Я считаю, что на оптимизацию вообще полагаться не надо. Надо стараться, чтобы написанный код был как можно более прозрачен.
Цитата:
Например, если мы неплохо владеем С++, то легко сможем составить на нем и полностью обратно-совместимый с чистым Си код.
В этом случае обычно пишут библиотеку на чистом C, а на C++ делают только обертку для желающих. Такой подход гораздо лучше потому, что обеспечивает совместимость не только на уровне исходников, но и на бинарном уровне - скажем, DLL на чистом Си гораздо универсальнее. То же самое справедливо и для скомпилированных статических библиотек.
_________________ Разница между теорией и практикой на практике гораздо больше, чем в теории.
char mask = 0; if (! (BTN_PIN & BTN_LINE_UP)) mask = BTN_SHRT_UP; if (! (BTN_PIN & BTN_LINE_DN)) mask = BTN_SHRT_DN; if (! (BTN_PIN & BTN_LINE_POWER)) mask = BTN_SHRT_POWER; if (! (BTN_PIN & BTN_LINE_SW)) mask = BTN_SHRT_SW;
if (mask){ //опрос состояния кнопки if (BtnLockCoun < (BTN_LOCK_TIME/10)){ //клавиша нажата BtnLockCoun++; return; //защелка еще не дощитала - возврат } BtnLastState = mask; BtnLockBit =1; //нажатие зафиксировано if (BtnLongCoun >= (BTN_LONG_TIME/10)) return; //возврат, т.к. счетчик длинн нажат досчитал до максимума еще раньше if (++BtnLongCoun >= (BTN_LONG_TIME/10)) BtnFlags |= (BtnLastState<<4); //счетчик досчитал до максимума - устанавливаем биты длинного нажатия } else{ //клавиша отжата if (BtnLockCoun){ BtnLockCoun --; return; //защелка еще не обнулилась - возврат } if (! BtnLockBit) //СТАТИЧЕСКИЙ ВОЗВРАТ return; BtnLockBit =0; //отжатие зафиксировано if (BtnLongCoun < (BTN_LONG_TIME/10)) BtnFlags |= BtnLastState; //установка бита короткого нажатия BtnLongCoun = 0; //сброс счетчика длительности нажатия } } //----------****7SEG****---------- #define SEGA 6 #define SEGB 5 #define SEGC 1 #define SEGD 2 #define SEGE 3 #define SEGF 4 #define SEGG 0
#define ANOD1 4 #define ANOD2 7 #define ANOD3 4 //---------- void segchar (unsigned char seg) { switch (seg) { case 0: PORTC=(0<<SEGA)|(0<<SEGB)|(0<<SEGC)|(0<<SEGD)|(0<<SEGE)|(0<<SEGF)|(1<<SEGG);break; case 1: PORTC=(1<<SEGA)|(0<<SEGB)|(0<<SEGC)|(1<<SEGD)|(1<<SEGE)|(1<<SEGF)|(1<<SEGG);break; case 2: PORTC=(0<<SEGA)|(0<<SEGB)|(1<<SEGC)|(0<<SEGD)|(0<<SEGE)|(1<<SEGF)|(0<<SEGG);break; case 3: PORTC=(0<<SEGA)|(0<<SEGB)|(0<<SEGC)|(0<<SEGD)|(1<<SEGE)|(1<<SEGF)|(0<<SEGG);break; case 4: PORTC=(1<<SEGA)|(0<<SEGB)|(0<<SEGC)|(1<<SEGD)|(1<<SEGE)|(0<<SEGF)|(0<<SEGG);break; case 5: PORTC=(0<<SEGA)|(1<<SEGB)|(0<<SEGC)|(0<<SEGD)|(1<<SEGE)|(0<<SEGF)|(0<<SEGG);break; case 6: PORTC=(0<<SEGA)|(1<<SEGB)|(0<<SEGC)|(0<<SEGD)|(0<<SEGE)|(0<<SEGF)|(0<<SEGG);break; case 7: PORTC=(0<<SEGA)|(0<<SEGB)|(0<<SEGC)|(1<<SEGD)|(1<<SEGE)|(1<<SEGF)|(1<<SEGG);break; case 8: PORTC=(0<<SEGA)|(0<<SEGB)|(0<<SEGC)|(0<<SEGD)|(0<<SEGE)|(0<<SEGF)|(0<<SEGG);break; case 9: PORTC=(0<<SEGA)|(0<<SEGB)|(0<<SEGC)|(0<<SEGD)|(1<<SEGE)|(0<<SEGF)|(0<<SEGG);break; case 99: //OFF Все сегменты PORTC=(1<<SEGA)|(1<<SEGB)|(1<<SEGC)|(1<<SEGD)|(1<<SEGE)|(1<<SEGF)|(1<<SEGG);break; } }
void BtnUpdate(void) { ................................ ................................ ZoneNumber++; // !!!! здесь может вызваться обработчик INT0_vect??? Если да, то внимательно посмотрите, что произойдет!!!! if (ZoneNumber==6) { ZoneNumber=0; } .............................................. .............................................. }
Последний раз редактировалось viiv Пн окт 23, 2017 17:07:05, всего редактировалось 3 раз(а).
Обработчик прерываний асинхронный? т.е. может прервать программу в любом месте? Тогда посмотрите внимательно, что будет, если обработчик вызовится там, где у меня комментарий (при вызове обработчика ZoneNumber равняется 6).
2) unsigned char count = 0; // используется только в WriteSeg() - нет причин делать данную переменную глобальной; Ограничте видимость функцией WriteSeg(). Это не единственное, "видимость" чего желательно ограничить.
Насчет _delay_ms () знаю что не комильфо, но тот цикл используется всего лишь один раз при включении а более элегантного решения в голову не пришло (. Насчет всего остального спасибо еще немного покопаюсь в этом коде. А насчет 5того пункта компилятор не выдавал Warningов , хотя согласен логическое И правильней писать &&, странно что эти условия еще и работали.
Могут одновревенно стоять носколько бит? Если да, то (BtnMask == BTN_SHRT_UP) - неверно. Если нет, то и незачем определять, как маску - это сильно запутывет.
Вы не можете начинать темы Вы не можете отвечать на сообщения Вы не можете редактировать свои сообщения Вы не можете удалять свои сообщения Вы не можете добавлять вложения