Go Down

Topic: Programming flash from a sketch (Read 558 times) previous topic - next topic

RayRandom

One of the interesting things about the ATmega devices is their ability to write to their own flash. There have been a couple of questions about this on the forum but the conclusion seems to be that it is difficult. I decided to see what was the simplest way writing to flash could be achieved, and this is the result.

I used a Uno and an ISP programmer.

step one was to find the smallest piece of code that that needed to be in the bootloader section. I came up with this:

Code: [Select]

          .equ SPMCSR, 0x37
.equ SPMEN, 0
.equ PGERS, 1
.equ PGWRT, 2
.equ RWWSRE, 4
.equ RWWSB, 6

; work registers
.equ SPM_CMD, 18

; SPM commands
.equ SPM_ERASE, (1<<PGERS) | (1<<SPMEN)
.equ SPM_ENABLE, (1<<RWWSRE) | (1<<SPMEN)
.equ SPM_LOAD, (1<<SPMEN)
.equ SPM_WRITE, (1<<PGWRT) | (1<<SPMEN)

  68 0000 27BF      pmburn: out SPMCSR, SPM_CMD
  69 0002 E895      spm
  70 0004 27B7      A010: in SPM_CMD,SPMCSR
  71 0006 20FD      sbrc SPM_CMD,SPMEN
  72 0008 00C0      rjmp A010
  73 000a 21E1      ldi SPM_CMD,SPM_ENABLE
  74 000c 27BF      pmload: out SPMCSR,SPM_CMD
  75 000e E895      spm
  76 0010 0895      ret
 


Call pmburn for erase or write, pmload to fill the page buffer. That's nine words. Unfortunately the Uno bootloader, optiboot, has only five words free, but all is not lost, as in the same directory there is also optiboot_amega_328-Mini.hex, which has ten words free. Result! So assemble the above and relocate it to the end of ROM gives the following two hex records:

:107FEE0027BFE89527B720FDFDCF21E127BFE895F4
:027FFE000895E4

Now paste those into the end of the optiboot mini hex file just before the entry point and end of file records. Update boards.txt to reference the file like so:

uno.bootloader.file=optiboot/optiboot_atmega328-Mini.hex

Now all you have to do is fire up the Arduino IDE and "Burn Bootloader".

To use it in a sketch you will need a function declaraion like so:

byte Flash(word, byte*)__attribute__((naked));

First argument is the ROM address in bytes, second is a pointer to 128 byes to burn. I was going to return success/fail but couldn't be bothered. The function definition looks like this:
Code: [Select]

/*
 * write to flash
 *
 *  This code relies on a 9 word patch to optiboot 328 Mini
 */
byte Flash(word, byte*)
{
  asm(

".equ    ARG0,   24\n"
".equ    ARG1,   22\n"
".equ    RX,     26\n"
".equ    RZ,     30\n"
".equ    CMD,    18\n"
".equ    COUNT,  19\n"
   
".equ    SPM_LOAD,    1\n"
".equ    SPM_ERASE,   3\n"
".equ    SPM_WRITE,   5\n"

".equ    pmburn,  0x7FEE\n"
".equ    pmload,  0x7FFA\n"

"        cli                     ; interrupts off\n"
"        andi    ARG0,0x80       ; force page boundary\n"

"        movw    RZ,ARG0         ; set Z from arg0\n"
"        ldi     CMD,SPM_ERASE   \n"
"        call    pmburn          \n"

"        movw    RX,ARG1         ; set X from arg1\n"
"        ldi     COUNT,64        ; page size in words\n"
"        ldi     CMD,SPM_LOAD    \n"
"stbuf:  ld      r0,x+           \n"
"        ld      r1,x+           \n"
"        call    pmload          \n"
"        adiw    RZ,2            ;Z+=2\n"
"        dec     COUNT           \n"
"        brne    stbuf           \n"

"        movw    RZ,ARG0         ; restore Z\n"
"        ldi     CMD,SPM_WRITE   \n"
"        call    pmburn          \n"

"        clr     r1              ; restore r1\n"
"        mov     ARG0,r1         ; return 0\n"
"        sei                     \n"
"        ret                     \n"
     ); 
}

And that's it. It's  bodge of course, interrupts are off for about seven ms so the counters will be affected, but it may be of interest to someone. Let me know what you think.

pert

Regarding the use of the optiboot_amega_328-Mini.hex bootloader, you should be aware that this was reported as being very outdated:
https://github.com/arduino/Arduino/issues/6800

I know the current version of optiboot was modified by majekw to allow writing to flash:
http://forum.arduino.cc/index.php?topic=332191.0
That bootloader is used in the excellent MightyCore, MiniCore, and Megacore. Is your approach different from that one?

Go Up