芯启元 发表于 2026-7-3 15:28:44

60-DMA-I2C与存储器数据自动收发,STC32G144K246实验箱演示程序

60-DMA-I2C与存储器数据自动收发,STC32G144K246实验箱演示程序
主程序main.C代码如下(汇编版本在附件中):


/*---------------------------------------------------------------------*/
/* --- Web: www.STCAI.com ---------------------------------------------*/
/* --- BBS: www.STCAIMCU.com-----------------------------------------*/
/*---------------------------------------------------------------------*/

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

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

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

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

串口发指令通过I2C DMA读写AT24C02数据.

串口(P3.0,P3.1)默认设置: 115200,8,N,1.

串口命令设置: (字母不区分大小写)
    W 0x10 12345678 --> 写入操作十六进制地址写入内容.
    R 0x10 8      --> 读出操作十六进制地址读出字节数.

24CXX连续写入的字节数不同, 1\2Kbit 8-Byte/Page, 4\8\16Kbit 16-Byte/Page.
连续写入的话,地址+数据长度不要超过一个PAGE范围.

下载时, 选择时钟 48MHz (用户可自行修改频率).

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

#include "../comm/STC32G144K246.h"

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

/****************用户定义宏****************/

#define MAIN_Fosc       48000000UL
#define Baudrate      115200L
#define TM            (65536 -(MAIN_Fosc/Baudrate/4))

/****************本地常量声明**************/

#define EE_BUF_LENGTH       255          //
#define UART1_BUF_LENGTH    (EE_BUF_LENGTH+7)   //串口缓冲长度

#define SLAW    0xA0
#define SLAR    0xA1

/****************IO口定义******************/

#define WP_DISABLE()    P9CLRB = 0x10//P94 = 0
#define WP_ENABLE()   P9SETB = 0x10//P94 = 1

/****************本地变量声明**************/

u8 EEPROM_addr;
u8 xdata DmaTxBuffer;
u8 xdata DmaRxBuffer;

u16 RX1_Cnt;    //接收计数
bit B_TX1_Busy; //发送忙标志
bit      DmaTxFlag=0;
bit      DmaRxFlag=0;
bit B_RX1_TimeOut;//接收超时标志

u8RX1_Buffer;   //接收缓冲

/****************本地函数声明**************/

void UartInit(void);
void I2C_init(void);
void WriteNbyte(u8 addr, u8 number);
void ReadNbyte( u8 addr, u8 number);
void delay_ms(u16 ms);
void RX1_Check(void);
void DMA_Config(void);

/**********************************************/
void main(void)
{
    EAXFR = 1; //扩展寄存器(XFR)访问使能
    CKCON = 1; //避免 DMA 与 CPU 同时访问 xdata 时产生总线冲突

    P7M1 = 0x00;   P7M0 = 0x80;   //P77设置为推挽输出
    P9M1 = 0x00;   P9M0 = 0x10;   //P94设置为推挽输出
    P3M1 = 0x00;   P3M0 = 0x02;   //设置P3.1推挽输出

    P7PU |= 0x40;   //P76内部上拉使能
   
    I2C_init();
    UartInit();
    DMA_Config();
    EA = 1;   //允许总中断
   
    WP_DISABLE();

    printf("命令设置:\r\n");
    printf("W 0x10 12345678 --> 写入操作十六进制地址写入内容\r\n");
    printf("R 0x10 8      --> 读出操作十六进制地址读出字节内容\r\n");

    while(1)
    {
      if(B_RX1_TimeOut)
      {
            B_RX1_TimeOut = 0;

            if(RX1_Cnt > 0)
            {
                RX1_Check();    //串口1处理数据
            }
            RX1_Cnt = 0;
      }
    }
}

//========================================================================
// 函数: void delay_ms(unsigned int ms)
// 描述: 延时函数。
// 参数: ms,要延时的ms数.
// 返回: none.
// 版本: VER1.0
// 日期: 2025-11-01
// 备注: 由于芯片使能了Cache功能,软件延时时间可能不太准确
//========================================================================
void delay_ms(u16 ms)
{
    u16 i;
    do{
      i = MAIN_Fosc / 6000;
      while(--i);
    }while(--ms);
}

/**************** ASCII码转BIN ****************************/
u8 CheckData(u8 dat)
{
    if((dat >= '0') && (dat <= '9'))      return (dat-'0');
    if((dat >= 'A') && (dat <= 'F'))      return (dat-'A'+10);
    return 0xff;
}

/**************** 获取写入地址 ****************************/
u8 GetAddress(void)
{
    u8 address;
    u8i,j;
   
    address = 0;
    if((RX1_Buffer == '0') && (RX1_Buffer == 'X'))
    {
      for(i=4; i<6; i++)
      {
            j = CheckData(RX1_Buffer);
            if(j >= 0x10)   return 0;//error
            address = (address << 4) + j;
      }
      return (address);
    }
    return0; //error
}

/**************** 获取要读出数据的字节数 ****************************/
u8GetDataLength(void)
{
    u8i;
    u8length;
   
    length = 0;
    for(i=7; i<RX1_Cnt; i++)
    {
if(CheckData(RX1_Buffer) >= 10)break;
      length = length * 10 + CheckData(RX1_Buffer);
    }
    return (length);
}

/**************** 串口处理函数 ****************************/

void RX1_Check(void)
{
    u8i,j;

    F0 = 0;
    if((RX1_Cnt >= 8) && (RX1_Buffer == ' '))   //最短命令为8个字节
    {
      for(i=0; i<6; i++)
      {
            if((RX1_Buffer >= 'a') && (RX1_Buffer <= 'z'))    RX1_Buffer = RX1_Buffer - 'a' + 'A';//小写转大写
      }
      EEPROM_addr = GetAddress();
      if(EEPROM_addr <= 255)
      {
            if((RX1_Buffer == 'W') && (RX1_Cnt >= 8) && (RX1_Buffer == ' '))   //写入N个字节
            {
                j = RX1_Cnt - 7;
               
                for(i=0; i<j; i++)DmaTxBuffer = RX1_Buffer;
                WriteNbyte(EEPROM_addr, j);   //写N个字节
                printf("已写入%d字节内容!\r\n",j);
                delay_ms(5);

                ReadNbyte(EEPROM_addr, j);
                printf("读出%d个字节内容如下:\r\n",j);
                for(i=0; i<j; i++)    printf("%c", DmaRxBuffer);
                printf("\r\n");

                F0 = 1;
            }
            else if((RX1_Buffer == 'R') && (RX1_Cnt >= 8) && (RX1_Buffer == ' '))   //读出N个字节
            {
                j = GetDataLength();
                if((j > 0) && (j <= EE_BUF_LENGTH))
                {
                  ReadNbyte(EEPROM_addr, j);
                  printf("读出%d个字节内容如下:\r\n",j);
                  for(i=0; i<j; i++)    printf("%c", DmaRxBuffer);
                  printf("\r\n");
                  F0 = 1;
                }
            }
      }
    }
    if(!F0) printf("命令错误!\r\n");
}

//========================================================================
// 函数: void DMA_Config(void)
// 描述: I2C DMA 功能配置.
// 参数: none.
// 返回: none.
// 版本: V1.0, 2021-5-6
//========================================================================
void DMA_Config(void)
{
    DMA_I2CT_STA = 0x00;
    DMA_I2CT_CFG = 0x80;      //bit7 1:Enable Interrupt
    DMA_I2CT_AMT = 0xff;      //设置传输总字节数(低8位):n+1
    DMA_I2CT_AMTH = 0x00;      //设置传输总字节数(高8位):n+1
    DMA_I2CT_TXAH = (u8)((u16)&DmaTxBuffer >> 8);      //I2C发送数据存储地址
    DMA_I2CT_TXAL = (u8)((u16)&DmaTxBuffer);
    DMA_I2CT_CR = 0x80;                //bit7 1:使能 I2CT_DMA, bit6 1:开始 I2CT_DMA

    DMA_I2CR_STA = 0x00;
    DMA_I2CR_CFG = 0x80;      //bit7 1:Enable Interrupt
    DMA_I2CR_AMT = 0xff;      //设置传输总字节数(低8位):n+1
    DMA_I2CR_AMTH = 0x00;      //设置传输总字节数(高8位):n+1
    DMA_I2CR_RXAH = (u8)((u16)&DmaRxBuffer >> 8);      //I2C接收数据存储地址
    DMA_I2CR_RXAL = (u8)((u16)&DmaRxBuffer);
    DMA_I2CR_CR = 0x81;                //bit7 1:使能 I2CT_DMA, bit5 1:开始 I2CT_DMA, bit0 1:清除 FIFO

    DMA_I2C_ST1 = 0xff;                //设置需要传输字节数(低8位):n+1
    DMA_I2C_ST2 = 0x00;                //设置需要传输字节数(高8位):n+1
}

//========================================================================
// 函数: void UART1_int (void) interrupt UART1_VECTOR
// 描述: UART1中断函数。
// 参数: nine.
// 返回: none.
// 版本: VER1.0
// 日期: 2014-11-28
// 备注:
//========================================================================
void UART1_int (void) interrupt 4
{
    if(RI)
    {
      RI = 0;    //Clear Rx flag
      RX1_Buffer = SBUF;
      if(++RX1_Cnt >= UART1_BUF_LENGTH)   RX1_Cnt = 0;
    }

    if(TI)
    {
      TI = 0;    //Clear Tx flag
      B_TX1_Busy = 0;
    }

    if(UR1TOSR & 0x01)
    {
      B_RX1_TimeOut = 1;
      UR1TOSR = 0x80;   //设置 RTOCF 清除超时标志位 TOIF
    }
}

/********************** I2C函数 ************************/
void I2C_init(void)
{
    I2C_S1 = 1;   //I2C1功能脚选择,00:P1.5,P1.4; 01:P2.5,P2.4; 10:P7.7,P7.6; 11:P3.2,P3.3
    I2C_S0 = 0;
    I2CCFG = 0xe0;            //使能I2C主机模式
    I2CMSST = 0x00;
}

void WriteNbyte(u8 addr, u8 number)/*WordAddress,First Data Address,Byte lenth   */
{
    while(I2CMSST & 0x80);//检查I2C控制器忙碌状态

    DmaTxFlag = 1;
    DmaTxBuffer = SLAW;
    DmaTxBuffer = addr;

    I2CMSST = 0x00;
    I2CMSCR = 0x89;             //set cmd //write_start_combo
    DMA_I2C_CR = 0x01;
    DMA_I2CT_AMT = number+1;    //设置传输总字节数(低8位):number + 设备地址 + 存储地址
    DMA_I2CT_AMTH = 0x00;       //设置传输总字节数(高8位):n+1
    DMA_I2C_ST1 = number+1;   //设置需要传输字节数(低8位):number + 设备地址 + 存储地址
    DMA_I2C_ST2 = 0x00;         //设置需要传输字节数(高8位):n+1
    DMA_I2CT_CR |= 0x40;      //bit7 1:使能 I2CT_DMA, bit6 1:开始 I2CT_DMA

    while(DmaTxFlag);         //DMA忙检测
    DMA_I2C_CR = 0x00;
}

void ReadNbyte(u8 addr, u8 number)   /*WordAddress,First Data Address,Byte lenth   */
{
    while(I2CMSST & 0x80);    //检查I2C控制器忙碌状态
    DMA_I2C_CR = 0x00;
    I2CMSST = 0x00;

    //发送起始信号+设备地址+写信号
    I2CTXD = SLAW;
    I2CMSCR = 0x09;
    while ((I2CMSST & 0x40) == 0);
    I2CMSST = 0x00;

    //发送存储器地址
    I2CTXD = addr;
    I2CMSCR = 0x0a;
    while ((I2CMSST & 0x40) == 0);
    I2CMSST = 0x00;
   
    //发送起始信号+设备地址+读信号
    I2CTXD = SLAR;
    I2CMSCR = 0x09;
    while ((I2CMSST & 0x40) == 0);
    I2CMSST = 0x00;

    DmaRxFlag = 1;
    //触发数据读取命令
    I2CMSCR = 0x8b;
    DMA_I2C_CR = 0x01;

    DMA_I2CR_AMT = number-1;    //设置传输总字节数(低8位):n+1
    DMA_I2CR_AMTH = 0x00;       //设置传输总字节数(高8位):n+1
    DMA_I2C_ST1 = number-1;   //设置需要传输字节数(低8位):number + 设备地址 + 存储地址
    DMA_I2C_ST2 = 0x00;         //设置需要传输字节数(高8位):n+1
    DMA_I2CR_CR |= 0x40;      //bit7 1:使能 I2CT_DMA, bit5 1:开始 I2CT_DMA, bit0 1:清除 FIFO
    while(DmaRxFlag);         //DMA忙检测
    DMA_I2C_CR = 0x00;
}

//========================================================================
// 函数: void I2C_DMA_Interrupt (void) interrupt 60/61
// 描述: I2C DMA中断函数
// 参数: none.
// 返回: none.
// 版本: VER1.0
// 日期: 2021-5-8
// 备注:
//========================================================================
void I2CT_DMA_Interrupt(void) interrupt DMA_I2CT_VECTOR
{
    if(DMA_I2CT_STA & 0x01)   //发送完成
    {
      DMA_I2CT_STA &= ~0x01;//清除标志位
      DmaTxFlag = 0;
    }
    if(DMA_I2CT_STA & 0x04)   //数据覆盖
    {
      DMA_I2CT_STA &= ~0x04;//清除标志位
    }
}

void I2CR_DMA_Interrupt(void) interrupt DMA_I2CR_VECTOR
{
    if(DMA_I2CR_STA & 0x01)   //接收完成
    {
      DMA_I2CR_STA &= ~0x01;//清除标志位
      DmaRxFlag = 0;
    }
    if(DMA_I2CR_STA & 0x02)   //数据丢弃
    {
      DMA_I2CR_STA &= ~0x02;//清除标志位
    }
}

//========================================================================
// 函数: void I2C_Interrupt (void) interrupt 24
// 描述: I2C 中断函数
// 参数: none.
// 返回: none.
// 版本: VER1.0
// 日期: 2022-3-18
// 备注:
//========================================================================
void I2C_Interrupt() interrupt I2C_VECTOR
{
      I2CMSST &= ~0x40;       //I2C指令发送完成状态清除

      if(DMA_I2C_CR & 0x04)   //ACKERR
      {
                DMA_I2C_CR &= ~0x04;//发数据后收到NAK
      }
}

/******************** 串口打印函数 ********************/
void UartInit(void)
{
    S1_S1 = 0;      //UART1 switch to, 0x00: P3.0 P3.1, 0x40: P3.6 P3.7, 0x80: P1.6 P1.7, 0xC0: P4.3 P4.4
    S1_S0 = 0;
    SCON = (SCON & 0x3f) | 0x40;
    T1x12 = 1;      //定时器时钟1T模式
    S1BRT = 0;      //串口1选择定时器1为波特率发生器
    TL1= TM;
    TH1= TM>>8;
    TR1 = 1;      //定时器1开始计时

//    SCON = (SCON & 0x3f) | 0x40;
//    T2L= TM;
//    T2H= TM>>8;
//    AUXR |= 0x15; //串口1选择定时器2为波特率发生器

    UR1TOCR = 0xc0; //bit7:使能超时接收,bit6:使能超时中断,bit5:超时时钟选择 1:系统时钟0:串口数据位率(波特率)
    UR1TOTL = 0x20;
    UR1TOTH = 0x00; //0x5666 = 22118
    UR1TOTE = 0x00; //需要写 UR1TOTE 后,新的TM值才会生效

    REN = 1;      //允许接收
    ES = 1;         //使能串口1中断
}

void UartPutc(unsigned char dat)
{
    SBUF = dat;
    B_TX1_Busy = 1;
    while(B_TX1_Busy);
}

char putchar(char c)
{
    UartPutc(c);
    return c;
}




页: [1]
查看完整版本: 60-DMA-I2C与存储器数据自动收发,STC32G144K246实验箱演示程序