AI8051内置了硬件的IIC,在之前的帖子中分享了IIC读取AT24C02的数据,这里分享一下硬件IIC使用DMA的方式进行数据的存储与读取。
基本的实现思路是:配置硬件IIC从机/主机模式,设置DMA通道,将内存中的数据通过DMA自动传输到IIC外设,从而发送到IIC存储设备。
一:几个关键的寄存器
首先,需要明确几个关键寄存器的分工,这对于正确配置至关重要。
DMA_I2CT_AMT / DMA_I2CR_AMT:这是控制寄存器,用于设置DMA发送或接收通道需要传输的总字节数,每次传输的数据需要提前配置好。
DMA_I2C_ST:这是一个状态寄存器,用于监控DMA传输的实时状态,比如传输是否完成、是否出错以及剩余字节数等。它主要用于在传输过程中或传输后进行状态查询和错误处理。
TXAH / TXAL (发送地址寄存器):这两个寄存器用于指定DMA传输的源数据起始地址(即你要发送的数据在哪儿)。如果你的数据存储在xdata区域的数组array中,你需要将数组的24位地址分解,低8位赋值给TXAL,高8位赋值给TXAH。
二 配置过程中需要注意的地方:
DMA只负责数据传输阶段:这是一个非常重要的概念。DMA启动后,确实会自动处理IIC总线上的数据bit流时序,但起始条件(START)、停止条件(STOP)以及从机地址的发送,通常仍需通过操作IIC控制寄存器由CPU来触发。
流程一般是:CPU发起起始信号和从机地址 -> 确认从机应答后 -> 启动DMA传输数据 -> 数据传完后,CPU再发起停止信号。
数据存储位置:用于DMA传输的数据缓冲区必须定义在 xdata 区域。因为DMA控制器直接访问的是扩展内存(xdata),无法访问有限的内部直接寻址内存(data/idata)。
无阻塞式数据传输:利用DMA的一大优势就是可以实现“无阻塞”传输。CPU配置好DMA并启动后,就可以立即去处理其他任务,数据传输完全由硬件在后台完成,极大地提高了CPU的利用率。
三:软件代码:
3.1 IIC的初始化:
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
}3.2 使用IIC 写入多个字节
void WriteNbyte(u8 addr, u8 number) /* WordAddress,First Data Address,Byte lenth */
{
while(I2CMSST & 0x80); //检查I2C控制器忙碌状态
DmaTxFlag = 1;
DmaTxBuffer[0] = SLAW;
DmaTxBuffer[1] = 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;
}3.3 使用IIC 读取多个字节
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;
}3.4 IIC的DMA中断服务函数
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; //清除标志位
}
}
我要赚赏金
