12:HAL----I2C

目录

一:I2C通信协议

1:I2C简历

2:硬件电路

3:I2C时序基本单元

A : 开/ 终条件

2:发送一个字节

3:接收一个字节

4:应答机制 

4:I2C时序 

1:指定地址写

2:当前地址读

3: 指定地址读

二:HAL库

A:轮询方式

B:中断方式

三:案例

A:轮询方式--24C02

B:轮询方式---BH1750

C:中断方式---24C02

D:中断方式--BH1750​编辑

E:MDA---BH1750+24C02

F:SEQ传输数据换向

一主多从模式

A:一主二从--单帧

B:一主二从--两帧

C:解决最后一阵必须是接收的情况

D:十位寻址

E:从机双地址寻址

F:主机广播

 G:中断

H:I2C_OTHER_FRAME

I:一个板子上面的主从机


一:I2C通信协议

STM32F1C8T6有2个I2C接口,分别是I2C1和I2C2

1:I2C简历

I2C(Inter IC Bus)是由Philips公司开发的一种通用数据总线

两根通信线:SCL(Serial Clock)、SDA(Serial Data)

同步,半双工

带数据应答

支持总线挂载多设备(一主多从、多主多从)

        许多外设都遵守I2C的通信协议,eg:  上面图片中的: MPU6050,OLED,  AT24C02, DS3231模块

        SCL: 时钟     SDA:数据  主机对CS具有完全的控制权

        半双工 : 一根数据线负责发送和接收数据,   eg:I2C通信的SDA线

        同步: 接收方可以在时钟信号的指引下进行采样

        我们一般使用的为一主多从,下面讲的也为一主多从

2:硬件电路

所有I2C设备的SCL连在一起,SDA连在一起

设备的SCL和SDA均要配置成开漏输出模式

SCL和SDA各添加一个上拉电阻,阻值一般为4.7KΩ左右

下面为一主多从模式下的I2C通信

 主机:

        A : 对SCL线的完全控制--------任何时候,都是主机完全掌控SCL线

        B: 在空闲状态下,主机可以主动发起对SDA的控制,     只有在从机发送数据和从机应答的时候,  主机才会转交SDA的控制权给从机

从机:

         A  : 从机不允许控制SCL线------对于SCL时钟线,在任何时刻都只能被动的读取

        B : 对于SDA数据线,从机不允许主动发起对SDA的控制, -----只有在主机发送读取从机的命令后,或者从机应答的时候,  从机才能短暂地取得SDA的控制权

 弱上拉---弹簧杆子模型

        只允许向下拉,不允许向上拉.

        向下拉为--低电频;   不拉----高电频

线与现象:

        A : 只要有任意一个或多个从机设备输出了低电平,  总线就处手低电平

        B : 只有所有设备都输出高电平, 总线才处手高电平

优点

        辟免了引脚模式的频繁切换

        完全杜绝了电源短路现象,保证电路的安全

3:I2C时序基本单元

A : 开/ 终条件

        起始条件:SCL高电平期间,SDA从高电平切换到低电平

        终止条件:SCL高电平期间,SDA从低电平切换到高电平

        起始和终止,都是由主机产生的 ,  从机不允许产生起始和终止.  在总线空闲状态时,从机必须始终双手放开,   不允许主动跳出来,去碰总线

2:发送一个字节

        主机发送一个字节:SCL低电平期间,主机将数据位依次放到SDA线上(高位先行),然后主机释放SCL,从机将在SCL高电平期间读取数据位,所以SCL高电平期间SDA不允许有数据变化,依次循环上述过程8次,即可发送一个字节

注意: 这里是高位先行,  所以第一位是一个字节的最高位B7, 然后依次是次高B6 B5..........B0

        和串口是不一样的,  串口时序是低位先行,这里I2C是高位先行

        由于这整个时序是主机发送一个字节---------所以在这个单元里,SCL和SDA全程都由主机掌控------只有在从机发送数据和从机应答的时候,  主机才会转交SDA的控制权给从机----- 从机不允许控制SCL线------对于SCL时钟线,在任何时刻都只能被动的读取

        SCL全程由主机控制

3:接收一个字节

        主机接收一个字节:SCL低电平期间,从机将数据位依次放到SDA线上(高位先行),然后主机释放SCL,主机将在SCL高电平期间读取数据位,所以SCL高电平期间SDA不允许有数据变化,依次循环上述过程8次,即可接收一个字节(主机在接收之前,需要释放SDA) 

         主机在接收之前,需要释放SDA------这时候是从机发送数据需要在SCL低电频期间把数据放在SDA上面, 把SDA的控制器交给从机;        主机释放数据的实质就是将SDA置为高电频;


void MyI2C_W_SDA(uint8_t BitValue)
{
	GPIO_WriteBit(GPIOB, GPIO_Pin_11, (BitAction)BitValue);
	Delay_us(10);
} 
MyI2C_W_SDA(1); //从机发送数据,主机需要把SDA的控制权给从机

        SCL全程由主机控制

综上所述:  不管是主机发送数据, 还是接受数据; 都是在SCL低电平期间放数据,  高电平期间读取数据;   不过接就是在主机读取数据是要把SDA的控制权交给从机---SDA置1;         全部的程序我们只需要操作主机, 从机不需要我们操作, ( 我们只写主机的代码)


void MyI2C_W_SDA(uint8_t BitValue)
{
	GPIO_WriteBit(GPIOB, GPIO_Pin_11, (BitAction)BitValue);
	Delay_us(10);
} 
MyI2C_W_SDA(1); //从机发送数据,主机需要把SDA的控制权给从机

4:应答机制 

谁发送数据,谁接收应答

        主机发送应答:主机在接收完一个字节之后,在下一个时钟发送一位数据(应答数据),数据0表示应答,数据1表示非应答。

          从机给主机发送数据, 主机需要应答从机,  看还需不需要从机继续给主机发送数据

        数据0表示应答,数据1表示非应答------0把主机SCL时钟线拉低,,使得从机可以继续把数据放在SDA数据上;     1:把主机SCL时钟线拉高,  从机不能在SDA数据线上放数据. 主机可以读取数据,    主机也可以在CSL高电频期间给SDA一个上升沿结束

        主机接收应答:主机在发送完一个字节之后,在下一个时钟接收一位数据(应答数据),判断从机是否应答,数据0表示应答,数据1表示非应答(主机在接收之前,需要释放SDA

        主机给从机发送数据, 从机给主机应答, 看还需不需要主机继续给从机发送数据

        数据0表示应答,数据1表示非应答------0把主机SCL时钟线拉低,,使得主机可以继续把数据放在SDA数据上;     1:把SCL时钟线拉高,  从机不能在SDA数据线上读取数据. 从机读取数据,   主机也可以在CSL高电频期间给SDA一个上升沿结束

4:I2C时序 

Restart 信号----改变传输方向

1:指定地址写

       指定地址写----主机在指定的地址对从机写入数据

        对于指定设备(Slave Address),在指定地址(Reg Address)下,写入指定数据(Data)

        首先把每个从设备都确定一个唯一的设备地址,  从机设备地址就相当于每个设备的名字,   主机在起始条件之后,要先发送一个字节叫一下从机名字,   所有从机都会收到第一个字节,和自己的名字进行比较

        从机设备地址,在12C协议标准里分为7位地址和10位地址--说下7位地址的模式

          起始条件:SCL高电平期间,SDA从高电平切换到低电平

        第一个字节: 前七位写入要写从机的地址, 确定给谁写入数据.  第8位: 读写位---0表示,之后的时序主机要进行写入操作,   1表示,之后的时序主机要进行读出操作.(

第8位:

        0:主机------>从机   发送数据。

        1:从机------>主机   发送数据。

        应答位 : 在每一个字节完成后都需要一个应答位,    这里是主机给从机发送数据, 从机给主机应答, 看还需不需要主机继续给从机发送数据-----------在这个时刻,主机要释放SDA,释放SDA之后,引脚电平回弹到高电平,上升沿;   从机要在这个位拉低SDA,下降沿;   根据线与的特点为低电频.   这个过程,就代表从机产生了应答-------最终高电平期间,主机读取SDA,发现是0,就说明,我进行寻址,有人给我应答了,传输没有问题;   如果主机读取SDA,发现是1,说明进行寻址,应答位期间,我松手了,没人拽住它,没人给我应答,直接产生停止条件吧,并提示一些信息。

        第二个字节 : 就可以送到指定设备的内部了, 从机设备可以自己定义第二个字节和后续字节的用途.    一般第二个字节可以是寄存器地址或者是指令控制字等  eg:主机向从机发送了0x19这个数据,  在MPU6050里,就表示要操作你0x19地址下的寄存器了

        第三个字节 :这个字节就是主机想要写入到0x19地址下寄存器的内容了

这个数据帧的目的就是,对于指定从机地址为1101000的设备,  在其内部0x19地址的寄存器中,写入0xAA这个数据

2:当前地址读

        当前地址读-----------主机在当前地址读取从机的数据

        对于指定设备(Slave Address),在当前地址指针指示的地址下,读取从机数据(Data)

由手当前地址读并不能指定读的地址,  所以这个时序用的不是很多

3: 指定地址读

指定地址读----主机在指定地址读取从机的数据

对于指定设备(Slave Address),在指定地址(Reg Address)下,读取从机数据(Data) 

又叫做复合模式   :   也就是把指定地址写的前2个字节Copy过来,  不用结束, 但是需要重新开始.   在重新写第一个字节,   因为第一个字节的最后一位决定了我们的操作为读取数据还是写入数据;   然后就正常的读取从机的数据

二:HAL库

A:轮询方式

B:中断方式

三:案例

A:轮询方式--24C02

功能:使用I2C给24c02写入数据,然后在读取出来

 选择:I2C_MEMADD_SIZE_8BIT

24c02是2k内存的,也就是2048bit=256byte(从0数的话就是255)

255=0xFF。所以是8位的。以

#include "stm32f1xx_hal.h"

I2C_HandleTypeDef I2C_Handle;
void I2C_Init(void)
{
	I2C_Handle.Instance=I2C1;
	I2C_Handle.Init.ClockSpeed=100000;    ///通信速率1
	HAL_I2C_Init(&I2C_Handle);
}

void HAL_I2C_MspInit(I2C_HandleTypeDef *hi2c)
{
		if(hi2c->Instance==I2C1)
		{
			__HAL_RCC_I2C1_CLK_ENABLE();
			__HAL_RCC_GPIOB_CLK_ENABLE();
			
			GPIO_InitTypeDef GPIO_InitType;
	
			GPIO_InitType.Mode=GPIO_MODE_AF_OD;    //复用开漏输出
			GPIO_InitType.Pin=GPIO_PIN_6|GPIO_PIN_7;
			GPIO_InitType.Speed=GPIO_SPEED_FREQ_HIGH;
			HAL_GPIO_Init(GPIOB,&GPIO_InitType); 
				
		}
}
#include "stm32f1xx_hal.h"
#include "rcc.h"
#include "led.h"
#include "delay.h"
#include "OLED.h"
#include "i2c.h"
#include "UART.h"
#include <stdarg.h>
#include "stdio.h"


uint8_t send_address[16]={1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16};
uint8_t receive_address[16];
uint8_t i=0;

int main(void)
{

  
	HAL_Init();                         /* 初始化HAL库 */
  sys_stm32_clock_init(RCC_PLL_MUL9); /* 设置时钟, 72Mhz */
  delay_init(72);                     /* 延时初始化 */
  LED_Init();                        /* LED初始化 */
	Uart_Init(115200);
	LED_Exit_Init();
	OLED_Init();
	I2C_Init();
	printf("串口模块正常启动\r\n");
	

	
	//主机向某个从设备写数据 
	HAL_I2C_Mem_Write(&I2C_Handle,0xA0,0,I2C_MEMADD_SIZE_8BIT,send_address,8,5000);
	//是否准备好I2C通信
	if(HAL_I2C_IsDeviceReady(&I2C_Handle,0xA0,80,1000)==HAL_OK)
	{
			//主设备开始读取
			HAL_I2C_Mem_Read(&I2C_Handle,0xA0,0,I2C_MEMADD_SIZE_8BIT,receive_address,8,5000);
			for(i=0;i<8;i++)
			{			
				printf("receive_address[%d]=%d\r\n",i,receive_address[i]);
			}	
	}
	else
	{
		printf("芯片忙碌");
	}

	 while (1)
	 {			 
	 }   
}

 经过测试:使用我们要写8的整数倍,所有提到了16的倍数的全部改为8的倍数

      

          AT24C02的存储空间为2K位(256字节),由于其一页大小为8个字节,因此**,总共有32页**。在写入数据时,如果写入的数据跨页,需要发送一个停止位,等待并检测EEPROM的空闲模式,直到上一页数据完全写到非易失区域后,再进行下一页的写入,这样可以在很大程度上提高数据的写入效率。

请注意,实际操作时务必参考技术手册或官方文档,以确保数据的正确写入和读取。

1:回卷问题

因为我们的24c02一页最大位16个字节,所以写入的地址应该为16的整数倍,(保证为每页的开头开始写)

2:多页写入

	
	//主机向某个从设备写数据 
	HAL_I2C_Mem_Write(&I2C_Handle,0xA0,0,I2C_MEMADD_SIZE_8BIT,send_address,8,5000);
	//是否准备好I2C通信
	if(HAL_I2C_IsDeviceReady(&I2C_Handle,0xA0,80,1000)==HAL_OK)
	{
			HAL_I2C_Mem_Write(&I2C_Handle,0xA0,8,I2C_MEMADD_SIZE_8BIT,&send_address[8],8,5000);
				if(HAL_I2C_IsDeviceReady(&I2C_Handle,0xA0,80,1000)==HAL_OK)
				{	
					//主设备开始读取
					HAL_I2C_Mem_Read(&I2C_Handle,0xA0,0,I2C_MEMADD_SIZE_8BIT,receive_address,16,5000);
					for(i=0;i<16;i++)
					{			
						printf("receive_address[%d]=%d\r\n",i,receive_address[i]);
					}	
				}
				else
				{
					printf("第二次写入芯片忙碌");
				}
		
	}
	
	else
	{
		printf("第一次写入芯片忙碌");
	}
-------------------------或者-----------------------
	for(uint8_t i=0;i<2;i++)
	{
		//主机向某个从设备写数据 
		HAL_I2C_Mem_Write(&I2C_Handle,0xA0,(8*i),I2C_MEMADD_SIZE_8BIT,&send_address[8*i],8,5000);
		delay_ms(5);
		
	}
	
	
	if(HAL_I2C_IsDeviceReady(&I2C_Handle,0xA0,80,1000)==HAL_OK)
	{	
		//主设备开始读取
		HAL_I2C_Mem_Read(&I2C_Handle,0xA0,0,I2C_MEMADD_SIZE_8BIT,receive_address,16,5000);
		for(i=0;i<16;i++)
		{			
			printf("receive_address[%d]=%d\r\n",i,receive_address[i]);
		}	
	}

3:I2C1串口重新映射

不要忘记开AFIO的时钟
__HAL_RCC_AFIO_CLK_ENABLE()

4:从机地址改写

如果出现很多从机的现象,并且从机的地址重复,我们可以改写24c02的地址。

我们前面的4为是固定的,A1-A2是我们可以编程的:2^3=8;所以24C02有8中从机地址。

        芯片封装把A全部拉低,所以默认写入地址为:1010 0000 =0xA0(最后一个位 R/W位为0表示,主机发送数据)

        写保护端拉低:因此默认也是关闭写保护的。

重要函数

HAL_I2C_Init()

typedef struct  
{  
    uint32_t ClockSpeed;       !< 指定时钟频率。  
                                   此参数必须设置为低于400kHz的值 
  
    uint32_t DutyCycle;        !< 指定I2C快速模式的占空比。  
                                   此参数可以是@ref I2C_duty_cycle_in_fast_mode的一个值   
  
    uint32_t OwnAddress1;      !< 指定设备的第一个自有地址。  
                                   此参数可以是7位或10位地址。 
  
    uint32_t AddressingMode;   !< 指定是否选择7位或10位寻址模式。  
                                   此参数可以是@ref I2C_addressing_mode的一个值 
  
    uint32_t DualAddressMode;  !< 指定是否选择双地址模式。  
                                   此参数可以是@ref I2C_dual_addressing_mode的一个值  
  
    uint32_t OwnAddress2;      !< 如果选择了双地址模式,则指定设备的第二个自有地址。  
                                   此参数可以是7位地址。 */  
  
    uint32_t GeneralCallMode;  !< 指定是否选择通用呼叫模式。  
                                   此参数可以是@ref I2C_general_call_addressing_mode的一个值 */  
  
    uint32_t NoStretchMode;    !< 指定是否选择无拉伸模式。  
                                   此参数可以是@ref I2C_nostretch_mode的一个值 
  
} I2C_InitTypeDef;

B:轮询方式---BH1750

BH1750具体使用请参数:原文链接:https://blog.csdn.net/ShenZhen_zixian/article/details/103542972

#include "stm32f1xx_hal.h"

I2C_HandleTypeDef I2C_Handle;
void I2C_Init(void)
{
	I2C_Handle.Instance=I2C1;
	I2C_Handle.Init.ClockSpeed=100000;    ///通信速率1
	I2C_Handle.Init.AddressingMode=I2C_ADDRESSINGMODE_7BIT;  //7位的寻址模式
	HAL_I2C_Init(&I2C_Handle);
}

void HAL_I2C_MspInit(I2C_HandleTypeDef *hi2c)
{
		if(hi2c->Instance==I2C1)
		{
			__HAL_RCC_I2C1_CLK_ENABLE();
			__HAL_RCC_GPIOB_CLK_ENABLE();
			
			GPIO_InitTypeDef GPIO_InitType;
	
			GPIO_InitType.Mode=GPIO_MODE_AF_OD;    //复用开漏输出
			GPIO_InitType.Pin=GPIO_PIN_6|GPIO_PIN_7;
			GPIO_InitType.Speed=GPIO_SPEED_FREQ_HIGH;
			HAL_GPIO_Init(GPIOB,&GPIO_InitType); 
				
		}
}
#include "stm32f1xx_hal.h"
#include "rcc.h"
#include "led.h"
#include "delay.h"
#include "OLED.h"
#include "i2c.h"
#include "UART.h"
#include <stdarg.h>
#include "stdio.h"


uint8_t send_address[16]={11,21,32,12,45,12,12,45,53,85,94,78,52,65,32,165};
uint8_t receive_address[16];
uint8_t i=0;

uint8_t Bh1750_order1=0x01;  //上电命令
uint8_t Bh1750_order2=0x20;  //一次分辨率模式
uint8_t address_BH1750[2];    //读取BH1750的数据地址
int main(void)
{

  
	HAL_Init();                         /* 初始化HAL库 */
  sys_stm32_clock_init(RCC_PLL_MUL9); /* 设置时钟, 72Mhz */
  delay_init(72);                     /* 延时初始化 */
  LED_Init();                        /* LED初始化 */
	Uart_Init(115200);
	LED_Exit_Init();
	OLED_Init();
	I2C_Init();
	printf("串口模块正常启动\r\n");
	

	
	//主机向某个从设备写数据 
	HAL_I2C_Mem_Write(&I2C_Handle,0xA0,0,I2C_MEMADD_SIZE_8BIT,send_address,8,5000);
	//是否准备好I2C通信
	if(HAL_I2C_IsDeviceReady(&I2C_Handle,0xA0,50,1000)==HAL_OK)
	{
			//主设备开始读取
			HAL_I2C_Mem_Read(&I2C_Handle,0xA0,0,I2C_MEMADD_SIZE_8BIT,receive_address,8,5000);
			for(i=0;i<16;i++)
			{			
				printf("receive_address[%d]=%d\r\n",i,receive_address[i]);
			}	
	}
	else
	{
		printf("芯片忙碌");
	}

	 while (1)
	 {		
		 HAL_I2C_Master_Transmit(&I2C_Handle,0x46,&Bh1750_order1,1,5000);
		 if(HAL_I2C_IsDeviceReady(&I2C_Handle,0x46,50,1000)==HAL_OK)
		 {
				HAL_I2C_Master_Transmit(&I2C_Handle,0x46,&Bh1750_order2,1,5000);
			  if(HAL_I2C_IsDeviceReady(&I2C_Handle,0x46,80,1000)==HAL_OK)
				{
						HAL_I2C_Master_Receive(&I2C_Handle,0x46,address_BH1750,2,1000);
						printf("光强度:%f Lux \r\n",(address_BH1750[0]*256+address_BH1750[1])/1.2);
				}
				else
				{
						printf("发送测量指令是失败\r\n");
				
				}
		 
		 }
		 else
		{
			printf("没有完成上电\r\n");
		
		}

		delay_ms(1000);
	 }   
}

        因为在使用BH1750的时候,使用的主从的API的I2C函数,所以在I2C初始化的时候,要加上I2C的寻址方式

I2C_Handle.Init.AddressingMode=I2C_ADDRESSINGMODE_7BIT;  //7位的寻址模式

C:中断方式---24C02


 

#include "stm32f1xx_hal.h"
#include "UART.h"
#include <stdarg.h>
#include "stdio.h"

I2C_HandleTypeDef I2C_Handle;


uint8_t send_address[16]={11,21,32,12,45,12,12,45,222,85,94,78,52,65,32,165};
uint8_t receive_address[16];
void I2C_Init(void)
{
	I2C_Handle.Instance=I2C1;
	I2C_Handle.Init.ClockSpeed=100000;    ///通信速率1
	HAL_I2C_Init(&I2C_Handle);
}

void HAL_I2C_MspInit(I2C_HandleTypeDef *hi2c)
{
		if(hi2c->Instance==I2C1)
		{
			__HAL_RCC_I2C1_CLK_ENABLE();
			__HAL_RCC_GPIOB_CLK_ENABLE();
			//开启事件中断
			HAL_NVIC_SetPriority(I2C1_EV_IRQn,1,2);
			HAL_NVIC_EnableIRQ(I2C1_EV_IRQn);
				
			GPIO_InitTypeDef GPIO_InitType;
	
			GPIO_InitType.Mode=GPIO_MODE_AF_OD;    //复用开漏输出
			GPIO_InitType.Pin=GPIO_PIN_6|GPIO_PIN_7;
			GPIO_InitType.Speed=GPIO_SPEED_FREQ_HIGH;
			HAL_GPIO_Init(GPIOB,&GPIO_InitType); 
			
			
		}
}

//开启事件中断
void I2C1_EV_IRQHandler()
{
	HAL_I2C_EV_IRQHandler(&I2C_Handle);
}


//存储器模式:写入完成回调函数。
//它的触发条件通常是当主机向从机发送数据完成后,每完成一次数据的发送调用一次
void HAL_I2C_MemTxCpltCallback(I2C_HandleTypeDef *hi2c)
{
	if(hi2c->Instance==I2C1)
	{
			printf("写入数据完成\r\n");
	}

}
//存储器模式:读取完成回调函数
//它的触发条件是当主机从从机接收数据完成后,每完成一次数据的接收调用一次
void HAL_I2C_MemRxCpltCallback(I2C_HandleTypeDef *hi2c)
{
	if(hi2c->Instance==I2C1)
	{
		printf("读取数据完成\r\n");
		for(uint8_t j=0;j<16;j++) printf("receive_address[%d]=%d\r\n",j,receive_address[j]);

	}
}
#include "stm32f1xx_hal.h"
#include "rcc.h"
#include "led.h"
#include "delay.h"
#include "OLED.h"
#include "i2c.h"
#include "UART.h"
#include <stdarg.h>
#include "stdio.h"
#include "key.h"



int main(void)
{

  
	HAL_Init();                         /* 初始化HAL库 */
  sys_stm32_clock_init(RCC_PLL_MUL9); /* 设置时钟, 72Mhz */
  delay_init(72);                     /* 延时初始化 */
  LED_Init();                        /* LED初始化 */
	Uart_Init(115200);
	LED_Exit_Init();
	OLED_Init();
	KEY_Init();
	I2C_Init();
	printf("串口模块正常启动\r\n");
	

	 while (1)
	 {
			if(KEY_Scan())
			{	//PA0按下写入数据
				if(HAL_I2C_IsDeviceReady(&I2C_Handle,0xA0,50,1000)==HAL_OK)
				{
					for(uint8_t i=0;i<2;i++)
					{
							HAL_I2C_Mem_Write_IT(&I2C_Handle,0xA0,(8*i),I2C_MEMADD_SIZE_8BIT,&send_address[8*i],8);
							delay_ms(5);
					}
				}
				else
				{
					printf("写入数据错误\r\n");
				}
			}
			
				if(KEY_Scan1())
			{	//PA1按下读取数据
				if(HAL_I2C_IsDeviceReady(&I2C_Handle,0xA0,50,1000)==HAL_OK)
				{
					HAL_I2C_Mem_Read_IT(&I2C_Handle,0xA0,0,I2C_MEMADD_SIZE_8BIT,receive_address,16);
				}
				else
				{
					printf("读取数据错误\r\n");
				}
			}
			
	 }   
}

D:中断方式--BH1750

#include "stm32f1xx_hal.h"
#include "UART.h"
#include <stdarg.h>
#include "stdio.h"
#include "TIMx.h"
I2C_HandleTypeDef I2C_Handle;


uint8_t send_address[16]={11,21,32,12,45,12,12,45,53,85,94,78,52,65,32,165};
uint8_t receive_address[16];
void I2C_Init(void)
{
	I2C_Handle.Instance=I2C1;
	I2C_Handle.Init.ClockSpeed=100000;    ///通信速率1
	I2C_Handle.Init.AddressingMode=I2C_ADDRESSINGMODE_7BIT;  //7位寻址的方式
	HAL_I2C_Init(&I2C_Handle);
}

void HAL_I2C_MspInit(I2C_HandleTypeDef *hi2c)
{
		if(hi2c->Instance==I2C1)
		{
			__HAL_RCC_I2C1_CLK_ENABLE();
			__HAL_RCC_GPIOB_CLK_ENABLE();
			//开启事件中断
			HAL_NVIC_SetPriority(I2C1_EV_IRQn,1,2);
			HAL_NVIC_EnableIRQ(I2C1_EV_IRQn);
				
			GPIO_InitTypeDef GPIO_InitType;
	
			GPIO_InitType.Mode=GPIO_MODE_AF_OD;    //复用开漏输出
			GPIO_InitType.Pin=GPIO_PIN_6|GPIO_PIN_7;
			GPIO_InitType.Speed=GPIO_SPEED_FREQ_HIGH;
			HAL_GPIO_Init(GPIOB,&GPIO_InitType); 
			
			
		}
}

//开启事件中断
void I2C1_EV_IRQHandler()
{
	HAL_I2C_EV_IRQHandler(&I2C_Handle);
}



//存储器模式:写入完成回调函数。
//它的触发条件通常是当主机向从机发送数据完成后,每完成一次数据的发送调用一次
void HAL_I2C_MemTxCpltCallback(I2C_HandleTypeDef *hi2c)
{
	if(hi2c->Instance==I2C1)
	{
			printf("写入数据完成24C02\r\n");
	}	
}


//存储器模式:读取完成回调函数
//它的触发条件是当主机从从机接收数据完成后,每完成一次数据的接收调用一次
void HAL_I2C_MemRxCpltCallback(I2C_HandleTypeDef *hi2c)
{
	if(hi2c->Instance==I2C1)
	{
		printf("读取数据完成24C02\r\n");
		for(uint8_t j=0;j<8;j++) printf("receive_address[%d]=%d\r\n",j,receive_address[j]);

	}
}


//主模式:写入完成回调函数。
//它的触发条件通常是当主机向从机发送数据完成后,每当数据发送完成的时候调用
void HAL_I2C_MasterTxCpltCallback(I2C_HandleTypeDef *hi2c)
{
	if(hi2c->Instance==I2C1)
	{
			printf("写入数据完成BH1750\r\n");
	}

}

//主模式:读取完成回调函数
//它的触发条件是当主机从从机接收数据完成后,每当数据接收完成的时候调用
void HAL_I2C_MasterRxCpltCallback(I2C_HandleTypeDef *hi2c)
{
	if(hi2c->Instance==I2C1)
	{
		printf("读取数据完成BH1750\r\n");
		printf("光强度:%f Lux \r\n",(address_BH1750[0]*256+address_BH1750[1])/1.2);
	}
}
--------------定时器回调函数-------------------
关于定时器的其他代码省略
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
	if(htim->Instance==TIM3)
	{
			if(HAL_I2C_IsDeviceReady(&I2C_Handle,0x46,80,1000)==HAL_OK)
			{
				HAL_I2C_Master_Receive_IT(&I2C_Handle,0x46,address_BH1750,2);
			}
			else
			{
				printf("读取BH1750数据错误\r\n");
			
			}
	}
}
#include "stm32f1xx_hal.h"
#include "rcc.h"
#include "led.h"
#include "delay.h"
#include "OLED.h"
#include "i2c.h"
#include "UART.h"
#include <stdarg.h>
#include "stdio.h"
#include "key.h"
#include "TIMx.h"


int main(void)
{
	
	HAL_Init();                        /* 初始化HAL库 */
  sys_stm32_clock_init(RCC_PLL_MUL9); /* 设置时钟, 72Mhz */
  delay_init(72);                     /* 延时初始化 */
  LED_Init();                        /* LED初始化 */
	Uart_Init(115200);
	LED_Exit_Init();
	OLED_Init();
	KEY_Init();
	I2C_Init();
	TIM3_CH2_init(36000,2000);  //T=1s
	printf("串口模块正常启动\r\n");
	
	
	 HAL_I2C_Master_Transmit_IT(&I2C_Handle,0x46,&Bh1750_order1,1);//上电命令
	 delay_ms(5);
	 HAL_I2C_Master_Transmit_IT(&I2C_Handle,0x46,&Bh1750_order2,1);//连续次分辨率模式
	
	 while (1)
	 {
			if(KEY_Scan())
			{	//PA0按下写入数据
				if(HAL_I2C_IsDeviceReady(&I2C_Handle,0xA0,50,1000)==HAL_OK)
				{
					HAL_I2C_Mem_Write_IT(&I2C_Handle,0xA0,0,I2C_MEMADD_SIZE_8BIT,send_address,8);
				}
				else
				{
					printf("写入数据错误\r\n");
				}
			}
			
				if(KEY_Scan1())
			{	//PA1按下读取数据
				if(HAL_I2C_IsDeviceReady(&I2C_Handle,0xA0,50,1000)==HAL_OK)
				{
					HAL_I2C_Mem_Read_IT(&I2C_Handle,0xA0,0,I2C_MEMADD_SIZE_8BIT,receive_address,8);
				}
				else
				{
					printf("读取数据错误\r\n");
				}
			}
			
	 }   
}

E:MDA---BH1750+24C02

#include "stm32f1xx_hal.h"
#include "UART.h"
#include <stdarg.h>
#include "stdio.h"
#include "TIMx.h"
I2C_HandleTypeDef I2C_Handle;
DMA_HandleTypeDef DMA_HandleTX;  //发送DMA
DMA_HandleTypeDef DMA_HandleRX;   //接收

uint8_t send_address[16]={11,21,32,12,45,12,12,45,53,85,94,78,52,65,32,165};
uint8_t receive_address[16];

void I2C_Init(void)
{
	I2C_Handle.Instance=I2C1;
	I2C_Handle.Init.ClockSpeed=100000;    ///通信速率1
	I2C_Handle.Init.AddressingMode=I2C_ADDRESSINGMODE_7BIT;    ///7位寻址方式
	HAL_I2C_Init(&I2C_Handle);
}

void HAL_I2C_MspInit(I2C_HandleTypeDef *hi2c)
{
		if(hi2c->Instance==I2C1)
		{
			__HAL_RCC_I2C1_CLK_ENABLE();
			__HAL_RCC_GPIOB_CLK_ENABLE();
			__HAL_RCC_DMA1_CLK_ENABLE();
			
			//开启事件中断
			HAL_NVIC_SetPriority(I2C1_EV_IRQn,1,2);
			HAL_NVIC_EnableIRQ(I2C1_EV_IRQn);
				
			GPIO_InitTypeDef GPIO_InitType;
	
			GPIO_InitType.Mode=GPIO_MODE_AF_OD;    //复用开漏输出
			GPIO_InitType.Pin=GPIO_PIN_6|GPIO_PIN_7;
			GPIO_InitType.Speed=GPIO_SPEED_FREQ_HIGH;
			HAL_GPIO_Init(GPIOB,&GPIO_InitType); 
			
			//MDA通道6配置--发送数据
			DMA_HandleTX.Instance=DMA1_Channel6;
			//传输方向:内存(数组)--->外设
			DMA_HandleTX.Init.Direction=DMA_MEMORY_TO_PERIPH;
			DMA_HandleTX.Init.MemDataAlignment=DMA_MDATAALIGN_BYTE; //内存数据宽度
			DMA_HandleTX.Init.MemInc=DMA_MINC_ENABLE;    //存储区地址自增
			DMA_HandleTX.Init.Mode=DMA_NORMAL;
			DMA_HandleTX.Init.PeriphDataAlignment=DMA_PDATAALIGN_BYTE;//外设数据宽度
			DMA_HandleTX.Init.PeriphInc=DMA_PINC_DISABLE;//外设地址不自增
			DMA_HandleTX.Init.Priority=DMA_PRIORITY_MEDIUM;   //优先级
			__HAL_LINKDMA(&I2C_Handle,hdmatx,DMA_HandleTX);   //双向链接
			//__HAL_LINKDMA(hi2c,hdmatx,DMA_HandleTX);   //双向链接
			HAL_DMA_Init(&DMA_HandleTX);
			
			HAL_NVIC_SetPriority(DMA1_Channel6_IRQn,1,2);
			HAL_NVIC_EnableIRQ(DMA1_Channel6_IRQn);
			
			//MDA通道7配置---接收数据
			DMA_HandleRX.Instance=DMA1_Channel7;
			//传输方向:外设  ---> 内存(数组)
			DMA_HandleRX.Init.Direction=DMA_PERIPH_TO_MEMORY;
			DMA_HandleRX.Init.MemDataAlignment=DMA_MDATAALIGN_BYTE; //内存数据宽度
			DMA_HandleRX.Init.MemInc=DMA_MINC_ENABLE;    //存储区地址自增
			DMA_HandleRX.Init.Mode=DMA_NORMAL;
			DMA_HandleRX.Init.PeriphDataAlignment=DMA_PDATAALIGN_BYTE;//外设数据宽度
			DMA_HandleRX.Init.PeriphInc=DMA_PINC_DISABLE;//外设地址不自增
			DMA_HandleRX.Init.Priority=DMA_PRIORITY_MEDIUM;   //优先级
			__HAL_LINKDMA(&I2C_Handle,hdmarx,DMA_HandleRX);   //双向链接
			//__HAL_LINKDMA(hi2c,hdmarx,DMA_HandleRX);   //双向链接
			HAL_DMA_Init(&DMA_HandleRX);
			
			HAL_NVIC_SetPriority(DMA1_Channel7_IRQn,1,2);
			HAL_NVIC_EnableIRQ(DMA1_Channel7_IRQn);
			
			
		}
}

//开启事件中断
void I2C1_EV_IRQHandler()
{
	HAL_I2C_EV_IRQHandler(&I2C_Handle);
}

//开启MDA通道6发送中断
void DMA1_Channel6_IRQHandler()
{
	//HAL_DMA_IRQHandler(I2C_Handle.hdmatx);  
	HAL_DMA_IRQHandler(&DMA_HandleTX); 
}
//开启MDA通道6接收中断
void DMA1_Channel7_IRQHandler()
{
	//HAL_DMA_IRQHandler(I2C_Handle.hdmarx);
	HAL_DMA_IRQHandler(&DMA_HandleRX);
}



//存储器模式:写入完成回调函数。
//它的触发条件通常是当主机向从机发送数据完成后,每完成一次数据的发送调用一次
void HAL_I2C_MemTxCpltCallback(I2C_HandleTypeDef *hi2c)
{
	if(hi2c->Instance==I2C1)
	{
			printf("写入数据完成24C02\r\n");
	}

}
//存储器模式:读取完成回调函数
//它的触发条件是当主机从从机接收数据完成后,每完成一次数据的接收调用一次
void HAL_I2C_MemRxCpltCallback(I2C_HandleTypeDef *hi2c)
{
	if(hi2c->Instance==I2C1)
	{
		printf("读取数据完成24C02\r\n");
		for(uint8_t j=0;j<16;j++) printf("receive_address[%d]=%d\r\n",j,receive_address[j]);

	}
}


//主模式:DMA写入完成回调函数。
//它的触发条件通常是当主机向从机发送数据完成后,写入完成一次调用一次
void HAL_I2C_MasterTxCpltCallback(I2C_HandleTypeDef *hi2c)
{
	if(hi2c->Instance==I2C1)
	{
			printf("写入数据完成:MDA-BH1750\r\n");
	}

}


//存储器模式:DMA读取完成回调函数
//它的触发条件是当主机从从机接收数据完成后,读取完成一次调用一次
void HAL_I2C_MasterRxCpltCallback(I2C_HandleTypeDef *hi2c)
{
	if(hi2c->Instance==I2C1)
	{
		printf("读取数据完成:DMA-BH1750\r\n");
		printf("光强度:%f Lux \r\n",(address_BH1750[0]*256+address_BH1750[1])/1.2);
	}
}
-------------------------定时器-----------------------
定时器其他代码省略
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
	if(htim->Instance==TIM3)
	{
			if(HAL_I2C_IsDeviceReady(&I2C_Handle,0x46,50,1000)==HAL_OK)
			{
				HAL_I2C_Master_Receive_DMA(&I2C_Handle,0x46,address_BH1750,2);
			}
			else
			{
				printf("定时器:读取BH1750数据错误\r\n");
			
			}
	}

#include "stm32f1xx_hal.h"
#include "rcc.h"
#include "led.h"
#include "delay.h"
#include "OLED.h"
#include "i2c.h"
#include "UART.h"
#include <stdarg.h>
#include "stdio.h"
#include "key.h"
#include "TIMx.h"


int main(void)
{
	
	HAL_Init();                        /* 初始化HAL库 */
  sys_stm32_clock_init(RCC_PLL_MUL9); /* 设置时钟, 72Mhz */
  delay_init(72);                     /* 延时初始化 */
  LED_Init();                        /* LED初始化 */
	Uart_Init(115200);
	LED_Exit_Init();
	OLED_Init();
	KEY_Init();
	I2C_Init();
	TIM3_CH2_init(36000,2000);  //T=1s
	printf("串口模块正常启动\r\n");
	
	
	 HAL_I2C_Master_Transmit_DMA(&I2C_Handle,0x46,&Bh1750_order1,1);//上电命令
	 delay_ms(5);
	 HAL_I2C_Master_Transmit_DMA(&I2C_Handle,0x46,&Bh1750_order2,1);//连续次分辨率模式
	
	
	 while (1)
	 {
			if(KEY_Scan())
			{	//PA0按下写入数据
				if(HAL_I2C_IsDeviceReady(&I2C_Handle,0xA0,50,1000)==HAL_OK)
				{
					HAL_I2C_Mem_Write_DMA(&I2C_Handle,0xA0,0,I2C_MEMADD_SIZE_8BIT,send_address,8);
				}
				else
				{
					printf("写入数据错误:按键24C02\r\n");
				}
			}
			
				if(KEY_Scan1())
			{	//PA1按下读取数据
				if(HAL_I2C_IsDeviceReady(&I2C_Handle,0xA0,50,1000)==HAL_OK)
				{
					HAL_I2C_Mem_Read_DMA(&I2C_Handle,0xA0,0,I2C_MEMADD_SIZE_8BIT,receive_address,8);
				}
				else
				{
					printf("读取数据错误:按键24C02\r\n");
				}
			}
			
	 }   
}

DMA重要参数

HAL_DMA_Init(&DMA_HandleTX);
typedef struct  
{  
    uint32_t Direction;     !< 数据传输方向,指定数据是从内存传输到外设,  
                             从内存传输到内存,还是从外设传输到内存。  
                             这个参数可以是@ref DMA_Data_transfer_direction中的一个值   
  
    uint32_t PeriphInc;     !< 指定外设地址寄存器是否应该递增。  
                             这个参数可以是@ref DMA_Peripheral_incremented_mode中的一个值   
  
    uint32_t MemInc;     !< 指定内存地址寄存器是否应该递增。  
                          这个参数可以是@ref DMA_Memory_incremented_mode中的一个值 
  
    uint32_t PeriphDataAlignment; !< 指定外设数据的宽度。  
                                  这个参数可以是@ref DMA_Peripheral_data_size中的一个值 
  
    uint32_t MemDataAlignment;   !< 指定内存数据的宽度。  
                                 这个参数可以是@ref DMA_Memory_data_size中的一个值 
  
    uint32_t Mode;       !< 指定DMAy通道x的操作模式。  
                        这个参数可以是@ref DMA_mode中的一个值 @note 如果在选定的通道上配置了        
                        内存到内存的数据传输,则不能使用循环缓冲区模式   
  
    uint32_t Priority;      !< 指定DMAy通道x的软件优先级。  
                          这个参数可以是@ref DMA_Priority_level中的一个值  
} DMA_InitTypeDef;

F:SEQ传输数据换向

seqHAL库的AP函数带SEQ和不带SEQ的区别:

如果需要换向就需要使用带SEQ的API函数

#include "stm32f1xx_hal.h"
#include "UART.h"
#include <stdarg.h>
#include "stdio.h"
#include "delay.h"
I2C_HandleTypeDef I2C_Handle;


uint8_t send_address[16]={0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15};
uint8_t receive_address[64];
void I2C_Init(void)
{
	I2C_Handle.Instance=I2C1;
	I2C_Handle.Init.ClockSpeed=100000;    ///通信速率1
	I2C_Handle.Init.AddressingMode=I2C_ADDRESSINGMODE_7BIT;  //7位地寻址模式
	HAL_I2C_Init(&I2C_Handle);
}

void HAL_I2C_MspInit(I2C_HandleTypeDef *hi2c)
{
		if(hi2c->Instance==I2C1)
		{
			__HAL_RCC_I2C1_CLK_ENABLE();
			__HAL_RCC_GPIOB_CLK_ENABLE();
			//开启事件中断
			HAL_NVIC_SetPriority(I2C1_EV_IRQn,1,2);
			HAL_NVIC_EnableIRQ(I2C1_EV_IRQn);
				
			GPIO_InitTypeDef GPIO_InitType;
	
			GPIO_InitType.Mode=GPIO_MODE_AF_OD;    //复用开漏输出
			GPIO_InitType.Pin=GPIO_PIN_6|GPIO_PIN_7;
			GPIO_InitType.Speed=GPIO_SPEED_FREQ_HIGH;
			HAL_GPIO_Init(GPIOB,&GPIO_InitType); 
			
			
		}
}

//开启事件中断
void I2C1_EV_IRQHandler()
{
	HAL_I2C_EV_IRQHandler(&I2C_Handle);
}


//主模式:写入完成回调函数,带换向和不带换向的为一个回调函数
//它的触发条件通常是当主机向从机发送数据完成后。
void HAL_I2C_MasterTxCpltCallback(I2C_HandleTypeDef *hi2c)
{
	if(I2C_Handle.XferOptions==I2C_FIRST_FRAME)
	{
				printf("第一个数据帧写入完成:带换向\r\n");
				HAL_I2C_Master_Seq_Receive_IT(&I2C_Handle,0xA0,receive_address,16,I2C_LAST_FRAME);
	
	}
	
	else
	{	
			printf("不带换向写入完成\r\n");
			if(HAL_I2C_IsDeviceReady(&I2C_Handle,0xA0,80,1000)==HAL_OK)
			{
				HAL_I2C_Master_Receive_IT(&I2C_Handle,0xA0,receive_address,8);		
			}
			
	}
	

}

//主模式:读取完成回调函数,带换向和不带换向的为一个回调函数
//它的触发条件是当主机从从机接收数据完成后。
void HAL_I2C_MasterRxCpltCallback(I2C_HandleTypeDef *hi2c)
{
	
	
	if(I2C_Handle.XferOptions==I2C_LAST_FRAME)
	{
		printf("读取数据完成:带换向\r\n");
		for(uint8_t j=0;j<16;j++) printf("receive_address[%d]=%d\r\n",j,receive_address[j]);

	}
	else
	{
			printf("不带换向的接收完成\r\n");
			for(uint8_t j=0;j<8;j++) printf("receive_address[%d]=%d\r\n",j,receive_address[j]);
	}


}
#include "stm32f1xx_hal.h"
#include "rcc.h"
#include "led.h"
#include "delay.h"
#include "OLED.h"
#include "i2c.h"
#include "UART.h"
#include <stdarg.h>
#include "stdio.h"
#include "key.h"


uint8_t i=0;

int main(void)
{

  
	HAL_Init();                         /* 初始化HAL库 */
  sys_stm32_clock_init(RCC_PLL_MUL9); /* 设置时钟, 72Mhz */
  delay_init(72);                     /* 延时初始化 */
  LED_Init();                        /* LED初始化 */
	Uart_Init(115200);
	LED_Exit_Init();
	OLED_Init();
	KEY_Init();
	I2C_Init();
	printf("串口模块正常启动\r\n");
	

	 while (1)
	 {
			if(KEY_Scan())
			{	//PA0按下发送数据
				if(HAL_I2C_IsDeviceReady(&I2C_Handle,0xA0,50,1000)==HAL_OK)
				{
					//以主机的方式去写,写入的第一个数据为写入的地址(send_address【0】)
					//一页max为8个字节,写入9个:第一个字节为地址,后8个字节为发送的数据
					HAL_I2C_Master_Transmit_IT(&I2C_Handle,0xA0,send_address,9);
				}
				else
				{
					printf("写入数据错误\r\n");
				}
			}
			
				if(KEY_Scan1())
			{	//PA1按下发送数据:带换向的,随机地址读
				if(HAL_I2C_IsDeviceReady(&I2C_Handle,0xA0,50,1000)==HAL_OK)
				{
					//seq带换向
					HAL_I2C_Master_Seq_Transmit_IT(&I2C_Handle,0xA0,&send_address[0],1,I2C_FIRST_FRAME);
				}
				else
				{
					printf("发送数据错误\r\n");
				}
			}
			
	 }   
}


 


数据传输选项:

        发送没有BUG。接收有BUG点:最后一次接收会把它的信号改为BUSY, 过了一段时间我们在接收:

         无法有起始信号,不能开始通信。所以最后一帧不能是接收。

在接收回调时候,把他的信号变为0;

hi2c->PreviousState=0;    会引发另一个问题:从机没有停止信号	


一主多从模式

多机通信 带seq的AP|函数 I2C_FIRST_AND_LAST_FRAME(有BUG): 接收使不带SEQ的API

A:一主二从--单

I2C_FIRST_AND_LAST_FRAME这个信号只能发送一帧的数据,有开始和结束信号。

连线问题:

注意请查看自己板子的原理图,SDA和SCL有没有上拉电阻,如果没有的话,加一个外部的上拉电阻。 4.7K左右

主机程序:

#include "stm32f1xx_hal.h"
#include "UART.h"
#include <stdarg.h>
#include "stdio.h"

I2C_HandleTypeDef I2C_Handle;
DMA_HandleTypeDef DMA_HandleTX;  //发送DMA
DMA_HandleTypeDef DMA_HandleRX;   //接收



uint8_t send_address1[16]={111,11,1,1,2,3,4,5,6,7,8,9,10,11,12,13};
uint8_t receive_address1[16];

uint8_t send_address2[16]={222,22,2,15,14,13,12,15,16,15,13,13,15,12,16,12};
uint8_t receive_address2[16];
void I2C_Init(void)
{
	I2C_Handle.Instance=I2C1;
	I2C_Handle.Init.ClockSpeed=100000;    ///通信速率1
	I2C_Handle.Init.AddressingMode=I2C_ADDRESSINGMODE_7BIT;  //7位寻址模式
	HAL_I2C_Init(&I2C_Handle);
}

void HAL_I2C_MspInit(I2C_HandleTypeDef *hi2c)
{
		if(hi2c->Instance==I2C1)
		{
			__HAL_RCC_I2C1_CLK_ENABLE();
			__HAL_RCC_GPIOB_CLK_ENABLE();
			__HAL_RCC_DMA1_CLK_ENABLE();
			
			//开启事件中断
			HAL_NVIC_SetPriority(I2C1_EV_IRQn,1,2);
			HAL_NVIC_EnableIRQ(I2C1_EV_IRQn);
				
			GPIO_InitTypeDef GPIO_InitType;
	
			GPIO_InitType.Mode=GPIO_MODE_AF_OD;    //复用开漏输出
			GPIO_InitType.Pin=GPIO_PIN_6|GPIO_PIN_7;
			GPIO_InitType.Speed=GPIO_SPEED_FREQ_HIGH;
			HAL_GPIO_Init(GPIOB,&GPIO_InitType); 
			
			//MDA通道6配置--发送数据
			DMA_HandleTX.Instance=DMA1_Channel6;
			//传输方向:内存(数组)--->外设
			DMA_HandleTX.Init.Direction=DMA_MEMORY_TO_PERIPH;
			DMA_HandleTX.Init.MemDataAlignment=DMA_MDATAALIGN_BYTE; //内存数据宽度
			DMA_HandleTX.Init.MemInc=DMA_MINC_ENABLE;    //存储区地址自增
			DMA_HandleTX.Init.Mode=DMA_NORMAL;
			DMA_HandleTX.Init.PeriphDataAlignment=DMA_PDATAALIGN_BYTE;//外设数据宽度
			DMA_HandleTX.Init.PeriphInc=DMA_PINC_DISABLE;//外设地址不自增
			DMA_HandleTX.Init.Priority=DMA_PRIORITY_MEDIUM;   //优先级
			__HAL_LINKDMA(&I2C_Handle,hdmatx,DMA_HandleTX);   //双向链接
			HAL_DMA_Init(&DMA_HandleTX);
			
			HAL_NVIC_SetPriority(DMA1_Channel6_IRQn,1,2);
			HAL_NVIC_EnableIRQ(DMA1_Channel6_IRQn);
			
			//MDA通道7配置---接收数据
			DMA_HandleRX.Instance=DMA1_Channel7;
			//传输方向:外设  ---> 内存(数组)
			DMA_HandleRX.Init.Direction=DMA_PERIPH_TO_MEMORY;
			DMA_HandleRX.Init.MemDataAlignment=DMA_MDATAALIGN_BYTE; //内存数据宽度
			DMA_HandleRX.Init.MemInc=DMA_MINC_ENABLE;    //存储区地址自增
			DMA_HandleRX.Init.Mode=DMA_NORMAL;
			DMA_HandleRX.Init.PeriphDataAlignment=DMA_PDATAALIGN_BYTE;//外设数据宽度
			DMA_HandleRX.Init.PeriphInc=DMA_PINC_DISABLE;//外设地址不自增
			DMA_HandleRX.Init.Priority=DMA_PRIORITY_MEDIUM;   //优先级
			__HAL_LINKDMA(&I2C_Handle,hdmarx,DMA_HandleRX);   //双向链接
			HAL_DMA_Init(&DMA_HandleRX);
			
			HAL_NVIC_SetPriority(DMA1_Channel7_IRQn,1,2);
			HAL_NVIC_EnableIRQ(DMA1_Channel7_IRQn);
			
		}
}



//开启事件中断
void I2C1_EV_IRQHandler()
{
	HAL_I2C_EV_IRQHandler(&I2C_Handle);
}

//开启MDA通道6发送中断
void DMA1_Channel6_IRQHandler()
{
	HAL_DMA_IRQHandler(&DMA_HandleTX);
}
//开启MDA通道6接收中断
void DMA1_Channel7_IRQHandler()
{
	HAL_DMA_IRQHandler(&DMA_HandleRX);
}

//主模式:DMA写入完成回调函数:主机给从机发送数据调用,发送完毕一次调用一次。
//它的触发条件通常是当主机向从机发送数据完成后。
void HAL_I2C_MasterTxCpltCallback(I2C_HandleTypeDef *hi2c)
{
	if(hi2c->Instance==I2C1)
	{
			if(hi2c->Devaddress==0x10)
			{
				printf("主机给从机1发送数据完毕\r\n");
			}
			else if(hi2c->Devaddress==0x20)
			{
				printf("主机给从机2发送数据完毕\r\n");
			
			}	
	}

}
//存储器模式:DMA读取完成回调函数,主机接收从机发送来的数据,发送完成一次调用一次
//它的触发条件是当主机从从机接收数据完成后。
void HAL_I2C_MasterRxCpltCallback(I2C_HandleTypeDef *hi2c)
{
	if(hi2c->Instance==I2C1)
	{
		//hi2c->PreviousState=0;    //会引发另一个问题:从机没有停止信号
		if(hi2c->Devaddress==0x10)
			{
				printf("主机接收从机1的数据完毕\r\n");
				for(uint16_t i =0;i<16;i++)
				{
					printf("receive_address1[%d]=%d\r\n",i,receive_address1[i]);
				}
				
			}
			else if(hi2c->Devaddress==0x20)
			{
				printf("主机接收从机2的数据完毕\r\n");
				for(uint16_t j =0;j<16;j++)
				{
					printf("receive_address2[%d]=%d\r\n",j,receive_address2[j]);
				}
			
			}
	}
}
#include "stm32f1xx_hal.h"
#include "rcc.h"
#include "led.h"
#include "delay.h"
#include "OLED.h"
#include "i2c.h"
#include "UART.h"
#include <stdarg.h>
#include "stdio.h"
#include "key.h"


uint8_t i=0;

int main(void)
{

  
	HAL_Init();                         /* 初始化HAL库 */
  sys_stm32_clock_init(RCC_PLL_MUL9); /* 设置时钟, 72Mhz */
  delay_init(72);                     /* 延时初始化 */
  LED_Init();                        /* LED初始化 */
	Uart_Init(115200);
	LED_Exit_Init();
	OLED_Init();
	KEY_Init();
	I2C_Init();
	printf("串口模块正常启动\r\n");
	

	 while (1)
	 {
			if(KEY_Scan())
			{	
				//PA0按下:主机给从机1发送数据
				printf("PA0按下\r\n");
				HAL_I2C_Master_Seq_Transmit_DMA(&I2C_Handle,0x10,send_address1,16,I2C_FIRST_AND_LAST_FRAME);
			}
			
			else if(KEY_Scan1())
			{	
				//PA1按下:主机接收从机1发送来的数据
				printf("PA1按下\r\n");
				HAL_I2C_Master_Seq_Receive_DMA(&I2C_Handle,0x10,receive_address1,16,I2C_FIRST_AND_LAST_FRAME);

			}
			
				else if(KEY_Scan2())
			{	
				//PA3按下:主机给从机2发送数据
				printf("PA3按下\r\n");
				
				HAL_I2C_Master_Seq_Transmit_DMA(&I2C_Handle,0x20,send_address2,16,I2C_FIRST_AND_LAST_FRAME);
			}
			
				else if(KEY_Scan3())
			{	
					//PA5按下:主机接收从机2发送来的数据
				printf("PA5按下\r\n");
				HAL_I2C_Master_Seq_Receive_DMA(&I2C_Handle,0x20,receive_address2,16,I2C_FIRST_AND_LAST_FRAME);

			}
	 }   
}

从机1   0x10

#include "stm32f1xx_hal.h"
#include "UART.h"
#include <stdarg.h>
#include "stdio.h"

I2C_HandleTypeDef I2C_Handle;
DMA_HandleTypeDef DMA_HandleTX;  //发送DMA
DMA_HandleTypeDef DMA_HandleRX;   //接收

uint8_t send_address[16]={11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11};
uint8_t receive_address[16];


void I2C_Init(void)
{
	I2C_Handle.Instance=I2C1;
	I2C_Handle.Init.ClockSpeed=100000;    ///通信速率1
	I2C_Handle.Init.AddressingMode=I2C_ADDRESSINGMODE_7BIT;  //7位寻址模式
	I2C_Handle.Init.OwnAddress1=0x10;     //从机的地址
	HAL_I2C_Init(&I2C_Handle);
}

void HAL_I2C_MspInit(I2C_HandleTypeDef *hi2c)
{
		if(hi2c->Instance==I2C1)
		{
			__HAL_RCC_I2C1_CLK_ENABLE();
			__HAL_RCC_GPIOB_CLK_ENABLE();
			__HAL_RCC_DMA1_CLK_ENABLE();
			
			//开启事件中断
			HAL_NVIC_SetPriority(I2C1_EV_IRQn,1,2);
			HAL_NVIC_EnableIRQ(I2C1_EV_IRQn);
				
			GPIO_InitTypeDef GPIO_InitType;
	
			GPIO_InitType.Mode=GPIO_MODE_AF_OD;    //复用开漏输出
			GPIO_InitType.Pin=GPIO_PIN_6|GPIO_PIN_7;
			GPIO_InitType.Speed=GPIO_SPEED_FREQ_HIGH;
			HAL_GPIO_Init(GPIOB,&GPIO_InitType); 
			
			//MDA通道6配置--发送数据
			DMA_HandleTX.Instance=DMA1_Channel6;
			//传输方向:内存(数组)--->外设
			DMA_HandleTX.Init.Direction=DMA_MEMORY_TO_PERIPH;
			DMA_HandleTX.Init.MemDataAlignment=DMA_MDATAALIGN_BYTE; //内存数据宽度
			DMA_HandleTX.Init.MemInc=DMA_MINC_ENABLE;    //存储区地址自增
			DMA_HandleTX.Init.Mode=DMA_NORMAL;
			DMA_HandleTX.Init.PeriphDataAlignment=DMA_PDATAALIGN_BYTE;//外设数据宽度
			DMA_HandleTX.Init.PeriphInc=DMA_PINC_DISABLE;//外设地址不自增
			DMA_HandleTX.Init.Priority=DMA_PRIORITY_MEDIUM;   //优先级
			__HAL_LINKDMA(hi2c,hdmatx,DMA_HandleTX);   //双向链接
			HAL_DMA_Init(&DMA_HandleTX);
			
			HAL_NVIC_SetPriority(DMA1_Channel6_IRQn,1,2);
			HAL_NVIC_EnableIRQ(DMA1_Channel6_IRQn);
			
			//MDA通道7配置---接收数据
			DMA_HandleRX.Instance=DMA1_Channel7;
			//传输方向:外设  ---> 内存(数组)
			DMA_HandleRX.Init.Direction=DMA_PERIPH_TO_MEMORY;
			DMA_HandleRX.Init.MemDataAlignment=DMA_MDATAALIGN_BYTE; //内存数据宽度
			DMA_HandleRX.Init.MemInc=DMA_MINC_ENABLE;    //存储区地址自增
			DMA_HandleRX.Init.Mode=DMA_NORMAL;
			DMA_HandleRX.Init.PeriphDataAlignment=DMA_PDATAALIGN_BYTE;//外设数据宽度
			DMA_HandleRX.Init.PeriphInc=DMA_PINC_DISABLE;//外设地址不自增
			DMA_HandleRX.Init.Priority=DMA_PRIORITY_MEDIUM;   //优先级
			__HAL_LINKDMA(hi2c,hdmarx,DMA_HandleRX);   //双向链接
			HAL_DMA_Init(&DMA_HandleRX);
			
			HAL_NVIC_SetPriority(DMA1_Channel7_IRQn,1,2);
			HAL_NVIC_EnableIRQ(DMA1_Channel7_IRQn);
			
		}
}



//开启事件中断
void I2C1_EV_IRQHandler()
{
	HAL_I2C_EV_IRQHandler(&I2C_Handle);
}

//开启MDA通道6发送中断
void DMA1_Channel6_IRQHandler()
{
	HAL_DMA_IRQHandler(I2C_Handle.hdmatx);
}
//开启MDA通道6接收中断
void DMA1_Channel7_IRQHandler()
{
	HAL_DMA_IRQHandler(I2C_Handle.hdmarx);
}

//监听功能回调函数
//监听动作全部完成:调用这个回调函数
void HAL_I2C_ListenCpltCallback(I2C_HandleTypeDef *hi2c)
{
	if(hi2c->Instance==I2C1)
	{
			printf("从机1监听完成");
			HAL_I2C_EnableListen_IT(&I2C_Handle);  //	开启下一次监听
	}


}
//地址匹配中断回调
void HAL_I2C_AddrCallback(I2C_HandleTypeDef *hi2c, uint8_t TransferDirection, uint16_t AddrMatchCode)
{
	if(hi2c->Instance==I2C1)
	{
	
		if(AddrMatchCode==0x10)
		{
			if(TransferDirection==I2C_DIRECTION_RECEIVE)
			{
				//I2C_DIRECTION_RECEIVE:主机读取数据:从机发送数据
				printf("主机读取数据:从机1准备开始发送数据\r\n");
				HAL_I2C_Slave_Seq_Transmit_DMA(hi2c,send_address,16,0);
			
			}
			
			else if(TransferDirection==I2C_DIRECTION_TRANSMIT)
			{
				//I2C_DIRECTION_TRANSMIT:主机发送数据:从机接收数据
				printf("主机发送数据:从机1准备开始接收数据\r\n");
				HAL_I2C_Slave_Seq_Receive_DMA(hi2c,receive_address,16,0);
			
			}
		}
		
		}
	
}
//主模式:DMA写入完成回调函数。从机给主机发送完一帧数据后调用

void HAL_I2C_SlaveTxCpltCallback(I2C_HandleTypeDef *hi2c)
{
	if(hi2c->Instance==I2C1)
	{
			printf("从机1发送数据完成\r\n");
	}

}
//存储器模式:DMA读取完成回调函数 ,从机接收调用

void HAL_I2C_SlaveRxCpltCallback(I2C_HandleTypeDef *hi2c)
{
	if(hi2c->Instance==I2C1)
	{
		printf("从机1接收主机的数据完成\r\n");
		for(uint8_t i=0;i<16;i++)
		{
			printf("receive_address[%d]=%d\r\n",i,receive_address[i]);
		}
		
	}
}
#include "stm32f1xx_hal.h"
#include "rcc.h"
#include "led.h"
#include "delay.h"
#include "OLED.h"
#include "i2c.h"
#include "UART.h"
#include <stdarg.h>
#include "stdio.h"
#include "key.h"


uint8_t i=0;


int main(void)
{

  
	HAL_Init();                         /* 初始化HAL库 */
  sys_stm32_clock_init(RCC_PLL_MUL9); /* 设置时钟, 72Mhz */
  delay_init(72);                     /* 延时初始化 */
  LED_Init();                        /* LED初始化 */
	Uart_Init(115200);
	LED_Exit_Init();
	OLED_Init();
	I2C_Init();
	printf("从机串口模块正常启动\r\n");
	HAL_I2C_EnableListen_IT(&I2C_Handle);  //	启动监听

	 while (1)
	 {
			
	 }   
}

 从机2   0x20

只需要在初始化的时候把自己的地址改为0x20,和改地址回调函数的0x20

void I2C_Init(void)
{
	I2C_Handle.Instance=I2C1;
	I2C_Handle.Init.ClockSpeed=100000;    ///通信速率1
	I2C_Handle.Init.AddressingMode=I2C_ADDRESSINGMODE_7BIT;  //7位寻址模式
	I2C_Handle.Init.OwnAddress1=0x20;     //从机的地址
	HAL_I2C_Init(&I2C_Handle);
}
//地址匹配中断回调
void HAL_I2C_AddrCallback(I2C_HandleTypeDef *hi2c, uint8_t TransferDirection, uint16_t AddrMatchCode)
{
	if(hi2c->Instance==I2C1)
	{
		//hi2c->PreviousState=0; 会引发另一个问题:从机没有停止信号
		if(AddrMatchCode==0x20)
		{
			if(TransferDirection==I2C_DIRECTION_RECEIVE)
			{
				//I2C_DIRECTION_RECEIVE:主机读取数据:从机发送数据
				printf("主机读取数据:从机2准备开始发送数据\r\n");
				HAL_I2C_Slave_Seq_Transmit_DMA(hi2c,send_address,16,0);
			
			}
			
			else if(TransferDirection==I2C_DIRECTION_TRANSMIT)
			{
				//I2C_DIRECTION_TRANSMIT:主机发送数据:从机接收数据
				printf("主机发送数据:从机2准备开始接收数据\r\n");
				HAL_I2C_Slave_Seq_Receive_DMA(hi2c,receive_address,16,0);
			
			}
		}
		
		}
	
}

发现:

主机不能连续2次以上的接收数据,接收否则接收的第二次会出现错误,影响整个通信;

 解决:给主机程序的接收回调函数中加一个把它的状态强行拉回来。不过,这个样子会导致从机没有停止信号。  也就是说不能以接收信号结尾。

存储器模式:DMA读取完成回调函数,主机接收从机发送来的数据,发送完成一次调用一次
它的触发条件是当主机从从机接收数据完成后。
void HAL_I2C_MasterRxCpltCallback(I2C_HandleTypeDef *hi2c)
{
		hi2c->PreviousState=0;    //会引发另一个问题:从机没有停止信号	
}

在:C:解决最后一阵必须是接收的情况    解决

B:一主二从--两

主机:

#include "stm32f1xx_hal.h"
#include "UART.h"
#include <stdarg.h>
#include "stdio.h"

I2C_HandleTypeDef I2C_Handle;
DMA_HandleTypeDef DMA_HandleTX;  //发送DMA
DMA_HandleTypeDef DMA_HandleRX;   //接收



uint8_t send_address1[16]={111,11,1,1,2,3,4,5,6,7,8,9,10,11,12,13};
uint8_t receive_address1[16];

uint8_t send_address2[16]={222,22,2,15,14,13,12,15,16,15,13,13,15,12,16,12};
uint8_t receive_address2[16];
void I2C_Init(void)
{
	I2C_Handle.Instance=I2C1;
	I2C_Handle.Init.ClockSpeed=100000;    ///通信速率1
	I2C_Handle.Init.AddressingMode=I2C_ADDRESSINGMODE_7BIT;  //7位寻址模式
	HAL_I2C_Init(&I2C_Handle);
}

void HAL_I2C_MspInit(I2C_HandleTypeDef *hi2c)
{
		if(hi2c->Instance==I2C1)
		{
			__HAL_RCC_I2C1_CLK_ENABLE();
			__HAL_RCC_GPIOB_CLK_ENABLE();
			__HAL_RCC_DMA1_CLK_ENABLE();
			
			//开启事件中断
			HAL_NVIC_SetPriority(I2C1_EV_IRQn,1,2);
			HAL_NVIC_EnableIRQ(I2C1_EV_IRQn);
				
			GPIO_InitTypeDef GPIO_InitType;
	
			GPIO_InitType.Mode=GPIO_MODE_AF_OD;    //复用开漏输出
			GPIO_InitType.Pin=GPIO_PIN_6|GPIO_PIN_7;
			GPIO_InitType.Speed=GPIO_SPEED_FREQ_HIGH;
			HAL_GPIO_Init(GPIOB,&GPIO_InitType); 
			
			//MDA通道6配置--发送数据
			DMA_HandleTX.Instance=DMA1_Channel6;
			//传输方向:内存(数组)--->外设
			DMA_HandleTX.Init.Direction=DMA_MEMORY_TO_PERIPH;
			DMA_HandleTX.Init.MemDataAlignment=DMA_MDATAALIGN_BYTE; //内存数据宽度
			DMA_HandleTX.Init.MemInc=DMA_MINC_ENABLE;    //存储区地址自增
			DMA_HandleTX.Init.Mode=DMA_NORMAL;
			DMA_HandleTX.Init.PeriphDataAlignment=DMA_PDATAALIGN_BYTE;//外设数据宽度
			DMA_HandleTX.Init.PeriphInc=DMA_PINC_DISABLE;//外设地址不自增
			DMA_HandleTX.Init.Priority=DMA_PRIORITY_MEDIUM;   //优先级
			__HAL_LINKDMA(&I2C_Handle,hdmatx,DMA_HandleTX);   //双向链接
			HAL_DMA_Init(&DMA_HandleTX);
			
			HAL_NVIC_SetPriority(DMA1_Channel6_IRQn,1,2);
			HAL_NVIC_EnableIRQ(DMA1_Channel6_IRQn);
			
			//MDA通道7配置---接收数据
			DMA_HandleRX.Instance=DMA1_Channel7;
			//传输方向:外设  ---> 内存(数组)
			DMA_HandleRX.Init.Direction=DMA_PERIPH_TO_MEMORY;
			DMA_HandleRX.Init.MemDataAlignment=DMA_MDATAALIGN_BYTE; //内存数据宽度
			DMA_HandleRX.Init.MemInc=DMA_MINC_ENABLE;    //存储区地址自增
			DMA_HandleRX.Init.Mode=DMA_NORMAL;
			DMA_HandleRX.Init.PeriphDataAlignment=DMA_PDATAALIGN_BYTE;//外设数据宽度
			DMA_HandleRX.Init.PeriphInc=DMA_PINC_DISABLE;//外设地址不自增
			DMA_HandleRX.Init.Priority=DMA_PRIORITY_MEDIUM;   //优先级
			__HAL_LINKDMA(&I2C_Handle,hdmarx,DMA_HandleRX);   //双向链接
			HAL_DMA_Init(&DMA_HandleRX);
			
			HAL_NVIC_SetPriority(DMA1_Channel7_IRQn,1,2);
			HAL_NVIC_EnableIRQ(DMA1_Channel7_IRQn);
			
		}
}



//开启事件中断
void I2C1_EV_IRQHandler()
{
	HAL_I2C_EV_IRQHandler(&I2C_Handle);
}

//开启MDA通道6发送中断
void DMA1_Channel6_IRQHandler()
{
	HAL_DMA_IRQHandler(&DMA_HandleTX);
}
//开启MDA通道6接收中断
void DMA1_Channel7_IRQHandler()
{
	HAL_DMA_IRQHandler(&DMA_HandleRX);
}

//主模式:DMA写入完成回调函数:主机给从机发送数据调用,发送完毕一次调用一次。
//它的触发条件通常是当主机向从机发送数据完成后。
void HAL_I2C_MasterTxCpltCallback(I2C_HandleTypeDef *hi2c)
{
	if(hi2c->Instance==I2C1)
	{
			if(hi2c->Devaddress==0x10)
			{
					if(hi2c->XferOptions==I2C_FIRST_AND_NEXT_FRAME)
					{
						printf("主机给从机1第一帧发送数据完成\r\n");
						
						HAL_I2C_Master_Seq_Transmit_DMA(&I2C_Handle,0x10,send_address1,16,I2C_LAST_FRAME);
					}
						else if (hi2c->XferOptions==I2C_LAST_FRAME)
						{
							printf("主机给从机1第二帧发送数据完成\r\n");
						
						}				
			}
		
			else if(hi2c->Devaddress==0x20)
			{
				if (hi2c->XferOptions==I2C_LAST_FRAME)
				{
					printf("主机给从机2发送数据完毕\r\n");
				}
				
			
			}	
	}

}
//存储器模式:DMA读取完成回调函数,主机接收从机发送来的数据,发送完成一次调用一次
//它的触发条件是当主机从从机接收数据完成后。
void HAL_I2C_MasterRxCpltCallback(I2C_HandleTypeDef *hi2c)
{
	if(hi2c->Instance==I2C1)
	{
		if(hi2c->Devaddress==0x10)
		{	
			}
			else if(hi2c->Devaddress==0x20)
			{
				if (hi2c->XferOptions==I2C_FIRST_FRAME)
				{
						printf("主机接收从机2的第一帧数据完毕\r\n");
						for(uint16_t j =0;j<16;j++)
						{
							printf("receive_address2[%d]=%d\r\n",j,receive_address2[j]);
						}
						HAL_I2C_Master_Seq_Transmit_DMA(&I2C_Handle,0x20,send_address2,16,I2C_LAST_FRAME);

				}
				
					
			}
	}
}
#include "stm32f1xx_hal.h"
#include "rcc.h"
#include "led.h"
#include "delay.h"
#include "OLED.h"
#include "i2c.h"
#include "UART.h"
#include <stdarg.h>
#include "stdio.h"
#include "key.h"


uint8_t i=0;

int main(void)
{

  
	HAL_Init();                         /* 初始化HAL库 */
  sys_stm32_clock_init(RCC_PLL_MUL9); /* 设置时钟, 72Mhz */
  delay_init(72);                     /* 延时初始化 */
  LED_Init();                        /* LED初始化 */
	Uart_Init(115200);
	LED_Exit_Init();
	OLED_Init();
	KEY_Init();
	I2C_Init();
	printf("串口模块正常启动\r\n");
	

	 while (1)
	 {
			if(KEY_Scan())
			{	
				//PA0按下:具体操作:主机1给从机两次发发
				printf("PA0按下\r\n");
				HAL_I2C_Master_Seq_Transmit_DMA(&I2C_Handle,0x10,send_address1,16,I2C_FIRST_AND_NEXT_FRAME);
			}
			
			else if(KEY_Scan1())
			{	
				//PA1按下:主机先接收从机2的数据,主机先给从机2发送数据.
				printf("PA1按下\r\n");
				HAL_I2C_Master_Seq_Receive_DMA(&I2C_Handle,0x20,receive_address2,16,I2C_FIRST_FRAME);

			}
			
			
	 }   
}

从1   0x10

#include "stm32f1xx_hal.h"
#include "UART.h"
#include <stdarg.h>
#include "stdio.h"

I2C_HandleTypeDef I2C_Handle;
DMA_HandleTypeDef DMA_HandleTX;  //发送DMA
DMA_HandleTypeDef DMA_HandleRX;   //接收

uint8_t send_address[16]={11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11};
uint8_t receive_address[32];


void I2C_Init(void)
{
	I2C_Handle.Instance=I2C1;
	I2C_Handle.Init.ClockSpeed=100000;    ///通信速率1
	I2C_Handle.Init.AddressingMode=I2C_ADDRESSINGMODE_7BIT;  //7位寻址模式
	I2C_Handle.Init.OwnAddress1=0x10;     //从机的地址
	HAL_I2C_Init(&I2C_Handle);
}

void HAL_I2C_MspInit(I2C_HandleTypeDef *hi2c)
{
		if(hi2c->Instance==I2C1)
		{
			__HAL_RCC_I2C1_CLK_ENABLE();
			__HAL_RCC_GPIOB_CLK_ENABLE();
			__HAL_RCC_DMA1_CLK_ENABLE();
			
			//开启事件中断
			HAL_NVIC_SetPriority(I2C1_EV_IRQn,1,2);
			HAL_NVIC_EnableIRQ(I2C1_EV_IRQn);
				
			GPIO_InitTypeDef GPIO_InitType;
	
			GPIO_InitType.Mode=GPIO_MODE_AF_OD;    //复用开漏输出
			GPIO_InitType.Pin=GPIO_PIN_6|GPIO_PIN_7;
			GPIO_InitType.Speed=GPIO_SPEED_FREQ_HIGH;
			HAL_GPIO_Init(GPIOB,&GPIO_InitType); 
			
			//MDA通道6配置--发送数据
			DMA_HandleTX.Instance=DMA1_Channel6;
			//传输方向:内存(数组)--->外设
			DMA_HandleTX.Init.Direction=DMA_MEMORY_TO_PERIPH;
			DMA_HandleTX.Init.MemDataAlignment=DMA_MDATAALIGN_BYTE; //内存数据宽度
			DMA_HandleTX.Init.MemInc=DMA_MINC_ENABLE;    //存储区地址自增
			DMA_HandleTX.Init.Mode=DMA_NORMAL;
			DMA_HandleTX.Init.PeriphDataAlignment=DMA_PDATAALIGN_BYTE;//外设数据宽度
			DMA_HandleTX.Init.PeriphInc=DMA_PINC_DISABLE;//外设地址不自增
			DMA_HandleTX.Init.Priority=DMA_PRIORITY_MEDIUM;   //优先级
			__HAL_LINKDMA(hi2c,hdmatx,DMA_HandleTX);   //双向链接
			HAL_DMA_Init(&DMA_HandleTX);
			
			HAL_NVIC_SetPriority(DMA1_Channel6_IRQn,1,2);
			HAL_NVIC_EnableIRQ(DMA1_Channel6_IRQn);
			
			//MDA通道7配置---接收数据
			DMA_HandleRX.Instance=DMA1_Channel7;
			//传输方向:外设  ---> 内存(数组)
			DMA_HandleRX.Init.Direction=DMA_PERIPH_TO_MEMORY;
			DMA_HandleRX.Init.MemDataAlignment=DMA_MDATAALIGN_BYTE; //内存数据宽度
			DMA_HandleRX.Init.MemInc=DMA_MINC_ENABLE;    //存储区地址自增
			DMA_HandleRX.Init.Mode=DMA_NORMAL;
			DMA_HandleRX.Init.PeriphDataAlignment=DMA_PDATAALIGN_BYTE;//外设数据宽度
			DMA_HandleRX.Init.PeriphInc=DMA_PINC_DISABLE;//外设地址不自增
			DMA_HandleRX.Init.Priority=DMA_PRIORITY_MEDIUM;   //优先级
			__HAL_LINKDMA(hi2c,hdmarx,DMA_HandleRX);   //双向链接
			HAL_DMA_Init(&DMA_HandleRX);
			
			HAL_NVIC_SetPriority(DMA1_Channel7_IRQn,1,2);
			HAL_NVIC_EnableIRQ(DMA1_Channel7_IRQn);
			
		}
}



//开启事件中断
void I2C1_EV_IRQHandler()
{
	HAL_I2C_EV_IRQHandler(&I2C_Handle);
}

//开启MDA通道6发送中断
void DMA1_Channel6_IRQHandler()
{
	HAL_DMA_IRQHandler(I2C_Handle.hdmatx);
}
//开启MDA通道6接收中断
void DMA1_Channel7_IRQHandler()
{
	HAL_DMA_IRQHandler(I2C_Handle.hdmarx);
}

//监听功能回调函数
//监听动作全部完成:调用这个回调函数
void HAL_I2C_ListenCpltCallback(I2C_HandleTypeDef *hi2c)
{
	if(hi2c->Instance==I2C1)
	{
			printf("从机1监听完成\r\n");
			HAL_I2C_EnableListen_IT(&I2C_Handle);  //	开启下一次监听
	}


}
//地址匹配中断回调
void HAL_I2C_AddrCallback(I2C_HandleTypeDef *hi2c, uint8_t TransferDirection, uint16_t AddrMatchCode)
{
	if(hi2c->Instance==I2C1)
	{
		//hi2c->PreviousState=0; 会引发另一个问题:从机没有停止信号
		if(AddrMatchCode==0x10)
		{
			if(TransferDirection==I2C_DIRECTION_RECEIVE)
			{
				//I2C_DIRECTION_RECEIVE:主机读取数据:从机发送数据
				printf("主机读取数据:从机1准备开始发送数据\r\n");
				HAL_I2C_Slave_Seq_Transmit_DMA(hi2c,send_address,16,0);
			
			}
			
			else if(TransferDirection==I2C_DIRECTION_TRANSMIT)
			{
				//I2C_DIRECTION_TRANSMIT:主机发送数据:从机接收数据
				
				//由于主机给从机2次都是发送的操作:同向的之间是不会叉RESTAR信号(没有从机地址)
				//主机虽然是发送了2次,但是对于从机而言是一次
				//在这一次把,主机发送的32个字节全部拿到
				
				
				
				printf("主机发送数据:从机1准备开始接收数据\r\n");
				HAL_I2C_Slave_Seq_Receive_DMA(hi2c,receive_address,32,0);
			
			}
		}
		
		}
	
}
//主模式:DMA写入完成回调函数。
//它的触发条件通常是当主机向从机发送数据完成后。
void HAL_I2C_SlaveTxCpltCallback(I2C_HandleTypeDef *hi2c)
{
	if(hi2c->Instance==I2C1)
	{
			printf("从机1发送数据完成\r\n");
	}

}
//存储器模式:DMA读取完成回调函数
//它的触发条件是当主机从从机接收数据完成后。
void HAL_I2C_SlaveRxCpltCallback(I2C_HandleTypeDef *hi2c)
{
	if(hi2c->Instance==I2C1)
	{
		printf("从机1接收主机的数据完成\r\n");
		for(uint8_t h=0;h<32;h++)
		{
			printf("receive_address[%d]=%d\r\n",h,receive_address[h]);
		}
		
	}
}
#include "stm32f1xx_hal.h"
#include "rcc.h"
#include "led.h"
#include "delay.h"
#include "OLED.h"
#include "i2c.h"
#include "UART.h"
#include <stdarg.h>
#include "stdio.h"
#include "key.h"


uint8_t i=0;


int main(void)
{

  
	HAL_Init();                         /* 初始化HAL库 */
  sys_stm32_clock_init(RCC_PLL_MUL9); /* 设置时钟, 72Mhz */
  delay_init(72);                     /* 延时初始化 */
  LED_Init();                        /* LED初始化 */
	Uart_Init(115200);
	LED_Exit_Init();
	OLED_Init();
	I2C_Init();
	printf("从机串口模块正常启动\r\n");
	HAL_I2C_EnableListen_IT(&I2C_Handle);  //	启动监听

	 while (1)
	 {
			
	 }   
}

从2:

#include "stm32f1xx_hal.h"
#include "UART.h"
#include <stdarg.h>
#include "stdio.h"

I2C_HandleTypeDef I2C_Handle;
DMA_HandleTypeDef DMA_HandleTX;  //发送DMA
DMA_HandleTypeDef DMA_HandleRX;   //接收

uint8_t send_address[16]={22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22};
uint8_t receive_address[16];


void I2C_Init(void)
{
	I2C_Handle.Instance=I2C1;
	I2C_Handle.Init.ClockSpeed=100000;    ///通信速率1
	I2C_Handle.Init.AddressingMode=I2C_ADDRESSINGMODE_7BIT;  //7位寻址模式
	I2C_Handle.Init.OwnAddress1=0x20;     //从机的地址
	HAL_I2C_Init(&I2C_Handle);
}

void HAL_I2C_MspInit(I2C_HandleTypeDef *hi2c)
{
		if(hi2c->Instance==I2C1)
		{
			__HAL_RCC_I2C1_CLK_ENABLE();
			__HAL_RCC_GPIOB_CLK_ENABLE();
			__HAL_RCC_DMA1_CLK_ENABLE();
			
			//开启事件中断
			HAL_NVIC_SetPriority(I2C1_EV_IRQn,1,2);
			HAL_NVIC_EnableIRQ(I2C1_EV_IRQn);
				
			GPIO_InitTypeDef GPIO_InitType;
	
			GPIO_InitType.Mode=GPIO_MODE_AF_OD;    //复用开漏输出
			GPIO_InitType.Pin=GPIO_PIN_6|GPIO_PIN_7;
			GPIO_InitType.Speed=GPIO_SPEED_FREQ_HIGH;
			HAL_GPIO_Init(GPIOB,&GPIO_InitType); 
			
			//MDA通道6配置--发送数据
			DMA_HandleTX.Instance=DMA1_Channel6;
			//传输方向:内存(数组)--->外设
			DMA_HandleTX.Init.Direction=DMA_MEMORY_TO_PERIPH;
			DMA_HandleTX.Init.MemDataAlignment=DMA_MDATAALIGN_BYTE; //内存数据宽度
			DMA_HandleTX.Init.MemInc=DMA_MINC_ENABLE;    //存储区地址自增
			DMA_HandleTX.Init.Mode=DMA_NORMAL;
			DMA_HandleTX.Init.PeriphDataAlignment=DMA_PDATAALIGN_BYTE;//外设数据宽度
			DMA_HandleTX.Init.PeriphInc=DMA_PINC_DISABLE;//外设地址不自增
			DMA_HandleTX.Init.Priority=DMA_PRIORITY_MEDIUM;   //优先级
			__HAL_LINKDMA(hi2c,hdmatx,DMA_HandleTX);   //双向链接
			HAL_DMA_Init(&DMA_HandleTX);
			
			HAL_NVIC_SetPriority(DMA1_Channel6_IRQn,1,2);
			HAL_NVIC_EnableIRQ(DMA1_Channel6_IRQn);
			
			//MDA通道7配置---接收数据
			DMA_HandleRX.Instance=DMA1_Channel7;
			//传输方向:外设  ---> 内存(数组)
			DMA_HandleRX.Init.Direction=DMA_PERIPH_TO_MEMORY;
			DMA_HandleRX.Init.MemDataAlignment=DMA_MDATAALIGN_BYTE; //内存数据宽度
			DMA_HandleRX.Init.MemInc=DMA_MINC_ENABLE;    //存储区地址自增
			DMA_HandleRX.Init.Mode=DMA_NORMAL;
			DMA_HandleRX.Init.PeriphDataAlignment=DMA_PDATAALIGN_BYTE;//外设数据宽度
			DMA_HandleRX.Init.PeriphInc=DMA_PINC_DISABLE;//外设地址不自增
			DMA_HandleRX.Init.Priority=DMA_PRIORITY_MEDIUM;   //优先级
			__HAL_LINKDMA(hi2c,hdmarx,DMA_HandleRX);   //双向链接
			HAL_DMA_Init(&DMA_HandleRX);
			
			HAL_NVIC_SetPriority(DMA1_Channel7_IRQn,1,2);
			HAL_NVIC_EnableIRQ(DMA1_Channel7_IRQn);
			
		}
}



//开启事件中断
void I2C1_EV_IRQHandler()
{
	HAL_I2C_EV_IRQHandler(&I2C_Handle);
}

//开启MDA通道6发送中断
void DMA1_Channel6_IRQHandler()
{
	HAL_DMA_IRQHandler(I2C_Handle.hdmatx);
}
//开启MDA通道6接收中断
void DMA1_Channel7_IRQHandler()
{
	HAL_DMA_IRQHandler(I2C_Handle.hdmarx);
}

//监听功能回调函数
//监听动作全部完成:调用这个回调函数
void HAL_I2C_ListenCpltCallback(I2C_HandleTypeDef *hi2c)
{
	if(hi2c->Instance==I2C1)
	{
			printf("从机2监听完成");
			HAL_I2C_EnableListen_IT(&I2C_Handle);  //	开启下一次监听
	}


}
//地址匹配中断回调
void HAL_I2C_AddrCallback(I2C_HandleTypeDef *hi2c, uint8_t TransferDirection, uint16_t AddrMatchCode)
{
	if(hi2c->Instance==I2C1)
	{
	
		if(AddrMatchCode==0x20)
		{
			//主机先发送在接收,所以会进入这个回调函数2次,因为方向发生了变化,有了RESTAR信号
			if(TransferDirection==I2C_DIRECTION_RECEIVE)
			{
				//I2C_DIRECTION_RECEIVE:主机读取数据:从机发送数据
				printf("主机读取数据:从机2准备开始发送数据\r\n");
				HAL_I2C_Slave_Seq_Transmit_DMA(hi2c,send_address,16,0);
			
			}
			
			else if(TransferDirection==I2C_DIRECTION_TRANSMIT)
			{
				//I2C_DIRECTION_TRANSMIT:主机发送数据:从机接收数据
				printf("主机发送数据:从机2准备开始接收数据\r\n");
				HAL_I2C_Slave_Seq_Receive_DMA(hi2c,receive_address,16,0);
			
			}
		} 
		
		}
	
}
//主模式:DMA写入完成回调函数。
//它的触发条件通常是当主机向从机发送数据完成后。
void HAL_I2C_SlaveTxCpltCallback(I2C_HandleTypeDef *hi2c)
{
	if(hi2c->Instance==I2C1)
	{
			printf("从机2发送数据完成\r\n");
	}

}
//存储器模式:DMA读取完成回调函数
//它的触发条件是当主机从从机接收数据完成后。
void HAL_I2C_SlaveRxCpltCallback(I2C_HandleTypeDef *hi2c)
{
	if(hi2c->Instance==I2C1)
	{
		printf("从机2接收主机的数据完成\r\n");
		for(uint8_t i=0;i<16;i++)
		{
			printf("receive_address[%d]=%d\r\n",i,receive_address[i]);
		}
		
	}
}

#include "stm32f1xx_hal.h"
#include "rcc.h"
#include "led.h"
#include "delay.h"
#include "OLED.h"
#include "i2c.h"
#include "UART.h"
#include <stdarg.h>
#include "stdio.h"
#include "key.h"


uint8_t i=0;


int main(void)
{

  
	HAL_Init();                         /* 初始化HAL库 */
  sys_stm32_clock_init(RCC_PLL_MUL9); /* 设置时钟, 72Mhz */
  delay_init(72);                     /* 延时初始化 */
  LED_Init();                        /* LED初始化 */
	Uart_Init(115200);
	LED_Exit_Init();
	OLED_Init();
	I2C_Init();
	printf("从机串口模块正常启动\r\n");
	HAL_I2C_EnableListen_IT(&I2C_Handle);  //	启动监听

	 while (1)
	 {
			
	 }   
}

C:解决最后一阵必须是接收的情况

 解决上面遗留的BUG点,为了方便我们只在这里只做主机从机2的

主机程序:

#include "stm32f1xx_hal.h"
#include "UART.h"
#include <stdarg.h>
#include "stdio.h"

I2C_HandleTypeDef I2C_Handle;
DMA_HandleTypeDef DMA_HandleTX;  //发送DMA
DMA_HandleTypeDef DMA_HandleRX;   //接收



uint8_t send_address1[16]={111,11,1,1,2,3,4,5,6,7,8,9,10,11,12,13};
uint8_t receive_address1[16];

uint8_t send_address2[32]={1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16};
uint8_t receive_address2[32];
void I2C_Init(void)
{
	I2C_Handle.Instance=I2C1;
	I2C_Handle.Init.ClockSpeed=100000;    ///通信速率1
	I2C_Handle.Init.AddressingMode=I2C_ADDRESSINGMODE_7BIT;  //7位寻址模式
	HAL_I2C_Init(&I2C_Handle);
}

void HAL_I2C_MspInit(I2C_HandleTypeDef *hi2c)
{
		if(hi2c->Instance==I2C1)
		{
			__HAL_RCC_I2C1_CLK_ENABLE();
			__HAL_RCC_GPIOB_CLK_ENABLE();
			__HAL_RCC_DMA1_CLK_ENABLE();
			
			//开启事件中断
			HAL_NVIC_SetPriority(I2C1_EV_IRQn,1,2);
			HAL_NVIC_EnableIRQ(I2C1_EV_IRQn);
				
			GPIO_InitTypeDef GPIO_InitType;
	
			GPIO_InitType.Mode=GPIO_MODE_AF_OD;    //复用开漏输出
			GPIO_InitType.Pin=GPIO_PIN_6|GPIO_PIN_7;
			GPIO_InitType.Speed=GPIO_SPEED_FREQ_HIGH;
			HAL_GPIO_Init(GPIOB,&GPIO_InitType); 
			
			//MDA通道6配置--发送数据
			DMA_HandleTX.Instance=DMA1_Channel6;
			//传输方向:内存(数组)--->外设
			DMA_HandleTX.Init.Direction=DMA_MEMORY_TO_PERIPH;
			DMA_HandleTX.Init.MemDataAlignment=DMA_MDATAALIGN_BYTE; //内存数据宽度
			DMA_HandleTX.Init.MemInc=DMA_MINC_ENABLE;    //存储区地址自增
			DMA_HandleTX.Init.Mode=DMA_NORMAL;
			DMA_HandleTX.Init.PeriphDataAlignment=DMA_PDATAALIGN_BYTE;//外设数据宽度
			DMA_HandleTX.Init.PeriphInc=DMA_PINC_DISABLE;//外设地址不自增
			DMA_HandleTX.Init.Priority=DMA_PRIORITY_MEDIUM;   //优先级
			__HAL_LINKDMA(&I2C_Handle,hdmatx,DMA_HandleTX);   //双向链接
			HAL_DMA_Init(&DMA_HandleTX);
			
			HAL_NVIC_SetPriority(DMA1_Channel6_IRQn,1,2);
			HAL_NVIC_EnableIRQ(DMA1_Channel6_IRQn);
			
			//MDA通道7配置---接收数据
			DMA_HandleRX.Instance=DMA1_Channel7;
			//传输方向:外设  ---> 内存(数组)
			DMA_HandleRX.Init.Direction=DMA_PERIPH_TO_MEMORY;
			DMA_HandleRX.Init.MemDataAlignment=DMA_MDATAALIGN_BYTE; //内存数据宽度
			DMA_HandleRX.Init.MemInc=DMA_MINC_ENABLE;    //存储区地址自增
			DMA_HandleRX.Init.Mode=DMA_NORMAL;
			DMA_HandleRX.Init.PeriphDataAlignment=DMA_PDATAALIGN_BYTE;//外设数据宽度
			DMA_HandleRX.Init.PeriphInc=DMA_PINC_DISABLE;//外设地址不自增
			DMA_HandleRX.Init.Priority=DMA_PRIORITY_MEDIUM;   //优先级
			__HAL_LINKDMA(&I2C_Handle,hdmarx,DMA_HandleRX);   //双向链接
			HAL_DMA_Init(&DMA_HandleRX);
			
			HAL_NVIC_SetPriority(DMA1_Channel7_IRQn,1,2);
			HAL_NVIC_EnableIRQ(DMA1_Channel7_IRQn);
			
		}
}



//开启事件中断
void I2C1_EV_IRQHandler()
{
	HAL_I2C_EV_IRQHandler(&I2C_Handle);
}

//开启MDA通道6发送中断
void DMA1_Channel6_IRQHandler()
{
	HAL_DMA_IRQHandler(&DMA_HandleTX);
}
//开启MDA通道6接收中断
void DMA1_Channel7_IRQHandler()
{
	HAL_DMA_IRQHandler(&DMA_HandleRX);
}

//主模式:DMA写入完成回调函数:主机给从机发送数据调用,发送完毕一次调用一次。
//它的触发条件通常是当主机向从机发送数据完成后。
void HAL_I2C_MasterTxCpltCallback(I2C_HandleTypeDef *hi2c)
{
	if(hi2c->Instance==I2C1)
	{
			if(hi2c->Devaddress==0x10)
			{
					if(hi2c->XferOptions==I2C_FIRST_AND_LAST_FRAME)
					{
						printf("主机给从机1第一帧发送数据完成\r\n");
						HAL_I2C_Master_Seq_Transmit_DMA(&I2C_Handle,0x10,send_address1,16,I2C_LAST_FRAME);
					}
						else if (hi2c->XferOptions==I2C_LAST_FRAME)
						{
							printf("主机给从机1第二帧发送数据完成\r\n");
						
						}				
			}
		
			else if(hi2c->Devaddress==0x20)
			{
				if (hi2c->XferOptions==I2C_LAST_FRAME)
				{
					printf("为了消除BUG使最后一帧不是接收发送0字节\r\n");
				}
				else if (hi2c->XferOptions==I2C_NEXT_FRAME)
				{
					printf("主机给从机2第二帧发送数据完毕\r\n");
					HAL_I2C_Master_Seq_Receive_DMA(&I2C_Handle,0x20,receive_address2,3,I2C_LAST_FRAME_NO_STOP);
				}
			
			}	
	}

}
//存储器模式:DMA读取完成回调函数,主机接收从机发送来的数据,发送完成一次调用一次
//它的触发条件是当主机从从机接收数据完成后。
void HAL_I2C_MasterRxCpltCallback(I2C_HandleTypeDef *hi2c)
{
	if(hi2c->Instance==I2C1)
	{
		if(hi2c->Devaddress==0x10)
		{	
			}
			else if(hi2c->Devaddress==0x20)
			{
				if (hi2c->XferOptions==I2C_FIRST_FRAME)
				{
						printf("主机接收从机2的第一帧数据完毕\r\n");
						for(uint16_t j =0;j<3;j++)
						{
							printf("receive_address2[%d]=%d\r\n",j,receive_address2[j]);
						}
						HAL_I2C_Master_Seq_Transmit_DMA(&I2C_Handle,0x20,send_address2,3,I2C_NEXT_FRAME);
			
				}
				
				
				else if (hi2c->XferOptions==I2C_LAST_FRAME_NO_STOP)
				{
						
						printf("主机接收从机2的第二帧数据完毕\r\n");
						for(uint16_t j =0;j<3;j++)
						{
							printf("receive_address2[%d]=%d\r\n",j,receive_address2[j]);
						}

						HAL_I2C_Master_Seq_Transmit_DMA(&I2C_Handle,0x20,&send_address2[3],0,I2C_LAST_FRAME);
				}
					
			}
	}
}
#include "stm32f1xx_hal.h"
#include "rcc.h"
#include "led.h"
#include "delay.h"
#include "OLED.h"
#include "i2c.h"
#include "UART.h"
#include <stdarg.h>
#include "stdio.h"
#include "key.h"


uint8_t i=0;

int main(void)
{

  
	HAL_Init();                         /* 初始化HAL库 */
  sys_stm32_clock_init(RCC_PLL_MUL9); /* 设置时钟, 72Mhz */
  delay_init(72);                     /* 延时初始化 */
  LED_Init();                        /* LED初始化 */
	Uart_Init(115200);
	LED_Exit_Init();
	OLED_Init();
	KEY_Init();
	I2C_Init();
	printf("串口模块正常启动\r\n");
	

	 while (1)
	 {
		
			if(KEY_Scan1())
			{	
				//PA1按下:
				printf("PA1按下\r\n");
				HAL_I2C_Master_Seq_Receive_DMA(&I2C_Handle,0x20,receive_address2,3,I2C_FIRST_FRAME);

			}
			
			
	 }   
}

从机0x20

#include "stm32f1xx_hal.h"
#include "UART.h"
#include <stdarg.h>
#include "stdio.h"

I2C_HandleTypeDef I2C_Handle;
DMA_HandleTypeDef DMA_HandleTX;  //发送DMA
DMA_HandleTypeDef DMA_HandleRX;   //接收

uint8_t send_address[16]={11,1,11,22,2,22,22,22,22,22,22,22,22,22,22,22};
uint8_t receive_address[16];


void I2C_Init(void)
{
	I2C_Handle.Instance=I2C1;
	I2C_Handle.Init.ClockSpeed=100000;    ///通信速率1
	I2C_Handle.Init.AddressingMode=I2C_ADDRESSINGMODE_7BIT;  //7位寻址模式
	I2C_Handle.Init.OwnAddress1=0x20;     //从机的地址
	HAL_I2C_Init(&I2C_Handle);
}

void HAL_I2C_MspInit(I2C_HandleTypeDef *hi2c)
{
		if(hi2c->Instance==I2C1)
		{
			__HAL_RCC_I2C1_CLK_ENABLE();
			__HAL_RCC_GPIOB_CLK_ENABLE();
			__HAL_RCC_DMA1_CLK_ENABLE();
			
			//开启事件中断
			HAL_NVIC_SetPriority(I2C1_EV_IRQn,1,2);
			HAL_NVIC_EnableIRQ(I2C1_EV_IRQn);
				
			GPIO_InitTypeDef GPIO_InitType;
	
			GPIO_InitType.Mode=GPIO_MODE_AF_OD;    //复用开漏输出
			GPIO_InitType.Pin=GPIO_PIN_6|GPIO_PIN_7;
			GPIO_InitType.Speed=GPIO_SPEED_FREQ_HIGH;
			HAL_GPIO_Init(GPIOB,&GPIO_InitType); 
			
			//MDA通道6配置--发送数据
			DMA_HandleTX.Instance=DMA1_Channel6;
			//传输方向:内存(数组)--->外设
			DMA_HandleTX.Init.Direction=DMA_MEMORY_TO_PERIPH;
			DMA_HandleTX.Init.MemDataAlignment=DMA_MDATAALIGN_BYTE; //内存数据宽度
			DMA_HandleTX.Init.MemInc=DMA_MINC_ENABLE;    //存储区地址自增
			DMA_HandleTX.Init.Mode=DMA_NORMAL;
			DMA_HandleTX.Init.PeriphDataAlignment=DMA_PDATAALIGN_BYTE;//外设数据宽度
			DMA_HandleTX.Init.PeriphInc=DMA_PINC_DISABLE;//外设地址不自增
			DMA_HandleTX.Init.Priority=DMA_PRIORITY_MEDIUM;   //优先级
			__HAL_LINKDMA(hi2c,hdmatx,DMA_HandleTX);   //双向链接
			HAL_DMA_Init(&DMA_HandleTX);
			
			HAL_NVIC_SetPriority(DMA1_Channel6_IRQn,1,2);
			HAL_NVIC_EnableIRQ(DMA1_Channel6_IRQn);
			
			//MDA通道7配置---接收数据
			DMA_HandleRX.Instance=DMA1_Channel7;
			//传输方向:外设  ---> 内存(数组)
			DMA_HandleRX.Init.Direction=DMA_PERIPH_TO_MEMORY;
			DMA_HandleRX.Init.MemDataAlignment=DMA_MDATAALIGN_BYTE; //内存数据宽度
			DMA_HandleRX.Init.MemInc=DMA_MINC_ENABLE;    //存储区地址自增
			DMA_HandleRX.Init.Mode=DMA_NORMAL;
			DMA_HandleRX.Init.PeriphDataAlignment=DMA_PDATAALIGN_BYTE;//外设数据宽度
			DMA_HandleRX.Init.PeriphInc=DMA_PINC_DISABLE;//外设地址不自增
			DMA_HandleRX.Init.Priority=DMA_PRIORITY_MEDIUM;   //优先级
			__HAL_LINKDMA(hi2c,hdmarx,DMA_HandleRX);   //双向链接
			HAL_DMA_Init(&DMA_HandleRX);
			
			HAL_NVIC_SetPriority(DMA1_Channel7_IRQn,1,2);
			HAL_NVIC_EnableIRQ(DMA1_Channel7_IRQn);
			
		}
}



//开启事件中断
void I2C1_EV_IRQHandler()
{
	HAL_I2C_EV_IRQHandler(&I2C_Handle);
}

//开启MDA通道6发送中断
void DMA1_Channel6_IRQHandler()
{
	HAL_DMA_IRQHandler(I2C_Handle.hdmatx);
}
//开启MDA通道6接收中断
void DMA1_Channel7_IRQHandler()
{
	HAL_DMA_IRQHandler(I2C_Handle.hdmarx);
}

//监听功能回调函数
//监听动作全部完成:调用这个回调函数
void HAL_I2C_ListenCpltCallback(I2C_HandleTypeDef *hi2c)
{
	if(hi2c->Instance==I2C1)
	{
			printf("从机2监听完成");
			HAL_I2C_EnableListen_IT(&I2C_Handle);  //	开启下一次监听
			hi2c->XferOptions=0;   //注意每次监控完毕,需要把这个状态重置为0,否则通信出错误
	}


}
//地址匹配中断回调
void HAL_I2C_AddrCallback(I2C_HandleTypeDef *hi2c, uint8_t TransferDirection, uint16_t AddrMatchCode)
{
	if(hi2c->Instance==I2C1)
	{
		/*HAL_I2C_Slave_Seq_Transmit_DMA(hi2c,send_address,3,I2C_FIRST_FRAME)
		**上面这中发生或者是接收API函数的第四个参数,只是为了表示它的状态,
		**并不像主机哪个样子
		**
		*/
		if(AddrMatchCode==0x20)
		{
			//主机先发送在接收所以,会不会进入这个回调函数2次
			if(TransferDirection==I2C_DIRECTION_RECEIVE)
			{
				//开始进来的第四给参数为0;
				if(hi2c->XferOptions==0)
				{
					//I2C_DIRECTION_RECEIVE:主机读取数据:从机发送数据
					printf("主机读取数据:从机2准备开始发送数据\r\n");
					HAL_I2C_Slave_Seq_Transmit_DMA(hi2c,send_address,3,I2C_FIRST_FRAME);	
				}
				else if(hi2c->XferOptions==I2C_NEXT_FRAME)
				{
						HAL_I2C_Slave_Seq_Transmit_DMA(hi2c,&send_address[3],3,I2C_FIRST_FRAME);
				
				}
			}
			
			else if(TransferDirection==I2C_DIRECTION_TRANSMIT)
			{
				if(hi2c->XferOptions==I2C_FIRST_FRAME)
				{
						//I2C_DIRECTION_TRANSMIT:主机发送数据:从机接收数据
						printf("主机发送数据:从机2准备开始接收数据\r\n");
						HAL_I2C_Slave_Seq_Receive_DMA(hi2c,receive_address,3,I2C_NEXT_FRAME);
				}
	
			}
		}
		
		}
	
}
//主模式:DMA写入完成回调函数。
//它的触发条件通常是当主机向从机发送数据完成后。
void HAL_I2C_SlaveTxCpltCallback(I2C_HandleTypeDef *hi2c)
{
	if(hi2c->Instance==I2C1)
	{
			printf("从机2发送数据完成\r\n");
	}

}
//存储器模式:DMA读取完成回调函数
//它的触发条件是当主机从从机接收数据完成后。
void HAL_I2C_SlaveRxCpltCallback(I2C_HandleTypeDef *hi2c)
{
	if(hi2c->Instance==I2C1)
	{
		printf("从机2接收主机的数据完成\r\n");
		for(uint8_t i=0;i<3;i++)
		{
			printf("receive_address[%d]=%d\r\n",i,receive_address[i]);
		}
		
	}
}
#include "stm32f1xx_hal.h"
#include "rcc.h"
#include "led.h"
#include "delay.h"
#include "OLED.h"
#include "i2c.h"
#include "UART.h"
#include <stdarg.h>
#include "stdio.h"
#include "key.h"


uint8_t i=0;


int main(void)
{

  
	HAL_Init();                         /* 初始化HAL库 */
  sys_stm32_clock_init(RCC_PLL_MUL9); /* 设置时钟, 72Mhz */
  delay_init(72);                     /* 延时初始化 */
  LED_Init();                        /* LED初始化 */
	Uart_Init(115200);
	LED_Exit_Init();
	OLED_Init();
	I2C_Init();
	printf("从机串口模块正常启动\r\n");
	HAL_I2C_EnableListen_IT(&I2C_Handle);  //	启动监听

	 while (1)
	 {
			
	 }   
}

D:十位寻址

十位地址+R/W位显然,1位字节已经不能满足我们了,那我们就使用2个字节。 

没有太大的变化也是微调 ,实验内容和B:一主二从--两帧 一样

主机:初始化的时候改为10位寻址模式

void I2C_Init(void)
{
	I2C_Handle.Instance=I2C1;
	I2C_Handle.Init.ClockSpeed=100000;    ///通信速率1
	I2C_Handle.Init.AddressingMode=I2C_ADDRESSINGMODE_10BIT;  //10位寻址模式
	HAL_I2C_Init(&I2C_Handle);
}

在把所有出现了,从机地址的地方全部改为新的地址。

从机:

void I2C_Init(void)
{
	I2C_Handle.Instance=I2C1;
	I2C_Handle.Init.ClockSpeed=100000;    ///通信速率1
	I2C_Handle.Init.AddressingMode=I2C_ADDRESSINGMODE_10BIT;  //10位寻址模式
	I2C_Handle.Init.OwnAddress1=0x1FF;     //从机的地址
	HAL_I2C_Init(&I2C_Handle);
}

在把所有出现了,从机地址的地方全部改为新的地址。

E:从机双地址寻址

STM32在做从机的时候,是可以有2个从机地址的。主机发送任意一个都可以找到从机。

没有太大的变化也是微调 ,实验内容和B:一主二从--两帧 一样,不过就是把从机的地址改为了2个

主机:


#define address1 0x10
#define address2 0x30

void I2C_Init(void)
{
	I2C_Handle.Instance=I2C1;
	I2C_Handle.Init.ClockSpeed=100000;    ///通信速率1
	I2C_Handle.Init.AddressingMode=I2C_ADDRESSINGMODE_7BIT;  //10位寻址模式
	I2C_Handle.Init.DualAddressMode=I2C_DUALADDRESS_ENABLE; //打开双地址模式,默认为关闭
	HAL_I2C_Init(&I2C_Handle);
}

发现改address1 为0x10或者0x20都可以正常和从机1通信,因为从机1有2个地址。

从机

#define address0 0x10
#define address 0x20
void I2C_Init(void)
{
	I2C_Handle.Instance=I2C1;
	I2C_Handle.Init.ClockSpeed=100000;    ///通信速率1
	I2C_Handle.Init.AddressingMode=I2C_ADDRESSINGMODE_7BIT;  //7位寻址模式
	I2C_Handle.Init.OwnAddress1=address0;     //从机的地址
	I2C_Handle.Init.OwnAddress2=address;     //从机的地址
	I2C_Handle.Init.DualAddressMode=I2C_DUALADDRESS_ENABLE; //打开双地址模式,默认为
	HAL_I2C_Init(&I2C_Handle);
}

//地址匹配中断回调
void HAL_I2C_AddrCallback(I2C_HandleTypeDef *hi2c, uint8_t TransferDirection, uint16_t AddrMatchCode)
{
	if(hi2c->Instance==I2C1)
	{
		//hi2c->PreviousState=0; 会引发另一个问题:从机没有停止信号
		if((AddrMatchCode==address)||(AddrMatchCode==address0))
		{

2个从机需要改的地方几乎一样

F:主机广播

注意:广播的地址为:0x00  ;内容和C:解决最后一阵必须是接收的情况一样

主机:

void I2C_Init(void)
{
	I2C_Handle.Instance=I2C1;
	I2C_Handle.Init.ClockSpeed=100000;    ///通信速率1
	I2C_Handle.Init.AddressingMode=I2C_ADDRESSINGMODE_7BIT;  //7位寻址模式
	I2C_Handle.Init.GeneralCallMode=I2C_GENERALCALL_ENABLE; //打开广播功能
	HAL_I2C_Init(&I2C_Handle);
}
--------------------main函数--------------
if(KEY_Scan())
{	
	/PA1按下:
	printf("PA0按下\r\n");
HAL_I2C_Master_Seq_Transmit_DMA(&I2C_Handle,0x00,send_address1,3,I2C_FIRST_AND_LAST_FRAME);

}
----------------发送回调函数--------------
else if(hi2c->Devaddress==0x00)
{
	if(	hi2c->XferOptions==I2C_FIRST_AND_LAST_FRAME)
	{
		printf("广播数据发送完毕\r\n");
	}		
}

从机:

void I2C_Init(void)
{
	I2C_Handle.Instance=I2C1;
	I2C_Handle.Init.ClockSpeed=100000;    ///通信速率1
	I2C_Handle.Init.AddressingMode=I2C_ADDRESSINGMODE_7BIT;  //7位寻址模式
	I2C_Handle.Init.OwnAddress1=0x20;     //从机的地址
	I2C_Handle.Init.GeneralCallMode=I2C_GENERALCALL_ENABLE; //打开广播功能
	HAL_I2C_Init(&I2C_Handle);
}

void HAL_I2C_AddrCallback(I2C_HandleTypeDef *hi2c, uint8_t TransferDirection, uint16_t AddrMatchCode)
{
	else if(TransferDirection==I2C_DIRECTION_TRANSMIT)
	{
		if(hi2c->XferOptions==I2C_FIRST_FRAME)
		{
			//I2C_DIRECTION_TRANSMIT:主机发送数据:从机接收数据
			printf("主机发送数据:从机2准备开始接收数据\r\n");
			HAL_I2C_Slave_Seq_Receive_DMA(hi2c,receive_address,3,I2C_NEXT_FRAME);
		}
		else if(hi2c->XferOptions==0)
		{				
                //hi2c->XferOptions==0 以为广播只有一个方向,从机只能接收
				
				printf("从机接收到广播数据\r\n");
		  HAL_I2C_Slave_Seq_Receive_DMA(hi2c,receive_address,3,I2C_FIRST_AND_LAST_FRAME);
		}
	
	}
}

 G:中断

主机从机

设置正确可以

正常通信。

中断DMA
中断中断
DMA中断
DMADMA

内容和C:解决最后一阵必须是接收的情况 一样。

        主机我们采用中断的方式,从机我们依然采用DMA。中断的效率没有DMA高,所以我们如果要使用串口,注意再整个通信完成(出现STOP信号的时候)再输出串口。否则会影响我们的通信正常进行。

主机:

#include "stm32f1xx_hal.h"
#include "UART.h"
#include <stdarg.h>
#include "stdio.h"

I2C_HandleTypeDef I2C_Handle;
//DMA_HandleTypeDef DMA_HandleTX;  //发送DMA
//DMA_HandleTypeDef DMA_HandleRX;   //接收



uint8_t send_address1[16]={111,11,1,1,2,3,4,5,6,7,8,9,10,11,12,13};
uint8_t receive_address1[16];

uint8_t send_address2[32]={1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16};
uint8_t receive_address2[32];
void I2C_Init(void)
{
	I2C_Handle.Instance=I2C1;
	I2C_Handle.Init.ClockSpeed=100000;    ///通信速率1
	I2C_Handle.Init.AddressingMode=I2C_ADDRESSINGMODE_7BIT;  //7位寻址模式
	HAL_I2C_Init(&I2C_Handle);
}

void HAL_I2C_MspInit(I2C_HandleTypeDef *hi2c)
{
		if(hi2c->Instance==I2C1)
		{
			__HAL_RCC_I2C1_CLK_ENABLE();
			__HAL_RCC_GPIOB_CLK_ENABLE();
			__HAL_RCC_DMA1_CLK_ENABLE();
			
			//开启事件中断
			HAL_NVIC_SetPriority(I2C1_EV_IRQn,1,2);
			HAL_NVIC_EnableIRQ(I2C1_EV_IRQn);
				
			GPIO_InitTypeDef GPIO_InitType;
	
			GPIO_InitType.Mode=GPIO_MODE_AF_OD;    //复用开漏输出
			GPIO_InitType.Pin=GPIO_PIN_6|GPIO_PIN_7;
			GPIO_InitType.Speed=GPIO_SPEED_FREQ_HIGH;
			HAL_GPIO_Init(GPIOB,&GPIO_InitType); 
			
//			//MDA通道6配置--发送数据
//			DMA_HandleTX.Instance=DMA1_Channel6;
//			//传输方向:内存(数组)--->外设
//			DMA_HandleTX.Init.Direction=DMA_MEMORY_TO_PERIPH;
//			DMA_HandleTX.Init.MemDataAlignment=DMA_MDATAALIGN_BYTE; //内存数据宽度
//			DMA_HandleTX.Init.MemInc=DMA_MINC_ENABLE;    //存储区地址自增
//			DMA_HandleTX.Init.Mode=DMA_NORMAL;
//			DMA_HandleTX.Init.PeriphDataAlignment=DMA_PDATAALIGN_BYTE;//外设数据宽度
//			DMA_HandleTX.Init.PeriphInc=DMA_PINC_DISABLE;//外设地址不自增
//			DMA_HandleTX.Init.Priority=DMA_PRIORITY_MEDIUM;   //优先级
//			__HAL_LINKDMA(&I2C_Handle,hdmatx,DMA_HandleTX);   //双向链接
//			HAL_DMA_Init(&DMA_HandleTX);
//			
//			HAL_NVIC_SetPriority(DMA1_Channel6_IRQn,1,2);
//			HAL_NVIC_EnableIRQ(DMA1_Channel6_IRQn);
			
//			//MDA通道7配置---接收数据
//			DMA_HandleRX.Instance=DMA1_Channel7;
//			//传输方向:外设  ---> 内存(数组)
//			DMA_HandleRX.Init.Direction=DMA_PERIPH_TO_MEMORY;
//			DMA_HandleRX.Init.MemDataAlignment=DMA_MDATAALIGN_BYTE; //内存数据宽度
//			DMA_HandleRX.Init.MemInc=DMA_MINC_ENABLE;    //存储区地址自增
//			DMA_HandleRX.Init.Mode=DMA_NORMAL;
//			DMA_HandleRX.Init.PeriphDataAlignment=DMA_PDATAALIGN_BYTE;//外设数据宽度
//			DMA_HandleRX.Init.PeriphInc=DMA_PINC_DISABLE;//外设地址不自增
//			DMA_HandleRX.Init.Priority=DMA_PRIORITY_MEDIUM;   //优先级
//			__HAL_LINKDMA(&I2C_Handle,hdmarx,DMA_HandleRX);   //双向链接
//			HAL_DMA_Init(&DMA_HandleRX);
//			
//			HAL_NVIC_SetPriority(DMA1_Channel7_IRQn,1,2);
//			HAL_NVIC_EnableIRQ(DMA1_Channel7_IRQn);
			
		}
}



//开启事件中断
void I2C1_EV_IRQHandler()
{
	HAL_I2C_EV_IRQHandler(&I2C_Handle);
}

//开启MDA通道6发送中断
//void DMA1_Channel6_IRQHandler()
//{
//	HAL_DMA_IRQHandler(&DMA_HandleTX);
//}
开启MDA通道6接收中断
//void DMA1_Channel7_IRQHandler()
//{
//	HAL_DMA_IRQHandler(&DMA_HandleRX);
//}

//主模式:DMA写入完成回调函数:主机给从机发送数据调用,发送完毕一次调用一次。
//它的触发条件通常是当主机向从机发送数据完成后。
void HAL_I2C_MasterTxCpltCallback(I2C_HandleTypeDef *hi2c)
{
	if(hi2c->Instance==I2C1)
	{
			if(hi2c->Devaddress==0x10)
			{
					if(hi2c->XferOptions==I2C_FIRST_AND_LAST_FRAME)
					{
						printf("主机给从机1第一帧发送数据完成\r\n");
						HAL_I2C_Master_Seq_Transmit_IT(&I2C_Handle,0x10,send_address1,16,I2C_LAST_FRAME);
					}
						else if (hi2c->XferOptions==I2C_LAST_FRAME)
						{
							printf("主机给从机1第二帧发送数据完成\r\n");
						
						}				
			}
		
			else if(hi2c->Devaddress==0x20)
			{
				if (hi2c->XferOptions==I2C_LAST_FRAME)
				{
						//再最一帧串口输出
						printf("通信完成");
						for(uint16_t j =0;j<6;j++)
						{
							printf("receive_address2[%d]=%d\r\n",j,receive_address2[j]);
						}
				}
				else if (hi2c->XferOptions==I2C_NEXT_FRAME)
				{
	
					HAL_I2C_Master_Seq_Receive_IT(&I2C_Handle,0x20,receive_address2,3,I2C_LAST_FRAME_NO_STOP);
				}
			
			}	
	}

}
//存储器模式:DMA读取完成回调函数,主机接收从机发送来的数据,发送完成一次调用一次
//它的触发条件是当主机从从机接收数据完成后。
void HAL_I2C_MasterRxCpltCallback(I2C_HandleTypeDef *hi2c)
{
	if(hi2c->Instance==I2C1)
	{
		if(hi2c->Devaddress==0x10)
		{	
			}
			else if(hi2c->Devaddress==0x20)
			{
				if (hi2c->XferOptions==I2C_FIRST_FRAME)
				{
					
						HAL_I2C_Master_Seq_Transmit_IT(&I2C_Handle,0x20,send_address2,3,I2C_NEXT_FRAME);
			
				}
				
				
				else if (hi2c->XferOptions==I2C_LAST_FRAME_NO_STOP)
				{
					
						HAL_I2C_Master_Seq_Transmit_IT(&I2C_Handle,0x20,&send_address2[3],0,I2C_LAST_FRAME);
				}
					
			}
	}
}
#include "stm32f1xx_hal.h"
#include "rcc.h"
#include "led.h"
#include "delay.h"
#include "OLED.h"
#include "i2c.h"
#include "UART.h"
#include <stdarg.h>
#include "stdio.h"
#include "key.h"


uint8_t i=0;

int main(void)
{

  
	HAL_Init();                         /* 初始化HAL库 */
  sys_stm32_clock_init(RCC_PLL_MUL9); /* 设置时钟, 72Mhz */
  delay_init(72);                     /* 延时初始化 */
  LED_Init();                        /* LED初始化 */
	Uart_Init(115200);
	LED_Exit_Init();
	OLED_Init();
	KEY_Init();
	I2C_Init();
	printf("串口模块正常启动\r\n");
	

	 while (1)
	 {
		
			if(KEY_Scan1())
			{	
				//PA1按下:
				printf("PA1按下\r\n");
				HAL_I2C_Master_Seq_Receive_IT(&I2C_Handle,0x20,receive_address2,3,I2C_FIRST_FRAME);

			}
			
			
	 }   
}

从机程序和上面的一样,采用DMA通信。

H:I2C_OTHER_FRAME

主机:

#include "stm32f1xx_hal.h"
#include "UART.h"
#include <stdarg.h>
#include "stdio.h"

I2C_HandleTypeDef I2C_Handle;
DMA_HandleTypeDef DMA_HandleTX;  //发送DMA
DMA_HandleTypeDef DMA_HandleRX;   //接收



uint8_t send_address1[16]={111,11,1,1,2,3,4,5,6,7,8,9,10,11,12,13};
uint8_t receive_address1[16];

uint8_t send_address2[16]={222,22,2,15,14,13,12,15,16,15,13,13,15,12,16,12};
uint8_t receive_address2[16];
uint8_t  count=0;
void I2C_Init(void)
{
	I2C_Handle.Instance=I2C1;
	I2C_Handle.Init.ClockSpeed=100000;    ///通信速率1
	I2C_Handle.Init.AddressingMode=I2C_ADDRESSINGMODE_7BIT;  //7位寻址模式
	HAL_I2C_Init(&I2C_Handle);
}

void HAL_I2C_MspInit(I2C_HandleTypeDef *hi2c)
{
		if(hi2c->Instance==I2C1)
		{
			__HAL_RCC_I2C1_CLK_ENABLE();
			__HAL_RCC_GPIOB_CLK_ENABLE();
			__HAL_RCC_DMA1_CLK_ENABLE();
			
			//开启事件中断
			HAL_NVIC_SetPriority(I2C1_EV_IRQn,1,2);
			HAL_NVIC_EnableIRQ(I2C1_EV_IRQn);
				
			GPIO_InitTypeDef GPIO_InitType;
	
			GPIO_InitType.Mode=GPIO_MODE_AF_OD;    //复用开漏输出
			GPIO_InitType.Pin=GPIO_PIN_6|GPIO_PIN_7;
			GPIO_InitType.Speed=GPIO_SPEED_FREQ_HIGH;
			HAL_GPIO_Init(GPIOB,&GPIO_InitType); 
			
			//MDA通道6配置--发送数据
			DMA_HandleTX.Instance=DMA1_Channel6;
			//传输方向:内存(数组)--->外设
			DMA_HandleTX.Init.Direction=DMA_MEMORY_TO_PERIPH;
			DMA_HandleTX.Init.MemDataAlignment=DMA_MDATAALIGN_BYTE; //内存数据宽度
			DMA_HandleTX.Init.MemInc=DMA_MINC_ENABLE;    //存储区地址自增
			DMA_HandleTX.Init.Mode=DMA_NORMAL;
			DMA_HandleTX.Init.PeriphDataAlignment=DMA_PDATAALIGN_BYTE;//外设数据宽度
			DMA_HandleTX.Init.PeriphInc=DMA_PINC_DISABLE;//外设地址不自增
			DMA_HandleTX.Init.Priority=DMA_PRIORITY_MEDIUM;   //优先级
			__HAL_LINKDMA(&I2C_Handle,hdmatx,DMA_HandleTX);   //双向链接
			HAL_DMA_Init(&DMA_HandleTX);
			
			HAL_NVIC_SetPriority(DMA1_Channel6_IRQn,1,2);
			HAL_NVIC_EnableIRQ(DMA1_Channel6_IRQn);
			
			//MDA通道7配置---接收数据
			DMA_HandleRX.Instance=DMA1_Channel7;
			//传输方向:外设  ---> 内存(数组)
			DMA_HandleRX.Init.Direction=DMA_PERIPH_TO_MEMORY;
			DMA_HandleRX.Init.MemDataAlignment=DMA_MDATAALIGN_BYTE; //内存数据宽度
			DMA_HandleRX.Init.MemInc=DMA_MINC_ENABLE;    //存储区地址自增
			DMA_HandleRX.Init.Mode=DMA_NORMAL;
			DMA_HandleRX.Init.PeriphDataAlignment=DMA_PDATAALIGN_BYTE;//外设数据宽度
			DMA_HandleRX.Init.PeriphInc=DMA_PINC_DISABLE;//外设地址不自增
			DMA_HandleRX.Init.Priority=DMA_PRIORITY_MEDIUM;   //优先级
			__HAL_LINKDMA(&I2C_Handle,hdmarx,DMA_HandleRX);   //双向链接
			HAL_DMA_Init(&DMA_HandleRX);
			
			HAL_NVIC_SetPriority(DMA1_Channel7_IRQn,1,2);
			HAL_NVIC_EnableIRQ(DMA1_Channel7_IRQn);
			
		}
}



//开启事件中断
void I2C1_EV_IRQHandler()
{
	HAL_I2C_EV_IRQHandler(&I2C_Handle);
}

//开启MDA通道6发送中断
void DMA1_Channel6_IRQHandler()
{
	HAL_DMA_IRQHandler(&DMA_HandleTX);
}
//开启MDA通道6接收中断
void DMA1_Channel7_IRQHandler()
{
	HAL_DMA_IRQHandler(&DMA_HandleRX);
}

//主模式:DMA写入完成回调函数:主机给从机发送数据调用,发送完毕一次调用一次。
//它的触发条件通常是当主机向从机发送数据完成后。
void HAL_I2C_MasterTxCpltCallback(I2C_HandleTypeDef *hi2c)
{
	if(hi2c->Instance==I2C1)
	{
			if(hi2c->Devaddress==0x10)
			{	
					if(hi2c->XferOptions==I2C_FIRST_FRAME)
					{	
						count++;
						if (count<=6)
						{
							HAL_I2C_Master_Seq_Transmit_DMA(&I2C_Handle,0x10,&send_address1[count],1,I2C_OTHER_FRAME);

						}
					
						else
						{
								HAL_I2C_Master_Seq_Transmit_DMA(&I2C_Handle,0x10,&send_address1[count],1,I2C_OTHER_AND_LAST_FRAME);
						
						}
					}
					
					else if (hi2c->XferOptions==I2C_FIRST_AND_LAST_FRAME)
					{
							printf("主机发送数据完成\r\n");
							count=0;
							
					}				
			}
			
			
	
	}

}
//存储器模式:DMA读取完成回调函数,主机接收从机发送来的数据,发送完成一次调用一次
//它的触发条件是当主机从从机接收数据完成后。
void HAL_I2C_MasterRxCpltCallback(I2C_HandleTypeDef *hi2c)
{
	if(hi2c->Instance==I2C1)
	{
	
			if(hi2c->Devaddress==0x20)
			{
				if (hi2c->XferOptions==I2C_FIRST_FRAME)
				{		
						count++;
						
						if(count<=6)
						{
							HAL_I2C_Master_Seq_Receive_DMA(&I2C_Handle,0x20,&receive_address2[count],1,I2C_OTHER_FRAME);
						}
						else
						{
							HAL_I2C_Master_Seq_Receive_DMA(&I2C_Handle,0x20,&receive_address2[count],1,I2C_OTHER_AND_LAST_FRAME);

						}			
				}
				else if(hi2c->XferOptions==I2C_FIRST_AND_LAST_FRAME)
				{
					printf("主机接收数据完毕\r\n");
					for(uint16_t j=0;j<8;j++)
					{
					
							printf("send_address2[%d]=%d\r\n",j,send_address2[j]);
							count=0;		
					}
				}
				
					
			}
	}
}
#include "stm32f1xx_hal.h"
#include "rcc.h"
#include "led.h"
#include "delay.h"
#include "OLED.h"
#include "i2c.h"
#include "UART.h"
#include <stdarg.h>
#include "stdio.h"
#include "key.h"


uint8_t i=0;

int main(void)
{

  
	HAL_Init();                         /* 初始化HAL库 */
  sys_stm32_clock_init(RCC_PLL_MUL9); /* 设置时钟, 72Mhz */
  delay_init(72);                     /* 延时初始化 */
  LED_Init();                        /* LED初始化 */
	Uart_Init(115200);
	LED_Exit_Init();
	OLED_Init();
	KEY_Init();
	I2C_Init();
	printf("串口模块正常启动\r\n");
	

	 while (1)
	 {
			if(KEY_Scan())
			{	
				//PA0按下:具体操作:主机1给从机两次发发
				printf("PA0按下\r\n");
				HAL_I2C_Master_Seq_Transmit_DMA(&I2C_Handle,0x10,&send_address1[count],1,I2C_FIRST_FRAME);
			}
			
			else if(KEY_Scan1())
			{	
				//PA1按下:主机先接收从机2的数据,主机先给从机2发送数据.
				printf("PA1按下\r\n");
				HAL_I2C_Master_Seq_Receive_DMA(&I2C_Handle,0x20,&receive_address2[count],1,I2C_FIRST_FRAME);

			}
			
			
	 }   
}

从机1:

#include "stm32f1xx_hal.h"
#include "UART.h"
#include <stdarg.h>
#include "stdio.h"

I2C_HandleTypeDef I2C_Handle;
DMA_HandleTypeDef DMA_HandleTX;  //发送DMA
DMA_HandleTypeDef DMA_HandleRX;   //接收

uint8_t send_address[16]={11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11};
uint8_t receive_address[32];
uint16_t count=0;

void I2C_Init(void)
{
	I2C_Handle.Instance=I2C1;
	I2C_Handle.Init.ClockSpeed=100000;    ///通信速率1
	I2C_Handle.Init.AddressingMode=I2C_ADDRESSINGMODE_7BIT;  //7位寻址模式
	I2C_Handle.Init.OwnAddress1=0x10;     //从机的地址
	HAL_I2C_Init(&I2C_Handle);
}

void HAL_I2C_MspInit(I2C_HandleTypeDef *hi2c)
{
		if(hi2c->Instance==I2C1)
		{
			__HAL_RCC_I2C1_CLK_ENABLE();
			__HAL_RCC_GPIOB_CLK_ENABLE();
			__HAL_RCC_DMA1_CLK_ENABLE();
			
			//开启事件中断
			HAL_NVIC_SetPriority(I2C1_EV_IRQn,1,2);
			HAL_NVIC_EnableIRQ(I2C1_EV_IRQn);
				
			GPIO_InitTypeDef GPIO_InitType;
	
			GPIO_InitType.Mode=GPIO_MODE_AF_OD;    //复用开漏输出
			GPIO_InitType.Pin=GPIO_PIN_6|GPIO_PIN_7;
			GPIO_InitType.Speed=GPIO_SPEED_FREQ_HIGH;
			HAL_GPIO_Init(GPIOB,&GPIO_InitType); 
			
			//MDA通道6配置--发送数据
			DMA_HandleTX.Instance=DMA1_Channel6;
			//传输方向:内存(数组)--->外设
			DMA_HandleTX.Init.Direction=DMA_MEMORY_TO_PERIPH;
			DMA_HandleTX.Init.MemDataAlignment=DMA_MDATAALIGN_BYTE; //内存数据宽度
			DMA_HandleTX.Init.MemInc=DMA_MINC_ENABLE;    //存储区地址自增
			DMA_HandleTX.Init.Mode=DMA_NORMAL;
			DMA_HandleTX.Init.PeriphDataAlignment=DMA_PDATAALIGN_BYTE;//外设数据宽度
			DMA_HandleTX.Init.PeriphInc=DMA_PINC_DISABLE;//外设地址不自增
			DMA_HandleTX.Init.Priority=DMA_PRIORITY_MEDIUM;   //优先级
			__HAL_LINKDMA(hi2c,hdmatx,DMA_HandleTX);   //双向链接
			HAL_DMA_Init(&DMA_HandleTX);
			
			HAL_NVIC_SetPriority(DMA1_Channel6_IRQn,1,2);
			HAL_NVIC_EnableIRQ(DMA1_Channel6_IRQn);
			
			//MDA通道7配置---接收数据
			DMA_HandleRX.Instance=DMA1_Channel7;
			//传输方向:外设  ---> 内存(数组)
			DMA_HandleRX.Init.Direction=DMA_PERIPH_TO_MEMORY;
			DMA_HandleRX.Init.MemDataAlignment=DMA_MDATAALIGN_BYTE; //内存数据宽度
			DMA_HandleRX.Init.MemInc=DMA_MINC_ENABLE;    //存储区地址自增
			DMA_HandleRX.Init.Mode=DMA_NORMAL;
			DMA_HandleRX.Init.PeriphDataAlignment=DMA_PDATAALIGN_BYTE;//外设数据宽度
			DMA_HandleRX.Init.PeriphInc=DMA_PINC_DISABLE;//外设地址不自增
			DMA_HandleRX.Init.Priority=DMA_PRIORITY_MEDIUM;   //优先级
			__HAL_LINKDMA(hi2c,hdmarx,DMA_HandleRX);   //双向链接
			HAL_DMA_Init(&DMA_HandleRX);
			
			HAL_NVIC_SetPriority(DMA1_Channel7_IRQn,1,2);
			HAL_NVIC_EnableIRQ(DMA1_Channel7_IRQn);
			
		}
}



//开启事件中断
void I2C1_EV_IRQHandler()
{
	HAL_I2C_EV_IRQHandler(&I2C_Handle);
}

//开启MDA通道6发送中断
void DMA1_Channel6_IRQHandler()
{
	HAL_DMA_IRQHandler(I2C_Handle.hdmatx);
}
//开启MDA通道6接收中断
void DMA1_Channel7_IRQHandler()
{
	HAL_DMA_IRQHandler(I2C_Handle.hdmarx);
}

//监听功能回调函数
//监听动作全部完成:调用这个回调函数
void HAL_I2C_ListenCpltCallback(I2C_HandleTypeDef *hi2c)
{
	if(hi2c->Instance==I2C1)
	{
			printf("从机1监听完成\r\n");
			HAL_I2C_EnableListen_IT(&I2C_Handle);  //	开启下一次监听
			count=0;
	}


}
//地址匹配中断回调
void HAL_I2C_AddrCallback(I2C_HandleTypeDef *hi2c, uint8_t TransferDirection, uint16_t AddrMatchCode)
{
	if(hi2c->Instance==I2C1)
	{
		if(AddrMatchCode==0x10)
		{
			
			if(TransferDirection==I2C_DIRECTION_TRANSMIT)
			{
				printf("主机发送数据:从机1准备开始接收数据\r\n");
				HAL_I2C_Slave_Seq_Receive_DMA(hi2c,&receive_address[count],1,0);
				count++;
			}
		}
		
		}
	
}
//主模式:DMA写入完成回调函数。
//它的触发条件通常是当主机向从机发送数据完成后。
void HAL_I2C_SlaveTxCpltCallback(I2C_HandleTypeDef *hi2c)
{
	if(hi2c->Instance==I2C1)
	{
			printf("从机1发送数据完成\r\n");
	}

}
//存储器模式:DMA读取完成回调函数
//它的触发条件是当主机从从机接收数据完成后。
void HAL_I2C_SlaveRxCpltCallback(I2C_HandleTypeDef *hi2c)
{
	if(hi2c->Instance==I2C1)
	{
		printf("从机1接收主机的数据完成\r\n");
		for(uint8_t h=0;h<8;h++)
		{
			printf("receive_address[%d]=%d\r\n",h,receive_address[h]);
		}
		
	}
}
#include "stm32f1xx_hal.h"
#include "rcc.h"
#include "led.h"
#include "delay.h"
#include "OLED.h"
#include "i2c.h"
#include "UART.h"
#include <stdarg.h>
#include "stdio.h"
#include "key.h"


uint8_t i=0;


int main(void)
{

  
	HAL_Init();                         /* 初始化HAL库 */
  sys_stm32_clock_init(RCC_PLL_MUL9); /* 设置时钟, 72Mhz */
  delay_init(72);                     /* 延时初始化 */
  LED_Init();                        /* LED初始化 */
	Uart_Init(115200);
	LED_Exit_Init();
	OLED_Init();
	I2C_Init();
	printf("从机串口模块正常启动\r\n");
	HAL_I2C_EnableListen_IT(&I2C_Handle);  //	启动监听

	 while (1)
	 {
			
	 }   
}

主机和从机1通信没有问题,因为是发送。

从机2:

#include "stm32f1xx_hal.h"
#include "UART.h"
#include <stdarg.h>
#include "stdio.h"

I2C_HandleTypeDef I2C_Handle;
DMA_HandleTypeDef DMA_HandleTX;  //发送DMA
DMA_HandleTypeDef DMA_HandleRX;   //接收

uint8_t send_address[16]={22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22};
uint8_t receive_address[16];

uint16_t count=0;
void I2C_Init(void)
{
	I2C_Handle.Instance=I2C1;
	I2C_Handle.Init.ClockSpeed=100000;    ///通信速率1
	I2C_Handle.Init.AddressingMode=I2C_ADDRESSINGMODE_7BIT;  //7位寻址模式
	I2C_Handle.Init.OwnAddress1=0x20;     //从机的地址
	HAL_I2C_Init(&I2C_Handle);
}

void HAL_I2C_MspInit(I2C_HandleTypeDef *hi2c)
{
		if(hi2c->Instance==I2C1)
		{
			__HAL_RCC_I2C1_CLK_ENABLE();
			__HAL_RCC_GPIOB_CLK_ENABLE();
			__HAL_RCC_DMA1_CLK_ENABLE();
			
			//开启事件中断
			HAL_NVIC_SetPriority(I2C1_EV_IRQn,1,2);
			HAL_NVIC_EnableIRQ(I2C1_EV_IRQn);
				
			GPIO_InitTypeDef GPIO_InitType;
	
			GPIO_InitType.Mode=GPIO_MODE_AF_OD;    //复用开漏输出
			GPIO_InitType.Pin=GPIO_PIN_6|GPIO_PIN_7;
			GPIO_InitType.Speed=GPIO_SPEED_FREQ_HIGH;
			HAL_GPIO_Init(GPIOB,&GPIO_InitType); 
			
			//MDA通道6配置--发送数据
			DMA_HandleTX.Instance=DMA1_Channel6;
			//传输方向:内存(数组)--->外设
			DMA_HandleTX.Init.Direction=DMA_MEMORY_TO_PERIPH;
			DMA_HandleTX.Init.MemDataAlignment=DMA_MDATAALIGN_BYTE; //内存数据宽度
			DMA_HandleTX.Init.MemInc=DMA_MINC_ENABLE;    //存储区地址自增
			DMA_HandleTX.Init.Mode=DMA_NORMAL;
			DMA_HandleTX.Init.PeriphDataAlignment=DMA_PDATAALIGN_BYTE;//外设数据宽度
			DMA_HandleTX.Init.PeriphInc=DMA_PINC_DISABLE;//外设地址不自增
			DMA_HandleTX.Init.Priority=DMA_PRIORITY_MEDIUM;   //优先级
			__HAL_LINKDMA(hi2c,hdmatx,DMA_HandleTX);   //双向链接
			HAL_DMA_Init(&DMA_HandleTX);
			
			HAL_NVIC_SetPriority(DMA1_Channel6_IRQn,1,2);
			HAL_NVIC_EnableIRQ(DMA1_Channel6_IRQn);
			
			//MDA通道7配置---接收数据
			DMA_HandleRX.Instance=DMA1_Channel7;
			//传输方向:外设  ---> 内存(数组)
			DMA_HandleRX.Init.Direction=DMA_PERIPH_TO_MEMORY;
			DMA_HandleRX.Init.MemDataAlignment=DMA_MDATAALIGN_BYTE; //内存数据宽度
			DMA_HandleRX.Init.MemInc=DMA_MINC_ENABLE;    //存储区地址自增
			DMA_HandleRX.Init.Mode=DMA_NORMAL;
			DMA_HandleRX.Init.PeriphDataAlignment=DMA_PDATAALIGN_BYTE;//外设数据宽度
			DMA_HandleRX.Init.PeriphInc=DMA_PINC_DISABLE;//外设地址不自增
			DMA_HandleRX.Init.Priority=DMA_PRIORITY_MEDIUM;   //优先级
			__HAL_LINKDMA(hi2c,hdmarx,DMA_HandleRX);   //双向链接
			HAL_DMA_Init(&DMA_HandleRX);
			
			HAL_NVIC_SetPriority(DMA1_Channel7_IRQn,1,2);
			HAL_NVIC_EnableIRQ(DMA1_Channel7_IRQn);
			
		}
}



//开启事件中断
void I2C1_EV_IRQHandler()
{
	HAL_I2C_EV_IRQHandler(&I2C_Handle);
}

//开启MDA通道6发送中断
void DMA1_Channel6_IRQHandler()
{
	HAL_DMA_IRQHandler(I2C_Handle.hdmatx);
}
//开启MDA通道6接收中断
void DMA1_Channel7_IRQHandler()
{
	HAL_DMA_IRQHandler(I2C_Handle.hdmarx);
}

//监听功能回调函数
//监听动作全部完成:调用这个回调函数
void HAL_I2C_ListenCpltCallback(I2C_HandleTypeDef *hi2c)
{
	if(hi2c->Instance==I2C1)
	{
			printf("从机2监听完成");
			HAL_I2C_EnableListen_IT(&I2C_Handle);  //	开启下一次监听
			count=0;
	}


}
//地址匹配中断回调
void HAL_I2C_AddrCallback(I2C_HandleTypeDef *hi2c, uint8_t TransferDirection, uint16_t AddrMatchCode)
{
	if(hi2c->Instance==I2C1)
	{
	
		if(AddrMatchCode==0x20)
		{
			//主机先发送在接收,所以会进入这个回调函数2次,因为方向发生了变化,有了RESTAR信号
			if(TransferDirection==I2C_DIRECTION_RECEIVE)
			{
				//I2C_DIRECTION_RECEIVE:主机读取数据:从机发送数据
				printf("主机读取数据:从机2准备开始发送数据\r\n");
				HAL_I2C_Slave_Seq_Transmit_DMA(hi2c,&send_address[count],1,0);
				count++;
			}
			
	
		} 
		
		}
	
}
//主模式:DMA写入完成回调函数。
//它的触发条件通常是当主机向从机发送数据完成后。
void HAL_I2C_SlaveTxCpltCallback(I2C_HandleTypeDef *hi2c)
{
	if(hi2c->Instance==I2C1)
	{
			printf("从机2发送数据完成\r\n");
	}

}
//存储器模式:DMA读取完成回调函数
//它的触发条件是当主机从从机接收数据完成后。
void HAL_I2C_SlaveRxCpltCallback(I2C_HandleTypeDef *hi2c)
{
	if(hi2c->Instance==I2C1)
	{
		printf("从机2接收主机的数据完成\r\n");
		for(uint8_t i=0;i<16;i++)
		{
			printf("receive_address[%d]=%d\r\n",i,receive_address[i]);
		}
		
	}
}
#include "stm32f1xx_hal.h"
#include "rcc.h"
#include "led.h"
#include "delay.h"
#include "OLED.h"
#include "i2c.h"
#include "UART.h"
#include <stdarg.h>
#include "stdio.h"
#include "key.h"


uint8_t i=0;


int main(void)
{

  
	HAL_Init();                         /* 初始化HAL库 */
  sys_stm32_clock_init(RCC_PLL_MUL9); /* 设置时钟, 72Mhz */
  delay_init(72);                     /* 延时初始化 */
  LED_Init();                        /* LED初始化 */
	Uart_Init(115200);
	LED_Exit_Init();
	OLED_Init();
	I2C_Init();
	printf("从机串口模块正常启动\r\n");
	HAL_I2C_EnableListen_IT(&I2C_Handle);  //	启动监听

	 while (1)
	 {
			
	 }   
}

因为接收存在BUG,所以只能接收一次,后面的会错误,不能正常通信。

如果给主机的接收回调函数中加上:把它的信号强行拉回来

存储器模式:DMA读取完成回调函数,主机接收从机发送来的数据,发送完成一次调用一次
它的触发条件是当主机从从机接收数据完成后。
void HAL_I2C_MasterRxCpltCallback(I2C_HandleTypeDef *hi2c)
{
		hi2c->PreviousState=0;    //会引发另一个问题:从机没有停止信号	
}

注意我们的从机没有停止信号,无法进入监听回调函数,count无法回零,count一直加,导致程序错误。

//监听动作全部完成:调用这个回调函数
void HAL_I2C_ListenCpltCallback(I2C_HandleTypeDef *hi2c)
{
	if(hi2c->Instance==I2C1)
	{
			printf("从机2监听完成");
			HAL_I2C_EnableListen_IT(&I2C_Handle);  //	开启下一次监听
			count=0;
	}


}

必须以接收结尾,我们就再加一个发送就ok了。参考我们:C:解决最后一阵必须是接收的情况

覆盖问题: 

 

I:一个板子上面的主从机

一个板子上面的主从机都是通过一个串口输出的。

#include "stm32f1xx_hal.h"
#include "UART.h"
#include <stdarg.h>
#include "stdio.h"
//主机I2C1
I2C_HandleTypeDef I2C_Handle;
DMA_HandleTypeDef DMA_HandleTX;  //发送DMA
DMA_HandleTypeDef DMA_HandleRX;   //接收
uint8_t send_address1[8]={1,2,3,4,5,6,7,8};
uint8_t  count=0;

//从机i2C2
I2C_HandleTypeDef I2C_Handle2;
DMA_HandleTypeDef DMA_HandleTX2;  //发送DMA
DMA_HandleTypeDef DMA_HandleRX2;   //接收
uint8_t  count2=0;
uint8_t receive_address[8];


void I2C_Init(void)
{
	I2C_Handle.Instance=I2C1;
	I2C_Handle.Init.ClockSpeed=100000;    ///通信速率1
	I2C_Handle.Init.AddressingMode=I2C_ADDRESSINGMODE_7BIT;  //7位寻址模式
	HAL_I2C_Init(&I2C_Handle);
}
//I2C2做从机初始化
void I2C2_Init(void)
{
	I2C_Handle2.Instance=I2C2;
	I2C_Handle2.Init.ClockSpeed=100000;    ///通信速率1
	I2C_Handle2.Init.AddressingMode=I2C_ADDRESSINGMODE_7BIT;  //7位寻址模式
	I2C_Handle2.Init.OwnAddress1=0x10;
	HAL_I2C_Init(&I2C_Handle2);
}

void HAL_I2C_MspInit(I2C_HandleTypeDef *hi2c)
{
		if(hi2c->Instance==I2C1)
		{
			__HAL_RCC_I2C1_CLK_ENABLE();
			__HAL_RCC_GPIOB_CLK_ENABLE();
			__HAL_RCC_DMA1_CLK_ENABLE();
			
			//开启事件中断
			HAL_NVIC_SetPriority(I2C1_EV_IRQn,1,2);
			HAL_NVIC_EnableIRQ(I2C1_EV_IRQn);
				
			GPIO_InitTypeDef GPIO_InitType;
	
			GPIO_InitType.Mode=GPIO_MODE_AF_OD;    //复用开漏输出
			GPIO_InitType.Pin=GPIO_PIN_6|GPIO_PIN_7;
			GPIO_InitType.Speed=GPIO_SPEED_FREQ_HIGH;
			HAL_GPIO_Init(GPIOB,&GPIO_InitType); 
			
			//MDA通道6配置--发送数据
			DMA_HandleTX.Instance=DMA1_Channel6;
			//传输方向:内存(数组)--->外设
			DMA_HandleTX.Init.Direction=DMA_MEMORY_TO_PERIPH;
			DMA_HandleTX.Init.MemDataAlignment=DMA_MDATAALIGN_BYTE; //内存数据宽度
			DMA_HandleTX.Init.MemInc=DMA_MINC_ENABLE;    //存储区地址自增
			DMA_HandleTX.Init.Mode=DMA_NORMAL;
			DMA_HandleTX.Init.PeriphDataAlignment=DMA_PDATAALIGN_BYTE;//外设数据宽度
			DMA_HandleTX.Init.PeriphInc=DMA_PINC_DISABLE;//外设地址不自增
			DMA_HandleTX.Init.Priority=DMA_PRIORITY_MEDIUM;   //优先级
			__HAL_LINKDMA(&I2C_Handle,hdmatx,DMA_HandleTX);   //双向链接
			HAL_DMA_Init(&DMA_HandleTX);
			
			HAL_NVIC_SetPriority(DMA1_Channel6_IRQn,1,2);
			HAL_NVIC_EnableIRQ(DMA1_Channel6_IRQn);
			
			//MDA通道7配置---接收数据
			DMA_HandleRX.Instance=DMA1_Channel7;
			//传输方向:外设  ---> 内存(数组)
			DMA_HandleRX.Init.Direction=DMA_PERIPH_TO_MEMORY;
			DMA_HandleRX.Init.MemDataAlignment=DMA_MDATAALIGN_BYTE; //内存数据宽度
			DMA_HandleRX.Init.MemInc=DMA_MINC_ENABLE;    //存储区地址自增
			DMA_HandleRX.Init.Mode=DMA_NORMAL;
			DMA_HandleRX.Init.PeriphDataAlignment=DMA_PDATAALIGN_BYTE;//外设数据宽度
			DMA_HandleRX.Init.PeriphInc=DMA_PINC_DISABLE;//外设地址不自增
			DMA_HandleRX.Init.Priority=DMA_PRIORITY_MEDIUM;   //优先级
			__HAL_LINKDMA(&I2C_Handle,hdmarx,DMA_HandleRX);   //双向链接
			HAL_DMA_Init(&DMA_HandleRX);
			
			HAL_NVIC_SetPriority(DMA1_Channel7_IRQn,1,2);
			HAL_NVIC_EnableIRQ(DMA1_Channel7_IRQn);
			
		}
		//I2C2做从机初始化
		if(hi2c->Instance==I2C2)
		{
			__HAL_RCC_I2C2_CLK_ENABLE();
			__HAL_RCC_GPIOB_CLK_ENABLE();
			__HAL_RCC_DMA1_CLK_ENABLE();
			
			//开启事件中断
			HAL_NVIC_SetPriority(I2C2_EV_IRQn,1,2);
			HAL_NVIC_EnableIRQ(I2C2_EV_IRQn);
				
			GPIO_InitTypeDef GPIO_InitType;
	
			GPIO_InitType.Mode=GPIO_MODE_AF_OD;    //复用开漏输出
			GPIO_InitType.Pin=GPIO_PIN_10|GPIO_PIN_11;
			GPIO_InitType.Speed=GPIO_SPEED_FREQ_HIGH;
			HAL_GPIO_Init(GPIOB,&GPIO_InitType); 
			
			//MDA通道6配置--发送数据
			DMA_HandleTX2.Instance=DMA1_Channel4;
			//传输方向:内存(数组)--->外设
			DMA_HandleTX2.Init.Direction=DMA_MEMORY_TO_PERIPH;
			DMA_HandleTX2.Init.MemDataAlignment=DMA_MDATAALIGN_BYTE; //内存数据宽度
			DMA_HandleTX2.Init.MemInc=DMA_MINC_ENABLE;    //存储区地址自增
			DMA_HandleTX2.Init.Mode=DMA_NORMAL;
			DMA_HandleTX2.Init.PeriphDataAlignment=DMA_PDATAALIGN_BYTE;//外设数据宽度
			DMA_HandleTX2.Init.PeriphInc=DMA_PINC_DISABLE;//外设地址不自增
			DMA_HandleTX2.Init.Priority=DMA_PRIORITY_MEDIUM;   //优先级
			__HAL_LINKDMA(&I2C_Handle2,hdmatx,DMA_HandleTX2);   //双向链接
			HAL_DMA_Init(&DMA_HandleTX2);
			
			HAL_NVIC_SetPriority(DMA1_Channel4_IRQn,1,2);
			HAL_NVIC_EnableIRQ(DMA1_Channel4_IRQn);
			
			//MDA通道7配置---接收数据
			DMA_HandleRX2.Instance=DMA1_Channel5;
			//传输方向:外设  ---> 内存(数组)
			DMA_HandleRX2.Init.Direction=DMA_PERIPH_TO_MEMORY;
			DMA_HandleRX2.Init.MemDataAlignment=DMA_MDATAALIGN_BYTE; //内存数据宽度
			DMA_HandleRX2.Init.MemInc=DMA_MINC_ENABLE;    //存储区地址自增
			DMA_HandleRX2.Init.Mode=DMA_NORMAL;
			DMA_HandleRX2.Init.PeriphDataAlignment=DMA_PDATAALIGN_BYTE;//外设数据宽度
			DMA_HandleRX2.Init.PeriphInc=DMA_PINC_DISABLE;//外设地址不自增
			DMA_HandleRX2.Init.Priority=DMA_PRIORITY_MEDIUM;   //优先级
			__HAL_LINKDMA(&I2C_Handle2,hdmarx,DMA_HandleRX2);   //双向链接
			HAL_DMA_Init(&DMA_HandleRX2);
			
			HAL_NVIC_SetPriority(DMA1_Channel5_IRQn,1,2);
			HAL_NVIC_EnableIRQ(DMA1_Channel5_IRQn);
			
		}
}



//开启事件中断
void I2C1_EV_IRQHandler()
{
	HAL_I2C_EV_IRQHandler(&I2C_Handle);
}

//开启MDA通道6发送中断
void DMA1_Channel6_IRQHandler()
{
	HAL_DMA_IRQHandler(&DMA_HandleTX);
}
//开启MDA通道6接收中断
void DMA1_Channel7_IRQHandler()
{
	HAL_DMA_IRQHandler(&DMA_HandleRX);
}



/*------从机中断---------*/
//开启事件中断
void I2C2_EV_IRQHandler()
{
	HAL_I2C_EV_IRQHandler(&I2C_Handle2);
}

//开启MDA通道6发送中断
void DMA1_Channel4_IRQHandler()
{
	HAL_DMA_IRQHandler(&DMA_HandleTX2);
}
//开启MDA通道6接收中断
void DMA1_Channel5_IRQHandler()
{
	HAL_DMA_IRQHandler(&DMA_HandleRX2);
}
//主模式:DMA写入完成回调函数:主机给从机发送数据调用,发送完毕一次调用一次。
//它的触发条件通常是当主机向从机发送数据完成后。
void HAL_I2C_MasterTxCpltCallback(I2C_HandleTypeDef *hi2c)
{
	if(hi2c->Instance==I2C1)
	{
			if(hi2c->Devaddress==0x10)
			{	
					if(hi2c->XferOptions==I2C_FIRST_FRAME)
					{	
						count++;
						if (count<=6)
						{
							HAL_I2C_Master_Seq_Transmit_DMA(&I2C_Handle,0x10,&send_address1[count],1,I2C_OTHER_FRAME);

						}
					
						else
						{
								HAL_I2C_Master_Seq_Transmit_DMA(&I2C_Handle,0x10,&send_address1[count],1,I2C_OTHER_AND_LAST_FRAME);
						
						}
					}
					
					else if (hi2c->XferOptions==I2C_FIRST_AND_LAST_FRAME)
					{
							printf("主机发送数据完成\r\n");
							count=0;
							
					}				
			}
			
			
	
	}

}

/*-------------------------------I2C2做从机的从机回调函数-----------------------------------*/

//监听动作全部完成:调用这个回调函数
void HAL_I2C_ListenCpltCallback(I2C_HandleTypeDef *hi2c)
{
	if(hi2c->Instance==I2C2)
	{
			printf("从机1监听完成\r\n");
			HAL_I2C_EnableListen_IT(&I2C_Handle2);  //	开启下一次监听
			count2=0;
	}


}
//地址匹配中断回调
void HAL_I2C_AddrCallback(I2C_HandleTypeDef *hi2c, uint8_t TransferDirection, uint16_t AddrMatchCode)
{
	if(hi2c->Instance==I2C2)
	{
		if(AddrMatchCode==0x10)
		{
			
			if(TransferDirection==I2C_DIRECTION_TRANSMIT)
			{
				printf("主机发送数据:从机1准备开始接收数据\r\n");
				HAL_I2C_Slave_Seq_Receive_DMA(hi2c,&receive_address[count2],1,0);
				count2++;
			}
		}
		
		}
	
}

//存储器模式:DMA读取完成回调函数
//它的触发条件是当主机从从机接收数据完成后。
void HAL_I2C_SlaveRxCpltCallback(I2C_HandleTypeDef *hi2c)
{
	if(hi2c->Instance==I2C2)
	{
		printf("从机1接收主机的数据完成\r\n");
		for(uint8_t h=0;h<8;h++)
		{
			printf("receive_address[%d]=%d\r\n",h,receive_address[h]);
		}
		
	}
}
#include "stm32f1xx_hal.h"
#include "rcc.h"
#include "led.h"
#include "delay.h"
#include "OLED.h"
#include "i2c.h"
#include "UART.h"
#include <stdarg.h>
#include "stdio.h"
#include "key.h"


uint8_t i=0;

int main(void)
{

  
	HAL_Init();                         /* 初始化HAL库 */
  sys_stm32_clock_init(RCC_PLL_MUL9); /* 设置时钟, 72Mhz */
  delay_init(72);                     /* 延时初始化 */
  LED_Init();                        /* LED初始化 */
	Uart_Init(115200);
	LED_Exit_Init();
	OLED_Init();
	KEY_Init();
	I2C_Init();
	I2C2_Init();
	printf("串口模块正常启动\r\n");
	HAL_I2C_EnableListen_IT(&I2C_Handle2);

	 while (1)
	 {
			if(KEY_Scan())
			{	
				//PA0按下:具体操作:主机1给从机两次发发
				printf("PA0按下\r\n");
				HAL_I2C_Master_Seq_Transmit_DMA(&I2C_Handle,0x10,&send_address1[count],1,I2C_FIRST_FRAME);
			}
			

			
	 }   
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/581374.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

代码随想录算法训练营第12天:滑动窗口和前缀和

代码随想录算法训练营第12天&#xff1a;滑动窗口和前缀和 这里我参考了西法的博客&#xff0c; 467. 环绕字符串中唯一的子字符串(中等)795. 区间子数组个数(中等)904. 水果成篮(中等)992. K 个不同整数的子数组&#xff08;困难&#xff09;1109. 航班预订统计(中等) 前四…

第G9周:ACGAN理论与实战

&#x1f368; 本文为&#x1f517;365天深度学习训练营 中的学习记录博客&#x1f356; 原作者&#xff1a;K同学啊 由于ACGAN的原理在上一篇文章中已经很详细的解释过了&#xff0c;这次我们直接上代码 一、代码解读 import argparse import os import numpy as npimport t…

视频批量下载工具

1、功能演示 该工具实现了某个人主页视频批量下载&#xff0c;最多支持一次下载50个视频&#xff0c;这50个选取的是最新发布的50个视频&#xff0c;视频为高清的1080p&#xff0c;并直接将视频保存到本地。 2、软件使用介绍 2.1 解压 拿到工具软件后&#xff0c;首先是对软件…

什么是外汇爆仓?怎样避免?

外汇爆仓是指当交易者的保证金低于特定比例时&#xff0c;经纪商会自动平仓一个或所有的开仓头寸。避免外汇爆仓的关键在于合理配置资金、设置止损、适度交易、顺势而为以及调整心态。 外汇爆仓是外汇交易中的一种风险控制机制。当交易者的账户净值低于已用保证金的特定比例时&…

AI图书推荐:《企业AI转型:如何在企业中部署ChatGPT?》

Jay R. Enterprise AI in the Cloud. A Practical Guide...ChatGPT Solutions &#xff08;《企业AI转型&#xff1a;如何在企业中部署ChatGPT&#xff1f;》&#xff09;是一本由Rabi Jay撰写、于2024年由John Wiley & Sons出版的书籍&#xff0c;主要为企业提供实施AI转型…

【网络安全】00后程序员,找 Bug 赚了 6,700,000元!他是怎么做到的?

1. 漏洞赏金计划&#xff08;Bug Bounty Programs&#xff09; 2. 安全咨询服务 3. 安全培训和教育 4. 写作和发表研究 5. 参与安全竞赛&#xff08;CTFs&#xff09; 6. 开发和销售安全工具 在网络安全领域&#xff0c;通过合法的方式利用漏洞赚钱主要涉及以下几种方法。…

【服务器部署篇】Linux下Tomcat安装和配置

作者介绍&#xff1a;本人笔名姑苏老陈&#xff0c;从事JAVA开发工作十多年了&#xff0c;带过刚毕业的实习生&#xff0c;也带过技术团队。最近有个朋友的表弟&#xff0c;马上要大学毕业了&#xff0c;想从事JAVA开发工作&#xff0c;但不知道从何处入手。于是&#xff0c;产…

记录一次 vue3 + ele pls 改写 饿了么主题色实践

一、改写 element 主题色 在 main.ts 中引入需要改写的 scss 文件 main.ts: import ./styles/element-plus.scss在自定义的 element-plus.scss 文件中改写 ele pls 的变量 element-plus.scss&#xff1a; /** * 更改主题色 */ :root {--el-color-primary: #285fbb; // 主…

python使用opencv对图像的基本操作(3)

17.颜色空间的转换 17.1.rgb图像转gray图像 from skimage import io,color img io.imread(lbxx.jpg) img_gray color.rgb2gray(img) #将rgb图像转换成gray图像 io.imshow(img_gray)运行结果&#xff1a; 17.2.rgb图像转hsv图像 from skimage import io,color img io.im…

网红大佬的面子,高阶智驾的里子 | 2024北京车展

相关阅读&#xff1a;2023北京车展 《没有争奇斗艳的车模&#xff0c;只有往死里卷的智能汽车》。 文&#xff5c;刘俊宏 李想、李斌绑定“车圈新顶流”雷军互相抬轿子&#xff0c;红衣大叔周鸿祎高情商点评各家汽车新品...... 为了流量&#xff0c;今年车企大佬们比任何时候…

volatile的相关知识点

volatitle这个关键字&#xff0c;不管是在Java还是在C中都有使用到&#xff0c;但是在两种语言中&#xff0c;由于编译器的原因&#xff0c;他们存在一点点区别。 C中的volatile关键字 在C中volatile主要用于告诉编译器&#xff0c;这个变量的值可能会意外改变 ,例如被硬件或者…

力扣刷题 62.不同路径

题干 一个机器人位于一个 m x n 网格的左上角 &#xff08;起始点在下图中标记为 “Start” &#xff09;。 机器人每次只能向下或者向右移动一步。机器人试图达到网格的右下角&#xff08;在下图中标记为 “Finish” &#xff09;。 问总共有多少条不同的路径&#xff1f; …

HertzBeat:一款开源实时监控告警系统,简直太好用了!

&#x1f604; 19年之后由于某些原因断更了三年&#xff0c;23年重新扬帆起航&#xff0c;推出更多优质博文&#xff0c;希望大家多多支持&#xff5e; &#x1f337; 古之立大事者&#xff0c;不惟有超世之才&#xff0c;亦必有坚忍不拔之志 &#x1f390; 个人CSND主页——Mi…

训练营第三十六天动态规划(基础题part2)

训练营第三十六天动态规划&#xff08;基础题part2&#xff09; 62.不同路径 力扣题目链接 题目 一个机器人位于一个 m x n 网格的左上角 &#xff08;起始点在下图中标记为 “Start” &#xff09;。 机器人每次只能向下或者向右移动一步。机器人试图达到网格的右下角&am…

企业计算机服务器中了rmallox勒索病毒怎么办,rmallox勒索病毒解密流程

对于众多的企业来说&#xff0c;通过网络开展各项工作业务已经成为常态&#xff0c;网络为企业的生产运营提供了极大便利&#xff0c;也大大加快了企业发展的步伐&#xff0c;但众多企业越来越重视企业发展中的核心数据安全问题。近期&#xff0c;云天数据恢复中心接到众多企业…

Linux的学习之路:21、线程(1)

摘要&#xff1a; 本章说一下线程 目录 摘要&#xff1a; 一、回忆一下 二、如何理解线程 三、命令行看线程 四、利用函数进行使用 五、本章总结 1、线程的优点 2、线程的缺点 3、线程的异常 4、线程的用途 一、回忆一下 1、exe就是一个文件 2、我们的可执行程序…

企业工厂如何逆风翻盘:VR全景打破多重桎梏

现阶段&#xff0c;制造业工厂面临的困境&#xff0c;就是用着上百万的设备&#xff0c;却赚着几毛钱的利润。传统的工厂参观方式也存在着很多的局限性&#xff0c;例如时间上不方便、不能实地参访、生产线具有隐患等&#xff0c;都会使得参观者不能深入地了解工厂的生产环境和…

大模型对数字营销的驱动赋能

一、大模型驱动的营销数智化个信未来发展趋势 1.模型算法能力全面升级 大模型凭借智能化的用户洞察&#xff0c;个性化的需求预测、系统化的数据分析、效率化的营销决策以及实实化的全域检测支持&#xff0c;为营销行业更加准确地把握市场动态和消费者需求提供了强大支持。可以…

ubuntu22.04 修改内核源码教程

1. 确认当前内核版本 uname -a 2. 去ubuntu官网下载对应版本内核源码 6.5.0-28.29 : linux package : Ubuntu (launchpad.net) 3. 准备编译环境 sudo apt-get install libncurses5-dev libssl-dev build-essential openssl flex bison libelf-dev tar -xzvf linux_6.5.…

【VS+QT】visual studio 2022配置和搭建QT

一、下载QT 可以去QT官网下载:https://www.qt.io/product/development-tools。 直接安装。 二、安装qt插件 打开visual studio 2022&#xff0c;选择菜单栏中扩展->管理扩展 ,然后直接在vs插件市场搜索Qt Visual Studio Tools就行。 安装的时候根据提示&#xff0c;关闭…
最新文章