Например TDA7294

Форум РадиоКот • Просмотр темы - C для AVR -- пишем аккуратно.
Форум РадиоКот
Здесь можно немножко помяукать :)





Текущее время: Вт апр 23, 2024 09:22:49

Часовой пояс: UTC + 3 часа


ПРЯМО СЕЙЧАС:



Начать новую тему Ответить на тему  [ Сообщений: 112 ]  1, , , , ,  
Автор Сообщение
Не в сети
 Заголовок сообщения: C для AVR -- пишем аккуратно.
СообщениеДобавлено: Пт авг 24, 2012 13:35:36 
Опытный кот
Аватар пользователя

Карма: 7
Рейтинг сообщений: 52
Зарегистрирован: Чт дек 31, 2009 19:27:45
Сообщений: 842
Откуда: Бровари, Україна
Рейтинг сообщения: 2
Выделено из темы про ассемблер AVR. Начало тут.
Цитаты оттуда:
shads писал(а):
Сообразил тут декодер радиоканала на тиньке 13-й, в двух вариантах, на С и на асме, так вот мож кому интересно будет узнать, разница в весе кода - больше чем в 2 раза, на С - 870 байт, а на асме тоже самое - 420 байт.....
Помоему асма остается актуальной для таких мелкашек как tiny13
http://asis-kbr.ru/forum/viewtopic.php?f=9&t=122

avreal писал(а):
Я даже больше скажу -- если написать на асме программу размером байт десять, то С-шный вариант проиграет раз в пять-восемь, так как С (по крайней мере WinAVR) и всю таблицу прерываний заполняет переходом на ловушку необработанных прерываний, и прочие «общеподготовительные» вещи могут место занять. Т.е. у С-шной программы даже без желания автора обычно несколько другой функционал.
...
Увидел всё по линку, гляну по свободе. На носу куча выходных :-)

Ну вот и глянул.

Табличка результатов на разных версиях avr-gcc.
orig -- код по ссылке
patch -- то, что я с ним сделал.
Ключи компиляции везде одинаковые, взяты из стандартного проекта. Для 4.7.1 ещё добавлялся ключ -fno-ivopts, чтобы он менше чудил с оптимизацией induction variables в циклах. Они там что-то в «корневом» gcc похимичили, для процессоров с развитыми адресациями точно стало лучше, для AVR компилятор иногда слишком «мудрит» и сам себя перехитрить умудряется.
Код:
version  orig    patch   avr-gcc build

3.4.6    884     702     WinAVR-20060421
4.2.2    874     678     WinAVR-20071221
4.3.3    872     658     WinAVR-20100110
4.7.1    844     674     MobileChessBoard 4.7.1rc1 (-fno-ivopts -> 668)

4.3.4    894     680     Ubuntu-10.04
4.5.3    860     660     Ubuntu-12.04
«MobileChessBoard» -- это такие сборки свежего avr-gcc для Windows.

Т.е. проигрыш у ассемблера уже не два, а полтора раза (675 / 450) :-)

Но сейчас я не о том, насколько проигрывает C ассемблеру на проектах такого размера, а о том, что можно сделать с нормальным в принципе С-шным кодом для уменьшения размера прошивки байт так с 870-ти до байт так 670-ти (больше, чем на четверть).

Причём смотрел «обще-микроконтроллерно-С-шные» вещи, там осталась одна большая беда именно avr-gcc, IAR должен бы ещё короче сделать на несколько десятков байт, но мне лень проверять.

Пришиваю новый исходник, надеюсь, оно будет работать :-)
Немного позже (сейчас гости пришли) напишу немного по принципы — «общие» и что там осталось специфического для avr-gcc


Вложения:
receiver_433-win1251.c [8.86 KiB]
Скачиваний: 730

_________________
Лень в виде мании величия: «ты гений, зачем стараться?». В виде комплекса: «всё равно не выйдет, зачем упираться?». Как логика: «если достаточно, зачем знать и уметь больше?». Цель одна: остановить. Не любит тепло работающих мышц и шум работающего мозга.
Вернуться наверх
 
Не в сети
 Заголовок сообщения: Re: C для AVR -- пишем аккуратно.
СообщениеДобавлено: Пт авг 24, 2012 15:46:05 
Опытный кот
Аватар пользователя

Карма: 8
Рейтинг сообщений: 105
Зарегистрирован: Ср фев 22, 2012 01:25:21
Сообщений: 882
Рейтинг сообщения: 0
Гы..... С трудом узнал свою программу.....
Даже не верилось что она заработает, НО..... она работает.....
Спасибо за уделенное внимание. Хотя для понимания она стала сложнее (по крайней мере для меня, т.к. я тока начал писать на С), но надо будет обязательно разобраться, чего это вы там такого понаписали :)


Вернуться наверх
 
Не в сети
 Заголовок сообщения: Re: C для AVR -- пишем аккуратно.
СообщениеДобавлено: Пт авг 24, 2012 15:52:32 
Друг Кота
Аватар пользователя

Карма: 93
Рейтинг сообщений: 1351
Зарегистрирован: Вт мар 16, 2010 22:02:27
Сообщений: 14062
Откуда: ДОНЕЦК
Рейтинг сообщения: 0
однако в конце-концов всеравно все сводится к машинным кодам :)
корректность программы на С во многом зависит от качества компилятора (и знаний правильных приемов работы с оным), а в ассемблере львиная доля - знание аппаратной структуры и схемотехники устройств
в идеале - знание того и другого (да еще минимум на всех базовых семействах)
из С более перспективным выглядит микроси - единый подход ко всем 4-м "ходовым" семействам.... но... полные версии уж "оччень платные" :cry:


Вернуться наверх
 
PCBWay - всего $5 за 10 печатных плат, первый заказ для новых клиентов БЕСПЛАТЕН

Сборка печатных плат от $30 + БЕСПЛАТНАЯ доставка по всему миру + трафарет

Онлайн просмотровщик Gerber-файлов от PCBWay + Услуги 3D печати
Не в сети
 Заголовок сообщения: Re: C для AVR -- пишем аккуратно.
СообщениеДобавлено: Пт авг 24, 2012 19:49:31 
Друг Кота
Аватар пользователя

Карма: 74
Рейтинг сообщений: 1244
Зарегистрирован: Вс мар 29, 2009 22:09:05
Сообщений: 7517
Рейтинг сообщения: 0
Я, кстати, достаточно подробно разбирал, чего генерирует AVR-GCC в нагрузку к основному коду.

_________________
Разница между теорией и практикой на практике гораздо больше, чем в теории.


Вернуться наверх
 
Выбираем схему BMS для заряда литий-железофосфатных (LiFePO4) аккумуляторов

Обязательным условием долгой и стабильной работы Li-FePO4-аккумуляторов, в том числе и производства EVE Energy, является применение специализированных BMS-микросхем. Литий-железофосфатные АКБ отличаются такими характеристиками, как высокая многократность циклов заряда-разряда, безопасность, возможность быстрой зарядки, устойчивость к буферному режиму работы и приемлемая стоимость. Но для этих АКБ очень важен контроль процесса заряда и разряда для избегания воздействия внешнего зарядного напряжения после достижения 100% заряда. Инженеры КОМПЭЛ подготовили список таких решений от разных производителей.

Подробнее>>
Не в сети
 Заголовок сообщения: Re: C для AVR -- пишем аккуратно.
СообщениеДобавлено: Пт авг 24, 2012 20:05:55 
Опытный кот
Аватар пользователя

Карма: 7
Рейтинг сообщений: 52
Зарегистрирован: Чт дек 31, 2009 19:27:45
Сообщений: 842
Откуда: Бровари, Україна
Рейтинг сообщения: 0
shads писал(а):
Гы..... С трудом узнал свою программу.....
Так я же в ней ничего принципиального не менял :-)
Так, точечно, «тактически»., а «стратегически» все осталось как было.

Итак, пошли сверху вниз по тексту.
Код:
#define INLINE   inline __attribute__((__always_inline__))
#define NOINLINE __attribute__((__noinline__))
GCC-шные штуки, эквивалентные IAR-овским #pragma inline и т.п. Команды компилятору принудительно «инлайнить» (встраивать по месту как макросы) и ни в коем случае не инлайнить определённые функции. Будут использованы ниже.

Код:
//прототипы функций
static void Receive(void);
static void Int100Hz(void);
Каждая функция используются только в данном файле, поэтому её стоит объявить как static. При этом компилятор знает, что снаружи она не может быть вызвана и поступает с ней по собственному усмотрению. Если функция вызывается из одного места, то он практически гарантированно её встроит по месту, а не будет вызывать. Если функция вызывается из нескольких мест, но она короткая — тоже может встроить.
При встраивании единожды вызываемой функции экономи размера кода небольшая, но есть.
Для небольшой функции без static может быть вообще плохо — он её и по месту встроит (маленькая, экономия на саом вызове и на стандартной передаче параметров может быть больше размера функции), но и отдельно тело тоже оставит. Так как при компиляции из .c в .o компилятор не знает, что файл один, оставляет функцию на случай, если ее кто-то снаружи вызовет. Т.е. функция займёт место во флеше дважды.
Кроме того, static-объекты не захламляют глобальное пространство имён, не занимают место (и время) в соответствующих таблицах при работе компилятора и линкера.
Объявлять как static все функции и переменные, которые не используются за пределами файла — «правило хорошего тона».

Код:
enum { DW_B0 = 0, DW_B1 = 1, DW_B2 = 2, DW_B3 = 3 };  // LITTLE ENDIAN CPU
// enum { DW_B0 = 3, DW_B1 = 2, DW_B2 = 1, DW_B3 = 0 }; // BIG ENDIAN CPU
union {
    uint32_t dw;
    uint8_t  b[4];
} ReceiveData;      //4 байта для чтения кода с радиоканала
Я уже об этом писал, для приёма битового потока удобно объявить объединение, т.е. наложение в памяти двух переменных: 32-битного целого и 4-бійтового массива. Слдвиг делается в 32-битовое поле dw, а разбор потом — из байтового массива.
Перед ним «для порядку» объявлено перечислимый тип, который описывает порядок байтов в 32-битном слове. Надо было бы ещё и #ifdef LITTLE_ENDIAN или там #if BYTE_ORDER == LITTLE_ENDIAN, но тут я так, для демонстрации того, что такую вещь можно при желании сделать переносимой и она скомпилируется для любого процессора.
Это место сэкономило довольно много кода — при записи в EEPROM и при сравнивании с таблицей кодов пропали >> 8, >> 16, которые хоть и делаются пересылкой байтов, но все же…
Было:
Код:
   eeprom_write_byte (&EEData[EEPntTmp +0], (unsigned char) (ReceiveData >> 0));
   eeprom_write_byte (&EEData[EEPntTmp +1], (unsigned char) (ReceiveData >> 8));
   eeprom_write_byte (&EEData[EEPntTmp +2], (unsigned char) (ReceiveData >> 16UL));
Стало:
Код:
            EEWrite(ee_addr,     ReceiveData.b[DW_B0]);
            EEWrite(++ee_addr,   ReceiveData.b[DW_B1]);
            EEWrite(++ee_addr,   ReceiveData.b[DW_B2]);
(а временная переменная ee_addr практически только для удобства записи, на размер кода не влияет).

Код:
static NOINLINE void delay(uint8_t ticks10ms)
{
   while(ticks10ms--) _delay_ms(10);
}
В ассемблерном варианте для задержки везде вызівается функция с параметром времени в 10мс квантах. В С-шном везде было _delay_ms(). Но это макрос, который по месту вставляет все те вложенные циклы, при многократном применении флеш разлетается ужасающим образом. Это для малых, микросекундных задержек там три команды выходит, не так и жалко.
Но вот тут понадобилось NOINLINE, так как функция короткая и некоторые из оттестированных версий компиляторов норовили вставить тело по месту.
У меня NOINLINE и компания вместе с уже упоминавшимися INIT() сидят в gcc_macros.h и я для таких небольших функций со специально вынесенным отдельно кодом ставлю практически рефлекторно.


Пойду попью чаю…

_________________
Лень в виде мании величия: «ты гений, зачем стараться?». В виде комплекса: «всё равно не выйдет, зачем упираться?». Как логика: «если достаточно, зачем знать и уметь больше?». Цель одна: остановить. Не любит тепло работающих мышц и шум работающего мозга.


Последний раз редактировалось avreal Пт авг 24, 2012 20:26:42, всего редактировалось 1 раз.

Вернуться наверх
 
Новый аккумулятор EVE серии PLM для GSM-трекеров, работающих в жёстких условиях (до -40°С)

Компания EVE выпустила новый аккумулятор серии PLM, сочетающий в себе высокую безопасность, длительный срок службы, широкий температурный диапазон и высокую токоотдачу даже при отрицательной температуре. Эти аккумуляторы поддерживают заряд при температуре от -40/-20°С (сниженным значением тока), безопасны (не воспламеняются и не взрываются) при механическом повреждении (протыкание и сдавливание), устойчивы к вибрации. Они могут применяться как для автотранспорта (трекеры, маячки, сигнализация), так и для промышленных устройств мониторинга, IoT-устройств.

Подробнее>>
Не в сети
 Заголовок сообщения: Re: C для AVR -- пишем аккуратно.
СообщениеДобавлено: Пт авг 24, 2012 20:23:50 
Опытный кот
Аватар пользователя

Карма: 7
Рейтинг сообщений: 52
Зарегистрирован: Чт дек 31, 2009 19:27:45
Сообщений: 842
Откуда: Бровари, Україна
Рейтинг сообщения: 0
YS писал(а):
Я, кстати, достаточно подробно разбирал, чего генерирует AVR-GCC в нагрузку к основному коду.
О, значит про это можно не писать :-)
Только добавлю (недавно писал на этом форуме), что обработчик незадействованого прерывания можно определить в своей программе, он заменит тот по умолчанию переход на старт.

_________________
Лень в виде мании величия: «ты гений, зачем стараться?». В виде комплекса: «всё равно не выйдет, зачем упираться?». Как логика: «если достаточно, зачем знать и уметь больше?». Цель одна: остановить. Не любит тепло работающих мышц и шум работающего мозга.


Вернуться наверх
 
Не в сети
 Заголовок сообщения: Re: C для AVR -- пишем аккуратно.
СообщениеДобавлено: Пт авг 24, 2012 20:34:21 
Друг Кота
Аватар пользователя

Карма: 74
Рейтинг сообщений: 1244
Зарегистрирован: Вс мар 29, 2009 22:09:05
Сообщений: 7517
Рейтинг сообщения: 0
Цитата:
обработчик незадействованого прерывания можно определить в своей программе


Ага. BADISR_vect его вектор.

#pragma offtopic

А AVReal еще не поддерживает FTBB?

_________________
Разница между теорией и практикой на практике гораздо больше, чем в теории.


Вернуться наверх
 
Не в сети
 Заголовок сообщения: Re: C для AVR -- пишем аккуратно.
СообщениеДобавлено: Пт авг 24, 2012 20:50:21 
Опытный кот
Аватар пользователя

Карма: 8
Рейтинг сообщений: 105
Зарегистрирован: Ср фев 22, 2012 01:25:21
Сообщений: 882
Рейтинг сообщения: 0
Ооо.... Спасибо avreal, многое постараюсь применить, я тут как раз одну страшную весч затеял, думаю в итоге под 8кб коду будет (mega8), так что надо будет экономить. Пока только начало, нашкрябал 1,5кб.....
Спойлер
Код:
//#######################################################################################################################
//#######################################################################################################################
//ВКЛЮЧАЕМЫЕ ФАЙЛЫ
#define F_CPU 8000000
#include <util/delay.h>
#include <avr/io.h>
#include <avr/interrupt.h>
#include <avr/eeprom.h>                   //eeprom_read_byte(), eeprom_write_byte(), eeprom_read_word(), eeprom_write_word()

//ДЕФАЙНЫ
#define CtrlPortOut PORTC                  /*порт светодиода для отладки*/
#define CtrlPortIn PINC
#define CtrlPin (1<<0)                     /*светодиод для отладки*/

#define DisplayType 255                     /*0 - дисплей с OK, 255 - дисплей с OA*/
#define DispDataPort PORTB                  /*порт данных дисплея*/
#define DispStrobePort PORTD               /*порт стробов дисплея*/
#define DispStrb0 (1<<4)                  /*линия строба 0*/
#define DispStrb1 (1<<5)                  /*линия строба 1*/
#define DispStrb2 (1<<6)                  /*линия строба 2*/
#define DispStrb3 (1<<7)                  /*линия строба 3*/

#define BtnPort PINC                     /*порт чтения кнопок*/
#define BtnPin (1<<2)                     /*пин чтения кнопок*/

#define BuzPort   PORTC                     /*порт баззера*/
#define BuzPin   (1<<3)                     /*пин баззера*/

#define LPortOut PORTC                     /*порт записи линии 1,2*/
#define LPortIn   PINC                     /*порт чтения линии 1,2*/
#define LPin1 (1<<0)                     /*пин линии 1*/
#define LPin2 (1<<1)                     /*пин линии 2*/

#define LinePortOut PORTD                  /*порт записи в линии*/
#define LinePinOut (1<<3)                  /*пин записи в линию*/
#define LinePortIn PIND                     /*порт чтения с линии*/
#define LinePinIn (1<<2)                  /*пин чтения с линии*/

//ГЛОБАЛЬНЫЕ ПЕРЕМЕННЫЕ
volatile char volatilei;                  /*вставка для борьбы с оптимизацией*/

unsigned char Flags1;                     /*байт флагов №1*/
#define b1DispFlash (1<<0)                  /*бит мигающего режима дисплея*/

unsigned char ButtonFlags;
unsigned char *pButtonFlags = &ButtonFlags;      /*АДРЕС БАЙТА ФЛАГОВ НАЖАТЫХ КЛАВИШ*/
#define ShortLeft   (1<<0)                  /*бит короткого нажатия кнопки вверх*/
#define ShortDown   (1<<1)                  /*бит короткого нажатия кнопки вниз*/
#define ShortRight   (1<<2)                  /*бит короткого нажатия кнопки влево*/
#define ShortUp      (1<<3)                  /*бит короткого нажатия кнопки вправо*/
#define LongLeft   (1<<4)                  /*бит длиного нажатия кнопки вверх*/
#define LongDown   (1<<5)                  /*бит длиного нажатия кнопки вниз*/
#define LongRight   (1<<6)                  /*бит длиного нажатия кнопки влево*/
#define LongUp      (1<<7)                  /*бит длиного нажатия кнопки вправо*/

unsigned char DispFlash;                  /*переменная режима индикатора FLASH (счет 0-99 за секунду)*/

unsigned char DispData [] =    {0,0,0,0};      /*БУФЕР ВЫВОДА ДАННЫХ НА ДИСПЛЕЙ*/
#define BitPoint (1<<3)                     /*бит точки дисплея*/
const unsigned char SimbMass [] =             /*символьный массив (не используются K,M,V,W,X,Z)*/
      {0xf5,0x05,0x73,0x57,0x87,            /*01234*/
      0xd6,0xf6,0x45,0xf7,0xd7,            /*56789*/
      0x00,0xe7,0xb6,0xf0,0x37,            /* ABCD*/
      0xf2,0xe2,0xf4,0xa7,0x05,            /*EFGHI*/
      0x35,0x00,0xb0,0x00,0x26,            /*JKLMN*/
      0x36,0xe3,0xc7,0x22,0xd6,            /*OPQRS*/
      0xb2,0x34,0x00,0x00,0x00,            /*TUVWX*/
      0x97,0x00,0xa0,0x23,0x05,            /*YZ[/]*/
      0x02,0x10};                        /*-_   */   

unsigned char TxtSure [] = {"sure"};                  /*МАССИВ СООБЩЕНИЙ ДЛЯ ВЫВОДА НА ДИСПЛЕЙ*/
unsigned char TxtDone [] = {"done"};
unsigned char TxtStup [] = {"stup"};
unsigned char TxtDflt [] = {"dflt"};
unsigned char TxtRet  [] = {" ret"};
unsigned char TxtSpace[] = {"    "};
unsigned char TxtHallo[] = {"^^^^"};
unsigned char TxtUp   [] = {"  up"};
unsigned char TxtDown [] = {"  dn"};
unsigned char TxtLeft [] = {"left"};
unsigned char TxtRight[] = {"rght"};
unsigned char TxtLU   [] = {"  lu"};
unsigned char TxtLD   [] = {"  ld"};
unsigned char TxtLL   [] = {"  ll"};
unsigned char TxtLR   [] = {"  lr"};


//#define EEDataLen 60                     /*длина буфера данных в EEPROM*/
//unsigned char EEData[EEDataLen] EEMEM;      //массив данных в EEPROM
//unsigned char EEPnt EEMEM;                //указатель на байт массива в EEPROM (0-59)   

//прототипы функций   
void DispFresh (void);
void DispSimbShow (unsigned char, unsigned char);
void DispMessageShow (unsigned char*);
void DispIntShow (unsigned int);
void DispIntEdit (unsigned int*, unsigned int, unsigned int);
void ButtonGet (unsigned char);
unsigned char div (unsigned int* , unsigned int);
unsigned char MenuBtnPressWait (unsigned char*, unsigned char*);
unsigned char BtnPressWait1Sec (void);












































//#######################################################################################################################
//#######################################################################################################################
//Главная функция
int main (void)
{
   DDRB  = 0xff;                        //линии порта на вывод (данные дисплея)
   PORTB = 0;                           //подтяжки нет

   DDRC  = (BuzPin|CtrlPin);               //линия порта на вывод для buzzer
   PORTC = (BtnPin & (~DisplayType));         //подтяжка для кнопки с учетом типа дисплея

   DDRD  = (DispStrb0|DispStrb1|DispStrb2|DispStrb3|LinePinOut); //линий порта на вывод                           
   PORTD = 0;                           //подтяжки нет

   TIMSK = 0x80;                         //режим работы - прерывание по совпадению
   TCCR2 = 0x0e;                        //режим таймера T2 = CTC (0x08 сброс при совпадении)(0x06 предделитель на 256)
   OCR2  = 77;                           //регистр сравнения (сброс таймера счетч по совп)

   sei ();                              //разрешаем глобально прерывания

   unsigned int integer = 500;
   DispMessageShow (TxtHallo);

   while(1)                           //бесконечный цикл   
   {
      //volatilei ++;

      unsigned char BtnValue = BtnPressWait1Sec (); //ждем нажатия кнопки

      if   ( BtnValue == ShortUp)
         DispMessageShow (TxtUp);

      if   ( BtnValue == ShortDown)
         DispMessageShow (TxtDown);

      if   ( BtnValue == LongRight)
      {
         DispIntEdit (&integer, 0, 9999);            
         DispMessageShow (TxtHallo);   
      }
   }
}

//----------
//функция вывода сообщения на дисплей
void DispMessageShow (unsigned char *pAdrTxt)
{
   for (unsigned char position =0; position<4; position++)
      DispSimbShow (*pAdrTxt++,position);
}

//----------
//функция вывода символа на дисплей
//аргумент 1 - символ "0-9,A-Z"
//аргумент 2 - знакоместо (0-3)

void DispSimbShow (unsigned char simbol, unsigned char position)
{
   unsigned char tmp =10;

   if (simbol >= 96)
      simbol -= 32;                     //преобразуем знаки a-z в знаки A-Z
   
   if (simbol < 10)                  
      tmp = simbol;                     //значения 0-9
   
   if ((simbol >= 48)&&(simbol < 58))
      tmp = (simbol -48);                  //символы 0-9
   
   if ((simbol >= 64)&&(simbol < 96))
      tmp = (simbol -64 +10);               //символы A-Z

   DispData[position] = SimbMass[tmp];
}

//----------
//функция вывода на дисплей числа 0-9999

void DispIntShow (unsigned int integer)
{
   unsigned char position = 0;               //позиция печати на дисплее

   if (integer < 1000)
      DispData [position ++] = 0;            //очистить тысячи
   if (integer < 100)
      DispData [position ++] = 0;            //очистить сотни
   if (integer < 10)
      DispData [position ++] = 0;            //очистить десятки

   switch (position)
   {
      case 0: DispSimbShow (div (&integer, 1000), position ++);
      case 1: DispSimbShow (div (&integer, 100), position ++);
      case 2: DispSimbShow (div (&integer, 10), position ++);
      case 3: DispSimbShow (integer, position);
   }
}

//ф-ция деления возвращает результат деления
//аргумент 1 - делимое (указатель)
//аргумент 2 - делитель

unsigned char div (unsigned int *pDivident, unsigned int divisor)
{
   unsigned char result =0;
   while (*pDivident >= divisor)
   {
      *pDivident -= divisor;
      result ++;
   }
   return result;
}

//----------
//функция редактирования числа
//аргумент 1 - указатель на редактируемые данные
//аргумент 2 - максимально возможно нижнее значение
//аргумент 3 - максимально возможное верхнее значение

void DispIntEdit (unsigned int* pInteger, unsigned int IntMin, unsigned int IntMax)
{
   unsigned int IntegerTemp = *pInteger;      //редактируем пока промежуточную IntegerTemp
   
   while (1)
   {
      Flags1 |= b1DispFlash;               //включить режим дисплея - FLASH
      DispIntShow (IntegerTemp);            //вывести значение на дисплей
      unsigned char BtnValue = MenuBtnPressWait (0, 0);//ожидаем нажатия кнопки
      Flags1 &= ~b1DispFlash;               //выключить режим дисплея - FLASH       

      if (BtnValue == LongRight){
         *pInteger = IntegerTemp;         //выход с сохранением данных
         DispMessageShow (TxtDone);
         _delay_ms(1500);
         return;                        
      }

      if ((!BtnValue)||(BtnValue == LongLeft)){
         DispMessageShow (TxtRet);         //выход кнопкой без сохранения и по таймауту
         _delay_ms(1500);   
         return;                        
      }

      if (BtnValue == ShortUp){
         if (IntegerTemp < IntMax)         //увеличить значение
            IntegerTemp ++;
      }

      if (BtnValue == ShortDown){
         if (IntegerTemp > IntMin)         //уменьшить значение
            IntegerTemp --;
      }

      if (BtnValue == LongUp){
         if (IntegerTemp < IntMax)         //увеличить значение
            IntegerTemp ++;
      }

      if (BtnValue == LongDown){
         if (IntegerTemp > IntMin)         //уменьшить значение
            IntegerTemp --;
      }
   }
}

//----------
//функция индикации строк меню, и ожидание нажатия кнопки
//аргумент 1 - строка меню №1 (если 0 - не выводится)
//аргумент 2 - строка меню №2 (если 0 - не выводится)
//значение на выходе - байт маска нажатой кнопки (если 0 - выход по таймауту)

unsigned char MenuBtnPressWait (unsigned char *pMenu1, unsigned char *pMenu2)
{
   #define TimeOut 10                     /*если указанное кол-во секунд, кнопка не нажимается, то выход из ф-ции*/
   unsigned char Button;                  //регистр маски нажатой кнопки

   for (unsigned char i =0; i<(TimeOut/2); i++)         
   {
      if (pMenu1)
         DispMessageShow (pMenu1);         //выводим строку 1
      Button = BtnPressWait1Sec();
      if (Button)
         return Button;                  //кнопка нажата, возвращаем маску кнопок

      if (pMenu2)
         DispMessageShow (pMenu2);         //выводим строку 2
      Button = BtnPressWait1Sec();
      if (Button)
         return Button;                  //кнопка нажата, возвращаем маску кнопок
   }
   return 0;                            //выход по таймауту
}

//----------
//функция ожидания нажатия кнопки в течение 1 сек
//значение на выходе - маска кнопок *pButtonFlags (если timeout, то на выходе 0)
unsigned char BtnPressWait1Sec (void)
{
   for (unsigned char i=0; i<10; i++)         //крутимся 10 раз по 100мс т.е. 1 сек
   {   
      volatilei ++;   
      if (*pButtonFlags)
      {
         unsigned char temp = *pButtonFlags;   //кнопка нажата
         *pButtonFlags = 0;
         return temp;                  //возвращаем маску нажатой кнопки
      }
      _delay_ms(100);
   }
   return 0;                           //секунда закончилась, возвращаем значение 0
}









































//#######################################################################################################################
//#######################################################################################################################
//обработка прерывания (частота вызова 400 Гц)
ISR (TIMER2_COMP_vect)
{
   DispFresh ();                     //динамическое обновление дисплея
}

//----------
//функция динамического отображения данных на 4-х разрядном дисплее.
//
void DispFresh (void)
{
   static unsigned char strobe;            //переменная активного строба дисплея и опроса кнопок (0-3)

   ButtonGet (strobe);                     //ф-ция обработки опроса кнопок (аргумент - номер включенного строба индикатора 0-3)

   if (++ strobe >= 4){                  //инкремент строба знакоместа
      strobe =0;

      //здесь обслуживание переменных с частотой 100Гц
      if (++ DispFlash == 100)            //инкремент счетчика FLASH (за секунду счет 0-99)
         DispFlash = 0;
   }

   if (DisplayType)
   {   //обработка дисплея с ОА
      DispStrobePort &= ~(DispStrb0 |DispStrb1 |DispStrb2 |DispStrb3);//погасить все разряды
      DispDataPort = ~(DispData [strobe]);   //вывод данныx

      if (Flags1 & b1DispFlash){            //проверка включен ли режим FLASH
         if (DispFlash >= 50)
            DispDataPort = DisplayType;      //при счетчике 0-49 индикация данных, 50-99 погасить дисплей
      }

      switch (strobe){                  //включение обновленного знакоместа
         case 0: DispStrobePort |= DispStrb0; break;
         case 1: DispStrobePort |= DispStrb1; break;
         case 2: DispStrobePort |= DispStrb2; break;
         case 3: DispStrobePort |= DispStrb3;
      }
   }
   else
   {   //обработка дисплея с ОК                           
      DispStrobePort |= (DispStrb0 |DispStrb1 |DispStrb2 |DispStrb3);//погасить все разряды
      DispDataPort = DispData [strobe];      //вывод данных

      if (Flags1 & b1DispFlash){            //проверка включен ли режим FLASH
         if (DispFlash >= 50)
            DispDataPort = DisplayType;      //при счетчике 0-49 индикация данных, 50-99 погасить дисплей
      }

      switch (strobe){                  //включение обновленного знакоместа                     
         case 0: DispStrobePort &= ~DispStrb0; break;
         case 1: DispStrobePort &= ~DispStrb1; break;
         case 2: DispStrobePort &= ~DispStrb2; break;
         case 3: DispStrobePort &= ~DispStrb3;
      }
   }   
}

//----------
//ф-ция обработки нажатий клавиш (вызывается с частотой 400 Гц)
//аргумент - номер включенного строба индикатора 0-3 (0 - left, 1 - down, 2 - right, 3 - up)

void ButtonGet (unsigned char strobe)
{
   static unsigned char BtnLockBit;         //ащелка (защита от дребезга)
   static unsigned char BtnLockCoun;         //счетчик защелки (защита от дребезга)
   static unsigned char BtnLongCoun;         //счетчик длинного нажатия
   static unsigned char BtnStack;            //здесь накапливаем за время опроса 4-х стробов, маску нажатых кнопок
   static unsigned char BtnTemp;            //бит последней   нажатой кнопки
   #define BtnLock 30                     /*время обработки дребезга в милисекундах (10-100)*/
   #define BtnLongPress 2000               /*время фиксации длинного нажатия в милисекундах (1000 - 2500)*/

   if (!((BtnPort ^ DisplayType) & BtnPin))   //грубый опрос кнопок во время обработки стробов, с учетом типа индикатора (ОК, ОА)
      BtnStack = (1 << strobe);

   if (strobe <3)   
      return;                           //выход первые 3 строба (обработка данных только после последнего 4-го строба)

   if (BtnStack)
   {                        //клавиша нажата
      BtnTemp = BtnStack;
      BtnStack = 0;
      if (BtnLockCoun < (BtnLock/10)){
         BtnLockCoun++;
         return;
      }
      BtnLockBit=1;                     //нажатие зафиксировано
      if (BtnLongCoun >= (BtnLongPress/10))
         return;
      BtnLongCoun ++;
      if (BtnLongCoun >= (BtnLongPress/10))
         *pButtonFlags |= (BtnTemp << 4);   //установка бита длинного нажатия (старшие 4 бита флагов ButtonByte)
   }      
   else
   {                                 //клавиша отжата         
      if (BtnLockCoun != 0){
         BtnLockCoun --;
         return;
      }
      if (! (BtnLockBit))                  //отжатие зафиксировано
         return;
      BtnLockBit =0;
      if (BtnLongCoun < (BtnLongPress/10)){
         *pButtonFlags |= (BtnTemp);         //установка бита короткого нажатия (младшие 4 бита флагов ButtonByte)
         DispFlash = 0;                  //сбросить на начало цикл FLASH дисплея
      }
      BtnLongCoun = 0;
   }
}

//#######################################################################################################################
//#######################################################################################################################
/*
//индикация светиком
if (*pButtonFlags) CtrlPortOut |= CtrlPin;
else CtrlPortOut &= ~CtrlPin;
*/


Вернуться наверх
 
Не в сети
 Заголовок сообщения: Re: C для AVR -- пишем аккуратно.
СообщениеДобавлено: Пт авг 24, 2012 21:33:35 
Опытный кот
Аватар пользователя

Карма: 7
Рейтинг сообщений: 52
Зарегистрирован: Чт дек 31, 2009 19:27:45
Сообщений: 842
Откуда: Бровари, Україна
Рейтинг сообщения: 0
Начинаем продолжать…

Код:
static NOINLINE void EEWrite(uint8_t addr, uint8_t data) { … }
static NOINLINE uint8_t EERead(uint8_t addr) { … }
Библиотечные функции работы с EEPROM принимают «как бы указатель» на переменную в EEPROM. Оно выглядит красиво, но EEMEM только атрибут размещения и всё равно тот указатель как указатель не работает (в gcc 4.7 добавлены пространства памяти и в avr-gcc они использованы, но это другая история).
Соответственно в функции eeprom_* нужно передавать 16-битный аргумент и для вычисления адресов используется 16-разрядная арифметика. Но ведь в ATtiny13 всего 64 байта EEPROM!
Не ленимся и пишем свои функции, принимающие 8-битный номер ячейки. Тоже NOINLINE, так как иначе некоторые версии компилятора их встраивают (особенно Read, она совсем короткая).

Теперь иллюстрация к моим словам «т.е. у С-шной программы даже без желания автора обычно несколько другой функционал.»
Тут с некоторым желанием, но всё же.
Исходно:
Код:
unsigned char EEData[EEDataLen] EEMEM;
unsigned char EEPnt EEMEM;

      for (unsigned char i=0; i<EEDataLen; i++)
         eeprom_write_byte(&EEData [i], 0);
      eeprom_write_byte(&EEPnt, 0);
Всё вроде бы нормально. Ассемблерная программа зануляла не глядя всю EEPROM-ину, а тут одна из переменных зануляется отдельно. Лишний вызов (а в исходном варианте ещё и с передачей 16-разрядного адреса). И сохранить «высокоуровневость» с занулением только необходимой части, причём не обязательно начиная с нулевого адреса, и сэкономить код можно объединением массива и индекса в структуру (имена такие с целью упрощения редактирования текста). Зануляем всю структуру одним махом:
Код:
#define EEDataLen 60            /*длина буфера данных в EEPROM */
struct {
    unsigned char Data[EEDataLen];  //массив данных в EEPROM
    unsigned char Pnt;         //указатель на байт массива в EEPROM (0-59)
} EE EEMEM;

        uint8_t from = (unsigned)&EE;
        uint8_t to   = (unsigned)&EE + sizeof(EE);
        do  { EEWrite(from, 0); ++from; } while(from < to); //цикл стирания данных EEPROM
При обращении используем адреса полей
Код:
            EEWrite((unsigned)&EE.Pnt, EEPntTmp+3);
            uint8_t ee_addr = (unsigned)&EE.Data[EEPntTmp];


Дальше мелочи.
Код:
        //сдвиг буфера и запись в младший разряд принятого бита
        unsigned long ultemp = ReceiveData.dw << 1;
        if (BitLoPartCoun < BitHiPartCoun) ultemp |= 1;
        ReceiveData.dw = ultemp;
об этом я тоже уже писал — компилятору тут желательна подсказка в виде временной переменной и он перестаёт делать лишнее сохранение после сдвига.
Код:
        //ReceiveData &= 0x00ffffff;      //очистить лишние старшие 8 бит
Это я сразу закомментировал, еще до создания union для приёмного буфера. Старший байт в программе нигде не анализировался, ничего страшного, если там поболтается мусор.

Ну вот, собственно, и всё… Не так уж и много изменил. Сделать было быстрее, чем описать :)))

Что осталось:
1. Обще-С-шная беда, особенно сильно проявляющаяся у AVR -- у него много регистров, а при обработке прерывания их нужно сохранить.
Если обработчик делает всё «сам», то компилятор знает, какие регистры он задействовал и сохраняет/восстанавливает только их.
Если из обработчика вызывается какая-то функция, то в обработчике сохраняются все регистры, которые вызываемая функция не обязана сохранять. Даже если она их не использует — компилятор-то про это «не знает» и сохраняет на всякий случай все *) Из-за обилия регистров у AVR стоит избегать вызовов не-inline функций из обработчиков. Вот даже тут если поменять функции EERead/EEwrite на INLINE, то код увеличится, но ненамного, так как пропадёт с десяток push/pop (т.е. десятка четыре байт). Для avr-gcc 4.3.4 из убунты 10.04 NOINLINE даёт 680 байт, а INLINE, размноживший тела функций по месту, 712.

2. Чисто avr-gcc-шная беда. Он почему-то экономит указательные регистры и предпочитает 4-байтовые lds/sts, которых там в обработчике немеряно. Даже если собрать все используемые в Receive() переменные в одну структуру, завести в подпрограмме указатель на эту структуру (под использование ldd/std), то он всё равно радостно скажет «да это же не какая-то неизвестная и каждій раз другая структура, это вот она одна конкретная фиксированная я ее вижу», выбросит указатель и будет напрямую lds/sts использовать.
IAR, насколько я знаю, умеет сам собирать рядом объявленные переменные в структуру и использовать смещение к адресной регистровой паре. Прикидочно, на этом в данной программе можно сэкономить ещё около полусотни байт.
avr-gcc иногда удаётся убедить использовать указатель, но это если вообще не поленюсь, то уж не в эти выходные.

*) Keil/MCS51 умеет вкупе с линкером делать глобальную регистровую оптимизацию, анализируя по дереву вызовов использование регистров. Не помню, работает ли это с обработчиками прерываний, они у меня на асме написаны были. На 25-килобайтной программе эта оптимизация давала около килобайта выиграша (для всех своих ассмблерных подпрограмм я тоже дал описания используемых регистров, иначе от них вверх по дереву шло «неизвестно, а значит надо беречь всё»).
Возможно, в GCC что-то поправится в связи с LTO, но, опять таки, не уверен, что на обработчики преріваний это повлияет.

_________________
Лень в виде мании величия: «ты гений, зачем стараться?». В виде комплекса: «всё равно не выйдет, зачем упираться?». Как логика: «если достаточно, зачем знать и уметь больше?». Цель одна: остановить. Не любит тепло работающих мышц и шум работающего мозга.


Последний раз редактировалось avreal Пт авг 24, 2012 21:41:25, всего редактировалось 1 раз.

Вернуться наверх
 
Не в сети
 Заголовок сообщения: Re: C для AVR -- пишем аккуратно.
СообщениеДобавлено: Пт авг 24, 2012 21:34:36 
Опытный кот
Аватар пользователя

Карма: 7
Рейтинг сообщений: 52
Зарегистрирован: Чт дек 31, 2009 19:27:45
Сообщений: 842
Откуда: Бровари, Україна
Рейтинг сообщения: 0
YS писал(а):
#pragma offtopic
А AVReal еще не поддерживает FTBB?
Ну вот как меньше буду по форумам сидеть, так и найду время :-)

_________________
Лень в виде мании величия: «ты гений, зачем стараться?». В виде комплекса: «всё равно не выйдет, зачем упираться?». Как логика: «если достаточно, зачем знать и уметь больше?». Цель одна: остановить. Не любит тепло работающих мышц и шум работающего мозга.


Вернуться наверх
 
Не в сети
 Заголовок сообщения: Re: C для AVR -- пишем аккуратно.
СообщениеДобавлено: Сб авг 25, 2012 10:22:27 
Держит паяльник хвостом
Аватар пользователя

Карма: 15
Рейтинг сообщений: 70
Зарегистрирован: Ср мар 28, 2012 21:45:24
Сообщений: 904
Откуда: ВО
Рейтинг сообщения: 0
Ну так это уже на статью тянет


Вернуться наверх
 
Не в сети
 Заголовок сообщения: Re: C для AVR -- пишем аккуратно.
СообщениеДобавлено: Сб авг 25, 2012 11:51:25 
Опытный кот
Аватар пользователя

Карма: 7
Рейтинг сообщений: 52
Зарегистрирован: Чт дек 31, 2009 19:27:45
Сообщений: 842
Откуда: Бровари, Україна
Рейтинг сообщения: 0
«Охота пуще неволи» («Начинаем продолжать - 2»).

Мелкая правка:
Поменял имена типа LedPin на LedMask и завёл LedPIN как именно регистр PIN, запись в него инвертирует ножку. Таким образом мигание светодиодом ещё укоротилось (это же и для асм-варианта полезно).

Тяжёлая артиллерия (заставляем avr-gcc работать со структурой через указатель и смещения — struct_receiver.c):
Объединил переменные состояния приёма и буфер приёма в одну структуру (связанные логикой программы переменные я чаще всего сразу объединяю в структуру), добавил в Receive() переменную-указатель и макрос для предзагрузки указателя (там в исходниках ссылка на сайт, где обсуждается этот фокус).
После столь убедительной просьбы avr-gcc начинает таки использовать ldd/std и размер программы для всех версий, кроме 3.4.6, упал до значений в диапазоне 600-620 байт, в минимуме 598.
Уже только на треть больше 450-байтового ассемблерного варианта у которого все переменные в регистрах и прямая работа с ними на месте. Было бы переменных больше и они не лезли бы все в регистры, так разница была бы еще меньше.
Хотя асмовый вариант тоже можно немного сократить.

Если ещё такое же сделать для Int100Hz(), причём в ту же структуру затолкать и счётчик Div100, то размер упадёт ниже отметки 600 байт уже для большинства версий компилятора.

Веником его, веником (reg_struct_receiver.c):
Почти все глобальные переменные размещены в регистрах при помощи конструкций вида
Код:
register uint8_t RelayOnCoun asm("r2");
Нужно в ключах добавить
Код:
-ffixed-r2 -ffixed-r3 -ffixed-r4 -ffixed-r5 -ffixed-r6 -ffixed-r7 -ffixed-r8
При этом компилятор все равно иногда работает с ними плохо («I like to move it move it»), вместо dec r2 может влупить mov r16, r2 $ subi r16, 1 $ mov r2, r16, но всё равно код выходит короче и быстрее.

Повтор таблички с новой колонкой ptr для результатов со структурой и указателем и с колонкой reg для принудительного размещения переменных в регистрах.
Не зря я не люблю насиловать компилятор размещением в регистрах. Две версии (*** в соответствующей колонке) просто взглюкнули, выбросив почти весь код receive():
Код:
avr-gcc   orig    patch   ptr     reg     avr-gcc build

3.4.6     884     702     646     616     WinAVR-20060421
4.2.2     874     678     620     586     WinAVR-20071221
4.3.3     872     658     598     ***     WinAVR-20100110

4.7.1     844     674     612     576     MobileChessBoard 4.7.1rc1
4.7.1     844     668     606     570     MobileChessBoard 4.7.1rc1  -fno-ivopts

4.3.4     894     680     620     ***     Ubuntu-10.04
4.5.3     860     660     598     566     Ubuntu-12.04

И новые исходники


Вложения:
struct_receiver.7z [3.74 KiB]
Скачиваний: 320

_________________
Лень в виде мании величия: «ты гений, зачем стараться?». В виде комплекса: «всё равно не выйдет, зачем упираться?». Как логика: «если достаточно, зачем знать и уметь больше?». Цель одна: остановить. Не любит тепло работающих мышц и шум работающего мозга.


Последний раз редактировалось avreal Сб авг 25, 2012 12:09:31, всего редактировалось 1 раз.
Вернуться наверх
 
Не в сети
 Заголовок сообщения: Re: C для AVR -- пишем аккуратно.
СообщениеДобавлено: Сб авг 25, 2012 12:06:40 
Опытный кот
Аватар пользователя

Карма: 7
Рейтинг сообщений: 52
Зарегистрирован: Чт дек 31, 2009 19:27:45
Сообщений: 842
Откуда: Бровари, Україна
Рейтинг сообщения: 0
ILYAUL писал(а):
Ну так это уже на статью тянет
Так это ж нужно садиться и делать хоть маленькую, но дополнительную работу :-)
Надо сначала отлежаться/отлизаться :-)

_________________
Лень в виде мании величия: «ты гений, зачем стараться?». В виде комплекса: «всё равно не выйдет, зачем упираться?». Как логика: «если достаточно, зачем знать и уметь больше?». Цель одна: остановить. Не любит тепло работающих мышц и шум работающего мозга.


Вернуться наверх
 
Не в сети
 Заголовок сообщения: Re: C для AVR -- пишем аккуратно.
СообщениеДобавлено: Сб авг 25, 2012 13:37:47 
Опытный кот
Аватар пользователя

Карма: 8
Рейтинг сообщений: 105
Зарегистрирован: Ср фев 22, 2012 01:25:21
Сообщений: 882
Рейтинг сообщения: 0
Уххххх......
Высший пилотаж! Я уж и боюсь вникать во все эти тонкости, стока их.....

Для начала попробую разобраться, как это вы просите компилятор использовать ldd/std вместо команд прямой загрузки, ато он немерено жрет ресурсов чтобы переменную загрузить и выгрузить. Я на асме вообще всегда резервировал регистровую пару с адресом начала оперативки, и всегда пользовался ldd/std. Честно говоря сначала в шоке был когда увидел что на обращение к переменной С тратит целых 8 байт..... это кроме операций с самой переменной.....

Со структурами пока страшновато, постепенно думаю буду вникать в ваши уроки.

Еще раз спасибо!


Вернуться наверх
 
Не в сети
 Заголовок сообщения: Re: C для AVR -- пишем аккуратно.
СообщениеДобавлено: Сб авг 25, 2012 14:37:07 
Друг Кота
Аватар пользователя

Карма: 74
Рейтинг сообщений: 1244
Зарегистрирован: Вс мар 29, 2009 22:09:05
Сообщений: 7517
Рейтинг сообщения: 0
Приятно посмотреть на качественную работу с кодом! :beer:

_________________
Разница между теорией и практикой на практике гораздо больше, чем в теории.


Вернуться наверх
 
Не в сети
 Заголовок сообщения: Re: C для AVR -- пишем аккуратно.
СообщениеДобавлено: Сб авг 25, 2012 15:10:55 
Опытный кот
Аватар пользователя

Карма: 7
Рейтинг сообщений: 52
Зарегистрирован: Чт дек 31, 2009 19:27:45
Сообщений: 842
Откуда: Бровари, Україна
Рейтинг сообщения: 0
shads писал(а):
Для начала попробую разобраться, как это вы просите компилятор использовать ldd/std вместо команд прямой загрузки
...
Я на асме вообще всегда резервировал регистровую пару с адресом начала оперативки, и всегда пользовался ldd/std.
...
Со структурами пока страшновато, постепенно думаю буду вникать в ваши уроки.
Вот когда на асме регистровая пара на начало оперативки — фактически, все переменные начинают характеризоваться не своим абсолютным адресом, а смещением относительно того начала.
Структуры в С чем-то на это похожи.
Объединяя переменные в структуру мы говорим компилятору «они должны лежать вместе, рядом, нас будут интересовать смещения относительно начала структуры». Причём самих переменны=-структур может быть несколько одинаковых, при работе с каждой пользуемся смещениями относительно её начала.
Адресация к «полям» — элементам структуры (переменной соответствующего типа) идёт через точку, например
Код:
   имя_структуры.имя_поля = 0;
Это кже неплохая подсказка компилятору о том, что можно в указательную пару загрузить адрес начала структуры и работать дальше по смещению.
Но в оптимизаторе gcc/avr-gcc где-то недоработка. Возможно, несколько неправильно расставлены «стоимости» операций / «ценность» регистров. И он почему-то считает нежелательным занимать пару регистров. Приходится обращаться через указатель
Код:
   имя_указателя_на_структуру->имя_поля = 0;
и загружать вручную указатель в пару макросом PRELOAD()
После ручной загрузки регистровая пара «уже потрачена» и компилятор её использует.

_________________
Лень в виде мании величия: «ты гений, зачем стараться?». В виде комплекса: «всё равно не выйдет, зачем упираться?». Как логика: «если достаточно, зачем знать и уметь больше?». Цель одна: остановить. Не любит тепло работающих мышц и шум работающего мозга.


Вернуться наверх
 
Не в сети
 Заголовок сообщения: Re: C для AVR -- пишем аккуратно.
СообщениеДобавлено: Сб авг 25, 2012 16:16:35 
Друг Кота

Карма: -18
Рейтинг сообщений: 29
Зарегистрирован: Вс дек 05, 2010 06:10:34
Сообщений: 4583
Откуда: ЮВ
Рейтинг сообщения: 0
Подобные костыли и порождают трудноуловимые глюки...
Не потому ли компилерописатели "пропустили" подобные "обороты"??? ))))))))

_________________
"Я не даю готовых решений, я заставляю думать!"(С)


Вернуться наверх
 
Не в сети
 Заголовок сообщения: Re: C для AVR -- пишем аккуратно.
СообщениеДобавлено: Сб авг 25, 2012 16:58:05 
Опытный кот
Аватар пользователя

Карма: 7
Рейтинг сообщений: 52
Зарегистрирован: Чт дек 31, 2009 19:27:45
Сообщений: 842
Откуда: Бровари, Україна
Рейтинг сообщения: 0
HHIMERA писал(а):
Подобные костыли и порождают трудноуловимые глюки...
А подробнее? Конкретно что за костыль, какие, к примеру, глюки? Из собственного либо чужого опыта…

HHIMERA писал(а):
Не потому ли компилерописатели "пропустили" подобные "обороты"??? ))))))))
Шо, и тут всемирный заговор?

_________________
Лень в виде мании величия: «ты гений, зачем стараться?». В виде комплекса: «всё равно не выйдет, зачем упираться?». Как логика: «если достаточно, зачем знать и уметь больше?». Цель одна: остановить. Не любит тепло работающих мышц и шум работающего мозга.


Вернуться наверх
 
Не в сети
 Заголовок сообщения: Re: C для AVR -- пишем аккуратно.
СообщениеДобавлено: Сб авг 25, 2012 17:31:30 
Друг Кота

Карма: -18
Рейтинг сообщений: 29
Зарегистрирован: Вс дек 05, 2010 06:10:34
Сообщений: 4583
Откуда: ЮВ
Рейтинг сообщения: 0
Мне проще посчитать свой пост вопросом, а не утверждением... чем впадать в полемику...
(Просто мне лениво)...

_________________
"Я не даю готовых решений, я заставляю думать!"(С)


Вернуться наверх
 
Не в сети
 Заголовок сообщения: Re: C для AVR -- пишем аккуратно.
СообщениеДобавлено: Сб авг 25, 2012 18:41:01 
Опытный кот
Аватар пользователя

Карма: 7
Рейтинг сообщений: 52
Зарегистрирован: Чт дек 31, 2009 19:27:45
Сообщений: 842
Откуда: Бровари, Україна
Рейтинг сообщения: 0
Понятно. Ляпнуть было не лениво, а отвечать — лениво.

Вы — трепло.

Можете считать мой пост вопросом, а не утверждением, так Вам будет проще, не надо будет отвечать за свои слова.

_________________
Лень в виде мании величия: «ты гений, зачем стараться?». В виде комплекса: «всё равно не выйдет, зачем упираться?». Как логика: «если достаточно, зачем знать и уметь больше?». Цель одна: остановить. Не любит тепло работающих мышц и шум работающего мозга.


Вернуться наверх
 
Показать сообщения за:  Сортировать по:  Вернуться наверх
Начать новую тему Ответить на тему  [ Сообщений: 112 ]  1, , , , ,  

Часовой пояс: UTC + 3 часа


Кто сейчас на форуме

Сейчас этот форум просматривают: OKF и гости: 51


Вы не можете начинать темы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете добавлять вложения

Найти:
Перейти:  


Powered by phpBB © 2000, 2002, 2005, 2007 phpBB Group
Русская поддержка phpBB
Extended by Karma MOD © 2007—2012 m157y
Extended by Topic Tags MOD © 2012 m157y