|
К знатокам, Локальные переменные. |
|
|
|
 |
Ответов
(75 - 89)
|
Sep 23 2007, 21:00
|
дятел
    
Группа: Свой
Сообщений: 1 681
Регистрация: 13-05-06
Из: Питер
Пользователь №: 17 065

|
Цитата(zltigo @ Sep 23 2007, 23:21)  Такие интимные подробности описаны? Типа, что фронт прерывания от встроеного таймера чисто конкретно приходится на конец выполнения команды а не на ее начало? Честно говоря совсем не верю. Цитату не дадите? Zltigo, вы знаете, собирался по честному процитировать Вам даташит, но уже не буду, потому как видимо бессмысленно... Чего то щелкнуло в голове и я решил пересмотреть Ваши высказывания: Цитата(zltigo @ Sep 20 2007, 18:09)  ..... Такие длинные групповые команды можно просто не использовать, тогда самая длинная это 8 тактов. Итого, при необходимости быстрой реакции на прерывание получаем 15 тактов. ...... Ну а дальше можно считать сколько времени займет вход в обработчик FIQ у типичного 60MHz ARM. Считаем. Максимальная задержка = 250ns, ну джиттер соответственно 7 тактов (причем независимо от IRQ/FIQ) = 117ns. Вот мне и интересно, 8 - (8-1=7)тактов для АРМ это норма  а для AVR Вам нужна цитата ?  Как минимум, интересное замечание. Если захотите оправдаться, не буду Вам мешать, но попробуйте сделать это как минимум достойно....
|
|
|
|
|
Sep 23 2007, 21:36
|
Гуру
     
Группа: Свой
Сообщений: 2 712
Регистрация: 28-11-05
Из: Беларусь, Витебск, Строителей 18-4-220
Пользователь №: 11 521

|
Попробую я вмешаться. Я тоже делал много изделий с требованиями минимизации джиттера.
Если рассматривать джиттер входа в прерывание, то он зависит по сути только от разброса времени исполнения комманд МК. И больше не от чего. Иными словами для AVR это 3(4-1) такта без учёта работы с внешней памятью. Если рассматривается скорость реакции на прерывание (я бы это джиттером не назвал), то в рамках Си это будет зависеть от реализации п/п обработки прерывания. В частности от количества сохраненных регистров. А это напрямую зависит от сложности написания самого прерывания в том числе, что также очевидно, и применения программирования более высокого уровня C/C++, естественно при сравнении проги одного программиста. Если рассматривать, к примеру, дрожание принятия какого нибудь решения внутри прерывания относительно его начала, то это будет зависеть от линейности написания программы от начала прерывания до места принятия решения.
Так определитесь о каком джиттере идёт речь? Иначе дальнейшее обсуждение данного вопроса эээ... малоинформативно.
|
|
|
|
|
Sep 23 2007, 21:52
|

Гуру
     
Группа: Свой
Сообщений: 13 372
Регистрация: 27-11-04
Из: Riga, Latvia
Пользователь №: 1 244

|
Цитата(singlskv @ Sep 24 2007, 00:00)  Zltigo, вы знаете, собирался по честному процитировать Вам даташит, но уже не буду, Дело Ваше, можете и не по честному, мне без разницы. Цитата Вот мне и интересно, 8 - (8-1=7)тактов для АРМ это норма  а для AVR Вам нужна цитата ?  Да, это норма, ибо у ARMов имеются семь тактов до начала процесса вклинивания Цитата(SasaVitebsk @ Sep 24 2007, 00:36)  для AVR это 3(4-1) такта Прерывание возникло за 1ns до окончания команды. Прерывание возниколо через одну наносекунду после начала 4x тактовой команды. Какая разница будет? Утверждается, что где-то документировано возниковение прерывания от внутреннего таймера сразу после начала исполнения команды, нежели такое документировано, то тогда действительно для случая внутренего таймера разница будет в 3 такта. В любом другом - 4. Цитата Иначе дальнейшее обсуждение данного вопроса эээ... малоинформативно. Оно уже абсолютно не информативно для хоть какой-либо невырожденной системы, ибо наличие более одного прерывания, или критической секции, времена ммеют совершенно другой порядок. Еще более неинформативны, точнее 100% бессысленны, рассуждения о "джиттере" из-за использования C++ затеяные singlskv.
--------------------
Feci, quod potui, faciant meliora potentes
|
|
|
|
|
Sep 23 2007, 22:50
|
дятел
    
Группа: Свой
Сообщений: 1 681
Регистрация: 13-05-06
Из: Питер
Пользователь №: 17 065

|
Цитата(zltigo @ Sep 24 2007, 01:52)  Дело Ваше, можете и не по честному, мне без разницы.
Да, это норма, ибо у ARMов имеются семь тактов до начала процесса вклинивания. Прерывание возникло за 1ns до окончания команды. Прерывание возниколо через одну наносекунду после начала 4x тактовой команды. Какая разница будет? Утверждается, что где-то документировано возниковение прерывания от внутреннего таймера сразу после начала исполнения команды, нежели такое документировано, то тогда действительно для случая внутренего таймера разница будет в 3 такта. В любом другом - 4. Хм, а для АРМ это конечно все не так? да ? Бред будем нести лишь бы не оказаться не правым? ну-ну, и танк на встречу... Цитата(zltigo @ Sep 24 2007, 01:52)  после начала 4x тактовой команды. Какая разница будет? Утверждается, что где-то документировано возниковение прерывания от внутреннего таймера сразу после начала исполнения команды, нежели такое документировано, то тогда действительно для случая внутренего таймера разница будет в 3 такта. В бессысленны, рассуждения о "джиттере" из-за испо льзования C++ затеяные singlskv. Я вам советую очень сильно расслабится, я могу все команды обращения к внешним портам перевести на сттандартаные командыи доступа к портам
|
|
|
|
|
Sep 24 2007, 03:38
|

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

|
Цитата(singlskv @ Sep 24 2007, 00:20)  Ну давайте четко определимся, настоящий хак там был только в одном моменте, это несохранение SREG за ненадобностью в этом коде... Нет, хак - это рассовывание переменных уровня С в РОН процессора с использованием нестандартных, непереносимых средств компилятора, имеющее последствиями как минимум ухудшение качества оптимизации со стоторы компилятора - за этим кодом нужно постоянно следить, дает ли он дейтсвительно выигрыш, и как меняется ситуация при изменении кода - не пришла ли, скажем, пора освободить пару регистров уже для компилятора, а то он что-то медленный код стал генерить - регистров ему не хватат. Т.е. хак в данном случае - это ручная низкоуровенвая оптимизация распределения ресурсов, оптимизация на уровне асма в программе на С. Цитата(singlskv @ Sep 24 2007, 00:20)  Пример ? Что "пример"? Цитата(singlskv @ Sep 24 2007, 00:20)  Да вроде с прерываний то все и началось  Вот собственно об этом и была речь, и отсутствие С++ кода для работы в прерываниях тоже о чем то говорит, нету кода, не о чем спорить... На что вам код? Ну вот, нате, смотрите (написано было еще году в 2001-м, как только появились первые компиляторы ЕС++ от IAR для AVR): Код //------------------------------------------------------------------------------ class TUartCmd { public: TUartCmd(byte Baud_rate) { UBRR = Baud_rate; SetBits(UCR, ( (1 << RxEnable) | (1 << TxEnable)) ); enableRxInt(); Cmd = &fptrUartCmd; MaxCmdCode = 3; }
enum TBlockCode { bcMsg, bcRead, bcWrite, bcLogOp, bcUser, bcDataFlash };
static bool send(const byte* ptr, const byte count, TBlockCode block_code = bcUser); static void mgr();
private: #pragma vector = UART_UDRE_vect static __interrupt void UART_UDRE_interrupt();
#pragma vector = UART_RX_vect static __interrupt void UART_Rx_interrupt();
static __monitor byte lockUDREInt(); // { byte ucr = TUartCmd::Control_Reg; disableUDREInt(); return ucr; } static __monitor void unlockUDREInt(byte ucr); // { TUartCmd::Control_Reg = ucr; }
protected: static byte IsFrameError() { return USR & FrameError; } static byte IsOverRun() { return USR & Rx_OverRun; } static byte IsUartRxErrors() { return USR & (Rx_OverRun | FrameError); } static void resetRx();
static void msg(byte* ptr, byte count); static void read(byte* ptr, byte count); static void write(byte* ptr, byte count); static void log_op(byte* ptr, byte count);
protected: static void (*__flash(*Cmd)[])(byte* ptr, byte count); static byte MaxCmdCode;
private: static struct Flags { bool RxTimeout : 1; } status; static __flash void (*fptrUartCmd[])(byte* ptr, byte count);
static byte RxTimer; static byte RxBuf[C_BufferSize]; static byte RxIndex; static byte TxBuf[C_BufferSize]; static TCbuf Tx;
private: enum { Rx_OverRun = (1 << 0), Rx_FrameError = (1 << 1), Rx_BufOverRun = (1 << 2) };
enum UART_Status_Register_Bits { RxComplete = 7, TxComplete = 6, UDREmpty = 5, FrameError = 4, Overrun = 3 };
enum UART_Control_Register_Bits { RxCompleteIE = 7, TxCompleteIE = 6, UDREmptyIE = 5, RxEnable = 4, TxEnable = 3, Char9 = 2, RxBit8 = 1, TxBit8 = 0 };
enum TOpCodes { opcodeAND, opcodeOR, opcodeXOR }; }; //------------------------------------------------------------------------------ __interrupt void TUartCmd::UART_UDRE_interrupt() { if(TUartCmd::Tx.get_count()) { UDR = TUartCmd::Tx.get(); } else disableUDREInt(); } //------------------------------------------------------------------------------ __interrupt void TUartCmd::UART_Rx_interrupt() { byte Symbol = UDR; //__enable_interrupt();
if(IsUartRxErrors()) { resetRx(); return; }
byte t = get_system_tick(); byte dt = t - RxTimer;
if(RxIndex) { if(dt > C_MaxRxSymbolTransferTime) { resetRx(); return; } }
RxTimer = t; RxBuf[RxIndex++] = Symbol;
byte header = RxBuf[0]; byte size = (header & 0x0f) + 2;
if(size == RxIndex) { byte code = header >> 4;
RxIndex = 0; if(get_checksum(RxBuf, size)) { resetRx(); return; }
if(code == 7) code = TUartCmd::bcUser; // patch if(code > TUartCmd::MaxCmdCode) return;
(*(*TUartCmd::Cmd)[code])(RxBuf+1, size-2); } } //------------------------------------------------------------------------------ Полегчало? Код несколько обширен т.к. это рабочий пример, обработчик приемника содержит всю логику приема пакетов (прием заголовка, тела пакета, трейлера, проверка контрольной суммы и вызов соответсвующего хендлера пакета, также слежение за таймаутами). Я привел только определение класса и код обработчиков прерываний. Сам по себе класс содержит только базовый код, реально используемый класс расширяется путем наследования от этого базового класса, например, так: Код class TUartCmdUsr : public TUartCmd { public: TUartCmdUsr(byte Baud_rate) : TUartCmd(Baud_rate) { Cmd = &fptrUartCmd; MaxCmdCode = 4; } static void user(byte* ptr, byte count);
private: static __flash void (*fptrUartCmd[])(byte* ptr, byte count); }; Здесь добавлена еще одна функция-обработчик пакетов. Все это, конечно, пишется и на С, но при той же функциональности результирующий код эффективнее не будет. Не с чего ему быть эффективнее. Цитата(singlskv @ Sep 24 2007, 00:20)  Дык может и писали, тока в майнстрим исходниках такого почему-то не наблюдал, таки все таки почему??? В бытность, когда я пасся на su.c-cpp, там был один очень квалифицированный чел по С/С++, я не помню случая, чтобы он чего-то не знал до мельчайших тонкостей. Так вот он грил, что у них есть большой проект, который они ведут полностью на С исключительно из-за портабельности - не на всех платформах, которые им надо, имеется компилятор С++, либо имеющийся компилятор низкого качества. Кроме того, проект практически закончен и на тот момент просто сопровождался, поэтому переписывать работающий и отлаженный код нет ни малейшего смысла. Было это сказано где-то в районе 2000-го года. С линухом, предполагаю, ситуация примерно та же самая - когда он интенсивно разрабатывался, достойных компиляторов С++ еще не было, да и будущее С++ в те годы было еще достаточно туманным. Ну, а сегодня переписывать стабильный код ради идеи просто неразумно.
--------------------
«Отыщи всему начало, и ты многое поймёшь» К. Прутков
|
|
|
|
|
Sep 24 2007, 07:05
|

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

|
Лично я в данном моменте (обмен по RSу, например) предпочитаю использовать костыль собственного изобретения примерно такого плана: Код #include "setjmp.h"
#define CONTEXT_SWT_vect SPM_RDY_vect
#pragma vector=CONTEXT_SWT_vect __interrupt void CNTX_SWT(void);
#define IN_CSWT ACSR_ACIS1
jmp_buf main_task; jmp_buf rs_task; char rs_rstack[8]; char rs_cstack[64];
char rxbuf[16];
#pragma vector=USART0_RXC_vect __interrupt __raw void rs_rx(void) { UCSR0B_RXCIE0=0; if (!IN_CSWT) { IN_CSWT=1; __enable_interrupt(); ((void(*)(void))CNTX_SWT)(); __disable_interrupt(); IN_CSWT=0; } }
#pragma vector=USART0_UDRE_vect __interrupt __raw void rs_tx(void) { UCSR0B_UDRIE0=0; //Запретили прерывания if (!IN_CSWT) { IN_CSWT=1; __enable_interrupt(); ((void(*)(void))CNTX_SWT)(); __disable_interrupt(); IN_CSWT=0; } }
#pragma vector=USART0_TXC_vect __interrupt __raw void rs_txc(void) { //UCSR0A=1<<TXC0; //Сбросили флаг if (!IN_CSWT) { IN_CSWT=1; __enable_interrupt(); ((void(*)(void))CNTX_SWT)(); __disable_interrupt(); IN_CSWT=0; } }
#pragma vector=TIMER1_OVF_vect __interrupt __raw void rs_timeout(void) { UCSR0B_RXCIE0=0; //Запретили прерывания от приемника if (!IN_CSWT) { IN_CSWT=1; __enable_interrupt(); ((void(*)(void))CNTX_SWT)(); __disable_interrupt(); IN_CSWT=0; } }
#pragma vector=CONTEXT_SWT_vect __interrupt void CNTX_SWT(void) { if (!setjmp(main_task)) //Запомнили контекст осн. задачи { longjmp(rs_task,1); //Перешли в контекст RS_TRX } }
void wait_int() { if (!setjmp(rs_task)) //Запомнили контекст RS_TASK { longjmp(main_task,1); //Перешли в контекст осн. задачи } }
char rxbuf[16];
//txsize - количество байт без контрольной суммы __x char DO_RS(char *out, char txsize, char n_rs, char CRC8RX) { char CRC8; char c; char rxerr=0; UCSR0A=1<<TXC0; //Сбросили флаг от предыдущего прерывания CRC8=CRC8tab[c=(*out++)]; //ПУ всегда начинает с нуля __disable_interrupt(); UDR0=c; while(PINE_Bit1); //Ждем начала стартового бита if (n_rs) EnaTX2; else EnaTX0; //Разрешаем передатчик __enable_interrupt(); while(--txsize) { CRC8=CRC8tab[CRC8^(UDR0=(*out++))]; UCSR0B_UDRIE0=1; //Разрешили прерывания от UDRIE wait_int(); //Ждем освобождения буфера } UDR0=CRC8; UCSR0B_TXCIE0=1; //Разрешили прерывание от TXC wait_int(); //Байт контрольной суммы - последний байт UCSR0B_TXCIE0=0; //Запретили прерывания от TXC DisTX2; //Выключили каналы передачи DisTX0; if (n_rs) EnaRX2; else EnaRX0; //Разрешили приемник (пока только на драйвере) TCNT1=Time1000; TIFR=1<<(TOV1); TIMSK_TOIE1=1; //Разрешили прерывание от таймера wait_int(); //Ждем 1 мс пока переходные процессы UCSR0B_RXEN0=1; //Разрешили приемник //txsize здесь равен 0 CRC8RX=CRC8tab[CRC8RX]; //Нач. значение out=rxbuf; TCNT1=Time2200; //Время ожидания первого байта for(;;) { UCSR0B_RXCIE0=1; //Разрешили прерывания от приемника wait_int(); if (!UCSR0A_RXC0) { //Выход по таймауту if (CRC8RX) txsize=0; //Не срослась контрольная сумма break; //Выходим из цикла } //Приняли байт if (UCSR0A_FE0||UCSR0A_DOR0) { rxerr=1; //Ошибки } c=UDR0; if (txsize<sizeof(rxbuf)) { CRC8RX=CRC8tab[CRC8RX^(*out=c)]; // Читаем принятый байт в буфер приема и перерасчет CRC txsize++; out++; } else { rxerr=1; //Слишком много байт break; } TCNT1=Time1600; } UCSR0B_RXEN0=0; //Запретили приемник TIMSK_TOIE1=0; //Запретили таймер DisRX0; DisRX2; if (rxerr) txsize=0; return(txsize); //Количество принятых байт, если 0 - нет ответа }
__task __noreturn RS_TRX(void) { struct { char PPKP; char Cmd; }; char abon=0; char c; char CurRS=0; for(;;) { c=DO_RS(&PPKP,1,CurRS,abon); switch(c) { case 4: //Есть событие //Записываем событие в стек Event2FIFO(abon,rxbuf[1],rxbuf[2],0); case 2: //Нет события //....... break; default: //Ошибка //............. break; } if (!(abon=(abon+1)&0x7F)) { CurRS^=1; } } } void InitRS(void) { __disable_interrupt(); DDRE=0xFE; PORTE=0xFF; UBRR0L=25; UCSR0B_TXEN0=1; //Разрешили передатчик TCCR1B=2; ((unsigned int *)rs_task)[10]=((unsigned int)rs_rstack)+7; //SP ((unsigned int *)rs_task)[8]=((unsigned int)rs_cstack)+64; //Y ((unsigned int *)rs_task)[9]=(unsigned int)RS_TRX; //Адрес перехода if (!setjmp(main_task)) longjmp(rs_task,1); //Переходим в RS_TRX __enable_interrupt(); } Зато все просто - переменные вообще локальные, код простой до боли, не надо организовывать кучу обработчиков, передавать руками данные между ними (все в локальных переменных, там все автоматически получается), время работы с запрещенными прерываниями - минимально. Конечно, это занимает немного больше ручками вылизаного кода, но зато насколько все проще. Я понимаю, что мне сейчас скажут - "А почему бы ОСь не использовать". На это отвечу - ну вот это и есть своя собственная ось без кода на асме  Вот этот код (как заготовка) кочует из проекта в проект, с минимальными изменениями. Очень удобно. Теперь хотелось бы услышать от адептов C++, что мне даст применение плюсов в данном месте? Чем мне облегчит жизнь? Положить все переменные в класс? А смысл?
--------------------
"Практика выше (теоретического) познания, ибо она имеет не только достоинство всеобщности, но и непосредственной действительности." - В.И. Ленин
|
|
|
|
|
Sep 24 2007, 08:35
|
Знающий
   
Группа: Свой
Сообщений: 771
Регистрация: 16-07-07
Из: Волгодонск
Пользователь №: 29 153

|
Цитата(Rst7 @ Sep 24 2007, 11:05)  Теперь хотелось бы услышать от адептов C++, что мне даст применение плюсов в данном месте? Чем мне облегчит жизнь? Положить все переменные в класс? А смысл? Лично у меня подход примерно следующий: есть класс, назовем его TCanal. У него есть методы типа getByte и onByteRecieve, которые вызываются из прерываний УАРТа. Этот класс реализует логику посылки запроса и получения ответа (или не получения - по таймауту ожидания). Логика обработки принятых байт лежит в производном классе (она своя для каждого протокола), где подключается соответствующий обработчик. Преимущества: есть совершенно не меняющийся файл canal.h / canal.cpp, общий для всех проектов. Есть также куча не меняющихся файлов canalXXX.h / canalXXX.cpp, которые реализуют логику получения пакета для конкретного протокола. Далее для конкретного проекта просто создаются объекты нужных классов, пишутся обработчики прерываний (равно 1 строка в каждом) и ВСЕ! Никакой ручной подгонки кода! Любое количество уартов! То же самое вполне можно писать на чистом C. Примерно так: Код typedef struct _TCanalData { .......... } TCanalData;
byte canal_getByteForSend(TCanalData *pData); void canal_onByteRecieved(TCanalData *pData, byte dt); Преимущества C++ при таком подходе - код выглядит красивее. Кстати, куча функций в WinAPI (чистый C) написаны примерно в таком духе - первым аргументом идет указатель на специфическую структуру данных, а затем - собственно аргументы ф-ции.
|
|
|
|
|
Sep 24 2007, 08:51
|

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

|
Цитата(Непомнящий Евгений @ Sep 24 2007, 11:35)  Лично у меня подход примерно следующий: есть класс, назовем его TCanal. У него есть методы типа getByte и onByteRecieve, которые вызываются из прерываний УАРТа. Этот класс реализует логику посылки запроса и получения ответа (или не получения - по таймауту ожидания). Логика обработки принятых байт лежит в производном классе (она своя для каждого протокола), где подключается соответствующий обработчик. Не понятен мне смысл навешивать классы на код (аналогичный код на приеме) Код while(--txsize) { CRC8=CRC8tab[CRC8^(UDR0=(*out++))]; UCSR0B_UDRIE0=1; //Разрешили прерывания от UDRIE wait_int(); //Ждем освобождения буфера } Ради чего? Цитата Преимущества C++ при таком подходе - код выглядит красивее. Т.е. делать обработчики, передавать параметры, указатели и т.д - не важно, это делается через классы или структуры. Непонятна мне такая красота. Куда уж красивее? Цикл и все. В соответствии с идеологией цпп я должен тут обязятельно навесить кучу методов. Но зачем??? Неужели будет красивее и понятнее?
--------------------
"Практика выше (теоретического) познания, ибо она имеет не только достоинство всеобщности, но и непосредственной действительности." - В.И. Ленин
|
|
|
|
|
Sep 24 2007, 09:25
|
Знающий
   
Группа: Свой
Сообщений: 771
Регистрация: 16-07-07
Из: Волгодонск
Пользователь №: 29 153

|
Мой класс TCanal посылает запрос, ждет получения ответа определенное время, при отсутствии ответа повторяет запрос указанное кол-во раз и т.д. При этом получается отнюдь не цикл из трех строк, как вы привели...
При объектном подходе (не важно на С или на С++) мне надо сделать это ровно один раз, а потом использовать без изменений. При этом в одном проекте у меня может быть любое количество таких объектов. Если не выделить эту функциональность в отдельные ф-ции + структуру С или класс С++, то максимум - у меня будет "рыба", которую я буду копи\пастить на N проектов и в рамках проекта на M уартов - в результате получится целая куча примерно одинакового кода, при адаптации которого я всякий раз должен буду вспоминать как он работает. Еще вариант - написать по такой "рыбе" соответствующие макросы и поиметь кучу нюансов, с ними связанных. Однако макросы будут довольно сложными (если учесть требование нескольких обработчиков в рамках проекта) + это фактически "скрытая" генерация дополнительного кода. Так не проще ли использовать классы в С++ или эмулировать их структурами + функциями в С? При этом всего два недостатка: 1. Надо тщательно продумать и разработать эту общую функциональность 2. Некоторая потеря в скорости по сравнению с копи\пастом \ макросами. Однако если учесть, что скорость по УАРТу обычно не слишком высокая, то этими потерями в десятков\сотен тактов вполне можно пренебречь.
|
|
|
|
|
Sep 24 2007, 09:39
|

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

|
Цитата(Непомнящий Евгений @ Sep 24 2007, 12:25)  Мой класс TCanal посылает запрос, ждет получения ответа определенное время, при отсутствии ответа повторяет запрос указанное кол-во раз и т.д. При этом получается отнюдь не цикл из трех строк, как вы привели... Из пяти. Во внешней функции, напрмер так (хотя у меня совсем по другому, там не сразу повторяется запрос, а на следующем круге) Код char r=3; do { c=DO_RS(&PPKP,1,CurRS,abon); if ((c!=4)||(c!=2)) break; //Ну или другое условие проверим } while(--c); Или тут тоже проще написать кучу методов, повызывать их? Или все-таки почеловечески, пробуем 3 раза, если обмен свершился, прекращаем попытки? Цитата При объектном подходе (не важно на С или на С++) мне надо сделать это ровно один раз, а потом использовать без изменений. При этом в одном проекте у меня может быть любое количество таких объектов. Уменьшение писанины. С этим согласен. Цитата Если не выделить эту функциональность в отдельные ф-ции + структуру С или класс С++, то максимум - у меня будет "рыба", которую я буду копи\пастить на N проектов и в рамках проекта на M уартов - в результате получится целая куча примерно одинакового кода, при адаптации которого я всякий раз должен буду вспоминать как он работает. Мой опыт говорит, что всегда надо что-то точить. Не получается универсальности. Цитата Еще вариант - написать по такой "рыбе" соответствующие макросы и поиметь кучу нюансов, с ними связанных. Однако макросы будут довольно сложными (если учесть требование нескольких обработчиков в рамках проекта) + это фактически "скрытая" генерация дополнительного кода. Так не проще ли использовать классы в С++ или эмулировать их структурами + функциями в С? Не доходит до меня. У меня есть код (приведенный выше), который я использую (конечно, внося изменения) во всех проектах. Что мне даст изготовление этого кода в соответствии с парадигмами цпп? Цитата При этом всего два недостатка: 1. Надо тщательно продумать и разработать эту общую функциональность На Си тоже надо. Не думаете ли вы, что я не думаю над своим софтом? Так что это не недостаток. Цитата 2. Некоторая потеря в скорости по сравнению с копи\пастом \ макросами. Однако если учесть, что скорость по УАРТу обычно не слишком высокая, то этими потерями в десятков\сотен тактов вполне можно пренебречь. Далеко не всегда.
--------------------
"Практика выше (теоретического) познания, ибо она имеет не только достоинство всеобщности, но и непосредственной действительности." - В.И. Ленин
|
|
|
|
|
Sep 24 2007, 09:51
|
Знающий
   
Группа: Свой
Сообщений: 771
Регистрация: 16-07-07
Из: Волгодонск
Пользователь №: 29 153

|
Вдогонку Цитата(Rst7 @ Sep 24 2007, 12:51)  В соответствии с идеологией цпп я должен тут обязятельно навесить кучу методов. Но зачем??? Неужели будет красивее и понятнее? 1. В соответствии с идеологией не цпп, а ООП. Это совершенно разные понятия  2. Нужен баланс - каждый класс должен иметь конкретную, вполне определенную функциональность. Если эта функциональность мизерна - можно удалить класс, перенеся его функциональность в тем места, где он использовался. Если класс имеет очень большую функциональность - хорошо бы его разбить на несколько более мелких. Цель - сделать программу понятнее, ее куски - независимыми от других (по возможности) и повторно используемыми. Это вполне работает и в чистом С - иметь миллион мелких функций плохо, равно как и иметь несколько гигантских...
|
|
|
|
|
Sep 24 2007, 10:14
|

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

|
Цитата(Непомнящий Евгений @ Sep 24 2007, 12:51)  Вдогонку 1. В соответствии с идеологией не цпп, а ООП. Это совершенно разные понятия  Безусловно. Я, конечно, имел в виду ООП. С++ как среду реализации идей ООП. Цитата 2. Нужен баланс - каждый класс должен иметь конкретную, вполне определенную функциональность. Если эта функциональность мизерна - можно удалить класс, перенеся его функциональность в тем места, где он использовался. Если класс имеет очень большую функциональность - хорошо бы его разбить на несколько более мелких. Цель - сделать программу понятнее, ее куски - независимыми от других (по возможности) и повторно используемыми. Это вполне работает и в чистом С - иметь миллион мелких функций плохо, равно как и иметь несколько гигантских... Программа понятна. Читабельна. Обозрима. Какой плюс мне даст переделка этого кода в соответствии с ООП? Как бы вы порекомендовали разложить это по классам/методам? Какой выигрыш я получу?
--------------------
"Практика выше (теоретического) познания, ибо она имеет не только достоинство всеобщности, но и непосредственной действительности." - В.И. Ленин
|
|
|
|
|
Sep 24 2007, 10:38
|
Знающий
   
Группа: Свой
Сообщений: 771
Регистрация: 16-07-07
Из: Волгодонск
Пользователь №: 29 153

|
Цитата(Rst7 @ Sep 24 2007, 13:39)  Или тут тоже проще написать кучу методов, повызывать их? Или все-таки почеловечески, пробуем 3 раза, если обмен свершился, прекращаем попытки? Еще раз  . У меня есть объект, который умеет передать запрос, получить ответ, повторить передачу при необходимости, сообщить об отсутствии ответа. Чтобы его использовать в конкретном проекте, я пишу обработчики 3-х прерываний примерно по 3 строки в каждом + пару 3-х строчных функций, которые в(ы)ключают приемник\передатчик УАРТа. Еще нужна ф-ция, которая выставляет флаг получения ответа - для его последующей обработки. Класс TCanal (а точнее производные от него) используется в других классах, которые содержит логику управления внешними устройствами. Преимущества моего TCanal перед несколькими вашими функциями: 1. Легко тиражировать - если у меня в устройстве 4 уарта, я просто создаю четыре объекта + пишу обвязку для каждого (элементарную). Для ваших функций мне потребуется руками растиражировать каждую используемую переменную (а это как минимум, указатель на буфер, длина, число повторов максимальное и текущее, время ожидания и т.д.) и каждую функцию. 2. Мне не надо вообще ничего менять в базовом классе при написании нового протокола. Мне надо просто написать производный класс, в котором будет подключаться обработчик полученных байт + сам этот обработчик. 3. У меня логика посылки\повтора посылки отделена от логики "выцепления" пакета из потока байт (начало\конец пакета, длина, crc и т.д.). У вас - нет. Если в новом протоколе формат пакетов будет другой, в вашем случае придется по живому резать весь код; в моем - просто написать новый обработчик. 4. Параметры - время ожидания, число повторов и т.д. задаются в одном месте. Их четко видно в коде и легко поменять на другие. У вас они вшиты в ваши функции. Цитата Мой опыт говорит, что всегда надо что-то точить. Не получается универсальности. Если написать грамотно (и потом раз несколько повторно переписать  ), точить придется в производном классе, не меняя код в базовом. А возможно - вообще не придется, если предусмотреть соответствующие параметры. Цитата Не доходит до меня. У меня есть код (приведенный выше), который я использую (конечно, внося изменения) во всех проектах. Что мне даст изготовление этого кода в соответствии с парадигмами цпп? см выше. Цитата На Си тоже надо. Не думаете ли вы, что я не думаю над своим софтом? Так что это не недостаток. Думаете конечно. Но написать общий код обычно сложнее, чем для конкретной задачи. Цитата Далеко не всегда. Если критично быстродействие - есть встраивание ф-ций, шаблоны и т.д. Цитата(Rst7 @ Sep 24 2007, 14:14)  Программа понятна. Читабельна. Обозрима. Какой плюс мне даст переделка этого кода в соответствии с ООП? Как бы вы порекомендовали разложить это по классам/методам? Какой выигрыш я получу? Ну вопрос о переделке существующего кода - это несколько другой вопрос. Если он уже есть и работает, а также читабелен и обозрим, то никакого смысла переписывать скорее всего нету. Если он постоянно меняется и растет - то можно очень постепенно в нем что-то менять (целые книжки умные люди по рефакторингу пишут  ). Лично мне удобнее организовывать код как несколько кирпичиков, которые можно складывать друг с другом в разных вариантах без переделки самих кирпичей, а только за счет "разного цемента"  . Т.е., я написал такой кирпичик, отладил его в рамках какого-нибудь проекта, а затем без изменений (вообще!) использую в другом. За счет ООП я могу, имея один кирпич, несколькими строками кода наклепать несколько подобных. Постепенно я улучшаю кирпичи, при этом они автоматически улучшаются и в старых проектах после перекомпиляции (или старые проекты становятся полностью неработоспособными  , но это уже проблема осторожного улучшения и использования системы контроля версий).
|
|
|
|
|
  |
2 чел. читают эту тему (гостей: 2, скрытых пользователей: 0)
Пользователей: 0
|
|
|