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
- pic24bootloader
- LeetCode Create Maximum Number
- Ultedit 配合 astyle 格式化C代码
- 增强的接口
- mysql 基础
- SQL解析及体会
- pic24bootloader
- Java命名规则
- [Latex] Math Equations
- 【BLE】CC2541之动态更新设备名
- Binary Tree Maximum Path Sum Java 递归解法
- [Latex] Matrix and Determinant
- IOS GameAudioManager 使用缺少 AudioToolbox Framework
- SQL 测验-自测结果
- [Latex] Algorithm