Помощь - Поиск - Пользователи - Календарь
Полная версия этой страницы: Глюки компилятора IAR?
Форум разработчиков электроники ELECTRONIX.ru > Микроконтроллеры (MCs) > AVR
Страницы: 1, 2
DiMonstr
Всех с прошедшими праздниками!

Присказка.
Чуть больше 6 месяцев вплотную работаю с контроллерами ATMEL и компилятором IAR. Каких только финтов не выкидывала эта связка и вот очередная проблема.Программлю я поэтапно, шаг за шагом проверяя функционально законченные блоки кода. В результате, у меня получился основной код девайса и код, который производит самотестирование периферии контроллера. По отдельности все отлично и стабильно работает. Пришло время отлаживать всё в комплексе и тут начался полтергейст, который я пытаюсь изкоренить методом прямого шаманства. В чём причина конккретно не знаю, одни догадки...
Суть проблемы.
Короче код после прошивки то запускаетя и работает, то вообще не исполняется ни одной команды. Крутил оптимизацию, распределение памяти - не помогло. Со сбросом всё в порядке, схема не причем, контроллер тоже. Остается компилятор и наверное настройки файла *.xcl
Контроллер использую Atmega8. Пишу на С. Использую стандартный lnkm8.xcl. high оптимизация проекта по размеру.
IDE:
IAR 4.20A/W32 [Evaluation] (4.20.1.3).
Компилятор:
IAR XLIB 3.29L/386 (3.29.0.12)
IAR XLINK 4.59Z (4.59.26.0)


Из своего опыта. Кодил я девайс: считыватель чип-карт, который подключается к USB. Отлаживал отдельно часть кода для работы с картой ичасть кода для обмена по USB. Поставил оптимизацию всего проекта по скорости, т.к. 10 байт с карты приходили верный, а остальные контроллер не успевал обрабатывать (кодил опять же на С). Ладно добился правильной работы оптимизацией. Как только я начал отлаживать все в комплексе - обмен с картой и обмен по USB, то начались проблемы! С картой обмен есть, с USB нет. Оптимизацию вырубаю - наоборот с USB работает, а с карты искаженные данные. Долго я шаманил над проектом... И в итоге сделал так: включил оптимизацию проекта по скорости, а перед теми функциями, которые конкретно отвечают за обмен с USB, воткнул директиву компилятора отключающую оптимизацию именно это процедуры. После этого всё работает. Это конечно ни есть хорошо, но выходя я пока так и не нашёл. Одним словом - шаманство!!!


Посоветуйте, в каком направлении мне копать! Кто сталкивался с такими проблемами?
prottoss
Цитата(DiMonstr @ Jan 11 2008, 02:42) *
Посоветуйте, в каком направлении мне копать! Кто сталкивался с такими проблемами?
С такими проблемами переодически сталкиваются все. ИМХО дело не в бобине. Копайте свой код
DiMonstr
Забыл спросить. А есть ли у компилятора такая фишка: хочу чтобы в зависимости от значения параметра в коде программы компилятор создавал прошивку с именем файла в зависимости от значения этого параметра.

Цитата(prottoss @ Jan 10 2008, 22:59) *


Пробовал сократить количество вхождений подпрограмм. Разместил почти все подпрограммы в одной. После этого ситуация немного изменилась в лучшую сторону. Количество отказов стало меньше, но проблема-то осталась. Сначала подозрение было на распределение памяти и стека, но я отбросил эту мысль, т.к. программа вообще не запускается и подпрограммы не возвращают никаких параметров.
arttab
у вас не было подозрения что работа по usb и картой отбирают друг у друга время? когда у меня были подозрения на борьбу функций за такты мк, то я иногда использовал вывод(ы) мк для указания что он выполняет. Т.е. для Вашего случая, если вывод 0 в 1, то обработка usb, а если вывод 1 в 1, то обработка карты. и поставить щупы цифрового осциллографа на эти выводы.
GDI
При борьбе со стеками очень помогает включение генерации map-файла, там есть табличка, где указано для каждой функции потребпости в стеках и общие потребности всей программы.
Сергей Борщ
Цитата(DiMonstr @ Jan 10 2008, 21:42) *
Пришло время отлаживать всё в комплексе и тут начался полтергейст, который я пытаюсь изкоренить методом прямого шаманства. В чём причина конккретно не знаю, одни догадки...
Первое, что нужно сделать - посмотреть в листинг и убедиться, что компилятор понял вас правильно.
Цитата(DiMonstr @ Jan 10 2008, 21:42) *
Короче код после прошивки то запускаетя и работает, то вообще не исполняется ни одной команды. Крутил оптимизацию, распределение памяти - не помогло. Со сбросом всё в порядке, схема не причем, контроллер тоже. Остается компилятор и наверное настройки файла *.xcl
Это понятно, что компилятор обвинить проще простого. У меня подобное поведение наблюдалось однажды, когда ошибочно был установлен фуз BRST. А у студента - когда подтяжка ресета вместо питания была подключена к одному из портов. Он порт настраивает на вывод - контроллер ресетится.

В общем есть предложение поспорить на ящик пива, что компилятор снова не при чем, а виноваты недостаток знаний и опыта.
DiMonstr
Цитата(Сергей Борщ @ Jan 11 2008, 11:11) *
...В общем есть предложение поспорить на ящик пива, что компилятор снова не при чем, а виноваты недостаток знаний и опыта.


Да запросто! Так все дело-то в том, что всё работает отлично, если закомментирована часть кода. С ресетом всё правильно - 100%, с фузами тоже - 100%. А подозрения у меня на компилятор. Да по любому он!!! Сейчас уже такая закономерность: написан код - все отлажено и работает. Начинаю добавлять дополнительные функции и достигается такая граница, при которой камень ведёт себя так, как описано выше.
Что скажете?

Цитата(arttab @ Jan 11 2008, 05:01) *


Тут и само собой все ясно: скорость передачи данных по USB в 10 раз выше, чем с картой. Это не критично. Версия девайса под RS-232работает медленнее, чем USB-шная.
zltigo
Цитата(DiMonstr @ Jan 12 2008, 15:59) *
Да по любому он!!!

smile.gif
Цитата
Что скажете?

1. smile.gif
2. Пока не сможете четко (читай грамотно) донести свою мысль до компилятора и не придется:
Цитата
...сделал так: включил оптимизацию проекта по скорости

дальше можете и не двигаться. Это первейшее необходимое, хотя возможно и недостаточное условие.
DiMonstr
Цитата(GDI @ Jan 11 2008, 10:02) *

Этой фичей компилятора я ещё не интересовался. Посмотрю...


А в целом, как правильно назначить размеры отдельным участкам карты памяти (RSTACK, CSTACK, HEAP)? Из каких соображений изходить при этом?

И такой вопрос. У меня примерно 100 константных байт которые разумно разместить во флэш памяти программы, но больше 20 не получается - компилятор ругается. Посмотрел hex-файл, а мои данные размещаются в самом его начале. Что нужно сделать и где, чтобы разместить их в конце?

Цитата(zltigo @ Jan 12 2008, 17:04) *

Нееет. Без оптимизации никак низя... Протокол с картой организован побитный и каждый бит, карта передает мне каждую 1 мкс (к примеру, сколько точно не помню сейчас), а я каждый бит обрабатываю в прерывании по таймеру - это подсчет бит, проверка на старт бит, проверка четности, и ещё что-то). Так как написано на С, то без оптимизации за 1 мкс эта часть кода контроллером не успевает обрабатывается и начинается потеря бит. Правда первые 2 десятка байт принимаются верно... wassat.gif
Dog Pawlowa
Цитата(DiMonstr @ Jan 12 2008, 18:28) *
И такой вопрос. У меня примерно 100 константных байт которые разумно разместить во флэш памяти программы, но больше 20 не получается - компилятор ругается. Посмотрел hex-файл, а мои данные размещаются в самом его начале. Что нужно сделать и где, чтобы разместить их в конце?

Давайте по порядку и конструктивно?
1) Как ругается компилятор на расположение констант во флэши,
2) как Вы их располагаете,
3) и зачем Вам нужно, чтобы они были в конце?
zltigo
Цитата(DiMonstr @ Jan 12 2008, 16:28) *
Нееет. Без оптимизации никак низя...

Повторяю - код должен быть работоспособным ПРИ ЛЮБОЙ ОПТИМИЗАЦИИ а не только без оптимизации или при оптимизации по размеру. Написанный Вами код который не работоспособен при оптимизации по скорости на самом деле НЕ РАБОТОСПОСОБЕН ВООБЩЕ - начинайте разбираться с него а не с усугубления ситуации нагромождением еще какого-то кода.
DiMonstr
Цитата(zltigo @ Jan 12 2008, 17:49) *


Если говорить про ATMEGa8, то без оптимизации код написанный на СИ очень громоздкий и не помещается в камень. Компилятор при этом выдает кучу ошибок относительно размера стэков. Но код на самом деле не поместиться без оптимизации. sad.gif
zltigo
Цитата
Если говорить...

Попробуйте хотя-бы прочитать о чем я написал, прежде чем говорить непонятно о чем.
DiMonstr
Цитата(zltigo @ Jan 12 2008, 18:10) *


Код рабочий smile.gif
Dog Pawlowa
Код рабочий? Тогда какие проблемы? biggrin.gif
singlskv
Цитата(zltigo @ Jan 12 2008, 17:49) *
Повторяю - код должен быть работоспособным ПРИ ЛЮБОЙ ОПТИМИЗАЦИИ а не только без оптимизации или при оптимизации по размеру.

Это не совсем правда,
бывает что нужно записать значение в регистр и потом в течении нескольких тактов в другой,
без оптимизации это может не работать,
хотя автор:
Цитата
Код рабочий smile.gif
конечно ЖЖЕТ!!!
Автор, если у Вас например прерывание вызывается каждые 1000тактов и
требует 1500тактов на обработку, виноват компилятор ???
Сергей Борщ
Цитата(singlskv @ Jan 12 2008, 20:21) *
прерывание вызывается каждые 1000тактов и требует 1500тактов на обработку, виноват компилятор ???
Конечно! Ведь в нем не предусмотрена большая зеленая кнопка "ХОЧУ!" lol.gif
DiMonstr
Цитата(singlskv @ Jan 12 2008, 21:21) *
Автор, если у Вас например прерывание вызывается каждые 1000тактов и
требует 1500тактов на обработку, виноват компилятор ???


В данном случае ДА! На асме этот же код работает без оптимизации, а программа на СИ как уже всем известно в мнемонике она в 1,5-2 раза больше по размеру. Это можно посмотреть в отладчике хоть в AvrStudio, хоть в штатном ИАРовском. Зачем же Вы спорите товарищи? Я разве не прав???


А у кого-нибудь есть на ходу рабочая плата/макет с МК ATMEGA8? Я тогда здесь исходники выложу, а вы посмотрите. beer.gif
zltigo
Цитата(singlskv @ Jan 12 2008, 20:21) *
Это не совсем правда,

Я не писал "рабочим" - я писал "работоспособным", т.е. в принципе способным без ошибок выполнять возложенные на него функции без относительно нюансов оптимизации.
Baser
Цитата(DiMonstr @ Jan 12 2008, 21:52) *
В данном случае ДА! На асме этот же код работает без оптимизации, а программа на СИ как уже всем известно в мнемонике она в 1,5-2 раза больше по размеру. Это можно посмотреть в отладчике хоть в AvrStudio, хоть в штатном ИАРовском. Зачем же Вы спорите товарищи? Я разве не прав???

Насчет обвинения компилятора неправы БЕЗУСЛОВНО, а насчет увеличения кода неправы ЧАСТИЧНО.
Если у вас есть некие критические секции программы, они должны быть написаны более тщательно, чем обычно. И уж без заглядывания в листинг при этом не обойтись smile.gif
А насчет увеличения размера кода в 1,5-2 раза это как сказать. Если в два раза, то это просто плохое знание Си. Некоторые конструкции компилятор может оттранслировать так что вам и не снилось написать на ассемблере, а может вас не понять и нагородить ерунды. Использование одинаковых кусков кода тоже в ИАРе реализовано отлично.

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

p.s. а мнемонику и отладчики вы вообще не к месту приплели sad.gif
Сергей Борщ
Цитата(DiMonstr @ Jan 12 2008, 21:56) *
А у кого-нибудь есть на ходу рабочая плата/макет с МК ATMEGA8? Я тогда здесь исходники выложу, а вы посмотрите. beer.gif
Есть. А вы потом не откажетесть от
Цитата
Цитата
...В общем есть предложение поспорить на ящик пива, что компилятор снова не при чем, а виноваты недостаток знаний и опыта.
Да запросто!
?
DiMonstr
Цитата(Сергей Борщ @ Jan 12 2008, 23:12) *
Есть. А вы потом не откажетесть от
Да запросто! ?

Лады. Но выложить смогу только в понедельник, сейчас нет на руках исходников.
AndyBig
Если контроллер не успевает выполнять все функции хотя бы на среднем уровне оптимизации, значит пора подумать о другом контроллере. Если код без оптимизации не влазит в контроллер, значит пора подумать о другом контроллере smile.gif.
Иначе все это получается шаманством, на которое в серьезном проекте расчитывать просто нельзя. Имхо smile.gif
DiMonstr
Выкладываю исходники проекта. Попробуйте прошить контроллер и прочитать содержимое EEPROM. Что можно предпринять по оптимизации проекта, с учетом вышеизложенных проблем?
SasaVitebsk
Цитата(DiMonstr @ Jan 20 2008, 19:26) *
Что можно предпринять по оптимизации проекта, с учетом вышеизложенных проблем?

Я вообще не экспериментирую с оптимизацией. Сразу включаю максимальную. Ведь вам изначально известно что надо оптимизировать длину или скорость. Я её включаю и больше не трогаю. Иногда только опции некоторые например cross call. Но это к работоспособности никакого отношения не имеет. То же самое, наверное, вам скажут и другие. Менять оптимизацию для получения работоспособного кода - это всё равно что пинать шины если заглох автомобиль. Совсем уж от безысходности. Бывает, что ты некоректно что-то сделал с точки зрения компилятора. Естественно по незнанию. В этом случае лучше локализовать ошибку и просмотреть выходной листинг. Как правило всё сразу становится на свои места.
Самые распространнённые в этом случае ошибки это ошибки связанные с volatile и с приведением типов.
DiMonstr
Цитата(SasaVitebsk @ Jan 20 2008, 19:58) *

Никак не могу найти причину такого поведения программы. Может я уже зацыклился? По выходному листингу всё вроде нормально. Посмотрите кто-нибудь свежим взглядом на программу? Подскажите где возможна исключительная ситуация.
aesok
Цитата(SasaVitebsk @ Jan 20 2008, 19:58) *
Самые распространнённые в этом случае ошибки это ошибки связанные с volatile ...


Я не нашел в Вашей програме ни одного "volatile". Прочитайте вот это и добавте "volatile" для тех переменных для которых надо.

Сорри что текст на англискон.

My program doesn't recognize a variable updated within an interrupt routine
When using the optimizer, in a loop like the following one:
Код
uint8_t flag;
...
ISR(SOME_vect) {
  flag = 1;
}
...

        while (flag == 0) {
                ...
        }

the compiler will typically access flag only once, and optimize further accesses completely away, since its code path analysis shows that nothing inside the loop could change the value of flag anyway. To tell the compiler that this variable could be changed outside the scope of its code path analysis (e. g. from within an interrupt routine), the variable needs to be declared like:

Код
volatile uint8_t flag;
DiMonstr
Цитата(aesok @ Jan 20 2008, 20:34) *

Правильно ли я понял, что глобальные переменные используемые в циклах подпрограмм прерываниий необходимо объявлять с ключевым словом volatile?
Спасибо.

Я к проекту подключил командный файл линкера lnkm8s.xcl. Может стоит изменить размеры стэков, как думаете?

Товарищи! Какие ещё будут нарекания по программе? help.gif
SasaVitebsk
Приведу пример для понимания происходящего
допустим есть переменные глобальные
Код
volatile uint8_t    AvarFuelWt, AvarCAN=0,                // Счётчик секунд задержки на потерю топлива и CAN
                    CAN_Temp,                            // Значение температуры двигателя по CAN
                    CAN_POil;                            // Значение давления масла двигателя по CAN

В прерывании есть такой код
Код
if((timesec--)==0)
{
   timesec=FPWM;
   Flag.fSecond=1;
   AvarFuelWt++;
   AvarCAN++;
}

В голове следующий код
Код
    if(AvarCAN>2)                                        // Обработка потери CAN
      CAN_Temp=CAN_POil=0;                                // Обнулить значение


Смысл обработки прост - если сообщений нет более 2 секунд, то обнулить значения (иначе при потере CAN сохраняются последние принятые значения).

Вот что получим при компиляции
Код
    540              if(AvarCAN>2)                                        // Обработка потери CAN
   \                     ??main_16:
   \   00000182   8D05               LDD     R16, Z+29
   \   00000184   3003               CPI     R16, 3
   \   00000186   F018               BRCS    ??main_17
    541                CAN_Temp=CAN_POil=0;                                // Обнулить значение
   \   00000188   E000               LDI     R16, 0
   \   0000018A   8F07               STD     Z+31, R16
   \   0000018C   8F06               STD     Z+30, R16


Уберём volatile.
Код
volatile uint8_t    AvarFuelWt, AvarCAN=0;                // Счётчик секунд задержки на потерю топлива и CAN
uint8_t                CAN_Temp,                            // Значение температуры двигателя по CAN
                    CAN_POil;                            // Значение давления масла двигателя по CAN

Получим следующее.
Код
    545              if(AvarCAN>2)                                        // Обработка потери CAN
   \                     ??main_16:
   \   00000182   8D05               LDD     R16, Z+29
    546                CAN_Temp=CAN_POil=0;                                // Обнулить значение
    547              // Обработка АЦП и выставление положения стрелок


Как видите значение AvarCAN было извлечено, а дальше обработка не осуществлялась, так как для компилятора в ней "не было смысла". smile.gif
forever failure
Кто нить ещё смотрел этот проект?
Впечатление в целом, если коротко - ужоснах. Конечно, брёвна в чужих глазах все мастера замечать, но в данном случае, автор действительно жжёт. (Автор, не обижайтесь, воспримите это как конструктивную критику).
Первое замечание в общем - код жутко неоптимален. ИАР, конечно многомощный комипилятор, но расчитывать на то, что он всё это дело оптимально разгребёт, не стоит. Пример в частности:
в функции main есть switch (channel), в котором по каждой метке выполняется почти один и тот же код:
case 0x01:
{
DebuggingData();

if (Value > MinValue & Value < MaxValue) /*<- здесь действительно побитовое И требуется ?*/
{
MeanValue_CH1 = MeanValue_CH1 + Value;
/* в каждой метке свитча своя только переменная MeanValue_CH*
можно было бы объявить массив этих переменных и обращатся в ним по индексу:
*/
// MeanValue_CH[channel] += Value;
if (MeanCount_CHx == StepMeanVoltage)
{
MeanValueVoltage[channel] = MeanValue_CH1 / StepMeanVoltage;
// MeanValueVoltage[channel] = MeanValue_CH[channel] / StepMeanVoltage;
MeanValue_CH1 = NULL; /* почему, кстати, целочисленной переменной присваивается значание указателя ? */
}
break;
}
ALARM_PROCCESS = TRUE;
break;
}
Если так поменять, тогда вообще никакого свитча не понадобится, и размер кода заметно уменьшиться. Такие места есть ещё. И ещё много чего интересного и занимательного есть, из-за чего этот код рабочим не является, и, похоже, никогда таковым не был.
DiMonstr
Цитата(forever failure @ Jan 21 2008, 06:50) *
...Если так поменять, тогда вообще никакого свитча не понадобится, и размер кода заметно уменьшиться. Такие места есть ещё.

Да зачем я буду обежаться, я и разместил здесь проект, чтобы в свой адрес услышать критику. Что и как я сделал не правильно, почему, как надо и т.д. Я же не профессионал, поэтому и прошу совета.
За предложение forever failure благодарю! Буду переделывать.

Цитата(forever failure @ Jan 21 2008, 06:50) *
И ещё много чего интересного и занимательного есть, из-за чего этот код рабочим не является, и, похоже, никогда таковым не был.

Ну зачем так жестко критиковать, код более менее рабочий, выполняет основные задачи без сбоев. Но уверенности у меня в нем 100%-ой нет, поэтому и обратился на форум с целью обогащения своих знаний и опыта.


Какие ещё есть косяки в коде? Огласите пожалуйста.


Цитата(forever failure @ Jan 21 2008, 06:50) *
1) if (Value > MinValue & Value < MaxValue) /*<- здесь действительно побитовое И требуется ?*/
2) MeanValue_CH1 = NULL; /* почему, кстати, целочисленной переменной присваивается значание указателя ? */

1) Никак нет, в данном случае производится элементарное сравнение - логическое И значит.
2) В данном случае переменная обнуляется.
Сергей Борщ
Цитата(DiMonstr @ Jan 21 2008, 13:58) *
1) Никак нет, в данном случае производится элементарное сравнение - логическое И значит.
Тогда и надо писать if (Value > MinValue && Value < MaxValue). Код будет значительно компактнее и быстрее.
Цитата(DiMonstr @ Jan 21 2008, 13:58) *
2) В данном случае переменная обнуляется.
Тогда так и надо писать: MeanValue_CH1 = 0. NULL имеет тип указателя, удивительно что компиялтор пропустил такое присваивание (если только в режиме C++, но в нем как раз рекомендуется использовать 0 в том числе и для указателей вместо NULL).
forever failure
/* if (Value > MinValue & Value < MaxValue) */ В таком случае просмотрите весь проект.
Таких мест я там заметил немало. И ещё, для большеё ясности, что требуется по логике работы, в операторах ветвления (if, for, while, ...) приоритет операций сравнения лучше определять явным образом, т. е. скобками:
if ((Value > MinValue) && (Value < MaxValue)). Или тщательно изучить главу про приоритеты операторов C, дабы не было неожиданностей.
SasaVitebsk
Если честно я тоже скачал данную прогу. Но при таком объёме смотреть её не буду. Надеюсь вы понимаете, что здесь вполне приличный проект и ожидать, что кто-нибудь из рыцарских соображений отладит его за вас - не приходится.

Либо локализуйте ошибку - либо выкиньте часть текста, до которого всё равно дело не доходит и тогда обращайтесь. Простите, это моё мнение.

По поводу оптимизации. В варианте со switch компилятор, тем не менее сам найдёт общие куски и объединит их. Я бы для начала отладил бы прогу в целом, а уж потом занялся оптимизацией проекта (если уж сразу не удалось написать оптимально). А то сейчас начать оптимизацию - это добавить новых ошибок.
Qwertty
Цитата(Сергей Борщ @ Jan 21 2008, 15:19) *
NULL имеет тип указателя, удивительно что компиялтор пропустил такое присваивание (если только в режиме C++, но в нем как раз рекомендуется использовать 0 в том числе и для указателей вместо NULL).

Я вот всю жизнь считал что NULL имеет тип unsigned int, такой же как указатель, но это все же не одно и тоже. Так что при присваивании переменная = NULL произойдет обычное преобразование типов, если это нужно, и никаких варнингов не последует.
defunct
Цитата(Qwertty @ Jan 21 2008, 15:56) *
Я вот всю жизнь считал что NULL имеет тип unsigned int, такой же как указатель, но это все же не одно и тоже.


typedef void *PVOID;
#define NULL ((PVOID)(0))


DiMonstr
Нашел у вас в коде такие записи:
Цитата
BufLog[0] = StartByte;
BufLog[1] = MSB(tics);
BufLog[2] = LSB(tics);
BufLog[3] = seconds;
BufLog[4] = minutes;
BufLog[5] = hours;
BufLog[6] = day;
BufLog[7] = month;
BufLog[8] = MSB(year);
BufLog[9] = LSB(year);
BufLog[10] = StopByte;


Но ведь можно просто объявить структуры, где будут храниться ваши записи, например:
Код
typedef struct tagRTC_TIME
{
    U16 Ticks;
    U8   seconds;
    U8   minutes;
    U8   hours;
    U8   day;
    U8   month;
    U16 year;
} TRTC_TIME, *PRTC_TIME;

TRTC_TIME rtcTime;


И далее в отчет просто копировать всю запись целиком одной строкой:

memcpy( &BufLog[ 1 ], &rtcTime, sizeof( rtcTime ));

При отправке этого BufLog'a, или записи в eeprom - подцеплять "старт" и "стоп" таги.
Qwertty
Цитата(defunct @ Jan 21 2008, 16:04) *
typedef void *PVOID;
#define NULL ((PVOID)(0))

Да, был неправ. Нашел целых три определения:
#define NULL __null
#define NULL ((void *)0)
#define NULL 0
Второе - С, третье С++, а первое непонятно...
DiMonstr
Цитата(Сергей Борщ @ Jan 21 2008, 15:19) *
Тогда и надо писать if (Value > MinValue && Value < MaxValue). Код будет значительно компактнее и быстрее.

Т.е., как я понимаю, результат при сравнении операторами '&' или '&&' будет одинаков, за исключением того, что оператор '&' делает это побитно, а '&&' побайтно. Верно?

Цитата(Сергей Борщ @ Jan 21 2008, 15:19) *
Тогда так и надо писать: MeanValue_CH1 = 0. NULL имеет тип указателя, удивительно что компиялтор пропустил такое присваивание (если только в режиме C++, но в нем как раз рекомендуется использовать 0 в том числе и для указателей вместо NULL).

В файле <stdio.h> NULL объявлена как 0:
#ifndef NULL
#define NULL (0)
#endif

И в других хидерах переопределений нет. Поэтому так и написал. А то, что NULL имеет тип указателя я и не знал. 05.gif
Для надежности всё же исправил.

Цитата(forever failure @ Jan 21 2008, 15:42) *

Логика такая: при выходе измеряемого напряжения (Value) за допустимые значения (MinValue и MaxValue) необходимо отключать питание системы.
Т.е. в данном случае, если условие соблюдается, то всё в порядке, иначе блокируем работу системы.
В отладчике проверял - работает. На практике проверял - тоже работает.Поэтому так и оставил.
Теперь исправил. smile.gif


Цитата(SasaVitebsk @ Jan 21 2008, 15:52) *
Либо локализуйте ошибку - либо выкиньте часть текста, до которого всё равно дело не доходит и тогда обращайтесь. Простите, это моё мнение.
По поводу оптимизации. В варианте со switch компилятор, тем не менее сам найдёт общие куски и объединит их. Я бы для начала отладил бы прогу в целом, а уж потом занялся оптимизацией проекта (если уж сразу не удалось написать оптимально). А то сейчас начать оптимизацию - это добавить новых ошибок.

Заметьте, отлаживать за себя я никого не просил. Я лишь хочу научиться правильно писать программу на практических советах уважаемых посетителей форума. Эти советы я думаю будут полезны и другим новичкам. Если человек с большим опытом, то ему не составит большого труда просмотрев код, даже не вникая в алгоритм, найти участки кода и сделать их более эффективными.
От свитча я вовсе избавился, действительно через массив оказалось всё просто, компактно и эффективно.
Оптимизация у меня давно выставлена по размеру и я её больше не трогаю с тех пор как компилятор по мере реализации новых фунций начал выдавать такие сообщения:
Код
Building configuration: pwr_control - Release
Updating build tree...
main.c
Linking
Error[e16]: Segment CODE (size: 0x202e align: 0x1) is too long for segment definition. At least 0xa0 more bytes  
needed. The problem occurred while processing the segment placement command  
"-Z(CODE)CODE=26-1FFF", where at the moment of placement the available memory ranges were  
"CODE:72-1fff"
   Reserved ranges relevant to this placement:
   CODE:26-3d           NEAR_F
   CODE:3e-65           SWITCH
   CODE:66-71           INITTAB
   CODE:72-1fff         ?FILL1
Error[e16]: Segment TINY_ID (size: 0 align: 0) is too long for segment definition. At least 0 more bytes needed.  
The problem occurred while processing the segment placement command  
"-Z(CODE)TINY_ID,NEAR_ID=26-1FFF", where at the moment of placement the available memory ranges  
were "-none-"
   Reserved ranges relevant to this placement:
   CODE:26-3d           NEAR_F
   CODE:3e-65           SWITCH
   CODE:66-71           INITTAB
   CODE:72-1fff         ?FILL1
Error[e16]: Segment NEAR_ID (size: 0xe1 align: 0) is too long for segment definition. At least 0xe1 more bytes  
needed. The problem occurred while processing the segment placement command  
"-Z(CODE)TINY_ID,NEAR_ID=26-1FFF", where at the moment of placement the available memory ranges  
were "-none-"
   Reserved ranges relevant to this placement:
   CODE:26-3d           NEAR_F
   CODE:3e-65           SWITCH
   CODE:66-71           INITTAB
   CODE:72-1fff         ?FILL1
Error[e16]: Segment CHECKSUM (size: 0x2 align: 0) is too long for segment definition. At least 0x2 more bytes  
needed. The problem occurred while processing the segment placement command  
"-Z(CODE)CHECKSUM#1FFF", where at the moment of placement the available memory ranges were  
"-none-"
   Reserved ranges relevant to this placement:
   CODE:0-17            INTVEC
   CODE:18-25           NEAR_ID
   CODE:26-3d           NEAR_F
   CODE:3e-65           SWITCH
   CODE:66-71           INITTAB
   CODE:72-1fff         ?FILL1
Error[e18]: Range error,  
Address out of range. Valid range is 0 to 8190 (0x1FFE).
File: E:\_CRYPTOSOFT\PWR_CONTROL\SRC\util.c, Line: 240  
Source: RCALL   ?PROLOGUE4_L09
  Where $ = slow_crc16 [0x370]
            in module "main" (E:\_CRYPTOSOFT\PWR_CONTROL\SRC\Release\Obj\main.r90),
            offset 0x0 in segment part 159, segment CODE
  What: ?PROLOGUE4_L09 [0x2000]
  Allowed range: 0x0 - 0x1FFE
  Operand: ?PROLOGUE4_L09 [0x2000]
           in module ?PROLOGUE_L09 (D:\Program Files\IAR Systems\Embedded Workbench 4.0 Evaluation\
avr\LIB\CLIB\cl1s-ec_mul.r90),
           Offset 0x0 in segment part 12, segment CODE
Error[e18]: Range error,  
Address out of range. Valid range is 0 to 8190 (0x1FFE).
File: E:\_CRYPTOSOFT\PWR_CONTROL\SRC\util.c, Line: 257  
Source: RJMP    ?EPILOGUE_B4_L09
  Where $ = slow_crc16 + 0x56  [0x3C6]
            in module "main" (E:\_CRYPTOSOFT\PWR_CONTROL\SRC\Release\Obj\main.r90),
            offset 0x56 in segment part 159, segment CODE
  What: ?EPILOGUE_B4_L09 [0x201A]
  Allowed range: 0x0 - 0x1FFE
  Operand: ?EPILOGUE_B4_L09 [0x201a]
           in module ?EPILOGUE_B_L09 (D:\Program Files\IAR Systems\Embedded Workbench 4.0 Evaluation\
avr\LIB\CLIB\cl1s-ec_mul.r90),
           Offset 0x0 in segment part 12, segment CODE
Error[e18]: Range error,  
Address out of range. Valid range is 0 to 8190 (0x1FFE).
File: E:\_CRYPTOSOFT\PWR_CONTROL\SRC\rdc.c, Line: 29  
Source: RCALL   ?PROLOGUE2_L09
  Where $ = BeginDataWriteToRDC [0x3C8]
            in module "main" (E:\_CRYPTOSOFT\PWR_CONTROL\SRC\Release\Obj\main.r90),
            offset 0x0 in segment part 160, segment CODE
  What: ?PROLOGUE2_L09 [0x2004]
  Allowed range: 0x0 - 0x1FFE
  Operand: ?PROLOGUE2_L09 [0x2004]
           in module ?PROLOGUE_L09 (D:\Program Files\IAR Systems\Embedded Workbench 4.0 Evaluation\
avr\LIB\CLIB\cl1s-ec_mul.r90),
           Offset 0x0 in segment part 14, segment CODE
.......
ну и так далее
.......
Total number of errors: 33
Total number of warnings: 0


Ну я и воткнул оптимизацию по размеру на максимум. Всё работает, но вот елси разрешить выполнение следующего кода:
Код
#if(CheckMCU)
  sum = slow_crc16(sum,(unsigned char __flash *)0, (unsigned long)&__checksum);
  // call with two 0 bytes for the correct calculation of crc
  sum = slow_crc16(sum,(unsigned char __flash *)&zero,2);                      
...
#endif

то прошивка лобо вообще не запускается либо запускается через раз. А по отдельности эти части кода фунциклируют по честному.


Цитата(defunct @ Jan 21 2008, 16:04) *

А я сначала и объявлял переменные через структуру. Только не смог передать содержимое структуры не в массив, не напрямую в eeprom. Я вообще не знал про эту функцию:
Код
memcpy( &BufLog[ 1 ], &rtcTime, sizeof( rtcTime ));

А каким образом можно без неё вообще обойтись и записывать структуру непосредственно в eeprom?
Спасибо за дельный совет. biggrin.gif
SasaVitebsk
Цитата(DiMonstr @ Jan 21 2008, 23:27) *
Т.е., как я понимаю, результат при сравнении операторами '&' или '&&' будет одинаков, за исключением того, что оператор '&' делает это побитно, а '&&' побайтно. Верно?

Нет не верно.
выражение внутри скобок if (Value > MinValue && Value < MaxValue) означает что если Value > MinValue И Value < MaxValue, то ... Если вы вместо && поставите &, то это будет означать, что сначала выполнятся два сравнения потом выполнится логическое & над результатами а потом результат будет сравнён с "0". Поскольку значение "true" определена как "не 0", то в общем случае результаты могут не совпадать.

По своему опыту скажу, что достаточно часто при такой ошибке написания результат операции не соответствует ожидаемому. Поэтому лучше указывать всё точно и явно. Также, как многие писали, лучше немного перестараться со скобками.
aesok
В функциях EEPROM_* - не должны разрешаться прерывания на выходе. Они должны востанавливать состояние состояние флага "I" таким каким оно было при входе в функцию.

И вообще используйте библиотечные функции для доступа к EEPROM. В них небудет детских ошибок.

И перед тем как сново захотите использовать их в прерываниях прочитайте про понятие "non-reenterable".

Анатолий.
defunct
Цитата
А я сначала и объявлял переменные через структуру. Только не смог передать содержимое структуры не в массив, не напрямую в eeprom. Я вообще не знал про эту функцию:
...
А каким образом можно без неё вообще обойтись и записывать структуру непосредственно в eeprom?

Можно написать свою функцию для записи блоков в eeprom, например такую:

Код
void store_to_eeprom( int dst_addr, void *pData, int size)
{
      unsigned char *p = (unsigned char *)pData;
      while( size--)
           EEPROM_write_byte( dst_addr++, *p++);
}


и передавать структуры посредством указателей:

Код
store_to_eeprom( RTC_OFFSET, &rtcTime, sizeof( rtcTime ));

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

Если пользуетесь компилятором от IAR, то перед структурой можно поставить ключевое слово __eeprom и она будет располагаться в eeprom'е сразу, и пользоваться ее полями можно точно так же как если бы она лежала в RAM. Но не следует этим злоупотреблять, т.к. надо помнить, что ресурс eeprom ограничен.

Цитата(SasaVitebsk @ Jan 21 2008, 22:43) *
Цитата
результат при сравнении операторами '&' или '&&' будет одинаков

Нет не верно.
выражение внутри скобок if (Value > MinValue && Value < MaxValue) означает что если Value > MinValue И Value < MaxValue, то ... Если вы вместо && поставите &, то это будет означать, что сначала выполнятся два сравнения потом выполнится логическое & над результатами а потом результат будет сравнён с "0". Поскольку значение "true" определена как "не 0", то в общем случае результаты могут не совпадать.

Саша, в данном конкретном случае & даст такой же результат как и &&, хотя и более долгим путем smile.gif
Но в общем случае, конечно надо быть внимательным.
Иногда помогает помнить, что в теле if() "==" - это сравнение, а "=" - катастрофа smile.gif
аналогично для & и | ;>
Freeze Anti
Как я и предполагал с самого начала, вашей программе не хватает оперативной памяти... ерроры это только подтверждают... посмотрите модель памяти (tiny и small) и поставьте small... узнайте диапазон значений всех ваших переменных и задайте им соответственный тип... постарайтесь как можно меньше использовать глобальных переменных... в общем - оптимизируйте...
SasaVitebsk
Цитата(defunct @ Jan 22 2008, 04:22) *
Саша, в данном конкретном случае & даст такой же результат как и &&, хотя и более долгим путем smile.gif
Но в общем случае, конечно надо быть внимательным.
Иногда помогает помнить, что в теле if() "==" - это сравнение, а "=" - катастрофа smile.gif
аналогично для & и | ;>


В данном случае я отвечал не на вопрос "будет ли такой же результат", а отвечал на вопрос "как я понимаю". И подтверждаю, что человек неправильно понимает. Компилятор IAR результат логической операции принимает как 0 или 1, но это не гарантируется стандартом о чём я и высказался. Согласно стандарту - 0 и отличное от нуля значение. А это отнюдь не 1. Иными словами если в одном выражении будет к примеру 1 а в другом 2, то согласно стандарту всё будет корректно но логическое & над этими операндами даст 0, а это не верный результат. Конечно это чисто теоретический случай, но понимание работы должно быть полным. Программист не должен полагаться на конкретную реализацию компилятора, а должен ориентироваться на стандарт. Тем более, что эта мелочь действительно приведёт к настоящей ошибке в другом месте. То есть человек должен понимать разницу между побитовой и логической операцией.

Сравните например:
79 if(~Flag.Sek)show(1,j);
\ 00000046 01A2 MOVW R21:R20, R5:R4
\ 00000048 01B3 MOVW R23:R22, R7:R6
\ 0000004A E001 LDI R16, 1
\ 0000004C .... RCALL show

и
79 if(!Flag.Sek)show(1,j);
\ 00000046 .... LDI R30, Flag
\ 00000048 E0F0 LDI R31, 0
\ 0000004A 8100 LD R16, Z
\ 0000004C FD00 SBRC R16, 0
\ 0000004E C004 RJMP ??main_0
\ 00000050 01A2 MOVW R21:R20, R5:R4
\ 00000052 01B3 MOVW R23:R22, R7:R6
\ 00000054 E001 LDI R16, 1
\ 00000056 .... RCALL show
Сергей Борщ
Цитата(SasaVitebsk @ Jan 23 2008, 21:45) *
Компилятор IAR результат логической операции принимает как 0 или 1, но это не гарантируется стандартом о чём я и высказался. Согласно стандарту - 0 и отличное от нуля значение. А это отнюдь не 1.
Ну это не совсем так и компилятор точно следует стандарту:
Цитата
6.5.13 Logical AND operator
3 The && operator shall yield 1 if both of its operands compare unequal to 0; otherwise, it yields 0. The result has type int.
4 Unlike the bitwise binary & operator, the && operator guarantees left-to-right evaluation; there is a sequence point after the evaluation of the first operand. If the first operand compares equal to 0, the second operand is not evaluated.
Т.е. "ноль" и "не ноль" отностится не к результату, а к операндам.
Цитата
6.5.13 Logical AND operator
3 The usual arithmetic conversions are performed on the operands.
4 The result of the binary & operator is the bitwise AND of the operands (that is, each bit in the result is set if and only if each of the corresponding bits in the converted operands is set).

Поэтому первое отличие: для A=1 и B=2 A&B=0, A&&B=1.
Второе - для && операнды вычисляются слева направо и если правый операнд равен нулю, то уже сразу известно, что и результат будет равен нулю, поэтому второй операнд вычисляться не будет.
defunct
Цитата(Сергей Борщ @ Jan 24 2008, 01:37) *
Ну это не совсем так и компилятор точно следует стандарту:Т.е. "ноль" и "не ноль" отностится не к результату, а к операндам.

Как я понял - речь шла о том гарантируется ли стандартом то, что результатом любого сравнения вида ">" ">=" и т.п. всегда будет только одно из двух "1" или "0", либо такие сравнения гарантируют только "0" и "!0".
Вы привели цитаты только для & и &&, что не раскрывает вопроса ;>
Хотя что-то мне подсказывает что цитаты для && достаточно.

Я всегда считал, что рез-татом любого сравнения будет гарантировано "1" или "0", всвязи с чем часто пользуюсь такими конструкциями:

x = (y & 1 << somebit ) != 0;

для "засовывания" интересующего меня бита в бит0.
SasaVitebsk
2 Сергей Борщ.
Собственно именно это я и хотел сказать, но ты, как всегда сказал это более кратко, более внятно и более аргументировано. smile.gif
Сергей Борщ
Цитата(defunct @ Jan 24 2008, 01:37) *
Как я понял - речь шла о том гарантируется ли стандартом то, что результатом любого сравнения вида ">" ">=" и т.п. всегда будет только одно из двух "1" или "0", либо такие сравнения гарантируют только "0" и "!0".
Вы привели цитаты только для & и &&, что не раскрывает вопроса ;>
Ну так недостающий пробел легко восполнить. Открываем Google, набираем ISO/IEC 9899-1999, скачиваем, открываем, search, >=, находим:
Цитата
6.5.8 Relational operators
6 Each of the operators < (less than), > (greater than), <= (less than or equal to), and >= (greater than or equal to) shall yield 1 if the specified relation is true and 0 if it is false. The result has type int.
6.5.9 Equality operators
3 The == (equal to) and != (not equal to) operators are analogous to the relational operators except for their lower precedence.
Each of the operators yields 1 if the specified relation is true and 0 if it is false. The result has type int. For any pair of operands, exactly one of the relations is true.
DiMonstr
Цитата(Freeze Anti @ Jan 23 2008, 21:45) *

Кстати, модель памяти я использую small.

А можно ли увеличить пространство оперативки следующими методами:
- объеденить булевые переменные в байт(регистр) и изменять их состояние побитно
- а глобальные переменные собрать в структуру?
В результате этого маневра, мы добъемся дефрагментированного расположения данных в ОЗУ. Правильно?

А по поводу диапазона переменных и их типа я не совсем понял. Расскажите немного поподробней.
prottoss
Цитата
А можно ли увеличить пространство оперативки следующими методами:
- объеденить булевые переменные в байт(регистр) и изменять их состояние побитно
- а глобальные переменные собрать в структуру?
Можно:
- объеденить булевые переменные в байт(регистр) и изменять их состояние побитно
Код
typedef struct __Reg
{  
char bit0: 1;
char bit1: 1;
....
char bit4567: 4:
} Reg_t

void Fn(...)
{
Reg_t RegA;
RegA.bit0 = 1;
RegA.bit1 ^= RegA.bit0;

RegA.bit4567 = 5;

}

-а глобальные переменные собрать в структуру
Project->Compiler->Optimizations: Medium - включится галка на Static Clustering - это то ОНО и есть
DiMonstr
Цитата(aesok @ Jan 21 2008, 23:05) *
В функциях EEPROM_* - не должны разрешаться прерывания на выходе. Они должны востанавливать состояние состояние флага "I" таким каким оно было при входе в функцию.

Я где-то читал, что во время записи в eeprom нужно отключать все прерывания, иначе, если во время операции записи в eeprom возникнет какое-либо прерывание, то возможно искажение записанной инфы. Как на самом деле?

Цитата
И вообще используйте библиотечные функции для доступа к EEPROM. В них небудет детских ошибок.

А они есть? Я что-то не нашёл ни в справочной IAR и ни в справке по библиотеке DLIB. Где искать?
Для просмотра полной версии этой страницы, пожалуйста, пройдите по ссылке.
Invision Power Board © 2001-2025 Invision Power Services, Inc.