Автономный программатор для AVR

Всё, на самом деле, ещё проще. Никакого этого колхоза не надо, просто при объявлении функции надо один символ добавить. Вместо

uint8_t writing_flash_from_file(String file_name)

написать

uint8_t writing_flash_from_file(String & file_name)

Я бы, конечно, ещё бы const добавил, но “хозяин-барин”.

Простите, но бред я не комментирую.

Вы имеете в виду в формате

Сами возвращайтесь. Оно мне надо? Можете назвать хоть одну причину для меня напрягаться?

Библиотекарей тебе не одолеть. Стрелковое оружие бесполезно :slight_smile:


На уно и про мини можно по блютуз скетчи писать, прям из ИДЕ, прям с ноутбука, метров с 10.

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

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

Я к Вам в голову не залезу, но учитывая Вашу активность на форуме (в том числе и на “старом”, где мы лет 10 назад пересекались), могу предположить что причиный может быть наставление заблудших инженеров на путь истинный и желание помочь. Вы же не просто так на форуме сидите и “этюды для начинающих” иногда пишите…

а “критикуешь - покажи как бы сделал сам” скорее для МММ предназначалась, в контексте “А в чем вообще проблема? Строка, например, 80 символов, буфер 10 символов - читаете строку в 8 приемов и все…”, без углубления в проблематику…

P.S.
В 2033 одолевал библиотекоря… весь боезапас положил… а потом второй пришел… :slight_smile:

Расскажи хоть, что там в 2025м будет…

2 лайка

:slight_smile:
Известно, что будет, раз в 33 с библиотекарем встреча.

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

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

Отклоняется.
Никому не интересно цитировать учебник.

Все уже давно придумано

Действительно, а зачем придумывать. Если предприятие, то оно обладает большими ресурсами. Им немного денег потратить на развитие не повредит. И программатор купить типа “TL866” цена вопроса 50 - 100 $. Хуже, когда предприятие гонит продукцию, а развитие как нибудь потом.

Оживим дискуссию.
Переписал в соответствии с рекомендации от сообщества.

Интересно что еще скажут…

(на .h и .c не делю осознано. это мой выбор. зачем и почему знаю, просто пока это не принципиально)

arduino_isf_flasher.ino

#include <Arduino.h>
#include <U8g2lib.h>
#include <FastLED.h>
#include <Versatile_RotaryEncoder.h>
#include <SPI.h>
#include "ARDUINOISP.h"
#include "ARDUINOISP_FILE.h"
#include <key.h>
#include <avr/wdt.h>

#include <SdFat.h>
SdFat SD;
File myFile;

#define NUM_LEDS 3
#define BRIGHTNESS 128
CRGB leds[NUM_LEDS];

digitalKey SD_CD(5);
digitalKey kill_key(6);

U8X8_ST7567_OS12864_4W_HW_SPI u8x8(/* cs=*/7, /* dc=*/9, /* reset=*/8);

// Set here your encoder reading pins
#define clk 2
#define dt 3
#define sw 4

// global variables for menu redraw and input event handling
uint8_t rotate_event = 0;      // 0 = not turning, 1 = CW, 2 = CCW
uint8_t press_event = 0;       // 0 = not pushed, 1 = pushed
uint8_t long_press_event = 0;  // 0 = not pushed, 1 = pushed

Versatile_RotaryEncoder versatile_encoder(clk, dt, sw);

int folder_name = 1;
uint8_t status = 0;
String str;

// Functions prototyping to be handled on each Encoder Event
void handleRotate(int8_t rotation) {
  if (rotation > 0)
    rotate_event = 2;  // CW
  else
    rotate_event = 1;  // CCW
}

void handlePressRelease() {
  press_event = 1;
}

void handleLongPress() {
  long_press_event = 1;
}

void open_info_file(String file_name) {
  u8x8.clear();
  Serial.print(F("open file "));
  Serial.print(file_name);
  Serial.println();
  myFile = SD.open(file_name);
  if (myFile) {
    while (myFile.available()) {
      char ch = myFile.read();
      u8x8.print(ch);
      Serial.print(ch);
    }
    u8x8.println();
    myFile.close();
    Serial.println();
    Serial.println(F("close readme.txt file"));
  } else {
    u8x8.println(F("error opening"));
  }
}

// Serial.println(F("---writing file---"));
// myFile.getName(buff, 32);
// for (int i = 0; i < 32; ++i) {
//   Serial.write(buff[i]);
// }
// Serial.println();
// Serial.println(F("checksum file error"));
// Serial.println(F("function write error"));
// Serial.println(F("open file error"));

void print_proress_bar(uint32_t current_line, uint32_t number_lines) {
  uint8_t n = (float(current_line) / float(number_lines)) * 13;
  u8x8.setCursor(0, 7);
  u8x8.print("                ");
  u8x8.setCursor(0, 7);
  for (int i = 0; i < 13; ++i) {
    if (i < n) u8x8.setInverseFont(1);
    else u8x8.setInverseFont(0);
    u8x8.print(F(" "));
  }
  u8x8.setInverseFont(0);

  u8x8.setCursor(13, 7);
  if (current_line * 100 / number_lines < 10) u8x8.print(F("0"));
  u8x8.print(current_line * 100 / number_lines);
  u8x8.print(F("%"));
}

void handle_events(void) {
  if (long_press_event == 1) {
    u8x8.clear();
    u8x8.println("-PROGRAMMING MC-");

    uint8_t error = 0;

    u8x8.print("connect");
    if (!start_pmode()) {
      u8x8.println(".......OK");

      u8x8.print("erase");
      chip_erase();
      u8x8.println(".........OK");

      u8x8.print("fuse");
      myFile = SD.open("/" + String(folder_name) + "/fuse.hex");
      if (writing_from_file(myFile, write_fuse_page)) u8x8.println(F("........SKIP"));
      else u8x8.println(F("..........OK"));
      myFile.close();

      u8x8.print("eeprom");
      myFile = SD.open("/" + String(folder_name) + "/eeprom.hex");
      if (writing_from_file(myFile, write_eeprom_page, print_proress_bar)) {
        u8x8.setCursor(6, 4);
        u8x8.println(F("......SKIP"));
      } else {
        u8x8.setCursor(6, 4);
        u8x8.println(F("........OK"));
      }
      myFile.close();

      u8x8.setCursor(0, 5);
      u8x8.print("flash");
      myFile = SD.open("/" + String(folder_name) + "/prog.hex");
      if (writing_from_file(myFile, write_flash_page, print_proress_bar)) {
        u8x8.setCursor(5, 5);
        u8x8.println(F(".......SKIP"));
      } else {
        u8x8.setCursor(5, 5);
        u8x8.println(F(".........OK"));
      }
      myFile.close();

      u8x8.print("check");
      myFile = SD.open("/" + String(folder_name) + "/prog.hex");
      switch (writing_from_file(myFile, verification_flash_page, print_proress_bar)) {
        case 0x13:
          u8x8.setCursor(5, 6);
          u8x8.println(F("......ERROR"));
          error = 1;
          break;
        case 0:
          u8x8.setCursor(5, 6);
          u8x8.println(F(".........OK"));
          break;
        default:
          u8x8.setCursor(5, 6);
          u8x8.println(F(".......SKIP"));
      }
    } else {
      u8x8.println("....ERROR");
      error = 1;
    }
    myFile.close();

    if (error) {
      leds[0] = CRGB::Red;
      leds[1] = CRGB::Red;
      leds[2] = CRGB::Red;
      FastLED.show();

      u8x8.setCursor(0, 7);
      u8x8.println(F("    -ERROR-     "));
    } else {
      leds[0] = CRGB::White;
      leds[1] = CRGB::Green;
      leds[2] = CRGB::Green;
      FastLED.show();

      u8x8.setCursor(0, 7);
      u8x8.println(F("   -COMPLITE-   "));
    }

    end_pmode();
    status = 1;
    long_press_event = 0;
  }

  // 0 = not pushed, 1 = pushed
  if (press_event == 1) {
    str = "/" + String(folder_name) + "/readme.txt";
    if (SD.exists(str)) {
      open_info_file(str);
    }
    press_event = 0;

    leds[0] = CRGB::White;
    leds[1] = CRGB::Green;
    leds[2] = CRGB::Green;
    FastLED.show();
    status = 0;
  }

  // 0 = not turning, 1 = CW, 2 = CCW
  if (rotate_event == 1) {
    if (status == 0) {
      str = "/" + String(folder_name + 1) + "/readme.txt";
      if (SD.exists(str)) {
        ++folder_name;
        open_info_file(str);
      }
    }
    rotate_event = 0;
  }

  if (rotate_event == 2) {
    if (status == 0) {
      str = "/" + String(folder_name - 1) + "/readme.txt";
      if (SD.exists(str)) {
        --folder_name;
        open_info_file(str);
      }
    }
    rotate_event = 0;
  }
}

void yield() {
  wdt_reset();
}

void setup(void) {
  Serial.begin(115200);
  Serial.println(F("START"));

  u8x8.begin();
  u8x8.setFlipMode(1);
  u8x8.setFont(u8x8_font_amstrad_cpc_extended_f);

  FastLED.addLeds<WS2811, A5, RGB>(leds, 3).setCorrection(TypicalLEDStrip);
  FastLED.setBrightness(BRIGHTNESS);

  leds[0] = CRGB::White;
  leds[1] = CRGB::Green;
  leds[2] = CRGB::Green;
  FastLED.show();

  versatile_encoder.setHandleRotate(handleRotate);
  versatile_encoder.setHandlePressRelease(handlePressRelease);
  versatile_encoder.setHandleLongPress(handleLongPress);

  Serial.print(F("Initializing SD card..."));
  u8x8.setCursor(2, 3);
  u8x8.println("Initializing");
  u8x8.setCursor(3, 4);
  u8x8.println("SD card...");

  if (SD_CD.readPin()) {
    if (!SD.begin(10)) {
      Serial.println(F("initialization failed!"));

      u8x8.clear();
      u8x8.setCursor(1, 3);
      u8x8.println(F("error SD card!"));

      leds[0] = CRGB::Red;
      leds[1] = CRGB::Red;
      leds[2] = CRGB::Red;
      FastLED.show();

      wdt_enable(WDTO_120MS);
      while (!SD_CD.readPush()) {
        wdt_reset();
      }
      while (1)
        ;
    }
    Serial.println(F("initialization done."));
    u8x8.println("done.");
  } else {
    Serial.println(F("no SD card!"));

    u8x8.clear();
    u8x8.setCursor(2, 3);
    u8x8.println(F("no SD card!"));

    leds[0] = CRGB::Red;
    leds[1] = CRGB::Red;
    leds[2] = CRGB::Red;
    FastLED.show();

    wdt_enable(WDTO_120MS);
    while (!SD_CD.readPush()) {
      wdt_reset();
    }
    while (1)
      ;
  }

  str = "/" + String(folder_name) + "/readme.txt";
  open_info_file(str);
}


void loop(void) {
  if (SD_CD.readPop(100)) {
    wdt_enable(WDTO_120MS);
    while (1)
      ;
  }

  if (kill_key.readPin()) {
    Serial.println(F("reset target"));
    pinMode(ARDUINOISP_PIN_RESET, OUTPUT);
    reset_target(true);
    delay(50);
    reset_target(false);
    pinMode(ARDUINOISP_PIN_RESET, INPUT);
  }

  versatile_encoder.ReadEncoder();  // Do the encoder reading and processing
  handle_events();
  wdt_reset();
}

ARDUINOISP_FILE.h

#ifndef ARDUINOISP_FILE_H
#define ARDUINOISP_FILE_H

#include "ARDUINOISP.h"
#include <SdFat.h>

uint8_t buff[32];

uint8_t char_to_nible(char n) {
  if (('0' <= n) && (n <= '9')) return uint8_t(n) - 48;
  else if (('A' <= n) && (n <= 'F')) return uint8_t(n) - 55;
  else return 0;
}

uint8_t read_byte(File &myFile) {
  return char_to_nible(myFile.read()) * 16 + char_to_nible(myFile.read());
}

uint8_t writing_from_file(File &myFile, uint8_t (*write_page)(uint8_t *, uint16_t, uint16_t), void (*callback_progress_file)(uint32_t, uint32_t) = nullptr) {
  uint8_t error = 0;
  uint8_t checksum = 0;
  if (myFile) {
    uint32_t file_position = 0;
    uint32_t file_size = myFile.size();
    while (myFile.available()) {
      char ch = myFile.read();
      if (ch == ':') {
        //читаем данные
        uint8_t RECLEN = read_byte(myFile);
        checksum += RECLEN;
        uint8_t LOAD_OFFSET_HIGH = read_byte(myFile);
        checksum += LOAD_OFFSET_HIGH;
        uint8_t LOAD_OFFSET_LOW = read_byte(myFile);
        checksum += LOAD_OFFSET_LOW;
        uint16_t LOAD_OFFSET = LOAD_OFFSET_HIGH * 256 + LOAD_OFFSET_LOW;
        uint8_t RECTYP = read_byte(myFile);
        checksum += RECTYP;
        for (int i = 0; i < RECLEN; ++i) {
          uint8_t DATA = read_byte(myFile);
          buff[i] = DATA;
          checksum += DATA;
        }
        uint8_t CHKSUM = read_byte(myFile);
        checksum += CHKSUM;

        //проверяем контрольную сумму строки
        if (checksum) {
          error = 0x22;
          break;
        }

        //пишем в МК
        if (RECTYP == 0x00) {
          error = write_page(buff, LOAD_OFFSET, RECLEN);
          if (error) {
            break;
          }
        }
      }
      file_position = myFile.position();
      if (callback_progress_file) callback_progress_file(file_position, file_size);
    }
  } else {
    error = 0x21;
  }
  return error;
}

#endif

ARDUINOISP.h

#ifndef ARDUINOISP_H
#define ARDUINOISP_H

#define ARDUINOISP_PIN_RESET A3
#define ARDUINOISP_PIN_MOSI A2
#define ARDUINOISP_PIN_MISO A1
#define ARDUINOISP_PIN_SCK A0

#define SPI_CLOCK_BB (1000000 / 6)

class BitBangedSPI {
public:
  void begin() {
    digitalWrite(ARDUINOISP_PIN_SCK, LOW);
    digitalWrite(ARDUINOISP_PIN_MOSI, LOW);
    pinMode(ARDUINOISP_PIN_SCK, OUTPUT);
    pinMode(ARDUINOISP_PIN_MOSI, OUTPUT);
    pinMode(ARDUINOISP_PIN_MISO, INPUT);
  }

  void beginTransaction(uint32_t clock) {
    pulseWidth = (500000 + clock - 1) / clock;
    if (pulseWidth == 0) {
      pulseWidth = 1;
    }
  }

  void end() {}

  uint8_t transfer(uint8_t b) {
    for (uint16_t i = 0; i < 8; ++i) {
      digitalWrite(ARDUINOISP_PIN_MOSI, (b & 0x80) ? HIGH : LOW);
      digitalWrite(ARDUINOISP_PIN_SCK, HIGH);
      delayMicroseconds(pulseWidth);
      b = (b << 1) | digitalRead(ARDUINOISP_PIN_MISO);
      digitalWrite(ARDUINOISP_PIN_SCK, LOW);  // slow pulse
      delayMicroseconds(pulseWidth);
    }
    return b;
  }

private:
  unsigned long pulseWidth;  // in microseconds
};
static BitBangedSPI SPI_BB;

uint8_t spi_transaction(uint8_t a, uint8_t b, uint8_t c, uint8_t d) {
  SPI_BB.transfer(a);
  SPI_BB.transfer(b);
  SPI_BB.transfer(c);
  return SPI_BB.transfer(d);
}

void isp_wait() {
  while (spi_transaction(0xF0, 0x00, 0x00, 0x00) & 0x01) {
    delay(1);
  }
}

void reset_target(bool reset) {
  digitalWrite(ARDUINOISP_PIN_RESET, reset ? LOW : HIGH);
}

uint8_t spi_enable_prog_mode() {
  SPI_BB.transfer(0xAC);
  SPI_BB.transfer(0x53);
  uint8_t result = !(SPI_BB.transfer(0x00) == 0x53);
  SPI_BB.transfer(0x00);
  return result;
}

uint8_t start_pmode() {
  reset_target(true);
  pinMode(ARDUINOISP_PIN_RESET, OUTPUT);
  SPI_BB.begin();
  SPI_BB.beginTransaction(SPI_CLOCK_BB);
  digitalWrite(ARDUINOISP_PIN_SCK, LOW);
  delay(20);
  reset_target(false);
  delayMicroseconds(100);
  reset_target(true);
  delay(50);
  return spi_enable_prog_mode();
}

void end_pmode() {
  SPI_BB.end();
  pinMode(ARDUINOISP_PIN_MOSI, INPUT);
  pinMode(ARDUINOISP_PIN_SCK, INPUT);
  reset_target(false);
  pinMode(ARDUINOISP_PIN_RESET, INPUT);
}

void write_flash(uint8_t hilo, uint16_t addr, uint8_t data) {
  spi_transaction(0x40 + 8 * hilo, addr >> 8 & 0xFF, addr & 0xFF, data);
}

void commit(uint16_t addr) {
  spi_transaction(0x4C, (addr >> 8) & 0xFF, addr & 0xFF, 0);
}

uint8_t write_flash_page(uint8_t *buff, uint16_t start, uint16_t length) {
  uint16_t here = start / 2;
  int x = 0;
  uint16_t page = here & 0xFFFFFFF0;
  while (x < length) {
    if (page != here & 0xFFFFFFF0) {
      commit(page);
      page = here & 0xFFFFFFF0;
      isp_wait();
    }
    write_flash(LOW, here, buff[x++]);
    write_flash(HIGH, here, buff[x++]);
    here++;
  }
  commit(page);
  isp_wait();
  return 0;
}

uint8_t flash_read(uint8_t hilo, uint16_t addr) {
  return spi_transaction(0x20 + hilo * 8, (addr >> 8) & 0xFF, addr & 0xFF, 0);
}

uint8_t verification_flash_page(uint8_t *buff, uint16_t start, uint16_t length) {
  uint16_t here = start / 2;
  int x = 0;
  while (x < length) {
    uint8_t low = flash_read(LOW, here);
    uint8_t high = flash_read(HIGH, here);
    here++;
    if (!((buff[x++] == low) && (buff[x++] == high))) return 0x13;
  }
  return 0;
}

uint8_t write_eeprom_page(uint8_t *buff, uint16_t start, uint16_t length) {
  for (uint16_t x = 0; x < length; x++) {
    uint16_t addr = start + x;
    spi_transaction(0xC0, (addr >> 8) & 0xFF, addr & 0xFF, buff[x]);
    isp_wait();
  }
  return 0;
}

uint8_t write_fuse_page(uint8_t *buff, uint16_t start, uint16_t length) {
  //low
  spi_transaction(0xAC, 0xA0, 0x00, buff[0]);
  isp_wait();
  //high
  spi_transaction(0xAC, 0xA8, 0x00, buff[1]);
  isp_wait();
  //extend
  spi_transaction(0xAC, 0xA4, 0x00, buff[2]);
  isp_wait();
  //lock
  spi_transaction(0xAC, 0xE0, 0x00, buff[3]);
  isp_wait();
  return 0;
}

void chip_erase() {
  spi_transaction(0xAC, 0x80, 0x00, 0x00);
  isp_wait();
};

//отладочные функции
void Serial_print_HEX(uint8_t n) {
  if (n <= 0x0F) Serial.print("0");
  Serial.print(n, HEX);
}

void flash_read_page(uint16_t start, int length) {
  uint16_t here = start / 2;
  for (int x = 0; x < length; x += 2) {
    uint8_t low = flash_read(LOW, here);
    Serial_print_HEX(low);
    Serial.print(" ");
    uint8_t high = flash_read(HIGH, here);
    Serial_print_HEX(high);
    Serial.print(" ");
    here++;
  }
  Serial.println();
}

void eeprom_read_page(uint16_t start, int length) {
  for (int x = 0; x < length; x++) {
    int addr = start + x;
    uint8_t ee = spi_transaction(0xA0, (addr >> 8) & 0xFF, addr & 0xFF, 0xFF);
    Serial_print_HEX(ee);
    Serial.print(" ");
  }
}

void read_signature() {
  Serial.print("signature: ");
  uint8_t high = spi_transaction(0x30, 0x00, 0x00, 0x00);
  Serial_print_HEX(high);
  Serial.print(" ");
  uint8_t middle = spi_transaction(0x30, 0x00, 0x01, 0x00);
  Serial_print_HEX(middle);
  Serial.print(" ");
  uint8_t low = spi_transaction(0x30, 0x00, 0x02, 0x00);
  Serial_print_HEX(low);
  Serial.println(" ");
}

void read_fuse() {
  Serial.print("fuse high: ");
  Serial_print_HEX(spi_transaction(0x58, 0x08, 0x00, 0x00));
  Serial.println();

  Serial.print("fuse low: ");
  Serial_print_HEX(spi_transaction(0x50, 0x00, 0x00, 0x00));
  Serial.println();

  Serial.print("extend: ");
  Serial_print_HEX(spi_transaction(0x50, 0x08, 0x00, 0x00));
  Serial.println();

  Serial.print("lock: ");
  Serial_print_HEX(spi_transaction(0x58, 0x00, 0x00, 0x00));
  Serial.println();
}

#endif

И вопрос на вскидку…

Может кому попадался протокол заливки прошивки через среду ардуины по UART ?
В гугле не забанили, просто найти не могу…

Утилита командной строки avrdude

протокол нужен, вернее его описание :slight_smile: утилиту я и сам знаю.

Заливка “через среду ардуины по UART” выполняется с помощью avrdude, а не напрямую с использованием каких-то протоколов :wink:

да штож ты какой неугомонный…

Нужна информация по протоколу обмена данными avrdude с загрузчиком ардуины (optiboot)

Как шить с ПК через avrdude я знаю :slight_smile:

Цель - с помощью ардуины прошить другую ардуину не прибегая к ПК.
Причина - через ISP это делается довольно медленно, через UART - значительно быстрее. Шил avrdude как через UART так и через ISP (USBasp).

Через UART быстрее. Нужен протокол обмена.

stk500.

Ну так выражайся правильно

См. сообщение #27. Если перейдешь на ютуб, там под роликом есть ссылка на прошивку

Спасиб.

Там ISP. это уже реализовано…