📄 m128_bootloader.c
字号:
/*****************************************************
Compiler: ICC-AVR 6.31
Target: Mega128
Crystal: 16Mhz
Used: T/C0,USART0
*****************************************************/
#include <iom128v.h>
#include <lcd.h>
#define SPM_PAGESIZE 256 /* M128的一个Flash页为256字节(128字) */
#define BAUD 38400 /* 波特率采用38400bps */
#define CRYSTAL 16000000 /* 系统时钟16MHz */
//计算和定义M128的波特率设置参数
#define BAUD_SETTING (unsigned char)((unsigned long)CRYSTAL/(16*(unsigned long)BAUD)-1)
#define BAUD_H (unsigned char)(BAUD_SETTING>>8)
#define BAUD_L (unsigned char)BAUD_SETTING
#define DATA_BUFFER_SIZE SPM_PAGESIZE /* 定义接收缓冲区长度 */
//定义Xmodem控制字符
#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'
/* 定义全局变量 \n:换行 \r:换行后下行垂直对齐 \0:空一行*/
const char startupString[]="Press 'd' download, Press 'e' write eepom \n\rOthers run app.\n\r\0";
char data[DATA_BUFFER_SIZE]; //256
long address = 0;
char temp=0;
char load_flag=0;
char wdee_flag=0;
char start_flag=0;
/******************************************************************************/
/* 擦除(code=0x03)和写入(code=0x05)一个Flash页 */
void boot_page_ew(long p_address,char code)
{
asm("mov r30,r16\n"
"mov r31,r17\n"
"out 0x3b,r18\n"); /* 将页地址放入Z寄存器和RAMPZ的Bit0中 */
SPMCSR = code; /* 寄存器SPMCSR中为操作码 */
asm("spm\n"); /* 对指定Flash页进行操作 */
}
/******************************************************************************/
/* 填充Flash缓冲页中的一个字 */
void boot_page_fill(unsigned int address,int data)
{
asm("mov r30,r16\n"
"mov r31,r17\n" /* Z寄存器中为填冲页内地址 */
"mov r0,r18\n"
"mov r1,r19\n"); /* R0R1中为一个指令字 */
SPMCSR = 0x01;
asm("spm\n");
}
/******************************************************************************/
/* 等待一个Flash页的写完成 */
void wait_page_rw_ok(void)
{
while(SPMCSR & 0x40)
{
while(SPMCSR & 0x01);
SPMCSR = 0x11;
asm("spm\n");
}
}
/******************************************************************************/
//更新一个Flash页的完整处理
void write_one_page(void)
{
int i;
boot_page_ew(address,0x03); //擦除一个Flash页
wait_page_rw_ok(); //等待擦除完成
for(i=0;i<SPM_PAGESIZE;i+=2) //将数据填入Flash缓冲页中
{
boot_page_fill(i, data[i]+(data[i+1]<<8));
}
boot_page_ew(address,0x05); //将缓冲页数据写入一个Flash页
wait_page_rw_ok(); //等待写入完成
}
/******************************************************************************/
//从RS232发送一个字节
void uart_putchar(char c)
{
while(!(UCSR0A & 0x20)); //发送寄存器空?
UDR0 = c;
}
/******************************************************************************/
//从RS232接收一个字节
int uart_getchar(void)
{
unsigned char status,res;
if(!(UCSR0A & 0x80)) return -1; //no data to be received
status = UCSR0A;
res = UDR0;
if (status & 0x1c) return -1; // If error, return -1
return res;
}
/******************************************************************************/
/* 等待从RS232接收一个有效的字节 */
char uart_waitchar(void)
{
int c;
while((c=uart_getchar())==-1);
return (char)c;
}
/******************************************************************************/
/* 计算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) //最高位为1
crc = crc << 1 ^ 0x1021; //先左移,然后与多项式(0x1021)异或
else
crc = crc << 1; //最高位为0的话,直接左移
} while(--i);
}
return (crc);
}
/******************************************************************************/
//退出Bootloader程序,从0x0000处执行应用程序
void quit(void)
{
uart_putchar('\n');
uart_putchar('O');
uart_putchar('K');
uart_putchar(0x0d); // '\'
uart_putchar(0x0a); // '\n'
while((UCSR0A&0x20)==0); //等待发送完(比较重要)
MCUCR = 0x01;
MCUCR = 0x00; //将中断向量表迁移到应用程序区头部
RAMPZ = 0x00; //RAMPZ清零初始化
asm("jmp 0x0000\n"); //跳转到Flash的0x0000处,执行用户的应用程序
}
/*************************************/
void ee_write(char adr,char dat)
{
SPMCSR&= 0xfe; //禁止写flash
while(EECR&0x02); //EEWE=1则等待
EEARH=0;
EEARL=adr; //写地址
EEDR=dat; //写数据
EECR|=0x04; //EEMWE置1
EECR|=0x02; //EEWE置1
}
////////////////////////////////////////////////////////////////////////////////
/******************************************************************************/
/* 主程序 */
void main(void)
{
int i = 0;
unsigned char timercount = 0;
unsigned char packNO = 1;
int bufferPoint = 0;
unsigned int crc;
init_io(); //点D1
asm("cli\n"); //关中断
CBI(PORTG,B_L); //开背光
init_lcd();
disp_boot();//显示启动界面
//初始化M128的USART0
UBRR0H = BAUD_H;
UBRR0L = BAUD_L; //Set baud rate
UCSR0B = 0x18; //Enable Receiver and Transmitter
UCSR0C = 0x0E; //Set frame format: 8data, 2stop bit
//初始化M128的T/C0,15ms自动重载
OCR0 = 0xEA;
TCCR0 = 0x0F;
//向PC机发送开始提示信息
while(startupString[i]!='\0')
{
uart_putchar(startupString[i]);
i++;
}
//3秒种等待PC下发"d",否则退出Bootloader程序,从0x0000处执行应用程序
while(1)
{
temp=uart_getchar();
if(temp == 'd')
{
uart_putchar('d');
uart_putchar('\r');
uart_putchar('\n');
load_flag=1;
break;
}
if(temp == 'e')
{
uart_putchar('e');
uart_putchar('\r');
uart_putchar('\n');
wdee_flag=1;
break;
}
if (TIFR & 0x02) //timer0 over flow
{
TIFR = TIFR|0x02;
if (++timercount > 100)
{
timercount=0;
quit(); //200*15ms = 3s
}
}
}
disp_load(); //显示开始下载程序
//每秒向PC机发送一个控制字符"C",等待控制字〈soh〉
while(uart_getchar()!=XMODEM_SOH) //receive the start of Xmodem
{
if(TIFR & 0x02) //timer0 over flow
{
TIFR=TIFR | 0x02;
if(++timercount > 67) //wait about 1 second
{
uart_putchar(XMODEM_RECIEVING_WAIT_CHAR); //send a "C"
uart_putchar('\r');
uart_putchar('\n');
timercount=0;
}
}
}
do //开始接收数据块
{
if ((packNO == uart_waitchar()) && (packNO ==(~uart_waitchar()))) //核对数据块编号正确
{
for(i=0;i<128;i++) //接收128个字节数据
{
data[bufferPoint]= uart_waitchar();
bufferPoint++; //最大256
}
crc = (uart_waitchar()<<8);
crc += uart_waitchar(); //接收2个字节的CRC效验字
if(calcrc(&data[bufferPoint-128],128)==crc) //CRC校验验证
{ //正确接收128个字节数据
if(load_flag)
{
while(bufferPoint >= SPM_PAGESIZE) //正确接受256个字节的数据
{
write_one_page(); //收到256字节写入一页Flash中
address += SPM_PAGESIZE; //Flash页加1 (256==>0x100)
bufferPoint = 0;
}
}
else if(wdee_flag) //写eeprom
{
while(bufferPoint >= SPM_PAGESIZE) //正确接受256个字节的数据
{
for(temp=0;temp<255;temp++)
ee_write(address,data[temp]);
address+=SPM_PAGESIZE;
bufferPoint=0;
uart_putchar(XMODEM_CAN);
quit(); //退出
}
}
uart_putchar(XMODEM_ACK); //正确收到一个数据块
packNO++; //数据块编号加1
}
else
{
uart_putchar(XMODEM_NAK); //要求重发数据块
}
}
else
{
uart_putchar(XMODEM_NAK); //要求重发数据块
}
}while(uart_waitchar()!=XMODEM_EOT); //循环接收,直到全部发完
uart_putchar(XMODEM_ACK); //通知PC机全部收到
if(bufferPoint) write_one_page(); //把剩余的数据写入Flash中
quit(); //退出Bootloader程序,从0x0000处执行应用程序
}
/******************************************************************************/
////////////////////////////////////////////////////////////////////////////////
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -