国学芯用 发表于 2026-7-2 11:29:52

66-DMA-I2C主机模式读写AT24C02-串口监控,AI8051U实验箱演示程序

66-DMA-I2C主机模式读写AT24C02-串口监控,AI8051U实验箱演示程序

主程序main.C代码如下(汇编版本在附件中):

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

/*************功能说明    **************


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

使用Keil C51编译器,为了达到比较高的效率,一般建议选择"Small"模式。

当编译器出现 "error C249: 'DATA': SEGMENT TOO LARGE" 错误时,则需要手动将部分比较大的数组通过"xdata" 强制分配到 XDATA 区域(例如: char xdata buffer ;)

串口发指令通过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范围.

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

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

#include "../comm/AI8051U.h"
#include "intrins.h"
#include "stdio.h"

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

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

#define MAIN_Fosc       22118400L   //定义主时钟(精确计算115200波特率)
#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

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

u8 EEPROM_addr;
u8 xdata DmaTxBuffer;
u8 xdata DmaRxBuffer;

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

u8RX1_Buffer;   //接收缓冲

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

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

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

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

    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;
}

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

    P0M1 = 0x00;   P0M0 = 0x00;   //设置为准双向口
    P1M1 = 0x00;   P1M0 = 0x00;   //设置为准双向口
    P2M1 = 0x00;   P2M0 = 0x00;   //设置为准双向口
    P3M1 = 0x00;   P3M0 = 0x00;   //设置为准双向口
    P4M1 = 0x00;   P4M0 = 0x00;   //设置为准双向口
    P5M1 = 0x00;   P5M0 = 0x00;   //设置为准双向口
    P6M1 = 0x00;   P6M0 = 0x00;   //设置为准双向口
    P7M1 = 0x00;   P7M0 = 0x00;   //设置为准双向口
   
    I2C_init();
    UartInit();
    DMA_Config();
    EA = 1;   //允许总中断

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

    while(1)
    {
      delay_ms(1);

      if(RX1_TimeOut > 0)
      {
            if(--RX1_TimeOut == 0)//超时,则串口接收结束
            {
                if(RX1_Cnt > 0)
                {
                  RX1_Check();    //串口1处理数据
                }
                RX1_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);
}

/**************** 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;
      RX1_TimeOut = 5;
    }

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

/********************** I2C函数 ************************/
void I2C_init(void)
{
    P_SW2 = (P_SW2 & ~(3<<4)) | (3<<4); //IO口切换. 0: P1.4 P1.5, 1: P2.4 P2.5, 3: P3.3 P3.2
    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 I2C_DMA_Interrupt(void) interrupt 13
{
    if(DMA_I2CT_STA & 0x01)   //发送完成
    {
      DMA_I2CT_STA &= ~0x01;//清除标志位
      DmaTxFlag = 0;
    }
    if(DMA_I2CT_STA & 0x04)   //数据覆盖
    {
      DMA_I2CT_STA &= ~0x04;//清除标志位
    }

    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 24
{
        I2CMSST &= ~0x40;       //I2C指令发送完成状态清除

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


页: [1]
查看完整版本: 66-DMA-I2C主机模式读写AT24C02-串口监控,AI8051U实验箱演示程序