assembly.s



  .text
  
SPMCR = 0x57   ;RWW区忙标志,读RWW区允许,允许写程序存储区

; void write_page (unsigned int adr, unsigned char function);
; bits 8:15 adr addresses the page...(must setup RAMPZ beforehand!!!)
_write_page::
    XCALL __WAIT_SPMEN__
    movw    r30, r16        ;move address to z pointer (R31 = ZH, R30 = ZL)
    STS     SPMCR, R18      ;argument 2 decides function
    SPM                     ;perform pagewrite
    RET

;-----------------------------------------

; void fill_temp_buffer (unsigned int data, unsigned int adr);
; bits 7:1 in adr addresses the word in the page... (2=first word, 4=second word etc..)
_fill_temp_buffer::
    XCALL __WAIT_SPMEN__
    movw    r30, r18        ;move adress to z pointer (R31=ZH R30=ZL)
    movw    r0, r16         ;move data to reg 0 and 1
    LDI     R19, 0x01
    STS     SPMCR, R19
    SPM                     ;Store program memory
    RET     
 
;----------------------------------------- 

;unsigned char read_flash(unsigned int add);
_read_flash::
    mov r31,r17
    mov r30,r16
    lpm r16,z
    clr r17
    ret 
  
;unsigned int read_program_memory (unsigned int adr ,unsigned char cmd);
_read_program_memory::
    movw    r30, r16        ;move adress to z pointer
    SBRC    R18, 0          ;read lockbits? (second argument = 0x09)
    STS     SPMCR, R18      ;if so, place second argument in SPMEN register
    LPM     r16, Z+
    LPM     r17, Z
    RET
 
;-----------------------------------------
       
_enableRWW::
 XCALL __WAIT_SPMEN__
    LDI R27,0x11
    STS SPMCR,R27
    SPM
    RET  
 
;-----------------------------------------          

__WAIT_SPMEN__:
    LDS     R27,SPMCR       ; load SPMCR to R27
    SBRC    R27,0           ; check SPMEN flag
    RJMP    __WAIT_SPMEN__  ; wait for SPMEN flag cleared       
    RET
 
;-----------------------------------------



assembly.h



void write_page (unsigned int adr, unsigned char function);
void fill_temp_buffer (unsigned int data,unsigned int adr);
unsigned int read_program_memory (unsigned int adr,unsigned char cmd);
//void write_lock_bits (unsigned char val);
void enableRWW(void);
unsigned char read_flash(unsigned int add);




main.c


/******************************************************************************
Atmega16 BootLoad程序 
日  期:2004年的最后一天

/*****************************************************************************/
#include <string.h>
#include <macros.h>
#include "assembly.h"//包含汇编代码头文件

//×××××××××××××××××××××××××××××××××××××××××××××××××××××××××
//请根据目标板选择芯片型号
//#define  ChipType     Atmega8      
#define  ChipType     Atmega16      
//#define  ChipType     Atmega32      
//#define  ChipType     Atmega64      
//#define  ChipType     Atmega128      
//#define  ChipType     Atmega8535      
//*********************************************************
//选择BOOT区大小
//#define  BootSize     'a'    //128  
//#define  BootSize     'b'    //256
//#define  BootSize       'c'    //512
//#define  BootSize     'd'    //1024
//#define  BootSize     'e'    //2048(不知道是否有2048字BOOT空间的芯片)
//选择BootLoad版本号
//#define  BootVer        'f'    //1.0版本
//#define  BootVer      'g'    //1.1版本
//#define  BootVer      'h'    //1.2版本
//#define  BootVer      'i'    //1.3版本
//#define  BootVer      'j'    //1.4版本
//#define  BootVer      'k'    //1.5版本
//#define  BootVer      'l'    //1.6版本
//#define  BootVer      'm'    //1.7版本
//#define  BootVer      'n'    //1.8版本
//#define  BootVer      'o'    //1.9版本
//#define  BootVer      'p'    //2.0版本(应该是最终版本了)
//**********************************************************

#define  Atmega16       0x31

//*****************************************************************************
//#define  InteClk                 //是否使用内部时钟
//#define  OscAddress     0x1fff   //时钟校准值存储地址
//#define  OscAddress     0x3fff   //时钟校准值存储地址
//*****************************************************************************
//8时钟下的波特率设置
//#define  BAU  103    //4800
//#define  BAU  51       //9600
//#define  BAU  34     //14400
//#define  BAU  25     //19200

//16M时钟下的波特率设置
#define  BAU  103   //9600

//使用的EEPROM
#define EEPROM_UPDATE_0X00_0X06 7
#define EEPROM_REPEAT_ENTER_BOOT_TIMES 0x07
#define EEPROM_DEBUG0 0x08
#define EEPROM_DEBUG1 0x09
#define ERASE_FLASH_PAGE_NUM 112//擦除页数


const unsigned char ucEepromWriteSuccess[EEPROM_UPDATE_0X00_0X06]={"success"};
const unsigned char ucEepromWriteUpdates[EEPROM_UPDATE_0X00_0X06]={"updates"};


//*****************************************************************************

#if (ChipType == Atmega16)
  #include  "iom16v.h"
  #define   PageByte     128   
  #define   AddressShift   7
#endif

void FlashLoad(void);
unsigned char GetPageNumber(void);
void ExecCode(void);
char GetOnePageData(void);
unsigned char WriteFlash(void);
char CheckFlash(void);
void SendChar(unsigned char c);
void delay(void);                 //1ms延时函数
//unsigned char RecChar(void);
static unsigned int GetCRC16Code(unsigned char* pCalcBuf, unsigned char ucSize);


//unsigned char PageBuffer[PageByte+2];
unsigned int  PageAddress=0;

#define BUFLEN 200
char uRxData;
char ucRecvBuf[BUFLEN];
unsigned int uiBufLen=0;
unsigned int uiStartBufDateLen=0;


//读EEPROM函数
static void EEPROM_Write(unsigned int Add,unsigned char Data)
{
 if(Add<512) //如果地址大于512,无效
 {
  while(EECR&BIT(EEWE)); //查询上一次EEPROM写操作是否完成
  EEAR=Add;    //写入地址
  EEDR=Data;    //写入数据
  EECR|=BIT(EEMWE);  //EEPROM写主机使能:准备
  EECR|=BIT(EEWE);  //EEPROM写使能:写入
 }
}

//读EEPROM函数
static unsigned char EEPROM_Read(unsigned int Add)
{
 unsigned char Temp=0;
 if(Add<512)  //如果地址大于512,读取0值
 {
  while(EECR&BIT(EEWE)); //查询上一次EEPROM写操作是否完成
  EEAR=Add;    //写入地址
  EECR|=BIT(EERE);  //EEPROM读使能:读出
  Temp=EEDR;    //读取数据
 } 
 return Temp;    //返回数据
}

#if 0
void PrintfStringToArm(char * str)
{
 int i=0;
 for(i=0;i<strlen(str);i++)
 {
  SendChar(*(str+i));
 }
}
#endif


/*****************************************************************************/
//Flash编程                            
/*****************************************************************************/
void FlashLoad(void)
{
 unsigned int i;

 unsigned char ucPageDateSuccessFlag=0;
 SendChar('!');
 while (1)
 {
  if(!GetPageNumber())//获取页地址
  {
   return;
  }
  else
  {
   if (PageAddress == 0xff80)//写结束
   {
    SendChar('$');
    CLI();
    
    for(i=0;i<EEPROM_UPDATE_0X00_0X06;i++)
    {
     EEPROM_Write(i,ucEepromWriteSuccess[i]);
    }
    SEI();

    return;
   }
   
   ucPageDateSuccessFlag=GetOnePageData();

   if (ucPageDateSuccessFlag==1)//获取一页正确的数据
   {
    if(WriteFlash())
    {
     if (CheckFlash())
      SendChar('!');
     else
      SendChar('@');
    }
    else
    {
     return;
    }
   }
   else if(ucPageDateSuccessFlag==2)//获取一页错误的数据
   {   
    SendChar('@');
   }
   else//超时
   {
    return;
   }
  }
 }

 //SendChar('%');
}

/*****************************************************************************/
unsigned char GetPageNumber(void)
{
 unsigned int i;
 unsigned char j; 
 unsigned int uiAddress;

 for(j=0;j<100;j++)
 {
  for(i=0; (i+4)<=uiBufLen; i++)
  {
   if(memcmp(ucRecvBuf+i,"##",2)==0)//"##"后紧跟页地址
   {
    memcpy(&uiAddress,ucRecvBuf+i+2,2);
    
    PageAddress=(uiAddress<<AddressShift);//验证了Z7~Z13(见中文版Datesheet P245) 

    uiStartBufDateLen=i+4;//页数据的偏移位
   
    return 1;   
   }
  }
  delay();
 }
 return 0;
}

static unsigned int GetCRC16Code(unsigned char* pCalcBuf, unsigned char ucSize)

 unsigned char i = 0;
 unsigned char j = 0;
 unsigned int usReturnValue = 0xFFFF;
 unsigned int usNew = 0xA001;
 
 for (i=0; i<ucSize; ++i)
 {
  usReturnValue ^= pCalcBuf[i];
  for(j=0; j<8; ++j)
  {
   if(usReturnValue & 0x0001)
   {
    usReturnValue >>= 1;
    usReturnValue ^= usNew;
   }
   else
   {
    usReturnValue >>= 1;
   }
  }
 }
 return usReturnValue;
}


/*****************************************************************************/
char GetOnePageData(void)
{
 unsigned char i;
 unsigned int LocalCheckSum = 0;
 unsigned int CheckSum = 0;

 for(i=0;i<200;i++)
 {
  if(uiBufLen>=(uiStartBufDateLen+PageByte+2))
  {
   //for(i=0;i<(PageByte+2);i++)
   //{
   // PageBuffer[i]=ucRecvBuf[i+uiStartBufDateLen];
   //}

   //LocalCheckSum=GetCRC16Code(PageBuffer, PageByte);
   LocalCheckSum=GetCRC16Code(&ucRecvBuf[uiStartBufDateLen-2], PageByte+2);//将页地址也一起校验,共校验PageByte+2个字节
   
   //memcpy(&CheckSum,&PageBuffer[PageByte],2);
   memcpy(&CheckSum,&ucRecvBuf[uiStartBufDateLen+PageByte],2);
   
   if (LocalCheckSum == CheckSum)
   {
    return 1;
   }
   else
   {
    CLI();
    
    memset(ucRecvBuf,0,BUFLEN);
    
    uiBufLen=0;

    SEI();

    return 2;
   }
  }
  delay();
 }

 return 0;
}

/*****************************************************************************/
unsigned char  WriteFlash(void)
{
 unsigned int i;
 unsigned int TempInt;
 
 for (i=0;i<PageByte;i+=2)
 {

  //TempInt=PageBuffer[i]+(PageBuffer[i+1]<<8);//写程序是按‘字‘写的,一页128字节,64个字
  TempInt=ucRecvBuf[uiStartBufDateLen+i]+(ucRecvBuf[uiStartBufDateLen+i+1]<<8);

  fill_temp_buffer(TempInt,i);    //call asm routine.见汇编代码,将一页的数据存储好
 }
 
 if(PageAddress==0x0000)
 {
  if(ucRecvBuf[uiStartBufDateLen]==0xFF)
  {
   //CLI();
    
   //EEPROM_Write(EEPROM_DEBUG1,0xD1);

   //SEI();

   return 0;
   
  }
  else//擦除数据,当为首页时,一致性擦除0-103页所有数据
  {
   for(;PageAddress<ERASE_FLASH_PAGE_NUM*128;PageAddress+=128)
   {    
    write_page(PageAddress,0x03);  //0x03,Bit1=1,页擦除,Bit0=1,存储程序存储器使能(SPMEN)
   }
   PageAddress=0x0000;
  }
 }
 else if(PageAddress==0x3700)//当为110页首地址时,一致性擦除110-111页的数据
 {
  for(;PageAddress<0x3800;PageAddress+=128)
  {   
   write_page(PageAddress,0x03);  //0x03,Bit1=1,页擦除,Bit0=1,存储程序存储器使能(SPMEN)
  }
  PageAddress=0x3700;
 }

 //write_page(PageAddress,0x03);       //0x03,Bit1=1,页擦除,Bit0=1,存储程序存储器使能(SPMEN)
 i=0;
 if((PageAddress < 0x3800)&&(PageAddress>=i))//从112页开始,为Bootloader程序,不允许写
 {
  write_page(PageAddress,0x05);       //0x05,Bit2=1,页写入,Bit0=1,存储程序存储器使能(SPMEN)
  
  enableRWW();

  return 1;
 }
 else
 {
  return 0;
 }
}

/*****************************************************************************/

char CheckFlash(void)
{
 unsigned char i;       
 unsigned int TempInt;
 unsigned int TempInt2;

 for (i=0;i<PageByte;i+=2)
 {
  TempInt = read_program_memory(PageAddress + i,0x00);
  //TempInt2 = PageBuffer[i] +(PageBuffer[i+1]<<8);
  TempInt2=ucRecvBuf[uiStartBufDateLen+i]+(ucRecvBuf[uiStartBufDateLen+i+1]<<8);
  if (TempInt != TempInt2)//烧写校验错误
  {
   CLI();
   
   memset(ucRecvBuf,0,BUFLEN);
   
   uiBufLen=0;
   
   SEI();
   
   return 0;
  }
 }
 CLI();
 
 memset(ucRecvBuf,0,BUFLEN);
 
 uiBufLen=0;
 
 SEI();
 
 return 1;
}

//串口接收完毕中断函数
//串口接收完毕中断触发声明
#pragma interrupt_handler  Usart0RecvdIsr:iv_USART_RX
void Usart0RecvdIsr(void)
{

  UCSRB&=~ BIT(RXCIE);  //接收完毕中断不使能
  
  uRxData=UDR;   //读取UDR的数据

  if(uiBufLen<BUFLEN)
  { 
   ucRecvBuf[uiBufLen]=uRxData;

   uiBufLen++;  
  }
  else
  {
   uiBufLen=0;
  }
  
  UCSRB|=BIT(RXCIE);  //接收完毕中断使能
}

 
/*****************************************************************************/
void SendChar(unsigned char c)

 while (!(UCSRA&(1<<UDRE)));
 UDR=c;
}


void delay(void)  //45-50ms左右
{
     unsigned int i,j;
     for(i=0;i<100;i++)
     for(j=0;j<1000;j++);
}


/*****************************************************************************/
void ExecCode(void)
{
 WDTCR=0x0F;//跳出前开看门狗2014-02-18添加

 GICR=0X01;           //中断向量表移位允许
 GICR=0X00;           //中断向量指向应用程序区
 
 asm("jmp 0x0000");             
}

void main()
{

 unsigned char i;
 unsigned char j;
 unsigned char ucEepromRepeatEnterBootTimes;
 unsigned char ucEepromRead[EEPROM_UPDATE_0X00_0X06];//={0};
 unsigned char bAppErrFlag=0;
 
 CLI();
 
 #if 0//2014-02-18屏蔽
 for(i=0; i<30;i++)//延时2s左右,等MCU电源稳定
 {
  delay();
 }
 #endif

 for(i=0;i<EEPROM_UPDATE_0X00_0X06;i++)
 {
  ucEepromRead[i]=EEPROM_Read(i);
 }
 

 if(memcmp(ucEepromRead,"updates",EEPROM_UPDATE_0X00_0X06)==0) 
 {
 
 AppErr: 
  
  TIMSK=0x00;//必须复位,否则从应用区跳转时会出问题
  WDTCR=0x00;
  
  GICR=(1<<IVCE);     //中断向量表移位允许
  GICR=(1<<IVSEL);     //中断向量指向Boot区

  
  UCSRC=(1<<URSEL)|(1<<UCSZ1)|(1<<UCSZ0); //8位数据+1位停止
  //UCSRB = (1<<RXEN)|(1<<TXEN);   //允许串口发送和接收
  UCSRB=(1<<RXEN)|(1<<TXEN)|(1<<RXCIE);//允许收发,打开接收中断
  UBRR = BAU;
  
  SEI();

  for(i=0;i<100;i++)//大约4-5s钟
  {
   SendChar('[');    //通知PC机,BOOT下载准备就绪

   for(j=0;j<uiBufLen;j++)
   {
    if(ucRecvBuf[j]==']')
    {
      goto UpDateMcu;       
    }       
   }

   delay();     //延时等待PC机响应
   
   if(bAppErrFlag)//应用程序异常
   {
    i=0;
   }
  }

  
  //CLI();
  //EEPROM_Write(EEPROM_DEBUG0,0xD0);//入口超时
  //SEI();
  goto EXIT;
  
 }
 #if 1
 else
 {
  if(read_program_memory (0x0000,0x00)==0xFFFF)//判断应用程序首页是否异常
  {  
   #if 0
   for(i=0; i<EEPROM_UPDATE_0X00_0X06;i++)
   {
    EEPROM_Write(i,ucEepromWriteUpdates[i]);
   }
   #endif
   
   delay(); 

   if(read_program_memory (0x0000,0x00)==0xFFFF)//判断应用程序首页是否异常
   {
   
    bAppErrFlag = 1;
    
    goto AppErr;
   }
  }

  ucEepromRepeatEnterBootTimes=EEPROM_Read(EEPROM_REPEAT_ENTER_BOOT_TIMES);
  
  if(ucEepromRepeatEnterBootTimes<0xFD)
  {
   for(i=0; i<EEPROM_UPDATE_0X00_0X06;i++)
   {
    EEPROM_Write(i,ucEepromWriteUpdates[i]);
   }
  }
  else
  {  
   #if 1
   ucEepromRepeatEnterBootTimes--;
   
   EEPROM_Write(EEPROM_REPEAT_ENTER_BOOT_TIMES,ucEepromRepeatEnterBootTimes);
   #endif
  }
  
  goto EXIT;
 }

 #endif
 
UpDateMcu: 

 FlashLoad();
EXIT: 
 ExecCode();
}