Как прошить новый Atmega328PB с помощью ArduinoIDE

Добавил в скетч ArduinoISP генерацию 4 МГц на выводе 3 Arduino (ножка 1 МК):

ArduinoISP + 4 MHz
// ArduinoISP
// Copyright (c) 2008-2011 Randall Bohn
// If you require a license, see
// https://opensource.org/licenses/bsd-license.php
//
// This sketch turns the Arduino into a AVRISP using the following Arduino pins:
//
// Pin 10 is used to reset the target microcontroller.
//
// By default, the hardware SPI pins MISO, MOSI and SCK are used to communicate
// with the target. On all Arduinos, these pins can be found
// on the ICSP/SPI header:
//
//               MISO °. . 5V (!) Avoid this pin on Due, Zero...
//               SCK   . . MOSI
//                     . . GND
//
// On some Arduinos (Uno,...), pins MOSI, MISO and SCK are the same pins as
// digital pin 11, 12 and 13, respectively. That is why many tutorials instruct
// you to hook up the target to these pins. If you find this wiring more
// practical, have a define USE_OLD_STYLE_WIRING. This will work even when not
// using an Uno. (On an Uno this is not needed).
//
// Alternatively you can use any other digital pin by configuring
// software ('BitBanged') SPI and having appropriate defines for PIN_MOSI,
// PIN_MISO and PIN_SCK.
//
// IMPORTANT: When using an Arduino that is not 5V tolerant (Due, Zero, ...) as
// the programmer, make sure to not expose any of the programmer's pins to 5V.
// A simple way to accomplish this is to power the complete system (programmer
// and target) at 3V3.
//
// Put an LED (with resistor) on the following pins:
// 9: Heartbeat   - shows the programmer is running
// 8: Error       - Lights up if something goes wrong (use red if that makes sense)
// 7: Programming - In communication with the slave
//

#include "Arduino.h"
#undef SERIAL


#define PROG_FLICKER true

// Configure SPI clock (in Hz).
// E.g. for an ATtiny @ 128 kHz: the datasheet states that both the high and low
// SPI clock pulse must be > 2 CPU cycles, so take 3 cycles i.e. divide target
// f_cpu by 6:
//     #define SPI_CLOCK            (128000/6)
//
// A clock slow enough for an ATtiny85 @ 1 MHz, is a reasonable default:

#define SPI_CLOCK 		(1000000/6)


// Select hardware or software SPI, depending on SPI clock.
// Currently only for AVR, for other architectures (Due, Zero,...), hardware SPI
// is probably too fast anyway.

#if defined(ARDUINO_ARCH_AVR)

#if SPI_CLOCK > (F_CPU / 128)
#define USE_HARDWARE_SPI
#endif

#endif

// Configure which pins to use:

// The standard pin configuration.
#ifndef ARDUINO_HOODLOADER2

#define RESET     10 // Use pin 10 to reset the target rather than SS
#define LED_HB    9
#define LED_ERR   8
#define LED_PMODE 7

// Uncomment following line to use the old Uno style wiring
// (using pin 11, 12 and 13 instead of the SPI header) on Leonardo, Due...

// #define USE_OLD_STYLE_WIRING

#ifdef USE_OLD_STYLE_WIRING

#define PIN_MOSI	11
#define PIN_MISO	12
#define PIN_SCK		13

#endif

// HOODLOADER2 means running sketches on the ATmega16U2 serial converter chips
// on Uno or Mega boards. We must use pins that are broken out:
#else

#define RESET     	4
#define LED_HB    	7
#define LED_ERR   	6
#define LED_PMODE 	5

#endif

// By default, use hardware SPI pins:
#ifndef PIN_MOSI
#define PIN_MOSI 	MOSI
#endif

#ifndef PIN_MISO
#define PIN_MISO 	MISO
#endif

#ifndef PIN_SCK
#define PIN_SCK 	SCK
#endif

// Force bitbanged SPI if not using the hardware SPI pins:
#if (PIN_MISO != MISO) ||  (PIN_MOSI != MOSI) || (PIN_SCK != SCK)
#undef USE_HARDWARE_SPI
#endif


// Configure the serial port to use.
//
// Prefer the USB virtual serial port (aka. native USB port), if the Arduino has one:
//   - it does not autoreset (except for the magic baud rate of 1200).
//   - it is more reliable because of USB handshaking.
//
// Leonardo and similar have an USB virtual serial port: 'Serial'.
// Due and Zero have an USB virtual serial port: 'SerialUSB'.
//
// On the Due and Zero, 'Serial' can be used too, provided you disable autoreset.
// To use 'Serial': #define SERIAL Serial

#ifdef SERIAL_PORT_USBVIRTUAL
#define SERIAL SERIAL_PORT_USBVIRTUAL
#else
#define SERIAL Serial
#endif


// Configure the baud rate:

#define BAUDRATE	19200
// #define BAUDRATE	115200
// #define BAUDRATE	1000000


#define HWVER 2
#define SWMAJ 1
#define SWMIN 18

// STK Definitions
#define STK_OK      0x10
#define STK_FAILED  0x11
#define STK_UNKNOWN 0x12
#define STK_INSYNC  0x14
#define STK_NOSYNC  0x15
#define CRC_EOP     0x20 //ok it is a space...

void pulse(int pin, int times);

#ifdef USE_HARDWARE_SPI
#include "SPI.h"
#else

#define SPI_MODE0 0x00

#if !defined(ARDUINO_API_VERSION) || ARDUINO_API_VERSION != 10001 // A SPISettings class is declared by ArduinoCore-API 1.0.1
class SPISettings {
    public:
        // clock is in Hz
        SPISettings(uint32_t clock, uint8_t bitOrder, uint8_t dataMode) : clockFreq(clock) {
            (void) bitOrder;
            (void) dataMode;
        };

        uint32_t getClockFreq() const {
            return clockFreq;
        }

    private:
        uint32_t clockFreq;
};
#endif  // !defined(ARDUINO_API_VERSION)

class BitBangedSPI {
    public:
        void begin() {
            digitalWrite(PIN_SCK, LOW);
            digitalWrite(PIN_MOSI, LOW);
            pinMode(PIN_SCK, OUTPUT);
            pinMode(PIN_MOSI, OUTPUT);
            pinMode(PIN_MISO, INPUT);
        }

        void beginTransaction(SPISettings settings) {
            pulseWidth = (500000 + settings.getClockFreq() - 1) / settings.getClockFreq();
            if (pulseWidth == 0) {
                pulseWidth = 1;
            }
        }

        void end() {}

        uint8_t transfer(uint8_t b) {
            for (unsigned int i = 0; i < 8; ++i) {
                digitalWrite(PIN_MOSI, (b & 0x80) ? HIGH : LOW);
                digitalWrite(PIN_SCK, HIGH);
                delayMicroseconds(pulseWidth);
                b = (b << 1) | digitalRead(PIN_MISO);
                digitalWrite(PIN_SCK, LOW); // slow pulse
                delayMicroseconds(pulseWidth);
            }
            return b;
        }

    private:
        unsigned long pulseWidth; // in microseconds
};

static BitBangedSPI SPI;

#endif

void setup() {
    // 4 MHZ clock on pin 3
    pinMode(3, OUTPUT);
    OCR2A = 1;
    TCNT2 = 0;
    TCCR2A = (1 << COM2B0) | (1 << WGM21);
    TCCR2B = (1 << CS20);

    SERIAL.begin(BAUDRATE);

    pinMode(LED_PMODE, OUTPUT);
    pulse(LED_PMODE, 2);
    pinMode(LED_ERR, OUTPUT);
    pulse(LED_ERR, 2);
    pinMode(LED_HB, OUTPUT);
    pulse(LED_HB, 2);

}

int ISPError = 0;
int pmode = 0;
// address for reading and writing, set by 'U' command
unsigned int here;
uint8_t buff[256]; // global block storage

#define beget16(addr) (*addr * 256 + *(addr+1) )
typedef struct param {
    uint8_t devicecode;
    uint8_t revision;
    uint8_t progtype;
    uint8_t parmode;
    uint8_t polling;
    uint8_t selftimed;
    uint8_t lockbytes;
    uint8_t fusebytes;
    uint8_t flashpoll;
    uint16_t eeprompoll;
    uint16_t pagesize;
    uint16_t eepromsize;
    uint32_t flashsize;
}
parameter;

parameter param;

// this provides a heartbeat on pin 9, so you can tell the software is running.
uint8_t hbval = 128;
int8_t hbdelta = 8;
void heartbeat() {
    static unsigned long last_time = 0;
    unsigned long now = millis();
    if ((now - last_time) < 40) {
        return;
    }
    last_time = now;
    if (hbval > 192) {
        hbdelta = -hbdelta;
    }
    if (hbval < 32) {
        hbdelta = -hbdelta;
    }
    hbval += hbdelta;
    analogWrite(LED_HB, hbval);
}

static bool rst_active_high;

void reset_target(bool reset) {
    digitalWrite(RESET, ((reset && rst_active_high) || (!reset && !rst_active_high)) ? HIGH : LOW);
}

void loop(void) {
    // is pmode active?
    if (pmode) {
        digitalWrite(LED_PMODE, HIGH);
    } else {
        digitalWrite(LED_PMODE, LOW);
    }
    // is there an error?
    if (ISPError) {
        digitalWrite(LED_ERR, HIGH);
    } else {
        digitalWrite(LED_ERR, LOW);
    }

    // light the heartbeat LED
    heartbeat();
    if (SERIAL.available()) {
        avrisp();
    }
}

uint8_t getch() {
    while (!SERIAL.available());
    return SERIAL.read();
}
void fill(int n) {
    for (int x = 0; x < n; x++) {
        buff[x] = getch();
    }
}

#define PTIME 30
void pulse(int pin, int times) {
    do {
        digitalWrite(pin, HIGH);
        delay(PTIME);
        digitalWrite(pin, LOW);
        delay(PTIME);
    } while (times--);
}

void prog_lamp(int state) {
    if (PROG_FLICKER) {
        digitalWrite(LED_PMODE, state);
    }
}

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

void empty_reply() {
    if (CRC_EOP == getch()) {
        SERIAL.print((char)STK_INSYNC);
        SERIAL.print((char)STK_OK);
    } else {
        ISPError++;
        SERIAL.print((char)STK_NOSYNC);
    }
}

void breply(uint8_t b) {
    if (CRC_EOP == getch()) {
        SERIAL.print((char)STK_INSYNC);
        SERIAL.print((char)b);
        SERIAL.print((char)STK_OK);
    } else {
        ISPError++;
        SERIAL.print((char)STK_NOSYNC);
    }
}

void get_version(uint8_t c) {
    switch (c) {
        case 0x80:
            breply(HWVER);
            break;
        case 0x81:
            breply(SWMAJ);
            break;
        case 0x82:
            breply(SWMIN);
            break;
        case 0x93:
            breply('S'); // serial programmer
            break;
        default:
            breply(0);
    }
}

void set_parameters() {
    // call this after reading parameter packet into buff[]
    param.devicecode = buff[0];
    param.revision   = buff[1];
    param.progtype   = buff[2];
    param.parmode    = buff[3];
    param.polling    = buff[4];
    param.selftimed  = buff[5];
    param.lockbytes  = buff[6];
    param.fusebytes  = buff[7];
    param.flashpoll  = buff[8];
    // ignore buff[9] (= buff[8])
    // following are 16 bits (big endian)
    param.eeprompoll = beget16(&buff[10]);
    param.pagesize   = beget16(&buff[12]);
    param.eepromsize = beget16(&buff[14]);

    // 32 bits flashsize (big endian)
    param.flashsize = buff[16] * 0x01000000
                      + buff[17] * 0x00010000
                      + buff[18] * 0x00000100
                      + buff[19];

    // AVR devices have active low reset, AT89Sx are active high
    rst_active_high = (param.devicecode >= 0xe0);
}

void start_pmode() {

    // Reset target before driving PIN_SCK or PIN_MOSI

    // SPI.begin() will configure SS as output, so SPI master mode is selected.
    // We have defined RESET as pin 10, which for many Arduinos is not the SS pin.
    // So we have to configure RESET as output here,
    // (reset_target() first sets the correct level)
    reset_target(true);
    pinMode(RESET, OUTPUT);
    SPI.begin();
    SPI.beginTransaction(SPISettings(SPI_CLOCK, MSBFIRST, SPI_MODE0));

    // See AVR datasheets, chapter "SERIAL_PRG Programming Algorithm":

    // Pulse RESET after PIN_SCK is low:
    digitalWrite(PIN_SCK, LOW);
    delay(20); // discharge PIN_SCK, value arbitrarily chosen
    reset_target(false);
    // Pulse must be minimum 2 target CPU clock cycles so 100 usec is ok for CPU
    // speeds above 20 KHz
    delayMicroseconds(100);
    reset_target(true);

    // Send the enable programming command:
    delay(50); // datasheet: must be > 20 msec
    spi_transaction(0xAC, 0x53, 0x00, 0x00);
    pmode = 1;
}

void end_pmode() {
    SPI.end();
    // We're about to take the target out of reset so configure SPI pins as input
    pinMode(PIN_MOSI, INPUT);
    pinMode(PIN_SCK, INPUT);
    reset_target(false);
    pinMode(RESET, INPUT);
    pmode = 0;
}

void universal() {
    uint8_t ch;

    fill(4);
    ch = spi_transaction(buff[0], buff[1], buff[2], buff[3]);
    breply(ch);
}

void flash(uint8_t hilo, unsigned int addr, uint8_t data) {
    spi_transaction(0x40 + 8 * hilo,
                    addr >> 8 & 0xFF,
                    addr & 0xFF,
                    data);
}
void commit(unsigned int addr) {
    if (PROG_FLICKER) {
        prog_lamp(LOW);
    }
    spi_transaction(0x4C, (addr >> 8) & 0xFF, addr & 0xFF, 0);
    if (PROG_FLICKER) {
        delay(PTIME);
        prog_lamp(HIGH);
    }
}

unsigned int current_page() {
    if (param.pagesize == 32) {
        return here & 0xFFFFFFF0;
    }
    if (param.pagesize == 64) {
        return here & 0xFFFFFFE0;
    }
    if (param.pagesize == 128) {
        return here & 0xFFFFFFC0;
    }
    if (param.pagesize == 256) {
        return here & 0xFFFFFF80;
    }
    return here;
}


void write_flash(int length) {
    fill(length);
    if (CRC_EOP == getch()) {
        SERIAL.print((char) STK_INSYNC);
        SERIAL.print((char) write_flash_pages(length));
    } else {
        ISPError++;
        SERIAL.print((char) STK_NOSYNC);
    }
}

uint8_t write_flash_pages(int length) {
    int x = 0;
    unsigned int page = current_page();
    while (x < length) {
        if (page != current_page()) {
            commit(page);
            page = current_page();
        }
        flash(LOW, here, buff[x++]);
        flash(HIGH, here, buff[x++]);
        here++;
    }

    commit(page);

    return STK_OK;
}

#define EECHUNK (32)
uint8_t write_eeprom(unsigned int length) {
    // here is a word address, get the byte address
    unsigned int start = here * 2;
    unsigned int remaining = length;
    if (length > param.eepromsize) {
        ISPError++;
        return STK_FAILED;
    }
    while (remaining > EECHUNK) {
        write_eeprom_chunk(start, EECHUNK);
        start += EECHUNK;
        remaining -= EECHUNK;
    }
    write_eeprom_chunk(start, remaining);
    return STK_OK;
}
// write (length) bytes, (start) is a byte address
uint8_t write_eeprom_chunk(unsigned int start, unsigned int length) {
    // this writes byte-by-byte, page writing may be faster (4 bytes at a time)
    fill(length);
    prog_lamp(LOW);
    for (unsigned int x = 0; x < length; x++) {
        unsigned int addr = start + x;
        spi_transaction(0xC0, (addr >> 8) & 0xFF, addr & 0xFF, buff[x]);
        delay(45);
    }
    prog_lamp(HIGH);
    return STK_OK;
}

void program_page() {
    char result = (char) STK_FAILED;
    unsigned int length = 256 * getch();
    length += getch();
    char memtype = getch();
    // flash memory @here, (length) bytes
    if (memtype == 'F') {
        write_flash(length);
        return;
    }
    if (memtype == 'E') {
        result = (char)write_eeprom(length);
        if (CRC_EOP == getch()) {
            SERIAL.print((char) STK_INSYNC);
            SERIAL.print(result);
        } else {
            ISPError++;
            SERIAL.print((char) STK_NOSYNC);
        }
        return;
    }
    SERIAL.print((char)STK_FAILED);
    return;
}

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

char flash_read_page(int length) {
    for (int x = 0; x < length; x += 2) {
        uint8_t low = flash_read(LOW, here);
        SERIAL.print((char) low);
        uint8_t high = flash_read(HIGH, here);
        SERIAL.print((char) high);
        here++;
    }
    return STK_OK;
}

char eeprom_read_page(int length) {
    // here again we have a word address
    int start = here * 2;
    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((char) ee);
    }
    return STK_OK;
}

void read_page() {
    char result = (char)STK_FAILED;
    int length = 256 * getch();
    length += getch();
    char memtype = getch();
    if (CRC_EOP != getch()) {
        ISPError++;
        SERIAL.print((char) STK_NOSYNC);
        return;
    }
    SERIAL.print((char) STK_INSYNC);
    if (memtype == 'F') {
        result = flash_read_page(length);
    }
    if (memtype == 'E') {
        result = eeprom_read_page(length);
    }
    SERIAL.print(result);
}

void read_signature() {
    if (CRC_EOP != getch()) {
        ISPError++;
        SERIAL.print((char) STK_NOSYNC);
        return;
    }
    SERIAL.print((char) STK_INSYNC);
    uint8_t high = spi_transaction(0x30, 0x00, 0x00, 0x00);
    SERIAL.print((char) high);
    uint8_t middle = spi_transaction(0x30, 0x00, 0x01, 0x00);
    SERIAL.print((char) middle);
    uint8_t low = spi_transaction(0x30, 0x00, 0x02, 0x00);
    SERIAL.print((char) low);
    SERIAL.print((char) STK_OK);
}
//////////////////////////////////////////
//////////////////////////////////////////


////////////////////////////////////
////////////////////////////////////
void avrisp() {
    uint8_t ch = getch();
    switch (ch) {
        case '0': // signon
            ISPError = 0;
            empty_reply();
            break;
        case '1':
            if (getch() == CRC_EOP) {
                SERIAL.print((char) STK_INSYNC);
                SERIAL.print("AVR ISP");
                SERIAL.print((char) STK_OK);
            } else {
                ISPError++;
                SERIAL.print((char) STK_NOSYNC);
            }
            break;
        case 'A':
            get_version(getch());
            break;
        case 'B':
            fill(20);
            set_parameters();
            empty_reply();
            break;
        case 'E': // extended parameters - ignore for now
            fill(5);
            empty_reply();
            break;
        case 'P':
            if (!pmode) {
                start_pmode();
            }
            empty_reply();
            break;
        case 'U': // set address (word)
            here = getch();
            here += 256 * getch();
            empty_reply();
            break;

        case 0x60: //STK_PROG_FLASH
            getch(); // low addr
            getch(); // high addr
            empty_reply();
            break;
        case 0x61: //STK_PROG_DATA
            getch(); // data
            empty_reply();
            break;

        case 0x64: //STK_PROG_PAGE
            program_page();
            break;

        case 0x74: //STK_READ_PAGE 't'
            read_page();
            break;

        case 'V': //0x56
            universal();
            break;
        case 'Q': //0x51
            ISPError = 0;
            end_pmode();
            empty_reply();
            break;

        case 0x75: //STK_READ_SIGN 'u'
            read_signature();
            break;

        // expecting a command, not CRC_EOP
        // this is how we can get back in sync
        case CRC_EOP:
            ISPError++;
            SERIAL.print((char) STK_NOSYNC);
            break;

        // anything else we will return STK_UNKNOWN
        default:
            ISPError++;
            if (CRC_EOP == getch()) {
                SERIAL.print((char)STK_UNKNOWN);
            } else {
                SERIAL.print((char)STK_NOSYNC);
            }
    }
}

Залейте этот скетч в рабочую UNO/NANO и пробуйте оживить 328PB.
MOSI MISO SCK брать с соответствующих ног,
RESET с 10 ноги Arduino
c 3 ноги Arduino меандр 4 МГц подать на вход XTAL1 (7 нога МК)

Прошивать из IDE выбрав в качестве программатора Arduino as ISP