AVRBOOT By SIL 2008.4.24
"您可以任意下载修改内容,但要标明 原作者SIL。sil.cublog.cn 谢谢。"
在你抄录时候请顺带上面一句
readme.txt
AVRBOOT By SIL 2008.4.24
"您可以任意下载修改内容,但要标明 原作者SIL。sil.cublog.cn 谢谢。"
在你抄录时候请顺带上面一句
Table of Contents:
~~~~~~~~~~~~~~~~~~
1,简介。Introduction
2,工作环境与用法。Work envirenment and how to use
3,程序风格与增加新命令。Style of code and add new command
4,程序的调试与串口。 Debug code and usart0,usart1 on atmega64
5,现在支持的命令。 all cmd supported now
6,感谢
1,Introduction
~~~~~~~~~~~~~~~
This document describes an AVRbootloader by sil on atmege64 cpu and
by convenience way
to extend more commands into avrboot. at same time showing all needed codes.
2,Work envirenment and how to use
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
平台:atmega64+WINAVR+AVRSTUDIO
下载所有文件之后,编译成功后,在用JTAG接口的软件,置位熔丝为BOOTLOAD开始地址为0X7000,也就
是最大8K,同时bootrst势能。
烧入编译好的bootload.之后用超级终端设置为串口0,波特率为9600,8,1,无数据流。
启动你的系统,会在超级终端中看到Please touch 'd' 如果你按下d键,将进入AVRBOOT>>提示符,
如果没有按下d,那么将进入应用程序的执行。在提示符下键入load回车,之后你在超级终端中发送文件即可。
协议是xmodem,文件小于64K-8K=56K。记得是.bin类型的文件而不是hex的。
3,Style of code and add new command
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
结构做的不是很完善,但还算方便。主要是命令的接收与对应命令处理2个部分。。
为了调试系统其他部分的功能,考虑可以加入新的命令。具体例子参见jmp.c。
你新增加的命令文件命名最好是cmd_xx.c,cmd_xx.h同时需要你修改MAKEFILE中2个
位置,你看一下很明显就知道修改那里,看看其他.c文件在MAKEFILE中那里,你加入即可
同时需要你把jmp.c中的主要是新命令处理函数的设计及其声明(一定显示的给出)。
所有命令是4个字母组成。如果你想修改可以方便的修改
命令处理部分以及命令的数据结构。
4,Debug code and usart0,usart1 on atmega64
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
在文件usart.c中你可以发现用了2个串口。串口0用于下载文件的交互,而串口1用于调试
以及 #undef DEBUG_PRINTF的开关。
5,all cmd supported now
~~~~~~~~~~~~~~~~~~~~~~~
5.1
command: load
usage:在命令提示符下,输入load即可,在发送文件
5.2
command: tjmp
usage:在命令提示符下,输入tjmp即可,系统就会跳转到0000处执行,如果没有写过应用程序,那么还回回到命令提示符下。
6,感谢
~~~~~~
如果你有任何的疑问,可以看看下面这个人的文章,我也是看了他
的文章后,决定公开自己的代码的。
46893b4c4380143cd48b4468d4e419ce3b461e1a26b4ac275543
4591d27d135dfc160bfdf140246a4777fcc1999f4ad9e0c43f2ef
e273e711b8455578e59f9c40c7a9c76c705feae69f0ccf225e0d8
c5a5df4324c1&p=973dd11186cc43f91bbe9b7a4f&user=baidu
makefile
#只需要修改3个地方:
# 1.第一行和第二行PRG,OBJ的定义
# 2.MCU_TARGET
# 3.# dependency:
#
#
PRG = avr64boot
OBJ = avr64boot.o usart.o cmd_load.o timer.o jmp.o
#MCU_TARGET = atmega48
MCU_TARGET = atmega64
#MCU_TARGET = atmega640
OPTIMIZE = -O2
DEFS =
LIBS =
# You should not have to change anything below here.
CC = avr-gcc
# Override is only needed by avr-lib build system.
override CFLAGS = -g -Wall $(OPTIMIZE) -mmcu=$(MCU_TARGET) $(DEFS)
#override LDFLAGS = -Wl,-Map,$(PRG).map
override LDFLAGS = -Wl,-Map=$(PRG).map -Ttext 0xE000
#-Wl,--section-start=.text=0x0000
#-Wl,--section-start=.text=0xE000
OBJCOPY = avr-objcopy
OBJDUMP = avr-objdump
all: $(PRG).elf lst text eeprom
$(PRG).elf: $(OBJ)
$(CC) $(CFLAGS) $(LDFLAGS) -o $@ $^ $(LIBS)
# dependency:
avr64boot.o: avr64boot.c
usart.o:usart.c
cmd_load.o:cmd_load.c
timer.o:timer.c
jmp.o:jmp.c
clean:
rm -rf *.o $(PRG).elf *.eps *.png *.pdf *.bak
rm -rf *.lst *.map $(EXTRA_CLEAN_FILES)
lst: $(PRG).lst
%.lst: %.elf
$(OBJDUMP) -h -S $< > $@
# Rules for building the .text rom images
text: hex bin srec
hex: $(PRG).hex
bin: $(PRG).bin
srec: $(PRG).srec
%.hex: %.elf
$(OBJCOPY) -j .text -j .data -O ihex $< $@
%.srec: %.elf
$(OBJCOPY) -j .text -j .data -O srec $< $@
%.bin: %.elf
$(OBJCOPY) -j .text -j .data -O binary $< $@
# Rules for building the .eeprom rom images
eeprom: ehex ebin esrec
ehex: $(PRG)_eeprom.hex
ebin: $(PRG)_eeprom.bin
esrec: $(PRG)_eeprom.srec
%_eeprom.hex: %.elf
$(OBJCOPY) -j .eeprom --change-section-lma .eeprom=0 -O ihex $< $@ \
|| { echo empty $@ not generated; exit 0; }
%_eeprom.srec: %.elf
$(OBJCOPY) -j .eeprom --change-section-lma .eeprom=0 -O srec $< $@ \
|| { echo empty $@ not generated; exit 0; }
%_eeprom.bin: %.elf
$(OBJCOPY) -j .eeprom --change-section-lma .eeprom=0 -O binary $< $@ \
|| { echo empty $@ not generated; exit 0; }
# Every thing below here is used by avr-libc's build system and can be ignored
# by the casual user.
FIG2DEV = fig2dev
EXTRA_CLEAN_FILES = *.hex *.bin *.srec
dox: eps png pdf
eps: $(PRG).eps
png: $(PRG).png
pdf: $(PRG).pdf
%.eps: %.fig
$(FIG2DEV) -L eps $< $@
%.pdf: %.fig
$(FIG2DEV) -L pdf $< $@
%.png: %.fig
$(FIG2DEV) -L png $< $@
avr64boot.h
struct command_struct
{
char *name;
void (*function)(void);
struct command_struct*next;
};
void add_new_command(struct command_struct*new);
#define __initcall __attribute__ ((naked)) __attribute__ ((section (".init8")))
#define __init __attribute__ ((naked)) __attribute__ ((section (".init7")))
void add_new_command(struct command_struct*new);
cmd_load.h
void init_load_module();
jmp.h
void init_tjmp_module();
void jmp_to_amplication();
timer.h
#define t_delay 1
#define t_timer 2
#define t_micros 3
//初始化都为0。execute_flag等于e表示有效,0无效
struct timer_list
{
long execute_flag; //定时器有效标识指针
unsigned long expires; //记录当前计数的大小。
void (*function)(long); //定时器函数
long data; //定时器函数的参数
char tm_tpye; //定时器类型
};
void enable_timer(unsigned char contccr0);
void disable_timer(void);
int get_new_timer(unsigned int settimer,struct timer_list* timer,char tm_tpye,void (*function)(long),long data);
void clear_timer_list(struct timer_list* timer);
usart.h
void uart_puts(const char *s);
void uart_init(void);
void uart_putc(char c);
char uart_getc();
char uart_waitchar(void);
avrboot.c
#include
#include
#include
#include "usart.h"
#include "avr64boot.h"
#include "cmd_load.h"
#include "timer.h"
#include "jmp.h"
char* prompt="\r\nAVRBOOT>>";
void __init init_command_module();
struct command_buff
{
char comandbuf[4];
char cn;
};
struct command_buff cmdbuf;
struct command_struct command_list_head;
struct command_struct* command_list_tail;
void clear_cmd_buff()
{
cmdbuf.comandbuf[0]=0;
cmdbuf.comandbuf[1]=0;
cmdbuf.comandbuf[2]=0;
cmdbuf.comandbuf[3]=0;
}
void add_new_command(struct command_struct*new)
{
if(command_list_head.next==NULL)
command_list_head.next=new;
else
command_list_tail->next=new;
new->next=NULL;
command_list_tail=new;
}
void init_command_module()
{
//改变系统中断地址为0xe000.这样定时器才能用
MCUCR= 0x1;
MCUCR= 0x2;
command_list_head.next=NULL;
}
static void search_cmd_done()
{
int ret;
struct command_struct* pcmd_struct;
struct command_struct* pt;
int i;
pt=command_list_head.next;
for(;pt!=NULL;)
{
pcmd_struct=pt;
if(strncmp(pcmd_struct->name,cmdbuf.comandbuf,4)==0)
{
//printf("555555555\n");
pcmd_struct->function();
break;
}
if(pcmd_struct->next!=NULL)
pt=pcmd_struct->next;
else
break;
}
clear_cmd_buff();
}
int main(void)
{
volatile struct timer_list delay_key;
char tmp;
//init_command_module();
uart_init();
timer1_module_init();
//加入时间的检测如果大于2秒,就进入应用程序。
uart_puts("\r\nPlease touch 'd'\r\n");
get_new_timer(3,&delay_key,t_delay,NULL,0);
tmp=0xff;
while(!delay_key.execute_flag)
{
tmp=uart_getc();
if(tmp=='d')
{
delay_key.execute_flag=0;
break;
}
}
if(delay_key.execute_flag==0xe)
jmp_to_amplication();
while(1)
{
uart_puts(prompt);
cmdbuf.cn=0;
while((tmp=uart_waitchar())!=13)
{
if((cmdbuf.cn)==4)
{
uart_puts(prompt);
cmdbuf.cn=0;
}else
{
cmdbuf.comandbuf[cmdbuf.cn]=tmp;
uart_putc(tmp);
(cmdbuf.cn)++;
}
}
search_cmd_done();
}
}
cmd_load.c
#include
#include
#include
#include
#include"avr64boot.h"
#include "usart.h"
#include "timer.h"
#include "jmp.h"
#define SPM_PAGESIZE 256 //M128的一个Flash页为256字节(128字)
//#define SPM_PAGE_WORD 128
#define DATA_BUFFER_SIZE SPM_PAGESIZE //定义接收缓冲区长度
//定义Xmoden控制字符
#define XMODEM_NUL 0x00
#define XMODEM_SOH 0x01
#define XMODEM_STX 0x02
#define XMODEM_EOT 0x04
#define XMODEM_ACK 0x06
#define XMODEM_NAK 0x15
#define XMODEM_CAN 0x18
#define XMODEM_EOF 0x1A
#define XMODEM_RECIEVING_WAIT_CHAR 'C'
struct command_struct cmd_load;
volatile struct timer_list delay_gsm;
//void init_load_module() __attribute__ ((naked)) \
//__attribute__ ((section (".init8")));
void __initcall init_load_module();
char data[DATA_BUFFER_SIZE]; //页数据
long address = 0; //字节表示的现在PROGRAM编程的页开始地址
char full; //用于记录当前操作的阶段,如果已经达到256字节,那么开始要写页操作,之后,重新回到0阶段
char buff[132]; //用于暂时的数据接收
void write_one_page()
{
unsigned long i;
//擦除
boot_page_erase(address);
//boot_spm_busy_wait();
while(boot_rww_busy())
boot_rww_enable();
//填充
for(i = 0; i < SPM_PAGESIZE; i+= 2)
boot_page_fill(i,*((unsigned int*)(data+i)));
//写入
boot_page_write(address);
//boot_spm_busy_wait();
while(boot_rww_busy())
boot_rww_enable();
//for(i=0;i //printf("%x\n",pgm_read_byte(i));
}
//计算CRC
int calcrc(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);
}
/*
为了支持连续第二次编程,在这里需要重新在次的初始化数据结构。
*/
void init_varial()
{
full=0;
address = 0;
}
void load_function()
{
int i = 0;
unsigned char timercount = 0;
unsigned char packNO = 1;
int bufferPoint = 0; //字节表示的当前缓冲区的索引
unsigned int crc,pcr;
address=0;
char c;
unsigned char tmp,tmp1,tmp2;
init_varial();
uart_puts("\r\nLoad start\r\n");
get_new_timer(1,&delay_gsm,t_delay,NULL,0);
while(uart_getc()!=XMODEM_SOH)
{
if(delay_gsm.execute_flag)
{
uart_putc(XMODEM_RECIEVING_WAIT_CHAR);
get_new_timer(1,&delay_gsm,t_delay,NULL,0);
}
}
disable_timer();
bufferPoint=0;
//开始接收数据块
do
{
for(i=0;i<132;i++) //每次都先用buff接收132个字节数据
{
buff[i]= uart_waitchar();
bufferPoint++;
}
tmp=buff[0];
tmp1=buff[1];
tmp2=(~tmp1);
if((packNO==tmp)&&(packNO==tmp2))
{
//核对数据块编号正确
crc = ((buff[130])<<8);
crc += (unsigned char)(buff[131]);
if((pcr=calcrc(&buff[2],128))==crc) //CRC校验验证
{ //正确接收128个字节数据
if(full==0)
{
bufferPoint=0;
full=1;
}else if(full==1)
{
bufferPoint=128;
full=2;
}
for(i=0;i<128;i++)
{
data[bufferPoint+i]=buff[i+2];
}
if(full==2)
{ //正确接受256个字节的数据
write_one_page(); //收到256字节写入一页Flash中
address += SPM_PAGESIZE; //Flash页加1
full=0;
}
uart_putc(XMODEM_ACK); //正确收到一个数据块
packNO++; //数据块编号加1
}
else
{
uart_putc(XMODEM_NAK); //要求重发数据块
}
}
else
{
uart_putc(XMODEM_NAK); //要求重发数据块
}
}while(uart_waitchar()!=XMODEM_EOT); //循环接收,直到全部发完
uart_putc(XMODEM_ACK); //通知PC机全部收到
if(full==1)
write_one_page(); //把剩余的数据写入Flash中
uart_puts("\r\nLoad is over\r\n");
jmp_to_amplication(); //退出Bootloader程序,从0x0000处执行应用程序
}
void init_load_module()
{
cmd_load.name="load";
cmd_load.function=load_function;
add_new_command(&cmd_load);
}
jmp.c
#include
#include"avr64boot.h"
#include "jmp.h"
struct command_struct cmd_jmp;
void __initcall init_tjmp_module();
void jmp_to_amplication()
{
uart_putc('O');uart_putc('K');
uart_putc(0x0d);uart_putc(0x0a);
//while(!(UCSR0A & 0x20)); //等待结束提示信息回送完成
loop_until_bit_is_set(UCSR0A, UDRE0);
MCUCR = 0x01; //改变系统中断地址为0x0000
MCUCR = 0x00; //将中断向量表迁移到应用程序区头部
asm("jmp 0x0000\n"); //跳转到Flash的0x0000处,执行用户的应用程序
}
void init_tjmp_module()
{
cmd_jmp.name="tjmp";
cmd_jmp.function=jmp_to_amplication;
add_new_command(&cmd_jmp);
}
timer.c
#include
#include
#include
#include "timer.h"
#include "usart.h"
#define max_needed_timer 3
//ONE_SECOND 用来表示一秒钟timer在默认频率的中断次数。//用总共中断次数表示多少秒
const int ONE_SECOND = 1;//1秒中断次数
const int fraction =0 ; //小数部分
volatile char systimer_nb=0;
volatile struct timer_list* systimer[max_needed_timer];//4
/****************************************定时器数据结构的初始化*********************************************/
static void timer_variable_init()
{
int i;
for(i=0;i {
systimer[i]=NULL;
}
}
/************************************* 硬件相关控制函数 *************************************/
void enable_timer_default()
{
TCCR1B=(_BV(WGM12)|_BV(CS12)|_BV(CS10)); //控制势能 //CTC ,NO Toggle 1024prescal
}
void disable_timer(void)
{
TCCR1B=0;
}
void timer1_module_init()
{
timer_variable_init();//数据结构变量初始化
TCCR1A=0;//no campare out ,ctc mode
TCCR1C=0;//
TCNT1=0;// increasing count reg_init =0;
//1s中断一次
//OCR1A=0x1e84;//溢出值 (8000000/1024)/0x1e84=1s
OCR1A=0x1c20;
SFIOR = _BV(TSM);//设置同步模式
TIMSK=_BV(OCIE1A);//比较中断 势能
TCCR1B=(_BV(WGM12)|_BV(CS12)|_BV(CS10)); //控制势能ctc,1024
}
/**************************************************** 设备无关 *************************************************************/
/****************************************** 模拟定时器实现 *****************************************************************/
SIGNAL(SIG_OUTPUT_COMPARE1A)
{
//循环安全的跳出
struct timer_list* ptimerl;
int i;
if(systimer_nb!=0)
{
for(i=0;i {
if(systimer[i])
{
if(systimer[i]->expires!=0)
{
(systimer[i]->expires)--;
}
else
{
if((systimer[i]->tm_tpye)==t_delay)
{
systimer[i]->execute_flag=0xe;
systimer[i]=NULL;
systimer_nb--;
}
else
{
ptimerl=systimer[i];
systimer[i]=NULL;
systimer_nb--;
ptimerl->function(ptimerl->data);
}
}
}
}
}
}
/*设置一个定时器的时间,,*/
void set_time(unsigned int sec,unsigned long* time_timer0)
{
disable_timer();
//timer_variable_init();
*time_timer0=sec*ONE_SECOND-1;
//printf("set timer %d ... count %d \n",sec,*time_timer0);
enable_timer_default();
}
void clear_timer_list(struct timer_list* timer)
{
timer->execute_flag=0;
timer->expires=0;
timer->function=NULL;
timer->data=0;
timer->tm_tpye=0;
}
/**************************************************************************************************************************
******************************************* New timer interface 设备无关 ***********************************************
***************************************************************************************************************************/
/*
函数说明:申请一个定时器,如果成功,就是直接启动这个定时器,现在限制为秒级别的设置
参数:参数1预计的定时时间,参数2,定时器的计数模块。
返回值:
成功:0
失败:-1 已经没有可以用的定时器了
*/
int get_new_timer(unsigned int settimer,struct timer_list* timer,char tm_tpye,void (*pfunction)(long),long pdata)
{
int i;
if(systimer_nb>(max_needed_timer-1))
{
//printf("sorry no timer give you now\n");
return -1;
}
for(i=0;i {
if(systimer[i]==timer)
{
//printf("cant get same timer when it is running\n");
return -1;
}
}
for(i=0;i {
if(systimer[i]==NULL)
{
timer->execute_flag=0;
set_time(settimer,&(timer->expires));
timer->function=pfunction;
timer->data=pdata;
timer->tm_tpye=tm_tpye;
systimer[i]=timer;//一定放在最后
systimer_nb++;
break;
}
}
//enable_timer_default();
sei(); //不知道为什么在把以前设计的用来做延迟的代码去掉后,而该用新的接口会出现中断被关闭的问题,
return 0;
}
usart.c
#include
#include "usart.h"
#include
//#define DEBUG_PRINTF
#undef DEBUG_PRINTF
#ifdef DEBUG_PRINTF
static int uart_putchar(char c, FILE *stream);
static FILE mystdout = FDEV_SETUP_STREAM(uart_putchar, NULL,
_FDEV_SETUP_WRITE);
static int uart_putchar(char c, FILE *stream)
{
if (c == '\n')
uart_putchar('\r', stream);
loop_until_bit_is_set(UCSR1A, UDRE);
UDR1 = c;
return 0;
}
static void call_in_initial(void)
{
stdout = &mystdout;
}
#else
static void call_in_initial(void)
{
}
#endif
void uart_putc(char c)
{
loop_until_bit_is_set(UCSR0A, UDRE);
DDRC=0XFF;
PORTC=0xff;
UDR0=c;
loop_until_bit_is_set(UCSR0A, UDRE);
DDRC=0XFF;
PORTC=0;
}
char uart_getc()
{
char reg;
//loop_until_bit_is_set(UCSR0A, UDRE);
reg=UCSR0A;
if(reg&0x1c)
return -1;
return UDR0;
}
char uart_waitchar(void)
{
char reg;
loop_until_bit_is_set(UCSR0A, RXC);
return UDR0;
}
void uart_puts(const char *s)
{
while(*s){
uart_putc(*s++);
}
}
#ifdef DEBUG_PRINTF
void uart1_init(void)
{
UCSR1B = 0x00; //disable while setting baud rate
UCSR1A = 0x00; //U2X0 = 0,不加倍数率
UCSR1C = 0x06;//|0x40; //8位
UBRR1L = 7; //set baud rate lo,波特率为57600
UBRR1H = 0x00; //set baud rate hi
UCSR1B = 0x98; //接收中断允许,接收缓冲自动清空,接收势能
}
#else
void uart1_init(void)
{
}
#endif
void uart_init(void)
{
call_in_initial();
uart1_init();
UCSR0B = 0x00; //disable while setting baud rate
UCSR0A = 0x00; //U2X0 = 0,不加倍数率
UCSR0C = 0x06;//|0x40; //8位
UBRR0L =47;//9600 47 4800 95 ; //set baud rate lo,波特率为115200
UBRR0H = 0x00; //set baud rate hi
//UCSR0B = 0x98; //接收中断允许,接收缓冲自动清空,接收允许
UCSR0B = 0x18;
}