芯启元 发表于 2026-6-26 11:09:07

18-通过串口2发送命令读写EEPROM测试程序,STC32G12K128实验箱演示程序,STC32G8K64,S

18-通过串口2发送命令读写EEPROM测试程序,STC32G12K128实验箱演示程序,STC32G8K64,STC32CL8K64
主程序main.C代码如下(汇编版本在附件中):

/*---------------------------------------------------------------------*/
/* --- Web: www.STCAI.com ---------------------------------------------*/
/* --- BBS: www.STCAIMCU.com-----------------------------------------*/
/* 如果要在程序中使用此代码,请在程序中注明使用了STC的资料及程序            */
/*---------------------------------------------------------------------*/


/*************本程序功能说明**************

本例程基于STC32G为主控芯片的实验箱进行编写测试。

使用Keil C251编译器,Memory Model推荐设置XSmall模式,默认定义变量在edata,单时钟存取访问速度快。

edata建议保留1K给堆栈使用,空间不够时可将大数组、不常用变量加xdata关键字定义到xdata空间。

通过串口2(P4.6 P4.7)对STC内部自带的EEPROM(FLASH)进行读写测试。

对FLASH做扇区擦除、写入、读出的操作,命令指定地址。

默认波特率:115200,8,N,1.
默认主时钟:22.1184MHz.

串口命令设置: (命令字母不区分大小写)
    W 0x000040 1234567890--> 对0x000040地址写入字符1234567890.
    R 0x000040 10          --> 对0x000040地址读出10个字节数据.

注意:下载时,下载界面"硬件选项"中设置用户EEPROM大小,

并确保串口命令中的地址在EEPROM设置的大小范围之内。

******************************************/

#include "..\..\comm\STC32G.h"
#include "intrins.h"

#define   MAIN_Fosc       22118400L   //定义主时钟(精确计算115200波特率)

typedef   unsigned char   u8;
typedef   unsigned int    u16;
typedef   unsigned long   u32;

#define   Baudrate2         115200L
#define   Tmp_Length          100      //读写EEPROM缓冲长度

#define   UART2_BUF_LENGTH    (Tmp_Length+11)//串口缓冲长度

u8RX2_TimeOut;
u8TX2_Cnt;    //发送计数
u8RX2_Cnt;    //接收计数
bit B_TX2_Busy; //发送忙标志

u8RX2_Buffer;   //接收缓冲
u8tmp;      //EEPROM操作缓冲


void    UART2_config(u8 brt);   // 选择波特率, 2: 使用Timer2做波特率, 其它值: 无效.
void    PrintString2(u8 *puts);
void    UART2_TxByte(u8 dat);
void    delay_ms(u8 ms);
u8      CheckData(u8 dat);
u32   GetAddress(void);
u8      GetDataLength(void);
void    EEPROM_SectorErase(u32 EE_address);
void    EEPROM_read_n(u32 EE_address,u8 *DataAddress,u8 length);
u8      EEPROM_write_n(u32 EE_address,u8 *DataAddress,u8 length);


/********************* 主函数 *************************/
void main(void)
{
    u8i,j;
    u32 addr;
    u8status;

    WTST = 0;//设置程序指令延时参数,赋值为0可将CPU执行指令的速度设置为最快
    EAXFR = 1; //扩展寄存器(XFR)访问使能
    CKCON = 0; //提高访问XRAM速度

    P0M1 = 0x30;   P0M0 = 0x30;   //设置P0.4、P0.5为漏极开路(实验箱加了上拉电阻到3.3V)
    P1M1 = 0x30;   P1M0 = 0x30;   //设置P1.4、P1.5为漏极开路(实验箱加了上拉电阻到3.3V)
    P2M1 = 0x3c;   P2M0 = 0x3c;   //设置P2.2~P2.5为漏极开路(实验箱加了上拉电阻到3.3V)
    P3M1 = 0x50;   P3M0 = 0x50;   //设置P3.4、P3.6为漏极开路(实验箱加了上拉电阻到3.3V)
    P4M1 = 0x3c;   P4M0 = 0x3c;   //设置P4.2~P4.5为漏极开路(实验箱加了上拉电阻到3.3V)
    P5M1 = 0x0c;   P5M0 = 0x0c;   //设置P5.2、P5.3为漏极开路(实验箱加了上拉电阻到3.3V)
    P6M1 = 0xff;   P6M0 = 0xff;   //设置为漏极开路(实验箱加了上拉电阻到3.3V)
    P7M1 = 0x00;   P7M0 = 0x00;   //设置为准双向口

    UART2_config(2);    // 选择波特率, 2: 使用Timer2做波特率, 其它值: 无效.
    EA = 1; //允许总中断

    PrintString2("STC32G系列单片机EEPROM测试程序,串口命令设置如下:\r\n");    //UART2发送一个字符串
    PrintString2("W 0x000040 1234567890--> 对0x000040地址写入字符1234567890.\r\n");   //UART2发送一个字符串
    PrintString2("R 0x000040 10          --> 对0x000040地址读出10个字节内容.\r\n"); //UART2发送一个字符串

    while(1)
    {
      delay_ms(1);
      if(RX2_TimeOut > 0)   //超时计数
      {
            if(--RX2_TimeOut == 0)
            {
            for(i=0; i<RX2_Cnt; i++)    UART2_TxByte(RX2_Buffer);    //把收到的数据原样返回,用于测试

                status = 0xff;//状态给一个非0值
                if((RX2_Cnt >= 12) && (RX2_Buffer == ' ') && (RX2_Buffer == ' ')) //最短命令为10个字节
                {
                  for(i=0; i<10; i++)
                  {
                        if((RX2_Buffer >= 'a') && (RX2_Buffer <= 'z'))    RX2_Buffer = RX2_Buffer - 'a' + 'A';//小写转大写
                  }
                  addr = GetAddress();
                  if(addr < 0x00ffffff)
                  {
                        if(RX2_Buffer == 'W')    //写入N个字节
                        {
                            j = RX2_Cnt - 11;
                            if(j > Tmp_Length)j = Tmp_Length; //越界检测
                            EEPROM_SectorErase(addr);         //擦除扇区
                            i = EEPROM_write_n(addr,&RX2_Buffer,j);      //写N个字节
                            if(i == 0)
                            {
                              PrintString2("\r\n已写入");
                              if(j >= 100)    {UART2_TxByte((u8)(j/100+'0'));   j = j % 100;}
                              if(j >= 10)   {UART2_TxByte((u8)(j/10+'0'));    j = j % 10;}
                              UART2_TxByte((u8)(j%10+'0'));
                              PrintString2("字节!\r\n");
                            }
                            else    PrintString2("\r\n写入错误!\r\n");
                            status = 0; //命令正确
                        }

                        else if(RX2_Buffer == 'R')   //PC请求返回N字节EEPROM数据
                        {
                            j = GetDataLength();
                            if(j > Tmp_Length)j = Tmp_Length; //越界检测
                            if(j > 0)
                            {
                              PrintString2("\r\n读出");
                              UART2_TxByte((u8)(j/10+'0'));
                              UART2_TxByte((u8)(j%10+'0'));
                              PrintString2("个字节内容如下:\r\n");
                              EEPROM_read_n(addr,tmp,j);
                              for(i=0; i<j; i++)UART2_TxByte(tmp);
                              UART2_TxByte(0x0d);
                              UART2_TxByte(0x0a);
                              status = 0; //命令正确
                            }
                        }
                  }
                }
                if(status != 0) PrintString2("\r\n命令错误!\r\n");
                RX2_Cnt= 0;   //清除字节数
            }
      }
    }
}
//========================================================================


//========================================================================
// 函数: void delay_ms(u8 ms)
// 描述: 延时函数。
// 参数: ms,要延时的ms数, 这里只支持1~255ms. 自动适应主时钟.
// 返回: none.
// 版本: VER1.0
// 日期: 2021-3-9
// 备注:
//========================================================================
void delay_ms(u8 ms)
{
   u16 i;
   do{
          i = MAIN_Fosc / 6000;
          while(--i);
   }while(--ms);
}

//========================================================================
// 函数: u8 CheckData(u8 dat)
// 描述: 将字符"0~9,A~F或a~f"转成十六进制.
// 参数: dat: 要检测的字符.
// 返回: 0x00~0x0F为正确. 0xFF为错误.
// 版本: V1.0, 2012-10-22
//========================================================================
u8 CheckData(u8 dat)
{
    if((dat >= '0') && (dat <= '9'))      return (dat-'0');
    if((dat >= 'A') && (dat <= 'F'))      return (dat-'A'+10);
    return 0xff;
}

//========================================================================
// 函数: u32 GetAddress(void)
// 描述: 计算各种输入方式的地址.
// 参数: 无.
// 返回: 24位EEPROM地址.
// 版本: V1.0, 2013-6-6
//========================================================================
u32 GetAddress(void)
{
    u32 address;
    u8i,j;

    address = 0;
    if((RX2_Buffer == '0') && (RX2_Buffer == 'X'))
    {
      for(i=4; i<10; i++)
      {
            j = CheckData(RX2_Buffer);
            if(j >= 0x10)   return 0xffffffff;   //error
            address = (address << 4) + j;
      }
      return (address);
    }
    return0xffffffff;//error
}

/**************** 获取要读出数据的字节数 ****************************/
u8 GetDataLength(void)
{
    u8i;
    u8length;

    length = 0;
    for(i=11; i<RX2_Cnt; i++)
    {
      if(CheckData(RX2_Buffer) >= 10)break;
      length = length * 10 + CheckData(RX2_Buffer);
    }
    return (length);
}


//========================================================================
// 函数: void UART2_TxByte(u8 dat)
// 描述: 发送一个字节.
// 参数: 无.
// 返回: 无.
// 版本: V1.0, 2014-6-30
//========================================================================
void UART2_TxByte(u8 dat)
{
    S2BUF = dat;
    B_TX2_Busy = 1;
    while(B_TX2_Busy);
}


//========================================================================
// 函数: void PrintString2(u8 *puts)
// 描述: 串口2发送字符串函数。
// 参数: puts:字符串指针.
// 返回: none.
// 版本: VER1.0
// 日期: 2014-11-28
// 备注:
//========================================================================
void PrintString2(u8 *puts) //发送一个字符串
{
    for (; *puts != 0;puts++) UART2_TxByte(*puts);    //遇到停止符0结束
}

//========================================================================
// 函数: void SetTimer2Baudraye(u16 dat)
// 描述: 设置Timer2做波特率发生器。
// 参数: dat: Timer2的重装值.
// 返回: none.
// 版本: VER1.0
// 日期: 2014-11-28
// 备注:
//========================================================================
void SetTimer2Baudraye(u16 dat)// 选择波特率, 2: 使用Timer2做波特率, 其它值: 无效.
{
    T2R = 0;                //Timer stop
    T2_CT = 0;      //Timer2 set As Timer
    T2x12 = 1;      //Timer2 set as 1T mode
    T2H = (u8)(dat / 256);
    T2L = (u8)(dat % 256);
    ET2 = 0;    //禁止中断
    T2R = 1;                //Timer run enable
}

//========================================================================
// 函数: void UART2_config(u8 brt)
// 描述: UART2初始化函数。
// 参数: brt: 选择波特率, 2: 使用Timer2做波特率, 其它值: 无效.
// 返回: none.
// 版本: VER1.0
// 日期: 2014-11-28
// 备注:
//========================================================================
void UART2_config(u8 brt)    // 选择波特率, 2: 使用Timer2做波特率, 其它值: 无效.
{
    /*********** 波特率固定使用定时器2 *****************/
    if(brt == 2)
    {
      SetTimer2Baudraye((u16)(65536UL - (MAIN_Fosc / 4) / Baudrate2));

      S2CFG |= 0x01;   //使用串口2时,W1位必需设置为1,否则可能会产生不可预期的错误
      S2CON = (S2CON & 0x3f) | 0x40;    //UART2模式, 0x00: 同步移位输出, 0x40: 8位数据,可变波特率, 0x80: 9位数据,固定波特率, 0xc0: 9位数据,可变波特率
      ES2   = 1;         //允许中断
      S2REN = 1;         //允许接收
      S2_S= 1;         //UART2 switch to: 0: P1.0 P1.1,1: P4.6 P4.7

      RX2_TimeOut = 0;
      B_TX2_Busy = 0;
      TX2_Cnt = 0;
      RX2_Cnt = 0;
    }
}


//========================================================================
// 函数: void UART2_int (void) interrupt UART2_VECTOR
// 描述: UART2中断函数。
// 参数: nine.
// 返回: none.
// 版本: VER1.0
// 日期: 2014-11-28
// 备注:
//========================================================================
void UART2_int (void) interrupt 8
{
    if(S2RI)
    {
      S2RI = 0;    //Clear Rx flag
      RX2_Buffer = S2BUF;
      if(++RX2_Cnt >= UART2_BUF_LENGTH)   RX2_Cnt = 0;
      RX2_TimeOut = 5;
    }

    if(S2TI)
    {
      S2TI = 0;    //Clear Tx flag
      B_TX2_Busy = 0;
    }
}


#define   IAP_STANDBY()   IAP_CMD = 0   //IAP空闲命令(禁止)
#define   IAP_READ()      IAP_CMD = 1   //IAP读出命令
#define   IAP_WRITE()   IAP_CMD = 2   //IAP写入命令
#define   IAP_ERASE()   IAP_CMD = 3   //IAP擦除命令

#define   IAP_ENABLE()    IAP_CONTR = IAP_EN; IAP_TPS = MAIN_Fosc / 1000000
#define   IAP_DISABLE()   IAP_CONTR = 0; IAP_CMD = 0; IAP_TRIG = 0; IAP_ADDRE = 0xff; IAP_ADDRH = 0xff; IAP_ADDRL = 0xff

#define IAP_EN          (1<<7)
#define IAP_SWBS      (1<<6)
#define IAP_SWRST       (1<<5)
#define IAP_CMD_FAIL    (1<<4)


//========================================================================
// 函数: void DisableEEPROM(void)
// 描述: 禁止EEPROM.
// 参数: none.
// 返回: none.
// 版本: V1.0, 2014-6-30
//========================================================================
void DisableEEPROM(void)      //禁止访问EEPROM
{
    IAP_CONTR = 0;          //关闭 IAP 功能
    IAP_CMD = 0;            //清除命令寄存器
    IAP_TRIG = 0;         //清除触发寄存器
    IAP_ADDRE = 0xff;       //将地址设置到非 IAP 区域
    IAP_ADDRH = 0xff;       //将地址设置到非 IAP 区域
    IAP_ADDRL = 0xff;
}

//========================================================================
// 函数: void EEPROM_Trig(void)
// 描述: 触发EEPROM操作.
// 参数: none.
// 返回: none.
// 版本: V1.0, 2014-6-30
//========================================================================
void EEPROM_Trig(void)
{
    F0 = EA;    //保存全局中断
    EA = 0;   //禁止中断, 避免触发命令无效
    IAP_TRIG = 0x5A;
    IAP_TRIG = 0xA5;                  //先送5AH,再送A5H到IAP触发寄存器,每次都需要如此
                                        //送完A5H后,IAP命令立即被触发启动
                                        //CPU等待IAP完成后,才会继续执行程序。
    _nop_();   //由于STC32G是多级流水线的指令系统,触发命令后建议加4个NOP,保证IAP_DATA的数据完成准备
    _nop_();
    _nop_();
    _nop_();
    EA = F0;    //恢复全局中断
}

//========================================================================
// 函数: void EEPROM_SectorErase(u32 EE_address)
// 描述: 擦除一个扇区.
// 参数: EE_address:要擦除的EEPROM的扇区中的一个字节地址.
// 返回: none.
// 版本: V1.0, 2014-6-30
//========================================================================
void EEPROM_SectorErase(u32 EE_address)
{
    IAP_ENABLE();                     //设置等待时间,允许IAP操作,送一次就够
    IAP_ERASE();                        //宏调用, 送扇区擦除命令,命令不需改变时,不需重新送命令
                                        //只有扇区擦除,没有字节擦除,512字节/扇区。
                                        //扇区中任意一个字节地址都是扇区地址。
    IAP_ADDRE = (u8)(EE_address >> 16); //送扇区地址高字节(地址需要改变时才需重新送地址)
    IAP_ADDRH = (u8)(EE_address >> 8);//送扇区地址中字节(地址需要改变时才需重新送地址)
    IAP_ADDRL = (u8)EE_address;         //送扇区地址低字节(地址需要改变时才需重新送地址)
    EEPROM_Trig();                      //触发EEPROM操作
    DisableEEPROM();                  //禁止EEPROM操作
}

//========================================================================
// 函数: void EEPROM_read_n(u32 EE_address,u8 *DataAddress,u8 lenth)
// 描述: 读N个字节函数.
// 参数: EE_address:要读出的EEPROM的首地址.
//       DataAddress: 要读出数据的指针.
//       length:      要读出的长度
// 返回: 0: 写入正确.1: 写入长度为0错误.2: 写入数据错误.
// 版本: V1.0, 2014-6-30
//========================================================================
void EEPROM_read_n(u32 EE_address,u8 *DataAddress,u8 length)
{
    IAP_ENABLE();                           //设置等待时间,允许IAP操作,送一次就够
    IAP_READ();                           //送字节读命令,命令不需改变时,不需重新送命令
    do
    {
      IAP_ADDRE = (u8)(EE_address >> 16); //送地址高字节(地址需要改变时才需重新送地址)
      IAP_ADDRH = (u8)(EE_address >> 8);//送地址中字节(地址需要改变时才需重新送地址)
      IAP_ADDRL = (u8)EE_address;         //送地址低字节(地址需要改变时才需重新送地址)
      EEPROM_Trig();                      //触发EEPROM操作
      *DataAddress = IAP_DATA;            //读出的数据送往
      EE_address++;
      DataAddress++;
    }while(--length);

    DisableEEPROM();
}


//========================================================================
// 函数: u8 EEPROM_write_n(u32 EE_address,u8 *DataAddress,u8 length)
// 描述: 写N个字节函数.
// 参数: EE_address:要写入的EEPROM的首地址.
//       DataAddress: 要写入数据的指针.
//       length:      要写入的长度
// 返回: 0: 写入正确.1: 写入长度为0错误.2: 写入数据错误.
// 版本: V1.0, 2014-6-30
//========================================================================
u8 EEPROM_write_n(u32 EE_address,u8 *DataAddress,u8 length)
{
    u8i;
    u16 j;
    u8*p;

    if(length == 0) return 1;   //长度为0错误

    IAP_ENABLE();                     //设置等待时间,允许IAP操作,送一次就够
    i = length;
    j = EE_address;
    p = DataAddress;
    IAP_WRITE();                            //宏调用, 送字节写命令
    do
    {
      IAP_ADDRE = (u8)(EE_address >> 16); //送地址高字节(地址需要改变时才需重新送地址)
      IAP_ADDRH = (u8)(EE_address >> 8);//送地址中字节(地址需要改变时才需重新送地址)
      IAP_ADDRL = (u8)EE_address;         //送地址低字节(地址需要改变时才需重新送地址)
      IAP_DATA= *DataAddress;         //送数据到IAP_DATA,只有数据改变时才需重新送
      EEPROM_Trig();                      //触发EEPROM操作
      EE_address++;                     //下一个地址
      DataAddress++;                      //下一个数据
    }while(--length);                     //直到结束

    EE_address = j;
    length = i;
    DataAddress = p;
    i = 0;
    IAP_READ();                           //读N个字节并比较
    do
    {
      IAP_ADDRE = (u8)(EE_address >> 16); //送地址高字节(地址需要改变时才需重新送地址)
      IAP_ADDRH = (u8)(EE_address >> 8);//送地址中字节(地址需要改变时才需重新送地址)
      IAP_ADDRL = (u8)EE_address;         //送地址低字节(地址需要改变时才需重新送地址)
      EEPROM_Trig();                      //触发EEPROM操作
      if(*DataAddress != IAP_DATA)      //读出的数据与源数据比较
      {
            i = 2;
            break;
      }
      EE_address++;
      DataAddress++;
    }while(--length);

    DisableEEPROM();
    return i;
}


页: [1]
查看完整版本: 18-通过串口2发送命令读写EEPROM测试程序,STC32G12K128实验箱演示程序,STC32G8K64,S