Проблема записи в EEPROM.

Обсуждаем контроллеры компании Atmel.
Ответить
Открыл глаза
Аватара пользователя
Сообщения: 47
Зарегистрирован: Вт авг 13, 2013 12:13:07

Сообщение mehanik_all »

Приветствую. Пытаюсь перенести кусок кода не своего с ATmega128 на ATmega2560, и возникает проблема с записью из SRAM в EEPROM, а конкретно записывает одну переменную из семи, и дальше зависает. AVR Studio 4.18, оптимизация O3/Os

Код: Выделить всё

#define F_CPU 8000000UL // Я использую кристалл 8 МГц

#include <avr/io.h>
#include <util/delay.h>
#include <avr/interrupt.h>
#include <avr/eeprom.h>

struct config_t {
  uint8_t cwl;				
  uint8_t cwh;				
  uint8_t tpsacold; 		
  uint8_t divider;			
  uint8_t alternate; 		
  uint8_t injpwm;			
  uint8_t injpwmt;
};

struct config_t config;
struct config_t config_ee EEMEM;


volatile uint8_t eeprom_store_idx;     /* счетчик обрабатываемых байт данных*/
volatile uint8_t eeprom_store_busy;    /* флаг, eeprom занято*/

/*
  Эта функция запускает процесс сохранение структуры конфигурации из sram в 
  eeprom и немедленно возвращает управление,позволяя обработчику прерывания 
  EEPROM продолжить работу по сохранению данных.
*/
uint8_t storeConfig(void) {

  if (eeprom_store_busy) {
    return -1; 				/* eeprom занято, подождите! */
  }

  eeprom_store_idx = 0;		/* обнулить счетчик обрабатываемых байт данных*/
  eeprom_store_busy = 1;	/* установить флаг, eeprom занято*/
  
  EECR |= _BV(EERIE);		/* разрешить прерывание от eeprom */
  return 0;
}


/*
  время выполнения этого прерывания может быть достаточно высоким в случае 
  самой плохой ситуации, это должно быть обязательно исследовано!
*/
SIGNAL(SIG_EEPROM_READY) {
  uint8_t *pe, *ps;
  uint8_t ebyte;

  pe = (uint8_t *)&config_ee + eeprom_store_idx; /* адрес данных в eeprom */
  ps = (uint8_t *)&config + eeprom_store_idx;    /* адрес данных в sram */
  ebyte = eeprom_read_byte(pe);

  /* перезаписывать данные в eeprom будем только если это необходимо */
  /* 
    сравним содержимое структур в sram и eeprom, выход из цикла 
	осуществляем по первому найденному различию в данных
  */	 
  while( (ebyte == *ps) && (eeprom_store_idx != (sizeof(struct config_t) - 1)) ) {
    eeprom_store_idx++;
    pe++;
    ps++;
    ebyte = eeprom_read_byte(pe);
  }

  /* записываем новое значение в eeprom */
  if (ebyte != *ps)
    eeprom_write_byte(pe, *ps);

  /*
    если последняя запись в структуре не достигнута переходим к поиску 
	следующего различия данных в sram и eeprom 
  */
  if (eeprom_store_idx == sizeof(struct config_t)-1) { 	/* запись закончена */
      EECR &= ~_BV(EERIE);     							/* запретить прерывание от eeprom */
      eeprom_store_busy = 0;							/* сбросить флаг, eeprom занято*/
      PORTB &= ~_BV(PB7); 							/* погасить светодиод */
  } else { 												/* переходим к следующему различию */
      eeprom_store_idx++;
      PORTB |= _BV(PB7); 							/* зажечь светодиод */
  /* 
    необходимо отметить, если светодиод горит более 5 сек следует снять питание 
	с контроллера иначе возможно повреждение eeprom
  */
  }
}


int main(void){

	DDRB |= _BV (PB7);		//установить как выход LED
	PORTB &=~ _BV (PB7);	//установить 0 LED

	config.cwl=50;			//0x32
	config.cwh=65;			//0x41
	config.tpsacold=155;	//0x9B
	config.divider=48;		//0x30
	config.alternate=4;		//0x04
	config.injpwm=94;		//0x5E
	config.injpwmt=223;		//0xDF

	uint8_t i=0;

	if (i==0)
	{
		eeprom_write_byte(0x11, config.injpwm);
		i++;
	}

	sei();

	while(1){
	
	storeConfig();

	}
}
В EEPROM записывает только переменную cwl, и переменную injpwm по указанному адресу, далее цикл в программе не продолжается.

:10000000 32000000000000FFFFFFFFFFFFFFFFFF C7
:10001000 FF5EFFFFFFFFFFFFFFFFFFFFFFFFFFFF 91

Для записи остальных переменных нужно шесть раз сбросить работу МК, нажатием на кнопку RESET.
Предполагаю что обработчик прерывания SIG_EEPROM_READY не генерируется постоянно. Либо сбрасывается EERIE где-то. Что я делаю не так? Где может быть ошибка?
Реклама
Мудрый кот
Сообщения: 1759
Зарегистрирован: Пт июн 01, 2018 07:28:45

Сообщение parovoZZ »

Я бы прогнал код в симуляторе студии и посмотрел, что к чему.

Обязательно учесть тот факт, что при выходе из прерывания, МК ОБЯЗАТЕЛЬНО исполняет одну следующую инструкцию программы (в данном случае функция main) и только потом снова входит в обработчик прерывания.

Ну и студию обновить до последней версии. Возможно, что что-то поменялось аппаратно у МК.
Реклама
Открыл глаза
Аватара пользователя
Сообщения: 47
Зарегистрирован: Вт авг 13, 2013 12:13:07

Сообщение mehanik_all »

Да, программа зависала в функции storeConfig() потому что перезапускалась через main. Но в симуляции ни разу не сработало прерывание SIG_EEPROM_READY, хотя в реале один раз запускается. Вынес функцию storeConfig() из main в прерывание таймера

Код: Выделить всё

ISR(TIMER0_OVF_vect)		//функция прерывания по переполнению Т0
{
storeConfig();
TIMSK0 &=~ _BV(TOIE0);
}
В симуляции выполняет как нужно функцию и уходит обратно в main в бесконечный цикл минуя прерывание епром.

Добавлено after 12 minutes 10 seconds:
В общем висит разрешение EERIE, но прерывание не происходит.
Мудрый кот
Сообщения: 1759
Зарегистрирован: Пт июн 01, 2018 07:28:45

Сообщение parovoZZ »

[uquote="mehanik_all",url="/forum/viewtopic.php?p=3959754#p3959754"]В общем висит разрешение EERIE, но прерывание не происходит.[/uquote]
Чтобы оно произошло, необходимо поднять бит EEPE. Как только этот бит обнулится, сработает прерывание EERIE.
Реклама
Эиком - электронные компоненты и радиодетали
Открыл глаза
Аватара пользователя
Сообщения: 47
Зарегистрирован: Вт авг 13, 2013 12:13:07

Сообщение mehanik_all »

EEPE сбрасывается после записи, а если записи до этого не было?
Реклама
Мудрый кот
Сообщения: 1759
Зарегистрирован: Пт июн 01, 2018 07:28:45

Сообщение parovoZZ »

[uquote="mehanik_all",url="/forum/viewtopic.php?p=3959916#p3959916"]EEPE сбрасывается после записи, а если записи до этого не было?[/uquote]

Значит всё: сушим вёсла. Прерывания по готовности контроллера EEPROM не будет.
В SPI та же проблема - флаг не встаёт, пока не запишешь в data регистр. Приходится писать, а только потом прерывание.
Реклама
Открыл глаза
Аватара пользователя
Сообщения: 47
Зарегистрирован: Вт авг 13, 2013 12:13:07

Сообщение mehanik_all »

Временно сделал прерывание через свободный таймер

Код: Выделить всё

#define F_CPU 8000000UL // Я использую кристалл 8 МГц

#include <avr/io.h>
#include <util/delay.h>
#include <avr/interrupt.h>
#include <avr/eeprom.h>

struct config_t {
  uint8_t cwl;            
  uint8_t cwh;            
  uint8_t tpsacold;       
  uint8_t divider;         
  uint8_t alternate;       
  uint8_t injpwm;         
  uint8_t injpwmt;
};

struct config_t config;
struct config_t config_ee EEMEM;

volatile uint8_t c=0;

volatile uint8_t eeprom_store_idx;     /* счетчик обрабатываемых байт данных*/
volatile uint8_t eeprom_store_busy;    /* флаг, eeprom занято*/

/* Эта функция запускает процесс сохранение структуры конфигурации из sram в 
  eeprom и немедленно возвращает управление,позволяя обработчику прерывания 
  TIMER0 продолжить работу по сохранению данных. */
uint8_t storeConfig(void) {

  if (eeprom_store_busy) {
    return -1;             /* eeprom занято, подождите! */
  }

  eeprom_store_idx = 0;      /* обнулить счетчик обрабатываемых байт данных*/
  eeprom_store_busy = 1;   /* установить флаг, eeprom занято*/
  
  TCCR0B |=  _BV(CS00);      /* запустить таймер без предделителя */
  TCNT0=240;				/* за 16 тактов до переполнения */
  return 0;
}

/* время выполнения этого прерывания может быть достаточно высоким в случае 
  самой плохой ситуации, это должно быть обязательно исследовано! */
ISR(TIMER0_OVF_vect) {
  uint8_t save_sreg=SREG;    /* сохранить регистр состояния */ 
  cli();					/* запретить прерывания */

  uint8_t *pe, *ps;
  uint8_t ebyte;

  pe = (uint8_t *)&config_ee + eeprom_store_idx; /* адрес данных в eeprom */
  ps = (uint8_t *)&config + eeprom_store_idx;    /* адрес данных в sram */
  ebyte = eeprom_read_byte(pe);

  /* перезаписывать данные в eeprom будем только если это необходимо */
  /* сравним содержимое структур в sram и eeprom, выход из цикла 
   осуществляем по первому найденному различию в данных */    
  while( (ebyte == *ps) && (eeprom_store_idx != (sizeof(struct config_t) - 1)) ) {
    eeprom_store_idx++;
    pe++;
    ps++;
    ebyte = eeprom_read_byte(pe);
  }

  /* записываем новое значение в eeprom */
  if (ebyte != *ps)
    eeprom_write_byte(pe, *ps);

  /* если последняя запись в структуре не достигнута переходим к поиску 
   следующего различия данных в sram и eeprom */
  if (eeprom_store_idx == sizeof(struct config_t)-1) {    /* запись закончена */
      TCCR0B &= ~_BV(CS00);                          /* выключить таймер */
      eeprom_store_busy = 0;                     /* сбросить флаг, eeprom занято*/
      PORTB &= ~_BV(PB7);                      /* погасить светодиод */
  } else {                                     /* переходим к следующему различию */
      eeprom_store_idx++;
      PORTB |= _BV(PB7);                      /* зажечь светодиод */
	  TCNT0=240;							/* обновить регистр счёта */
  /* необходимо отметить, если светодиод горит более 5 сек следует снять питание 
   с контроллера иначе возможно повреждение eeprom */
  }
  SREG=save_sreg;		/* восстановить регистр состояния */
}

int main(void){

	DDRB |= _BV (PB7);      //установить как выход LED
	PORTB &=~ _BV (PB7);   //установить 0 LED

	config.cwl=50;         //0x32
	config.cwh=65;         //0x41
	config.tpsacold=155;   //0x9B
	config.divider=48;      //0x30
	config.alternate=4;      //0x04
	config.injpwm=94;      //0x5E
	config.injpwmt=223;      //0xDF

	TIMSK0 |= _BV(TOIE0);	//разрешить прерывание по переполнению таймера Т0

	sei();

   while(1){

		if (c==0)
		{
		c=1;
		storeConfig();
		}
   }
}
Мудрый кот
Сообщения: 1759
Зарегистрирован: Пт июн 01, 2018 07:28:45

Сообщение parovoZZ »

Я бы в принципе пересмотрел концепцию (алгоритм) программы. Мы ж ведь так или иначе опрашиваем флаг занятости контроллера EEPROM (eeprom_store_busy). Так что мешает тогда всю работу делать в отдельной функции?
Открыл глаза
Аватара пользователя
Сообщения: 47
Зарегистрирован: Вт авг 13, 2013 12:13:07

Сообщение mehanik_all »

Весь алгоритм записи в память я завёл целиком в storeConfig(), проверку байт и саму запись объединил в цикл while. Вроде прокатило. Нужно будет проверить не словит ли глюка в процессе работы остальных прерываний. Вопрос в том как это работало в оригинальном устройстве? И почему оно вообще работало? Может компилятор не того, в оригинале вроде как через WinAVR компилили, но во всем коде из 25 файлов я только подправил названия регистров, и всё зафурычило как надо, кроме записи в епром.
Это не хвост, это антенна
Сообщения: 1480
Зарегистрирован: Ср июн 25, 2008 15:19:44

Сообщение Demiurg »

Вечером, если не забуду, выложу свой пример. Если забуду, не стесняйтесь постучать в личку. Сдача объекта, занят.
Контактная информация:
Ответить

Вернуться в «AVR»