STM32H7的硬件IIC+DMA,采用查询法,实现与EEPROM的IIC通讯

1 篇文章 0 订阅
1 篇文章 0 订阅
本文档详细介绍了如何使用STM32H7的IIC和DMA功能来实现对EEPROM的高效读写操作。代码中包含了IIC初始化、DMA配置以及读写驱动函数,特别指出DMA缓存地址需设置在特定区域。此外,还提供了一个查询函数用于确认EEPROM是否完成数据写入。
摘要由CSDN通过智能技术生成

        基于STM32H723的硬件IIC+DMA,实现对EEPROM的写入及读取操作,建议配合数据手册区查看代码帮助理解,使用DMA时,注意把DMA设置的缓存地址设置在0x24000000以后的区域,DMA才能够访问。

/**************************************************************************
*EEPROM的IIC驱动初始化,带DMA
*PF0----------------------IIC2_SDA_EEPROM
*PF1----------------------IIC2_SCL_EEPROM
*PF2----------------------WP_EEPROM
*
*
**************************************************************************/
uint8_t IIC_And_DMA_Driver_With_EEPROM_Init(void)
{
	DMA_InitTypeDef DMA_InitStruct;
	
	RCC->AHB4ENR  |= 1 << 5;        //使能GPIOF时钟																												      
	RCC->APB1LENR |= 1 << 22;       //使能I2C2外设时钟
    /*GPIO配置*/
	GPIO_Set(GPIOF,1 << 0,GPIO_MODE_AF,GPIO_OTYPE_OD,GPIO_SPEED_LOW,GPIO_PUPD_PU);	//PF0
    GPIO_Set(GPIOF,1 << 1,GPIO_MODE_AF,GPIO_OTYPE_OD,GPIO_SPEED_LOW,GPIO_PUPD_PU);	//PF1
    GPIO_Set(GPIOF,1 << 2,GPIO_MODE_OUT,GPIO_OTYPE_PP,GPIO_SPEED_LOW,GPIO_PUPD_PU);//PF2
    GPIO_AF_Set(GPIOF,0,4);	
    GPIO_AF_Set(GPIOF,1,4);	
    GPIO_Pin_Set(GPIOF,PIN2,0);	
    //I2C 时钟来源默认100M 来自PCLK1
    RCC->APB1LRSTR |= 1 << 22;
    RCC->APB1LRSTR &= ~(1 << 22);	
    I2C2->CR1 |= 0 << 0;
    I2C2->TIMINGR = 0x20601138 & 0xF0FFFFFF;
    //此处将IIC的时钟频率设置为400K,如果要设置为100K,则推荐0x20601138,具体配置方法可查数据手册
    I2C2->CR2 |= ((0xA0 >> 1) & 0x7F) << 1;//这里设置的IIC从机的地址
    I2C2->CR2 |= 1 << 10;
    I2C2->OAR1 = ((0 << 15) | (0));						//失能自身地址1 自身地址设为0 
    I2C2->CR2 |= (1 << 15);				//使能NACK,当作为从机时,需要失能NACK			
    I2C2->CR2 &= ~(1 << 25);									//失能AUTOEN
    I2C2->OAR2 = ((0 << 15) | (0) | (0 << 8));//失能自身地址2 自身地址设为0 无屏蔽位
    I2C2->CR1 = ((0 << 19) | (0 << 17));			//失能广播呼叫功能,时钟延长
    I2C2->CR1 |= 1 << 15;											//使能DMA接收请求
    I2C2->CR1 |= 1 << 14;											//使能DMA发送请求
    I2C2->CR1 |= 1 << 0;
    /*以上是IIC的寄存器配置,400K,以下则是DMA的配置,这里我用了自己仿ST标准库实现DMA配置*/
    DMA_InitStruct.u32DMA_NumberData = 0;		
    DMA_InitStruct.u32DMA_Memory1BaseAddr=(uint32_t)u8IIC_DMA_Transmit_For_EEPROM_Buffer;			    
    //DMA存储器地址		
    DMA_InitStruct.u32DMA_PeripheralBaseAddr = (uint32_t)&I2C2->TXDR;//外设地址	
    DMA_InitStruct.u8DMA_Channel = 36;//触发方式,需要查数据手册的DMA映射表	
    DMA_InitStruct.u8DMA_DIR = DMA_DIR_MemoryToPeripheral;	
    DMA_InitStruct.u8DMA_DoubleBufferMode = DMA_DoubleBuffer_Disable;	
    DMA_InitStruct.u8DMA_MemoryBurstTransfer = DMA_MemoryBurst_Single;
    DMA_InitStruct.u8DMA_MemoryDataSize	= DMA_MemoryDataSize_Byte;
    DMA_InitStruct.u8DMA_MemoryInc = DMA_MemoryInc_Enable;
    DMA_InitStruct.u8DMA_Mode = DMA_Mode_Normal;
    DMA_InitStruct.u8DMA_PeripheralBurstTransfer = DMA_PeripheralBurst_Single;
    DMA_InitStruct.u8DMA_PeripheralDataSize	= DMA_PeripheralDataSize_Byte;
    DMA_InitStruct.u8DMA_PeripheralInc = DMA_PeripheralInc_Disable;
    DMA_InitStruct.u8DMA_Priority = DMA_Priority_High;
    DMA_Driver_Init(DMA2_Stream6,&DMA_InitStruct);

    DMA_InitStruct.u32DMA_Memory1BaseAddr= (uint32_t)u8IIC_DMA_Receive_For_EEPROM_Buffer;			    
    //DMA存储器地址
    DMA_InitStruct.u32DMA_PeripheralBaseAddr = (uint32_t)&I2C2->RXDR;//外设地址
    DMA_InitStruct.u8DMA_Channel = 35;
    DMA_InitStruct.u8DMA_DIR = DMA_DIR_PeripheralToMemory;
    DMA_Driver_Init(DMA2_Stream7,&DMA_InitStruct); 
    return 1;
}






        以上便是IIC2主机+DMA的寄存器配置实现,接下来是IIC的主机发送代码实现

        

/*******************************************************************************
** 函数名称:IIC_DMA_Transmit_For_EEPROM_Driver
** 函数作用:IIC向EEPROM发送数据驱动
** 输入参数:u32EEPROM_Address------------访问的EEPROM地址
**           u8Length---------------------写入EEPROM的数据长度
**           pBuffer----------------------写入EEPROM的数据存储指针
** 输出参数:0------------------失败
**           1------------------成功
** 使用样例:无
** 函数备注:无
*******************************************************************************/
uint8_t IIC_DMA_Transmit_For_EEPROM_Driver(uint32_t u32EEPROM_Address,uint8_t                   u8Length,uint8_t* pBuffer)
{
    uint16_t t_u16WaitTime = 0;
    if(u8Length > EEPROM_TRANSMIT_BUFFER_MAX_SIZE)
    {
		return 0;//当发送的数据长度大于发送DMA缓存空间时,直接返回失败
	}
    I2C2->ICR |= 1 << 4;                  //清楚否应答标志
    I2C2->ICR |= 1 << 5;				//清除停止位产生标志
    I2C2->CR2 &= ~(1 << 10);	//EEPROM写指令
    I2C2->CR2 &= ~(0xFF << 16);			//清除Nbytes
    I2C2->CR2 |= 2 << 16;			//2个字节待传输,发送要写入的EEPROM地址
    I2C2->CR2 |= 1 << 24;			//重载模式
    I2C2->CR2 |= 1 << 25;			//自动发送停止位
    I2C2->CR2 |= 1 << 13;			//产生起始位+地址
    while((I2C2->ISR & 1 << 1) != (1 << 1))//等待发送完成
    {
        /*在这里实现超时判断,有用操作系统的,可以用操作系统的延时函数*/
    }
    I2C2->TXDR = (uint8_t)(u32EEPROM_Address >> 8);
    t_u16WaitTime = 0;
    while((I2C2->ISR & 1 << 1) != (1 << 1))//等待发送完成
	{
		/*在这里实现超时判断,有用操作系统的,可以用操作系统的延时函数*/
	}
    I2C2->TXDR = (uint8_t)u32EEPROM_Address;
	t_u16WaitTime = 0;
	while((I2C2->ISR & 1 << 7) != (1 << 7))//等待发送完成
	{
		/*在这里实现超时判断,有用操作系统的,可以用操作系统的延时函数*/
	}
    I2C2->CR2 &= ~(0xFF << 16);			//清除Nbytes
	I2C2->CR2 |= u8Length << 16;			//2个字节待传输
	I2C2->CR2 &= ~(1 << 24);			//重载模式
	SCB_CleanDCache();
	DMA2_Stream6->CR &= ~(1 << 0);
	t_u16WaitTime = 0;
    while(DMA2_Stream6->CR&0X01)//等待DMA可被配置
	{
		/*在这里实现超时判断,有用操作系统的,可以用操作系统的延时函数*/
	}
    DMA2->HIFCR |= 0x3D << 16;								//清空该Stream上的所有中断标志
	memcpy(u8IIC_DMA_Transmit_For_EEPROM_Buffer,pBuffer,u8Length);
	DMA2_Stream6->NDTR  = u8Length;								//发送数量为0
	DMA2_Stream6->CR |= 1 << 0;									//复位CR寄存器
	t_u16WaitTime = 0;
    while(DMA2_Stream6->NDTR != 0)//等待DMA发送完成
    {
		/*在这里实现超时判断,有用操作系统的,可以用操作系统的延时函数*/
	}
    t_u16WaitTime = 0;
	I2C2->ICR |= 1 << 5;						//清除停止位产生标志
	DMA2->HIFCR |= 0x3D << 16;	
	return 1;
}

        以上是向EEPROM写入数据的驱动,接下来则是实现从EEPROM读取数据的接收驱动

/*******************************************************************************
** 函数名称:IIC_DMA_Receive_For_EEPROM_Driver
** 函数作用:IIC从EEPROM接收数据驱动
** 输入参数:u32EEPROM_Address------------访问的EEPROM地址
**           u8Length---------------------写入EEPROM的数据长度
**           pBuffer----------------------写入EEPROM的数据存储指针
** 输出参数:0------------------失败
**           1------------------成功
** 使用样例:无
** 函数备注:无
*******************************************************************************/
uint8_t IIC_DMA_Receive_For_EEPROM_Driver(uint32_t u32EEPROM_Address,uint8_t u8Length,uint8_t * pBuffer)
{
    static uint8_t * pAllocateMemory;
	uint16_t t_u16WaitTime = 0;
    I2C2->ICR |= 1 << 4;                  //清楚否应答标志
	I2C2->ICR |= 1 << 5;						//清除停止位产生标志
    I2C2->CR2 &= ~(1 << 10);				//写
	I2C2->CR2 &= ~(0xFF << 16);			//2个字节待传输
	I2C2->CR2 |=2 << 16;						//2个字节待传输
	I2C2->CR2 &= ~(1 << 24);				//重载模式
	I2C2->CR2 &= ~(1 << 25);				//不发送停止位,延迟SCL
	I2C2->CR2 |= 1 << 13;						//产生起始位+地址
	t_u16WaitTime = 0;
    while((I2C2->ISR & 1 << 1) != (1 << 1))
	{
		//等待IIC发送完成,这里没有用DMA发送,这里需自己实现超时退出
	}
    I2C2->TXDR = (uint8_t)(u32EEPROM_Address >> 8);;
	t_u16WaitTime = 0;
	while((I2C2->ISR & 1 << 1) != (1 << 1))
	{
		//等待IIC发送完成,这里没有用DMA发送,这里需自己实现超时退出
	}
    I2C2->TXDR = (uint8_t)u32EEPROM_Address;
	t_u16WaitTime = 0;
	while((I2C2->ISR & 1 << 6) != (1 << 6))
	{
		//等待IIC发送完成,这里没有用DMA发送,这里需自己实现超时退出
	}
    pAllocateMemory = malloc(u8Length);//动态分配DMA接收缓存
    memset(pAllocateMemory,0,u8Length);
	DMA2_Stream7->CR &= ~(1 << 0);
	t_u16WaitTime = 0;
	while(DMA2_Stream7->CR&0X01)
	{
		//等待DMA可被配置,这里没有用DMA发送,这里需自己实现超时退出
	}
    DMA2->HIFCR |= 0x3D << 22;	
    DMA2_Stream7->M0AR = (uint32_t)pAllocateMemory;
	DMA2_Stream7->NDTR  = u8Length;		
	DMA2_Stream7->CR |= 1 << 0;	
	I2C2->CR2 |= (1 << 10);					//EEPROM读指令
	I2C2->CR2 &= ~(0xFF << 16);			//清空NBYTES位
	I2C2->CR2 |= u8Length << 16;				//127个字节待读取
	I2C2->CR2 |= 1 << 13;						//产生起始位+地址
	I2C2->CR2 |= (1 << 25);				//AutoEnd
	t_u16WaitTime = 0;
    while(DMA2_Stream7->NDTR != 0)
	{
		//等待DMA接收完成
	}
    SCB_CleanInvalidateDCache();
	memcpy(pBuffer,pAllocateMemory,u8Length);
    free(pAllocateMemory);
	DMA2->HIFCR |= 0x3D << 22;	
	return 1;
}

        由于向EEPROM写入数据时,当发送停止位后,EEPROM才真正开始将缓存里的数据写入EEPROM的真实地址,所以在这里加一个查询EEPROM是否完成真正从EEPROM写入数据的查询判断

uint8_t IIC_Wait_EEPROM_Write_Into_Memory(void)
{
    uint16_t t_u16WaitTime = 0;
    static uint8_t u8OperateStep = 0;
    static uint8_t u8Counters = 0;
    while(1)
    {
        switch(u8OperateStep)
        {
            case 0:
            {
                I2C2->ICR |= 1 << 4;
                I2C2->ICR |= 1 << 5;				//清除停止位产生标志
                I2C2->CR2 &= ~(1 << 10);		    //EEPROM写指令
                I2C2->CR2 &= ~(0xFF << 16);	        //0个字节待传输,发送要写入的EEPROM地址
                I2C2->CR2 &= ~(1 << 24);		    //重载模式
                I2C2->CR2 &= ~(1 << 25);		    //不生成停止位
                I2C2->CR2 |= 1 << 13;				//产生起始位+地址
                u8OperateStep = 1;
                break;
            }
            case 1:
            {
                if((I2C2->ISR & (1 << 4)) != (1 << 4))//接收到应答,允许继续向EEPROM操作
                {
                    u8Counters = 0;
                    u8OperateStep = 0;
                    return 1;
                }
                else
                {
                    if(++u8Counters > 60)
                    {
                        u8Counters = 0;
                        u8OperateStep = 0;
                        return 0;
                    }
                    else
                    {
                        I2C2->ICR |= 1 << 4;
                        I2C2->ICR |= 1 << 5;				//清除停止位产生标志
                        I2C2->CR2 &= ~(1 << 10);		    //EEPROM写指令
                        I2C2->CR2 &= ~(0xFF << 16);//0个字节待传输,发送要写入的EEPROM地址
                        I2C2->CR2 &= ~(1 << 24);		    //重载模式
                        I2C2->CR2 &= ~(1 << 25);		    //不生成停止位
                        I2C2->CR2 |= 1 << 13;				//产生起始位+地址
                    }
                    }
                break;
            }
            default:
            {
                
                break;
            }
        }
        //在这里可以增加操作系统的延时函数
    }
}

        以上便是STM32H7使用硬件IIC实现对EEPROM的读写操作,并使用查询法查询EEPRO是否完成真正的数据写入

  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
STM32 HAL库下实现硬件IIC DMA控制0.96寸OLED屏,可以按照以下步骤进行: 1. 配置IIC接口:使用HAL库提供的I2C功能函数,初始化IIC接口并配置相关参数,例如时钟频率、地址等等。可以通过HAL_I2C_Init()函数完成此步骤。 2. 配置DMA:使用HAL库提供的DMA功能函数,初始化DMA控制器,并配置相应的通道和数据传输方向。可以通过HAL_DMA_Init()函数完成此步骤。 3. 配置OLED屏:根据OLED屏的规格和数据手册,设置正确的OLED屏地址和显示模式等参数。 4. 编写DMA传输函数:使用HAL库提供的DMA传输函数,编写发送和接收数据的函数。可以通过HAL_DMA_Transmit()和HAL_DMA_Receive()函数来实现。 5. 编写显示函数:根据OLED屏的规格和显示方式,编写显示函数。可以使用HAL库提供的IIC发送函数,将相关数据发送给OLED屏进行显示。 6. 调用相关函数:在主函数中,调用初始化函数和显示函数,完成整个过程。可以使用HAL库提供的循环发送函数,实现周期性的OLED屏数据显示。 需要注意的是,在实现过程中,需要合理配置DMA传输的通道和缓冲区大小,保证数据的正确传输。并且,应遵循相应的时序规范,确保数据传输的准确性和稳定性。 总结:以上是一种实现硬件IIC DMA控制0.96寸OLED屏的简要步骤。通过使用STM32 HAL库提供的相关功能函数,结合正确的配置和调用,可以实现相应的功能。具体的实现过程中,可能还需要根据具体的硬件和使用环境进行一些适当的修改和调整。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

醉心客丶

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值