Проблемы с загрузкой кода в ATTiny84 с помощью Sparkfun AVR Pocket Programmer и ATTinyCore

Я возвращаюсь к программированию с помощью Arduino и построил спиннер от первого лица, вдохновленный инструкцией от MakersBox. Я использую карманный программатор SparkFun AVR и ATTiny84 (внутренняя частота 8 МГц) с ATTinyCore в качестве менеджера платы.

Несколько дней назад мне удалось безошибочно загрузить код с помощью программатора USBTinyISP (ATTinyCore) FAST для компонентов, работающих на частоте >= 2 МГц, но он перестал работать. Теперь отображается следующая ошибка:

avrdude: Version 6.3-20201216
         Copyright (c) 2000-2005 Brian Dean, http://www.bdmicro.com/
         Copyright (c) 2007-2014 Joerg Wunsch

         System wide configuration file is "C:\Users\Franklin Marquette\AppData\Local\Arduino15\packages\ATTinyCore\hardware\avr\1.5.2/avrdude.conf"

         Using Port                    : usb
         Using Programmer              : usbtiny
         Setting bit clk period        : 0.3
avrdude: usbdev_open(): Found USBtinyISP, bus:device: bus-0:\\.\libusb0-0001--0x1781-0x0c9f
         AVR Part                      : ATtiny84
         Chip Erase delay              : 15000 us
         PAGEL                         : P00
         BS2                           : P00
         RESET disposition             : possible i/o
         RETRY pulse                   : SCK
         serial program mode           : yes
         parallel program mode         : yes
         Timeout                       : 200
         StabDelay                     : 100
         CmdexeDelay                   : 25
         SyncLoops                     : 32
         ByteDelay                     : 0
         PollIndex                     : 3
         PollValue                     : 0x53
         Memory Detail                 :

                                  Block Poll               Page                       Polled
           Memory Type Mode Delay Size  Indx Paged  Size   Size #Pages MinW  MaxW   ReadBack
           ----------- ---- ----- ----- ---- ------ ------ ---- ------ ----- ----- ---------
           eeprom        65     6     4    0 no        512    4      0  4000  4500 0xff 0xff
           flash         65     6    32    0 yes      8192   64    128  4500  4500 0xff 0xff
           signature      0     0     0    0 no          3    0      0     0     0 0x00 0x00
           lock           0     0     0    0 no          1    0      0  9000  9000 0x00 0x00
           lfuse          0     0     0    0 no          1    0      0  9000  9000 0x00 0x00
           hfuse          0     0     0    0 no          1    0      0  9000  9000 0x00 0x00
           efuse          0     0     0    0 no          1    0      0  9000  9000 0x00 0x00
           calibration    0     0     0    0 no          1    0      0     0     0 0x00 0x00

         Programmer Type : USBtiny
         Description     : USBtiny simple USB programmer, http://www.ladyada.net/make/usbtinyisp/
avrdude: programmer operation not supported

avrdude: Setting SCK period to 1 usec
avrdude: initialization failed, rc=-1
         Double check connections and try again, or use -F to override
         this check.


avrdude done.  Thank you.

Failed programming: uploading error: exit status 1

СЕЙЧАС, если я загружаю с помощью "USBtinyISP (ATtinyCore) SLOW, для новых частей или частей с частотой 1 МГц" вариант программатора, код загружается. Но в 8 раз медленнее. Я загрузил скетч Blink в отдельный ATTiny84 на макетной плате, и подключенный светодиод оставался включенным в течение ~ 7,75 с, хотя он был запрограммирован на 1 с.

Я понятия не имею, что происходит, и был бы очень признателен за любую помощь, которую я могу получить. Спасибо!

Для справки код приведен ниже.

/*

Not all Attiny cores support the tone() function. Try this one
https://github.com/SpenceKonde/ATTinyCore

// АТМЕЛ ATTINY84 / АРДУИНО
//
// +-\/-+
// ВКК 1| |14 ЗАЗЕМЛЕНИЕ
// (D 0) PB0 2| |13 АРЕФ (Д 10)
// (D 1) PB1 3| |12 ПА1 (Д 9)
// ПБ3 4| |11 ПА2 (Д 8)
// ШИМ INT0 (D 2) PB2 5| |10 ПА3 (Д 7)
// ШИМ (D 3) PA7 6| |9 ПА4 (Д 6)
// ШИМ (D 4) PA6 7| |8 PA5 (D 5) ШИМ

*/

#include <EEPROMex.h>
#include <avr/pgmspace.h>
#include "font.h"
#include "textAndShapes.h"

const int charHeight = 8;
const int charWidth = 5;
int rows= 8;                // Всего светодиодов подряд
int LEDS[] = {0, 1, 2, 3, 4, 5, 7, 6};
bool STATES[] = {LOW, LOW, LOW, LOW, LOW, LOW, LOW, LOW};

char charArray[6];              // содержит символы для отображения
unsigned long lastTimeUs;                // время (нас) обнаружения магнита
unsigned long revTimeUs;                 // время (нас) последнего полного оборота
unsigned long dwellTimeUs;               // время (мкс) между сменами светодиодов (в зависимости от скорости вращения)
volatile unsigned long revolutions = 0;  // отслеживаем количество оборотов с момента последнего запуска
long totalRevolutions;          // отслеживание общего количества оборотов (хранится в EEPROM)
bool spinning = true;          // отслеживать сброс времени & считает
unsigned long startTimeUs;               // время (в мкс) начала текущего вращения
  
int mode;                       // текущий режим работы, хранится в EEPROM
int modes = 8;                  // количество доступных режимов
  // 0 -> текст "Привет, мир!"
  // 1 -> об/мин
  // 2 -> время в секундах
  // 3 -> количество вращений
  // 4 -> количество вращений (всего)
  // 5 -> "Лилли Пад"; шаблон
  // 6 -> форма 1 (сердце)
  // 7 -> форма 2 (улыбка)
  
byte ledPin = 0;     
byte magPin = 0;                // Датчик на эффекте Холла, подтянутый, переходит в низкий уровень, когда проходит магнит
byte magPullUp = 10;            // Контакт A0/D10
byte touchPin = 1;              // Нажать кнопку A1/D9
byte touchPullUp = 9;

volatile boolean rotationFlag = false;  // изменено ISR


void setup(){
  // настраиваем входы
  pinMode(touchPin, INPUT);
  digitalWrite(touchPullUp, HIGH);
  pinMode(magPin, INPUT);
  digitalWrite(magPullUp, HIGH);
  
  // устанавливаем другие светодиоды
  for(int LED=0; LED<(sizeof(LEDS)/sizeof(int)); LED++){
    pinMode(LEDS[LED], OUTPUT);
    digitalWrite(LEDS[LED], STATES[LED]);
  }

  // Настройка прерывания
  GIMSK  = bit (PCIE0);  // разрешение прерывания смены контакта
  PCMSK0 = bit (PCINT0); // Включить прерывание на PCINT0 (D10)
  sei();                 // разрешает прерывания

  // получить сохраненный режим из eeprom
  mode = EEPROM.read(0);
  if (mode >= modes) {
    mode = 0; // первый раз может быть очень большим
  }
  
  // получаем сохраненное количество оборотов из eeprom
  totalRevolutions = EEPROMReadlong(1);
  if (totalRevolutions < 0 || totalRevolutions > 999999UL){ // первый раз будет -1.
    totalRevolutions = 0;
    EEPROMWritelong(1, 0);
  }

  // показываем, что мы живы и какой режим активен
  blipLEDs();

  lastTimeUs = micros();
}


void loop(){
  int sigFigs = 6;               // количество значащих цифр для отображения
  unsigned long curTimeUs;
  int seconds;
  unsigned int rpm;
  
  checkButton();
  if (((micros() - lastTimeUs) > 1000000UL)){ // меньше 1 об/сек
    if (spinning){
      spinning = false;
      totalRevolutions = totalRevolutions + revolutions;
      EEPROMWritelong(1, totalRevolutions);
      revolutions = 0;
      clearArray();
      blipLEDs();
    }
    else {
      //digitalWrite(LEDS[mode], LOW);
    }
  }

  if (mode == 5){  // шаблон кувшинки, показать независимо от магнита.
    for(int LED=0; LED<(sizeof(LEDS)/sizeof(int)); LED++){
       digitalWrite(LEDS[LED], HIGH); 
       delay(1); 
       digitalWrite(LEDS[LED], LOW); 
    } 
    for(int LED=(sizeof(LEDS)/sizeof(int))-1; LED >= 0; LED--){
       digitalWrite(LEDS[LED], HIGH); 
       delay(1); 
       digitalWrite(LEDS[LED], LOW); 
    }     
  }

  else if (rotationFlag){ // мы крутимся!
    rotationFlag = false;
    if (!spinning){
      spinning = true;
      startTimeUs = micros(); 
    }
    curTimeUs = micros();
    revTimeUs = curTimeUs - lastTimeUs;
    dwellTimeUs = revTimeUs * 3UL / 360UL;   // 3 градуса
    seconds = (curTimeUs - startTimeUs) / 1000000UL;
    rpm = 60000 * 1000 / revTimeUs;
    lastTimeUs = curTimeUs;
    clearArray();    
    if (mode == 0){
      strcpy (charArray, text);
      //sprintf(charArray, "%lu", stayTimeUs);
    }
    else if (mode == 1){
      sprintf(charArray, "%d", rpm);
      sigFigs = 2;
    }
    else if (mode == 2){
      sprintf(charArray, "%d", seconds);
    }   
    else if (mode == 3){   
      sprintf(charArray, "%lu", revolutions);
    }
    else if (mode == 4){   
      sprintf(charArray, "%lu", totalRevolutions + revolutions);
    }
    else if (mode == 6){ // форма 1 (сердце)
      for(int k=0; k< sizeof(shape_1);k++){
        if (rotationFlag){
          break;
        }
        char b = pgm_read_byte_near(&(shape_1[k]));
        for (int j=0; j<charHeight; j++) {
          digitalWrite(LEDS[j], bitRead(b, 7-j));
        }
        dwellTimeUs = revTimeUs * 4UL / 360UL;   // 5 градусов
        delayMicroseconds(dwellTimeUs);
      }
    }    
    else if (mode == 7){ // форма 2 (улыбка)
      for(int k=0; k< sizeof(shape_2);k++){
        if (rotationFlag){
          break;
        }
        char b = pgm_read_byte_near(&(shape_2[k]));
        for (int j=0; j<charHeight; j++) {
          digitalWrite(LEDS[j], bitRead(b, 7-j));
        }
        dwellTimeUs = revTimeUs * 4UL / 360UL;   // 5 градусов
        delayMicroseconds(dwellTimeUs);
      }
    }

    // Текст в верхней половине
    if (mode < 5) { 
      int digits = 0;    
      for(int k=0; k< sizeof(charArray);k++){
        char c = charArray[k];
        if (rotationFlag){
          break;
        }
        if(c){
          if (digits < sigFigs){
            printLetter(c);
            //цифры += 1;
          }
          else{
            printLetter('0');
          }
          digits += 1;
        }
      }
      
      // Обработка отображения в нижней части
      clearArray();
      if(1 && (revTimeUs < 200000)){
        char * ptr = (char *) pgm_read_word (&string_table[mode]);
        //символьный буфер [6]; // должно быть достаточно большим!
        strcpy_P (charArray, ptr);
  
        // ждать его . . .
        while((micros() < (lastTimeUs + revTimeUs / 2)) && !rotationFlag){};
        
        // покажи это
        for (int k=sizeof(charArray)-1; k>=0; k--){
          if (rotationFlag){
            break;
          }
          printLetterLower(charArray[k]);;
        }
      }
    }
  }  
}


ISR(PCINT0_vect){  // обнаружен магнит
  if (!digitalRead(magPullUp)){
    rotationFlag = true;             // Увеличиваем изменчивые переменные
    revolutions += 1;
  }
}


void dwellDelay(){ // избегайте сбоев при первом вращении с ошибочным значением
  if (dwellTimeUs > 2000){
    dwellTimeUs = 2000;
  }
  if (dwellTimeUs < 100){
    dwellTimeUs = 100;
  }  
  delayMicroseconds(dwellTimeUs);
}


void printLetter(char ch){
// https://github.com/reger-men/Arduion-POV-clock/blob/master/clock.ino
  // убедитесь, что символ находится в пределах границ алфавита (определяется файлом font.h)
  // если это не так, сделать его пустым символом
  if (ch < 32 || ch > 126){
    ch = 32;
    }
  // вычесть символ пробела (преобразует число ASCII в номер индекса шрифта)
  ch -= 32;
  // пройтись по каждому байту массива символов
  for (int i=0; i<charWidth; i++) {
    char b = pgm_read_byte_near(&(font[ch][i]));
    
    for (int j=0; j<charHeight; j++) {
      digitalWrite(LEDS[j], bitRead(b, 7-j));
    }
    dwellDelay();
  }
  
  //очистить светодиоды
  for (int i = 0; i < rows; i++)
    digitalWrite(LEDS[i] , LOW);
  dwellDelay();
}


void printLetterLower(char ch){
  // убедитесь, что символ находится в пределах границ алфавита (определяется файлом font.h)
  // если это не так, сделать его пустым символом
  if (ch < 32 || ch > 126){
    ch = 32;
    }
  // вычесть символ пробела (преобразует число ASCII в номер индекса шрифта)
  ch -= 32;
  // пройтись по каждому байту массива символов
  for (int i=charWidth-1; i>-1; i--) {
    char b = pgm_read_byte_near(&(font[ch][i]));
    for (int j=0; j<charHeight; j++) {
      digitalWrite(LEDS[j+1], bitRead(b,j));
    }
    dwellDelay();
  }
  //очистить светодиоды
  for (int i = 0; i < rows; i++)
    digitalWrite(LEDS[i] , LOW);

  // пробел между буквами
  dwellDelay();
}  


bool touched(){
  // возвращает true, если коснулись, false, если нет. Светить светодиодом до тех пор, пока касание не будет отпущено
  bool touchVal = digitalRead(touchPullUp);
  if (!touchVal){
    while(!digitalRead(touchPullUp)){ // дождитесь отпускания касания
      delay(10);
      digitalWrite(LEDS[mode], LOW);
    }
    //цифровая запись(LEDS[0], LOW);
    return (true);
  }
  else{
    return (false);
  }
}


void checkButton(){
  // проверяем кнопку смены режима и отображаем текущий режим
  if (touched()){
    mode += 1;
    if (mode >= modes){
      mode = 0;
    }
    EEPROM.write(0, mode);
    blipLEDs();
  }
}


void blipLEDs(){
  // что-то, чтобы показать, что мы живы
  for(int LED=0; LED<(sizeof(LEDS)/sizeof(int)); LED++){
    digitalWrite(LEDS[LED], HIGH); 
    delay(10); 
    digitalWrite(LEDS[LED], LOW); 
  } 
  for(int LED=sizeof(LEDS)/sizeof(int); LED>mode; LED--){
    digitalWrite(LEDS[LED], HIGH); 
    delay(10); 
    digitalWrite(LEDS[LED], LOW); 
  } 
  digitalWrite(LEDS[mode], HIGH);
}



void EEPROMWritelong(int address, long value)  {
  //Эта функция запишет 4 байта (32 бита) в eeprom в
  // указанный адрес на адрес + 3.
  //https://playground.arduino.cc/Code/EEPROMReadWriteLong

  // Декомпозиция из длинного в 4 байта с использованием битового сдвига.
  //Один = самый значимый -> Четыре = младший значащий байт
  byte four = (value & 0xFF);
  byte three = ((value >> 8) & 0xFF);
  byte two = ((value >> 16) & 0xFF);
  byte one = ((value >> 24) & 0xFF);

  // Записываем 4 байта в память eeprom.
  EEPROM.write(address, four);
  EEPROM.write(address + 1, three);
  EEPROM.write(address + 2, two);
  EEPROM.write(address + 3, one);
}


long EEPROMReadlong(long address){
  //Прочитать 4 байта из памяти eeprom.
  long four = EEPROM.read(address);
  long three = EEPROM.read(address + 1);
  long two = EEPROM.read(address + 2);
  long one = EEPROM.read(address + 3);

  // Возвращаем перекомпонованную длинную с помощью битового сдвига.
  return ((four << 0) & 0xFF) + ((three << 8) & 0xFFFF) + ((two << 16) & 0xFFFFFF) + ((one << 24) & 0xFFFFFFFF);
}


void clearArray(){
  for(int i=0; i<sizeof(charArray); i++){      // очистить массив
    charArray[i] = 0;
  }
}

, 👍2

Обсуждение

Я, наверное, что-то упускаю. Я просто укажу, что выбор платы/опций задает ожидания IDE для компиляции кода и устанавливает конфигурацию, которую он запишет, если вы сделаете «запись загрузчика». Если вы сообщите IDE, что у вас конфигурация с частотой 8 МГц, когда на самом деле у вас конфигурация с частотой 1 МГц, ваши задержки будут в 8 раз больше, а ваши последовательные скорости — 1/8. Я не знаю, как это связано с невозможностью загрузки. Максимальная скорость интернет-провайдера составляет либо 1/4, либо 1/6 фактической тактовой частоты. Вы не можете использовать тактовую частоту ISP 1 МГц с чипом с тактовой частотой 1 МГц. Вы делаете это? без понятия., @timemage


1 ответ


Лучший ответ:

2

Когда вы впервые получаете один из этих чипов, он всегда внутренне настроен на понижение частоты внутреннего генератора на 8. Таким образом, он по-прежнему работает на физической частоте 8 МГц, но фактическая тактовая частота чипа составляет 1 МГц.

Если вы сообщите IDE, что это версия с частотой 8 МГц (делитель отключен), потребуется примерно в 8 раз больше времени для запуска любой задержки (именно поэтому вы видите импульс светодиода 7,75 с, а не 1 с).

Чтобы решить эту проблему, установите в IDE нужные параметры микросхемы (внутренний генератор 8 МГц*), затем нажмите "Записать загрузчик" На самом деле это не установит загрузчик, так как этот чип его не использует, но он установит внутренние предохранители, чтобы делитель тактовой частоты был выключен, и он работал на частоте 8 МГц, как и ожидалось.

Обратите внимание, что программатор по-прежнему должен быть установлен на низкую скорость (почему он говорит «МЕДЛЕННО, для новых [выделено мной] или частей с частотой 1 МГц»), когда вы делаете " Записать загрузчик" так как в противном случае чип будет работать слишком медленно на частоте 1 МГц, чтобы получать команды на скорости программатора по умолчанию (другая причина ошибки rc=-1, помимо плохой проводки).

Обратите внимание, что иногда программатор также не работает, если к какому-либо из контактов для программирования подключены светодиоды или переключатели. Вы удаляете чип перед программированием или он все еще подключен к схеме? Если второе, также попробуйте изолировать его перед программированием.

*Убедитесь, что вы не выбираете какие-либо параметры внешних часов, если вы не знаете, что делаете. Это приведет к поломке вашего чипа, если у вас нет внешнего источника тактового сигнала такого типа или высоковольтного программатора. Я не знаю, как они работают, но это особый (дорогой) вариант, в котором используется более высокое, чем обычно, напряжение для переопределения настроек предохранителя, даже если чип не синхронизируется.

,