⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 bootldr.c

📁 AVR 通用 Bootloader 使用说明 *支持多种型号的AVR单片机 *支持多串口的AVR单片机 *支持RS232/RS485/RS422模式 *支持多种通信波特率和时钟频率 *自动计
💻 C
字号:
/*

                           e Y8b    Y8b Y88888P888 88e                       
                          d8b Y8b    Y8b Y888P 888 888D                      
                         d888b Y8b    Y8b Y8P  888 88"                       
                        d8WuHan888b    Y8b Y   888 b,                        
                       d8888888b Y8b    Y8P    888 88b,                      
             e88'Y88                                                         
            d888  'Y e88 88e 888 888 8e 888 888 8e  e88 88e 888 8e           
           C8888    d888 888b888 888 88b888 888 88bd888 888b888 88b          
            Y888  ,dY888 8Shao88 zi8 8yang8 888 888Y888 888P888 888          
             "88,d88 "88 88" 888 888 888888 888 888 "88 88" 888 888          
 888 88b,                    d8  888                         888             
 888 88P' e88 88e  e88 88e  d88  888     e88 88e  ,"Y88b e88 888 ,e e, 888,8,
 888 8K  d888 888bd888 888bd20078888    d888 888b"8" 888d888 888d88 88b888 " 
 888 88b,Y888 888PY888 888P 888  888  ,dY888 888P,ee 888Y888 888888   ,888   
 888 88P' "88 88"  "88 88"  888  888,d88 "88 88" "88 888 "88 888 "YeeP"888   


  Project:       AVR Common BootLoader
                 AVR 通用 Bootloader
  File:          bootldr.c
                 主程序
  Version:       1.0

  Compiler:      GCC 4.1.1 + AVR Studio 4.13.528

  Author:        Shaoziyang
                 Shaoziyang@126.com
                 http://shaoziyang.bloger.com.cn
  Date:          2007.3

  Modify:        Add your modify log here

  See readme.txt to get more information.

*/

#include "bootcfg.h"
#include "bootldr.h"

//用户程序起始地址
#define PROG_START 0x0000

//接收缓冲区大小不能小于 SPM_PAGESIZE
#if BUFFERSIZE < SPM_PAGESIZE
#define BUFSIZE SPM_PAGESIZE
#else
#define BUFSIZE BUFFERSIZE
#endif

//接收缓冲区
unsigned char buf[BUFSIZE];

#if BUFSIZE > 255
unsigned int bufptr, pagptr;
#else
unsigned char bufptr, pagptr;
#endif

unsigned char ch, cl;

//Flash地址
#if FLASHEND > 0xFFFF
unsigned long FlashAddr;
#else
unsigned int FlashAddr;
#endif

//更新一个Flash页的完整处理
void write_one_page(unsigned char *buf)
{
  boot_page_erase(FlashAddr);                  //擦除一个Flash页
  boot_spm_busy_wait();
  for(pagptr = 0; pagptr < SPM_PAGESIZE; pagptr += 2) //将数据填入Flash缓冲页中
  {
    boot_page_fill(pagptr, buf[pagptr] + (buf[pagptr + 1] << 8));
  }
  boot_page_write(FlashAddr);                  //将缓冲页数据写入一个Flash页
  boot_spm_busy_wait();                        //等待页编程完成
}

//跳转到用户程序
void quit()
{
  boot_rww_enable();
  (*((void(*)(void))PROG_START))();
}

//写入数据到串口
void WriteCom(unsigned char dat)
{
#if RS485
  RS485Enable();
#endif

  UDRREG(COMPORTNo) = dat;
  //等待数据发送完成
  while(!(UCSRAREG(COMPORTNo) & (1<<TXCBIT(COMPORTNo))));
  UCSRAREG(COMPORTNo) |= (1 << TXCBIT(COMPORTNo));

#if RS485
  RS485Disable();
#endif
}

//等待串口数据
unsigned char WaitCom()
{
  while(!DataInCom());
  return ReadCom();
}

#if VERBOSE
void putstr(const char *str)
{
  while(*str)
    WriteCom(*str++);
  WriteCom(0x0D);
  WriteCom(0x0A);
}
#endif

//计算CRC校验:1021
#if BUFSIZE > 255
void crc16(unsigned char *buf, unsigned int n)
{
  unsigned int j;
#else
void crc16(unsigned char *buf, unsigned char n)
{
  unsigned char j;
#endif

  unsigned char i;
  unsigned int crc, t;

  crc = 0;
  for(j = n; j > 0; j--)
  {
#if CRCMODE == 0
    crc = (crc ^ (((unsigned int) *buf) << 8));
    for(i = 8; i > 0; i--)
    {
      t = crc << 1;
      if(crc & 0x8000)
        t = t ^ 0x1021;
      crc = t;
    }
#elif CRCMODE == 1
    crc += (unsigned int)(*buf);
#elif

#endif
    buf++;
  }
  ch = crc / 256;
  cl = crc % 256;
}

int main(void)
{
  unsigned char cnt;
  unsigned char packNO;
  unsigned char crch, crcl;

#if InitDelay > 255
  unsigned int di;
#elif InitDelay
  unsigned char di;
#endif

#if BUFFERSIZE > 255
  unsigned int li;
#else
  unsigned char li;
#endif

  //关闭中断:异常情况下的保护
  asm volatile("cli": : );

#if WDGEn
  //允许看门狗
  wdt_enable(WDTO_1S);
#endif

  //时钟初始化:CTC模式
  TimerInit();

#if RS485
  DDRREG(RS485PORT) |= (1 << RS485TXEn);
  RS485Disable();
#endif

#if LEDEn
  //设置LED端口为输出状态
  DDRREG(LEDPORT) = (1 << LEDPORTNo);
#endif

  //串口初始化
  ComInit();


#if InitDelay
  //某些型号的单片机需要延时,否则串口发出的数据是乱码
  for(di = InitDelay; di > 0; di--)
    __asm__ __volatile__ ("nop");
#endif

#if LEVELMODE
  //引脚电平启动Bootloader模式

  DDRREG(LEVELPORT) |= (1 << LEVELPIN);

#if PINLEVEL
  if(PINREG(LEVELPORT) & (1 << LEVELPIN))
#else
  if(!(PINREG(LEVELPORT) & (1 << LEVELPIN)))
#endif
  {
#if VERBOSE
    //进入升级模式
    putstr(msg6);
#endif
  }
  else
  {
#if VERBOSE
    //进入用户程序
    putstr(msg7);
#endif

    quit();
  }

#else
  //串口启动Bootloader模式

#if VERBOSE
  //提示等待密码
  putstr(msg1);
#endif

  cnt = TimeOutCnt;
  cl = 0;
  while(1)
  {
    if(TIFRREG & (1<<OCF1A))    //T1溢出
    {
      TIFRREG |= (1 << OCF1A);

      if(cl == CONNECTCNT)
        break;

#if LEDEn
      //LED指示状态
      LEDAlt();
#endif

#if WDGEn
      //清除看门狗
      wdt_reset();
#endif

      cnt--;
      if(cnt == 0)
      {

#if VERBOSE
        //提示超时
        putstr(msg2);
#endif
        //跳转到用户程序
        quit();
      }
    }

    if(DataInCom())
    {
      if(ReadCom() == KEY[cl])
        cl++;
      else
        cl = 0;
    }
  }

#endif

#if VERBOSE
  //提示等待接收文件
  putstr(msg3);
#endif

  //每个时隙向PC机发送一个控制字符“C”,等待控制字〈soh〉
  cnt = TimeOutCntC;
  while(1)        //receive the start of Xmodem
  {
    if(TIFRREG & (1<<OCF1A))    //T1溢出
    {
      TIFRREG |= (1 << OCF1A);
      WriteCom(XMODEM_RWC) ; //发送 "C"

#if LEDEn
      //LED指示状态
      LEDAlt();
#endif

#if WDGEn
      //清除看门狗
      wdt_reset();
#endif

      cnt--;
      if(cnt == 0)
      {
#if VERBOSE
        //提示超时
        putstr(msg2);
#endif
        quit();
      }
    }

    if(DataInCom())
    {
      if(ReadCom() == XMODEM_SOH)
        break;
    }
  }
  //关闭定时器
  TCCR1B = 0;

  //开始接受数据
  packNO = 1;
  bufptr = 0;
  cnt = 0;
  FlashAddr = 0;
  do
  {
    ch =  WaitCom();                          //获取包序号
    cl = ~WaitCom();
    if ((packNO == ch) && (packNO == cl))
    {
      for(li = 0; li < BUFFERSIZE; li++)      //接收完一帧数据
      {
        buf[bufptr++] = WaitCom();
      }
      crch = WaitCom();
      crcl = WaitCom();
      crc16(&buf[bufptr - BUFFERSIZE], BUFFERSIZE);//计算CRC校验
      if((crch == ch) && (crcl == cl))
      {

#if BUFFERSIZE <= SPM_PAGESIZE
        if(bufptr >= SPM_PAGESIZE)            //缓冲区满,写入数据;否则继续接收
        {                                     //接收多个帧,写入缓冲区
          write_one_page(buf);                //写入缓冲区内容到Flash中
          FlashAddr += SPM_PAGESIZE;          //修改Flash页地址
          bufptr = 0;
        }
#else
        while(bufptr > 0)                     //接收一帧,写入多个页面
        {
          write_one_page(&buf[BUFSIZE - bufptr]);
          FlashAddr += SPM_PAGESIZE;          //修改Flash页地址
          bufptr -= SPM_PAGESIZE;
        }
#endif

        //正确收到一个数据块
        WriteCom(XMODEM_ACK);
        packNO++;
        cnt = 0;

#if WDGEn
        //清除看门狗
        wdt_reset();
#endif

#if LEDEn
        //LED指示状态
        LEDAlt();
#endif
      }
      else
      {
        //要求重发数据块
        WriteCom(XMODEM_NAK);
        cnt++;
      }
    }
    else
    {
      //要求重发数据块
      WriteCom(XMODEM_NAK);
      cnt++;
    }
    //连续错误次数太多,中止升级
    if(cnt > 3)
      break;
  }
  while(WaitCom() != XMODEM_EOT);
  WriteCom(XMODEM_ACK);

#if VERBOSE
  if(cnt == 0)
  {
    //提示升级成功
    putstr(msg4);
  }
  else
  {
    //提示升级失败
    putstr(msg5);

#if WDGEn
    //等待看门狗复位
    while(1);
#endif

  }
#endif

  quit();
  return 0;
}

//End of file: bootldr.c

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -