guew 发表于 2013-5-23 20:49:53

我的bootloader,死活写不了程序

本帖最后由 guew 于 2013-5-23 21:01 编辑

参照马老师的程序,用cvavr给mega32写bootloader,上位机通讯正常,数据传送成功,单步调试确认接受到的数据也正常。但从0x0000开始的flash就是没有数据,那三个加载、页写,等待函数看不出有什么问题,好心人帮帮我。。。/*****************************************************
This program was produced by the
CodeWizardAVR V2.05.0 Professional
Automatic Program Generator
?Copyright 1998-2010 Pavel Haiduc, HP InfoTech s.r.l.
http://www.hpinfotech.com
Chip type               : ATmega32
Program type            : Boot Loader - Size:512words
AVR Core Clock frequency: 12.000000 MHz
Memory model            : Small
External RAM size       : 0
Data Stack size         : 512
*****************************************************/
#include <mega32.h>
#include <dtype.h>
#define PAGE_SIZE 128
#define DATA_BUFFER_SIZE PAGE_SIZE

#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'

flash uint8 startup_message[] = "type 'd' download, others run app.\0";
uint8 data;
uint16 address = 0;

//擦除(code = 0x03)和写入(code = 0x05)一个flash页
void write_page(uint16 p_address,uint8 p_code)
{
    #asm("lds r30,$025a");   //将p_address的低八位放入Y寄存器的低八位
    #asm("lds r31,$025b");   //将p_address的高八位放入Y寄存器的高八位
    SPMCR = p_code;   //给SPMCR寄存器赋擦除控制指令
    #asm("spm");      //擦除指令生效
}

// 填充一个flash缓冲页中的一个字
void page_fill(uint16 p_address,uint16 p_data)
{
    #asm("lds r30,$025a");   //将p_address的低八位放入Y寄存器的低八位
    #asm("lds r31,$025b");   //将p_address的高八位放入Y寄存器的高八位
    #asm("lds r0,$0258");    //将data的低八位放入r0寄存器
    #asm("lds r1,$0259");    //将data的高八位放入r1寄存器
    SPMCR = 0x01;   //给SPMCR寄存器赋加载页指令
    #asm("spm");    //加载页指令生效
}

//等待一个flash页的写完成
void wait_page_write_done(void)
{
    while(SPMCR & 0x40)   //如果正在擦除或页写RWWSB置位,执行while
    {
      while(SPMCR & 0x01);    //如果正在擦除,等待
      SPMCR = 0x11;   //给SPMCR寄存器写入RWW区使能(读)命令 ,并清除RWWSB,while结束,跳出等待
      #asm("spm");    //使能指令生效
    }
}

//更新一个flash页的完整处理
void write_one_page(void)
{
    uint16 i;
    write_page(address,0x03);   //擦除一个flash页
    wait_page_write_done();    //等待擦除完成
    for(i = 0;i < PAGE_SIZE;i += 2) //将数据填入flash缓冲页中
    {
      page_fill(i,data+(data << 8));
            
    }
    write_page(address,0x05);   //将缓冲数据写入一个flash页
    wait_page_write_done();   //等待写入完成
}

//从RS232发送一个字节
void uart_putchar(uint8 c)
{
    while(!(UCSRA & 0x20));      //等待上一个发送完成
    UDR = c;    //发送一个数据
}

//从RS232接受一个字节
int16 uart_getchar(void)
{
    uint8 status,res;
    if(!(UCSRA & 0x80))   //如果没有数据,返回-1
      return -1;
    status = UCSRA;
    res = UDR;
    if(status & 0x1c)       //如果通信硬件有错误
      return -1;
    return res;   //如果没有错误,返回收到的结果   
}

//等待从RS232接收一个有效字节
uint8 uart_waitchar(void)
{
    int16 c;
    while((c = uart_getchar()) == -1);
    return (uint8)c;   
}

//计算CRC
int16 cal_crc(uint8 *ptr, int16 counter)
{
    int16 crc = 0;
    uint8 i;
    while(--counter >= 0)
    {
      crc = crc^(int16)*ptr++ << 8;
      i = 8;
      do
      {
            if(crc & 0x8000)
                crc = crc << 1^0x1021;
            else
                crc = crc << 1;
      }while(--i);
    }
    return crc;
}

//退出bootlaoder程序,从0x0000处执行应用程序
void quit(void)
{
    uart_putchar('O');
    uart_putchar('K');
    uart_putchar(0x0d);
    uart_putchar(0x0a);
    while(!(UCSRA & 0x20));//等待结束提示信息发送完成
    GICR = 0x01;   //打开中断迁移允许开关
    GICR = 0x00;   //将中断向量表迁回到应用程序区头部
    #asm("jmp $0000");    //转跳到flash的0x0000处,执行用户的应用程序
}

void main(void)
{
    uint8 i = 0;
    uint8 timercount = 0;
    uint8 pack_no = 1;
    uint16 buffer_pointer = 0;
    uint16 crc;
    //GICR = 0x01;   //打开中断迁移允许开关
    //GICR = 0x02;   //将中断向量表迁回到应用程序区头部
    UBRRL = 0x4d; //波特率9600
    UCSRB = 0x18;   //使能传输与发送
    UCSRC = 0x86;   //8个数据位,1个stop位
    OCR0 = 0xea;    //(234+1) 30ms计时
    TCCR0 = 0x0d;   //CTC模式,1024分频
    //#asm("sei");
   
    //向PC发送开始提示信息
    while(startup_message != '\0')
    {   
      uart_putchar(startup_message);
      i++;
    }
   
    //3秒钟等待PC下发'd',否则退出bootloader程序,从0x0000处执行应用程序
    while(1)
    {
      if(uart_getchar() == 'd')
            break;
      if(TIFR & 0x02)   //timer0 overflow
      {
            if(++timercount > 250)      //100*30ms = 3s到点
                quit();
            TIFR = TIFR | 0x02;
      }
    }
   
    //每秒向PC机发送一个控制字符'C',等待控制字符 <soh>
    while(uart_getchar() != XMODEM_SOH)    //没有收到开始字符就等待
    {
      if(TIFR & 0x02)
      {
            if(++timercount > 100)
            {
                uart_putchar(XMODEM_RECIEVING_WAIT_CHAR);   //发送'C'
                timercount = 0;
            }
            TIFR |= 0x02;
      }
    }
   
    do
    {
      if((pack_no == uart_waitchar()) && (pack_no == (~uart_waitchar())))//校对阴阳两个数据包号
      {
            for(i = 0; i < 128;i++)
            {
                data = uart_waitchar();
                buffer_pointer++;
            }
            crc = (uart_waitchar() << 8);
            crc += uart_waitchar();
            if(cal_crc(&data,128) == crc)   //CRC校验
            {
                while(buffer_pointer >= PAGE_SIZE)
                {
                  write_one_page();
                  address += PAGE_SIZE;   
                  buffer_pointer = 0;
                }
                uart_putchar(XMODEM_ACK);
                pack_no++;
            }
            else
            {
                uart_putchar(XMODEM_NAK);   //要求重发数据块   
            }
      }
      
      else
      {
            uart_putchar(XMODEM_ACK);   //要求重发数据块
      }
    }
    while(uart_waitchar() != XMODEM_EOT);
   
    uart_putchar(XMODEM_ACK);   //通知PC全部数据收到
   
    if(buffer_pointer)
      write_one_page();   //写最后一页零散数据???
    quit();   //退出bootloader程序,从0x0000处执行应用程序
}

guew 发表于 2013-5-30 20:16:07

问题解决了!~
原来是CVAVR编译器在编译“SPMCR = 0x01”时,也用到了r30寄存器(见图)。这样一来,本应用来给spm寻址的Z寄存器(r30,r31)中的地址就被破坏了,程序自然就不能正常运行了。

canspider 发表于 2013-5-30 20:17:41

楼主这是没保护好现场啊

guew 发表于 2013-5-30 20:20:51

本帖最后由 guew 于 2013-5-30 20:23 编辑

guew 发表于 2013-5-30 20:16 static/image/common/back.gif
问题解决了!~
原来是CVAVR编译器在编译“SPMCR = 0x01”时,也用到了r30寄存器(见图)。这样一来,本应用 ...

这是修改后的代码void page_fill(uint16 p_address,uint16 p_data)
{
    #asm("lds r30,$025b");   //将p_address的低八位放入Z寄存器的低八位
    #asm("lds r31,$025c");   //将p_address的高八位放入Z寄存器的高八位
    #asm("lds r0,$0259");    //将data的低八位放入r0寄存器
    #asm("lds r1,$025a");    //将data的高八位放入r1寄存器
    #asm("ldi r22,0x01");
    #asm("sts $57,r22");
    #asm("spm");    //加载页指令生效
}
页: [1]
查看完整版本: 我的bootloader,死活写不了程序