单片机交流
登录
直播中
生龙活虎3
8年用户
802经验值
擅长:光电显示
私信
关注
[问答]
如何去实现一种基于AVR-BootLoader的远程升级DTU设计?
开启该帖子的消息推送
DTU
串口
AVR-BootLoader的源代码是什么?
远程升级DTU是如何连接云平台虚拟串口的?
如何去实现一种基于AVR-BootLoader的远程升级DTU设计?
回帖
(1)
王官君
2021-7-7 10:20:24
最近一直利用业余时间写自己的“基于AVR-BootLoader”,启发是由于一次在ourAVR论坛看到了绍子阳的bootloader,联想到公司在用AVR MCU,但每次升级程序都要花费很大的力气车马劳顿的跑到工程现场,而且很多机器还安装在国外,为了升级一次程序发费了很多的人力物力财力,加上公司的机器目前大部分都配有远程监控系统,所以本人决定写一个具有自有产权的“AVR-BootLoader”。
本人已测试过Atmega64A与Atmega128。话不多说了上源代码,网友们和AVR爱好者可以拷贝到CodeWizardAVR V2.03.9编译器下编译。
上位机截图:
远程升级DTU
远程升级连接云平台虚拟串口
// 关于上海霜蝉-AVR_BootLoade_V1.00
// 1、软件版本V1.00 编译环境CodeWizardAVR V2.03.9 Standard;
// 2、支持本公司常用的三种AVR芯片;
// 3、支持标准Xmodem和扩展Xmodem_1K协议;
// 4、联机握手密码为“00”,握手成功手的等待文件超时为1分钟;
// 5、默认复位等待3S退出boot到用户程序或循环运行boot;
// 6、支持1分钟以内的断网续传;
// 7、支持连续10帧以内数据错误的重传;
// 8、支持下载过程中的取消超作;
// 9、支持当收到包时,接收过程中每个字符的超时间隔为 1 秒;
// 10、支持所有的超时及错误事件至少重试 10 次;
// 11、支持数等待超时6S的请求;
// 12、Boot Loader - Size:1024words;
// 13、支持传输速度:38.400KB/S~2.400KB/S;
// 14、支持公司常用最多的三个型号ATMEGA32,64,128。
// 15、支持开门狗自定义开关,自定义时钟频率
/*****************************************************
This program was produced by the
CodeWizardAVR V2.03.9 Standard
Automatic Program Generator
?Copyright 1998-2008 Pavel Haiduc, HP InfoTech s.r.l.
http://www.hpinfotech.com
Project : AVR_ BootLoader
Version : V1.00
Date : 2014-7-19
Author : Sui Hongwei
Company : SCICALA
Comments:
Chip type : ATmega64L
Program type : Boot Loader - Size:1024words
AVR Core Clock frequency: 16.000000 MHz
Memory model : Small
External RAM size : 0
Data Stack size : 1024
*****************************************************/
#include “AVR_boot.h” // 头文件包含
//--------------------------------------------------//
//同步密码长度
#define CONNECTCNT 7
//同步密码
uchar KEY_Data [10 ] = { “SCICALA” } ;
// Declare your global variables here
//--------------------------------------------------//
// 全局变量定义
uchar RX_buff [ BUFSIZE ] ; // 数据拉收缓存
uchar Frame_State , SOH_Wait_cnt ; // 帧状态,帧头等待计数
uint Time_cnt , Error_cnt ; // 超时计数,连续帧错误计数
uint buffptr , buffptr_old , buffptr_New ; // 数据缓存指针必须大于1024
ulong FlashAddr ; // Flash地址
uchar UpdatedSta ; // 升级标志
//-----------------------------------------------//
//擦除(code=0x03)和写入(code=0x05)一个Flash页
void boot_page_ew ( ulong p_address , char code )
{
# asm
ldd r26 , y +1 ; R26 LSB
ldd r27 , y +2 ; R27 MSB
# endasm
SPM_REG = code ; // 寄存器SPMCSR中为操作码
# asm
mov r30 , r26
mov r31 , r27
# endasm
#ifdef ATMEGA128
RAMPZ = ( p_address 》》 16 ) ; // RAMPZ0 = 1: ELPM/SPM 可以访问程序存储器地址$8000 - $FFFF ( 高64K 字节)
#endif
# asm ( “spm” ) ; // 对指定Flash页进行擦操作
}
//填充Flash缓冲页中的一个字
void boot_page_fill ( uint A_address , uint data )
{
# asm
LDD R30 , Y +2 ; R30 LSB
LDD R31 , Y +3 ; R31 MSB
LD R26 , Y
LDD R27 , Y +1
MOV R0 , R26
MOV R1 , R27
MOV R26 , R30
MOV R27 , R31
# endasm
SPM_REG = 0x01 ; //寄存器SPMCSR中为操作码
# asm
mov r30 , r26
mov r31 , r27
# endasm
# asm ( “spm” ) ; //对指定Flash页进行擦操作
}
//等待一个Flash页的写完成
void wait_page_rw_ok ( void )
{
while ( SPM_REG & 0x40 )
{
while ( SPM_REG & 0x01 ) ;
SPM_REG = 0x11 ;
# asm ( “spm” ) ;
while ( SPM_REG & 0x01 ) ;
}
}
//-----------------------------------------------//
//更新一个Flash页的完整处理
void write_one_page ( uchar data [ ])
{
uint i ;
boot_page_ew ( FlashAddr , 0x03 ) ; //擦除一个Flash页
wait_page_rw_ok ( ) ; //等待擦除完成
for ( i = 0 ; i 《 SPM_PAGESIZE ; i += 2 ) //将数据填入Flash缓冲页中 按字填充
{
boot_page_fill ( i , ( uint ) data [ i ] + ( data [ i +1 ] 《《 8 )) ;
wait_page_rw_ok ( ) ;
}
boot_page_ew ( FlashAddr , 0x05 ) ; //将缓冲页数据写入一个Flash页
wait_page_rw_ok ( ) ; //等待写入完成
}
//--------------------------------------------------//
//等待串口数据1S超时自动转为应答
unsigned char Wait1S_UART ( )
{
uchar i = 0 ;
do
{
#if WDGEn
Watchdog_Reset ( ) ; // 喂狗
#endif
if ( TIFR & 0x10 ) // OCF1A: T/C1 输出比较 A 匹配标志位
{
TIFR| = 0x10 ; // 清除Time1定时器比较匹配标志
i ++ ;
if ( i 》= 4 ) // 等待1S 250ms*4=1S
{ Frame_State = 0x06 ; break ; } // 帧数据超时转为应答请求重发数据帧
}
if ( UCSRAREG ( COMPORTNo ) & 0x80 )
{
UCSRAREG ( COMPORTNo )| = 0x80 ; // 清除接收完成状态
#if Run_LEDEn
Run_LED ; // 运行LED闪烁
#endif
i = 100 ;
return UDRREG ( COMPORTNo ) ; // 读取UDR0
}
}
while ( 100 != i ) ; // 等待数据||超时退出
}
//***************************************************//
//===================================================//
void main ( void )
{
uchar packNO , packNO_old ; // 包号、包号留存
uint crc16 ; // 接收CRC缓存
uint li ; // 帧计数
uchar ch , cl ; // 包号
PORT_Init ( ) ; // 端口初始化
UART_Init ( ) ; // 串口初始化
Time1_Init ( ) ; // 定时器初始化 250ms
#if WDGEn
WatchDog_Enable ( ) ; // 打开看门狗(2S)
#else
WatchDog_Disenable(); // 禁止看门狗
#endif
#if Wait_BootTime
Time_cnt = EEPROM_Read ( EE_TimeAddr ) ; // 读取boot运行时间
#endif
#if SafeUpdated_En
UpdatedSta = EEPROM_Read ( EE_SafeAddr ) ; // 读取升级成功标志
#endif
# asm ( “cli” ) // 关总中断
#if Delay_En // 是否延时
for ( li = 0 ; li 《 5000 ; li ++ )
{ # asm ( “nop” ) }
#endif
//-----------------------------------------------//
//等待“握手”,否则退出Bootloader程序,从0x0000处执行应用程序
Time_cnt += WiteTimeCnt ;
cl = 0 ;
while ( 1 )
{
if ( TIFR & 0x10 ) // OCF1A: T/C1 输出比较 A 匹配标志位
{
TIFR| = 0x10 ; // 清除Time1定时器比较匹配标志
#if Run_LEDEn
Run_LED ; // 运行LED闪烁
#endif
Time_cnt -- ;
if ( Time_cnt == 0 ) // 等待握手超时
{
#if SafeUpdated_En
if ( UpdatedSta ) // 上次升级失败
{ while ( 1 ) ; } // 复位boot
else
#endif
{ quit_boot ( ) ; } // 跳转到用户程序
}
if ( UCSRAREG ( COMPORTNo ) & 0x80 )
{
ch = Wait_UART ( ) ;
if ( ch == KEY_Data [ cl ])
{ cl ++ ; }
else
{ cl = 0 ; }
//WriteCom(ch); // 密码回传
}
if ( cl == CONNECTCNT ) break ;
}
#if WDGEn
Watchdog_Reset ( ) ; // 喂狗
#endif
}
//-----------------------------------------------//
//启动Xmodex CRC传数据=字符“C”,等待控制字〈soh〉
Time_cnt = TimeOutCntC ;
while ( 1 )
{
if ( TIFR & 0x10 ) // OCF1A: T/C1 输出比较 A 匹配标志位
{
TIFR| = 0x10 ; // 清除Time1定时器比较匹配标志
#if Run_LEDEn
Run_LED ; // 运行LED闪烁
#endif
Send_UART ( XMODEM_RWC ) ; //发送 “C”
Time_cnt -- ;
if ( Time_cnt == 0 ) // 等待文件超时
{
#if SafeUpdated_En
if ( UpdatedSta ) // 上次升级失败
{ while ( 1 ) ; } // 复位boot
else
#endif
{ quit_boot ( ) ; } // 跳转到用户程序
} // 跳转到用户程序
}
if ( UCSRAREG ( COMPORTNo ) & 0x80 ) // 串口有数据
{
#ifdef Xmodem
if ( Wait_UART ( ) == XMODEM_SOH ) //XMODEM命令开始 0x01
#else // Xmodem_1K
if ( Wait_UART ( ) == XMODEM_STX ) //XMODEM命令开始 0x02
#endif
break ;
}
#if WDGEn
Watchdog_Reset ( ) ; // 喂狗
#endif
}
//-----------------------------------------------//
// 开始接收数据进入Xmodem协议接收文件
// 帧的两个时间很重要:连续出错10次*最大帧请求间隔6S
// 累计请求间隔10*6S=1min
packNO = 1 ; // Xmodem 起始为 0x01
packNO_old = 0 ;
buffptr = 0 ;
buffptr_old = 0 ;
Error_cnt = 0 ;
FlashAddr = 0 ;
Frame_State = 0x01 ; // 第一帧不判断帧头
while ( 1 )
{
switch ( Frame_State ) // 帧状态
{
case 0x00 : // 接受状态 帧头与停止信号判断
{
ch = XMODEM_NUL ; // 清除
if ( UCSRAREG ( COMPORTNo ) & 0x80 ) // 串口有数据
{ ch = Wait_UART ( ) ; } // 读取帧头或停止信号
if ( TIFR & 0x10 ) // OCF1A: T/C1 输出比较 A 匹配标志位
{
TIFR| = 0x10 ; // 清除Time1定时器比较匹配标志
SOH_Wait_cnt ++ ; // 等待计时 250ms
if ( SOH_Wait_cnt 》= 24 ) // 等待帧超时请求数据(6S*10=1min)
{
Frame_State = 0x06 ; // 要求重发数据块
SOH_Wait_cnt = 0 ; // 帧请求计时
#if Run_LEDEn
Run_LED ; // 运行LED闪烁
#endif
}
}
//------------------------------
if ( ch == XMODEM_EOT ) // 发送结束
{
for ( li = ( buffptr_New -128 ) ; li 《 buffptr_New ; ) // 判断上一帧数据尾16个正确性以决定是否重传
{
if (( RX_buff [ li ++ ]) != 0xff ) break ; // 填充帧判断争强EOT抗干扰能力 FF CF
{
if (( RX_buff [ li ++ ]) != 0xcf ) break ; // 填充帧判断争强EOT抗干扰能力 FF CF
}
}
if ( li == buffptr_New )
{ Send_UART ( XMODEM_ACK ) ; // 最后一帧应答完成
Frame_State = 0x0ff ; // 转为退出boot
}
else
{
Frame_State = 0x06 ; // 要求重发数据块
SOH_Wait_cnt = 0 ; // 帧请求计时
}
}
#ifdef Xmodem
if ( ch == XMODEM_SOH ) // Xmodem帧头判断
#else // Xmodem_1K
if ( ch == XMODEM_STX ) // Xmodem_1K帧头判断
#endif
{
Frame_State = 0x01 ; // 转为包号检查
SOH_Wait_cnt = 0 ; // 清除帧头等待计时
}
}
break ;
case 0x01 : //包序号校验
{
ch = Wait1S_UART ( ) ; // 获取包序号
cl = ~ Wait1S_UART ( ) ;
if ( ch == cl ) // 包号对比
{ Frame_State = 0x02 ;
packNO = ch ; // 正确保留包号
}
else
{ Frame_State = 0x06 ; } // 重发应答ANK
}
break ;
case 0x02 : // 进入二级CRC校验
{
for ( li = 0 ; li 《 BUFFER_SIZE ; li ++ ) // 接收完整一帧数据 (128字节)
{
RX_buff [ buffptr ++ ] = Wait1S_UART ( ) ; // 接收数据
// 帧数据接收超时1S退出for询环,再接收2个数据或超时1S+2S转为应答
if ( Frame_State == 0x06 ) break ;
}
crc16 = Wait1S_UART ( ) ;
crc16 = crc16 《《 8 ;
crc16 += Wait1S_UART ( ) ; // 接收2个字节的CRC效验字
if ( CRC16_Word ( & RX_buff [ buffptr - BUFFER_SIZE ] , BUFFER_SIZE ) == crc16 ) // CRC校验验证
{
if ( packNO == packNO_old ) // 接收相同包不写flash
{ Frame_State = 0x05 ; // 正常应答ACK
buffptr = buffptr_old ;
}
else
{ Frame_State = 0x03 ; // 正确应答
buffptr_New = buffptr ; // 记录当前数据指针
}
}
else
{ Frame_State = 0x06 ; // 应答NAK
buffptr = buffptr_old ; // 去掉错误数据的指针
}
}
break ;
case 0x03 : // 校验通过,执行写入
{
if ( FlashAddr 《 BootStart ) //避免写入Boot区
{
#if BUFFER_SIZE 《 SPM_PAGESIZE // ---
if ( buffptr 》= SPM_PAGESIZE ) //缓冲区满,写入数据;否则继续接收
{ //接收多个帧,写入一页
write_one_page ( & RX_buff [ 0 ]) ; //写入缓冲区内容到Flash中
FlashAddr += SPM_PAGESIZE ; //修改Flash页地址
buffptr = 0 ;
}
#else //----------------------
while ( buffptr 》 0 ) //接收一帧,写入多个页面
{
write_one_page ( & RX_buff [ BUFSIZE - buffptr ]) ;
FlashAddr += SPM_PAGESIZE ; //修改Flash页地址
buffptr -= SPM_PAGESIZE ;
}
#endif //-----------------------
}
else //超过BootStart,忽略写操作
{ buffptr = 0 ; } //重置接收指针
Frame_State = 0x04 ;
}
break ;
case 0x04 : //读取写入的Flash内容并和下载的缓冲区比较
{
Frame_State = 0x05 ; // 转为应答状态
buffptr_old = buffptr ; // 写入正常才记录数据指针
}
break ;
case 0x05 : // 正确应答ACK
{
packNO_old = packNO ; // 接收完整一帧保留本次包号
Send_UART ( XMODEM_ACK ) ; // 认可应答
//WriteCom(packNO); // 测试
//WriteCom(FlashAddr); // 测试
//WriteCom(FlashAddr》》8); // 测试
Error_cnt = 0 ; // 清除连续错误计数
Frame_State = 0x00 ; // 转为帧接收(请求)状态
}
break ;
case 0x06 : // 错误=重发应答ANK
{
Send_UART ( XMODEM_NAK ) ; // 要求重发数据块
Error_cnt ++ ; // 连续错误计数
Frame_State = 0x00 ; // 转为接收状态
}
break ;
default : // 升级完成退出处理
{
#if SafeUpdated_En
EEPROM_Write ( EE_SafeAddr , 0 ) ; // 写升级成功标志 0
#endif
quit_boot ( ) ; //退出Bootloader
}
break ;
} // switch End
if ( Error_cnt 》 10 ) // 连续出错10次退出升级(复位)
{
Send_UART ( XMODEM_CAN ) ; // 撤销传送(无条件停止)
#if SafeUpdated_En
EEPROM_Write ( EE_SafeAddr , 1 ) ; // 写升级失败标志 1
#endif
while ( 1 ) ; // 没有升级完成重新开始
}
#if WDGEn
Watchdog_Reset ( ) ; // 喂狗
#endif
} // while end
//-------------------------------------------------//
} // main end
//-----------------------------------------------//
#include “AVR_boot.h” // 头文件包含
//-----------------------------------------------//
//----------初始化-------------------------------//
// 输出端口初始化
void PORT_Init ( )
{
// 上电默认全为输入口
// 输出口设置
#if Run_LEDEn
DDRREG ( LEDPORT )| = ( 1 《《 LEDPORTNo ) ; // 指示灯
#endif
}
//-----------------------------------------------//
// 串口初始化
void UART_Init ( )
{
#if RS485_En
DDRREG ( RS485PORT )| = ( 1 《《 RS485TXEn ) ; // 设置(PC5)RS485方向控制引脚为输出
RX485DE_RX ; // 默认为接收
#endif
UCSRAREG ( COMPORTNo ) = 0x00 ;
UCSRBREG ( COMPORTNo ) = 0x18 ; // Enable Receiver and Transmitter
UCSRCREG ( COMPORTNo ) = 0x0E ; // Set frame. format: 8data, 2stop bit
// 通信速率
UBRRHREG ( COMPORTNo ) = BAUD_H ; // 0X00
UBRRLREG ( COMPORTNo ) = BAUD_L ; // Set baud rate 16M 9600 0x0067
}
// 250ms定时器设置Time1
//-----------------------------------------------//
//使用定时器1:1024分频,CTC模式4,产生以毫秒为单位的时间
void Time1_Init ( )
{
// Timer/Counter 1 initialization
// Clock source: System Clock
// Clock value: 16MHz
TCCR1A = 0x00 ; // CTC4
TCCR1B = 0x08 ; // CTC4
TCCR1B| = 0x03 ; // clkI/O/64 ( 来自预分频器) 16M=4us
OCR1A = T1_TCNT ; // 250MS
}
//-----------------------------------------------//
// 看门狗初始化函数 2s
void WatchDog_Enable ( void )
{
// Watchdog Timer initialization
// Watchdog Timer Prescaler: OSC/2048k
MCUCSR = 0x00 ; // 控制与状态寄存器
WDTCR = 0x1F ; // 看门狗使能 500ms(D)1.0S(E) 2.0S(F)
WDTCR = 0x0F ;
}
// 禁止看门狗
void WatchDog_Disenable ( void ) // 禁止看门狗
{
MCUCSR = 0x00 ; // 控制与状态寄存器
WDTCR =0 b00011111 ; // 看门狗 使能位修改 使能(B4=1) 2.0S(F)
WDTCR =0 b00000111 ; // 禁止看门狗 2.0S(7)
}
// 喂狗
void Watchdog_Reset ( void )
{ # asm ( “wdr” ) } // 喂狗
//-----------------------------------------------//
//-------------子函数----------------------------//
//写入数据到串口
void Send_UART ( unsigned char dat )
{
uchar i ;
#if RS485_En
RX485DE_TX ; // 使能RS485发送
#endif
UDRREG ( COMPORTNo ) = dat ; // UDR0
//等待数据发送完成
while ( ! ( UCSRAREG ( COMPORTNo ) & 0x40 )) ; // 等待数据发送完成 Bit 6 – TXC: USART 发送结束
UCSRAREG ( COMPORTNo )| = 0x40 ; // 清除发送完成状态
#if RS485_En
RX485DE_RX ; // 使能RS485接收
#endif
}
//-----------------------------------------------//
//等待串口数据(注意是”死等“)
unsigned char Wait_UART ( )
{
while ( ! ( UCSRAREG ( COMPORTNo ) & 0x80 )) ; // 等等数据到来 Bit 7 – RXC: USART 接收结束
UCSRAREG ( COMPORTNo )| = 0x80 ; // 清除接收完成状态
#if Run_LEDEn
Run_LED ; // 运行LED闪烁
#endif
return UDRREG ( COMPORTNo ) ;
}
//-----------------------------------------------//
//计算CRC
int CRC16_Word ( char * ptr , int count )
{
int crc = 0 ;
char i ;
while ( -- count 》= 0 )
{
crc = crc ^ ( int ) * ptr ++ 《《 8 ;
i = 8 ;
do
{
if ( crc & 0x8000 )
crc = crc 《《 1 ^ 0x1021 ;
else
crc = crc 《《 1 ;
} while ( -- i ) ;
}
return ( crc ) ;
}
//-----------------------------------------------//
//退出Bootloader程序,从0x0000处执行应用程序
void quit_boot ( void )
{
MCUCR = 0x00 ; //当IVSEL 为“0“ 时,中断向量位于Flash 存储器的起始地址;
#ifdef ATMEGA128
RAMPZ = 0x00 ; //0: ELPM/SPM 可以访问程序存储器地址$0000 - $7FFF ( 低64K 字节)
#endif
# asm ( ”jmp 0x0000“ ) //跳转到Flash的0x0000处,执行用户的应用程序
}
//-----------------------------------------------//
// 读EEPROM一个字节
uchar EEPROM_Read ( uint Addr ) // EEPROM读1个字节操作 0
{
while ( EECR & 0x02 ) ; // 等待上一次写操作结束
EEAR = Addr ; // 设置地址寄存器
EECR | = 0x01 ; // 设置EERE 以启动读操作 b0
return ( EEDR ) ; // 自数据寄存器返回数据
}
// 写EEPROM一个字节
void EEPROM_Write ( uint Addr , char Data ) // EEPROM 写1个字节操作
{
EEAR = Addr ; // 设置地址寄存器
EEDR = Data ; // 设置数据寄存器
// #asm(”cli“) // EEMPE置1,EEPE1,这两步操作中间不能超过4个时钟周期
EECR | = 0x04 ; // 置位EEMWE b2
EECR | = 0x02 ; // 置位EEWE以启动写操作 b1
//#asm(”sei“)
}
//-----------------------------------------------//
//-------------------------------------------------------//
#ifndef _AVR_BOOT_H_
#define _AVR_BOOT_H_ 1
// 配置文件包含
#include ”Config.h“
//------------------------------------------------------//
#ifdef ATMEGA32
#include 《mega32.h》
#endif
#ifdef ATMEGA64
#include 《mega64.h》
#endif
#ifdef ATMEGA128
#include 《mega128.h》
#endif
//---------------------------------------------------//
typedef signed char schar ;
typedef signed int sin t ;
typedef signed long slong ;
typedef unsigned char uchar ;
typedef unsigned int uint ;
typedef unsigned long ulong ;
//--------------------------------------------------//
// 函数申明
void UART_Init ( ) ;
void PORT_Init ( ) ;
void Time1_Init ( ) ;
void Send_UART ( unsigned char dat ) ;
unsigned char Wait_UART ( ) ;
unsigned char Wait1S_UART ( ) ;
void WatchDog_Disenable ( void ) ;
void quit_boot ( void ) ;
int CRC16_Word ( char * ptr , int count ) ;
void write_one_page ( uchar data [ ]) ;
void WatchDog_Enable ( void ) ;
void Watchdog_Reset ( void ) ;
uchar EEPROM_Read ( uint Addr ) ;
void EEPROM_Write ( uint Addr , char Data ) ;
//--------------------------------------------------//
//内部使用的宏定义
#define CONCAT(a, b) a ## b
#define CONCAT3(a, b, c) a ## b ## c
//端口以及位定义
#define PORTREG(No) CONCAT(PORT, No)
#define PINREG(No) CONCAT(PIN, No)
#define DDRREG(No) CONCAT(DDR, No)
#define UDRREG(No) CONCAT(UDR, No)
//串口初始化需要寄存器
#define UCSRAREG(No) CONCAT3(UCSR, No, A)
#define UCSRBREG(No) CONCAT3(UCSR, No, B)
#define UCSRCREG(No) CONCAT3(UCSR, No, C)
#define UBRRHREG(No) CONCAT3(UBRR, No, H)
#define UBRRLREG(No) CONCAT3(UBRR, No, L)
//---------------------------------------------------//
// 端口定义
#define RX485DE_RX PORTREG(RS485PORT)&=~(1 《《 RS485TXEn); // SCI接收使能
#define RX485DE_TX PORTREG(RS485PORT)|=(1 《《 RS485TXEn); // SCI发送使能
#define Run_LED PORTREG(LEDPORT)^= (1 《《 LEDPORTNo); // boot运行LED
//---------------------------------------------------//
//定义Xmoden控制字符
#define XMODEM_NUL 0x00 // null
#define XMODEM_SOH 0x01 // Xmodem数据头
#define XMODEM_STX 0x02 // Xmodem_1K数据头
#define XMODEM_EOT 0x04 // 发送结束
#define XMODEM_ACK 0x06 // 认可响应
#define XMODEM_NAK 0x15 // 不认可响应
#define XMODEM_CAN 0x18 // 撤销传送
#define XMODEM_EOF 0x1A // 填充数据包
#define XMODEM_RWC ‘C’ // CRC16-128
//-------------------------------------------------------//
#ifdef Xmodem_1K
#define BUFFER_SIZE 1024
#else // Xmodem
#define BUFFER_SIZE 128
#endif
//-----------------------------------------------------//
#ifdef ATMEGA32
#define SPM_PAGESIZE 128 // SPM 页大小
#define BootStart 0x3C00*2 // 按字节
#define SPM_REG SPMCR // SPM寄存器
#endif
#ifdef ATMEGA64
#define SPM_PAGESIZE 256 // SPM 页大小
#define BootStart 0x7C00*2 // 按字节
#define SPM_REG SPMCSR // SPM寄存器
#endif
#ifdef ATMEGA128
#define SPM_PAGESIZE 256 // SPM 页大小
#define BootStart 0xFC00*2 // 按字节
#define SPM_REG SPMCSR // SPM寄存器
#endif
//接收缓冲区大小不能小于 SPM_PAGESIZE
#if BUFFER_SIZE 《 SPM_PAGESIZE
#define BUFSIZE SPM_PAGESIZE // UART数据缓存
#else
#define BUFSIZE BUFFER_SIZE // UART数据缓存
#endif
//计算和定义波特率设置参数
#define BAUD_SETTING (unsigned char)((unsigned long)CRYSTAL/ ( 16 * ( unsigned long ) BAUDRATE ) -1 )
#define BAUD_H ((unsigned char)(BAUD_SETTING》》8))
#define BAUD_L (unsigned char)BAUD_SETTING
//计算T1定时器设置参数
#define T1_TCNT (unsigned int)((unsigned long)CRYSTAL*250/ ( 64 * 1000 )) ;
//--------------------------------------------------------//
#endif // _AVR_BOOT_H_
#ifndef _CONFIG_H_
#define _CONFIG_H_ 1
//*********************************************************//
// 关于上海霜蝉-AVR_bootV1.00
// 1、软件版本V1.00 编译环境CodeWizardAVR V2.03.9 Standard;
// 2、支持本公司常用的三种AVR芯片;
// 3、支持标准Xmodem和扩展Xmodem_1K协议;
// 4、联机握手密码为”00“,握手成功手的等待文件超时为1分钟;
// 5、默认复位等待3S退出boot到用户程序或循环运行boot;
// 6、支持1分钟以内的断网续传;
// 7、支持连续10帧以内数据错误的重传;
// 8、支持下载过程中的取消超作;
// 9、支持当收到包时,接收过程中每个字符的超时间隔为 1 秒;
// 10、支持所有的超时及错误事件至少重试 10 次;
// 11、支持数等待超时6S的请求;
// 12、Boot Loader - Size:1024words;
// 13、支持传输速度:38.400KB/S~2.400KB/S;
// 14、支持上海霜蝉常用最多的三个型号ATMEGA32,64,128。
// 15、支持开门狗自定义开关,自定义时钟频率
//*********************************************************//
// 注意:修改编译器配置 (char to int;char is unsigned)
// 注意:填充数据必须大于两包(》=256)
// 定义芯片型号
// 芯片型号选择
//#define ATMEGA32
#define ATMEGA64 // 已测试Atmgea64L
//#define ATMEGA128
// 协议类型选择:Xmodem或Xmodem_1K
#define Xmodem // Xmodex 128(CRC16)
//#define Xmodem_1K // Xmodem 1024(CRC16)
//-----------------------------------------------//
// 系统时钟MHz
#ifndef CRYSTAL
#define CRYSTAL 16000000 // 16M
#endif
// 波特率 38400~2400
#define BAUDRATE 9600
// 等待密码的超时时间 = WiteTimeCnt * 250ms
// 超时次数
#define WiteTimeCnt 10 // 10=2.5s
// 用户设置boot驻留等待握手时间(1=250ms)+2.5S(WiteTimeCnt)
#define Wait_BootTime 40 // (1=250ms)+2.5S
// 等待文件的超时时间 = TimeOutCntC * 250ms
// 发送‘C’的最大次数
#define TimeOutCntC 40 // 40=1min
// 串口号(ATMEA32 串口号为空)
#define COMPORTNo 0 // UART0
// 看门狗使能
#define WDGEn 1 // 使用看门狗
// 使用485模式
#define RS485_En 1 // 使用485使能脚
// 485控制端口和引脚
#define RS485PORT E // PORTE
#define RS485TXEn 2 // PORTE2
// 使用LED指示状态
#define Run_LEDEn 1 // 使能boot运行LED指示
// LED控制端口和引脚
#define LEDPORT B // PORTB
#define LEDPORTNo 6 // PORTB6
// 延时用于解决串口数据出错
#define Delay_En 0
// 用户设置boot驻留等待握手时间(1=250ms)+2.5S(WiteTimeCnt)
// 使此功能需要设置EEPROM
#define Wait_BootTime 40 // (40*250ms)+2.5S
#define EE_TimeAddr 0 // 使用EEPROM 占用1个字节
// 安全升级(升级不成功不进入用户区)
// 使此功能需要设置EEPROM
#define SafeUpdated_En 1
#define EE_SafeAddr 1 // 使用EEPROM 占用1个字节
//-----------------------------------------------//
#endif // _CONFIG_H_
//End of file: bootcfg.h
最近一直利用业余时间写自己的“基于AVR-BootLoader”,启发是由于一次在ourAVR论坛看到了绍子阳的bootloader,联想到公司在用AVR MCU,但每次升级程序都要花费很大的力气车马劳顿的跑到工程现场,而且很多机器还安装在国外,为了升级一次程序发费了很多的人力物力财力,加上公司的机器目前大部分都配有远程监控系统,所以本人决定写一个具有自有产权的“AVR-BootLoader”。
本人已测试过Atmega64A与Atmega128。话不多说了上源代码,网友们和AVR爱好者可以拷贝到CodeWizardAVR V2.03.9编译器下编译。
上位机截图:
远程升级DTU
远程升级连接云平台虚拟串口
// 关于上海霜蝉-AVR_BootLoade_V1.00
// 1、软件版本V1.00 编译环境CodeWizardAVR V2.03.9 Standard;
// 2、支持本公司常用的三种AVR芯片;
// 3、支持标准Xmodem和扩展Xmodem_1K协议;
// 4、联机握手密码为“00”,握手成功手的等待文件超时为1分钟;
// 5、默认复位等待3S退出boot到用户程序或循环运行boot;
// 6、支持1分钟以内的断网续传;
// 7、支持连续10帧以内数据错误的重传;
// 8、支持下载过程中的取消超作;
// 9、支持当收到包时,接收过程中每个字符的超时间隔为 1 秒;
// 10、支持所有的超时及错误事件至少重试 10 次;
// 11、支持数等待超时6S的请求;
// 12、Boot Loader - Size:1024words;
// 13、支持传输速度:38.400KB/S~2.400KB/S;
// 14、支持公司常用最多的三个型号ATMEGA32,64,128。
// 15、支持开门狗自定义开关,自定义时钟频率
/*****************************************************
This program was produced by the
CodeWizardAVR V2.03.9 Standard
Automatic Program Generator
?Copyright 1998-2008 Pavel Haiduc, HP InfoTech s.r.l.
http://www.hpinfotech.com
Project : AVR_ BootLoader
Version : V1.00
Date : 2014-7-19
Author : Sui Hongwei
Company : SCICALA
Comments:
Chip type : ATmega64L
Program type : Boot Loader - Size:1024words
AVR Core Clock frequency: 16.000000 MHz
Memory model : Small
External RAM size : 0
Data Stack size : 1024
*****************************************************/
#include “AVR_boot.h” // 头文件包含
//--------------------------------------------------//
//同步密码长度
#define CONNECTCNT 7
//同步密码
uchar KEY_Data [10 ] = { “SCICALA” } ;
// Declare your global variables here
//--------------------------------------------------//
// 全局变量定义
uchar RX_buff [ BUFSIZE ] ; // 数据拉收缓存
uchar Frame_State , SOH_Wait_cnt ; // 帧状态,帧头等待计数
uint Time_cnt , Error_cnt ; // 超时计数,连续帧错误计数
uint buffptr , buffptr_old , buffptr_New ; // 数据缓存指针必须大于1024
ulong FlashAddr ; // Flash地址
uchar UpdatedSta ; // 升级标志
//-----------------------------------------------//
//擦除(code=0x03)和写入(code=0x05)一个Flash页
void boot_page_ew ( ulong p_address , char code )
{
# asm
ldd r26 , y +1 ; R26 LSB
ldd r27 , y +2 ; R27 MSB
# endasm
SPM_REG = code ; // 寄存器SPMCSR中为操作码
# asm
mov r30 , r26
mov r31 , r27
# endasm
#ifdef ATMEGA128
RAMPZ = ( p_address 》》 16 ) ; // RAMPZ0 = 1: ELPM/SPM 可以访问程序存储器地址$8000 - $FFFF ( 高64K 字节)
#endif
# asm ( “spm” ) ; // 对指定Flash页进行擦操作
}
//填充Flash缓冲页中的一个字
void boot_page_fill ( uint A_address , uint data )
{
# asm
LDD R30 , Y +2 ; R30 LSB
LDD R31 , Y +3 ; R31 MSB
LD R26 , Y
LDD R27 , Y +1
MOV R0 , R26
MOV R1 , R27
MOV R26 , R30
MOV R27 , R31
# endasm
SPM_REG = 0x01 ; //寄存器SPMCSR中为操作码
# asm
mov r30 , r26
mov r31 , r27
# endasm
# asm ( “spm” ) ; //对指定Flash页进行擦操作
}
//等待一个Flash页的写完成
void wait_page_rw_ok ( void )
{
while ( SPM_REG & 0x40 )
{
while ( SPM_REG & 0x01 ) ;
SPM_REG = 0x11 ;
# asm ( “spm” ) ;
while ( SPM_REG & 0x01 ) ;
}
}
//-----------------------------------------------//
//更新一个Flash页的完整处理
void write_one_page ( uchar data [ ])
{
uint i ;
boot_page_ew ( FlashAddr , 0x03 ) ; //擦除一个Flash页
wait_page_rw_ok ( ) ; //等待擦除完成
for ( i = 0 ; i 《 SPM_PAGESIZE ; i += 2 ) //将数据填入Flash缓冲页中 按字填充
{
boot_page_fill ( i , ( uint ) data [ i ] + ( data [ i +1 ] 《《 8 )) ;
wait_page_rw_ok ( ) ;
}
boot_page_ew ( FlashAddr , 0x05 ) ; //将缓冲页数据写入一个Flash页
wait_page_rw_ok ( ) ; //等待写入完成
}
//--------------------------------------------------//
//等待串口数据1S超时自动转为应答
unsigned char Wait1S_UART ( )
{
uchar i = 0 ;
do
{
#if WDGEn
Watchdog_Reset ( ) ; // 喂狗
#endif
if ( TIFR & 0x10 ) // OCF1A: T/C1 输出比较 A 匹配标志位
{
TIFR| = 0x10 ; // 清除Time1定时器比较匹配标志
i ++ ;
if ( i 》= 4 ) // 等待1S 250ms*4=1S
{ Frame_State = 0x06 ; break ; } // 帧数据超时转为应答请求重发数据帧
}
if ( UCSRAREG ( COMPORTNo ) & 0x80 )
{
UCSRAREG ( COMPORTNo )| = 0x80 ; // 清除接收完成状态
#if Run_LEDEn
Run_LED ; // 运行LED闪烁
#endif
i = 100 ;
return UDRREG ( COMPORTNo ) ; // 读取UDR0
}
}
while ( 100 != i ) ; // 等待数据||超时退出
}
//***************************************************//
//===================================================//
void main ( void )
{
uchar packNO , packNO_old ; // 包号、包号留存
uint crc16 ; // 接收CRC缓存
uint li ; // 帧计数
uchar ch , cl ; // 包号
PORT_Init ( ) ; // 端口初始化
UART_Init ( ) ; // 串口初始化
Time1_Init ( ) ; // 定时器初始化 250ms
#if WDGEn
WatchDog_Enable ( ) ; // 打开看门狗(2S)
#else
WatchDog_Disenable(); // 禁止看门狗
#endif
#if Wait_BootTime
Time_cnt = EEPROM_Read ( EE_TimeAddr ) ; // 读取boot运行时间
#endif
#if SafeUpdated_En
UpdatedSta = EEPROM_Read ( EE_SafeAddr ) ; // 读取升级成功标志
#endif
# asm ( “cli” ) // 关总中断
#if Delay_En // 是否延时
for ( li = 0 ; li 《 5000 ; li ++ )
{ # asm ( “nop” ) }
#endif
//-----------------------------------------------//
//等待“握手”,否则退出Bootloader程序,从0x0000处执行应用程序
Time_cnt += WiteTimeCnt ;
cl = 0 ;
while ( 1 )
{
if ( TIFR & 0x10 ) // OCF1A: T/C1 输出比较 A 匹配标志位
{
TIFR| = 0x10 ; // 清除Time1定时器比较匹配标志
#if Run_LEDEn
Run_LED ; // 运行LED闪烁
#endif
Time_cnt -- ;
if ( Time_cnt == 0 ) // 等待握手超时
{
#if SafeUpdated_En
if ( UpdatedSta ) // 上次升级失败
{ while ( 1 ) ; } // 复位boot
else
#endif
{ quit_boot ( ) ; } // 跳转到用户程序
}
if ( UCSRAREG ( COMPORTNo ) & 0x80 )
{
ch = Wait_UART ( ) ;
if ( ch == KEY_Data [ cl ])
{ cl ++ ; }
else
{ cl = 0 ; }
//WriteCom(ch); // 密码回传
}
if ( cl == CONNECTCNT ) break ;
}
#if WDGEn
Watchdog_Reset ( ) ; // 喂狗
#endif
}
//-----------------------------------------------//
//启动Xmodex CRC传数据=字符“C”,等待控制字〈soh〉
Time_cnt = TimeOutCntC ;
while ( 1 )
{
if ( TIFR & 0x10 ) // OCF1A: T/C1 输出比较 A 匹配标志位
{
TIFR| = 0x10 ; // 清除Time1定时器比较匹配标志
#if Run_LEDEn
Run_LED ; // 运行LED闪烁
#endif
Send_UART ( XMODEM_RWC ) ; //发送 “C”
Time_cnt -- ;
if ( Time_cnt == 0 ) // 等待文件超时
{
#if SafeUpdated_En
if ( UpdatedSta ) // 上次升级失败
{ while ( 1 ) ; } // 复位boot
else
#endif
{ quit_boot ( ) ; } // 跳转到用户程序
} // 跳转到用户程序
}
if ( UCSRAREG ( COMPORTNo ) & 0x80 ) // 串口有数据
{
#ifdef Xmodem
if ( Wait_UART ( ) == XMODEM_SOH ) //XMODEM命令开始 0x01
#else // Xmodem_1K
if ( Wait_UART ( ) == XMODEM_STX ) //XMODEM命令开始 0x02
#endif
break ;
}
#if WDGEn
Watchdog_Reset ( ) ; // 喂狗
#endif
}
//-----------------------------------------------//
// 开始接收数据进入Xmodem协议接收文件
// 帧的两个时间很重要:连续出错10次*最大帧请求间隔6S
// 累计请求间隔10*6S=1min
packNO = 1 ; // Xmodem 起始为 0x01
packNO_old = 0 ;
buffptr = 0 ;
buffptr_old = 0 ;
Error_cnt = 0 ;
FlashAddr = 0 ;
Frame_State = 0x01 ; // 第一帧不判断帧头
while ( 1 )
{
switch ( Frame_State ) // 帧状态
{
case 0x00 : // 接受状态 帧头与停止信号判断
{
ch = XMODEM_NUL ; // 清除
if ( UCSRAREG ( COMPORTNo ) & 0x80 ) // 串口有数据
{ ch = Wait_UART ( ) ; } // 读取帧头或停止信号
if ( TIFR & 0x10 ) // OCF1A: T/C1 输出比较 A 匹配标志位
{
TIFR| = 0x10 ; // 清除Time1定时器比较匹配标志
SOH_Wait_cnt ++ ; // 等待计时 250ms
if ( SOH_Wait_cnt 》= 24 ) // 等待帧超时请求数据(6S*10=1min)
{
Frame_State = 0x06 ; // 要求重发数据块
SOH_Wait_cnt = 0 ; // 帧请求计时
#if Run_LEDEn
Run_LED ; // 运行LED闪烁
#endif
}
}
//------------------------------
if ( ch == XMODEM_EOT ) // 发送结束
{
for ( li = ( buffptr_New -128 ) ; li 《 buffptr_New ; ) // 判断上一帧数据尾16个正确性以决定是否重传
{
if (( RX_buff [ li ++ ]) != 0xff ) break ; // 填充帧判断争强EOT抗干扰能力 FF CF
{
if (( RX_buff [ li ++ ]) != 0xcf ) break ; // 填充帧判断争强EOT抗干扰能力 FF CF
}
}
if ( li == buffptr_New )
{ Send_UART ( XMODEM_ACK ) ; // 最后一帧应答完成
Frame_State = 0x0ff ; // 转为退出boot
}
else
{
Frame_State = 0x06 ; // 要求重发数据块
SOH_Wait_cnt = 0 ; // 帧请求计时
}
}
#ifdef Xmodem
if ( ch == XMODEM_SOH ) // Xmodem帧头判断
#else // Xmodem_1K
if ( ch == XMODEM_STX ) // Xmodem_1K帧头判断
#endif
{
Frame_State = 0x01 ; // 转为包号检查
SOH_Wait_cnt = 0 ; // 清除帧头等待计时
}
}
break ;
case 0x01 : //包序号校验
{
ch = Wait1S_UART ( ) ; // 获取包序号
cl = ~ Wait1S_UART ( ) ;
if ( ch == cl ) // 包号对比
{ Frame_State = 0x02 ;
packNO = ch ; // 正确保留包号
}
else
{ Frame_State = 0x06 ; } // 重发应答ANK
}
break ;
case 0x02 : // 进入二级CRC校验
{
for ( li = 0 ; li 《 BUFFER_SIZE ; li ++ ) // 接收完整一帧数据 (128字节)
{
RX_buff [ buffptr ++ ] = Wait1S_UART ( ) ; // 接收数据
// 帧数据接收超时1S退出for询环,再接收2个数据或超时1S+2S转为应答
if ( Frame_State == 0x06 ) break ;
}
crc16 = Wait1S_UART ( ) ;
crc16 = crc16 《《 8 ;
crc16 += Wait1S_UART ( ) ; // 接收2个字节的CRC效验字
if ( CRC16_Word ( & RX_buff [ buffptr - BUFFER_SIZE ] , BUFFER_SIZE ) == crc16 ) // CRC校验验证
{
if ( packNO == packNO_old ) // 接收相同包不写flash
{ Frame_State = 0x05 ; // 正常应答ACK
buffptr = buffptr_old ;
}
else
{ Frame_State = 0x03 ; // 正确应答
buffptr_New = buffptr ; // 记录当前数据指针
}
}
else
{ Frame_State = 0x06 ; // 应答NAK
buffptr = buffptr_old ; // 去掉错误数据的指针
}
}
break ;
case 0x03 : // 校验通过,执行写入
{
if ( FlashAddr 《 BootStart ) //避免写入Boot区
{
#if BUFFER_SIZE 《 SPM_PAGESIZE // ---
if ( buffptr 》= SPM_PAGESIZE ) //缓冲区满,写入数据;否则继续接收
{ //接收多个帧,写入一页
write_one_page ( & RX_buff [ 0 ]) ; //写入缓冲区内容到Flash中
FlashAddr += SPM_PAGESIZE ; //修改Flash页地址
buffptr = 0 ;
}
#else //----------------------
while ( buffptr 》 0 ) //接收一帧,写入多个页面
{
write_one_page ( & RX_buff [ BUFSIZE - buffptr ]) ;
FlashAddr += SPM_PAGESIZE ; //修改Flash页地址
buffptr -= SPM_PAGESIZE ;
}
#endif //-----------------------
}
else //超过BootStart,忽略写操作
{ buffptr = 0 ; } //重置接收指针
Frame_State = 0x04 ;
}
break ;
case 0x04 : //读取写入的Flash内容并和下载的缓冲区比较
{
Frame_State = 0x05 ; // 转为应答状态
buffptr_old = buffptr ; // 写入正常才记录数据指针
}
break ;
case 0x05 : // 正确应答ACK
{
packNO_old = packNO ; // 接收完整一帧保留本次包号
Send_UART ( XMODEM_ACK ) ; // 认可应答
//WriteCom(packNO); // 测试
//WriteCom(FlashAddr); // 测试
//WriteCom(FlashAddr》》8); // 测试
Error_cnt = 0 ; // 清除连续错误计数
Frame_State = 0x00 ; // 转为帧接收(请求)状态
}
break ;
case 0x06 : // 错误=重发应答ANK
{
Send_UART ( XMODEM_NAK ) ; // 要求重发数据块
Error_cnt ++ ; // 连续错误计数
Frame_State = 0x00 ; // 转为接收状态
}
break ;
default : // 升级完成退出处理
{
#if SafeUpdated_En
EEPROM_Write ( EE_SafeAddr , 0 ) ; // 写升级成功标志 0
#endif
quit_boot ( ) ; //退出Bootloader
}
break ;
} // switch End
if ( Error_cnt 》 10 ) // 连续出错10次退出升级(复位)
{
Send_UART ( XMODEM_CAN ) ; // 撤销传送(无条件停止)
#if SafeUpdated_En
EEPROM_Write ( EE_SafeAddr , 1 ) ; // 写升级失败标志 1
#endif
while ( 1 ) ; // 没有升级完成重新开始
}
#if WDGEn
Watchdog_Reset ( ) ; // 喂狗
#endif
} // while end
//-------------------------------------------------//
} // main end
//-----------------------------------------------//
#include “AVR_boot.h” // 头文件包含
//-----------------------------------------------//
//----------初始化-------------------------------//
// 输出端口初始化
void PORT_Init ( )
{
// 上电默认全为输入口
// 输出口设置
#if Run_LEDEn
DDRREG ( LEDPORT )| = ( 1 《《 LEDPORTNo ) ; // 指示灯
#endif
}
//-----------------------------------------------//
// 串口初始化
void UART_Init ( )
{
#if RS485_En
DDRREG ( RS485PORT )| = ( 1 《《 RS485TXEn ) ; // 设置(PC5)RS485方向控制引脚为输出
RX485DE_RX ; // 默认为接收
#endif
UCSRAREG ( COMPORTNo ) = 0x00 ;
UCSRBREG ( COMPORTNo ) = 0x18 ; // Enable Receiver and Transmitter
UCSRCREG ( COMPORTNo ) = 0x0E ; // Set frame. format: 8data, 2stop bit
// 通信速率
UBRRHREG ( COMPORTNo ) = BAUD_H ; // 0X00
UBRRLREG ( COMPORTNo ) = BAUD_L ; // Set baud rate 16M 9600 0x0067
}
// 250ms定时器设置Time1
//-----------------------------------------------//
//使用定时器1:1024分频,CTC模式4,产生以毫秒为单位的时间
void Time1_Init ( )
{
// Timer/Counter 1 initialization
// Clock source: System Clock
// Clock value: 16MHz
TCCR1A = 0x00 ; // CTC4
TCCR1B = 0x08 ; // CTC4
TCCR1B| = 0x03 ; // clkI/O/64 ( 来自预分频器) 16M=4us
OCR1A = T1_TCNT ; // 250MS
}
//-----------------------------------------------//
// 看门狗初始化函数 2s
void WatchDog_Enable ( void )
{
// Watchdog Timer initialization
// Watchdog Timer Prescaler: OSC/2048k
MCUCSR = 0x00 ; // 控制与状态寄存器
WDTCR = 0x1F ; // 看门狗使能 500ms(D)1.0S(E) 2.0S(F)
WDTCR = 0x0F ;
}
// 禁止看门狗
void WatchDog_Disenable ( void ) // 禁止看门狗
{
MCUCSR = 0x00 ; // 控制与状态寄存器
WDTCR =0 b00011111 ; // 看门狗 使能位修改 使能(B4=1) 2.0S(F)
WDTCR =0 b00000111 ; // 禁止看门狗 2.0S(7)
}
// 喂狗
void Watchdog_Reset ( void )
{ # asm ( “wdr” ) } // 喂狗
//-----------------------------------------------//
//-------------子函数----------------------------//
//写入数据到串口
void Send_UART ( unsigned char dat )
{
uchar i ;
#if RS485_En
RX485DE_TX ; // 使能RS485发送
#endif
UDRREG ( COMPORTNo ) = dat ; // UDR0
//等待数据发送完成
while ( ! ( UCSRAREG ( COMPORTNo ) & 0x40 )) ; // 等待数据发送完成 Bit 6 – TXC: USART 发送结束
UCSRAREG ( COMPORTNo )| = 0x40 ; // 清除发送完成状态
#if RS485_En
RX485DE_RX ; // 使能RS485接收
#endif
}
//-----------------------------------------------//
//等待串口数据(注意是”死等“)
unsigned char Wait_UART ( )
{
while ( ! ( UCSRAREG ( COMPORTNo ) & 0x80 )) ; // 等等数据到来 Bit 7 – RXC: USART 接收结束
UCSRAREG ( COMPORTNo )| = 0x80 ; // 清除接收完成状态
#if Run_LEDEn
Run_LED ; // 运行LED闪烁
#endif
return UDRREG ( COMPORTNo ) ;
}
//-----------------------------------------------//
//计算CRC
int CRC16_Word ( char * ptr , int count )
{
int crc = 0 ;
char i ;
while ( -- count 》= 0 )
{
crc = crc ^ ( int ) * ptr ++ 《《 8 ;
i = 8 ;
do
{
if ( crc & 0x8000 )
crc = crc 《《 1 ^ 0x1021 ;
else
crc = crc 《《 1 ;
} while ( -- i ) ;
}
return ( crc ) ;
}
//-----------------------------------------------//
//退出Bootloader程序,从0x0000处执行应用程序
void quit_boot ( void )
{
MCUCR = 0x00 ; //当IVSEL 为“0“ 时,中断向量位于Flash 存储器的起始地址;
#ifdef ATMEGA128
RAMPZ = 0x00 ; //0: ELPM/SPM 可以访问程序存储器地址$0000 - $7FFF ( 低64K 字节)
#endif
# asm ( ”jmp 0x0000“ ) //跳转到Flash的0x0000处,执行用户的应用程序
}
//-----------------------------------------------//
// 读EEPROM一个字节
uchar EEPROM_Read ( uint Addr ) // EEPROM读1个字节操作 0
{
while ( EECR & 0x02 ) ; // 等待上一次写操作结束
EEAR = Addr ; // 设置地址寄存器
EECR | = 0x01 ; // 设置EERE 以启动读操作 b0
return ( EEDR ) ; // 自数据寄存器返回数据
}
// 写EEPROM一个字节
void EEPROM_Write ( uint Addr , char Data ) // EEPROM 写1个字节操作
{
EEAR = Addr ; // 设置地址寄存器
EEDR = Data ; // 设置数据寄存器
// #asm(”cli“) // EEMPE置1,EEPE1,这两步操作中间不能超过4个时钟周期
EECR | = 0x04 ; // 置位EEMWE b2
EECR | = 0x02 ; // 置位EEWE以启动写操作 b1
//#asm(”sei“)
}
//-----------------------------------------------//
//-------------------------------------------------------//
#ifndef _AVR_BOOT_H_
#define _AVR_BOOT_H_ 1
// 配置文件包含
#include ”Config.h“
//------------------------------------------------------//
#ifdef ATMEGA32
#include 《mega32.h》
#endif
#ifdef ATMEGA64
#include 《mega64.h》
#endif
#ifdef ATMEGA128
#include 《mega128.h》
#endif
//---------------------------------------------------//
typedef signed char schar ;
typedef signed int sin t ;
typedef signed long slong ;
typedef unsigned char uchar ;
typedef unsigned int uint ;
typedef unsigned long ulong ;
//--------------------------------------------------//
// 函数申明
void UART_Init ( ) ;
void PORT_Init ( ) ;
void Time1_Init ( ) ;
void Send_UART ( unsigned char dat ) ;
unsigned char Wait_UART ( ) ;
unsigned char Wait1S_UART ( ) ;
void WatchDog_Disenable ( void ) ;
void quit_boot ( void ) ;
int CRC16_Word ( char * ptr , int count ) ;
void write_one_page ( uchar data [ ]) ;
void WatchDog_Enable ( void ) ;
void Watchdog_Reset ( void ) ;
uchar EEPROM_Read ( uint Addr ) ;
void EEPROM_Write ( uint Addr , char Data ) ;
//--------------------------------------------------//
//内部使用的宏定义
#define CONCAT(a, b) a ## b
#define CONCAT3(a, b, c) a ## b ## c
//端口以及位定义
#define PORTREG(No) CONCAT(PORT, No)
#define PINREG(No) CONCAT(PIN, No)
#define DDRREG(No) CONCAT(DDR, No)
#define UDRREG(No) CONCAT(UDR, No)
//串口初始化需要寄存器
#define UCSRAREG(No) CONCAT3(UCSR, No, A)
#define UCSRBREG(No) CONCAT3(UCSR, No, B)
#define UCSRCREG(No) CONCAT3(UCSR, No, C)
#define UBRRHREG(No) CONCAT3(UBRR, No, H)
#define UBRRLREG(No) CONCAT3(UBRR, No, L)
//---------------------------------------------------//
// 端口定义
#define RX485DE_RX PORTREG(RS485PORT)&=~(1 《《 RS485TXEn); // SCI接收使能
#define RX485DE_TX PORTREG(RS485PORT)|=(1 《《 RS485TXEn); // SCI发送使能
#define Run_LED PORTREG(LEDPORT)^= (1 《《 LEDPORTNo); // boot运行LED
//---------------------------------------------------//
//定义Xmoden控制字符
#define XMODEM_NUL 0x00 // null
#define XMODEM_SOH 0x01 // Xmodem数据头
#define XMODEM_STX 0x02 // Xmodem_1K数据头
#define XMODEM_EOT 0x04 // 发送结束
#define XMODEM_ACK 0x06 // 认可响应
#define XMODEM_NAK 0x15 // 不认可响应
#define XMODEM_CAN 0x18 // 撤销传送
#define XMODEM_EOF 0x1A // 填充数据包
#define XMODEM_RWC ‘C’ // CRC16-128
//-------------------------------------------------------//
#ifdef Xmodem_1K
#define BUFFER_SIZE 1024
#else // Xmodem
#define BUFFER_SIZE 128
#endif
//-----------------------------------------------------//
#ifdef ATMEGA32
#define SPM_PAGESIZE 128 // SPM 页大小
#define BootStart 0x3C00*2 // 按字节
#define SPM_REG SPMCR // SPM寄存器
#endif
#ifdef ATMEGA64
#define SPM_PAGESIZE 256 // SPM 页大小
#define BootStart 0x7C00*2 // 按字节
#define SPM_REG SPMCSR // SPM寄存器
#endif
#ifdef ATMEGA128
#define SPM_PAGESIZE 256 // SPM 页大小
#define BootStart 0xFC00*2 // 按字节
#define SPM_REG SPMCSR // SPM寄存器
#endif
//接收缓冲区大小不能小于 SPM_PAGESIZE
#if BUFFER_SIZE 《 SPM_PAGESIZE
#define BUFSIZE SPM_PAGESIZE // UART数据缓存
#else
#define BUFSIZE BUFFER_SIZE // UART数据缓存
#endif
//计算和定义波特率设置参数
#define BAUD_SETTING (unsigned char)((unsigned long)CRYSTAL/ ( 16 * ( unsigned long ) BAUDRATE ) -1 )
#define BAUD_H ((unsigned char)(BAUD_SETTING》》8))
#define BAUD_L (unsigned char)BAUD_SETTING
//计算T1定时器设置参数
#define T1_TCNT (unsigned int)((unsigned long)CRYSTAL*250/ ( 64 * 1000 )) ;
//--------------------------------------------------------//
#endif // _AVR_BOOT_H_
#ifndef _CONFIG_H_
#define _CONFIG_H_ 1
//*********************************************************//
// 关于上海霜蝉-AVR_bootV1.00
// 1、软件版本V1.00 编译环境CodeWizardAVR V2.03.9 Standard;
// 2、支持本公司常用的三种AVR芯片;
// 3、支持标准Xmodem和扩展Xmodem_1K协议;
// 4、联机握手密码为”00“,握手成功手的等待文件超时为1分钟;
// 5、默认复位等待3S退出boot到用户程序或循环运行boot;
// 6、支持1分钟以内的断网续传;
// 7、支持连续10帧以内数据错误的重传;
// 8、支持下载过程中的取消超作;
// 9、支持当收到包时,接收过程中每个字符的超时间隔为 1 秒;
// 10、支持所有的超时及错误事件至少重试 10 次;
// 11、支持数等待超时6S的请求;
// 12、Boot Loader - Size:1024words;
// 13、支持传输速度:38.400KB/S~2.400KB/S;
// 14、支持上海霜蝉常用最多的三个型号ATMEGA32,64,128。
// 15、支持开门狗自定义开关,自定义时钟频率
//*********************************************************//
// 注意:修改编译器配置 (char to int;char is unsigned)
// 注意:填充数据必须大于两包(》=256)
// 定义芯片型号
// 芯片型号选择
//#define ATMEGA32
#define ATMEGA64 // 已测试Atmgea64L
//#define ATMEGA128
// 协议类型选择:Xmodem或Xmodem_1K
#define Xmodem // Xmodex 128(CRC16)
//#define Xmodem_1K // Xmodem 1024(CRC16)
//-----------------------------------------------//
// 系统时钟MHz
#ifndef CRYSTAL
#define CRYSTAL 16000000 // 16M
#endif
// 波特率 38400~2400
#define BAUDRATE 9600
// 等待密码的超时时间 = WiteTimeCnt * 250ms
// 超时次数
#define WiteTimeCnt 10 // 10=2.5s
// 用户设置boot驻留等待握手时间(1=250ms)+2.5S(WiteTimeCnt)
#define Wait_BootTime 40 // (1=250ms)+2.5S
// 等待文件的超时时间 = TimeOutCntC * 250ms
// 发送‘C’的最大次数
#define TimeOutCntC 40 // 40=1min
// 串口号(ATMEA32 串口号为空)
#define COMPORTNo 0 // UART0
// 看门狗使能
#define WDGEn 1 // 使用看门狗
// 使用485模式
#define RS485_En 1 // 使用485使能脚
// 485控制端口和引脚
#define RS485PORT E // PORTE
#define RS485TXEn 2 // PORTE2
// 使用LED指示状态
#define Run_LEDEn 1 // 使能boot运行LED指示
// LED控制端口和引脚
#define LEDPORT B // PORTB
#define LEDPORTNo 6 // PORTB6
// 延时用于解决串口数据出错
#define Delay_En 0
// 用户设置boot驻留等待握手时间(1=250ms)+2.5S(WiteTimeCnt)
// 使此功能需要设置EEPROM
#define Wait_BootTime 40 // (40*250ms)+2.5S
#define EE_TimeAddr 0 // 使用EEPROM 占用1个字节
// 安全升级(升级不成功不进入用户区)
// 使此功能需要设置EEPROM
#define SafeUpdated_En 1
#define EE_SafeAddr 1 // 使用EEPROM 占用1个字节
//-----------------------------------------------//
#endif // _CONFIG_H_
//End of file: bootcfg.h
举报
更多回帖
rotate(-90deg);
回复
相关问答
DTU
串口
STM32的
远程
升级
设计怎样
去
实现
呢
2022-02-18
1173
请问怎样
去
设计
一种
远程
升级
系统?
2021-05-06
1215
嵌入式系统怎么
实现
远程
监控和
升级
?
2019-08-02
2423
如何通过霜蝉
远程
串口可
实现
单片机的
远程
升级
?
2021-10-29
1596
OTA的具体应用场景及
远程
升级
的
远程
的含义具体是什么?
2022-11-14
723
怎样
去
设计
一种
基于4G
DTU
终端的通用定时器呢
2022-01-17
823
请教大神怎样
去
设置
一种
AVR
单片机端口?
2021-07-07
721
如何
去
实现
一种
基于蓝牙的STM32 IAP在线
升级
呢
2021-11-26
1984
怎样
去
使用
一种
机械设备
远程
维护监控系统
2021-09-26
990
如何
去
实现
一种
基于Android系统的蓝牙
远程
控制功能?
2021-05-21
1436
发帖
登录/注册
20万+
工程师都在用,
免费
PCB检查工具
无需安装、支持浏览器和手机在线查看、实时共享
查看
点击登录
登录更多精彩功能!
首页
论坛版块
小组
免费开发板试用
ebook
直播
搜索
登录
×
20
完善资料,
赚取积分