Go Down

Topic: Pro Mini Serial standalone programmer (Read 1 time) previous topic - next topic

Leopard77

Sep 27, 2015, 01:52 pm Last Edit: Sep 30, 2015, 06:23 pm by Leopard77
I've developped a small board to emulate avrdude with an Arduino. It is based on an Arduino Pro Mini with a SD Card reader. All I need is to copy the HEX file generated by the Arduino IDE on the SD Card. Following I can reprogram Pro Minis without a computer. As adding a SD Card to an Arduino board is very common and simple I don't go into developping this part.
Below you'll find my code. Please feel free to give any comments.

Code: [Select]
/********************************************/
/* This program acts as an UART programmer  */
/*                                          */
/* It allows direct transfer of an HEX file */
/* generated by the Arduino IDE to an       */
/* Arduino Pro Mini                         */
/*                                          */
/* The HEX file must be stored on a SD Card */
/* with the following name : PGMASTER.HEX   */
/*                                          */
/* This program can only manage Pro Mini    */
/* but it can be adapted for other boards   */
/*                                          */
/* The Pro Mini must have the bootloader    */
/*                                          */
/*    Please report bugs or comments to     */
/*       leopard77.arduino@gmail.com        */
/*                                          */
/*      This program is free software       */
/*       This program is distributed        */
/*          WITHOUT ANY WARRANTY            */
/********************************************/

#include <SdFat.h>     // SD Card library
#include <SPI.h>       // SPI Bus library

/********************************************/
/*            Pins definition               */
/* RAW, GND on Arduinos must be connected   */
/* TX0 and RX1 pins must be cross connected */
/* (ie : TX0 on Master to RX1 on Slave)     */
/* PRG_DTR pin on Master must be connected  */
/* to reset pin on Slave Pro Mini           */
/********************************************/

#define SPI_SCLK 13    // Hardware SPI SCLK
#define SPI_MISO 12    // Hardware SPI MISO
#define SPI_MOSI 11    // Hardware SPI MOSI

#define SD_CS 9        // SD Chipselect
#define PRG_DTR 3      // DTR pin

/********************************************/
/*          Constants definition            */
/********************************************/

#define MAX_TIME_COUNT (F_CPU>>4) // Delay timer for communication
#define PAGE_SIZE 0x40U           // ATmega328p Flash memory page size
#define BAUD_RATE 57600           // Baud rate for transmission

SdFat sd;              // Create SdFat object
SdFile FileOrigin;     // Create SdFile object

void (*app_start)(void) = 0x0000; // Address if reset is needed


void setup() {
 
  sd.begin(SD_CS, SPI_FULL_SPEED); // Initialize SD Card
  pinMode(PRG_DTR, OUTPUT);        // Initialize PRG_DTR pin
  digitalWrite(PRG_DTR, HIGH);

  UBRR0L = (uint8_t)(F_CPU/(BAUD_RATE*16L)-1); // Set USART Baud Rate Register
  UBRR0H = (F_CPU/(BAUD_RATE*16L)-1);          // cf ATmega328P datasheet for calculation explanation

  UCSR0B = (1<<RXEN0) | (1<<TXEN0);   // Enables RX and TX interrupts (cf ATmega328p datasheet)
  UCSR0C = (1<<UCSZ00) | (1<<UCSZ01); // Set 8 bits per character (cf ATmega328p datasheet)

  DDRD &= ~_BV(PIND0); // Set RX as input
  PORTD |= _BV(PIND0); // Set pull-up resistor

}


void loop() {

  if (UploadProgram() != 0x00)  // Check returned value
    app_start();                // If not 0x00 reset Master Arduino

  for(;;) {} // Forever loop

}


/********************************************/
/*                Main program              */
/* The principle of this program is after   */
/* initialization of communications with    */
/* the slave Pro Mini to scan the HEX file  */
/* and fill a buffer which has the size of  */
/* a flash memory page with the bytes and   */
/* the send the buffer to the Pro Mini      */
/*                                          */
/* For more info on HEX file format see :   */
/* https://en.wikipedia.org/wiki/Intel_HEX  */
/********************************************/

byte UploadProgram () {

  char ProgFileName[13] = "PGMASTER.HEX";
  byte BytesBuffer[2 * PAGE_SIZE]; // ATMega flash memory pages are in words
  union address_union {
    uint16_t word;
    uint8_t byte[2];
  } PageAddress;
  unsigned int nBytesLoaded = 0;       
  unsigned int nBytesInHEXLine = 0;
  bool EndOfFile = false;
  unsigned int i;
  byte DummyData;

  PageAddress.word = 0; // Initialize address

  if (FileOrigin.open(ProgFileName, O_READ) != true) return 0x01; // Open file

  // Reset slave to enter bootloader
  digitalWrite(PRG_DTR, LOW);
  delay(120);
  digitalWrite(PRG_DTR, HIGH);
  delay(200);

  // Check if device is ready to listen
  putch(0x30);
  putch(0x20);

  // Check device answer
  if (getch() != 0x14) return 0x02;
  if (getch() != 0x10) return 0x02;

  // Check if device is ATmega328P
  putch(0x75);
  putch(0x20);

  // Check device answer
  if (getch() != 0x14) return 0x03;
  if (getch() != 0x1E) return 0x03;
  if (getch() != 0x95) return 0x03;
  if (getch() != 0x0F) return 0x03;
  if (getch() != 0x10) return 0x03;

  do {

    do {

      DummyData = FileOrigin.read(); // Ommit ":" new line marker

      nBytesInHEXLine = GetByteFromHEX(); // Retrieve number of bytes stored in line

      DummyData = FileOrigin.read(); // Ommit the 4 address bytes
      DummyData = FileOrigin.read(); // Ommit the 4 address bytes
      DummyData = FileOrigin.read(); // Ommit the 4 address bytes
      DummyData = FileOrigin.read(); // Ommit the 4 address bytes

      if (GetByteFromHEX() == 0x00) { // Check end of file not reached

        for (i = 0; i < nBytesInHEXLine; i++)
          BytesBuffer[nBytesLoaded++] = GetByteFromHEX(); // Retrieve and store bytes

        DummyData = FileOrigin.read(); // Ommit checksum
        DummyData = FileOrigin.read(); // Ommit checksum
        DummyData = FileOrigin.read(); // Ommit CR
        DummyData = FileOrigin.read(); // Ommit LF

      }

      else EndOfFile = true; // Set flag

    } while ((!EndOfFile) && (nBytesLoaded < PAGE_SIZE * 2)); // Loop if end of file note reached and page not complete

    if (nBytesLoaded != 0) { // Check there are bytes to transfer

      // Send page address
      putch(0x55);
      putch(PageAddress.byte[0]);
      putch(PageAddress.byte[1]);
      putch(0x20);

      // Check device answer
      if (getch() != 0x14) return 0x04;
      if (getch() != 0x10) return 0x04;

      // Send number of bytes to be loaded
      putch(0x64);
      putch(0x00);
      putch(nBytesLoaded);

      // Store in ATmega328p flash memory
      putch (0x46);

      for (i=0; i < nBytesLoaded; i++)
        putch(BytesBuffer[i]); // Send stored bytes

      // End of page
      putch (0x20);

      // Check device answer
      if (getch() != 0x14) return 0x05;
      if (getch() != 0x10) return 0x05;

      nBytesLoaded = 0;
      PageAddress.word += PAGE_SIZE;

    }

  } while (!EndOfFile); // Loop till end of HEX file

  FileOrigin.close(); // Close file

  // Stop transmission
  putch(0x51);
  putch(0x20);

  // Check device answer
  if (getch() != 0x14) return 0x06;
  if (getch() != 0x10) return 0x06;

  return 0x00;

}


void putch (uint8_t ch) {

  while (!(UCSR0A & _BV(UDRE0)));
  UDR0 = ch;

}


uint8_t getch(void) {

  uint32_t count = 0;

  while (!(UCSR0A & _BV(RXC0))) {

    count++;
    if (count > MAX_TIME_COUNT) app_start();

  }

  return UDR0;

}


byte GetByteFromHEX() {

  // Bytes are stored in text format in the HEX file
  // The first byte contains the ANSI code of the high HEX value of the byte
  // The second byte contains the ANSI code of the low HEX value of the byte

  return (GetHEXValue(FileOrigin.read()) << 4) | GetHEXValue(FileOrigin.read());

}


byte GetHEXValue(byte ANSIValue) {

  if (ANSIValue < 65) return ANSIValue -= 48; // If ANSIValue represents a number
  else return ANSIValue -= 55;                // If ANSIValue represents a character

}

septillion

Nice idea! Seen it before here though...

But I would like some feedback, at least an LED to show it programmed or something. Also a start buttons would be nice instead of the plug in moment.

And to make it real great, selectable hex's. But yeay, that would mean adding a display or something...
Use fricking code tags!!!!
I want x => I would like x, I need help => I would like help, Need fast => Go and pay someone to do the job...

NEW Library to make fading leds a piece of cake
https://github.com/septillion-git/FadeLed

DrAzzy

I think you mean UART, not USART; the 328 doesn't have a hardware USART, just a UART. A USART has a clock line (much like spi), in addition to tx and rx.
ATTinyCore and megaTinyCore for all ATtiny, DxCore for DA/DB-series! github.com/SpenceKonde
http://drazzy.com/package_drazzy.com_index.json
ATtiny breakouts, mosfets, awesome prototyping board in my store http://tindie.com/stores/DrAzzy

CrossRoads

I'd like to combine your code with Nick Gammon's code that I run on my standalone programmer so either method could be used:
http://www.crossroadsfencing.com/BobuinoRev17/
Designing & building electrical circuits for over 25 years.  Screw Shield for Mega/Due/Uno,  Bobuino with ATMega1284P, & other '328P & '1284P creations & offerings at  my website.

Leopard77

#4
Sep 30, 2015, 06:08 pm Last Edit: Sep 30, 2015, 06:12 pm by Leopard77
But I would like some feedback, at least an LED to show it programmed or something. Also a start buttons would be nice instead of the plug in moment.

And to make it real great, selectable hex's. But yeay, that would mean adding a display or something...
Thank you for your remark. My goal was to share what I've done in a very minimal code. On my own board I've got leds to indicate feedback and jumpers to select source for the code (ie SD Card or 25LC1024 EEPROM).

Leopard77

#5
Sep 30, 2015, 06:20 pm Last Edit: Sep 30, 2015, 06:40 pm by Leopard77
I'd like to combine your code with Nick Gammon's code that I run on my standalone programmer so either method could be used
No problem to do so. Just be aware that some parameters (BAUDRATE, etc...) must be adapted to comply with other boards than Pro Mini specs.
Important notice : as my goal was to have the simpliest code the only check I perform is to make sure that I've established communications with an ATmega 328p. I have not written the verification process to check what has been written to the flash.

Leopard77

I think you mean UART, not USART; the 328 doesn't have a hardware USART, just a UART. A USART has a clock line (much like spi), in addition to tx and rx.
Thanks for your remark. You're right. As I only use RX and TX this program is to be considered as UART. I changed the description part.
In the remaining of the code I've kept the use of USART to stay relevant with the registers description used in the Atmel datasheet.

CrossRoads

#7
Oct 02, 2015, 09:02 pm Last Edit: Oct 02, 2015, 09:02 pm by CrossRoads
Looks like a USART to me:
Quote
20. USART0

20.1 Features

• Full Duplex Operation (Independent Serial Receive and Transmit Registers)

• Asynchronous or Synchronous Operation

• Master or Slave Clocked Synchronous Operation

• High Resolution Baud Rate Generator

• Supports Serial Frames with 5, 6, 7, 8, or 9 Data Bits and 1 or 2 Stop Bits

• Odd or Even Parity Generation and Parity Check Supported by Hardware

• Data OverRun Detection

• Framing Error Detection

• Noise Filtering Includes False Start Bit Detection and Digital Low Pass Filter

• Three Separate Interrupts on TX Complete, TX Data Register Empty and RX Complete

• Multi-processor Communication Mode

• Double Speed Asynchronous Communication Mode

20.2 Overview

The Universal Synchronous and Asynchronous serial Receiver and Transmitter (USART) is a highly flexible serial

communication device.

The USART0 can also be used in Master SPI mode, see "USART in SPI Mode" on page 199. The Power Reduction

USART bit, PRUSART0, in "Minimizing Power Consumption" on page 41 must be disabled by writing a logical

zero to it.
Designing & building electrical circuits for over 25 years.  Screw Shield for Mega/Due/Uno,  Bobuino with ATMega1284P, & other '328P & '1284P creations & offerings at  my website.

DrAzzy

Okay, my bad. Why did I think the AVR's only supported UART, not USART? Not sure how I missed that.
ATTinyCore and megaTinyCore for all ATtiny, DxCore for DA/DB-series! github.com/SpenceKonde
http://drazzy.com/package_drazzy.com_index.json
ATtiny breakouts, mosfets, awesome prototyping board in my store http://tindie.com/stores/DrAzzy

Go Up