Помощь - Поиск - Пользователи - Календарь
Полная версия этой страницы: Mega48-20 не пишет в EEPROM
Форум разработчиков электроники ELECTRONIX.ru > Микроконтроллеры (MCs) > AVR
smk
Давно уже с АВРками дела не имел может что-то не так. Есть устройство с Mega48-20. Тактовая 4 МГц. Фуз еепрома стоит правильно, запись разрешена. Функции чтения-записи из даташита. Не пишет в еепром, в т.ч. и на симуляторе тоже. Что это может быть, что я забыл? Прерывания запрещаю. Пишется из главного цикла. Читать получается (0хFF).

Код
void EEPROM_write(unsigned int Address, unsigned char Data)
{
  while(EECR & (1<<EEPE)); /* Wait for completion of previous write */
  EEAR = Address; /* Set up address and Data Registers */
  EEDR = Data;
  EECR |= (1<<EEMPE); /* Write logical one to EEMPE */
  EECR |= (1<<EEPE); /* Start eeprom write by setting EEPE */
}

unsigned char EEPROM_read(unsigned int Address)
{
  while(EECR & (1<<EEPE)); /* Wait for completion of previous write */
  EEAR = Address; /* Set up address register */
  EECR |= (1<<EERE); /* Start eeprom read by writing EERE */
  return EEDR; /* Return data from Data Register */
}

    while(1)
    {
    if(mode)
    {
      PORTC = 0b00010000;
      delay(30000);
      PORTC = 0b00000000;
      delay(30000);
    }

    if(mem)
    {
      asm("cli");      
      EEPROM_write(0x0A, mode);
      EEPROM_write(0x0B, output);
      mem = 0;
      asm("sei");
    }
    //проверить питание
    }//while
Сергей Борщ
Код
EECR |= (1<<EEMPE); /* Write logical one to EEMPE */
EECR |= (1<<EEPE); /* Start eeprom write by setting EEPE */
Между установкой EEMPE и установкой EEPE должно пройти не более 4 тактов. Посмотрите, во что вылились эти операторы с вашими настройками компилятора. А чтобы работало железно - не используйте тут '|='. Вы ведь точно знаете сотояние всех остальных битов, вот и пишите каждый раз все биты через '='.
smk
Цитата(Сергей Борщ @ Dec 16 2014, 10:00) *
Код
EECR |= (1<<EEMPE); /* Write logical one to EEMPE */
EECR |= (1<<EEPE); /* Start eeprom write by setting EEPE */
Между установкой EEMPE и установкой EEPE должно пройти не более 4 тактов. Посмотрите, во что вылились эти операторы с вашими настройками компилятора. А чтобы работало железно - не используйте тут '|='. Вы ведь точно знаете сотояние всех остальных битов, вот и пишите каждый раз все биты через '='.

Компилятор GCC. Кстати оптимизация О0. Если включить другую, то код ужимается раза в два и более при этом перестает работать. Что-то я упускаю в своем понимании. А как кстати тогда правильно если не использовать '|='?
Xenia
Цитата(smk @ Dec 16 2014, 11:56) *
Компилятор GCC. Кстати оптимизация О0. Если включить другую, то код ужимается раза в два и более при этом перестает работать. Что-то я упускаю в своем понимании. А как кстати тогда правильно если не использовать '|='?


У вас все правильно написано. Непонятно, почему не работает. Возможно, ошибка вкралась в какое-то другое место - там, где вы делаете проверку или mem у вас никогда не бывает единичным. Последние предположения постороннему человеку проверить мудрено, т.к. вы привели лишь отрывки, в которых этих мест нет.
smk
Цитата(Xenia @ Dec 16 2014, 11:01) *
У вас все правильно написано. Непонятно, почему не работает. Возможно, ошибка вкралась в какое-то другое место - там, где вы делаете проверку или mem у вас никогда не бывает единичным. Последние предположения постороннему человеку проверить мудрено, т.к. вы привели лишь отрывки, в котороых этого места нет.

Бывает единицей. Проверял. Сам ничего не понимаю. Могу весь проект выложить. Он не большой. Еепром нужна чтоб запомнить последние настройки после нажатия кнопок. Нажмите для просмотра прикрепленного файла
Сергей Борщ
Цитата(smk @ Dec 16 2014, 10:56) *
Компилятор GCC.
Тогда используйте <avr/eeprom.h> и описанные в нем eeprom_write_byte(), eeprom_read_byte(). Там все проблемы времянок решены для всех уровней оптимизации:

Код
#include    <avr/eeprom.h>

void test()
{
    eeprom_write_byte((uint8_t *)0x0A, mode);
}



Цитата(smk @ Dec 16 2014, 10:56) *
А как кстати тогда правильно если не использовать '|='?

примерно так:
Код
void EEPROM_write(unsigned int Address, unsigned char Data)
{
  while(EECR & (1<<EEPE)) /* Wait for completion of previous write */
      ;
  EEAR = Address; /* Set up address and Data Registers */
  EEDR = Data;
  EECR = (1<<EEMPE); /* Write logical one to EEMPE */
  EECR = (1<<EEMPE)|(1<<EEPE); /* Start eeprom write by setting EEPE */
}
smk
Цитата(Сергей Борщ @ Dec 16 2014, 11:08) *
Тогда используйте <avr/eeprom.h> и описанные в нем eeprom_write_byte(), eeprom_read_byte(). Там все проблемы времянок решены для всех уровней оптимизации:

Код
#include    <avr/eeprom.h>

void test()
{
    eeprom_write_byte((uint8_t *)0x0A, mode);
}




примерно так:
Код
void EEPROM_write(unsigned int Address, unsigned char Data)
{
  while(EECR & (1<<EEPE)) /* Wait for completion of previous write */
     ;
  EEAR = Address; /* Set up address and Data Registers */
  EEDR = Data;
  EECR = (1<<EEMPE); /* Write logical one to EEMPE */
  EECR = (1<<EEMPE)|(1<<EEPE); /* Start eeprom write by setting EEPE */
}


Спасибо. Только я теперь не пойму что они в даташитах пишут и зачем? Раньше, помню, работало.
Сергей Борщ
Цитата(smk @ Dec 16 2014, 11:11) *
Только я теперь не пойму что они в даташитах пишут и зачем?
Это вы еще примеров в даташитах от ST не видели sm.gif
IgorKossak
Цитата(smk @ Dec 16 2014, 11:11) *
Спасибо. Только я теперь не пойму что они в даташитах пишут и зачем? Раньше, помню, работало.

Это зависит от Вашего понимания даташитов. Если там пишут "установить бит в 1", то это вовсе не означает, что это надо делать операцией |=.
А вот по поводу количества тактов между операциями сказано вполне ясно.
smk
Цитата(IgorKossak @ Dec 16 2014, 14:17) *
Это зависит от Вашего понимания даташитов. Если там пишут "установить бит в 1", то это вовсе не означает, что это надо делать операцией |=.
А вот по поводу количества тактов между операциями сказано вполне ясно.

Ну а какой операцией?
Bear_ku
Нашел старую программу для ATmega128, все было сделано по ДШ и именно с "|=", работает без нареканий.
IgorKossak
Цитата(smk @ Dec 16 2014, 14:40) *
Ну а какой операцией?

Операцией присваивания, как и советовал Сергей Борщ.

Цитата(Bear_ku @ Dec 16 2014, 14:45) *
Нашел старую программу для ATmega128, все было сделано по ДШ и именно с "|=", работает без нареканий.

Листинг кода посмотрите. В Вашем случае могла быть некоторая неявная оптимизация, например, битовыми операциями (не помню, попадают ли эти регистры для работы с ними битовыми операциями). Но в любом случае следует избегать непереносимого кода и зависеть от трюков конкретной версии компилятора.
smk
Цитата(IgorKossak @ Dec 16 2014, 15:43) *
Операцией присваивания, как и советовал Сергей Борщ.


Листинг кода посмотрите. В Вашем случае могла быть некоторая неявная оптимизация, например, битовыми операциями (не помню, попадают ли эти регистры для работы с ними битовыми операциями). Но в любом случае следует избегать непереносимого кода и зависеть от трюков конкретной версии компилятора.

Да, компилятор я понимаю, это может быть причиной. А присваивание это я так понимаю состоит из трех этапов. Сначала вычитать, потом изменить и прописать обратно. В STM регистры специально заточены под присваивание. Но там обстоятельства таковы что это допустимо. Может Вы имеете в виду операции типа REGISTR |= 0x01; ?
Сергей Борщ
Откровенно говоря - не стоит полагаться на компилятор, когда речь идет о жестких времянках. Сегодня он соберет правильно, а оптимизатор следующей версии посчитает нужным вставить между этими командами несколько других. Формально никто ему этого запретить не может. Поэтому iar для подобных операций имеет встроенные функции записи в eeprom, а gcc - библиотечные, написанные на инлайн-асме. Вот уж внутрь этих функций точно ничего не попадет и эти функции будут работать всегда, независимо ни от настроек компиоятора, ни от его версии. Примеры кода в даташите скорее чисто информационные.
smk
Код
../trsm_m48.c:21: warning: passing argument 1 of '__eerd_byte_m48' makes pointer from integer without a cast

Вот такое предупреждение. Что опять не так? Указатель нужен?

Все заработало, Спасибо! Однако с предупреждением не вполне ясно и чую включу оптимизацию (хоть и нет нужды) но работать перестанет. Отпишусь...
IgorKossak
Цитата(smk @ Dec 16 2014, 16:31) *
Да, компилятор я понимаю, это может быть причиной. А присваивание это я так понимаю состоит из трех этапов. Сначала вычитать, потом изменить и прописать обратно. В STM регистры специально заточены под присваивание. Но там обстоятельства таковы что это допустимо. Может Вы имеете в виду операции типа REGISTR |= 0x01; ?

Присваивание это просто запись, а то, что Вы описали, называется чтение-модификация-запись. Т. е. надо так, как Вам советовали:
Код
  EECR = (1<<EEMPE); /* Write logical one to EEMPE */
  EECR = (1<<EEMPE)|(1<<EEPE); /* Start eeprom write by setting EEPE */
smk
Ну вот... О1 и уже не работает. прикладываю проект для студии целиком. На самом деле действительно интересно что происходит. С кейлом таких чудес небыло ни разу. Не суть, просто хочу понять что происходит...
Нажмите для просмотра прикрепленного файла

Цитата(IgorKossak @ Dec 16 2014, 21:16) *
Присваивание это просто запись, а то, что Вы описали, называется чтение-модификация-запись. Т. е. надо так, как Вам советовали:
Код
  EECR = (1<<EEMPE); /* Write logical one to EEMPE */
  EECR = (1<<EEMPE)|(1<<EEPE); /* Start eeprom write by setting EEPE */

Ну пусть так. Однако серийное изделие (лет более 5-ти) на Тини24 работает и повторяется .... правда перепер на STM8S0003F, но это детали... и на тини отлично все работает. Версия компилятора не изменилась. В чем смысл тогда?

оптимизация Os сильно убыстряет циклы мигания
Код
      PORTC = 0b00010000;
      delay(30000);
      PORTC = 0b00000000;
      delay(30000);


Это почему?
Сергей Борщ
Цитата(smk @ Dec 16 2014, 21:14) *
Вот такое предупреждение. Что опять не так? Указатель нужен?
Да, нужен. смотрите еще раз:
Код
    eeprom_write_byte((uint8_t *)0x0A, mode);

А теперь смотрите на свой код:
Код
mode = eeprom_read_byte (0x0A);
Видите разницу около 0x0A?

Цитата(smk @ Dec 16 2014, 21:31) *
Ну вот... О1 и уже не работает.
Естественно. Ваша функция delay не делает с точки зрения компилятора ничего полезного. Наоборот, она делает маленькую быструю программу большой и медленной. Оптимизатор выкинул ее нафиг.

Изучайте <util/delay.h>

Цитата(smk @ Dec 16 2014, 21:14) *
С кейлом таких чудес небыло ни разу.
Там настолько плохой компилятор, что не умеет выкидывать пустые циклы?

Цитата(smk @ Dec 16 2014, 21:14) *
на тини отлично все работает. Версия компилятора не изменилась. В чем смысл тогда?
Откройте листинги, сравните, найдите разницу.
smk
так предлагаете unsigned char *0x0A или как? Читает и пишет правильно.

Код
mode = eeprom_read_byte (0x0A);


а на мой взгляд делей делает очень полезную штуку. как компилятору сказать?
smk
в смысле цикл тот
Код
void delay (unsigned long t)
{
  while(t--);
}


и еще, delay_ms... сразу дало большой довесок коду. включил оптимизацию, но это привело программу в нерабочий вид. хотя код ужало. в чем смысл?
Сергей Борщ
Цитата(smk @ Dec 16 2014, 23:47) *
в чем смысл?
В чтении документации. В ней все написано. И про большой код и про оптимизацию. И смею вас заверить - delay_ms работает именно так, как описано в документации. Что вы понимаете под нерабочим видом программы телепатировать не получается.
Bear_ku
Цитата(IgorKossak @ Dec 16 2014, 18:43) *
Но в любом случае следует избегать непереносимого кода и зависеть от трюков конкретной версии компилятора.
Переносимость меня абсолютно не волнует. А вот "зависеть от трюков" это видимо действительно было, примеры в ДШ даны для IAR и поэтому в IAR работают без нареканий.

Ну а по поводу того, что программа перестает работать при включении оптимизации - это нормально. Проблему можно легко вычислить воспользовавшись средствами отладки. Есть подозрение что виновата переменная consol, попробуйте в ее объявление добавить квалификатор volatile.
demiurg1978
Я делал один проект. Сначала взял ATMEGA8535. Проект писал в IAR. Когда прошил программу, все работало, кроме записи в EEPROM. Стал разбираться. Залил давний проект, написанный на асме. Запись в EEPROM работает. Стал сверять в дизасме. Вроде все нормально, а запись EEPROM не работает. Решил проверить эту же программу, но скомпилированную под ATMEGA32. Запись в EEPROM нормально работает. Выходит, что библиотеку записи в EEPROM нужно написать на асме. Но я в IAR так и не смог пока разобраться, как правильно писать и использовать функции на асме. Да и в тот раз обошелся ATMEGA32A. И некогда мне было с асмом в IAR разбираться.
Еще один случай. Умер один человек, мне достались его проекты. Написаны в IAR. Его девайсы сделаны на ATMEGA1280. Для сохранения данных использовались внешние EEPROM. То есть, когда-то этот человек тоже испытывал трудности с записью в EEPROM в проектах, созданных в IAR, на некоторых МК AVR.
Сергей Борщ
Цитата(demiurg1978 @ Dec 17 2014, 09:17) *
Выходит, что библиотеку записи в EEPROM нужно написать на асме.
Я порой вам удивляюсь. ИАР еще пятнадцать лет назад имел квалификатор __eeprom. Достаточно было прочитать документацию, объявить переменную с таким квалификатором и просто читать-писать ее как обычную переменную. Компилятор подставлял доступ к eeprom куда нужно сам и этот доступ всегда 100% работал. Вот какой смысл было изобретать велосипед и реализовывать свои процедуры чтения/записи? Причем судя по вашему описанию велосипед-то получился с квадратными колесами.
Xenia
Хочу напомнить, что у IAR EWAVR в хидере intrinsics.h (он ко всем типам МК относится) определены следующие макросы:
Код
#define __EEPUT(ADR,VAL)  {while (EECR & 0x02); EEAR = (ADR); EEDR = (VAL); EECR = 0x04; EECR = 0x02;}
#define __EEGET(VAR, ADR) {while (EECR & 0x02); EEAR = (ADR); EECR = 0x01; (VAR) = EEDR;}

С одной стороны полезно на это посмотреть, а то и позаимствовать.
С другой стороны, когда используется именно этот компилятор, можно этими макросами пользоваться.
demiurg1978
Цитата(Сергей Борщ @ Dec 17 2014, 14:00) *
Я порой вам удивляюсь. ИАР еще пятнадцать лет назад имел квалификатор __eeprom. Достаточно было прочитать документацию, объявить переменную с таким квалификатором и просто читать-писать ее как обычную переменную. Компилятор подставлял доступ к eeprom куда нужно сам и этот доступ всегда 100% работал. Вот какой смысл было изобретать велосипед и реализовывать свои процедуры чтения/записи? Причем судя по вашему описанию велосипед-то получился с квадратными колесами.

Сергей, вы заранее не исходите из ваших предположений, ладно?
Все на месте, один и тот же проект, на ATMEGA8535 запись в EEPROM не работает, на ATMEGA32\A работает.
Код
__eeprom u08 ee_empty_val = 0;
__eeprom u16 ee_tim_heat_val = 12;
__eeprom u16 ee_tim_formovka_val = 12;
__eeprom u16 ee_tim_pnevmosyem_val = 6;

Этот случай давно был. На сахаре мне тогда ответили, что, возможно, придется писать свою библиотеку для работы с EEPROM на асме.
Xenia
Цитата(Сергей Борщ @ Dec 17 2014, 11:00) *
Я порой вам удивляюсь. ИАР еще пятнадцать лет назад имел квалификатор __eeprom. Достаточно было прочитать документацию, объявить переменную с таким квалификатором и просто читать-писать ее как обычную переменную. Компилятор подставлял доступ к eeprom куда нужно сам и этот доступ всегда 100% работал. Вот какой смысл было изобретать велосипед и реализовывать свои процедуры чтения/записи? Причем судя по вашему описанию велосипед-то получился с квадратными колесами.


Проблема именно в том, что у топикстартера компилятор не IAR, а GCC:
Цитата(smk @ Dec 16 2014, 11:56) *
Компилятор GCC.

Однако те макросы, которые я привела из IAR, легко вживляемы в GCC.
А вот __eeprom приживить едва ли будет возможно, т.к. это не просто определение типа, а в придачу встроенные возможности.
Сергей Борщ
Цитата(demiurg1978 @ Dec 17 2014, 10:18) *
Сергей, вы заранее не исходите из ваших предположений, ладно?
Все на месте, один и тот же проект, на ATMEGA8535 запись в EEPROM не работает, на ATMEGA32\A работает.
Видимо недопонял. Получается, была ошибка в компиляторе?

Цитата(Xenia @ Dec 17 2014, 10:22) *
Проблема именно в том, что у топикстартера компилятор не IAR, а GCC:
Ксения, с топикстартером уже давно разобрались и перешли к стадии "бойцы вспоминают минувшие дни". Практически дословно содержимое вашего макроса уже было в этой теме на прошлой странице. И там же было обсосано, почему так лучше не делать.
demiurg1978
Цитата(Сергей Борщ @ Dec 17 2014, 14:39) *
Видимо недопонял. Получается, была ошибка в компиляторе?

Получается, что так. Досконально я не разбирался. Включал разные степени оптимизации кода. Смотрел в дизасме. А так как проект тогда оброс некоторыми дополнительными функциями, и размер кода увеличился, и запись в EEPROM заработала на другом МК, мне тем более пришлось взять вместо ATMEGA8535, ATMEGA32A. Кстати, прикол в том, что запись в EEPROM работает на МК, выпущенных позднее ATMEGA8535.
Нужно уделить время, сесть и понять разницу между моим рабочим проектом на асме и дизасмом в IAR. Тогда не до этого было. Сейчас тоже со свободным временем не очень...
Для просмотра полной версии этой страницы, пожалуйста, пройдите по ссылке.
Invision Power Board © 2001-2025 Invision Power Services, Inc.