AT90CAN128 BOOTLOADER - SPMEN NOT CLEARING

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

Overview: For a project at work I need a CAN bootloader for the at90can128.  It's my responsibility to get the bootloader working and then help our software engineer write an application to support what I come up with.  I am proficient programming in C but don't have much experience in assembly and although I've spent a good amount of time reading the datasheet and app notes and forums I find a good bit of it regarding memory and the bootloader support confusing.

 

IDE: CodeVisionAVR V2.05.8 - I have Atmel Studio 6.2 also but I have not used it since I think version 4 something...

 

Progress: I found a serial bootloader written by HP Infotech based on AVR109 for the Atmega128 and I'm trying to use that as a template. My first goal is to successfully erase flash.  I get stuck waiting for SPMEN to clear - I can see bits 5-6 of PORTE toggling indefinitely... any direction would be completely appreciated.   BOOTSZ = 00, BOOTRST = 0. Code below:

 

//_____ H E A D E R S __________________________________________________________
#include <90can128.h>
#include <stdio.h>
#include <delay.h>
#include <stdlib.h>

//_____ M A C R O S _ __________________________________________________________

#asm
     #define WR_SPMCR_REG_R22 sts 0x57,r22
#endasm

#define    PAGESIZE    256
#define    BLOCKSIZE PAGESIZE
#define    APP_END    126976 //131072(flash)-4096(bootloader)

#define  LARGE_MEMORY	
#define _GET_LOCK_BITS() __AddrToZByteToSPMCR_LPM( (void flash *) 0x0001, 0x09 )
#define _GET_LOW_FUSES() __AddrToZByteToSPMCR_LPM( (void flash *) 0x0000, 0x09 )
#define _GET_HIGH_FUSES() __AddrToZByteToSPMCR_LPM( (void flash *) 0x0003, 0x09 )
#define _GET_EXTENDED_FUSES() __AddrToZByteToSPMCR_LPM( (void flash *) 0x0002, 0x09 )
#define _SET_LOCK_BITS(data) __DataToR0ByteToSPMCR_SPM( data, 0x09 )
#define _ENABLE_RWW_SECTION() __DataToR0ByteToSPMCR_SPM( 0x00, 0x11 )

#define _WAIT_FOR_SPM() while( SPMCSR & (1<<SPMEN) )
                        
#define _LOAD_PROGRAM_MEMORY(addr) (*( (unsigned char flash *) (addr) ))

#ifdef _MODEL_SMALL_
#define _FILL_TEMP_WORD(addr,data) __AddrToZWordToR1R0ByteToSPMCR_SPM( (void flash *) (addr), data, 0x01 )
#define _PAGE_ERASE(addr) __AddrToZByteToSPMCR_SPM( (void flash *) (addr), 0x03 )
#define _PAGE_WRITE(addr) __AddrToZByteToSPMCR_SPM( (void flash *) (addr), 0x05 )
#else
#define _FILL_TEMP_WORD(addr,data) __AddrToZ24WordToR1R0ByteToSPMCR_SPM( (void flash *) (addr), data, 0x01 )
#define _PAGE_ERASE(addr) __AddrToZ24ByteToSPMCR_SPM( (void flash *) (addr), 0x03 )
#define _PAGE_WRITE(addr) __AddrToZ24ByteToSPMCR_SPM( (void flash *) (addr), 0x05 )
#endif

//_____ G L O B A L   V A R I A B L E S_________________________________________

//_____ E X T E R N A L   V A R I A B L E S_____________________________________

//_____ F U N C T I O N   P R O T O T Y P E S __________________________________
unsigned char BlockLoad(unsigned int size, long *address);
void BlockRead(unsigned int size, long *address);
void (*funcptr)( void ) = 0x0000; // Set up function pointer to RESET vector.
unsigned char __AddrToZByteToSPMCR_LPM(void flash *addr, unsigned char ctrl);
void __DataToR0ByteToSPMCR_SPM(unsigned char data, unsigned char ctrl);
void __AddrToZByteToSPMCR_SPM(void flash *addr, unsigned char ctrl);
void __AddrToZWordToR1R0ByteToSPMCR_SPM(void flash *addr, unsigned int data, unsigned char ctrl);
void __AddrToZ24WordToR1R0ByteToSPMCR_SPM(void flash *addr, unsigned int data, unsigned char ctrl);
void __AddrToZ24ByteToSPMCR_SPM(void flash *addr, unsigned char ctrl);

//_____ E X T E R N A L   F U N C T I O N S ____________________________________


void main(void)
{
//_____ LOCAL   V A R I A B L E S_______________________________________________
long address;
unsigned int temp_int;
unsigned char val;
char run_bootloader=1;

// Crystal Oscillator division factor: 1
#pragma optsize-
CLKPR=0x80;
CLKPR=0x00;
#ifdef _OPTIMIZE_SIZE_
#pragma optsize+
#endif

// Port E initialization
PORTE=0x00;
DDRE=0x60;// bit5-estop relay, bit6-enable relay


    
    while(1)
      {
      // Place your code here
        
        //remove after testing  -  wait awhile in case I do manage to write/erase flash
        delay_ms(5000);
      
        
        if(run_bootloader)
        {
            // Chip erase.
            for(address = 0; address < APP_END;address += PAGESIZE)
            { // NOTE: Here we use address as a byte-address, not word-address, for convenience.
                _WAIT_FOR_SPM()
                {
                    PORTE ^= 0x60;//relays toggle;0110
                    delay_ms(500);
                }
                _PAGE_ERASE( address );
            }
        }     
        else
        {
            _WAIT_FOR_SPM();
            _ENABLE_RWW_SECTION();
            //put interrupts back to the start of application mem
            MCUCR = (1<<IVCE);
            MCUCR = 0;
            funcptr(); // Jump to Reset vector 0x0000 in Application Section.
        }
      }
}


unsigned char BlockLoad(unsigned int size, long *address)
{
    unsigned char buffer[BLOCKSIZE];
    unsigned int data;
    long tempaddress;
    unsigned int ctr=0;
    char test_app[387]={
                        0x0C,0x94,0x5E,
                        0x4F,0x00,0x0C,0x94,0x00,0x00,0x0C,0x94,0x00,0x00,0x0C,0x94,0x00,0x00,0x0C,0x94,
                        0x00,0x00,0x0C,0x94,0x00,0x00,0x0C,0x94,0x00,0x00,0x0C,0x94,0x00,0x00,0x0C,0x94,
                        0x00,0x00,0x0C,0x94,0x00,0x00,0x0C,0x94,0x00,0x00,0x0C,0x94,0x00,0x00,0x0C,0x94,
                        0x00,0x00,0x0C,0x94,0x00,0x00,0x0C,0x94,0x00,0x00,0x0C,0x94,0x00,0x00,0x0C,0x94,
                        0x00,0x00,0x0C,0x94,0x00,0x00,0x0C,0x94,0x00,0x00,0x0C,0x94,0x00,0x00,0x0C,0x94,
                        0x00,0x00,0x0C,0x94,0x00,0x00,0x0C,0x94,0x00,0x00,0x0C,0x94,0x00,0x00,0x0C,0x94,
                        0x00,0x00,0x0C,0x94,0x00,0x00,0x0C,0x94,0x00,0x00,0x0C,0x94,0x00,0x00,0x0C,0x94,
                        0x00,0x00,0x0C,0x94,0x00,0x00,0x0C,0x94,0x00,0x00,0x0C,0x94,0x00,0x00,0x0C,0x94,
                        0x00,0x00,0x0C,0x94,0x00,0x00,0x0C,0x94,0x00,0x00,0x0C,0x94,0x00,0x00,0x0C,0x94,
                        0x00,0x00,0xE8,0x03,0x02,0x00,0x03,0x00,0x94,0x00,0x00,0x00,0xF8,0x94,0xEE,0x27,
                        0xEF,0xBB,0xF1,0xE0,0xF5,0xBF,0xE5,0xBF,0xE0,0x93,0x74,0x00,0xE0,0x93,0x75,0x00,
                        0xF8,0xE1,0xF0,0x93,0x60,0x00,0xE0,0x93,0x60,0x00,0x8D,0xE0,0xA2,0xE0,0xBB,0x27,
                        0xED,0x93,0x8A,0x95,0xE9,0xF7,0x80,0xE0,0x90,0xE1,0xA0,0xE0,0xB1,0xE0,0xED,0x93,
                        0x01,0x97,0xE9,0xF7,0xE6,0xE9,0xF0,0xE0,0x85,0x91,0x95,0x91,0x00,0x97,0x61,0xF0,
                        0xA5,0x91,0xB5,0x91,0x05,0x90,0x15,0x90,0xBF,0x01,0xF0,0x01,0x05,0x90,0x0D,0x92,
                        0x01,0x97,0xE1,0xF7,0xFB,0x01,0xF0,0xCF,0x8B,0xBF,0xE0,0xE0,0xEE,0xBB,0xEF,0xEF,
                        0xED,0xBF,0xE0,0xE1,0xEE,0xBF,0xC0,0xE0,0xD5,0xE0,0x0C,0x94,0x88,0x00,0xE0,0xE8,
                        0xE0,0x93,0x61,0x00,0xE0,0xE0,0xE0,0x93,0x61,0x00,0xE2,0xB9,0xE1,0xB9,0xE5,0xB9,
                        0xE4,0xB9,0xE8,0xB9,0xE7,0xB9,0xEB,0xB9,0xEA,0xB9,0xEE,0xB9,0xE0,0xEE,0xED,0xB9,
                        0xE0,0xE0,0xE1,0xBB,0xE0,0xBB,0xE4,0xBB,0xE3,0xBB,0xA1,0xE0,0xB0,0xE0,0x0E,0x94,
                        0xB7,0x00,0xEE,0xB1,0xA0,0xE8,0xEA,0x27,0xEE,0xB9,0x03,0x2C,0x04,0x28,0x21,0xF0,
                        0xE3,0x2D,0xF4,0x2D,0x31,0x97,0x06,0xC0,0xEE,0xB1,0xA0,0xE6,0xEA,0x27,0xEE,0xB9,
                        0xE8,0xEE,0xF3,0xE0,0x3E,0x2E,0x4F,0x2E,0xE8,0xCF,0xFF,0xCF,0x10,0x96,0x39,0xF0,
                        0x80,0xEA,0x9F,0xE0,0x01,0x97,0xF1,0xF7,0xA8,0x95,0x11,0x97,0xC9,0xF7,0x08,0x95
                    };
                    

    // NOTE: For flash programming, 'address' is given in words.
    (*address) <<= 1; // Convert address to bytes temporarily.
    tempaddress = (*address);  // Store address in page.
    do
    {
        data = test_app[ctr++];//recchar();
        data |= ((unsigned int)test_app[ctr++]<< 8);//(recchar() << 8);
        _FILL_TEMP_WORD(*address,data);
        (*address)+=2; // Select next word in memory.
        size -= 2; // Reduce number of bytes to write by two.
    } while(size); // Loop until all bytes written.

	_PAGE_WRITE(tempaddress);
	_WAIT_FOR_SPM();
	_ENABLE_RWW_SECTION();

        (*address) >>= 1; // Convert address back to Flash words again.
        return '\r'; // Report programming OK
}


void BlockRead(unsigned int size, long *address)
{
    // Flash memory type.
    (*address) <<= 1; // Convert address to bytes temporarily.
//replace with CAN code later
//    do
//    {
//        sendchar( _LOAD_PROGRAM_MEMORY(*address) );
//        sendchar( _LOAD_PROGRAM_MEMORY((*address)+1) );
//        (*address) += 2; // Select next word in memory.
//        size -= 2; // Subtract two bytes from number of bytes to read
//    } while (size); // Repeat until all block has been read

    (*address) >>= 1; // Convert address back to Flash words again.
}


unsigned char __AddrToZByteToSPMCR_LPM(void flash *addr, unsigned char ctrl)
{
#asm
     ldd  r30,y+1
     ldd  r31,y+2
     ld   r22,y
     WR_SPMCR_REG_R22
     lpm
     mov  r30,r0
#endasm
}

void __DataToR0ByteToSPMCR_SPM(unsigned char data, unsigned char ctrl)
{
#asm
     ldd  r0,y+1
     ld   r22,y
     WR_SPMCR_REG_R22
     spm
#endasm
}

void __AddrToZWordToR1R0ByteToSPMCR_SPM(void flash *addr, unsigned int data, unsigned char ctrl)
{
#asm
     ldd  r30,y+3
     ldd  r31,y+4
     ldd  r0,y+1
     ldd  r1,y+2
     ld   r22,y
     WR_SPMCR_REG_R22
     spm
#endasm
}

void __AddrToZByteToSPMCR_SPM(void flash *addr, unsigned char ctrl)
{
#asm
     ldd  r30,y+1
     ldd  r31,y+2
     ld   r22,y
     WR_SPMCR_REG_R22
     spm
#endasm
}

void __AddrToZ24WordToR1R0ByteToSPMCR_SPM(void flash *addr, unsigned int data, unsigned char ctrl)
{
#asm
     ldd  r30,y+3
     ldd  r31,y+4
     ldd  r22,y+5
     out  rampz,r22
     ldd  r0,y+1
     ldd  r1,y+2
     ld   r22,y
     WR_SPMCR_REG_R22
     spm
#endasm
}

void __AddrToZ24ByteToSPMCR_SPM(void flash *addr, unsigned char ctrl)
{
#asm
cli
     ldd  r30,y+1
     ldd  r31,y+2
     ldd  r22,y+3
     out  rampz,r22
     ld   r22,y
     WR_SPMCR_REG_R22
     spm
#endasm
}

 

 

 

 

 

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

Am I posting in the correct forum?

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

Ok, I just made the following edit.  Now when I program the chip, nothing for 5 seconds then I can hear the relay buzz for about 1/2 second and then nothing after that.  This is a good sign to me, the first delay(5000); works then the buzzing relay indicates it is looping through erasing flash(I hope) but I think maybe I'm erasing the bootloader since it only runs once?  Anyways I have renewed hopes after struggling for days with this... I'm going to examine the z pointer to see if it's correct.

 

if(run_bootloader)
        {
            // Chip erase.      **********SEE PAGE 328 OF THE DATASHEET ABOUT Z-POINTER ADDRESSING
            for(address = 0; address < APP_END;address += PAGESIZE)
            { // NOTE: Here we use address as a byte-address, not word-address, for convenience.
                _WAIT_FOR_SPM();
                _PAGE_ERASE( address );
                PORTE ^= 0x60;//relays toggle;0110
            }
        }

 

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

... anyone out there?

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

In the function below, how do I know where y is pointing??

 

void __AddrToZ24ByteToSPMCR_SPM(void flash *addr, unsigned char ctrl)
{
#asm
cli
     ldd  r30,y+1
     ldd  r31,y+2
     ldd  r22,y+3
     out  rampz,r22
     ld   r22,y
     WR_SPMCR_REG_R22
     spm
#endasm
}

 

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

CodeVision, right?  See "Calling Assembly Functions from C"

in the Help/manual.

 

In CV, y is the data stack pointer.  Nearly always.

 

As the stack grows downward, the top of stack is in the lowest address.  y points to that.  And as the parameters are pushed in a certain order, we can see that y points to "ctrl".

 

The memory model must be one with 3-byte flash pointers.  Hmmm--high byte not used? 

 

In order to improve the code efficiency several memory models are implemented.

 

The TINY memory model uses 8 bits for storing pointers to the variables placed in RAM. In this memory model you can only have access to the first 256 bytes of RAM.

 

The SMALL memory model uses 16 bits for storing pointers the variables placed in RAM. In this memory model you can have access to 65536 bytes of RAM.

 

In both TINY and SMALL memory models pointers to the FLASH memory area use 16 bits.

Because in these memory models pointers to the FLASH memory are 16 bits wide, the total size of the constant arrays and literal char strings is limited to 64K.

However the total size of the program can be the full amount of FLASH.

 

In order to remove the above mentioned limitation, there are available two additional memory models: MEDIUM and LARGE.

The MEDIUM memory model is similar to the SMALL memory model, except it uses pointers to constants in FLASH that are 32 bits wide. The pointers to functions are however 16 bit wide because they hold the word address of the function, so 16 bits are enough to address a function located in all 128kbytes of FLASH.

The MEDIUM memory model can be used only for chips with 128kbytes of FLASH.

 

The LARGE memory model is similar to the SMALL memory model, except it uses pointers to the FLASH memory area that are 32 bits wide.

The LARGE memory model can be used for chips with 256kbytes or more of FLASH.

 

In all memory models pointers to the EEPROM memory area are 16 bit wide.

 

You can put lipstick on a pig, but it is still a pig.

I've never met a pig I didn't like, as long as you have some salt and pepper.

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

(look at the calling sequence in the .LST file.  If not called, make a dummy call for examination)

You can put lipstick on a pig, but it is still a pig.

I've never met a pig I didn't like, as long as you have some salt and pepper.

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

-Thank you- 

 

Being new to assembly (rookie mistake) I looked everywhere but the codevision user manual for info about the y register... lots of googling done when I had the information available right in front of me...! Thank you for the direction, I will spend more time with the user manual.

 

also...

I made the following edit to correctly define the end of application flash (for an 8k bootloader with 128kflash)

#define    APP_END    0x1DFFF//131072(flash)-8192(bootloader)

 

Also, after switching to the medium memory model(again in the user manual!) the erase function is working as planned.  I'm using the "read" function (atmel studio + jtagicemkii) to verify my flash is what I expect.

 

Thanks theusch for helping me reach my first goal - to erase flash using spm.  Now I'm going to begin trying to write flash... then add serial(CAN) support.  I will continue to post my progress in hopes of helping the next guy...

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

For a project at work I need a CAN bootloader for the at90can128.  It's my responsibility to get the bootloader working ...

http://www.atmel.com/Images/doc8...

http://www.gaw.ru/pdf/Atmel/app/...

http://www.avrfreaks.net/forum/a...

http://www.avrfreaks.net/forum/c...

...

 

But indeed, some work may be needed to port to CV.  Perhaps see

http://www.avrfreaks.net/user/19...

http://www.dessy.ru/include/imag...

 

Hmmm--nothing says you couldn't use a pre-made bootloader built with GCC or IAR or Rowley or whatever, and develop the app with CV.

 

You can put lipstick on a pig, but it is still a pig.

I've never met a pig I didn't like, as long as you have some salt and pepper.