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

📄 bootldr.c

📁 单片机BOOT程序
💻 C
字号:

#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 > 0xFFFFUL
unsigned long int 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))();            //跳转,这样比'jmp 0'节省空间
}

//写入数据到串口
void WriteCom(unsigned char dat)
{
  UDRREG(COMPORTNo) = dat;
  //等待数据发送完成
  while(!(UCSRAREG(COMPORTNo) & (1<<TXCBIT(COMPORTNo))));
  UCSRAREG(COMPORTNo) |= (1 << TXCBIT(COMPORTNo));

}

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

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

//计算CRC校验:1021

void crc16(unsigned char *buf)
{
	#if BUFSIZE > 255
		unsigned int j;
	#else
	  unsigned char j;
	#endif

	#if CRCMODE == 0
	  unsigned char i;
	  unsigned int t;
	#endif

    unsigned int crc;

	crc = 0;
	for(j = BUFFERSIZE; j > 0; j--)
	{
		#if CRCMODE == 0
			//标准CRC校验
			crc = (crc ^ (((unsigned int) *buf) << 8));
			for(i = 8; i > 0; i--)
			{
			  t = crc << 1;
			  if(crc & 0x8000)
				t = t ^ 0x1021;
			  crc = t;
			}
		#else 
			#if CRCMODE == 1
			  //累加和校验
			  crc += (unsigned int)(*buf);
			#endif
		#endif
		buf++;
	}
	ch = crc / 256;
	cl = crc % 256;
}

unsigned char hex_to_ascii(unsigned char i)
{
	i=i&0xff;
	if(i<10)return i+'0';
	else return i+'A'-10;	
}

int main(void)
{
	unsigned char cnt,del;
	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

    //关闭中断,异常情况下的保护
    cli();
	
	#if WDG_En
		//允许看门狗,设置看门狗超时时间
		wdt_enable(WDTO_1S);
	#else
		//禁止看门狗
		#ifndef MCUSR
		#define MCUSR    MCUCSR
		#endif
		MCUSR = 0;
		wdt_disable();
	#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

	//串口启动Bootloader模式

	#if VERBOSE
	  //提示等待密码
	  putstr("\r\n请输入 D 健进入升级模式\r\nDEBUG:>   ");
	#endif

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

			#if LEDEn
				  //LED指示状态
				  LEDAlt();
			#endif           
            
			if(--cnt == 0)
			{				
				#if VERBOSE
						//提示超时
				        putstr("\r\n进入用户程序...");
				#endif
				//跳转到用户程序
				quit();
			}
			putstr("\b\b");WriteCom(hex_to_ascii(cnt>>4));WriteCom(hex_to_ascii(cnt));
        }
		
		#if WDGEn
			//清除看门狗
			wdt_reset();
		#endif

		if(DataInCom())
		{char data_tmp;
			data_tmp=ReadCom();
		    if( data_tmp== 'd'||data_tmp== 'D'){break;}
		}
	}

    //每个时隙向PC机发送一个控制字符“C”,等待控制字〈soh〉
    del = 1;
    while(1)
    {
		if(TIFRREG & (1 << OCF1A))  //T1溢出
		{
			TIFRREG |= (1 << OCF1A);
			#if LEDEn
				//LED指示状态
				LEDAlt();
			#endif
				
			if(--del == 0)
			{   del=3;
				WriteCom(XMODEM_RWC) ;    //发送 "C"
			}
		}

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

		if(DataInCom())
		{
			if(ReadCom() == XMODEM_SOH)break; //XMODEM命令开始			
		}
    }
  
  //关闭定时器
  TCCR1B = 0;
  //开始接受数据
  packNO = 0;
  bufptr = 0;
  cnt = 0;
  FlashAddr = 0;
  
  do
  {
    packNO++;
    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]);       //计算校验
		if((crch == ch) && (crcl == cl))
		{
			#if BootStart
			if(FlashAddr < BootStart)             //避免写入Boot区
			{
			#endif

			#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

			#if BootStart
			}
			else                                  //超过BootStart,忽略写操作
			{
				bufptr = 0;                       //重置接收指针
			}
		    #endif

			//读取写入的Flash内容并和下载的缓冲区比较
			#if (ChipCheck > 0) && (BootStart > 0)
				#if BUFFERSIZE < SPM_PAGESIZE
				if((bufptr == 0) && (FlashAddr <= BootStart))
				#else
				if(FlashAddr <= BootStart)
				#endif
				{
				  boot_rww_enable();                  //允许读用户程序
				  cl = 1;                             //清除错误标志
				  for(pagptr = 0; pagptr < BUFSIZE; pagptr++)
				  {
					#if FLASHEND > 0xFFFFUL
					if(pgm_read_byte_far(FlashAddr - BUFSIZE + pagptr) != buf[pagptr])
					#else
					if(pgm_read_byte(FlashAddr - BUFSIZE + pagptr) != buf[pagptr])
					#endif
					{
					  cl = 0;                         //设置错误标志
					  break;
					}
				  }
		  
					  if(cl)                              //校验正确,发送正常反馈
					  {
						WriteCom(XMODEM_ACK);
						cnt = 0;
					  }
					  else
					  {
						WriteCom(XMODEM_NAK);          //校验错误,请求重发
						cnt++;                            //错误计数加1
						FlashAddr -= BUFSIZE;             //修正FlashAddr地址
					  }
				}
				else //不校验Boot区,直接发送正确回应
				{
				  WriteCom(XMODEM_ACK);
				  cnt = 0;
				}
			#else
					//不校验,直接发送正确响应
					WriteCom(XMODEM_ACK);
					cnt = 0;
			#endif

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

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


#if VERBOSE
  
 
  if(cnt == 0)
  {
    //提示升级成功
    putstr("\r\n升级成功");
  }
  else
  {
    //提示升级失败
    putstr("\r\n升级失败");

	#if WDGEn
		//等待看门狗复位
		while(1);
	#endif
	boot_rww_enable();     
	(*((void(*)(void))0x1c00))();
  }
#endif

  //退出Bootloader
  quit();
  return 0;
}

//End of file: bootldr.c

⌨️ 快捷键说明

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