最近一直利用业余时间写自己的“基于AVR-BootLoader”,启发是由于一次在ourAVR论坛看到了绍子阳的bootloader,联想到公司在用AVR MCU,但每次升级程序都要花费很大的力气车马劳顿的跑到工程现场,而且很多机器还安装在国外,为了升级一次程序发费了很多的人力物力财力,加上公司的机器目前大部分都配有远程监控系统,所以本人决定写一个具有自有产权的“AVR-BootLoader”。
特别说明:本 “AVR-BootLoad” 软件代码属上海霜蝉版权所有,在此贡献发布,仅限于个人免费使用不得用于商业用途,本人也不保证代码的严谨性,如在升级中出现任何问题与本人无关,本人已测试过Atmega64A与Atmega128。话不多说了上源代码,网友们和AVR爱好者可以拷贝到 CodeWizardAVR V2.03.9编译器下编译。
需要讨论或有遇到BUG的网友们可以联系我:QQ:285247488 mail:shw@scicala.com
上位机截图:
远程升级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.
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