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:
.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:
/*
* 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.