CH32V203 I2C DMA问题

我用I2C2 DMA读取从机寄存器,问什么总是比我要读取的多了一个字节?

icon_rar.gifI2C2_DMA.zip

我代码里写了DMA bufsize是1,但逻辑分析仪看到的是读回来两个字节,请帮忙看看是哪里配置出错了么?

    logan_DMA_I2C2_RX_Init((u32)RxData,1);

    logan_i2c2_rx(0x5b, 0x12);

image.png

这是由于没有提前关闭I2C的自动应答ACK造成的。DMA配置的长度只决定DMA会从I2C外设搬运几个数据,而I2C接收多少数据是由I2C逻辑控制的。浏览代码发现I2C接收第一个字节前并未关闭自动应答ACK。这就造成从机收到主机的ACK从而认为主机需要再读1字节而继续发送,主机无法发出停止条件。

根据CH32FV2x_V3xRM的275页所述:

“在接收模式时,主设备需要在最后一个数据位的应答位置 NAK,接收到 NACK 后,从设备释放对 SCL 和 SDA 线的控制”

故对于只读1字节的情况,需要在读取之前关闭自动应答ACK。对于只读取1字节的情况,代码应如下修改:

while( !I2C_CheckEvent( I2C2, I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED ) ); // 原有代码
I2C_AcknowledgeConfig( I2C2, DISABLE ); // 新增代码,提前关闭自动应答ACK
DMA_Cmd( DMA1_Channel5, ENABLE ); // 原有代码

而对于读取2字节或更多的情况,自动应答ACK应等到还剩1字节待读取时再关闭。


另外附上一个X035系列I2C逻辑的状态机实现代码,楼主可以参考:(链接会直接跳转到读时序的接收阶段,可以看到若只剩1字节需要接收时,程序中关闭自动应答ACK的功能)

https://github.com/WuxiProject-offical/CH32X035-HelperLibrary/blob/main/I2C/Master/i2c.c#L617


灰常感谢,那如果用DMA的话有什么方法可以处理这个“BUG”,总不能去一直循环查询还剩多少个数据吧?


我用某业内主流32位MCU时遇到类似的问题,我的策略是DMA长度比需要读取的长度少一个,在DMA的传输完成中断里关闭自动应答ACK,然后最后一字节用中断接收;如果只读1字节,直接用中断读取而不开DMA。

但V203上,似乎可以卡bug做一个非常骚的操作,即直接设置DMA长度为比需要读取的长度少一个,不在DMA传输完成中断里添加代码。按你的测试现象来看,如果最后一个字节没有被DMA取走,主机会自动发NACK并结束通信?但这一行为我不确定是否可靠,建议等周一官方工作人员回复下。


我在手册里看到这样一个寄存器,是解决这个问题的,如下图。


初始化的时候加上这一句就行了 I2C_DMALastTransferCmd(I2C2,ENABLE);

image.png


如果最后一个字节没有被DMA取走,主机会自动发NACK并结束通信?” 我在DMA完成中断里发送了STOP。


解决了上述问题,依然有两个BUG,帮忙看看是哪里的问题:

1.红色圈的地方,逻辑分析仪可以看到接受了4个数据,但print接受数字时,第一个字节没有正确输出。

2.紫色圈出来的位置,从逻辑分析仪看发送了5个字节(包括寄存器),但程序里TX的长度写成6才能正确发送4个字节。

icon_rar.gifI2C2_DMA.zip

附件是代码。

1.jpg


1.红色圈的地方,逻辑分析仪可以看到接受了4个数据,但print接受数字时,第一个字节没有正确输出。

针对这个问题如下代码中加了一个延时解决了,但不明白为什么会这样,如果不加延时该怎么处理?

void logan_i2c2_rx(u16 slave_add, u16 reg)

{


    while( I2C_GetFlagStatus( I2C2, I2C_FLAG_BUSY ) != RESET );

    I2C_GenerateSTART( I2C2, ENABLE);


    while( !I2C_CheckEvent( I2C2, I2C_EVENT_MASTER_MODE_SELECT ) );

    logan_send_addr( I2C2, slave_add, I2C_Direction_Transmitter);


    while( !I2C_CheckEvent( I2C2, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED ) );


#if (Address_Lenth  == Address_8bit)

    I2C_SendData( I2C2, (u8) (reg & 0x00FF));

    while( !I2C_CheckEvent( I2C2, I2C_EVENT_MASTER_BYTE_TRANSMITTED ) );


#elif (Address_Lenth  == Address_16bit)

    I2C_SendData( I2C1, (u8)(reg>>8) );

    while( !I2C_CheckEvent( I2C1, I2C_EVENT_MASTER_BYTE_TRANSMITTED ) );


    I2C_SendData( I2C1, (u8)(reg&0x00FF) );

    while( !I2C_CheckEvent( I2C1, I2C_EVENT_MASTER_BYTE_TRANSMITTED ) );


#endif


    I2C_GenerateSTART( I2C2, ENABLE);


    while( !I2C_CheckEvent( I2C2, I2C_EVENT_MASTER_MODE_SELECT ) );

    logan_send_addr( I2C2, slave_add, I2C_Direction_Receiver);


    while( !I2C_CheckEvent( I2C2, I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED ) );

    Delay_Us(100);

    DMA_Cmd( DMA1_Channel5, ENABLE );


}




由于我手上并无V203的硬件,以下均为本人按照经验所做的推测,请酌情参考。

对于问题1,我没有什么头绪,不确定是否与问题2有关,建议只保留读取逻辑再次测试。

对于问题2,我个人认为是因为DMA发出最后一字节后,该字节数据刚刚装入I2C数据寄存器还未来得及发送,DMA就因数据已全部传输完毕而触发TC中断,而程序中TC中断立即发出了STOP信号,故I2C外设未来得及发出该字节即产生了停止序列。故,可在只写入目标器件的操作时,在DMA传输完毕的中断内开启I2C的BTF中断,并在I2C的BTF中断内再发出STOP信号。



您好,@雷龙飞-Logan.lei,麻烦具体说明一下你的程序如何测试使用,我这边打开你的程序发现没有做主从的区分,下载到测试板逻辑分析仪并没有采到波形,在收发通信时需要把另外收发代码注释掉么。可以跟我(lzs@wch.cn)描述一下使用方法,我这边具体测试一下。或我们V203 EVT有IIC DMA收发的例程,你也可以参考一下。


不用注释掉,我是CH32V203做主机,通过I2C2(PB10,PB11)对从机AW9523BTQR进行读写的。


您好,附件是修改后的例程,关于发送长度以及接收问题这边测试修改后是没有问题的,如下图。由于没有你所说的从机模块。例程是基于对EEPROM的读写测试的,所以对设备地址进行了修改。你拿回去测试时注意地址要改过来。

icon_rar.gifI2C2_DMA.zip

image.png


问题一。 打印读取到的I2C数据时, 第一个数据丢失。是因为DMA还没有传输完成。可以在设置标志位等传输完成在打印。


I2C状态转换,全放在中断里面进行。

I2C DMA主机模式测试没问题 。从机 MP4247可以正常通讯 。

I2C DMA从机模式不完整未测试。

大佬们可以优化一下。我这写的有点烂。


#define I2C_OUTTIME 70000


uint8_t i2cDataTx[Size] = { 0 };

uint8_t i2cDataRx[Size];


void I2C1_EV_IRQHandler(void) __attribute__((interrupt("WCH-Interrupt-fast")));

void DMA1_Channel7_IRQHandler(void) __attribute__((interrupt("WCH-Interrupt-fast")));

//void DMA1_Channel6_IRQHandler(void) __attribute__((interrupt("WCH-Interrupt-fast")));

void I2C1_ER_IRQHandler(void)  __attribute__((interrupt("WCH-Interrupt-fast")));


//波特率  本机地址 (1

void IIC_Init(u32 bound, uint8_t myadr)

{

    GPIO_InitTypeDef GPIO_InitStructure={0};

    I2C_InitTypeDef I2C_InitTSturcture={0};


    RCC_APB2PeriphClockCmd( RCC_APB2Periph_GPIOB, ENABLE );

    RCC_APB1PeriphClockCmd( RCC_APB1Periph_I2C1, ENABLE );

    RCC_AHBPeriphClockCmd( RCC_AHBPeriph_DMA1, ENABLE );


    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6;

    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_OD;

    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;

    GPIO_Init( GPIOB, &GPIO_InitStructure );


    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_7;

    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_OD;

    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;

    GPIO_Init( GPIOB, &GPIO_InitStructure );


    I2C_InitTSturcture.I2C_ClockSpeed = bound;

    I2C_InitTSturcture.I2C_Mode = I2C_Mode_I2C;

    I2C_InitTSturcture.I2C_DutyCycle = I2C_DutyCycle_2;

    I2C_InitTSturcture.I2C_OwnAddress1 = myadr?myadr<<1:MYAdderss<<1;

    I2C_InitTSturcture.I2C_Ack = I2C_Ack_Enable;

    I2C_InitTSturcture.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit;

    I2C_Init( I2C1, &I2C_InitTSturcture );


    DMA_InitTypeDef DMA_InitStructure={0};

    DMA_DeInit(DMA1_Channel7);


    DMA_InitStructure.DMA_PeripheralBaseAddr = ( u32 )&I2C1->DATAR;

    DMA_InitStructure.DMA_MemoryBaseAddr = ( u32)&i2cDataRx;

    DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;

    DMA_InitStructure.DMA_BufferSize = Size;

    DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;

    DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;

    DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;

    DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;

    DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;

    DMA_InitStructure.DMA_Priority = DMA_Priority_High;

    DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;

    DMA_Init(DMA1_Channel7, &DMA_InitStructure );


    DMA_DeInit(DMA1_Channel6);

    DMA_InitStructure.DMA_PeripheralBaseAddr = ( u32 )&I2C1->DATAR;

    DMA_InitStructure.DMA_MemoryBaseAddr =( u32)&i2cDataTx;

    DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST;

    DMA_InitStructure.DMA_BufferSize = Tize;

    DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;

    DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;

    DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;

    DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;

    DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;

    DMA_InitStructure.DMA_Priority = DMA_Priority_VeryHigh;//DMA_Priority_High;DMA_Priority_VeryHigh

    DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;

    DMA_Init( DMA1_Channel6, &DMA_InitStructure );


    I2C_DMACmd( I2C1, ENABLE );


    NVIC_InitTypeDef  NVIC_InitStructure = {0};

    NVIC_InitStructure.NVIC_IRQChannel = I2C1_EV_IRQn;//事件中断

    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;

    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 5;

    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;

    NVIC_Init(&NVIC_InitStructure);


    NVIC_InitStructure.NVIC_IRQChannel = DMA1_Channel7_IRQn;

    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2;

    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2;

//    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;

    NVIC_Init(&NVIC_InitStructure);


//    NVIC_InitStructure.NVIC_IRQChannel = DMA1_Channel6_IRQn;



    NVIC_InitStructure.NVIC_IRQChannel = I2C1_ER_IRQn;//错误中断

    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 7;                 //从优先级

    NVIC_Init(&NVIC_InitStructure);


//    DMA_ITConfig(DMA1_Channel7, DMA1_IT_TC1, ENABLE);

    DMA1_Channel7->CFGR|=0x0002;//接收完成中断

//    DMA1_Channel6->CFGR|=0x0002;//发送完成中断

    I2C_ITConfig(I2C1, I2C_IT_EVT, ENABLE);//I2C_IT_EVT 开启事件中断 I  2C_IT_BUF|接收发送缓冲器中断  I2C_IT_ERR 错误中断

    if(myadr)

    {

        I2C_GeneralCallCmd(I2C1,ENABLE);//响应广播

    }


    I2C_Cmd( I2C1, ENABLE );


//    I2C_AcknowledgeConfig( I2C1, ENABLE );

    Rst_i2c_salve();

}



uint8_t I2C_READ_REG8(uint8_t addr, uint8_t reg, uint8_t* rbuf, uint16_t rsize)

{

//[状态(bit7 1写读数据 0仅写数据 其他位为状态指示),发送数据尺寸H,发送数据尺寸L,读取数据尺寸H,读取数据尺寸L,发送地址   ,(写寄存器|读寄存器|指令|数据1),(数据2),...]

    static uint8_t data[6+2];


    uint32_t time_out=I2C_OUTTIME;

//    I2C1->CTLR1 |= (uint16_t)0x0001;

    while( I2C_GetFlagStatus( I2C1, I2C_FLAG_BUSY ) != RESET ) //等待I2C空闲

    {

        time_out--;

        if(time_out==0)

        {


            Rst_i2c_salve();


            return I2C_BUSY;

        }

    }

    data[0] = 0x80;//写读

    data[1] = 0;//发送数据字节数 高位

    data[2] = 1;//发送数据字节数 低位

    data[3] = (rsize>>8)&0xFF;//接收字节数 高位

    data[4] = rsize&0xFF;//接收字节数 低位

    data[5] = addr<<1;//从机地址

    data[6] = reg;//写数据



    //DMA接收设置

    DMA1_Channel7->CFGR &= (uint16_t)(~DMA_CFGR1_EN);//关闭DMA指定通道

    DMA1_Channel7->CNTR = 0;

    DMA1_Channel7->MADDR = (uint32_t)(rbuf);

    //DMA发送设置

    DMA1_Channel6->CFGR &= (uint16_t)(~DMA_CFGR1_EN);//关闭DMA指定通道

    DMA1_Channel6->CNTR = 7;

    DMA1_Channel6->MADDR = (uint32_t)(data);

    I2C_GenerateSTART(I2C1, ENABLE);//转换到主机模式  发起起始条件成功 SB状态置1  MSL状态置1表明当前为主机模式

    time_out = I2C_OUTTIME;

    while( !I2C_CheckEvent( I2C1, I2C_EVENT_MASTER_BYTE_TRANSMITTED ))//等待传输完成 BTF=1 TXE=1   MSL=1 TRA=1 BUSY=1

    {

        time_out--;

        if(time_out==0)

        {

            Rst_i2c_salve();

            return I2C_ERROR;

        }

    }

    return 0;

}


uint8_t I2C_WRITE_REG8(uint8_t addr, uint8_t reg, uint8_t*wbuf, uint16_t wsize)

{

//[状态(bit7 1写读数据 0仅写数据 其他位为状态指示),发送数据尺寸H,发送数据尺寸L,读取数据尺寸H,读取数据尺寸L,发送地址   ,(写寄存器|读寄存器|指令|数据1),(数据2),...]

    static uint8_t data[6+8];

    uint32_t i;

    uint32_t time_out=I2C_OUTTIME;


    wsize=wsize+1;

//    I2C1->CTLR1 |= (uint16_t)0x0001;

    while( I2C_GetFlagStatus( I2C1, I2C_FLAG_BUSY ) != RESET ) //等待I2C空闲

    {

        time_out--;

        if(time_out==0)

        {

//            if(DMA1_Channel6->CNTR==0 && DMA1_Channel7->CNTR==0)

//            {

            Rst_i2c_salve();

//            }

            return I2C_BUSY;

        }

    }


    if(wsize>8)

    {

        return 8;

    }

    data[0] = 0x00;//仅写


    data[1] = (wsize>>8)&0xFF;//发送数据字节数 高位

    data[2] = wsize&0xFF;//发送数据字节数 低位


    data[3] = 0;//接收字节数 高位

    data[4] = 0;//接收字节数 低位


    data[5] = addr<<1;//从机地址

    data[6] = reg;//从机寄存器地址或指令


    for (i = 0; i < wsize; ++i) {

        data[i+7] = wbuf[i];

    }



    DMA1_Channel6->CFGR &= (uint16_t)(~DMA_CFGR1_EN);//关闭DMA指定通道

    DMA1_Channel6->CNTR = wsize;

    DMA1_Channel6->MADDR = (uint32_t)(data);

    I2C_GenerateSTART(I2C1, ENABLE);


    return 0;

}


void I2C1_EV_IRQHandler(void)

{

    volatile uint32_t temp = 0;


    ///////////////DMA///////////////////

    static uint8_t *_status;

    volatile uint32_t temp1 = 0;

    volatile uint32_t temp2 = 0;


    temp1 = I2C1->STAR1;//读状态1寄存器

    temp2 = I2C1->STAR2;//读状态2寄存器

    temp2 = temp2<<16;

    temp = (temp1|temp2);


    if((temp&I2C_FLAG_MSL)==I2C_FLAG_MSL)//主机模式

    {

        if((temp&I2C_IT_SB))//起始条件已发出 开始发送从机地址

        {

            if(DMA1_Channel6->CNTR)//首次发送数据 更新保存 数据基址 读从机寄存器操作(先写指令再读数据)第二次起始条件发出时 不应该再触发该处

            {

                _status = (uint8_t *)DMA1_Channel6->MADDR;

                GPIO_WriteBit(GPIOB, GPIO_Pin_2, 1);//LED1 蓝色

            }


            I2C1->DATAR = _status[5];//bit0 0发送或读取地址bit0 1 写地址后会清除SB

            _status[0]=(_status[0]&0x80)|1;//更新状态 发送地址

            GPIO_WriteBit(GPIOB, GPIO_Pin_14, 0);//LED2 绿色

            // GPIO_WriteBit(GPIOB, GPIO_Pin_15, 0);//LED3 红灯


        }


//          ADDR 用户读取状态寄存器 1 后,对状态寄存器 2 的读操作将会清除此位

        if(temp&I2C_IT_ADDR)//从机地址匹配(应答) 开始写或者读数据

        {

            //开始DMA传输

            if(DMA1_Channel6->CNTR)//发送

            {

//                [状态(bit7 1写读数据 0仅写数据 其他位为状态指示),发送数据尺寸H,发送数据尺寸L,读取数据尺寸H,读取数据尺寸L,发送地址,(写寄存器|读寄存器|指令|数据1),(数据2),...]

                DMA1_Channel6->CFGR &= (uint16_t)(~DMA_CFGR1_EN);//关闭DMA指定通道


                if((_status[0]&0x80))//写读

                {

                    _status[5] = _status[5]|0x0001;//转换为读地址

                    _status[0] = (_status[0]&0x80)|2;//更新状态为读 等待数据传输完成

                    DMA1_Channel7->CNTR = (_status[3]<<8)|_status[4];

                    GPIO_WriteBit(GPIOB, GPIO_Pin_2, 0);//LED1 蓝色



                }else {//仅写

                    _status[0]=(_status[0]&0x80)|3;//更新状态为仅写  等待数据传输完成


                }

                DMA1_Channel6->CNTR = (_status[1]<<8)|_status[2];

                DMA1_Channel6->MADDR = (uint32_t)(_status+6);

                DMA1_Channel6->CFGR |= DMA_CFGR1_EN;//开启DMA指定通道


            }else if(DMA1_Channel7->CNTR)//接收

            {

                DMA1_Channel6->CFGR &= (uint16_t)(~DMA_CFGR1_EN);//关闭DMA指定通道

//                I2C1->CTLR1 = ((uint16_t)0x0401);//开启应答

                I2C1->CTLR1 |= ((uint16_t)0x0400);//开启应答

                I2C1->CTLR2 |= ((uint16_t)0x1000);//主机接收时 DMA传输 最后一位NACK DMA接收不触发BTF 需要开启DMA传输完成中断处理发送停止事件

                DMA1_Channel7->CFGR |= DMA_CFGR1_EN;//开启DMA指定通道

                _status[0]=(_status[0]&0x80)|4;

            }

        }



//        BTF字节发送结束标志位,用户读取状态寄存器 1后,对数据寄存器的读写将清除此位;

        if(temp&I2C_IT_BTF)//数据传输完成 ->  一个数据传输完成 (硬件延时超时) (发送 数据寄存器空) (接收 数据寄存器有数据未被读取)触发完成事件

        {

            (void)I2C1->DATAR;


            switch (_status[0]&0x7F) {

                case 1://地址发送完成 等待地址匹配 在此不做任何事情

                    break;

                case 2://写读数据状态 开始读

                    DMA1_Channel7->CFGR &= (uint16_t)(~DMA_CFGR1_EN);//关闭DMA指定通道

//                    I2C1->CTLR1 = ((uint16_t)0x0101);//再次发出开始信号 准备读取

                    I2C1->CTLR1 |= ((uint16_t)0x0100);//再次发出开始信号 准备读取

                    DMA1_Channel6->CNTR = (_status[3]<<8)|_status[4];//读取长度

                    _status[0]=(_status[0]&0x80)|4;

                    break;

                case 3://仅写数据   完成

                    _status[0]=(_status[0]&0x80)|4;

//                    GPIO_WriteBit(GPIOB, GPIO_Pin_2, 1);//LED1 蓝色

                case 4:// 读写完成


//                     I2C1->CTLR1 = ((uint16_t)0x0201);

                     I2C1->CTLR1 ^=((uint16_t)0x0400);// 取消应答

                     I2C1->CTLR1 |= ((uint16_t)0x0200);// 发起停止事件 转变为从机模式

                     _status[0]=(_status[0]&0x80)|5;

                     DMA1_Channel6->CFGR &= (uint16_t)(~DMA_CFGR1_EN);//关闭DMA指定通道

                     DMA1_Channel6->CNTR = 0;

//                     DMA1_Channel7->CFGR &= (uint16_t)(~DMA_CFGR1_EN);//关闭DMA指定通道

//                     if((_status[0]&0x7F)==4)GPIO_WriteBit(GPIOB, GPIO_Pin_14, 1);//LED2 绿灯

                    break;

                default:

                    break;

            }


        }



    }else {//从机模式 I2C_IT_ADDR  I2C_IT_BTF  I2C_IT_STOPF

//        if(temp&I2C_IT_BTF)

//        {

//

//        }

        if((temp&I2C_IT_STOPF)||(temp&I2C_IT_BTF))//从机检测到停止事件 或 发送完成标志

        {

            DMA1_Channel6->CFGR &= (uint16_t)(~DMA_CFGR1_EN);//关闭DMA指定通道

            DMA1_Channel7->CFGR &= (uint16_t)(~DMA_CFGR1_EN);//关闭DMA指定通道

        }


        if(temp&I2C_IT_ADDR)

        {

            //主机请求数据 重复的起始条件、停止条件会清除 仲裁丢失、关闭I2C 该位硬件清零

            if(temp&I2C_FLAG_TRA)//主机请求数据 根据接收的值准备数据上传

            {

                //从机 DMA发送数据 外部设置对应数据源

//               DMA1_Channel6->CFGR &= (uint16_t)(~DMA_CFGR1_EN);//关闭DMA指定通道

//               DMA1_Channel6->MADDR = ( u32)&i2cDataTx;

//               DMA1_Channel6->CNTR = Tize;

               DMA1_Channel6->CFGR |= DMA_CFGR1_EN;//开启DMA指定通道

            }else//主机写数据 从机接收

            {

                /* 准备接收数据 设置大一点的缓冲区 */

                DMA1_Channel7->MADDR = ( u32)&i2cDataRx;

                DMA1_Channel7->CNTR = Size;

                DMA1_Channel7->CFGR |= DMA_CFGR1_EN;

            }

        }


    }

//////////////////DMA///////////////////


}


//SCL为高电平时,SDA由高变低表示起始信号;

//

//SCL为高电平时,SDA由低变高表示停止信号;

//

//起始信号和停止信号都是由主机发出,起始信号产生后总线处于占用状态,停止信号产生后总线被释放,处于空闲状态。


// 错误中断

void I2C1_ER_IRQHandler(void)

{

    uint32_t temp = 0;


    I2C_GenerateSTOP(I2C1, ENABLE);

    DMA1_Channel6->CFGR &= (uint16_t)(~DMA_CFGR1_EN);//关闭DMA指定通道

    DMA1_Channel7->CFGR &= (uint16_t)(~DMA_CFGR1_EN);//关闭DMA指定通道

    temp = I2C1->STAR1;

    I2C_Cmd(I2C1, DISABLE);

    if(temp&0x0400)//应答错误

    {

        GPIO_WriteBit(GPIOB, GPIO_Pin_2, 0);//LED1 蓝色

    }else if (temp&0x0100)//仲裁丢失

    {


    }else if (temp&0x4000)//超时

    {

        GPIO_WriteBit(GPIOB, GPIO_Pin_15, 1);//LED3 红灯

    }else if (temp&0x0200)//过载  禁止时钟延长条件下

    {


    }else {


    }



    I2C_Cmd(I2C1, ENABLE);


}

//传输完成的回调函数

//extern void I2C_SlaveDMARxCpltCallback();


//I2C DMA接收

void DMA1_Channel7_IRQHandler(void)

{

    if(DMA_GetITStatus(DMA1_IT_TC7)==SET)

    {

        //        从机 状态下 应该解析更新数据

        //        I2C_SlaveDMARxCpltCallback();

        I2C1->CTLR1 ^=((uint16_t)0x0400);// 取消应答

        I2C1->CTLR1 |= ((uint16_t)0x0200);// 发起停止事件 转换为从机模式  静默

        DMA1_Channel7->CFGR &= (uint16_t)(~DMA_CFGR1_EN);//关闭dma指定通道


        DMA_ClearITPendingBit(DMA1_IT_TC7);

        GPIO_WriteBit(GPIOB, GPIO_Pin_14, 1);//LED2 绿色


    }

}

//I2C DMA 发送完成

//void DMA1_Channel6_IRQHandler(void)

//{

//    if(DMA_GetITStatus(DMA1_IT_TC6)==SET)

//    {

//        DMA1_Channel6->CFGR &= (uint16_t)(~DMA_CFGR1_EN);//关闭dma指定通道

//        DMA_ClearITPendingBit(DMA1_IT_TC6);

//

//    }

//}




//主机模式 尝试复位IIC

void Rst_i2c_salve()

{

    uint8_t n = 9;

//    GPIOB->CFGLR &= 0x00FFFFFF;// 复位

//    GPIOB->CFGLR |= (uint32_t)0x44000000;//输入模式 PB6 PB7

//    printf("IDR:0x%08X  GPIOB:0x%08X\n",GPIOB->INDR,GPIOB->CFGLR);

    if((GPIOB->INDR&0x00000080)==0)//读取SDA 总线 如果被拉低

    {

        I2C_GenerateSTART( I2C1, ENABLE );

        Delay_Ms(5);

        I2C_GenerateSTOP( I2C1, ENABLE );

        Delay_Ms(20);

        if((GPIOB->INDR&0x00000080))// SDA 为高 退出

        {

            return;

        }


        I2C_Cmd( I2C1, DISABLE );


        GPIOB->CFGLR &= 0x00FFFFFF;// 复位

//        GPIOB->CFGLR |= (uint32_t)0x33000000;//输出模式 PB6 PB7 通用推挽输出 50 MHz

        GPIOB->CFGLR |= (uint32_t)0x77000000;//输出模式 PB6 PB7 通用开漏输出 50 MHz

//        GPIOB->OUTDR |= 0x000000C0;

        while(n>0)//发送9个时钟信号

        {

            GPIOB->OUTDR |= 0x00000040;//PB6 SCL 拉高

            Delay_Ms(5);

            GPIOB->OUTDR &= ~0x00000040;// SCL 拉低

            n--;

            if((GPIOB->INDR&0x00000080))// SDA 为高 退出

            {

                break;

            }

        }


//        GPIOB->OUTDR &= ~0x00000080; //PB7 SDA 拉低

//        GPIOB->OUTDR |= 0x00000040;//SCL 拉高

//        Delay_Ms(20);

//        GPIOB->OUTDR |= 0x00000080; //PB7 SDA 拉高


    //    GPIOB->CFGLR &= 0x00FFFFFF;// 复位

        GPIOB->CFGLR |= 0xFF000000;// 复用 开漏输出 50 MHz

        I2C1->STAR1 = 0;

        I2C1->STAR2 = 0;

        I2C_Cmd( I2C1, ENABLE );

    }else if ((GPIOB->INDR&0x00000040)==0){//SCL 总线被拉低 尝试 恢复

        I2C_GenerateSTOP( I2C1, ENABLE );

        Delay_Ms(10);

        I2C_Cmd( I2C1, DISABLE );

        I2C1->STAR1 = 0;

        I2C1->STAR2 = 0;

        while((GPIOB->INDR&0x00000040)==0);//等待SCL总线 拉起

//        Delay_Ms(500);

        I2C_Cmd( I2C1, ENABLE );


    }else {

        I2C_Cmd( I2C1, DISABLE );

        I2C1->DATAR = 0;

        Delay_Ms(100);

        I2C_Cmd( I2C1, ENABLE );

    }

}



只有登录才能回复,可以选择微信账号登录