заливка прошивки (hex файл) в ардуино через UART с SD карты другой ардуинки

gonzales
Offline
Зарегистрирован: 13.07.2015

> невнимательно читаете документацию. Параметр STK_LOAD_ADDRESS указывается не в байтах, а в 16-битных словах:

Точно, упустил. Интересно, зачем они так сделали?

-NMi-
Онлайн
Зарегистрирован: 20.08.2018

Интересует тема, как можно залить прошивку в ардуино не через ISP, а через UART, по типу как это делается с помощью программатора USB-TTL, но используя не Arduino IDE, а другую Ардуино.

Если хватит "скилла" - изобрети собственный бутлоадер или "подсмотри" у других. Reset тебе не нужен, сделай сеть из ардуин и адресное обращение к ним и Boot то-же адресный. AVR БЕЗ Reset-а запросто "бутлоадится" , впринципе в датащах есть примеры и на С и на Asm. Так-шо, как грицца, фпуть)))))

slider
Offline
Зарегистрирован: 17.06.2014

все было сделано 7 лет назад, посмотрите здесь , как одна ардуинка  шьет другую через UART:

http://robocraft.ru/blog/arduino/784.html

https://github.com/osbock/Baldwisdom/tree/master/BootDrive

тож подпишусь на тему.  мало-ли пригодится.

// в виду наличия в своей схеме GPRS SIM800c , подумывал так : по типу web-сервера , можно перекинуть hex файл прошивки через GPRS   на  microSD главной ардуинкой.  Вторая ардуинка промини только для программирования, считывает этот файл программирует главную ардуину. В инете вроде находил , как можно зашить ардуину с помощью другой ардуины и microSD .

Как-то было  здесь обсуждение как ресетить ардуину , и вроде как самый простой и рабочий вариант - это чтоб она сама дергала своим пином, так не геморно и без софтовых ограничений, требуется только пин.
   Зашиваете в еепром в каждой ардуинке разный ID . Посылаете команду по rs485  нужной ардуинке заресетиться , и команду помолчать всем одну минутку, дальше шьете по rs485. 

//// вот еще проект , чтоб ардуина вообще одна сама себя прошивала , считывая прошивку из microSD
https://toster.ru/q/149309
https://github.com/thseiler/embedded/tree/master/avr/2boots

gonzales
Offline
Зарегистрирован: 13.07.2015

slider пишет:

все было сделано 7 лет назад, посмотрите здесь , как одна ардуинка  шьет другую через UART:

Где же ты раньше был, мил человек))))

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

Одного не понял в проекте BootDrive - там стандартный загрузчик используется?

gonzales
Offline
Зарегистрирован: 13.07.2015

Назрел еще один вопрос, при экспорте скетчей в hex ардуино ide генерит два файла, один например blink.ino.hex, а второй blink.ino.with_bootloader.hex

Логично предполагаю, что во втором варианте находится еще код загрузчика. Но если загрузчик уже залит в МК, а я буду прошивать его первым файлом начиная с нулевого адреса загрузчик же затирается, правильно? Как в таком случае поступить, с какого адреса начинать заливку пользовательской программы? Или брать файл с загрузчиком и не париться?

MaksVV
Offline
Зарегистрирован: 06.08.2015

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

а вы используйте трансиверы CAN вместо RS485. Будет всё тоже самое как на RS485, только не нужно ногу дёргать и при коллизиях аппаратного вреда не будет. только лучше mcp2551 

Алексей.
Алексей. аватар
Offline
Зарегистрирован: 02.02.2018

ua6em пишет:
Дело за малым перевести RS-485 в хардовый rs232
МАксим выпускает (с 2012-го) драйверы MAX13487 со встроенной логикой, про ногодрыг уже можно забыть. С одной стороны rs485 (A-B) с другой RX-TX ttl.  К rx-tx подключал пролифик2303 и без дерганий за всякие dtr-ы и rts-ы опрашивал эл.счетчики. Единственный недостаток – цена.

gonzales
Offline
Зарегистрирован: 13.07.2015

Алексей. пишет:

ua6em пишет:
Дело за малым перевести RS-485 в хардовый rs232
МАксим выпускает (с 2012-го) драйверы MAX13487 со встроенной логикой, про ногодрыг уже можно забыть. С одной стороны rs485 (A-B) с другой RX-TX ttl.  К rx-tx подключал пролифик2303 и без дерганий за всякие dtr-ы и rts-ы опрашивал эл.счетчики. Единственный недостаток – цена.

Да, интересно. Посмотрел схему подключния по даташиту, там установлена гальваническая развязка на tx и rx. Я так понимаю трансивер питается от 5 вольт и RO DI у него на 5В, и развязки для использования последовательного порта с напряжением -12 - +12В. то есть в варианте с ардуино это все не нужно, подаем RX на RO, TX на DI и 5В на RE и SHDN, так? 

gonzales
Offline
Зарегистрирован: 13.07.2015

MaksVV пишет:

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

а вы используйте трансиверы CAN вместо RS485. Будет всё тоже самое как на RS485, только не нужно ногу дёргать и при коллизиях аппаратного вреда не будет. только лучше mcp2551 

Так к нему же еще can контроллер надо типа mcp2515

Алексей.
Алексей. аватар
Offline
Зарегистрирован: 02.02.2018

gonzales пишет:
подаем RX на RO, TX на DI и 5В на RE и SHDN, так?
Именно так и делал только не к ардуине а к пролифику юсб-ттл, и поддтяжки делал для линий A-B и терминатор на них повесил. Но дорого, в чип-дипе по 60₽ в розницу.

MaksVV
Offline
Зарегистрирован: 06.08.2015

gonzales пишет:
Так к нему же еще can контроллер надо типа mcp2515

не обязательно. MCP2515 нужен для полноценного CANa. В вашем случае можно просто цеплять на rx tx . И будет эдакий недо CAN не до RS485. Т.е. по сути физическая линия будет CAN, а пользоваться будете как RS485, только без ноги прием передача. 

Проверено это работает. 

MaksVV
Offline
Зарегистрирован: 06.08.2015
gonzales
Offline
Зарегистрирован: 13.07.2015

Вот первая рабочая версия. Программа читает файл на SD и пишет его по обычному UART в пациента. Для понимания, что прошивка перезаписывается создал на SD в корне 3 файла и прошиваю каждым из них в зависимости от отправленного символа в Serial порт (a,s,d).   

#define SS_SD_PIN BUILTIN_SDCARD - это константа для teensy, для обчной ардуино обчно 4.

В программе нет проверки загруженной программы на корректность и проверок ответов optiboot, лишь OK неОК.

Serial.begin(500000); - скорость поставил побольше, чтобы не тормозила при выводе инфы в Serial

 

#include <SD.h>
#include <SPI.h>

#define STK_OK              0x10
#define STK_FAILED          0x11  // Not used
#define STK_UNKNOWN         0x12  // Not used
#define STK_NODEVICE        0x13  // Not used
#define STK_INSYNC          0x14  // ' '
#define STK_NOSYNC          0x15  // Not used
#define ADC_CHANNEL_ERROR   0x16  // Not used
#define ADC_MEASURE_OK      0x17  // Not used
#define PWM_CHANNEL_ERROR   0x18  // Not used
#define PWM_ADJUST_OK       0x19  // Not used
#define STK_CRC_EOP         0x20  // 'SPACE'
#define STK_GET_SYNC        0x30  // '0'
#define STK_GET_SIGN_ON     0x31  // '1'
#define STK_SET_PARAMETER   0x40  // '@'
#define STK_GET_PARAMETER   0x41  // 'A'
#define STK_SET_DEVICE      0x42  // 'B'
#define STK_SET_DEVICE_EXT  0x45  // 'E'
#define STK_ENTER_PROGMODE  0x50  // 'P'
#define STK_LEAVE_PROGMODE  0x51  // 'Q'
#define STK_CHIP_ERASE      0x52  // 'R'
#define STK_CHECK_AUTOINC   0x53  // 'S'
#define STK_LOAD_ADDRESS    0x55  // 'U'
#define STK_UNIVERSAL       0x56  // 'V'
#define STK_PROG_FLASH      0x60  // '`'
#define STK_PROG_DATA       0x61  // 'a'
#define STK_PROG_FUSE       0x62  // 'b'
#define STK_PROG_LOCK       0x63  // 'c'
#define STK_PROG_PAGE       0x64  // 'd'
#define STK_PROG_FUSE_EXT   0x65  // 'e'
#define STK_READ_FLASH      0x70  // 'p'
#define STK_READ_DATA       0x71  // 'q'
#define STK_READ_FUSE       0x72  // 'r'
#define STK_READ_LOCK       0x73  // 's'
#define STK_READ_PAGE       0x74  // 't'
#define STK_READ_SIGN       0x75  // 'u'
#define STK_READ_OSCCAL     0x76  // 'v'
#define STK_READ_FUSE_EXT   0x77  // 'w'
#define STK_READ_OSCCAL_EXT 0x78  // 'x'
#define STK_HW_VER          0x80
#define STK_SW_MAJOR        0x81
#define STK_SW_MINOR        0x82
#define STK_VTARGET         0x84
#define STK_VADJUST         0x85
#define STK_OSC_PSCALE      0x86
#define STK_OSC_CMATCH      0x87
#define STK_SCK_DURATION    0x89
#define STK_TOPCARD_DETECT  0x98

#define SS_SD_PIN BUILTIN_SDCARD      //Пин SD-карты
#define SWITCH_TO_SD digitalWrite(SS_SD_PIN,LOW); delay(2)
#define RESET_PIN 2

byte downloadstate = 0;
char inBuf[200];
byte tic = 0;
String Filename = "";

void setup() {
  Serial.begin(500000);
  while (!Serial) {
  }
  if (!SD.begin(SS_SD_PIN)) {
    Serial.println("SD failed!");
    return;
  }
  Serial.println("SD OK");
  Serial1.begin(115200);
  Serial.println("READY");
  Serial.println("Press a, s or d");
}

void loop() {
  if (downloadstate == 0) { //ждем нажатия а, s, d
    if (Serial.available()) {
      char inChar = (char)Serial.read();
      if (inChar == 'a') {
        Serial.println("WORKING");
        downloadstate = 1;
        Filename = "Blink13.hex";
      }
      else if (inChar == 's') {
        Serial.println("WORKING");
        downloadstate = 1;
        Filename = "Blink11.hex";
      }
      else if (inChar == 'd') {
        Serial.println("WORKING");
        downloadstate = 1;
        Filename = "Blink51.hex";
      }
    }
  }
  else if (downloadstate == 1) { //команда на перезагрузку
    pinMode(RESET_PIN, OUTPUT);
    digitalWrite(RESET_PIN, HIGH);
    delay(20);
    digitalWrite(RESET_PIN, LOW);
    Serial.println("REBOOT PROMINI");
    downloadstate = 2;
  }
  else if (downloadstate == 2) { //работа с загрузчиком
    if (OptibootWork()) {
      downloadstate = 0;
      Serial.println("FLASH OK");
    }
    else {
      Fail();
    }
    digitalWrite(RESET_PIN, HIGH);
  }
}

byte OptibootWork() {
  Serial.println("START FLASH");
  if (!WriteSTK_Sync()) {
    delay(200);
    if (!WriteSTK_Sync()) {
      delay(200);
      if (!WriteSTK_Sync()) {
        return 0;
      }
    }
  }
  Serial.println("STK_HW_VER");
  if (!WriteSTK_GetParamert(STK_HW_VER)) return 0;
  Serial.println("STK_SW_MAJOR");
  if (!WriteSTK_GetParamert(STK_SW_MAJOR)) return 0;
  Serial.println("STK_SW_MINOR");
  if (!WriteSTK_GetParamert(STK_SW_MINOR)) return 0;
  Serial.println("STK_TOPCARD_DETECT");
  if (!WriteSTK_GetParamert(STK_TOPCARD_DETECT)) return 0;
  Serial.println("STK_VTARGET");
  if (!WriteSTK_GetParamert(STK_VTARGET)) return 0;
  Serial.println("STK_VADJUST");
  if (!WriteSTK_GetParamert(STK_VADJUST)) return 0;
  Serial.println("STK_OSC_PSCALE");
  if (!WriteSTK_GetParamert(STK_OSC_PSCALE)) return 0;
  Serial.println("STK_OSC_CMATCH");
  if (!WriteSTK_GetParamert(STK_OSC_CMATCH)) return 0;
  Serial.println("STK_SCK_DURATION");
  if (!WriteSTK_GetParamert(STK_SCK_DURATION)) return 0;
  Serial.println("STK_SW_MAJOR");
  if (!WriteSTK_GetParamert(STK_SW_MAJOR)) return 0;
  Serial.println("STK_SW_MINOR");
  if (!WriteSTK_GetParamert(STK_SW_MINOR)) return 0;
  Serial.println("ENTER PROG MODE");
  if (!WriteSTK_EnterProgMode()) return 0;
  Serial.println("READ SIGN");
  if (!WriteSTK_ReadSign()) return 0;
  Serial.println("UPLOAD");
  if (!UploadFileFromSD(Filename)) return 0;
  Serial.println("LEAVE PROG MODE");
  if (!WriteSTK_LeaveProgMode()) return 0;
  return 1;
}

void Fail() {
  downloadstate = 0;
  Serial.println("FAIL TO FLASH");
}

byte WriteSTK_Sync() {
  Serial1.write(STK_GET_SYNC);
  Serial1.write(STK_CRC_EOP);
  if (!optibootRcv()) return 0;
  return 1;
}

byte WriteSTK_GetParamert(byte Paramert) {
  Serial1.write(STK_GET_PARAMETER);
  Serial1.write(Paramert);
  Serial1.write(STK_CRC_EOP);
  if (!optibootRcv()) return 0;
  return 1;
}

byte WriteSTK_SetDevice() {
  Serial1.write(STK_SET_DEVICE);
  Serial1.write(0x86);            //device code
  Serial1.write(0x00);            //revision
  Serial1.write(0x00);            //progtype: “0” – Both Parallel/High-voltage and Serial mode
  Serial1.write(0x01);            //parmode: “1” – Full parallel interface
  Serial1.write(0x01);            //polling: “1” – Polling may be used
  Serial1.write(0x01);            //selftimed: “1” – Self timed
  Serial1.write(0x01);            //lockbytes: Number of Lock bytes.
  Serial1.write(0x03);            //fusebytes: Number of Fuse bytes
  Serial1.write(0xff);            //flashpollval1
  Serial1.write(0xff);            //flashpollval2
  Serial1.write(0xff);            //eeprompollval1
  Serial1.write(0xff);            //eeprompollval2
  Serial1.write(0x00);            //pagesizehigh
  Serial1.write(0x80);            //pagesizelow
  Serial1.write(0x04);            //eepromsizehigh
  Serial1.write(0x00);            //eepromsizelow
  Serial1.write(0x00);            //flashsize4
  Serial1.write(0x00);            //flashsize3
  Serial1.write(0x80);            //flashsize2
  Serial1.write(0x00);            //flashsize1
  Serial1.write(STK_CRC_EOP);
  if (!optibootRcv()) return 0;
  return 1;
}

byte WriteSTK_SetDeviceExt() {
  Serial1.write(STK_SET_DEVICE_EXT);
  Serial1.write(0x05);            //commandsize: how many bytes follow
  Serial1.write(0x04);            //eeprompagesize: EEPROM page size in bytes.
  Serial1.write(0xd7);            //signalpagel:
  Serial1.write(0xc2);            //signalbs2:
  Serial1.write(0x00);            //ResetDisable: Defines whether a part has RSTDSBL Fuse
  Serial1.write(STK_CRC_EOP);
  if (!optibootRcv()) return 0;
  return 1;
}

byte WriteSTK_EnterProgMode() {
  Serial1.write(STK_ENTER_PROGMODE);
  Serial1.write(STK_CRC_EOP);
  if (!optibootRcv()) return 0;
  return 1;
}

byte WriteSTK_ReadSign() {
  Serial1.write(STK_READ_SIGN);
  Serial1.write(STK_CRC_EOP);
  if (!optibootRcv()) return 0;
  return 1;
}

byte WriteSTK_LoadAddress(unsigned int addr) {
  byte highbyte = addr & 0xff;
  byte lowbyte = (addr >> 8) & 0xff;
  Serial1.write(STK_LOAD_ADDRESS);
  Serial1.write(highbyte);
  Serial1.write(lowbyte);
  Serial1.write(STK_CRC_EOP);
  if (!optibootRcv()) return 0;
  return 1;
}

byte WriteSTK_LeaveProgMode() {
  Serial1.write(STK_LEAVE_PROGMODE);
  Serial1.write(STK_CRC_EOP);
  if (!optibootRcv()) return 0;
  return 1;
}

byte optibootRcv() {
  tic = 0;
  byte inCount;
  byte inByte;
  while (Serial1.available() < 2) {
    delay(1);
    tic++;
    if (tic > 100) {
      return 0;
    }
  }
  delay(50);
  inCount = 0;
  inBuf[inCount] = 0;
  while (Serial1.available()) {
    inByte = Serial1.read();
    //Serial.print(inByte, HEX);
    //Serial.print(" ");
    if (inCount < 199)
    {
      inBuf[inCount] = inByte;
      inCount++;
    }
  }
  //Serial.println("");

  if (inBuf[0] == STK_INSYNC && inBuf[inCount - 1] == STK_OK) {
    Serial.println("OK");
    return 1;
  }
  else return 0;
}


unsigned int HexToInt ( char buf[], byte n, byte m) {
  unsigned int y = 0;
  for (int i = 0; i < m; i++)
  {
    if (buf[n + i] - '0' > 16) {
      y = y + ((buf[n + i] - '0' - 7) << 4 * (m - 1 - i)) ;
    }
    if (buf[n + i] - '0' < 16) {
      y = y + ((buf[n + i] - '0') << 4 * (m - 1 - i));
    }
  }
  return y;
}


byte UploadFileFromSD (String filename) {
  const char *fn = filename.c_str();
  char header[9] = "00000000";
  char charbyte[3] = "00";
  //SWITCH_TO_SD;
  byte linecount = 0;
  byte blockcount = 0;
  if (!SD.exists(fn)) return 0;
  Serial.print("START TO FLASH FILE ");
  Serial.println(filename);

  File myFile = SD.open(fn);
  if (!myFile) return 0;

  if (!WriteSTK_LoadAddress(0)) return 0;
  Serial.println("LOAD ADDRESS");
  Serial1.write(STK_PROG_PAGE);
  Serial1.write(0x00);
  Serial1.write(0x80);
  Serial1.write(0x46);

  while (myFile.available()) {
    char c = myFile.read();
    if (c == ':') {
      linecount++;
      for (int i = 0; i < 8; i++) {
        header[i] = myFile.read();
      }
      unsigned int bytecount = HexToInt(header, 0, 2);
      unsigned int address = HexToInt(header, 2, 4);
      unsigned int comm = HexToInt(header, 6, 2);
      if (comm == 0) { //данные для записи
        for (int i = 0; i < bytecount; i++) {
          for (int j = 0; j < 2; j++) {
            charbyte[j] = myFile.read();
          }
          //Serial.print(charbyte);
          byte bytetowrite = HexToInt(charbyte, 0, 2);
          Serial1.write(bytetowrite);
        }
        if (bytecount < 16) {
          for (int i = bytecount; i < 16; i++) {
            //Serial.print("FF");
            Serial1.write(0xFF);
          }
        }
      }
      else if (comm == 1) { // последняя строка
        for (int i = linecount; i < 9; i++) {
          for (int j = 0; j < 16; j++) {
            //Serial.print("FF");
            Serial1.write(0xFF);
          }
        }
        Serial1.write(STK_CRC_EOP);
        Serial.println("END BLOCK");
        if (!optibootRcv()) return 0;
        myFile.close();
        Serial.println("ENF FILE");
        return 1;
      }
    }
    if (linecount == 8) {
      Serial.println("END BLOCK");
      Serial1.write(STK_CRC_EOP);
      if (!optibootRcv()) return 0;
      Serial.println("LOAD ADDRESS");
      linecount = 0;
      blockcount++;
      if (!WriteSTK_LoadAddress(blockcount * 64)) return 0;
      Serial1.write(STK_PROG_PAGE);
      Serial1.write(0x00);
      Serial1.write(0x80);
      Serial1.write(0x46);
    }
  }
}

Дальше самое интересное, прикрутить это все к рабочей системе.

gonzales
Offline
Зарегистрирован: 13.07.2015

Хмм, возникла проблема с резетом путем посылки команды и прижатия линии резета к нулю.

Подключил следующим образом, Reset притянут к +5 через 10кОм, пин 2 подключен к Reset через кондер 0,1мФ.

Логика следующая, кидаю команду с доктора на пациента, пациент отвечает доктору и перегружается (устанавливает на пине 2 - LOW), доктор получает ответ и пытается установить синхронизацию, но пациент не отвечает на синхронизацию, такое ощущение что на нем уже начинает работать основная программа. Для проверки в setup выставил 

Serial.begin(RS485SPEED);
  Serial.write(7);
  Serial.flush();

В мониторе вижу

WORKING
REBOOT PROMINI
START FLASH

no sync

no sync

no sync
7 
no sync

no sync

no sync

no sync

no sync

no sync

no sync

no sync

no sync

no sync

no sync

no sync

Видно, в какой момент пациент перегружается. Но вот почему он не заходит в бутлоадер не понятно. 

Вот программа пациента. Посмотрите пожалуйста, может я чего-то упускаю

#define RS485SPEED 115200 //скорость RS-485
#define LED1 13
#define RESET_PIN 2
#define DIR A5 
#define REBOOT  0x8
#define REBOOT_OK  0x9

unsigned long CurrentMillis;
byte flag = 0;

void setup() {
  // put your setup code here, to run once:
  pinMode(DIR, OUTPUT);
  digitalWrite(DIR, HIGH);  
  Serial.begin(RS485SPEED);
  Serial.write(7);
  Serial.flush();
  pinMode(LED1, OUTPUT);
  digitalWrite(LED1, HIGH);
  delay(7000);
  digitalWrite(LED1, LOW);
}

void loop() {
  if (Serial.available()) {
    byte inChar = Serial.read();
    if (inChar == REBOOT) {
      Serial.write(REBOOT_OK);
      Serial.flush();
      delay(100);
      pinMode(RESET_PIN, OUTPUT);
      digitalWrite(RESET_PIN, HIGH);
      delay(20);
      digitalWrite(RESET_PIN, LOW);
    }
  }
  if (flag == 0) {
    digitalWrite(LED1, HIGH);
    CurrentMillis = millis();
    flag = 1;
  }
  else if (flag == 1) {
    if (millis() - CurrentMillis > 1000) {
      digitalWrite(LED1, LOW);
      CurrentMillis = millis();
      flag = 2;
    }
  }
  else if (flag == 2) {
    if (millis() - CurrentMillis > 500) {
      flag = 0;
    }
  }
}

 

gonzales
Offline
Зарегистрирован: 13.07.2015

Проблема нашлась, но я пока не понимаю как ее решить. При прошивке программатором Usbasp походу затирается загрузчик, потому что после такой прошивке пациент уже не реагирует на команды доктора. Если же просто загрузить загрузчик, то все работает, но затирается программа. Отсюда вопрос, можно ли загрузить программу через Usbasp вместе с загрузчиком. Конечно есть обходной маневр, загрузчик записывать через ubsasp, а первичную прошивку уже лить через какой-нибудь USB-TTL программатор, но хотелось бы обойтись одним устройством.

ЕвгенийП
ЕвгенийП аватар
Онлайн
Зарегистрирован: 25.05.2015

gonzales пишет:

Отсюда вопрос, можно ли загрузить программу через Usbasp вместе с загрузчиком. 

А то!

В IDE в меню выбираете

В результате в папке скетча появятся два файла, типа таких

А теперь угадайте с трёх раз в каком из них есть загрузчик? Вот его программатором и заливайте.

gonzales
Offline
Зарегистрирован: 13.07.2015

Ну да, походу это единственный вариант. 

gonzales
Offline
Зарегистрирован: 13.07.2015

Нашел несколько недоработок в функции UploadFileFromSD

В hex файле могут оказывается быть вот такие истории

:10 6A00 00 69F4309729F41092BF031092BE0302C0 BC
:10 6A10 00 138212821093BD030093BC03DF91CF91 C8
:10 6A20 00 1F910F91089581E090E0F8940C941835 2F
:04 6A30 00 F894FFCF 08
:10 6A34 00 01DE10DE10010000000000C003800000 31
:10 6A44 00 000A0014001E00280032003C00460050 DA
:10 6A54 00 005A006400F000D200BB00A4008D0076 50
:10 6A64 00 005F00480031001A00000000000000F8 38

:10 6A74 00 10AF0F8611F7104C116411DF105E00BC CB
:10 6A84 00 007701000000004210AF0FDA0F89100B ED
:10 6A94 00 10E90FFD0F000000008911AF0F861185 6A
:10 6AA4 00 11891189118911000000008911AF0F86 25
:10 6AB4 00 11851170616765302E696E7075742E76 5C
:10 6AC4 00 616C3D0070616765302E696E7075742E 5F
:10 6AD4 00 7478743D220070616765302E696E7075 3C
:0C 6AE4 00 742E7478743D2230002E0000 E7
:00 0000 01 FF

то есть в середине блок записывается не полностью. 

Вот исправленная функция

byte UploadFileFromSD (String filename) {
  const char *fn = filename.c_str();
  char header[9] = "00000000";
  char charbyte[3] = "00";
  //SWITCH_TO_SD;
  byte linecount = 0;
  unsigned int globaladress = 0;
  unsigned int bytecount = 0;
  unsigned int address = 0;
  unsigned int comm = 0;
  if (!SD.exists(fn)) return 0;
  Serial.print("START TO FLASH FILE ");
  Serial.println(filename);

  File myFile = SD.open(fn);
  if (!myFile) return 0;

  if (!WriteSTK_LoadAddress(0)) return 0;
  Serial.println("LOAD ADDRESS 0");
  RS485.write(STK_PROG_PAGE);
  RS485.write(0x00);
  RS485.write(0x80);
  RS485.write(0x46);
  RS485.flush();


  while (myFile.available()) {
    char c = myFile.read();
    if (c == ':') {
      linecount++;
      for (int i = 0; i < 8; i++) {
        header[i] = myFile.read();
      }
      bytecount = HexToInt(header, 0, 2);
      address = HexToInt(header, 2, 4);
      comm = HexToInt(header, 6, 2);

      if (linecount == 9) {
        linecount = 1;
        Serial.println("END BLOCK");
        RS485.write(STK_CRC_EOP);
        RS485.flush();
        if (!optibootRcv()) return 0;
        if (!WriteSTK_Sync()) return 0;
        if (comm == 0) {
          Serial.print("LOAD ADDRESS ");
          float f = globaladress / 2;
          Serial.println(int(f));
          if (!WriteSTK_LoadAddress(int(f))) return 0;
          RS485.write(STK_PROG_PAGE);
          RS485.write(0x00);
          RS485.write(0x80);
          RS485.write(0x46);
        }
      }
      globaladress = address + bytecount;
      if (comm == 0) { //данные для записи
        for (int i = 0; i < bytecount; i++) {
          for (int j = 0; j < 2; j++) {
            charbyte[j] = myFile.read();
          }
          Serial.print(charbyte);
          byte bytetowrite = HexToInt(charbyte, 0, 2);
          RS485.write(bytetowrite);
          RS485.flush();
        }
        if (bytecount < 16) {
          for (int i = bytecount; i < 16; i++) {
            Serial.print("FF");
            RS485.write(0xFF);
            RS485.flush();
          }
        }
        Serial.println("");
      }
      else if (comm == 1) { // последняя строка
        Serial.print("LAST LINE ");
        Serial.println(linecount);
        if (linecount != 1) {
          for (int i = linecount; i < 9; i++) {
            for (int j = 0; j < 16; j++) {
              Serial.print("FF");
              RS485.write(0xFF);
            }
          }
          Serial.println("");
          RS485.write(STK_CRC_EOP);
          RS485.flush();
          if (!optibootRcv()) return 0;
        }
        Serial.println("LAST BLOCK");

        myFile.close();
        Serial.println("ENF FILE");
        return 1;
      }
    }
  }
}

Но есть и проблемы в целом. Мой скетч с самой программой занимает почти всю память МК, то есть достаточно большой. И я не могу заставить его прошиваться по RS-485. Да, я поставил трансиверы о которых шла речь выше (MAX13487) чтобы бутлоадер не переписывать. Маленькие скетчи шьются отлично до какого-то размера, а дальше как повезет. Причем через обычный UART (без RS485) все прошивается без проблем, то есть явно проблема из-за RS-485. Я пробывал собрать свой бутлоадер с пониженной скоростью прошивки (57600 и 9600), но становилось еще хуже, как будто на не родной скорости оптибуту плохо работается. 

Пока теряюсь, что можно предпринять.... Может кто чего подскажет

gonzales
Offline
Зарегистрирован: 13.07.2015

Все!!! Задачу решил, все отлично прошивается. 

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

#include <SD.h>
#include <SPI.h>

#define STK_OK              0x10
#define STK_FAILED          0x11  // Not used
#define STK_UNKNOWN         0x12  // Not used
#define STK_NODEVICE        0x13  // Not used
#define STK_INSYNC          0x14  // ' '
#define STK_NOSYNC          0x15  // Not used
#define ADC_CHANNEL_ERROR   0x16  // Not used
#define ADC_MEASURE_OK      0x17  // Not used
#define PWM_CHANNEL_ERROR   0x18  // Not used
#define PWM_ADJUST_OK       0x19  // Not used
#define STK_CRC_EOP         0x20  // 'SPACE'
#define STK_GET_SYNC        0x30  // '0'
#define STK_GET_SIGN_ON     0x31  // '1'
#define STK_SET_PARAMETER   0x40  // '@'
#define STK_GET_PARAMETER   0x41  // 'A'
#define STK_SET_DEVICE      0x42  // 'B'
#define STK_SET_DEVICE_EXT  0x45  // 'E'
#define STK_ENTER_PROGMODE  0x50  // 'P'
#define STK_LEAVE_PROGMODE  0x51  // 'Q'
#define STK_CHIP_ERASE      0x52  // 'R'
#define STK_CHECK_AUTOINC   0x53  // 'S'
#define STK_LOAD_ADDRESS    0x55  // 'U'
#define STK_UNIVERSAL       0x56  // 'V'
#define STK_PROG_FLASH      0x60  // '`'
#define STK_PROG_DATA       0x61  // 'a'
#define STK_PROG_FUSE       0x62  // 'b'
#define STK_PROG_LOCK       0x63  // 'c'
#define STK_PROG_PAGE       0x64  // 'd'
#define STK_PROG_FUSE_EXT   0x65  // 'e'
#define STK_READ_FLASH      0x70  // 'p'
#define STK_READ_DATA       0x71  // 'q'
#define STK_READ_FUSE       0x72  // 'r'
#define STK_READ_LOCK       0x73  // 's'
#define STK_READ_PAGE       0x74  // 't'
#define STK_READ_SIGN       0x75  // 'u'
#define STK_READ_OSCCAL     0x76  // 'v'
#define STK_READ_FUSE_EXT   0x77  // 'w'
#define STK_READ_OSCCAL_EXT 0x78  // 'x'
#define STK_HW_VER          0x80
#define STK_SW_MAJOR        0x81
#define STK_SW_MINOR        0x82
#define STK_VTARGET         0x84
#define STK_VADJUST         0x85
#define STK_OSC_PSCALE      0x86
#define STK_OSC_CMATCH      0x87
#define STK_SCK_DURATION    0x89
#define STK_TOPCARD_DETECT  0x98

#define PAGE_SIZE 128
#define MAX_TRIES 3

#define REBOOT  0x8
#define REBOOT_OK  0x9
#define DIR 3

#define SS_SD_PIN BUILTIN_SDCARD      //Пин SD-карты
#define SWITCH_TO_SD digitalWrite(SS_SD_PIN,LOW); delay(2) //Переключение на использование SD карты в случае нескольких устройств на SPI

HardwareSerial & RS485 = Serial3; //Изменить на нужный интерфейс

byte downloadstate = 0;
char inBuf[PAGE_SIZE];
byte buf[PAGE_SIZE];
byte tic = 0;
String Filename = "";
byte trycount = 0;

void setup() {
  Serial.begin(500000);
  while (!Serial) {
  }
  if (!SD.begin(SS_SD_PIN)) {
    Serial.println("SD failed!");
    return;
  }
  Serial.println("SD OK");
  RS485.begin(38400);
  pinMode(DIR, OUTPUT);
  digitalWrite(DIR, LOW);

  Serial.println("READY");
  Serial.println("Press a, s or d");
}

void loop() {
  if (downloadstate == 0) { //ждем нажатия а, s, d
    if (Serial.available()) {
      char inChar = (char)Serial.read();
      if (inChar == 'a') {
        Serial.println("WORKING");
        downloadstate = 1;
        Filename = "test1.hex";
      }
      else if (inChar == 's') {
        Serial.println("WORKING");
        downloadstate = 1;
        Filename = "test2.hex";
      }
      else if (inChar == 'd') {
        Serial.println("WORKING");
        downloadstate = 1;
        Filename = "Blink51.hex";
      }
    }
  }
  else if (downloadstate == 1) { //команда на перезагрузку
    digitalWrite(DIR, HIGH);
    delay(1);
    RS485.write(REBOOT);
    RS485.flush();
    digitalWrite(DIR, LOW);
    delay(1);
    downloadstate = 2;
  }
  else if (downloadstate == 2) { //работа с загрузчиком
    if (OptibootWork()) {
      downloadstate = 0;
      Serial.println("FLASH OK");
    }
    else {
      Fail();
    }
  }
}

byte OptibootWork() {
  while (RS485.available()) {
    RS485.read();
  }
  Serial.println("START FLASH");
  if (!WriteSTK_Sync()) return 0;
  Serial.println("STK_HW_VER");
  if (!WriteSTK_GetParamert(STK_HW_VER)) return 0;
  Serial.println("STK_SW_MAJOR");
  if (!WriteSTK_GetParamert(STK_SW_MAJOR)) return 0;
  Serial.println("STK_SW_MINOR");
  if (!WriteSTK_GetParamert(STK_SW_MINOR)) return 0;
  Serial.println("STK_TOPCARD_DETECT");
  if (!WriteSTK_GetParamert(STK_TOPCARD_DETECT)) return 0;
  Serial.println("STK_VTARGET");
  if (!WriteSTK_GetParamert(STK_VTARGET)) return 0;
  Serial.println("STK_VADJUST");
  if (!WriteSTK_GetParamert(STK_VADJUST)) return 0;
  Serial.println("STK_OSC_PSCALE");
  if (!WriteSTK_GetParamert(STK_OSC_PSCALE)) return 0;
  Serial.println("STK_OSC_CMATCH");
  if (!WriteSTK_GetParamert(STK_OSC_CMATCH)) return 0;
  Serial.println("STK_SCK_DURATION");
  if (!WriteSTK_GetParamert(STK_SCK_DURATION)) return 0;
  Serial.println("STK_SW_MAJOR");
  if (!WriteSTK_GetParamert(STK_SW_MAJOR)) return 0;
  Serial.println("STK_SW_MINOR");
  if (!WriteSTK_GetParamert(STK_SW_MINOR)) return 0;

  if (!WriteSTK_EnterProgMode()) return 0;
  if (!WriteSTK_ReadSign()) return 0;
  if (!UploadFileFromSD(Filename)) return 0;
  if (!WriteSTK_LeaveProgMode()) return 0;

  return 1;
}

void Fail() {
  downloadstate = 0;
  Serial.println("FAIL TO FLASH");
}

byte WriteSTK_Sync() {
  trycount = 0;
  digitalWrite(DIR, HIGH);
  delay(1);
  RS485.write(STK_GET_SYNC);
  RS485.write(STK_CRC_EOP);
  RS485.flush();
  digitalWrite(DIR, LOW);
  delay(1);
  if (!optibootRcv()) {
    delay(100);
    trycount++;
    Serial.println("ERROR SYNC");
    WriteSTK_Sync();
    if (trycount > MAX_TRIES) {
      return 0;
    }
  }
  return 1;
}


byte WriteSTK_GetParamert(byte Paramert) {
  Serial.println("GET PARAMETR");
  trycount = 0;
  digitalWrite(DIR, HIGH);
  delay(2);
  RS485.write(STK_GET_PARAMETER);
  RS485.write(Paramert);
  RS485.write(STK_CRC_EOP);
  RS485.flush();
  digitalWrite(DIR, LOW);
  delay(2);
  if (!optibootRcv()) {
    delay(100);
    trycount++;
    Serial.println("GET PARAMETR");
    WriteSTK_GetParamert(Paramert);
    if (trycount > MAX_TRIES) {
      return 0;
    }
  }
  return 1;
}

byte WriteSTK_SetDevice() {
  Serial.println("SET DEVICE");
  trycount = 0;
  digitalWrite(DIR, HIGH);
  delay(2);
  RS485.write(STK_SET_DEVICE);
  RS485.write(0x86);            //device code
  RS485.write(0x00);            //revision
  RS485.write(0x00);            //progtype: “0” – Both Parallel/High-voltage and Serial mode
  RS485.write(0x01);            //parmode: “1” – Full parallel interface
  RS485.write(0x01);            //polling: “1” – Polling may be used
  RS485.write(0x01);            //selftimed: “1” – Self timed
  RS485.write(0x01);            //lockbytes: Number of Lock bytes.
  RS485.write(0x03);            //fusebytes: Number of Fuse bytes
  RS485.write(0xff);            //flashpollval1
  RS485.write(0xff);            //flashpollval2
  RS485.write(0xff);            //eeprompollval1
  RS485.write(0xff);            //eeprompollval2
  RS485.write(0x00);            //pagesizehigh
  RS485.write(0x80);            //pagesizelow
  RS485.write(0x04);            //eepromsizehigh
  RS485.write(0x00);            //eepromsizelow
  RS485.write(0x00);            //flashsize4
  RS485.write(0x00);            //flashsize3
  RS485.write(0x80);            //flashsize2
  RS485.write(0x00);            //flashsize1
  RS485.write(STK_CRC_EOP);
  RS485.flush();
  digitalWrite(DIR, LOW);
  delay(2);
  if (!optibootRcv()) {
    delay(100);
    trycount++;
    Serial.println("SET DEVICE");
    WriteSTK_SetDevice();
    if (trycount > MAX_TRIES) {
      return 0;
    }
  }
  return 1;
}

byte WriteSTK_SetDeviceExt() {
  Serial.println("SET DEVICE EXT");
  trycount = 0;
  digitalWrite(DIR, HIGH);
  delay(2);
  RS485.write(STK_SET_DEVICE_EXT);
  RS485.write(0x05);            //commandsize: how many bytes follow
  RS485.write(0x04);            //eeprompagesize: EEPROM page size in bytes.
  RS485.write(0xd7);            //signalpagel:
  RS485.write(0xc2);            //signalbs2:
  RS485.write(0x00);            //ResetDisable: Defines whether a part has RSTDSBL Fuse
  RS485.write(STK_CRC_EOP);
  RS485.flush();
  digitalWrite(DIR, LOW);
  delay(2);
  if (!optibootRcv()) {
    delay(100);
    trycount++;
    Serial.println("SET DEVICE EXT");
    WriteSTK_SetDeviceExt();
    if (trycount > MAX_TRIES) {
      return 0;
    }
  }
  return 1;
}

byte WriteSTK_EnterProgMode() {
  Serial.println("ENTER PROG MODE");
  trycount = 0;
  digitalWrite(DIR, HIGH);
  delay(2);
  RS485.write(STK_ENTER_PROGMODE);
  RS485.write(STK_CRC_EOP);
  RS485.flush();
  digitalWrite(DIR, LOW);
  delay(2);
  if (!optibootRcv()) {
    delay(100);
    trycount++;
    Serial.println("ENTER PROG MODE");
    WriteSTK_EnterProgMode();
    if (trycount > MAX_TRIES) {
      return 0;
    }
  }
  return 1;
}

byte WriteSTK_ReadSign() {
  Serial.println("READ SIGN");
  trycount = 0;
  digitalWrite(DIR, HIGH);
  delay(2);
  RS485.write(STK_READ_SIGN);
  RS485.write(STK_CRC_EOP);
  RS485.flush();
  digitalWrite(DIR, LOW);
  delay(2);
  if (!optibootRcv()) {
    delay(100);
    trycount++;
    Serial.println("READ SIGN");
    WriteSTK_ReadSign();
    if (trycount > MAX_TRIES) {
      return 0;
    }
  }
  return 1;
}

byte WriteSTK_WritePage() {
  Serial.println("WRITE PAGE");
  trycount = 0;
  digitalWrite(DIR, HIGH);
  delay(2);
  RS485.write(STK_PROG_PAGE);
  RS485.write(0x00);
  RS485.write(0x80);
  RS485.write(0x46);
  for (int i = 0; i < PAGE_SIZE; i++) {
    RS485.write(buf[i]);
  }
  RS485.write(STK_CRC_EOP);
  RS485.flush();
  digitalWrite(DIR, LOW);
  delay(2);
  if (!optibootRcv()) {
    delay(100);
    trycount++;
    Serial.println("ERROR WRITE PAGE");
    WriteSTK_WritePage();
    if (trycount > MAX_TRIES) {
      return 0;
    }
  }
  return 1;
}

byte WriteSTK_LoadAddress(unsigned int addr) {
  Serial.print("LOAD ADDRESS ");
  Serial.println(addr);
  byte highbyte = addr & 0xff;
  byte lowbyte = (addr >> 8) & 0xff;
  trycount = 0;
  digitalWrite(DIR, HIGH);
  delay(2);
  RS485.write(STK_LOAD_ADDRESS);
  RS485.write(highbyte);
  RS485.write(lowbyte);
  RS485.write(STK_CRC_EOP);
  RS485.flush();
  digitalWrite(DIR, LOW);
  delay(2);
  if (!optibootRcv()) {
    delay(100);
    trycount++;
    Serial.println("ERROR LOAD ADDRESS");
    WriteSTK_LoadAddress(addr);
    if (trycount > MAX_TRIES) {
      return 0;
    }
  }
  return 1;
}

byte WriteSTK_LeaveProgMode() {
  Serial.print("LEAVE PROG MODE");
  trycount = 0;
  digitalWrite(DIR, HIGH);
  delay(2);
  RS485.write(STK_LEAVE_PROGMODE);
  RS485.write(STK_CRC_EOP);
  RS485.flush();
  digitalWrite(DIR, LOW);
  delay(2);
  if (!optibootRcv()) {
    delay(100);
    trycount++;
    Serial.println("ERROR LEAVE PROG MODE");
    WriteSTK_LeaveProgMode();
    if (trycount > MAX_TRIES) {
      return 0;
    }
  }
  return 1;
}

byte optibootRcv() {
  tic = 0;
  byte inCount;
  byte inByte;
  while (RS485.available() < 2) {
    delay(1);
    tic++;
    if (tic > 200) {
      return 0;
    }
  }
  inCount = 0;
  inBuf[inCount] = 0;
  while (RS485.available()) {
    inByte = RS485.read();
    //Serial.print(inByte, HEX);
    //Serial.print(" ");
    if (inCount < 199)
    {
      inBuf[inCount] = inByte;
      inCount++;
    }
  }
  //Serial.println("");
  if (inBuf[0] == STK_INSYNC && inBuf[inCount - 1] == STK_OK) {
    Serial.println("OK");
    return 1;
  }
  else return 0;
}


unsigned int HexToInt ( char buf[], byte n, byte m) {
  unsigned int y = 0;
  for (int i = 0; i < m; i++)
  {
    if (buf[n + i] - '0' > 16) {
      y = y + ((buf[n + i] - '0' - 7) << 4 * (m - 1 - i)) ;
    }
    if (buf[n + i] - '0' < 16) {
      y = y + ((buf[n + i] - '0') << 4 * (m - 1 - i));
    }
  }
  return y;
}


byte UploadFileFromSD (String filename) {
  const char *fn = filename.c_str();
  char header[9] = "00000000";
  char charbyte[3] = "00";
  //SWITCH_TO_SD;
  unsigned int bytecount = 0;
  unsigned int address = 0;
  unsigned int comm = 0;
  byte n = 0;
  unsigned int blockcount = 0;

  if (!SD.exists(fn)) return 0;
  Serial.print("START TO FLASH FILE ");
  Serial.println(filename);
  File myFile = SD.open(fn);
  if (!myFile) return 0;
  while (myFile.available()) {
    char c = myFile.read();

    if (c == ':') { //начальный символ
      for (int i = 0; i < 8; i++) {
        header[i] = myFile.read();
      }
      bytecount = HexToInt(header, 0, 2); //кол-во байт в строке
      address = HexToInt(header, 2, 4);   //адрес
      comm = HexToInt(header, 6, 2);      //команда 

      if (comm == 0) { //данные для записи
        for (int i = 0; i < bytecount; i++) {
          for (int j = 0; j < 2; j++) {
            charbyte[j] = myFile.read();
          }
          Serial.print(charbyte);
          byte bytetowrite = HexToInt(charbyte, 0, 2);
          buf[n] = bytetowrite;
          n++;
          if (n == PAGE_SIZE) { //блок заполнен
            if (!WriteSTK_LoadAddress(blockcount * 64)) return 0;
            if (!WriteSTK_WritePage()) return 0;
            blockcount++;
            n = 0;
          }
        }
        Serial.println("");
      }

      else if (comm == 1) { // последняя строка
        Serial.print("LAST LINE ");
        if (n != 0) {
          for (int j = n; j < PAGE_SIZE; j++) {
            Serial.print("FF");
            buf[j] = 0xFF;
          }
          if (!WriteSTK_LoadAddress(blockcount * 64)) return 0;
          if (!WriteSTK_WritePage()) return 0;
        }
        myFile.close();
        Serial.println("ENF FILE");
        return 1;
      }
    }
  }
}

И вот скетч пациента для теста

#define RS485SPEED 38400 //скорость RS-485
#define LED1 7
#define RESET_PIN 2
#define DIR A5
#define REBOOT  0x8
#define REBOOT_OK  0x9

unsigned long CurrentMillis;
byte flag = 0;

void setup() {
  // put your setup code here, to run once:
  pinMode(DIR, OUTPUT);
  digitalWrite(DIR, LOW);
  Serial.begin(RS485SPEED);
  digitalWrite(DIR, HIGH);
  delay(1);
  Serial.write(7);
  Serial.write(7);
  Serial.flush();
  digitalWrite(DIR, LOW);
  delay(1);
  pinMode(LED1, OUTPUT);
  digitalWrite(LED1, HIGH);
  delay(4000);
  digitalWrite(LED1, LOW);
}

void loop() {
  if (Serial.available()) {
    byte inChar = Serial.read();
    if (inChar == REBOOT) {
      delay(200);
      pinMode(RESET_PIN, OUTPUT);
      digitalWrite(RESET_PIN, HIGH);
      delay(1);
      digitalWrite(RESET_PIN, LOW);
      delay(1);
      digitalWrite(RESET_PIN, HIGH);
    }
  }
  if (flag == 0) {
    digitalWrite(LED1, HIGH);
    CurrentMillis = millis();
    flag = 1;
  }
  else if (flag == 1) {
    if (millis() - CurrentMillis > 1000) {
      digitalWrite(LED1, LOW);
      CurrentMillis = millis();
      flag = 2;
    }
  }
  else if (flag == 2) {
    if (millis() - CurrentMillis > 500) {
      flag = 0;
    }
  }
}

Теперь что касается загрузчика, пришлось повозиться, чтобы переделать загрузчик для использования стандартных 485 трансиверов. В результате удалось адаптировать последний optiboot под нужды RS485.

По ссылке можно его скачать https://yadi.sk/d/hStc67QUkG-UIQ

Но для того, что пользоваться нужно сделать следующее

1. Скачать архив с загрузчиком, распаковать папку optiboot в C:\Program Files (x86)\Arduino\hardware\arduino\avr\bootloaders

2. Скачать дистрибутив Arduino IDE ver 1.5.6 r2 beta (не старше), только не установщик, а архив. Вот ссылка https://www.arduino.cc/download_handler.php?f=/arduino-1.5.6-r2-windows.zip

3. Открыть архив и вытащить оттуда папку Utils (arduino-1.5.6-r2-windows.zip\arduino-1.5.6-r2\hardware\tools\avr\utils)

4. Положить ее в папку с ардуино C:\Program Files (x86)\Arduino\hardware\tools\avr\

5. Перейти в папку с загрузчиком C:\Program Files (x86)\Arduino\hardware\arduino\avr\bootloaders\optiboot

6. Запустить командную строку от имени админа. Для генерации загрузчика использовать команду

my atmega328 

Создастся файл optiboot_atmega328.hex - это и есть загрузчик

Возможные команды BAUD_RATE=скорость, SOFT_UART - используем софтовый uart, LED=D7 - какой пин использовать для светодиода, LED_START_FLASHES=2 - кол-во миганий светодиода при старте, LED_DATA_FLASH - мигание светодиода при загрузке программы, UART=0 - номер uart, если их несколько, RS485=C5 - пин для ногодрыга RS485. Например команда my atmega328 BAUD_RATE=38400 LED=D6 RS485=C5 создаст загрузчик, который будет работать на скорости 38400, при этом светодиод будет находиться на 6-ом пине, и нога для работы RS485 на пине А5.

7. Поправить board.txt (C:\Program Files (x86)\Arduino\hardware\arduino\avr) если необходимо, например выставить фьюзы и скорость прошивки

Вот моя секция


##############################################################
## Optiboot on 32pin (SMT) CPUs (Nano, Pro Micro, etc.)
##############################################################

optiboot32.name=Optiboot on 32-pin cpus

optiboot32.upload.tool=arduino:avrdude
optiboot32.upload.protocol=arduino
optiboot32.upload.speed=38400

optiboot32.bootloader.tool=arduino:avrdude
//optiboot32.bootloader.low_fuses=0xF7
optiboot32.bootloader.low_fuses=0xFF
optiboot32.bootloader.unlock_bits=0x3F
optiboot32.bootloader.lock_bits=0x2F
optiboot32.build.f_cpu=16000000L

#
# Other Clock speeds.
#  For 8MHz using the internal RC Oscillator, we adjust fuses, use the same
#  bootloader binary, and halve the upload rate.
#
optiboot32.menu.mhz.16MHz=16MHz
optiboot32.menu.mhz.16MHz.upload.speed=38400
optiboot32.menu.mhz.8MHz=8MHz (int)
optiboot32.menu.mhz.8MHz.build.f_cpu=8000000L
optiboot32.menu.mhz.8MHz.bootloader.low_fuses=0xE2
optiboot32.menu.mhz.8MHz.upload.speed=57600
optiboot32.menu.mhz.1MHz=1MHz (int)
optiboot32.menu.mhz.1MHz.build.f_cpu=1000000L
optiboot32.menu.mhz.1MHz.bootloader.low_fuses=0x62
optiboot32.menu.mhz.1MHz.upload.speed=9600

# optiboot platforms should be UNO-like more than anything else.
optiboot32.build.board=AVR_UNO
optiboot32.build.core=arduino:arduino
optiboot32.build.variant=arduino:eightanaloginputs


## Optiboot for ATmega328p
## ---------------------------------------------
optiboot32.menu.cpu.atmega328p=ATmega328p
optiboot32.menu.cpu.atmega328p.upload.maximum_size=32256
optiboot32.menu.cpu.atmega328p.upload.maximum_data_size=2048

optiboot32.menu.cpu.atmega328p.bootloader.high_fuses=0xD6
optiboot32.menu.cpu.atmega328p.bootloader.extended_fuses=0x05
optiboot32.menu.cpu.atmega328p.bootloader.file=optiboot/optiboot_atmega328.hex

optiboot32.menu.cpu.atmega328p.build.mcu=atmega328p

## Optiboot for ATmega328
## ---------------------------------------------
optiboot32.menu.cpu.atmega328=ATmega328
optiboot32.menu.cpu.atmega328.upload.maximum_size=32256
optiboot32.menu.cpu.atmega328.upload.maximum_data_size=2048

optiboot32.menu.cpu.atmega328.bootloader.high_fuses=0xDE
optiboot32.menu.cpu.atmega328.bootloader.extended_fuses=0x05
optiboot32.menu.cpu.atmega328.bootloader.file=optiboot/optiboot_atmega328.hex
# lie!  Arduino wise, these are compatible
optiboot32.menu.cpu.atmega328.build.mcu=atmega328p


## Optiboot ATmega168
## ---------------------------------------------
optiboot32.menu.cpu.atmega168=ATmega168

optiboot32.menu.cpu.atmega168.upload.maximum_size=15872
optiboot32.menu.cpu.atmega168.upload.maximum_data_size=1024

optiboot32.menu.cpu.atmega168.bootloader.high_fuses=0xDD
optiboot32.menu.cpu.atmega168.bootloader.extended_fuses=0xFC
optiboot32.menu.cpu.atmega168.bootloader.file=optiboot/optiboot_atmega168.hex

optiboot32.menu.cpu.atmega168.build.mcu=atmega168

## ---------------------------------------------
optiboot32.menu.cpu.atmega168p=ATmega168p

optiboot32.menu.cpu.atmega168p.upload.maximum_size=15872
optiboot32.menu.cpu.atmega168p.upload.maximum_data_size=1024

optiboot32.menu.cpu.atmega168p.bootloader.high_fuses=0xDD
optiboot32.menu.cpu.atmega168p.bootloader.extended_fuses=0xFC
optiboot32.menu.cpu.atmega168p.bootloader.file=optiboot/optiboot_atmega168.hex

optiboot32.menu.cpu.atmega168p.build.mcu=atmega168

Вот собственно все.