2014 dxdy logo

Научный форум dxdy

Математика, Физика, Computer Science, Machine Learning, LaTeX, Механика и Техника, Химия,
Биология и Медицина, Экономика и Финансовая Математика, Гуманитарные науки




Начать новую тему Ответить на тему На страницу Пред.  1, 2, 3
 
 Re: Странное поведение fuses в atmega8a
Сообщение27.04.2025, 13:35 
Заслуженный участник


20/08/14
12141
Россия, Москва
Кстати совет про другой проц/МК - хорош: скажем среда ардуино поддерживает STM32F103, а это уже честный 32-битный проц, с быстрым доступом к памяти (вместо 3 такта на байт в AVR - это же мрак!), с DMA и на uart и на i2c, с частотой до 72МГц, аппаратным умножением и делением, бывают со встроенным аппаратным USB, по цене как avr меги. Правда от 5В не работает, и встроенной eeprom (для настроек) нет.

Хотя по моему и мега вполне справится с задачей если сделать аккуратно на прерываниях (приём по i2c и суммирование в массиве, uart можно оставить и как есть, без прерываний, но только в главном цикле). И даже кварца типа 3МГц хватит (а от 8МГц должно остаться 2/3 свободного времени).

 Профиль  
                  
 
 Re: Странное поведение fuses в atmega8a
Сообщение28.04.2025, 01:02 


15/12/22
239
Dmitriy40 в сообщении #1683916 писал(а):
Прерывание по I2C есть конечно: "18 0x011 TWI Two-wire Serial Interface".
Как им пользоваться смотрите описание работы TWI, например на стр.167 Figure 21-10 для записи данных в I2C, стр.171 Table 21-3 как реагировать на разные события при передаче. Со стр.173 раздел 21.6.3 о приёме, на стр.175 подробно статус и реакции. Все отсылки по текущему выложенному pdf
.

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

-- 28.04.2025, 01:22 --

Помнится, я тут спрашивал, почему minipro стирает fuses. Опытным путём разобрался. Пожалуй выложу здесь, а то про него в инете почти ничего не написано. Прошивать нужно так:
Код:
minipro -p ${MCU} -z -e -w ${TARGET}.hex -c code

опция -z просто проверяет все ли пины подключены, -e запрещает стирать чип (без неё то fuses и выставляются по дефолту), - w указывает что выполняется запись, ${TARGET}.hex - это файл прошивки, -c code указывает, что записывается область программ.

установленные fuses можно считать командой:
Код:
minipro -p ${MCU} -r fuses.conf -c config

если что то не устраивает, файл fuses.conf можно подправить как нужно, и записать командой:
Код:
minipro -p ${MCU} -z -u -e -w fuses.conf -c config

опция -u это разблокировка, с ней будто бы получается надёжнее, но у меня прошивалось и без неё.

ещё старание чипа (если он вдруг залочен):
Код:
minipro -p ${MCU} -E


* это всё для весьма популярного программатора TL866 под Linux.

-- 28.04.2025, 01:37 --

Dmitriy40 в сообщении #1683945 писал(а):
совет про другой проц

да, stm сейчас в моде, но я с ними не работал, а с avr опыт довольно большой, так что пока позволяют возможности,буду обходиться ими, если уж совсем никак - придётся изучать stm.

Dmitriy40 в сообщении #1683945 писал(а):
среда ардуино поддерживает STM32F103

честно говоря я скептически отношусь к этой среде, по моему от этой приблуды больше вреда чем пользы, абсолютно всё можно сделать без неё, гораздо и проще и лучше.

 Профиль  
                  
 
 Re: Странное поведение fuses в atmega8a
Сообщение28.04.2025, 22:25 


15/12/22
239
Dmitriy40
есть ещё вопрос, я тут обнаружил, что atmega8a у меня потребляет больше 10 мА, это вообще нормально по Вашему?

 Профиль  
                  
 
 Re: Странное поведение fuses в atmega8a
Сообщение28.04.2025, 23:47 
Заслуженный участник


20/08/14
12141
Россия, Москва
Missir в сообщении #1684243 писал(а):
я тут обнаружил, что atmega8a у меня потребляет больше 10 мА, это вообще нормально по Вашему?
Вот уж я точно без понятия. В pdf есть данные по току потребления на разных частотах, но это только тот ток, что утекает внутрь кристалла, к нему добавляется ток через выходные пины в лог.1 если они на что-то нагружены - а этого Вы не показывали.
По графику если 3.3В продолжить до 16МГц, будет около 6мА. Плюс включенный таймер и I2C (и UART), легко на 1-2мА потянет. Плюс это typical значения, максимально допустимые могут быть и вдвое больше. Так что в 10мА ничего особо странного нет, это не 50мА.

 Профиль  
                  
 
 Re: Странное поведение fuses в atmega8a
Сообщение29.04.2025, 03:13 


15/12/22
239
Dmitriy40
в общем я переписал код как положено, во внешнем прерывании толко старт i2c, остальное в прерывании TWI, посмотрел примеры в инете, вот что получилось

код: [ скачать ] [ спрятать ]
Используется синтаксис C

ISR(INT0_vect)
{    
    TWCR=(1<<TWINT) | (1<<TWSTA) | (1<<TWEN); // START
    send_uart('A');
}

ISR(TWI_vect)
{   static uchar n;
    static union {uchar byts[2]; short v;} num;
    switch(TWSR & 0xF8)
    {
       case 0x08: send_uart('B'); TWDR = GY521<<1; TWCR = (1<<TWINT) | (1<<TWEN); break;
       case 0x10: send_uart('C'); TWDR = (GY521<<1)|0x01; TWCR = (1<<TWINT) | (1<<TWEN); n=0; break;
       case 0x18: send_uart('D'); TWDR = 0x3B; TWCR = (1<<TWINT) | (1<<TWEN); break;
       case 0x28: send_uart('E'); TWCR=(1<<TWINT) | (1<<TWSTA) | (1<<TWEN); break;      // START
       case 0x40: send_uart('F'); if (n & 0x01)                                      // REC
                  { if (n == 13) TWCR = (1<<TWINT)|(1<<TWEN);           // END
                  else TWCR = (1<<TWINT)|(1<<TWEN)|(1<<TWEA);           // NO END
                  num.byts[0]=TWDR; vals[n>>1]=num.v;
                  }
                  else { TWCR = (1<<TWINT)|(1<<TWEN)|(1<<TWEA); num.byts[1]=TWDR; }
                  n++;  break;
       case 0x50: send_uart('G'); TWCR=(1<<TWINT) | (1<<TWEN) | (1<<TWSTO); d=1; break; // STOP i2c
    }
}

 

в принципе тот же самый алгоритм, буквы по uart отправляю для проверки,
приходит только буква А, внешнее прерывание срабатывает, а прерывание TWI - ни разу. Быть может Вы знаете в чём может быть дело?

 Профиль  
                  
 
 Re: Странное поведение fuses в atmega8a
Сообщение29.04.2025, 11:45 
Заслуженный участник


20/08/14
12141
Россия, Москва
Missir
Поставьте send_uart() сразу по входу в TWI_vect() - может оно срабатывает, только статус не попадает на указанные. А скорее вообще не срабатывает так как не разрешено, см. ниже.
Потом замените отсылку буквы на отсылку регистра TWSR без обработки дальше (только send_uart_str(sprintf("%02X,",TWSR)) и всё, выход).
Потом верните обработку статуса - должны увидеть как именно перещёлкиваются статусы.

Вы забыли разрешить прерывания от TWI (TWIE в TWCR), все записи в TWCR его обнуляют.
Кроме того, сброс флага прерывания TWINT в TWCR надо делать в начале обработчика - до реакции на TWSR, иначе от записи в TWDR может успеть возникнуть новое прерывание с другим статусом, которое Вы и сбросите последующей записью в TWCR (ну или писать в TWDR после записи в TWCR). Например командой TWCR|=1<<TWINT; в самом начале прерывания, а потом уже разбираться с TWSR.


Ещё hint: вызов обработчика довольно затратный (call+rjmp+reti это уже 9 тактов, плюс сохранение и восстановление регистров если компилятор это делает), потому если нужна максимальная скорость обмена по I2C за счёт всего остального, то можно обернуть весь код обработчика в цикл do { ... } while TWCR&(1<<TWINT); - так он будет непрерывно обрабатывать все возникающие прерывания если они успеют выставиться до выхода из обработчика (например сигналы stat и stop).

 Профиль  
                  
 
 Re: Странное поведение fuses в atmega8a
Сообщение29.04.2025, 18:21 


15/12/22
239
Dmitriy40
в силу своей ограниченности понял наверное не всё и сделал пока вот так:
код: [ скачать ] [ спрятать ]
Используется синтаксис C
ISR(INT0_vect)
{    
    send_uart('A');
    TWCR=(1<<TWIE) | (1<<TWINT) | (1<<TWSTA) | (1<<TWEN); // START
}

ISR(TWI_vect)
{   static uchar n;
    static union {uchar byts[2]; short v;} num;

    send_uart('q');
    switch(TWSR & 0xF8)
    {
       case 0x08: send_uart('B'); TWDR = GY521<<1; TWCR = (1<<TWIE) | (1<<TWINT) | (1<<TWEN); break;
       case 0x10: send_uart('C'); TWDR = (GY521<<1)|0x01; TWCR = (1<<TWIE) | (1<<TWINT) | (1<<TWEN); n=0; break;
       case 0x18: send_uart('D'); TWDR = 0x3B; TWCR = (1<<TWIE) | (1<<TWINT) | (1<<TWEN); break;
       case 0x28: send_uart('E'); TWCR = (1<<TWIE) | (1<<TWINT) | (1<<TWSTA) | (1<<TWEN); break;      // START
       case 0x40: send_uart('F'); if (n & 0x01)                                      // REC
                  { if (n == 13) TWCR = (1<<TWIE) | (1<<TWINT)|(1<<TWEN);           // END
                  else TWCR = (1<<TWIE) | (1<<TWINT)|(1<<TWEN)|(1<<TWEA);           // NO END
                  num.byts[0]=TWDR; vals[n>>1]=num.v;
                  }
                  else { TWCR = (1<<TWIE) | (1<<TWINT)|(1<<TWEN)|(1<<TWEA); num.byts[1]=TWDR; }
                  n++;  break;
       case 0x50: send_uart('G'); TWCR=(1<<TWINT) | (1<<TWEN) | (1<<TWSTO); d=1; break; // STOP i2c
    }
}
 


при включении по uart вывод такой:
Цитата:
AqBqDqEqCqFqG +000000 +000000 +0000A00 +000000 AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA......


т.е. правильно проходит весь цикл, но почему то всего 1 раз, потом прерывание TWI работать перестаёт, ну и числа почему то нулевые, хотя vals глобальный массив типа volatile

 Профиль  
                  
 
 Re: Странное поведение fuses в atmega8a
Сообщение29.04.2025, 20:24 
Заслуженный участник


20/08/14
12141
Россия, Москва
Missir
Оказывается я ошибся (спутал с UART, где запись/чтение в/из UDR инициирует передачу/приём), надо сначала обработать TWDR (прочитать или записать), а уже потом сбрасывать TWINT в TWCR. Соответственно чтения у Вас в неправильном порядке.

Далее, по 0x40 (это момент когда девайс ответил правильным ACK) надо запустить чтение байтов с выдачей ACK (или без ACK если осталось последний байт прочитать), но сами байты будут приходить по 0x50 кроме последнего (который без ACK), последний байт по 0x58 и там же выдавать stop.

Т.е. по идее должна быть такая последовательность событий:
даём start,
0x08 (ушёл start, посылаем адрес с битом записи),
0x18 (ушёл адрес, посылаем регистр 0x38),
0x28 (ушёл байт данных, посылаем repeat start),
0x10 (ушёл повторный старт, посылаем адрес с битом чтения),
0x40 (ушёл адрес, запускаем приём байтов с ACK),
12 раз 0x50 (пришёл байт с ACK, повторяем снова с ACK),
0x50 (пришёл 13-й байт с ACK, повторяем снова уже без ACK),
0x58 (пришёл 14-й байт без ACK, посылаем stop).
В итоге 13 байтов принимаются по 0x50, последний по 0x58.

Почему не запускается в следующие разы я не знаю.
Уберите приём вообще и выдавайте стоп сразу в 0x28 - будет ли перезапускаться по таймеру.

Hint: в union можно было забрать не v[], а сразу val[] (или как 7 слов или как 14 байтов) - и просто читать в него 14 байтов в нужном порядке.

 Профиль  
                  
 
 Re: Странное поведение fuses в atmega8a
Сообщение29.04.2025, 21:41 


15/12/22
239
Dmitriy40 в сообщении #1684381 писал(а):
Уберите приём вообще и выдавайте стоп сразу в 0x28 - будет ли перезапускаться по таймеру

представляете, теперь работает постоянно:
Цитата:
AqBqDqE +000000 +000000 +000000 +AqBqDqE000000

до стоп доходит как положено, видимо что то при считывании

-- 29.04.2025, 21:50 --

Dmitriy40
но прироста производительности я что то не вижу. Между внешними прерываниями основной цикл успевает вывести всего 3 числа из 7,
в то время как в варианте с while(!(TWCR & (1<<TWINT))) {}; всё выводится отлично (сейчас частота внешних прерываний 20 гц)

 Профиль  
                  
 
 Re: Странное поведение fuses в atmega8a
Сообщение30.04.2025, 00:13 
Заслуженный участник


20/08/14
12141
Россия, Москва
Missir в сообщении #1684393 писал(а):
в то время как в варианте с while(!(TWCR & (1<<TWINT))) {}; всё выводится отлично
И уверены что тогда не было потерь прерываний таймера? У Вас всего один символ выводился дольше чем прерывания были (9600/10<1кГц)! Я ведь предлагал замерить сколько реально проходит прерываний таймера за минуту (символов A в консоли).

 Профиль  
                  
 
 Re: Странное поведение fuses в atmega8a
Сообщение30.04.2025, 03:55 


15/12/22
239
Dmitriy40 в сообщении #1684406 писал(а):
И уверены что тогда не было потерь прерываний таймера?

По крайней мере такое впечатление. Я снизил частоту отправки данных slave до 20 гц, но и убрал усреднение на 256. Вывод: первая бува "А", затем подряд 7 чисел с адекватными значениями. Между ними нет букв "А", судя по этому, новые внешние прерывания за время вывода прийти не успевают. Ну и время там 0.02с, довольно приличное.
Тут же, как Вы сами видите, прерывания приходят раньше чем строка выводится полностью. Может это потому, что здесь буфер полностью не считывается и slave посылает прерывание раньше, чем должно было послать. Но это лишь догадка. Выяснится, когда данные начнут читаться.

 Профиль  
                  
 
 Re: Странное поведение fuses в atmega8a
Сообщение30.04.2025, 05:42 


15/12/22
239
Dmitriy40, благодаря вашим замечаниям разобрался со статусами, вот что получилось:
Цитата:
AqBqCqDqEqFqGqGqGqGqGqGqGqGqGqGqGqGqGqH -000073 AqBqCqDqEqFqGqGqGqGqGqGqGqGqGqGqGqGqGqH AqBqCqDqEqFqGqGqGqGqGqGqGqGqGqGqGqGqGqH +000672 AqBqCqDqEqFqGqGqGqGqGqGqGqGqGqGqGqGqGqH

т.е. порядок событий верный, но работает медленно, между прерываниями успевает вывести только 1 число,
потом я убрал из прерывания TWI вывод букв на uart, вывод стал такой:
Цитата:
A +000724 -000812 +016572 +003298A +000439 +000568 -000062

т.е. теперь всё успевает, выводимые значения адекватные, получается вывод на uart так тормозит?

сейчас буду прибавлять частоту slave и смотреть при какой перестанет успевать всё обрабатывать
вообще, я ожидаю что должно быть быстрее чем с while(!(TWCR & (1<<TWINT))) {}; ...

-- 30.04.2025, 06:30 --

В общем получается так,
старый обработчик:
код: [ скачать ] [ спрятать ]
Используется синтаксис C
static inline  uchar start_i2c(void)
{
  TWCR=(1<<TWINT) | (1<<TWSTA) | (1<<TWEN);                    // START i2c
  while (!(TWCR & (1<<TWINT))) {};
  return (((TWSR & 0xF8) == 0x08) || ((TWSR & 0xF8) == 0x10)); // i2c стартовал
}

static inline uchar rec_i2c(uchar next)
{
  if (next)
    TWCR = (1<<TWINT)|(1<<TWEN)|(1<<TWEA); // получение данных
  else
    TWCR = (1<<TWINT)|(1<<TWEN);

  while(!(TWCR & (1<<TWINT)));
  return TWDR;
}

static inline uchar send_i2c(uchar addr, uchar ask)
{
  TWDR = addr;                              // адрес ведомого устройства
  TWCR = (1<<TWINT) | (1<<TWEN);
  while(!(TWCR & (1<<TWINT))) {};           // ждём завершения передачи
  return ((TWSR & 0xF8) == ask);            // устройство откликнулось
}

ISR(INT0_vect)
{    
  union {uchar byts[2]; short v;} num;
  send_uart('A');
  if (start_i2c())
  {  if (send_i2c(GY521<<1, 0x18))   // адрес GY-521 на запись
       if (send_i2c(0x3B, 0x28))     // адрес первой ячейки памяти accel_xout_high
         if (start_i2c())
           if (send_i2c((GY521<<1)|0x01, 0x40)) // адрес GY-521 для чтения
           {  for (int i=0; i<6; i++)
              {num.byts[1]=rec_i2c(1); num.byts[0]=rec_i2c(1); write_uart(num.v);}
               num.byts[1]=rec_i2c(1); num.byts[0]=rec_i2c(0); write_uart(num.v);
               //send_uart('\r');              
           }
  TWCR=(1<<TWINT) | (1<<TWEN) | (1<<TWSTO);    // STOP i2c
  }
}
 


вывод такой:
Цитата:
A +000372 -001080 +016708 +003833 +000429 +000570 -000071 A +000288 -001064 +016600 +003831 +000438 +000569 -000068 A +000336 -001088 +016552 +003832 +000427 +000565 -000070

в общем ни одной лишней буквы "A" нет.

Новый обработчик со всеми рекомендациями:
код: [ скачать ] [ спрятать ]
Используется синтаксис C
ISR(INT0_vect)
{    
    send_uart('A');
    TWCR=(1<<TWIE) | (1<<TWINT) | (1<<TWSTA) | (1<<TWEN); // START
}

ISR(TWI_vect)
{   static uchar n;
    static union {uchar byts[2]; short v;} num;
    do
    {
      switch(TWSR & 0xF8)
      {
       case 0x08: TWDR = GY521<<1; TWCR = (1<<TWIE) | (1<<TWINT) | (1<<TWEN); break;
       case 0x10: TWDR = (GY521<<1)|0x01; TWCR = (1<<TWIE) | (1<<TWINT) | (1<<TWEN); break;
       case 0x18: TWDR = 0x3B; TWCR = (1<<TWIE) | (1<<TWINT) | (1<<TWEN); break;
       case 0x28: TWCR = (1<<TWIE) | (1<<TWINT) | (1<<TWSTA) | (1<<TWEN); break;   // START
       case 0x40: TWCR = (1<<TWIE) | (1<<TWINT)|(1<<TWEN)|(1<<TWEA); n=0; break;
       case 0x50: if (n & 0x01)
                  { num.byts[0]=TWDR; vals[n>>1]=num.v; TWCR = (1<<TWIE)|(1<<TWINT)|(1<<TWEN)|(1<<TWEA); } // NO END
                  else  { num.byts[1]=TWDR;
                          if (n==12) TWCR = (1<<TWIE)|(1<<TWINT)|(1<<TWEN);         // END
                          else TWCR = (1<<TWIE)|(1<<TWINT)|(1<<TWEN)|(1<<TWEA);     // NO END
                        }
                  n++;  break;
       case 0x58: num.byts[0]=TWDR; vals[6]=num.v; TWCR=(1<<TWINT)|(1<<TWEN)|(1<<TWSTO); d=1; break; // STOP i2c
      }
    } while (TWCR&(1<<TWINT));  
}
 


вывод у него такой:
Цитата:
A +000680 -000840 +016496 +003524A +000430 +000573 -000040 A +000704 -000784 +016548 +003527A +000435 +000570 -000035 A +000740 -000824 +016752 +003527A +000428 +000568 -000039 A

тут лишние "А" проскакивают, кстати, в выводе что я приводил выше, есть тоже лишние "A", просто я не заметил.

Частота slave одинаковая. Так что получается, что новый обработчик работаем по крайней мере в 2 раза медленнее старого, хотя и работает правильно.

Как Вы думаете Dmitriy40 в чём тут дело? ведь ожидался обратный эффект

 Профиль  
                  
 
 Re: Странное поведение fuses в atmega8a
Сообщение30.04.2025, 08:51 


15/12/22
239
Похоже сейчас всё упирается в uart, как я уже писал, у меня было 9600, сейчас переделал на 115200 и прибавил частоту slave до 200 гц,
вывод такой:
Цитата:
A +000388 -000912 +016504 +004023 +000403 A +000534 -000061

т.е. стало в 10 раз быстрее

 Профиль  
                  
 
 Re: Странное поведение fuses в atmega8a
Сообщение30.04.2025, 13:27 


15/12/22
239
Dmitriy40, пожалуйста посмотрите, нет ли каких ещё ошибок или подводных камней,
кажется учёл всё что можно, и пока даже работает:


код: [ скачать ] [ спрятать ]
Используется синтаксис C

#define GY521         0x68
#define START         0x08
#define RESTART       0x10
#define ADDR_WRITE    0x18
#define ADDR_MEMORY   0x28
#define ADDR_READ     0x40
#define NEXT_DATA     0x50
#define END_DATA      0x58

volatile uchar done=0;
volatile union {uchar byts[14]; short vals[7];} num;

............................................

ISR(INT0_vect)
{ TWCR=(1<<TWIE)|(1<<TWINT)|(1<<TWSTA)|(1<<TWEN); }  // START i2c

ISR(TWI_vect)
{   static uchar n;
    do
    { switch(TWSR & 0xF8)
      { case START:       TWDR = GY521<<1; TWCR = (1<<TWIE)|(1<<TWINT)|(1<<TWEN); break;            // SEND DEV ADDR
        case ADDR_WRITE:  TWDR = 0x3B; TWCR = (1<<TWIE)|(1<<TWINT)|(1<<TWEN); break;                // SEND MEM ADDR
        case ADDR_MEMORY: TWCR = (1<<TWIE)|(1<<TWINT)|(1<<TWSTA)|(1<<TWEN); break;                  // RESTART i2c
        case RESTART:     TWDR = (GY521<<1)|0x01; TWCR = (1<<TWIE)|(1<<TWINT)|(1<<TWEN); break;     // SEND DEV ADDR FOR READ        
        case ADDR_READ:   TWCR = (1<<TWIE)|(1<<TWINT)|(1<<TWEN)|(1<<TWEA); n=0; break;              // requesting data
        case NEXT_DATA:   num.byts[n^1]=TWDR;  
                          if (n==12) TWCR = (1<<TWIE)|(1<<TWINT)|(1<<TWEN);                         // PENULTIMATE BYTE
                          else TWCR = (1<<TWIE)|(1<<TWINT)|(1<<TWEN)|(1<<TWEA);          
                          n++;  break;
        case END_DATA:    num.byts[12]=TWDR; TWCR=(1<<TWINT)|(1<<TWEN)|(1<<TWSTO); done=1; break;   // LAST BYTE, STOP i2c
      }
    } while (TWCR&(1<<TWINT));  
}
 


-- 30.04.2025, 13:39 --

Меня смущает while (TWCR&(1<<TWINT)); после него ведь не нужно делать (1<<TWIE)|, т.к. нет выхода из прерывания. Это не вызовет каких либо ошибок?

 Профиль  
                  
Показать сообщения за:  Поле сортировки  
Начать новую тему Ответить на тему  [ Сообщений: 44 ]  На страницу Пред.  1, 2, 3

Модераторы: Karan, Toucan, PAV, maxal, Супермодераторы



Кто сейчас на конференции

Сейчас этот форум просматривают: нет зарегистрированных пользователей


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

Найти:
Powered by phpBB © 2000, 2002, 2005, 2007 phpBB Group