RSS

Tag Archives: Tutorial

Atmega: Reading and Writing to Flash Memory during Runtime

There may be a day when you are plodding along, doing stuff on your Atmega micro controller when suddenly, you realise that you suddenly are in need of a way to store some data persistently. EEPROM isn’t big enough, so what can you do? How about utilising left over flash/program memory?!

There exist tutorials already on how to utilise flash memory for static data structures (such as large static arrays), so I’ll just focus on reading and writing to flash on runtime. I’m also NOT writing a tutorial on writing a bootloader either.

So without further ado, I present a basic rundown on reading and writing to flash during runtime!

The functionality is based around the “boot.h” library FOUND HERE!
My example is based on the Atmega168, so all code will be tailored to that chip.
Code was developed in Atmel studio 6.

Once you have created your project for your particular micro controller and have successfully connected to the chip with your programmer (AVR ISP MKII, AVR Dragon etc…), we need to reprogram a few fuses on the Atmega.

Heading into the datasheet for your device, we need to find the boot loader parameters (the BOOTSZn bits).
For my Atmega168, the relevant information is on page 282, Table 27-9 (under the boot loader support section).

Page 282, datasheet.

Page 282, datasheet.

The code being stored in the bootloader section is less than 128 words, so we can go ahead and set the BOOTSZ fuses to 1 1.

Final fuse settings for device

Final fuse settings for device

However, that’s not all. We need to tell the compiler where the bootloader section starts so it knows where to place our flash memory writing function.

We can do this by right clicking your project in the “solution explorer” and clicking “properties”.

Opening Properties

Setting Compiler Flash Settings

  1. Select “All Configurations” from Configurations
  2. Click “Memory Settings” under AVR/GNU Linker
  3. Click the green plus for the FLASH segment
  4. Type “.bootloader=[start address of your bootloader section as defined by your fuse selection]”
  5. Hit OK
  6. Save

Cool! Now we are all ready for the code!

First up we have “FlashMem.h”. This is simply function definitions. Something to note is the “__attribute__” line is what tells the compiler that that particular function should be placed in the .bootloader section of memory.


#ifndef FLASHMEM_H_
#define FLASHMEM_H_

#include <avr/io.h>
#include <avr/boot.h>
#include <avr/interrupt.h>
#include <avr/pgmspace.h>

/* Main writing method for flash memory. Takes in an address and
a pointer to a buffer. Function stored in the bootloader section
of memory to enable proper functionality (NRWW mem section).
If the buffer is greater than SPM_PAGESIZE, the remainder data
is discarded.
Function is taken from: 
http://www.nongnu.org/avr-libc/user-manual/group__avr__boot.html */
void boot_program_page (uint32_t page, uint8_t *buf) \
__attribute__ ((section (".bootloader")));

/* Wrapper function for boot_program_page. Allows user to input a
page number instead of an address for simplicity of use. Requires
page number and a pointer to a buffer */
void flash_write_page (uint8_t page, uint8_t *buf);

/* Read a character from flash memory at the given address */
char flash_read_byte (uint32_t addr);

/* Reads a page of flash memory and stores it in the passed in buffer.
Functionality not defined if buffer is shorter than length
SPM_PAGESIZE */
void flash_read_page (uint8_t page, uint8_t *buf);


#endif /* FLASHMEM_H_ */

Next is “FlashMem.c”, the guts to code.

#include "FlashMem.h"

void boot_program_page (uint32_t page, uint8_t *buf)
{
    //This is copy paste from the boot.h GNU website
    uint16_t i;
    uint8_t sreg;

    sreg = SREG;
    
    cli(); // Disable interrupts.
    
    eeprom_busy_wait ();
    
    boot_page_erase (page);
    boot_spm_busy_wait ();      // Wait until the memory is erased.

    for (i=0; i<SPM_PAGESIZE; i+=2)
    {
        // Set up little-endian word.
        uint16_t w = *buf++;
        w += (*buf++) << 8;
        
        boot_page_fill (page + i, w);
    }
    
    boot_page_write (page);     // Store buffer in flash page.
    boot_spm_busy_wait();       // Wait until the memory is written.
    
    // Re-enable RWW-section again.
    boot_rww_enable ();

    // Re-enable interrupts
    sei();

    SREG = sreg;
}

void flash_write_page(uint8_t page, uint8_t *buf)
{
    uint32_t addr = page*SPM_PAGESIZE;
    boot_program_page(addr,buf);
}

char flash_read_byte (uint32_t addr)
{
    return pgm_read_byte(addr);
}

void flash_read_page (uint8_t page, uint8_t *buf)
{
    uint32_t addr = (page*SPM_PAGESIZE);
        
    for (int i = 0; i < 128; i++)
    {
        *buf = flash_read_byte(addr);
        boot_spm_busy_wait();
        buf++;
        addr++;
    }
}

And finally and example of its usage: (USART functionality not covered. There are plenty of guides on USART out there)

#define F_CPU 8000000UL

#include <avr/io.h>
#include <avr/interrupt.h>
#include <avr/pgmspace.h>
#include <stdbool.h>

#include "USART.h"
#include "FlashMem.h"

//Declare a buffer in RAM the size of a single page of flash memory
uint8_t flash_page_buffer[SPM_PAGESIZE]; 
//To keep track of how full the buffer is
uint8_t flash_buffer_index = 0;
//Flag which indicates if the flash page buffer is full
volatile bool flash_page_buffer_full = false; 

void Clear_Array(char *array)
{
    while (*array)
    {
        *array = 0;
        array++;
    }
}

int main(void)
{
    USART_Init((int)9600);
    
    sei(); //set enable interrupts
    
    USART_Transmit_String("Startup\r\n");
        
    while(1)
    {
        if (flash_page_buffer_full == true)
        {
            flash_page_buffer_full = false;
            flash_write_page(50,flash_page_buffer);
            
            USART_Transmit_String("\r\n<Page Received>:");
            USART_Transmit_String((char*)flash_page_buffer);
            USART_Transmit_String("\r\n");
            
            Clear_Array((char*)flash_page_buffer);
            
            flash_read_page(50, flash_page_buffer);
            
            USART_Transmit_String("\r\n<From Mem>:");
            USART_Transmit_String((char*)flash_page_buffer);
            USART_Transmit_String("\r\n");
            
            Clear_Array((char*)flash_page_buffer);
        }
    }
}

ISR(USART_RX_vect)
{
    uint8_t data;
    
    data = UDR0;
    
    //If user hits enter or inputs 128 characters, trigger flash write.
    if ((data == '\r') || (flash_buffer_index >= (SPM_PAGESIZE - 1)))
    {
        flash_page_buffer_full = true;
        flash_buffer_index = 0;
    }
    else
    {
        flash_page_buffer[flash_buffer_index++] = data;
        USART_Transmit_Char(data);
    }
}

Note: My use of page 50 is arbitrary. I would not recommend reading and writing to the same
page over and over again. Flash memory has limited read/write cycles. It’s better to keep
shifting the page you are storing too. An idea could be to use EEPROM to keep track of which
pages contain data between power cycles.

That’s all folks. Hope this helped in any way~!

 
Leave a comment

Posted by on November 15, 2014 in Programming

 

Tags: , , , , , , , , ,