关于STC8H8K64U单片机IAP升级过程

本文详细介绍了STC8H8K64单片机内部FLASH的规划,以及如何利用IAP(In-Application Programming)进行程序更新。通过解析官方例程,阐述了复位程序的实现,包括如何定义和调用IAP函数。同时,给出了IAP升级代码的解析,以及在KEIL中定位IAP升级代码的步骤。
摘要由CSDN通过智能技术生成

本文参考了STC的官方例程,根据官方例程来进行讲解IAP的详细操作

一、内部FLASH的规划

STC不同型号的单片机,内部FLASH的规划大不相同,详细的使用规格书都有介绍,以下我只截图规格书当中我需要的部分进行讲解

1、首先是规格书当中STC8H8K64单片机的内部使用空间划分

FLASH 空间中,从地址 0000H 开始的连续 62.5K 字节的空间为用户程序区。当满足特定的下载条 件时,需要用户将 PC 跳转到用户 ISP 程序区,此时可对用户程序区进行擦除和改写,以达到更新用户 程序的目的。(只有0.5K的ISP空间所以实际使用代码是参考了官方例程的)

二、程序的基本框架

实际使用的程序框架如图所示:

 三、复位程序

以下是官方代码

#include "reg51.h"

typedef unsigned char BYTE;
typedef unsigned int WORD;
typedef void (* FARPROC)(BYTE *);     

#define FOSC 22118400L                  //系统时钟频率
#define BAUD (65536 - FOSC/4/115200)     //定义串口波特率

/* 定义串口相关SFR */
sfr AUXR = 0x8E;                        //波特率发生器控制寄存器
sfr BRT = 0x9C;                         //波特率发生器定时器

sfr T2H = 0xd6;
sfr T2L = 0xd7;

void (*Isp_Check)(BYTE *) = 0xfa00;      //定义指针函数
BYTE cnt7f;                             //Isp_Check内部使用的变量

void uart() interrupt 4 using 1         //串口中断服务程序
{
    if (TI) TI = 0;                     //发送完成中断
    if (RI)                             //接收完成中断
    {
        Isp_Check(&cnt7f);              //ISP检测
        RI = 0;                         //清接收完成标志
    }
}

void main()
{
    SCON = (SCON & 0x3f) | 0x50; 
    T2L  = BAUD;
    T2H  = BAUD>>8;
    AUXR |= 0x15;
    ES = 1;                             //使能串口中断
    EA = 1;                             //打开全局中断开关

    while (1)
    {
        P0++;
    }
}

 官方代码复位的主要用到以下三段代码用来复位

typedef void (* FARPROC)(BYTE *);   

void (*Isp_Check)(BYTE *) = 0xFA00;      //定义指针函数

这是重定义了一下指针函数使其指向地址0XFA00,也就是用户ISP区,实际使用的时候只需要满足条件后调用

        Isp_Check(&cnt7f);     

即可进入用户ISP区,这里操作较为简单不做其他介绍

PS:规格书上也标注了调用   IAP_CONTR=0x60 即可软件复位,然后通过ISP的方式进行升级,这种也是可以的,就和重新上电烧程序的方式一致,这里不多做介绍,感兴趣的可以去官网看看有例程介绍

四、IAP升级代码

以下是官方例程,可以根据自己使用来修改协议(笔者是自己更改了串口、协议来进行升级,这个看个人需求)

#include "reg51.h"
#include "absacc.h"

/*定义常数*/

#define FOSC 22118400L                  //系统时钟频率
#define BAUD (65536 - (FOSC/4/115200))    //定义串口波特率

#define MAX_SIZE 118                    //用户程序最大的可用扇区数

#define ENABLE_IAP 0x80               //使能IAP功能

/* 定义串口相关SFR */
sfr AUXR	    = 0x8E; 	              //波特率发生器控制寄存器
sfr WDT_CONTR	= 0xC1;                 //看门狗控制寄存器
sfr IAP_DATA	= 0xC2;                 //IAP数据寄存器
sfr IAP_ADDRH	= 0xC3;                 //IAP高地址寄存器
sfr IAP_ADDRL	= 0xC4;                 //IAP低地址寄存器
sfr IAP_CMD	  = 0xC5;                 //IAP命令寄存器
sfr IAP_TRIG	= 0xC6;                 //IAP命令触发寄存器
sfr IAP_CONTR	= 0xC7;                 //IAP控制寄存器
sfr IAP_TPS   = 0xF5;                 //IAP等待时间控制寄存器

sfr T2H = 0xd6;
sfr T2L = 0xd7;
//?PR?_ISP_CHECK?ISP(0xEE00),?PR?ISP_SOFTRESET?ISP(0xEF38),?PR?_ISP_SENDUART?ISP(0xEF3C),?PR?ISP_RECVUART?ISP(0xEF49),?PR?_ISP_RECVBLOCK?ISP(0xEF5B),?C?LIB_CODE(0xEF72)

typedef unsigned char BYTE;
typedef unsigned int WORD;
typedef struct
{
    BYTE cmd;
    WORD addr;
    WORD len;
    BYTE chk;
} CBW;

void Isp_Check(BYTE *p);
BYTE Isp_RecvUart();
void Isp_RecvBlock(BYTE *p, BYTE n);
void Isp_SendUart(BYTE dat);
void Isp_SoftReset();

BYTE cnt7f;                             //Isp_Check内部使用的变量,接收7F的计数器,当连续接收到16次7F后进入ISP下载模式
CBW cbw;                                //串口命令块
BYTE sum;                               //校验和变量
BYTE buf[64];                           //数据缓冲区

/*------------------------------------------------
  串口中断服务程序
------------------------------------------------*/
void uart() interrupt 4
{
    if (TI) TI = 0;                     //发送完成中断
    if (RI)                             //接收完成中断
    {
        Isp_Check(&cnt7f);              //ISP检测
        RI = 0;                         //清接收完成标志
    }
}

void main()
{
    SCON = (SCON & 0x3f) | 0x50; 
    T2L  = BAUD;
    T2H  = BAUD>>8;
    AUXR |= 0x15;
    ES = 1;                             //使能串口中断
    EA = 1;                             //打开全局中断开关

    while (1);
}

/*------------------------------------------------
  串口ISP命令序列检测模块
------------------------------------------------*/
void Isp_Check(BYTE *p)
{
    BYTE i;
    WORD j;
    
    if (SBUF != 0x7f)                   //检测串口数据是否为7F
    {
        *p = 0;                         //若不是7F,则清7F计数值
    }
    else
    {
        (*p)++;                         //若是7F,则7F计数值+1
        if (*p >= 16)                   //判断7F是否已连续接收到16次
        {                               //若>=16次,则进入ISP下载模式
            IE = 0;                     //关闭所有中断
            PSW = 0;                    //ISP模块使用第0组寄存器
            SP = 0x5f;                  //重置ISP模块的堆栈指针
            RI = 0;                     //清除串口接收标志
            TI = 1;                     //置串口发送标志
            Isp_SendUart(0x5a);         //返回5A 69到PC,表示ISP模块已准备就绪
            Isp_SendUart(0x69);
            while (1)                   //ISP下载模式,主循环
            {
                sum = 0;                //清校验和值
                if ((Isp_RecvUart() == 0x5a) &&     //判断命令头是否为5A,69
                    (Isp_RecvUart() == 0x69))
                {
                    Isp_RecvBlock((BYTE *)&cbw, 6); //接收6字节的命令序列
                    if (sum != 0)                   //判断命令序列是否正确
                    {
                        Isp_SoftReset();            //若命令出错,则程序复位
                    }
                    switch (cbw.cmd)
                    {
                    case 0:                         //0号命令为擦除命令
                        IAP_ADDRL = 0;              //从第0扇区开始擦除
                        IAP_ADDRH = 0;
                        IAP_CONTR = ENABLE_IAP;     //使能IAP功能
                        IAP_TPS = 23;
                        IAP_CMD = 3;                //擦除命令
                        if (cbw.len > MAX_SIZE)     //判断擦除扇区数是否超出范围
                        {
                            cbw.len = MAX_SIZE;
                        }
                        while (cbw.len--)           //判断是否擦除完成
                        {
                            WDT_CONTR = 0x17;       //清看门狗
                            IAP_TRIG = 0x5a;        //触发ISP命令
                            IAP_TRIG = 0xa5;
                            IAP_ADDRH += 2;         //目标地址+512
                        }
                        Isp_SendUart(0);            //正确返回
                        break;
                    case 1:                         //1号命令为编程命令
                        sum = 0;                    //清除校验和值
                        Isp_RecvBlock(buf, 64);     //接收64字节的编程数据
                        Isp_RecvUart();             //接收校验和
                        if (sum != 0)               //判断数据是否正确
                        {
                            Isp_SoftReset();        //若数据出错,则程序复位
                        }
                        IAP_CONTR = ENABLE_IAP;     //使能IAP功能
                        IAP_TPS = 23;
                        IAP_CMD = 2;                //编程命令
                        j = cbw.addr;               //编程目标地址
                        for (i=0; i<64; i++)        //编程64字节数据
                        {
                            WDT_CONTR = 0x17;       //清看门狗
                            IAP_DATA = buf[i];      //将当前数据送IAP数据寄存器
                            IAP_ADDRL = j;          //目标地址送IAP地址寄存器
                            IAP_ADDRH = j >> 8;
                            IAP_TRIG = 0x5a;        //触发ISP命令
                            IAP_TRIG = 0xa5;
                            j++;                    //目标地址+1
                        }
                        j = cbw.addr;               //校验目标地址
                        for (i=0; i<64; i++)        //校验64字节数据
                        {
                            WDT_CONTR = 0x17;       //清看门狗
                            if (buf[i] != CBYTE[j]) //源数据与目标数据进行比较
                                break;              //不相等,则编程出错
                            j++;                    //校验下一个字节
                        }
                        Isp_SendUart(!(i == 64));   //校验成功返回0; 否则返回1
                        break;
                    default:
                        Isp_SoftReset();            //接收到非法命令时,复位系统
                        break;
                    }
                }
            }
        }
    }
}

/*------------------------------------------------
  接收1字节串口数据
  出口参数: ACC (接收到的数据)
------------------------------------------------*/
BYTE Isp_RecvUart()
{
    BYTE dat;
    
    while (!RI)                         //等待接收完成
    {
        WDT_CONTR = 0x17;               //清看门狗
    }
    dat = SBUF;                         //读取串口数据
    RI = 0;                             //清除标志
    sum += dat;                         //计算校验和
    
    return dat;                         //返回接收到的串口数据
}

/*------------------------------------------------
  接收一块串口数据
  入口参数: R0 (数据缓冲区地址)
            R7 (缓冲区长度)
------------------------------------------------*/
void Isp_RecvBlock(BYTE *p, BYTE n)
{
    while (n--)                         //检测长度
    {
        *p = Isp_RecvUart();            //接收1字节,并保存到缓冲区
        p++;                            //缓冲区地址+1
    }
}

/*------------------------------------------------
  发送1字节串口数据 
  入口参数: ACC (待发送的数据) 
------------------------------------------------*/
void Isp_SendUart(BYTE dat)
{
    while (!TI)                         //等待前一个数据发送完成
    {
        WDT_CONTR = 0x17;               //清看门狗
    }
    TI = 0;                             //清除标志
    SBUF = dat;                         //发送当前数据
}

/*------------------------------------------------
  软件复位
------------------------------------------------*/
void Isp_SoftReset()
{
    IAP_CONTR = 0x20;                   //用户程序区复位
}
#ifndef __ABSACC_H__
#define __ABSACC_H__

#define CBYTE ((unsigned char volatile code  *) 0)
#define DBYTE ((unsigned char volatile data  *) 0)
#if !defined (__CX2__)
#define PBYTE ((unsigned char volatile pdata *) 0)
#endif
#define XBYTE ((unsigned char volatile xdata *) 0)

#define CWORD ((unsigned int volatile code  *) 0)
#define DWORD ((unsigned int volatile data  *) 0)
#if !defined (__CX2__)
#define PWORD ((unsigned int volatile pdata *) 0)
#endif
#define XWORD ((unsigned int volatile xdata *) 0)


#if defined (__CX51__) || defined (__CX2__)
#define FVAR(object, addr)   (*((object volatile far *) (addr)))
#define FARRAY(object, base) ((object volatile far *) (base))
#define FCVAR(object, addr)   (*((object const far *) (addr)))
#define FCARRAY(object, base) ((object const far *) (base))
#else
#define FVAR(object, addr)    (*((object volatile far *) ((addr)+0x10000L)))
#define FCVAR(object, addr)   (*((object const far *) ((addr)+0x810000L)))
#define FARRAY(object, base)  ((object volatile far *) ((base)+0x10000L))
#define FCARRAY(object, base) ((object const far *) ((base)+0x810000L))
#endif

#if defined (__CX2__)
#define HBYTE ((unsigned char volatile huge *)  0)
#define HWORD ((unsigned int volatile huge *)  0)
#define HVAR(object, addr)   (*((object volatile huge *) (addr)))
#define HARRAY(object, base) ((object volatile huge *) (base))
#endif

#define CVAR(object, addr)   (*((object volatile code *) (addr)))
#define CARRAY(object, base) ((object volatile code *) (base))
#define DVAR(object, addr)   (*((object volatile data *) (addr)))
#define DARRAY(object, base) ((object volatile data *) (base))
#define XVAR(object, addr)   (*((object volatile xdata *) (addr)))
#define XARRAY(object, base) ((object volatile xdata *) (base))

#endif

五、KEIL中定位IAP升级代码的方法

         这是最后一步,这里函数定位结束后IAP就可以使用了,这里参考了官方文档,具体定位方法如下:

下面以演示程序进行说明

演示程序中有ReadIAPProgramIAPEraseIAP三个函数

最终目的是将这三个函数都定位到0x8000之后

第一步:新建一个项目“Demo”,并将源文件“Demo.C”添加到项目中

第二步:直接编译,并打开编译后生成的“Demo.M51”文件  

 

从M51文件的“CODE MEMORY”信息中,可以看到3个函数的链接名称、链接地址和函数长度

ReadIAP的链接名称为“?PR?_READIAP?DEMO”,链接地址为“0003H”,长度为16H字节

ProgramIAP的链接名称为“?PR?_PROGRAMIAP?DEMO”,链接地址为“0019H”,长度为16H字节

EraseIAP的链接名称为“?PR?_ERASEIAP?DEMO”,链接地址为“0044H”,长度为14H字节

第三步:根据M51中函数的长度信息计算出各个函数重定位的地址,

ReadIAP的重定位的地址为0x8000

ProgramIAP的重定位的地址为0x8016

EraseIAP的重定位的地址为0x802C

第四步:打开项目选项中的“BL51 Locate”属性页

 

在上图的“Code”域中输入下列语句

“?PR?_READIAP?DEMO(0x8000), ?PR?_PROGRAMIAP?DEMO(0x8016), ?PR?_ERASEIAP?DEMO(0x802C)”

以上参考了官方文档  WWW.STCMCU.COM ; Tel: 0755-82948412, Mobile:139-2280,5190

  • 1
    点赞
  • 25
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值