pic24bootloader

来源:互联网 发布:海王星写频软件 编辑:程序博客网 时间:2024/06/05 20:17
<pre name="code" class="cpp">//PIC24系列的串行自举(Bootloader)代码设计如下://文件名:boot.c,该文件调用memory.c和C30编译器系统配置文件config.h。//本Boot代码兼容AN851通信协议,是在PIC24F和PIC16/18 的AN851基础上开发出来//的最新版本。#include "PIC24F Serial Bootloader\config.h"//全局变量WORD responseBytes;        //命令响应的字节个数DWORD_VAL sourceAddr;    //通用变量表DWORD_VAL userReset;    //用户代码复位向量DWORD_VAL userTimeout; //进入Boot的溢出时间WORD userResetRead;        //缓冲池,用于重定位用户复位向量//变量#ifdef USE_RUNAWAY_PROTECTvolatile WORD writeKey1 = 0xFFFF;volatile WORD writeKey2 = 0x5555;volatile WORD keyTest1 = 0x0000;volatile WORD keyTest2 = 0xAAAA;#endif//发送/接收缓存BYTE buffer[MAX_PACKET_SIZE+1];//配置位的设置,相关设置可以根据用户需求进行修改。#ifndef DEV_HAS_CONFIG_BITS#ifdef DEV_HAS_USB_CONFIG1(JTAGEN_OFF & GWRP_OFF & ICS_PGx2 & FWDTEN_OFF)_CONFIG2(POSCMOD_HS & FNOSC_PRIPLL & PLLDIV_DIV2 & PLL_96MHZ_ON)#else_CONFIG1(JTAGEN_OFF & GWRP_OFF & ICS_PGx2 & FWDTEN_OFF)_CONFIG2(POSCMOD_HS & FNOSC_PRIPLL)#endif#else_FOSCSEL(FNOSC_PRIPLL)_FOSC(POSCFREQ_MS & POSCMOD_XT)_FWDT(FWDTEN_OFF)_FICD(ICS_PGx3)#endif//函数名int main(),使能32位定时器2/3,使能UART。初始化主程序和主循环。int main(){    DWORD_VAL delay;    //配置Boot的入口延时    sourceAddr.Val = DELAY_TIME_ADDR;    //Boot定时器的地址    delay.Val = ReadLatch(sourceAddr.word.HW, sourceAddr.word.LW); //读BL溢出时间    //配置用户复位向量    sourceAddr.Val = USER_PROG_RESET;    userReset.Val = ReadLatch(sourceAddr.word.HW, sourceAddr.word.LW);    //为防止Boot出错,如果不使用用户复位向量,则复位到Bool开始执行。    if(userReset.Val == 0xFFFFFF)    {        userReset.Val = BOOT_ADDR_LOW;    }    userResetRead = 0;    //如果溢出数值为0,则判断复位状态。    if(delay.v[0] == 0)    {        //如果芯片从复位状态返回,则Boot是关闭的,系统直接调用用户代码。//在其他状态下,将直接进入Boot或者从用户代码调用Boot。        //otherwise assume the BL was called from use code and enter BL        if(RCON & 0xFED3)        {            //如果Boot关闭,则系统跳转到用户代码区。            ResetDevice(userReset.Val);        }        else        {            delay.Val = 0xFF;        }    }    T2CONbits.TON = 0;    T2CONbits.T32 = 1; //配置定时器2/3作为32位定时器,每个时钟周期加1    IFS0bits.T3IF = 0;  //清除定时器3中断标志    IEC0bits.T3IE = 0;  //关闭定时器3中断    if((delay.Val & 0x000000FF) != 0xFF)      //将分钟转换成定时器计数值    {        delay.Val = ((DWORD)(FCY)) * ((DWORD)(delay.v[0]));        PR3 = delay.word.HW;              //设置定时器的溢出数值        PR2 = delay.word.LW;        TMR2 = 0;        TMR3 = 0;        T2CONbits.TON=1;                //使能定时器    }#ifdef DEV_HAS_PPS                  //如果使用PPS部分,影射UART的I/O口    ioMap();#endif#ifdef UTX_ANA                      //配置 UART 引脚作为数字I/O.    UTX_ANA = 1;#endif#ifdef URX_ANA    URX_ANA = 1;#endif    // 配置UART串行口:无奇偶效验位,1位停止位,自适应波特率,轮询模式。    UxMODEbits.UARTEN = 1;                //使能 uart#ifdef USE_AUTOBAUD    UxMODEbits.ABAUD = 1;            //使用自适应波特率#else    UxBRG = BAUDRATEREG;#endif#ifdef USE_HI_SPEED_BRG    UxMODEbits.BRGH = 1;            //使用高速模式#endif    UxSTA = 0x0400;                     //使能发送端TX    while(1)    {#ifdef USE_RUNAWAY_PROTECT        writeKey1 = 0xFFFF;        //修改关键参数确保正确的编程流程            writeKey2 = 0x5555;#endif        GetCommand();               //从UART口获取命令#ifdef USE_RUNAWAY_PROTECT        writeKey1 += 10;           //修改关键参数确保正确的编程流程        writeKey2 += 42;#endif        HandleCommand();             //处理命令        PutResponse(responseBytes);       //响应命令    }//结束 while(1)}//结束 main(void)//函数名:void GetCommand(),用于轮询UART口,接收命令,接收缓冲区是buffer[1024]。void GetCommand(){    BYTE RXByte;    BYTE checksum;    WORD dataCount;    while(1)    {#ifndef USE_AUTOBAUD        GetChar(&RXByte);   //获取第一个STX        if(RXByte == STX)        {#else        AutoBaud();            //根据第一个STX计算波特率大小        RXByte = UxRXREG;    //冗余读#endif            T2CONbits.TON = 0;  //关闭定时器,该定时器用于数据接收            GetChar(&RXByte);    //读2个字节            if(RXByte == STX)     //2 STX, 数据部分            {                checksum = 0;      //数据效验和清零                dataCount = 0;                while(dataCount <= MAX_PACKET_SIZE+1)     //最大的接收字节数                {                    GetChar(&RXByte);                    switch(RXByte)                    {                    case STX:                     //传输开始 STX                            checksum = 0;                        dataCount = 0;                        break;                    case ETX:                    //传输结束ETX                            checksum = ~checksum +1;  //测试效验和                        Nop();                        if(checksum == 0) return;    //如果效验和正确,系统返回                        dataCount = 0xFFFF;        //否则,重新启动                        break;                    case DLE:                    //如果DLE, 获取下一个数据                            GetChar(&RXByte);                    default:                        //获取数据放入缓存器中                            checksum += RXByte;                        buffer[dataCount++] = RXByte;                        break;                    }//结束 switch(RXByte)                }//结束 while(byteCount <= 1024)            }//结束 if(RXByte == STX)#ifndef USE_AUTOBAUD        }//结束 if(RXByte == STX)#endif    }//结束 while(1)}//结束 GetCommand()//函数名:void HandleCommand(),用于处理冲主机接收到的命令。void HandleCommand(){    BYTE Command;    BYTE length;    //在EE和在配置字读/写中使用的变量#if (defined(DEV_HAS_EEPROM) || defined(DEV_HAS_CONFIG_BITS))    WORD i=0;    WORD_VAL temp;    WORD bytesRead = 0;#endif    Command = buffer[0];              //从缓冲区获得命令    length = buffer[1];                  //从缓冲区获得数据的长度    if(length == 0x00)               //复位命令    {        UxMODEbits.UARTEN = 0;  //关闭UART        ResetDevice(userReset.Val);    }    //从缓冲区获得24位的地址    sourceAddr.v[0] = buffer[2];    sourceAddr.v[1] = buffer[3];    sourceAddr.v[2] = buffer[4];    sourceAddr.v[3] = 0;#ifdef USE_RUNAWAY_PROTECT    writeKey1 |= (WORD)sourceAddr.Val;        //修改关键参数确保程序流程的正确执行    writeKey2 =  writeKey2 << 1;#endif    //处理命令    switch(Command)    {    case RD_VER:                    //读版本号            buffer[2] = MINOR_VERSION;        buffer[3] = MAJOR_VERSION;        responseBytes = 4; //set length of reply        break;    case RD_FLASH:                //读Flash存储区            ReadPM(length, sourceAddr);        responseBytes = length*PM_INSTR_SIZE + 5; //设置接收的长度                           break;    case WT_FLASH:                  //写Flash存储区#ifdef USE_RUNAWAY_PROTECT            writeKey1 -= length;        //修改关键参数确保程序流程的正确执行        writeKey2 += Command;#endif        WritePM(length, sourceAddr);        responseBytes = 1;         //设置响应命令的长度        break;    case ER_FLASH:                //擦除Flash存储区#ifdef USE_RUNAWAY_PROTECT            writeKey1 += length;    //修改关键参数确保程序流程的正确执行        writeKey2 -= Command;#endif        ErasePM(length, sourceAddr);        responseBytes = 1;        //设置响应命令的长度        break;#ifdef DEV_HAS_EEPROM    case RD_EEDATA:            //读EEPROM            //如果芯片有板上EEPROM, 则允许读EE            //读EEPROM的字节长度            while(i < length*2)            {                temp.Val = ReadLatch(sourceAddr.word.HW,                                     sourceAddr.word.LW);                buffer[5+i++] = temp.v[0];                buffer[5+i++] = temp.v[1];                sourceAddr.Val += 2;            }        responseBytes = length*2 + 5; //设置响应命令的长度        break;    case WT_EEDATA:             //写EEPROM            //写EEPROM字节的长度            while(i < length*2)            {                temp.byte.LB = buffer[5+i++];  //装载写入的数据                temp.byte.HB = buffer[5+i++];                WriteLatch(sourceAddr.word.HW,sourceAddr.word.LW,                           0, temp.Val);     //写入数据到寄存器中#ifdef USE_RUNAWAY_PROTECT                writeKey1++;                writeKey2--;                //配置应用程序保护测试键值                keyTest1 =(((0x0009 | (WORD)(sourceAddr.Val-i)) -                            length) + i/2) - 5;                keyTest2 = (((0x557F << 1) + WT_EEDATA) - i/2) + 6;                //初始化写序列                WriteMem(EE_WORD_WRITE);                writeKey1 += 5; //修改关键参数确保程序流程的正确执行                writeKey2 -= 6;#else                //初始化写入序列                WriteMem(EE_WORD_WRITE);#endif                sourceAddr.Val +=2;            }        responseBytes = 1;                      //设置响应命令的长度        break;#endif#ifdef DEV_HAS_CONFIG_BITS    case RD_CONFIG:                          //读存储区的配置字            while(bytesRead < length)                //读配置存储器中的字节长度            {                //read flash                temp.Val = ReadLatch(sourceAddr.word.HW, sourceAddr.word.LW);                buffer[bytesRead+5] = temp.v[0];       //将读出的数据放入缓存                bytesRead++;                sourceAddr.Val += 2;               //地址数值自动加2            }        responseBytes = length + 5;        break;    case WT_CONFIG:                        //写存储器配置            while(i < length)                       //写配置字字节长度            {                temp.byte.LB = buffer[5+i++];       //装载要写入的数据                temp.byte.HB = 0;#ifdef USE_RUNAWAY_PROTECT                writeKey1++;                writeKey2--;#endif       //确保配置字写入是在内部配置存储空间执行                if(sourceAddr.Val >= CONFIG_START &&                        sourceAddr.Val <= CONFIG_END)                {                    TBLPAG = sourceAddr.byte.UB;                    __builtin_tblwtl(sourceAddr.word.LW,temp.Val);#ifdef USE_RUNAWAY_PROTECT                    //设置程序流程,保护测试键值                    keyTest1 =(((0x0009 | (WORD)(sourceAddr.Val-i*2)) -                                length) + i) - 5;                    keyTest2 = (((0x557F << 1) + WT_CONFIG) - i) + 6;                    //初始化写入序列                    WriteMem(CONFIG_WORD_WRITE);                    writeKey1 += 5;  //修改键值确保程序正确的执行                    writeKey2 -= 6;#else                    //初始化写入序列                    WriteMem(CONFIG_WORD_WRITE);#endif                }//结束 if(sourceAddr.Val...)                sourceAddr.Val +=2;            }//结束 while(i < length)        responseBytes = 1;            //设置响应命令的长度        break;#endif    case VERIFY_OK:#ifdef USE_RUNAWAY_PROTECT            writeKey1 -= 1;           //修改键值确保程序正确的执行        writeKey2 += Command;#endif        WriteTimeout();        responseBytes = 1;           //设置响应命令的长度        break;    default:            break;    }// 结束 switch(Command)}//结束 HandleCommand()//函数名:void PutResponse(),用于配置UART,通过UART发送数据缓冲器中的数据响应//字节,表示接收到数据。void PutResponse(WORD responseLen){    WORD i;    BYTE data;    BYTE checksum;    UxSTAbits.UTXEN = 1;        //确保 TX使能    PutChar(STX);            //发送2个STX字节    PutChar(STX);    //输出缓冲作为响应数据报文    checksum = 0;    for(i = 0; i < responseLen; i++)    {        asm("clrwdt");        //清看门狗WDT        data = buffer[i];    //从响应的数据缓存区中获得数据        checksum += data;    //计算效验和        if(data == STX || data == ETX || data == DLE)        {            PutChar(DLE);        }        PutChar(data);      //发送数据    }    checksum = ~checksum + 1;    if(checksum == STX || checksum == ETX || checksum == DLE)    {        PutChar(DLE);    }    PutChar(checksum);        //发送效验和    PutChar(ETX);            //发送结束字符    while(!UxSTAbits.TRMT);    //等待发送过程完成}//结束 PutResponse()//函数名:void PutChar(BYTE Char),用于UART配置,参数Char是用于发送的字节。该函//数是通过UART2发送一个字节,等待TXREG的FIFO缓存区为空。void PutChar(BYTE txChar){    while(UxSTAbits.UTXBF);    //等待FIFO缓存区为空    UxTXREG = txChar;        //发送一个字节给 UART的FIFO缓存区,用于发送数据}//结束 PutChar(BYTE Char)//函数名:void GetChar(BYTE * ptrChar),用于UART配置,参数ptrChar是指向接收字符的//指针,该函数首先清除看门狗WDT,然后在UART2上接收一个字符。void GetChar(BYTE * ptrChar){    BYTE dummy;    while(1)    {        asm("clrwdt");                     //主循环,首先清看门狗 WDT        if((UxSTA & 0x000E) != 0x0000)  //判断接收是否出错        {            dummy = UxRXREG;         //冗余的读操作,用于清除FERR/PERR错误标志            UxSTAbits.OERR = 0;        //清除溢出标志OERR,保持连续接收模式        }        //获取数据        if(UxSTAbits.URXDA == 1)        {            * ptrChar = UxRXREG;        //从UART RX接收FIFO缓存器中接收数据            break;        }#ifndef USE_AUTOBAUD        //如果超时,跳出用户代码        if(IFS0bits.T3IF == 1)        {            ResetDevice(userReset.Val);        }#endif    }//结束 while(1)}//结束 GetChar(BYTE *ptrChar)//函数名:void ReadPM(WORD length, DWORD_VAL sourceAddr),参数length    表示读指令//的个数,参数sourceAddr表示源地址。该函数用于从程序存储器中读数据,然后保存数据//到缓冲器中。void ReadPM(WORD length, DWORD_VAL sourceAddr){    WORD bytesRead = 0;    DWORD_VAL temp;    //从Flash中读指令的长度    while(bytesRead < length*PM_INSTR_SIZE)    {        //读Flash        temp.Val = ReadLatch(sourceAddr.word.HW, sourceAddr.word.LW);        buffer[bytesRead+5] = temp.v[0];     //将读到的数据放到响应缓冲区中        buffer[bytesRead+6] = temp.v[1];        buffer[bytesRead+7] = temp.v[2];        buffer[bytesRead+8] = temp.v[3];        //每条指令4个字节        bytesRead+=PM_INSTR_SIZE;        sourceAddr.Val = sourceAddr.Val + 2;  //地址字节每次加2    }//结束 while(bytesRead < length*PM_INSTR_SIZE)}//结束 ReadPM(WORD length, DWORD_VAL sourceAddr)//函数名:void WritePM(WORD length, DWORD_VAL sourceAddr),参数length表示写入行//个数,参数sourceAddr表示写入的地址。该函数将行信息从缓冲器写入到Flash存储器中。void WritePM(WORD length, DWORD_VAL sourceAddr){    WORD bytesWritten;    DWORD_VAL data;#ifdef USE_RUNAWAY_PROTECT    WORD temp = (WORD)sourceAddr.Val;#endif    bytesWritten = 0;//开始的5个字节是1个命令字节(cmd),2个长度字节(len),2个地址字节(addr)    //写入列长度到Flash中    while((bytesWritten) < length*PM_ROW_SIZE)    {        asm("clrwdt");        //从缓出区中获得数据,并将数据写入        data.v[0] = buffer[bytesWritten+5];        data.v[1] = buffer[bytesWritten+6];        data.v[2] = buffer[bytesWritten+7];        data.v[3] = buffer[bytesWritten+8];        //每条指令4个字节        bytesWritten+=PM_INSTR_SIZE;        //Flash 配置字处理#ifndef DEV_HAS_CONFIG_BITS        if(sourceAddr.Val == CONFIG_END)        {            data.Val &= 0x007FFF;        }#endif        //保护Boot以及复位向量#ifdef USE_BOOT_PROTECT        //保护Boot复位地址以及或者用户程序复位地址        if(sourceAddr.Val == 0x0)        {            //获得用户应用程序复位地址低字节            userReset.Val = data.Val & 0xFFFF;            //获得Boot复位地址低字节            data.Val = 0x040000 + (0xFFFF & BOOT_ADDR_LOW);            userResetRead = 1;        }        if(sourceAddr.Val == 0x2)        {            //获得用户应用程序复位地址高字节            userReset.Val += (DWORD)(data.Val & 0x00FF)<<16;            //获得Boot复位地址高字节            data.Val = ((DWORD)(BOOT_ADDR_LOW & 0xFF0000))>>16;            userResetRead = 1;        }#else        //获得用户应用程序复位地址低字节        if(sourceAddr.Val == 0x0)        {            userReset.Val = data.Val & 0xFFFF;            userResetRead = 1;        }        //获得用户应用程序复位地址高字节        if(sourceAddr.Val == 0x2)        {            userReset.Val |= ((DWORD)(data.Val & 0x00FF))<<16;            userResetRead = 1;        }#endif        //在用户复位向量中从复位向量发送信息        if(sourceAddr.Val == USER_PROG_RESET)        {            if(userResetRead)   //是否从地址0x0获得复位向量吗?            {                //如果是,使用该数值作为复位向量                data.Val = userReset.Val;            }            else            {                //如果不是,是用用户数值作为复位向量                userReset.Val = data.Val;            }        }        if(sourceAddr.Val == DELAY_TIME_ADDR)        {            userTimeout.Val = data.Val;            data.Val = 0xFFFFFF;        }#ifdef USE_BOOT_PROTECT            //不擦除Boot和复位向量            if(sourceAddr.Val < BOOT_ADDR_LOW || sourceAddr.Val > BOOT_ADDR_HI)        {#endif#ifdef USE_CONFIGWORD_PROTECT    //不擦除最后一页            if(sourceAddr.Val < (CONFIG_START & 0xFFFC00))            {#endif#ifdef USE_VECTOR_PROTECT            //不擦除第一页                //if(sourceAddr.Val >= PM_PAGE_SIZE/2){                if(sourceAddr.Val >= VECTOR_SECTION)                {#endif                    WriteLatch(sourceAddr.word.HW, sourceAddr.word.LW,                               data.word.HW, data.word.LW);#ifdef USE_VECTOR_PROTECT                }//向量保护函数结束#endif#ifdef USE_CONFIGWORD_PROTECT            }//配置保护结束#endif#ifdef USE_BOOT_PROTECT        }//Boot保护结束#endif#ifdef USE_RUNAWAY_PROTECT        writeKey1 += 4;                  //修改键值确保程序正确的执行        writeKey2 -= 4;#endif        //如果行完成,写入数据到Flash存储器中        if((bytesWritten % PM_ROW_SIZE) == 0)        {#ifdef USE_RUNAWAY_PROTECT            keyTest1 =  (0x0009 | temp) - length + bytesWritten - 5;            keyTest2 =  (((0x557F << 1) + WT_FLASH) - bytesWritten) + 6;#endif#ifdef USE_BOOT_PROTECT     //保护Boot和复位向量            if((sourceAddr.Val < BOOT_ADDR_LOW || sourceAddr.Val > BOOT_ADDR_HI))            {#endif#ifdef USE_CONFIGWORD_PROTECT    //不擦除最后一页                if(sourceAddr.Val < (CONFIG_START & 0xFFFC00))                {#endif#ifdef USE_VECTOR_PROTECT           //不擦除第一页                    if(sourceAddr.Val >= VECTOR_SECTION)                    {#endif                        //执行写入过程                        WriteMem(PM_ROW_WRITE);#ifdef USE_RUNAWAY_PROTECT                        writeKey1 += 5; //修改关键参数确保程序正常执行                        writeKey2 -= 6;#endif#ifdef USE_VECTOR_PROTECT                    }//结束向量保护#endif#ifdef USE_CONFIGWORD_PROTECT                }//结束配置保护#endif#ifdef USE_BOOT_PROTECT            }//结束Boot保护#endif        }        sourceAddr.Val = sourceAddr.Val + 2;  //地址变量加2    } //结束 while((bytesWritten-5) < length*PM_ROW_SIZE)}//结束 WritePM(WORD length, DWORD_VAL sourceAddr)//函数名:void ErasePM(WORD length, DWORD_VAL sourceAddr),参数length表示擦除页//的个数,参数sourceAddr表示擦除页的地址,该函数用于从Flash存储器中擦除页。void ErasePM(WORD length, DWORD_VAL sourceAddr){    WORD i=0;#ifdef USE_RUNAWAY_PROTECT    WORD temp = (WORD)sourceAddr.Val;#endif    while(i<length)    {        i++;#ifdef USE_RUNAWAY_PROTECT        writeKey1++;    //修改关键参数确保程序正常执行        writeKey2--;#endif          //如果保护使能, 将保护Boot和复位向量#ifdef USE_BOOT_PROTECT        if(sourceAddr.Val < BOOT_ADDR_LOW ||       //不擦除Boot                sourceAddr.Val > BOOT_ADDR_HI)        {#endif#ifdef USE_CONFIGWORD_PROTECT          //不擦除最后一页            if(sourceAddr.Val < (CONFIG_START & 0xFFFC00))            {#endif#ifdef USE_VECTOR_PROTECT                  //不擦除第一页                if(sourceAddr.Val >= VECTOR_SECTION)                {#endif#ifdef USE_RUNAWAY_PROTECT                    //setup program flow protection test keys                    keyTest1 = (0x0009 | temp) + length + i + 7;                    keyTest2 = (0x557F << 1) - ER_FLASH - i + 3;#endif                    //执行擦除                    Erase(sourceAddr.word.HW, sourceAddr.word.LW, PM_PAGE_ERASE);#ifdef USE_RUNAWAY_PROTECT                    writeKey1 -= 7;    //修改关键参数确保正确的编程流程                    writeKey2 -= 3;#endif#ifdef USE_VECTOR_PROTECT                }#elif  defined(USE_BOOT_PROTECT) || defined(USE_RESET_SAVE)                    //Boot 复位向量区                    DWORD_VAL blResetAddr;                    if(sourceAddr.Val < PM_PAGE_SIZE/2)                    {                        //如果擦除,Boot复位向量区在0x00和0x02中                        blResetAddr.Val = 0;#ifdef USE_RUNAWAY_PROTECT                        //修改关键参数确保正确的编程流程                        keyTest1 = (0x0009 | temp) + length + i;                        keyTest2 = (0x557F << 1) - ER_FLASH - i;#endif                        replaceBLReset(blResetAddr);                    }#endif#ifdef USE_CONFIGWORD_PROTECT            }   //配置保护结束#endif#ifdef USE_BOOT_PROTECT        }   //Boot保护结束#endif        sourceAddr.Val += PM_PAGE_SIZE/2;    //通过页增加    }//end while(i<length)}//ErasePM函数结束//函数名:void WriteTimeout(),编程的数据在调用前需要首先效验。该函数用于将数据通过//Boot写入到存储器中。该函数需要在成功效验后才能被编程写入到数据存储器中。void WriteTimeout(){#ifdef USE_RUNAWAY_PROTECT    WORD temp = (WORD)sourceAddr.Val;    //将溢出数值写入到存储区中#endif#ifdef DEV_HAS_WORD_WRITE        //写入数据    WriteLatch((DELAY_TIME_ADDR & 0xFF0000)>>16,               (DELAY_TIME_ADDR & 0x00FFFF), userTimeout.word.HW, userTimeout.word.LW);#else    DWORD_VAL address;    WORD bytesWritten;    bytesWritten = 0;    address.Val = DELAY_TIME_ADDR & (0x1000000 - PM_ROW_SIZE/2);    //程序Boot进入需要延时,以完成Boot的过程。//在FLASH中装载0xFFFFFF,确保操作正常执行。    while(bytesWritten < PM_ROW_SIZE)    {        if(address.Val == DELAY_TIME_ADDR)        {            WriteLatch(address.word.HW, address.word.LW,                       userTimeout.word.HW,userTimeout.word.LW);        }        else        {            WriteLatch(address.word.HW, address.word.LW,                       0xFFFF,0xFFFF);        }        address.Val += 2;        bytesWritten +=4;    }#endif#ifdef USE_RUNAWAY_PROTECT    keyTest1 =  (0x0009 | temp) - 1 - 5;    keyTest2 =  ((0x557F << 1) + VERIFY_OK) + 6;#endif#ifdef DEV_HAS_WORD_WRITE    WriteMem(PM_WORD_WRITE);#else    WriteMem(PM_ROW_WRITE);      //执行写入设置#endif#ifdef USE_RUNAWAY_PROTECT    writeKey1 += 5;                //修改关键参数确保正确的编程流程    writeKey2 -= 6;#endif}// WriteTimeout函数结束//函数名:void AutoBaud(),用于设置自适应波特率。void AutoBaud(){    BYTE dummy;    UxMODEbits.ABAUD = 1;            //设置自适应波特率模式    while(UxMODEbits.ABAUD)        //等待同步字符0x55    {        asm("clrwdt");                    //首先清除看门狗定时器WDT        if(IFS0bits.T3IF == 1)          //如果超时溢出,则跳转到用户代码区        {            ResetDevice(userReset.Val);        }        if(UxSTAbits.OERR) UxSTAbits.OERR = 0;        if(UxSTAbits.URXDA) dummy = UxRXREG;    }#ifdef USE_WORKAROUNDS      //自适应波特率的校准    if(UxBRG == 0xD) UxBRG--;    if(UxBRG == 0x1A) UxBRG--;    if(UxBRG == 0x09) UxBRG--;#ifdef USE_HI_SPEED_BRG    UxBRG = (UxBRG+1)*4 -1;    if(UxBRG == 0x13) UxBRG=0x11;    if(UxBRG == 0x1B) UxBRG=0x19;    if(UxBRG == 0x08) UxBRG=0x22;    if (UxBRG & 0x0001)    UxBRG++;#endif#endif}//结束 AutoBaud()#ifdef DEV_HAS_PPS//函数名:void ioMap(),在PPS设备上影射UART IO作为通信void ioMap(){    //清除 IOLOCK 位    __builtin_write_OSCCONL(OSCCON & 0xFFBF);    //输入    PPS_URX_REG = PPS_URX_PIN;     //UxRX = RP19    //输出    PPS_UTX_PIN = UxTX_IO; //RP25 = UxTX    //锁住 IOLOCK位,因此IO不会被随机该变。    __builtin_write_OSCCONL(OSCCON | 0x0040);}//结束 ioMap()#endif#if defined(USE_BOOT_PROTECT) || defined(USE_RESET_SAVE)//函数名:void replaceBLReset(DWORD_VAL sourceAddr),参数sourceAddr ,是复位向量//的起始地址。本函数用于给输入的存储区中写入Boot的复位向量。void replaceBLReset(DWORD_VAL sourceAddr){    DWORD_VAL data;#ifndef DEV_HAS_WORD_WRITE    WORD i;#endif#ifdef USE_RUNAWAY_PROTECT    WORD tempkey1;    WORD tempkey2;    tempkey1 = keyTest1;    tempkey2 = keyTest2;#endif    //获得Boot复位向量的低字节,同时执行写入操作。    data.Val = 0x040000 + (0xFFFF & BOOT_ADDR_LOW);    WriteLatch(sourceAddr.word.HW, sourceAddr.word.LW, data.word.HW, data.word.LW);#ifdef DEV_HAS_WORD_WRITE#ifdef USE_RUNAWAY_PROTECT    writeKey1 += 5;         //修改关键参数确保正确的程序流程    writeKey2 -= 6;#endif                     //通过流程保护模式,执行Boot复位向量字节写入            WriteMem(PM_WORD_WRITE);    #endif                         //获得Boot复位向量高字节,同时执行写入操作    data.Val = ((DWORD)(BOOT_ADDR_LOW & 0xFF0000))>>16;    WriteLatch(sourceAddr.word.HW,sourceAddr.word.LW+2,data.word.HW,data.word.LW);#ifdef USE_RUNAWAY_PROTECT    keyTest1 = tempkey1;    keyTest2 = tempkey2;#endif#ifdef DEV_HAS_WORD_WRITE#ifdef USE_RUNAWAY_PROTECT    writeKey1 += 5;              //修改关键参数确保正确的程序流程    writeKey2 -= 6;#endif    WriteMem(PM_WORD_WRITE);   //执行写入Boot复位向量字节#else    for(i = 4; i < (PM_ROW_SIZE/PM_INSTR_SIZE*2); i+=2)    {        WriteLatch(sourceAddr.word.HW,sourceAddr.word.LW+i,0xFFFF,0xFFFF);    }#ifdef USE_RUNAWAY_PROTECT    writeKey1 += 5;             //修改关键参数确保正确的程序流程    writeKey2 -= 6;#endif    WriteMem(PM_ROW_WRITE);   //执行写入Boot复位向量字节#endif}//结束replaceBLReset()#endif为了测试上叙Boot代码,还需要用户应用程序代码,下面详细介绍应用程序文件代码。//文件名:test app.c,该Demo用户应用程序用于测试PIC24F系列的Boot功能。#include "p24fxxxx.h"//配置_CONFIG1(JTAGEN_OFF & GWRP_OFF & ICS_PGx1 & FWDTEN_OFF )_CONFIG2(POSCMOD_HS & FNOSC_PRIPLL & OSCIOFNC_ON)//函数申明void _ToggleLED(void);void _T1Interrupt();void _T2Interrupt();void ISRTable();               //中断服务程序#ifdef __PIC24FJ64GA004__    //兼容24FJ64GA004系列#define BL_ENTRY_BUTTON PORTAbits.RA9#else#define BL_ENTRY_BUTTON PORTDbits.RD7#endif//复位向量指针和Boot模式入口地址分别保存在0x100和0x102地址单元#define USE_VECTOR_SPACE   //Boot向量区#ifdef USE_VECTOR_SPACE//在0x100保存延时数值和用户复位向量(在复位向量保护模式不能使用)。用户复位向量数//值必须和C30编译器中设置的链接脚本文件(linker script)中的复位向量匹配。Boot 复位//向量必须保存在链接脚本文件(linker script)中unsigned int userReset  __attribute__ ((space(prog),section(".BLreset"))) = 0xC00;unsigned char timeout  __attribute__ ((space(prog),section(".BLreset"))) = 0xA ;#else//在用户空间启始地址保存延时数值和用户复位向量,用户复位向量数值必须是实际程序代//码的起始地址。因为这些变量都保存在同一个区域。unsigned int userReset  __attribute__ ((space(prog),section(".init"))) = 0xC04;unsigned char timeout  __attribute__ ((space(prog),section(".init"))) = 0x0A ;#endifint main(void){    AD1PCFG = 0xFFFF;              //设置I/O为输出#ifdef __PIC24FJ64GA004__      //兼容24FJ64GA004系列    TRISAbits.TRISA10 = 0;    TRISAbits.TRISA7 = 0;    TRISBbits.TRISB8 = 0;    TRISAbits.TRISA9 = 1;#else    TRISAbits.TRISA0 = 0;    TRISAbits.TRISA1 = 0;    TRISAbits.TRISA2 = 0;    TRISDbits.TRISD7 = 1;#endif    IEC0bits.T1IE = 1;    IFS0bits.T1IF = 0;    PR1 = 0xFFFF;    T1CON = 0x8010;    IEC0bits.T2IE = 1;    IFS0bits.T2IF = 0;    PR2 = 0xFFFF;    T2CON = 0x8020;    while(1)    {        _ToggleLED();        if(BL_ENTRY_BUTTON == 0)        {            T1CON = 0;            //关闭中断,防止意外操作影响Boot运行            T2CON = 0;            IEC0bits.T1IE = 0;            IEC0bits.T1IE = 0;            //配置和调用Boot            RCON = 0;            asm("goto 0x400");        }    }}void _ToggleLED(void){    static int count = 0;    if(++count == 0)    {#ifdef __PIC24FJ64GA004__    // LED D5翻转        LATBbits.LATB8 ^= 1;#else        LATAbits.LATA2 ^= 1;#endif    }}//中断重影射方法1:使用直接的中断地址void __attribute__ ((interrupt,address(0xF00),no_auto_psv)) _T1Interrupt(){    IFS0bits.T1IF = 0;#ifdef __PIC24FJ64GA004          //LED D3 翻转    LATAbits.LATA10 ^= 1;#else    LATAbits.LATA0 ^= 1;#endif}//中断重影射方法2:使用Goto或者跳转指令void __attribute__ ((interrupt,no_auto_psv)) _T2Interrupt(){    IFS0bits.T2IF = 0;    //Toggle LED D4#ifdef __PIC24FJ64GA004    LATAbits.LATA7 ^= 1;#else    LATAbits.LATA1 ^= 1;#endif}//中断服务程序void __attribute__ ((address(0x1000))) ISRTable(){    asm("reset");                       //复位指令防止代码跑飞    asm("goto %0"::"i"(&_T2Interrupt));  //T2中断地址}


                                             
0 0