AVR128Dx EEPROM writing

Go To Last Post
26 posts / 0 new
Author
Message
#1
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Somewhat a continuation from this thread.

 

https://www.avrfreaks.net/forum/looking-info-about-avrarch-gcc-use-vscode

 

The 3.6.2 toolchain, seems to support the m4809 but not the AVR128Dx. Specifically, this link (which was from the atpack.) Shows NVMCTRL_CMD_PAGEERASEWRITE_gc

 

https://github.com/epccs/ATmega_DFP/blob/b4d6bb3cc8fd1f8e60981bbe2f3811569708a92f/include/avr/iom4809.h#L935

 

the EEPROM write  functions available in the 3.6.2 toolchain support NVMCTRL_CMD_PAGEERASEWRITE_gc.

 

https://salsa.debian.org/debian/avr-libc/-/blob/09632ba9991dc8f7ad6d208b6c946940eaefc29e/libc/avr-libc/libc/misc/eewr_block_xmega.c

 

When I compile for the AVR128DA it finds the ‘NVMCTRL_CMD_PAGEERASEWRITE_gc’ undeclared. Looking at the ioavr128da28.h it is clear the Non-volatile Memory Controller commands are different.

 

https://github.com/epccs/AVR-Dx_DFP/blob/859ed6955f41261bb44c61a47852fdd7f9e24c94/include/avr/ioavr128da28.h#L1379

 

I can't even find the 3.6.2 source now, but it got packaged on Debian, so that is what I referenced. I guess there is little chance of finding the source for eewr_block_xmega.c that works with the AVR128Dx.

 

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Yup, your analysis is correct. The NVM controller of the Dx series has significant differences from the one from xmega. The one from the AVR-0/1 is more similar to xmega, so it was relatively simple to made the code work with small modifications.

 

Making it work with the Dx requires extensive changes in the code. I think even the most recent toolchain from microchip won't work.

 

ron_sutherland wrote:
I can't even find the 3.6.2 source now

Here (requires login)  https://www.microchip.com/wwwregister/default.aspx?ReturnURL=https://ww1.microchip.com/downloads/Secure/en/DeviceDoc/3.6.2.zip

 

Seems to me it's the same as Debian, so it won't work with Dx...

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I would just do my own eeprom code, where you don't have to go through functions for reading, and writing probably turns out easier, also.

 

Here is an avr0/1 eeprom alternative-

https://github.com/cv007/Avr01Li...

and although I have no Dx, the following probably comes close to the same.

 

The Dx can only write a byte at a time and is also not confined to pages, so it looks like simply enabling/disabling the eeprom erase/write mode is all you need to do and when writing after enabling you are doing a byte erase/write (and the next read/write will block until done). The linker script will need to give the .eeprom section a VMA address (0x1400) so reads/writes will be using the correct address (instead of 0 based), but the LMA address still still be 0 based (add the AT>) as the hex file (programmer) expects that. 

 

These functions are inline, but could be moved to source or create a library to become called functions instead. If a lot of ee writes are scattered around, then called functions may be nicer but probably makes little difference in most cases.

 

//eedx.h
#pragma once

 

#include <avr/io.h>

 

//this needs to be something more specific

#if (__AVR_ARCH__ != 104)
#error The header eedx.h is for avrDx series only
#endif

 

// a special linker script is required to give the .eeprom section a virtual address

 

static inline void eeEnableWrite(){
    //not sure if need to check busy here, but if needed
    //this will block until ee not busy as an ee read will block when busy (no loop required)
    //*(volatile uint8_t*)0x1400;
    CCP = 0xD9; //SPM
    NVMCTRL.CTRLA = 0x13; //EEERWR, erase/write
}

static inline void eeDisableWrite(){
    //not sure if need to check busy here, but if needed
    //this will block until ee not busy as an ee read will block when busy (no loop required)
    //*(volatile uint8_t*)0x1400;
    CCP = 0xD9; //SPM
    NVMCTRL.CTRLA = 0; //NOCMD
}

// if want to group a set of writes eeprom is enabled/disabled
#define EEWRITE(statement) do { eeEnableWrite(); statement eeDisableWrite(); } while(0)

 

/*
    linker script change for avrxmega4.xn, 
    add VMA address of 0x1400 and LMA address at 0 for hex (AT>eeprom)

    .eeprom  0x1400 : {
        KEEP(*(.eeprom*))
        __eeprom_end = . ;
    } AT>eeprom

    usage:

    Any EEMEM var can be read like any other var and has no need to go through
    a special function. An eeprom write needs to enable the nvm controller to
    write to eeprom, so either need to enable/disable eeprom writes via the
    above functions, or use the above macro to handle the enable/disable.

 

    EEMEM myvar = 1; //init data in hex file

 

    int main(){

 

        EEWRITE( myvar++; );

 

        while(1){}
        
    }

*/

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I asked a similar question in this thread.

https://www.avrfreaks.net/forum/...

It's inconvenient because there are no useful updates yet, but I'm not in trouble because accessing the EEPROM itself is not difficult.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Many thanks; I guess it is what it is.

 

@kabasan I will use the code from that thread to get started, Thanks.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

gchapman post a link to mplab 2.31 source for GCC, xc8 is not gpl, but mplab has both compilers.

 

https://www.avrfreaks.net/comment/3023176#comment-3023176

 

Which changes eewr_block_xmega.c a little. Basically turning everything in it off for Dx parts. That made me look a little at the .S files (renamed .asm since .S cannot be uploaded) where I found the attached eerd_byte.S and eewr_byte.S

 

VScode makes me think I am smarter than I am; it is not telling me much about these files; I don't even know how to set up the Makefile to build them and see if the compiler is happy, but I have a feeling this is what I am after.

 

Attachment(s): 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 2

>but I have a feeling this is what I am after

 

If you have an avr0/1. The eeprom .a libraries for the Dx appear to simply be from the avr0/1 (and this asm appears to produce them). The NVMCTRL_CMD_PAGEERASEWRITE_gc is an avr0/1 define (and is 0x03), but is not a Dx define (which is NVMCTRL_CMD_EEERWR_gc, 0x13). Another clue is they do not disable eeprom writing when done.

 

I would just forget trying to unscramble the X files and write your own. I cannot simulate a Dx eeprom write it appears, so the following is a guess. It uses the standard linker script so is also bound to using the 'old style' 0 based system of eeprom addresses. A simple linker script change as already shown would remove the need to do anything special for a read, and would also make writes nicer, but if not wanted then you just do as has always been done. You then create defines for the original eeprom functions needed, using EEread/write/do as the functions (macros) that do the work.

 

#include <avr/io.h>
#include <stdint.h>
#include <stdbool.h>

 

#define EEMEM __attribute(( section(".eeprom") ))

 

//lock/unlock eeprom
static inline void eeLock(bool tf){
    *(volatile uint8_t*)0x1400; //ee read blocks until ee ready
    if(tf) _PROTECTED_WRITE_SPM(NVMCTRL.CTRLA,0);
    else   _PROTECTED_WRITE_SPM(NVMCTRL.CTRLA,0x13);
}

 

// group a set of eeprom writes
#define EEdo(statement)             do { eeLock(false); statement eeLock(true); } while(0)

 

//read/write a type at a 0 based address
#define EEwrite(typ, addr, v)       (*(volatile typ*)(addr+0x1400)) = v
#define EEread (typ, addr)          (*(volatile typ*)(addr+0x1400))

 

//standard eeprom functions (0 based address)
#define eeprom_write_byte(addr,v)   EEdo( EEwrite(uint8_t, addr, v); )
#define eeprom_read_byte(addr)      EEread (uint8_t, addr, v)

//add eeprom.h-like functions as needed

 

//0 based (standard linker script)
EEMEM uint8_t ee0 = 5;

 

int main(void) {

 

    eeprom_write_byte( &ee0, 0x55 );
    eeprom_write_byte( 1, 0xAA );

    

    //bypass the eeprom functions so can do multiple writes

    //with eeprom unlocked

    EEdo(
        EEwrite( uint16_t, 2, 0x1234 );
        EEwrite( uint16_t, 4, 0x5678 );
    );

 

    for(;;){}
}

 

And since the only functions really needed outside of a header are the lock/unlock, which could then be be made into library (replacing the original  non-working version)-

 

;ee.s
;avr-gcc -c ee.s
;avr-ar -q libavr128da28.a ee.o

;replace libavr128da28.a with this new version (or whichever Dx mcu in use)

.global eeLock
.global eeUnlock

eeUnlock:
ldi    r25, 0x13 ;EEERWR
rjmp 1f
eeLock:
ldi    r25, 0 ;NOCMD
1:
lds    r24, 0x1400 ;wait on busy
ldi    r24, 0x9D   ;SPM unlock
out    0x34, r24   ;CCP
sts    0x1000, r25 ;cmd
ret

 

Then change previous code a little using the updated functions-

//ee library functions
void eeLock();
void eeUnlock();

 

// group a set of eeprom writes
#define EEdo(statement)             do { eeUnLock(); statement eeLock(); } while(0)

Last Edited: Mon. Jan 11, 2021 - 03:13 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Thank you for that, especially for showing the compile commands; I have never messed with the assembler. I will give it a spin and report back (it may take a few days).

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

It works, I have a few things to add, but I can change the bits in eeprom with my test framework; now I can worry about other things :-)

 

https://github.com/epccs/MacGyver/commit/dc45dd2573d14488ccac18cdbf8049fb35c5e0e8

 

Thanks

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I don't understand what this does.

 

//0 based (standard linker script)
EEMEM uint8_t ee0 = 5;

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

>I don't understand what this does.

 

It gives the EEMEM var an initial value of 5 (when programming). Since there is no data initialization for eeprom like ram data, it has to be done at time of programming. The programmer sees a specific section address as eeprom data, which at time of programming it will use to program the eeprom (subject to programmer settings). If no value assigned, its initial value is set to 0 like a global var.

 

EEMEM uint8_t ee0 = 5;

 

hex file result-

:02000004008179
:0100000005FA

 

EEMEM uint8_t ee0;

 

:02000004008179
:0100000000FF

 

Also, note the eeprom address for the hex file is 0 based, which is what the programmer expects. The linker script mod I showed earlier, gets the .eeprom virtual address aligned with the eeprom io address but still maintains the 0 based load address so the hex file remains as the programmer expects.

Last Edited: Fri. Dec 18, 2020 - 11:10 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Thanks for this article. I have tried a few things but so far can't write to AVR128DA28 eeprom. Looking at  GitHub - epccs/MacGyver: Board that connects an R-Pi to AVR128DA28  but don't have this:

#include "../lib/eerw_dx.h"  

I'm on Windows, have Atmel Studio 7 

 

I apologize in advance for this very basic question: How do I cobble together a library that I can compile in avr-gcc that will allow writes to the AVR128DA28 eeprom? I suppose I can dust off a Linux box and install Atmel Studio 7 on it and try the examples directly. I'd like to avoid that. I am also thinking of switching chips to something natively supported by the current avr-gcc but the AVR128DA28 is perfect for my application and I have the board built already...

 

I have an extensive software and hardware background, just not in Atmel embedded and am trying to find something simple. This project was ported from a PIC microcontroller with insufficient memory on MPLAB X-IDE. Overall Atmel Studio with avr-gcc is far easier except for the eeprom. On pic all you do is declare a variable as eeprom and use it like any other variable, remembering that you can't indiscriminately write...

 

Any help greatly appreciated. 

Last Edited: Mon. Jan 4, 2021 - 02:26 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

AVR-DA is so new that it is not yet supported in eeprom.h.
You will be looking at the datasheet to use the new chip.
The access method is not difficult.

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Thanks, Kaba-san. I didn't realize this chip was so new. So I will have to look up ST and SPM instructions and use inline assembler. It does look like others have already done it, I will report back on progress which may be slow as all I have is short evenings to work the problem. 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

This is my own code and operation check sample that I am using temporarily.
I'm glad if you can use it as a reference.

 

#include <avr/io.h>
#include <avr/eeprom.h>
#include <stdbool.h>

uint8_t EEMEM ee_dat;

bool my_eeprom_is_ready(void){
    if (NVMCTRL.STATUS & NVMCTRL_EEBUSY_bm) return false;
    return true;
}

void my_eeprom_update_byte (uint8_t *eeadr, uint8_t val){
    if(eeprom_read_byte(&ee_dat) == val)    return;
    _PROTECTED_WRITE_SPM(NVMCTRL.CTRLA, NVMCTRL_CMD_EEERWR_gc);
    eeadr[MAPPED_EEPROM_START] = val;   
    _PROTECTED_WRITE_SPM(NVMCTRL.CTRLA, NVMCTRL_CMD_NONE_gc);
}

int main(void){
    while (!my_eeprom_is_ready());
    my_eeprom_update_byte(&ee_dat, 0x5A);

    while (!my_eeprom_is_ready());
    my_eeprom_update_byte(&ee_dat, 0xA5);

    while (!my_eeprom_is_ready());
    asm("nop");
    while (1);
}

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

OK, guys, this from a newcomer desiring to just make it work while leaving the vendor libraries intact.

  • I am new to the idea of a "linker script". Basically this is an assembler file in a C project with a dot-s extension. i.e., the ee.s file above exactly as is. I added it to my c-project.
  • All I did was add extern function definitions to my higher level C library that I am making to work a da eeprom. 

File eepromDA.h

/*
 * eepromDA.h
 * eeprom routines are not yet available for AVRXXXDXXX chips. 
 * 
 *
 * Created: 1/2/2021 5:10:08 PM
 *  Author: bill
 */ 
#include <avr/io.h>
#include <stdint.h>
#include <stdbool.h>
//#include "../lib/eerw_dx.h"

#ifndef EEPROMDA_H_
#define EEPROMDA_H_

//#define EEMEM __attribute(( section(".eeprom") ))

//standard eeprom functions (0 based address)
void eepromda_write_byte(uint8_t* addr, uint8_t v);
#endif /* EEPROMDA_H_ */

 

 

File eepromDA.c

/*
 * eepromDA.c
 *
 * Routines to do eeprom in AVRxxxDAxx chips
 *
 * Created: 1/3/2021 4:09:18 PM
 *  Author: bill
 */ 

#include "eepromDA.h"
#define EEMEM_START 0X1400

// Function Prototypes - Assembler functions in ee.s defined .global referenced here
extern void eeUnlock();
extern void eeLock();

void eepromda_write_byte(uint8_t* addr, uint8_t v)
{
    uint8_t check= (*(volatile uint8_t*)(addr+EEMEM_START));
    if(check != v)
    {
        eeUnlock();
        (*(volatile uint8_t*)(addr+EEMEM_START))=v;
        eeLock();
    }
}

 

 

Usage to write bytes:

//Declarations

uint8_t EEMEM NonVolatileChar; //Declare using the built-in library
char sramByte;

 

 

//Code Snippet
    while(!eeprom_is_ready()); //Not strictly necessary
    eepromda_write_byte(&NonVolatileChar,(uint8_t)100); //New write function
    sramByte=eeprom_read_byte(&NonVolatileChar); // Built-in library read function

 

Presto, it works now. All I have to do is add all the other data types. No messing with vendor libraries. When they finally get DA chips in the standard library my code will have to change if I want to use that library, but to me that's no big deal. 

 

Thanks for the help!

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I am set up for AVR128DA28 with Atmel Studio 7 using the avr-gcc toolchain. I just downloaded and installed it a week or two ago. This is undefined: 

_PROTECTED_WRITE_SPM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

It is defined.
It works.
What's happening to you?

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

My compiler says that symbol 

_PROTECTED_WRITE_SPM

is not defined and the compilation errors out. So I got around it with ee.s . Mine works that way. I am curious as to why with AVR128DA28 set up (and I did verify that it is set up in the tool) the symbol is undefined.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

_PROTECTED_WRITE_SPM is from xmega.h, which is pulled in from io.h (the include is near the bottom of the file). I have included io.h in the eerw_dx.h file.

 

 

https://github.com/epccs/MacGyver/blob/master/Applications/lib/eerw_dx.h

 

https://github.com/epccs/MacGyver/blob/master/Applications/lib/eerw_dx.c

 

I am not using Microchip Studio, but I think it is just a matter of adding the files to your project; the project does need to be started with a valid device selection so that __AVR_ARCH__ has a number that will pull in xmega.h.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I am using Atmel Studio 7 on Windows. Your observation may explain it as DVR128DA != mega. However, I did solve it with ee.s and little custom write routines. Since I have spent a long time on this to the detriment of getting the project done my plan is to go with what I have. Hopefully for my next project AVR-GCC for AVR128DAXX will support. 

 

Between us I find it really bad that Microchip would release a new set of chips with eeprom write broken on their flagship tool. It's not like they released this stuff last week and it's not like they never issue patches. Kaba-san twigged me into the fact that the chip was new, which helped me break the logjam. 

 

Anyway, thanks for the help!

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

btribley wrote:
Between us I find it really bad that Microchip would release a new set of chips with eeprom write broken on their flagship tool.

Well, they created the problem for themselves by changing the AVR-Dx NMV controller too much relative to the xmega (original one) and AVR-0/1 series (newer but very similar to xmega). Basically trying to fix something that wasn't broken... never works well.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

btribley wrote:
Between us I find it really bad that Microchip would release a new set of chips with eeprom write broken on their flagship tool.

 

I am not so sure that eeprom.h was something that they (Atmel at the time) did in the first place; didn't they start working on avr-libc after it was developed (maybe that's the wrong idea?), and now the grassroots (SVN repo on savannah) has declined. Microchip is probably not sure if they should be doing peripheral control software (Why? ask what others think of ASF).

 

I think the world has shifted from patches (SVN) to push-pull (Git) style development. If savannah switched to Git, it might start moving again (though it is probably hopeless at this point), or if Microchip gave their GCC compiler a Git repo that others could contribute to, that might snag a few gems from time to time.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

ron_sutherland wrote:
I think the world has shifted from patches (SVN) to push-pull (Git) style development.

 

Unfortunately the world has switched from delivering finished, tested products to just getting stuff out and having people pick up the pieces along the way. What good is a uP without development tools? Most people who reach for a uP want to get something done besides battle with half-baked software and support. It's all a distraction from the real job of getting the system built, tested and out the door!

 

I actually switched my design from a PIC chip to an Atmel because of Atmel Studio 7, avr-gcc and at least a stab at keeping it all standard. I made two small boards to map the pins and include decoupling capacitors close to the uP so I didn't have to respin the project. The only wrinkle so far has been the eeprom. I am hoping that I2C works smoothly, if it does then I'm home free. My PIC design will be working on Atmel! 

 

One thing I can say is that AVR Freaks is a good place to share ideas and collaborate. 

Last Edited: Tue. Jan 5, 2021 - 08:38 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Final result for me is that you must indeed test for ready and write each individual byte. Fortunately I only need to write byte, word and dword. Here's my code, no messing with low level libraries, uses ee.s in this thread to implement the unlock/lock and it only needs to handle writes, reads and the EEMEM directive come from the standard eeprom.h. Each byte is tested for equivalence to what's already there, the read blocks until the device is ready which gives two functions for the price of one:

 

 

/*
 * eepromDA.h
 * eeprom write routines are broken for AVRXXXDXXX chips.
 * 
 *
 * Created: 1/2/2021 5:10:08 PM
 *  Author: bill
 */ 
#include <avr/io.h>
#include <stdint.h>
#include <stdbool.h>

#ifndef EEPROMDA_H_
#define EEPROMDA_H_

//standard eeprom functions (0 based address)
void eepromda_write_byte(uint8_t* addr, uint8_t v);
void eepromda_write_word(void* addr, uint16_t v);
void eepromda_write_dword(void* addr, uint32_t v);
#endif /* EEPROMDA_H_ */

 

/*
 * eepromDA.c
 *
 * Routines to do eeprom in AVRxxxDAxx chips
 *
 * Created: 1/3/2021 4:09:18 PM
 *  Author: bill
 */ 

#include "eepromDA.h"
#define EEMEM_START 0X1400

// Function Prototypes
extern void eeUnlock();
extern void eeLock();

void eepromda_write_byte(uint8_t* addr, uint8_t v)
{
    uint8_t check= (*(volatile uint8_t*)(addr+EEMEM_START));
    if(check != v)
    {
        eeUnlock();
        (*(volatile uint8_t*)(addr+EEMEM_START))=v;
        eeLock();
    }
}
void eepromda_write_word(void* addr, uint16_t v)
{
    uint16_t check= (*(volatile uint16_t*)(addr+EEMEM_START));
    if(check == v)
        return;
    eeUnlock();
    union {
        uint16_t value;
        uint8_t array[2];
        }wordChar;
    wordChar.value=v;
    for(int i=0;i<2;i++) 
    {
        if((*(volatile uint8_t*)(i+addr+EEMEM_START))!=wordChar.array[i])
            (*(volatile uint8_t*)(addr+i+EEMEM_START)=wordChar.array[i]);
    }
    eeLock();
}

void eepromda_write_dword(void* addr, uint32_t v)
{
    uint32_t check= (*(volatile uint32_t*)(addr+EEMEM_START));
    if(check == v)
        return;
    eeUnlock();
    union {
        uint32_t value;
        uint8_t array[4];
    }dwordChar;
    dwordChar.value=v;
    for(int i=0;i<4;i++) 
    {
        if((*(volatile uint8_t*)(i+addr+EEMEM_START))!=dwordChar.array[i])
            (*(volatile uint8_t*)(addr+i+EEMEM_START)=dwordChar.array[i]);
    }
    eeLock();
}

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

This thread has EEPROM info also.

 

https://www.avrfreaks.net/forum/avr32da-writing-eeprom-values-during-programming

 

I am thinking bread crumbs here, and there may be a good idea.