haso2007 发表于 2007-12-11 12:19:59

求教馬老師改自M128原理与使用指南中iap部分的m162 bootloader程序工作不正常.

改編自馬老師的用於m162的bootloader程序通訊正常但是下載方件總是無法完成,
源程序如下請高手幫忙看看那里出問題:
源程序
/*****************************************************
采用串行接口AVR Boot_load應用實例
華東師大電子系馬潮 2004.07
Compiler:    ICC-AVR 6.31
Target:    Mega162
Crystal:    4Mhz
Used:      T/C0,USART0
*****************************************************/
#include <iom162v.h>
#define SPM_PAGESIZE 128          //M162的一頁Flash為128字節(64字)
#define BAUD 19200                //波特率采用19200bps
#define CRYSTAL 4000000         //系用外部4MHz
//計算和定義M162的波特率設置
#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      //定義接收緩衝區的長度
//定義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'
//定義全局變量
const char startupString[]="3秒內按d入boot";
const char startloadstr[]="下載<16K bin";
char data;
unsigned int address = 0;
//擦除(code=0x03)和寫入(code=0x05)一個Flash頁
void boot_page_ew(unsigned int p_address,char code)
{while(SPMCR & 0x01);
    asm("movw r30,r16\n");         //地址放入Z寄存器
    SPMCR = code;               //寄存器SPMCSR中為操作碼
    asm("spm\n");                  //對指定Flash頁進行操作
}         
//填充Flash緩衝頁中的一個字
void boot_page_fill(unsigned int address,int data)
{while(SPMCR & 0x01);
    asm("movw r30,r16\n"              //Z寄存器中寫入地址值
           "movw r0,r18\n");                //R0,R1中寫入數據                                                                                
    SPMCR = 0x01;
    asm("spm\n");                          //寫到緩衝區中                                       
}
//等待一個Flash頁的寫完成
void wait_page_rw_ok(void)
{
      while(SPMCR & 0x40)
   {
         while(SPMCR & 0x01);           //等待SPMEN變為0
         SPMCR = 0x11;                           //重新使能RWW區                                  
         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+(data<<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)
            crc = crc << 1 ^ 0x1021;
      else
            crc = crc << 1;
      } while(--i);
    }
    return (crc);
}
//退出Bootloader程序,從0x0000執行應用程序
void quit(void)
{   
      uart_putchar('O');uart_putchar('K');
          uart_putchar(0x0d);uart_putchar(0x0a);
   while(!(UCSR0A & 0x20));      //等待結束提示信息回送完成
   GICR = 0x01;
   GICR = 0x00;                  //中斷向量表移到應用程序頂部
   asm("jmp 0x0000");            //跳轉到Flash的0x0000,執行應用程序
}
//主程序
void main(void)
{
    int i = 0;
    unsigned char timercount = 0;
    unsigned char packNO = 1;
    int bufferPoint = 0;
    unsigned int crc;
//初始化M162的USART0
    UBRR0H = BAUD_H;   
    UBRR0L = BAUD_L;            //Set baud rate
    UCSR0B = (1<<RXEN0)|(1<<TXEN0);                //Enable Receiver and Transmitter
    UCSR0C = (1<<URSEL0)|(1<<USBS0)|(1<<UCSZ01)|(1<<UCSZ00);            
//Set frame format: 8data, 2stop bit
//初始化M162的T/C0輸出比較匹配,15ms自動加載
OCR0 = 0xEA;
TCCR0 = 0x0C;                           //256分頻,WGM01(CTC0)=1                       
//向PC机發送開始提示信息
    while(startupString!='\0')
    {        
      uart_putchar(startupString);
      i++;
    }
                uart_putchar(0x0a);
                uart_putchar(0x0d);                       //撚行
//3秒种等待PC下發“d”,否則退出Bootloader程序,從0x0000執行應用程序
    while(1)
    {
      if(uart_getchar()=='d') break;
      if (TIFR & 0x01)                                  //timer0match
      {                                       
               if (++timercount > 200) quit();      //200*15ms = 3s
               TIFR = TIFR|0x01;                                             //寫1清零OCF0
      }
    }
                                       i=0;
                while(startloadstr!='\0')
                                                  {        
                                     uart_putchar(startloadstr);
                                     i++;
                                           }   
                uart_putchar(0x0a);
                uart_putchar(0x0d);                       //撚行                                       
    //每秒向PC机發送一個控制字符“C”,等待控制字〈soh〉
    while(uart_getchar()!=XMODEM_SOH)      //not receive the start of Xmodem
    {         if(TIFR&0x01)
           {                                                       
          if(++timercount > 67)                        //wait about 1 second
            {
                        uart_putchar(XMODEM_RECIEVING_WAIT_CHAR);
            timercount=0;
            }
            TIFR=TIFR | 0x01;
      }
    }
    //開始接收數据
    do
    {
      if ((packNO == uart_waitchar()) && (packNO ==(~uart_waitchar())))
      {    //核對數据塊編號是否正确
            for(i=0;i<128;i++)                //接收128個字節數据
            {
                data= uart_waitchar();
                bufferPoint++;   
            }
            crc = (uart_waitchar()<<8);
            crc += uart_waitchar();            //接收2個字節的CRC較驗字
            if(calcrc(&data,128)==crc)    //CRC校驗驗証
            {    //正确接收128個字節數据
                while(bufferPoint >= SPM_PAGESIZE)
                {    //正确接受128個字節的數据,也適用于256字節頁的情況
                  write_one_page();            //收到128字節寫入一個Flash頁中
                  address += SPM_PAGESIZE;    //Flash頁加1
                  bufferPoint = 0;                         //緩衝區計數器清零
                }   
                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執行應用程序
}

haso2007 发表于 2007-12-11 14:23:49

xmodem提示框如圖片取消後后原來的Boot程序有異常,應該有部分內容被下載但不正確請高手幫忙看看頁地址上有無錯誤
http://cache.amobbs.com/bbs_upload782111/files_8/ourdev_187410.gif

haso2007 发表于 2007-12-11 15:37:26

真是奇怪,將程序移植到mega16上運行ok,為什么在mega162上實現就不行呢.
有那位高手可以訴我162跟16的bootloader有什么不同

haso2007 发表于 2007-12-13 16:07:40

沒有人用過mega162嗎,我看了幾遍手冊,沒有發現meag16与mega162的RWW-bootloader有什么不同.但是怎樣就是實現不了下載.
用527的pc軟件與bootloader 程序也是 一樣通訊正常但是下載時出錯.

shaoziyang 发表于 2007-12-13 21:19:23

我在M162上使用Bootloader是正常的,你可以参考一下。使用GCC编译器和AVR通用bootloader

haso2007 发表于 2007-12-14 16:42:06

謝謝,請問有貼出來嗎網址在那里呢.
期待中-----

machao 发表于 2007-12-15 13:12:21

我手头没有M162,不能做测试,只能提参考.

1.M162同M16还是有区别的,寄存器的地址,名称都不同.因此请仔细检查程序,注意使用M162模式重新编译代码.
2.检查M162的溶丝,BLB0,BLB1的模式,应该都为模式1
3.如果M162使用内部RC,考虑将BPS降低,保证RS232的大数据量通信可靠性.

shaoziyang 发表于 2007-12-15 17:14:10

AVR通用Bootloader的网址:
http://shaoziyang.bloger.com.cn/user2/88141/avrboot.html 或者
http://shaoziyang.googlepages.com 或者
http://www.avrfreaks.net/index.php?module=Freaks%20Academy&func=viewItem&item_type=project&item_id=929

网友stephon1写的使用教程:
http://www.ourdev.cn/bbs/bbs_content.jsp?bbs_sn=853847&bbs_page_no=1&bbs_id=9999

一篇国外爱好者写的英文教程:
http://www.scienceprog.com/testing-avr-universal-bootloader-on-atmega128/

使用162时需要修改相关配置即可,使用前先看看readme文件和上面的教程。

haso2007 发表于 2007-12-16 19:11:04

非常感激前辈的耐心指点,


学习-发现问题-交流-再学习=不断进步

haso2007 发表于 2007-12-25 16:04:15

今天終於有空又做了一個GCC版本的162 bootloader程序通訊和顯示的下載程序都說是OK的可是就是不會跳至應用程序
(或者程序沒有真正被下載)
把源程序貼出來,各位前輩幫忙看一下,問題出在那里.
/*本BOOTLADER程序跟据OURAVR网的BOOTLOADER范例修改,原來M16改為M162使用,
設定外部晶体4MHz,F_CPU=4000000 使用USART,19200bps   
熔絲位設定: BOOTRST = 0 (复位地址為BOOT區) ,
            BOOTSZ1 = 0 ,BOOTSZ0 = 1(BOOT字節大小為512字)
makefile .text首地址改為0x3800
*/

#include <avr/io.h>
#include <util/delay.h>
#include <avr/boot.h>
#include <avr/crc16.h>
//移植至m162
#define UCSRA        UCSR0A
#define UCSRB         UCSR0B
#define UCSRC        UCSR0C
#define UBRRL        UBRR0L
#define UBRRH        UBRR0H
#define        UDR                UDR0

//串口腳設定
#define PIN_RXD       0 //PD0
#define PIN_TXD       1 //PD1

//常量定義
//#define SPM_PAGESIZE    128       //M8的一個Flash頁為64字節(32字)
#define DATA_BUFFER_SIZESPM_PAGESIZE//定義接收字節長度
#define BAUDRATE      19200      //波特率采用19200
//#define F_CPU       4000000    //系統晶振7.3728MHz

//定義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_WAIT_CHAR'C'

//定義全局變量
struct str_XMODEM
{
    unsigned char SOH;                    //起始字節
    unsigned char BlockNo;          //數据塊編號
    unsigned char nBlockNo;         //數據塊編號反碼
    unsigned char Xdata;              //數据128字節
    unsigned char CRC16hi;          //CRC16校驗資料高位
    unsigned char CRC16lo;          //CRC16校驗資料低位
}
strXMODEM;                                  //XMODEM的接收數据結构体

unsigned long FlashAddress;         //FLASH地址
#defineBootAdd      0x3800              //Boot區的首地址(應用區的最高字地址)
/*GCC里面地址使用32位(4字節)長度,适用所有AVR的容量*/

unsigned char BlockCount;                 //數据塊累計(只8位,不考虙溢出)

unsigned char STATUS;                 //運行狀態
#define ST_WAIT_START   0x00      //等待啟動
#define ST_BLOCK_OK       0x01      
#define ST_BLOCK_FAIL   0x02      //接收一個數据塊失敗
#define ST_OK             0x03      //完成

void(*reset)(void)=0x0000;
//長延時max 65536ms
void delay_ms(unsigned int t)
{
    while(t--)
    {
      _delay_ms(1);
    }
}

//更新一個Flash頁的完整處理
void write_one_page(unsigned char buf_start)
{
    unsigned char i;
    unsigned char *buf;
    unsigned intw;
    boot_page_erase(FlashAddress);      //擦除一個Flash頁
    boot_spm_busy_wait();                    //等待擦除完成
    buf=&strXMODEM.Xdata;
    for(i=0;i<SPM_PAGESIZE;i+=2)      //數据填入Flash緩沖中
    {
      w =*buf++;
      w+=(*buf++)<<8;
      boot_page_fill(i, w);                         //只是低7位(128字節/頁)有效
    }
    boot_page_write(FlashAddress);      //將緩衝頁數据寫入一個Flash中
    boot_spm_busy_wait();                    //等待寫過程完成
}

//傳送采用查詢方式
void put_c(unsigned char c) //發送采用查詢方式
{
    loop_until_bit_is_set(UCSRA,UDRE0);
    UDR=c;
}

//傳送字符串
void put_s(unsigned char *ptr)
{
    while (*ptr)
    {
      put_c(*ptr++);
    }
    put_c(0x0D);
    put_c(0x0A);//結尾傳送回車行
}


//接收指定字節數据(帶超時控制,Timer0的1ms時基)
//*ptr    數据緩衝區
//len   數据長度
//timeout   超時設定,最長65.536S
//返回值    已接收字節數目
unsigned char get_data(unsigned char *ptr,unsigned char len,unsigned int timeout)
{
    unsigned count=0;
    do
    {
      if (UCSRA & (1<<RXC0))
      {
            *ptr++=UDR;       //如果接收到數据,讀出
            count++;
            if (count>=len)
            {
                break;      //如夠了則退出
            }
      }
      if(TIFR & (1<<OCF2))    //T2溢出 1ms
      {
            TIFR|=(1<<OCF2);    //清除標志位
            timeout--;      //倒計時
      }
    }
    while (timeout);
    return count;
}

//計算CRC16
unsigned int calcrc(unsigned char *ptr, unsigned char count)
{
    unsigned int crc = 0;
    while (count--)
    {
      crc =_crc_xmodem_update(crc,*ptr++);
    }
    return crc;
}

//主程序
int main(void)
{
    unsigned char c;
    unsigned char i;
    unsigned int crc;
//考虙到BootLoader可能由應用程序中跳回,所以所用到的模塊需要全面初始化
    DDRB=0x00;
    DDRC=0x00;                     //不用的管腳使能內部上拉電阻。
    PORTB=0xFF;
    PORTC=0xFF;
        DDRE=0x00;
        PORTE=0xFF;
    PORTD=0xFF;
    DDRD=(1<<PIN_TXD);            //串口的輸出
    GICR = (1<<IVCE);
    GICR = (0<<IVCE)|(1<<IVSEL);      //中斷向量表轉移到Boot頂部
        asm volatile("cli"::);            //關全局中斷
    //初始化USART 19200 8, n,1PC上位机軟件(超級終端)對等設置
    UCSRC = (1<<URSEL0)|(1<<UCSZ01)|(1<<UCSZ00); //异步,8位數据+1停止位,1倍速
    UBRRL = (F_CPU/BAUDRATE/16-1)%256;      //設定波特率
    UBRRH = (F_CPU/BAUDRATE/16-1)/256;
    UCSRA = 0x00;
    UCSRB = (1<<RXEN0)|(1<<TXEN0);      //使能接收,使能傳送
    //初始化T/C2,CTC模式,256分分頻,1ms自動重啟
    TCNT2 = 0xf0;
    OCR2 = 0x0f;
    TCCR2 = (1<<WGM21)|(1<<CS22)|(1<<CS21)|(0<<CS20);

    //向PC机傳送開始提示信息
    put_s("3秒內按下d開始更新");
    c=0;
    get_data(&c,1,3000);            //限時3秒,接收一個數据
    if ((c=='d')||(c=='D'))
    {
      STATUS=ST_WAIT_START;         //并且數据='d'或'D',進入XMODEM
      put_s("BIN檔最大6KB");
    }
    else
    {
      STATUS=ST_OK;             //退出Bootloader程序
    }

    //進入XMODEM模式
    FlashAddress=0x0000;
    BlockCount=0x01;
    while(STATUS!=ST_OK)            //循環接收,直到全部收完
    {
      if (STATUS==ST_WAIT_START)
      {//XMODEM未啟動
            put_c(XMODEM_WAIT_CHAR);      //發送請求XMODEM_WAIT_CHAR
      }
      i=get_data(&strXMODEM.SOH,133,1000);//限時1秒,接收133字節數据
      if(i)
      {
            //分析數据包的第一個資料 SOH/EOT/CAN
            switch(strXMODEM.SOH)
            {
            case XMODEM_SOH:          //收到開始符SOH
                if (i>=133)
                {
                  STATUS=ST_BLOCK_OK;
                }
                else
                {
                  STATUS=ST_BLOCK_FAIL;   //如果數据不足,要求重發當前數据塊
                  put_c(XMODEM_NAK);
                }
                break;
            case XMODEM_EOT:          //收到結束符EOT
                put_c(XMODEM_ACK);      //通知PC机全部收到
                STATUS=ST_OK;
                put_s("升級OK");
                break;
            case XMODEM_CAN:          //收到取消符CAN
                put_c(XMODEM_ACK);      //回應PC机
                STATUS=ST_OK;
                put_s("升級fail");
                break;
            default:            //起始字節
                put_c(XMODEM_NAK);      //要求重發當前數据塊
                STATUS=ST_BLOCK_FAIL;
                break;
            }
      }
      if (STATUS==ST_BLOCK_OK)      //接收133字節OK,且起始字節正确
      {
            if (BlockCount!=strXMODEM.BlockNo)//核對數据塊編號及其反碼是否正确
            {
                put_c(XMODEM_NAK);      //否則重發
                continue;
            }
            if (BlockCount !=(unsigned char)(~strXMODEM.nBlockNo))
            {
                put_c(XMODEM_NAK);         
                continue;
            }
            crc=strXMODEM.CRC16hi<<8;
            crc+=strXMODEM.CRC16lo;
            //AVR的16位整數是低位在先,XMODEM的CRC16是高位在先
            if(calcrc(&strXMODEM.Xdata,128)!=crc)
            {
                put_c(XMODEM_NAK);            //CRC出錯,要求重發
                continue;
            }
            //正确接收128個字節數据,剛好是M162的一頁
            if (FlashAddress<(BootAdd-SPM_PAGESIZE))
            {                                               //如果地址在應用區內
                write_one_page(0);             //接收到0-128字節寫入一個Flash中
                FlashAddress+=SPM_PAGESIZE;   //Flash頁加1
               
            }
            else
            {
                put_c(XMODEM_CAN);          //程序已滿,取消發送
                put_c(XMODEM_CAN);
                put_c(XMODEM_CAN);
                STATUS=ST_OK;
                put_s("空間滿");
                break;
            }
            put_c(XMODEM_ACK);            //回應已正确收到一個數據塊
            BlockCount++;                     //數据塊累計加1
      }
    }

    //退出Bootloader程序,從0x0000執行應用程序
    put_s("GO");
    delay_ms(500);               
    loop_until_bit_is_set(UCSRA,UDRE0);      //等待結束提示信息回送完成
    GICR = (1<<IVCE);
    GICR = (0<<IVCE)|(0<<IVSEL);      //將中斷向量表移到應用程序頂部
    /*無論boot程序有無用到中斷,都將向量表移到應用區,以增強程序的鍵壯性*/
        boot_rww_enable();             //RWW區重開允許,否則無法馬上執行應用區程序
    reset();
        //asm volatile("rjmp 0x0000"::);          //跳到Flash的0x0000,執行用戶的應用程序
}

machao 发表于 2007-12-25 16:37:44

生成的BT执行代码是多少字,不要超出512字.

haso2007 发表于 2007-12-26 09:18:11

回馬老師,這個程序生成代碼有1200byte左右,需要用1K字的boot空間.即boot區首地址為0x1c00,因為程序里面有太多的說明字符串,
在makefile加上了:LDFLAGS+=-WI,--section-start=.text=0x3800,不知道是否有誤?

machao 发表于 2007-12-26 16:50:28

熔絲位設定: BOOTRST = 0 (复位地址為BOOT區) ,
            BOOTSZ1 = 0 ,BOOTSZ0 = 1(BOOT字節大小為512字)
==============================================================
以上是你在9楼的说明,我没仔细查过.而在11楼你讲代码有1200byte,那么512K字是放不下的,只能放1024byte.后面的代码没有了,返回主程序的指令应该在后面,所以不能转移的用户区了.

请检查是否此原因造成的.

haso2007 发表于 2008-1-24 22:40:57

终于用 shaoziyang 前辈的通用GCC bootloader 搞成功mega162的bootloader,
但是还是不明白,mega16 与mega162的bootloader有何区别,
怀疑是162的USART通讯的问题。
页: [1]
查看完整版本: 求教馬老師改自M128原理与使用指南中iap部分的m162 bootloader程序工作不正常.