MCU的ISP程序完全用C語言編寫,接收Ymodem協議時忽略CRC16 校驗,用戶可以向己加上。當然,用戶可以完全自己定義協議,更改接收程序來獲得自己獨一無二的下載協議。關于Ymodem協議的相關知識,用戶可以向己上網搜索,資料很多。
原則上,用戶程序不需要知道具體ISP程序的實際地址,滿足一定條件時復位到用戶入口即可。但是用戶程序的長度不可以超過ISP占用的部分。 本例程,ISP定位在0xE400用戶程序可以寫到57K-3字節。0xE3FD、0xE30xE3FF這3個字節,被ISP存放用戶程序的入口指令。用超級終端下載時, 如果用戶程序超過0xE3FC, ISP會停止執行下載。
本文以宏晶科技的IAP12C5A60S2和IAP15F2K61S2為例,詳細說明使用宏晶科技的IAP單片機開發用戶自己的ISP程序的方法,并給出了基于keil的匯編和c源代碼
FLASH空間中,從地址0000H開始的連續57K-3字節的空間為用戶程序區。當滿足特定的下載條件時,需要用戶將MCU復位到用戶如成入口,執行IAP_CONTR=0x20即可,此時可對用戶程序區進行擦除和改寫,以達到更新用戶程序的目的。
三.下位機固件程序說明
下位機固件程序包括兩部分:ISP(ISP代碼)和AP(用戶代碼)*ISP代碼向成一體,用戶不需要調用ISP,軟件復位總會先運行ISP。兩種方式進入ISP:
1、超級終端,鍵盤為小寫狀態,按下字母d鍵,給MCU上電,MCU檢測到10個字母(1,就會進入ISP。
2、用戶程序中,串口連續接收到字母d,超過10個就執行IAP_CONTR=0x20復位到ISP。
*用戶代碼
用戶代碼可以使用C或者匯編語言編寫,但對于匯編代碼需要注意一點:位于0000H的復位入口地址處的指令必須是一個長跳轉語句(類似LJMP START)。在用戶代碼中,需要設置好串口,并在串口連續接收到字母d,超過10個就執行IAP_CONTR=0x20復位到ISP。具體細節請參考附件中的代碼。
ISP程序在編譯時,要設置一下,見下閣:
STC_IAP_ISP監控程序
- #define MAIN_Fosc 22118400L //定義主時鐘
- #define Baudrate0 115200UL //串口0波特率600~115200
- #include "STC15Fxxxx.H"
- #include "ymodem.h"
- #define WDT_Enable 0 //1: 允許內部看門狗, 0: 禁止
- #define D_BRT0_1T_8bit (256 - MAIN_Fosc / 32 / Baudrate0) //計算波特率 1T 8bit
- #define ISP_ADDRESS 0xE400 //ISP開始地址, 高地址必須是偶數, 注意要預留ISP空間,本例程留3K
- #define UserflashLenth (ISP_ADDRESS-3) //用戶FLASH長度, 保留個字節存放用戶地址程序的跳轉地址
- /************* 本地變量聲明 **************/
- typedef void (*pFunction)(void);
- pFunction Jump_To_Application;
- u16 JumpAddress;
- u16 FileLength;
- u16 Y_TimeOut,WaitTime;
- u8 packets_received, session_begin;
- u8 idata file_name[FILE_NAME_LENGTH];
- u8 idata file_size[FILE_SIZE_LENGTH];
- u8 StartCode[3];
- u16 FlashDestination;
- u16 RxCnt;
- u8 HandCnt;
- u8 xdata RxBuff[1024];
- u8 idata RxBuff_10[10];
- u32 DownCheckSum,FlashCheckSum;
- /************* 本地函數聲明 **************/
- u8 Hex2Ascii(u8 dat);
- u8 UART_Download(void);
- u16 Str2Int(u8 *inputstr);
- void ISP_WriteByte(u16 addr, u8 dat);
- void ISP_EraseSector(u16 addr);
- u8 ISP_ReadByte(u16 addr);
- //========================================================================
- // 函數: void TX1_write2buff(u8 dat)
- // 描述: 串口發送一個字節.
- // 參數: dat: 要發送的字節.
- // 返回: none.
- // 版本: V1.0, 2013-4-29
- //========================================================================
- void TX1_write2buff(u8 dat)
- {
- TI = 0;
- SBUF = dat;
- while(!TI);
- }
- //========================================================================
- // 函數: void PrintString1(u8 *p)
- // 描述: 串口發送一個字符串.
- // 參數: *p: 要發送的字符串指針.
- // 返回: none.
- // 版本: V1.0, 2013-4-29
- //========================================================================
- void PrintString1(u8 *p)
- {
- for(; *p > 0; p++) TX1_write2buff(*p);
- }
- //========================================================================
- // 函數: void Tx_DEC_U16(u16 j)
- // 描述: 把一個16位整型數轉成十進制送串口發送.
- // 參數: j: 要處理的16位整型數.
- // 返回: none.
- // 版本: V1.0, 2013-4-29
- //========================================================================
- void Tx_DEC_U16(u16 j)
- {
- u8 i;
- u8 tmp[10];
- for(i=4; i<5; i--) tmp[i] = j % 10 + '0', j = j / 10;
- for(i=0; i<4; i++)
- {
- if(tmp[i] != '0') break;
- tmp[i] = ' ';
- }
- for(i=0; i<5; i++) TX1_write2buff(tmp[i]);
- }
- //========================================================================
- // 函數: void Tx_HEX_U32(u32 j)
- // 描述: 把一個32位長整型數轉成十進制送串口發送.
- // 參數: j: 要處理的32位整型數.
- // 返回: none.
- // 版本: V1.0, 2013-4-29
- //========================================================================
- void Tx_HEX_U32(u32 j)
- {
- u8 i,k;
- u8 tmp[10];
- for(i=8; i>0; i--)
- {
- k = ((u8)j) & 0x0f;
- if(k <= 9) tmp[i] = k+'0';
- else tmp[i] = k-10+'A';
- j >>= 4;
- }
- for(i=1; i<9; i++) TX1_write2buff(tmp[i]);
- }
- //========================================================================
- // 函數: void ReturnNameAndLength(void)
- // 描述: 返回程序的文件名和長度, 和累加校驗和.
- // 參數: none.
- // 返回: none.
- // 版本: V1.0, 2013-4-29
- //========================================================================
- void ReturnNameAndLength(void)
- {
- u16 i;
- PrintString1("================================\r\n File name: ");
- for(i=0; i<FILE_NAME_LENGTH; i++)
- {
- if(file_name[i] == 0) break;
- TX1_write2buff(file_name[i]);
- }
- PrintString1("\r\n File length: ");
- Tx_DEC_U16(FileLength);
- PrintString1(" Bytes\r\n DownChexkSum: ");
- Tx_HEX_U32(DownCheckSum);
- PrintString1("\r\n ISP Versiom: 2013-4-30 by Coody");
- PrintString1("\r\n================================\r\n\r\n");
- }
- //========================================================================
- // 函數: void UART1_RxPackage(void)
- // 描述: 串口接收一個數據塊.
- // 參數: none.
- // 返回: none.
- // 版本: V1.0, 2013-4-29
- //========================================================================
- void UART1_RxPackage(void)
- {
- u16 j; //5ms超時
- // RI = 0;
- RxCnt = 0;
- for(j = 0; j < 5000; j++) //最后收到一個字節5ms后,超時退出
- {
- if(RI)
- {
- RI = 0;
- if(RxCnt < 1024) RxBuff[RxCnt] = SBUF;
- else
- {
- RxBuff_10[RxCnt-1024] = SBUF;
- }
- if(++RxCnt >= 1034) RxCnt = 1033;
- j = 0; //重新定時5ms
- }
- }
- #if (WDT_Enable > 0)
- WDT_reset(D_WDT_SCALE_256);
- #endif
- }
- //========================================================================
- // 函數: void main(void)
- // 描述: 主函數.
- // 參數: none.
- // 返回: none.
- // 版本: V1.0, 2013-4-29
- //========================================================================
- void main(void)
- {
- u8 i;
- EA = 0;
- Timer0_Stop();
- Timer1_Stop();
- #if (WDT_Enable > 0)
- WDT_reset(D_WDT_SCALE_256);
- #endif
- S1_8bit();
- S1_RX_Enable();
- S1_BRT_UseTimer1();
- Timer1_AsTimer();
- Timer1_1T();
- Timer1_8bitAutoReload();
- Timer1_InterruptDisable();
- TH1 = D_BRT0_1T_8bit;
- Timer1_Run();
- // PrintString1("\r\n STC ISP Demo\r\n");
-
- while(1)
- {
- // TX1_write2buff('A');
- HandCnt = 0;
- for(WaitTime = 0; WaitTime < 300; WaitTime++) //1.5秒超時
- {
- UART1_RxPackage();
- if((RxCnt == 1) && (RxBuff[0] == 'd'))
- {
- if(++HandCnt >= 10)
- {
- i = UART_Download();
- WaitTime = 250;
- if(i == 1) PrintString1("\r\n User abort!\r\n");
- else if(i == 2) PrintString1("\r\n PC Cancel!\r\n");
- else if(i == 4) PrintString1("\r\n Programming Error!\r\n");
- else if(i == 0)
- {
- PrintString1("\r\n\r\n Programming Completed Successfully!\r\n");
- ReturnNameAndLength();
- }
- }
- }
- // else HandCnt = 0;
- }
- if(ISP_ReadByte(ISP_ADDRESS-3) == 0x02)
- {
- SCON = 0;
- AUXR = 0;
- TMOD = 0;
- TL0 = 0;
- TH0 = 0;
- TH1 = 0;
- TL1 = 0;
- TCON = 0;
- IAP_CMD = 0;
- JumpAddress = ISP_ReadByte(ISP_ADDRESS-2);
- JumpAddress = (JumpAddress << 8) | ISP_ReadByte(ISP_ADDRESS-1); //Jump to user application
- Jump_To_Application = (pFunction) JumpAddress;
- Jump_To_Application();
- }
- PrintString1(" No AP\r\n");
- }
- }
- //========================================================================
- // 函數: u16 Str2Int(u8 *inputstr)
- // 描述: 字符串轉整型.
- // 參數: *inputstr: 字符串指針.
- // 返回: 16位整型數.
- // 版本: V1.0, 2013-4-29
- //========================================================================
- u16 Str2Int(u8 *inputstr)
- {
- u16 val;
- u8 i;
-
- val = 0;
- for (i = 0; i < 5; i++)
- {
- if((inputstr[i] < '0') || (inputstr[i] > '9')) break;
- val = val * 10 + inputstr[i] - '0';
- }
- return val;
- }
- //========================================================================
- // 函數: void ISP_WriteByte(u16 addr, u8 dat)
- // 描述: 對一個地址寫FLASH一個字節.
- // 參數: addr: 16位FLASH地址.
- // dat: 要寫入的一字節數據.
- // 返回: none.
- // 版本: V1.0, 2013-4-29
- //========================================================================
- void ISP_WriteByte(u16 addr, u8 dat)
- {
- IAP_CONTR = ISP_CONTR = (ISP_EN + ISP_WAIT_FREQUENCY); //使能IAP功能
- ISP_WRITE(); //編程命令
- IAP_ADDRL = addr;
- IAP_ADDRH = addr >> 8;
- IAP_DATA = dat; //將當前數據送IAP數據寄存器
- ISP_TRIG(); //觸發ISP命令
- }
- //========================================================================
- // 函數: u8 ISP_ReadByte(u16 addr)
- // 描述: 從一個地址讀FLASH一個字節.
- // 參數: addr: 16位FLASH地址.
- // 返回: 讀出的一字節數據.
- // 版本: V1.0, 2013-4-29
- //========================================================================
- u8 ISP_ReadByte(u16 addr)
- {
- IAP_CONTR = ISP_CONTR = (ISP_EN + ISP_WAIT_FREQUENCY); //使能IAP功能
- ISP_READ(); //編程命令
- IAP_ADDRL = addr;
- IAP_ADDRH = addr >> 8;
- ISP_TRIG(); //觸發ISP命令
- return(IAP_DATA);
- }
- //========================================================================
- // 函數: void ISP_EraseSector(u16 addr)
- // 描述: 對FLASH擦除一個扇區.
- // 參數: addr: 16位FLASH地址.
- // 返回: none.
- // 版本: V1.0, 2013-4-29
- //========================================================================
- void ISP_EraseSector(u16 addr)
- {
- IAP_CONTR = ISP_CONTR = (ISP_EN + ISP_WAIT_FREQUENCY); //使能IAP功能
- ISP_ERASE(); //擦除命令
- IAP_ADDRL = addr;
- IAP_ADDRH = addr >> 8;
- ISP_TRIG(); //觸發ISP命令
- }
- //========================================================================
- // 函數: u8 UART_Download(void)
- // 描述: 按Ymodem接收文件數據并寫入用戶FLASH.
- // 參數: none.
- // 返回: none.
- // 版本: V1.0, 2013-4-29
- //========================================================================
- u8 UART_Download(void)
- {
- u16 i;
- u8 j;
- PrintString1("\r\n\r\n Waiting for the file to be sent ... (press 'a' to abort)\r\n");
-
- Y_TimeOut = 0;
- packets_received = 0;
- session_begin = 0;
- WaitTime = 40;
- DownCheckSum = 0;
-
- while(WaitTime > 0)
- {
- if(Y_TimeOut == 0)
- {
- TX1_write2buff(CRC16);
- Y_TimeOut = 300;
- if(WaitTime > 0) WaitTime--;
- }
- while(Y_TimeOut > 0)
- {
- UART1_RxPackage();
- if(RxCnt == 0) Y_TimeOut--;
- else
- {
- if(RxCnt == 1)
- {
- if(RxBuff[0] == EOT)
- {
- TX1_write2buff(ACK);
- Y_TimeOut = 40;
- }
- else if((RxBuff[0] == ABORT1) || (RxBuff[0] == ABORT2))
- {
- return 1;
- }
- }
- else if(RxCnt <= 5)
- {
- if((RxBuff[0] == CANCEL) && (RxBuff[1] == CANCEL))
- {
- return 2;
- }
- }
- else if((RxCnt == 133) || (RxCnt == 1029))
- {
- if (RxBuff[PACKET_SEQNO_INDEX] != (RxBuff[PACKET_SEQNO_COMP_INDEX] ^ 0xff))
- {
- TX1_write2buff(NAK); //錯誤, 請求重發
- Y_TimeOut = 300;
- }
- else
- {
- WaitTime = 5;
- if (packets_received == 0) //發送序號為0, 為文件名數據包
- {
- if (RxBuff[PACKET_HEADER] != 0) //文件名不為空
- {
- for (i = 0; i < FILE_NAME_LENGTH; i++) file_name[i] = 0;
- for (i = 0; i < FILE_SIZE_LENGTH; i++) file_size[i] = 0;
- j = PACKET_HEADER;
- for (i = 0; (i < FILE_NAME_LENGTH) && (RxBuff[j] != 0); i++)
- file_name[i] = RxBuff[j++]; //保存文件名
- for (i=0, j++; (RxBuff[j] != ' ') && (i < FILE_SIZE_LENGTH); i++)
- file_size[i] = RxBuff[j++]; //保存文件長度
- FileLength = Str2Int(file_size); //文件長度由字符串轉成十六進制數據
- if (FileLength >= UserflashLenth) //長度過長錯誤
- {
- TX1_write2buff(CANCEL); //錯誤返回2個 CA
- TX1_write2buff(CANCEL);
- return 3; //長度過長
- }
-
- StartCode[0] = ISP_ReadByte(0); //保存ISP跳轉地址
- StartCode[1] = ISP_ReadByte(1);
- StartCode[2] = ISP_ReadByte(2);
- ISP_EraseSector(0);
- ISP_WriteByte(0,StartCode[0]); //回寫ISP跳轉地址
- ISP_WriteByte(1,StartCode[1]);
- ISP_WriteByte(2,StartCode[2]);
- for(i=0x200; i < UserflashLenth; i+=0x200) //擦除N頁
- ISP_EraseSector(i);
-
- TX1_write2buff(ACK); //擦除完成, 返回應答
- Y_TimeOut = 40;
- packets_received ++;
- session_begin = 1;
- FlashDestination = 0;
- DownCheckSum = 0;
- }
- }
- else if(session_begin == 1) //收過文件名
- {
- if(RxBuff[PACKET_SEQNO_INDEX] == 0) //全0數據幀
- {
- ISP_WriteByte(ISP_ADDRESS-3,StartCode[0]); //全部下載結束,最后寫用戶入口地址
- ISP_WriteByte(ISP_ADDRESS-2,StartCode[1]);
- ISP_WriteByte(ISP_ADDRESS-1,StartCode[2]);
- TX1_write2buff(ACK);
-
- FlashCheckSum = ISP_ReadByte(ISP_ADDRESS-3);
- FlashCheckSum += ISP_ReadByte(ISP_ADDRESS-2);
- FlashCheckSum += ISP_ReadByte(ISP_ADDRESS-1);
- for(i = 3; i < FileLength; i++) FlashCheckSum += ISP_ReadByte(i); //計算FLASH累加和
- if(FlashCheckSum == DownCheckSum) return 0; //正確
- else
- {
- ISP_EraseSector(ISP_ADDRESS-0x200);
- return 4; //寫入錯誤
- }
- }
- else //數據幀
- {
- for(i=0; i<1024; i++) RxBuff[i] = RxBuff[i+3];
- RxBuff[1021] = RxBuff_10[0];
- RxBuff[1022] = RxBuff_10[1];
- RxBuff[1023] = RxBuff_10[2];
- RxCnt -= 5;
- for(i = 0; (i < RxCnt) && (FlashDestination < FileLength); i++)
- {
- if(FlashDestination == 0)
- {
- StartCode[0] = RxBuff[0];
- StartCode[1] = RxBuff[1];
- StartCode[2] = RxBuff[2];
- FlashDestination = 3;
- i += 3;
- DownCheckSum += RxBuff[0]; DownCheckSum += RxBuff[1]; DownCheckSum += RxBuff[2];
- }
- ISP_WriteByte(FlashDestination,RxBuff[i]);
- DownCheckSum += RxBuff[i];
- FlashDestination ++;
- }
- TX1_write2buff(ACK); //保存完成, 返回應答
- Y_TimeOut = 300;
- packets_received ++;
- }
- }
- }
- }
- }
- }
- }
- return 100; //其他錯誤
- }
復制代碼
用戶實際代碼范例
- /*------------------------------------------------------------------*/
- /* --- STC MCU International Limited -------------------------------*/
- /* --- STC IAP 系列單片機實現用戶ISP 演示程序 ----------------------*/
- /* 如果要在程序中使用或者在文章中引用該程序,請在程序中或文章中注明 */
- /* 使用了宏晶科技的資料或程序 */
- /*------------------------------------------------------------------*/
- #include "reg51.h"
- #define FOSC 22118400L //系統時鐘頻率
- #define BAUD (256 - FOSC/32/115200) //定義串口波特率 要與IAP里的相同
- /* 定義串口相關SFR */
- sfr AUXR = 0x8E; //波特率發生器控制寄存器
- sfr ISP_CONTR = 0xC7;
- sfr PCON2 = 0x97; //for IAP15F2K61S2
- sfr P_SW1 = 0xA2; //for IAP15F2K61S2
- char HandCnt; //Isp_Check內部使用的變量
- void uart() interrupt 4 //串口中斷服務程序
- {
- if (TI) TI = 0; //發送完成中斷
- if (RI) //接收完成中斷
- {
- if (SBUF == 'd')
- {
- HandCnt++;
- if (HandCnt >= 16)
- {
- ISP_CONTR = 0x20; //復位到AP入口
- }
- }
- else HandCnt = 0;
- RI = 0; //清接收完成標志
- }
- }
- void main()
- {
- SCON = 0x50; //定義串口模式為8bit可變,無校驗位
- AUXR = 0x40; //波特率發生器12倍速,并啟動波特率發生器定時器
- TMOD = 0x20; //初始化波特率發生器定時器的定時初值
- //對于IAP15F2K61S2, 增加這兩句,串口1可以切換P30,P31
- P_SW1 &= ~0xc0; //S1_USE_P30P31();
- PCON2 &= ~(1<<4); //S1_TXD_RXD_OPEN();
- TH1 = BAUD;
- TR1 = 1;
- ES = 1; //使能串口中斷
- EA = 1; //打開全局中斷開關
- while (1)
- {
- P1++;
- }
- }
復制代碼
全部資料51hei下載地址:
STC庫函數使用參考.pdf
(152.17 KB, 下載次數: 43)
2018-10-13 15:45 上傳
點擊文件名下載附件
STC庫函數使用參考
STC庫函數使用參考
庫函數.zip
(24.14 KB, 下載次數: 34)
2018-10-13 15:45 上傳
點擊文件名下載附件
庫函數
庫函數
例程.zip
(1.96 MB, 下載次數: 52)
2018-10-13 15:45 上傳
點擊文件名下載附件
例程
例程
ISP-Demo-使用Y-Modem協議.zip
(275.68 KB, 下載次數: 82)
2018-10-13 15:46 上傳
點擊文件名下載附件
ISP-Demo-使用Y-Modem協議
ISP-Demo-使用Y-Modem協議
|