STM32F407硬件I2C从机配置

1 篇文章 0 订阅
1 篇文章 0 订阅

公司项目需要用到I2C从机与上位机通信。在网上搜了搜,发现大部分都是说STM32硬件有问题的,与硬件I2C从机相关的资料很少。调通之后,想着把demo分享出来,丰富一下网上的例程。STM32F1系列稍微修改下也能适用。


#include "i2c.h"

//PB6 I2C1_SCL
//PB7 I2C1_SDA

 void MyI2C_Init(void)
 {
	  GPIO_InitTypeDef  GPIO_InitStructure;
	 I2C_InitTypeDef I2C_InitStructure;
	 NVIC_InitTypeDef NVIC_InitStructue;
	 
	 
	 RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB, ENABLE);//使能GPIOA时钟
	 RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C1,ENABLE);//使能I2C1时钟 
   //I2C_DeInit(I2C1);
	 I2C_InitStructure.I2C_Mode=I2C_Mode_I2C;
	 I2C_InitStructure.I2C_DutyCycle=I2C_DutyCycle_2;
	 I2C_InitStructure.I2C_OwnAddress1=0x30;//I2C_SLAVE_ADDRESS7为从机地址
	 I2C_InitStructure.I2C_Ack=I2C_Ack_Enable;
	 I2C_InitStructure.I2C_AcknowledgedAddress=I2C_AcknowledgedAddress_7bit;//指定地址的长度
	 I2C_InitStructure.I2C_ClockSpeed=ClockSpeed;//设置SCL时钟频率,
	 
	 I2C_Init(I2C1,&I2C_InitStructure);
	 
	 NVIC_InitStructue.NVIC_IRQChannel=I2C1_EV_IRQn;
	 NVIC_InitStructue.NVIC_IRQChannelPreemptionPriority =0;
	 NVIC_InitStructue.NVIC_IRQChannelSubPriority=2;
	 NVIC_InitStructue.NVIC_IRQChannelCmd=ENABLE;
	 NVIC_Init(&NVIC_InitStructue);
	 /*配置错误中断,ER_IRQ的中断只要响应没有应答和起始和停止条件出错等*/
	 NVIC_InitStructue.NVIC_IRQChannel=I2C1_ER_IRQn;
	 NVIC_InitStructue.NVIC_IRQChannelSubPriority=3;
	 NVIC_Init(&NVIC_InitStructue);
	 
	 	 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6|GPIO_Pin_7;
   GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;//模拟输入
	 GPIO_InitStructure.GPIO_OType=GPIO_OType_OD;
   GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL ;//上拉
	 GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
   GPIO_Init(GPIOB, &GPIO_InitStructure);//初始化  
	 
	 GPIO_PinAFConfig(GPIOB,GPIO_PinSource6,GPIO_AF_I2C1);//PB6复用为I2C1
	 GPIO_PinAFConfig(GPIOB,GPIO_PinSource7,GPIO_AF_I2C1);//PB7复用为I2C1
	 
	 
	 
	 /*使能I2C1 event and buffer interrupt*/
	 I2C_ITConfig (I2C1,I2C_IT_EVT|I2C_IT_BUF,ENABLE);
	 
	 /*使能I2C1 Error interrupts*/
	 I2C_ITConfig(I2C1,I2C_IT_ERR,ENABLE);
	 I2C_Cmd(I2C1,ENABLE);
	 
 }

void I2C1_EV_IRQHandler(void)     //事件中断处理函数 
{ 
	
    __IO uint16_t SR1Register =0;
    __IO uint16_t SR2Register =0; 
    SR1Register = I2C1->SR1;
    SR2Register = I2C1->SR2;

 switch (I2C_GetLastEvent(I2C1))//获取i2c1的中断事件 
 { 
 
  /* 从发送 Slave Transmitter ---------------------------------------------------*/  
 case I2C_EVENT_SLAVE_BYTE_TRANSMITTED: //i2c_event_slave_byte_transmitted//
 
  /* 移位寄存器为空,数据寄存器为空,在DR中写入Data1.
     这个和下面那个都是从发送模式下发送数据的,具体两个的区别我也不是很明白,感觉就是移位寄存器空与非  空的区别,准备好数据发送吧 
    ***** */ 
 
 I2C_SendData(I2C1, 0X88);
 
   break; 
 
 case I2C_EVENT_SLAVE_BYTE_TRANSMITTING: /* EV3 */   //i2c_event_slave_byte_transmitting 
 
  /* 
 移位寄存器非空,数据寄存器为空,通过对DR执行写操作来清零。
 Transmit I2C1 data 
 *******/
		if(I2C1_Data_Adress_Mark==1)
		{
		//
			I2C_SendData(I2C1, I2C1_Data_Page1[I2C1_Data_Adress++]); 
		}else
		{
		I2C_SendData(I2C1, 28); 
		}
 
 
   break; 
 
 /* 从接收 Slave Receiver ------------------------------------------------------*/ 
 
 case I2C_EVENT_SLAVE_RECEIVER_ADDRESS_MATCHED:     /* EV1:从接收,收到地址后响应EV1中断 */
 
  /* 地址匹配中断,不管从发送和接收都要匹配地址,如下图244、243发送地址之后都会响应EV1 */ 
 
  break; 
 
 case I2C_EVENT_SLAVE_BYTE_RECEIVED:                /* EV2 从接收,地址后开始收到的数据*/ 
 
  /* Store I2C1 received data */ 
 
  /* 这个中断就是响应EV2中断,如下图244,每次主机发送完一个数据就会产生一个EV2的中断 */ 
	 if(I2C1_Data_Adress_Mark==0)
	 {
	  I2C1_Data_Adress=I2C_ReceiveData(I2C1);
    I2C1_Data_Adress_Mark=1;//收到数据地址,数据地址标志位置1;
	 }else if(I2C1_Data_Adress_Mark==1)
		{
		
    I2C1_Data_Page1[I2C1_Data_Adress++]=I2C_ReceiveData(I2C1);

		}
  /* 把接收到的中断填充到数组中 */ 
 
  /* 注意:地址不会填充进来的 */
 
   break; 
 
 
 case I2C_EVENT_SLAVE_STOP_DETECTED:                /* EV4 收到STOP停止信号*/
 
   /* Clear I2C1 STOPF flag */ 
  I2C1_Data_Adress_Mark=0;
 
  /* 这个就是正常停止的时候产生的一个停止信号 */ 
 
  I2C_Cmd(I2C1, ENABLE); 
 
  /* 不清楚为什么要这样,如果接收完数据之后,不响应主机的情况可以 关闭i2c,然后在处理完数据后再  从新配置i2c */ 
 
 // Rx_Idx=0; 
 
//  i2c_event = EVENT_OPCOD_NOTYET_READ; 
 
  break; 
 
 default: 
 
  break;    
 
  } 
 
} 

void I2C1_ER_IRQHandler(void) 
{ 
 
 /* Check on I2C1 AF flag and clear it */ 
 
 if (I2C_GetITStatus(I2C1, I2C_IT_AF)) 
 
 { 
 
  /* 没有应答的中断,发送了一串数据后的中断,可以做清零工作 */  
 
  I2C_ClearITPendingBit(I2C1, I2C_IT_AF); 
 
//  Tx_Idx = 0; 
// 
//  i2c_event = EVENT_OPCOD_NOTYET_READ; 
 
 } 
 
 /* Check on I2C1 AF flag and clear it */ 
 
 if (I2C_GetITStatus(I2C1, I2C_IT_BERR))   //起始和停止条件出错
 
 { 
 
  I2C_ClearITPendingBit(I2C1, I2C_IT_BERR);
 
  }
 
 }

 

STM32F407ZET6芯片内置了多个I2C接口,其中I2C1和I2C2可以通过外部引脚进行访问。以下是使用硬件I2C通信的一些基本步骤: 1. 配置GPIO引脚为I2C功能并使能时钟。 2. 配置I2C控制器的时钟频率、I2C地址、传输模式等参数。 3. 初始化I2C控制器并开启I2C总线。 4. 发送起始信号,并发送I2C设备地址和读/写控制位。 5. 发送数据或接收数据,并等待传输完成。 6. 发送停止信号以结束I2C通信。 以下是一个简单的示例代码,实现了通过I2C1接口向设备地址为0x50的EEPROM芯片写入一个字节的数据: ```c #include "stm32f4xx.h" #define I2C_SPEED 100000 // I2C时钟频率 #define EEPROM_ADDRESS 0x50 // EEPROM设备地址 void I2C1_Init(void) { GPIO_InitTypeDef GPIO_InitStructure; I2C_InitTypeDef I2C_InitStructure; // 使能GPIOB和I2C1时钟 RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB, ENABLE); RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C1, ENABLE); // 配置PB6和PB7为复用功能,并开启开漏输出 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6 | GPIO_Pin_7; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_InitStructure.GPIO_OType = GPIO_OType_OD; GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP; GPIO_Init(GPIOB, &GPIO_InitStructure); // 配置GPIO复用映射 GPIO_PinAFConfig(GPIOB, GPIO_PinSource6, GPIO_AF_I2C1); GPIO_PinAFConfig(GPIOB, GPIO_PinSource7, GPIO_AF_I2C1); // 配置I2C控制器 I2C_InitStructure.I2C_ClockSpeed = I2C_SPEED; I2C_InitStructure.I2C_Mode = I2C_Mode_I2C; I2C_InitStructure.I2C_DutyCycle = I2C_DutyCycle_2; I2C_InitStructure.I2C_OwnAddress1 = 0x00; I2C_InitStructure.I2C_Ack = I2C_Ack_Enable; I2C_InitStructure.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit; I2C_Init(I2C1, &I2C_InitStructure); // 开启I2C总线 I2C_Cmd(I2C1, ENABLE); } void I2C1_WriteByte(uint8_t data) { // 发送起始信号 I2C_GenerateSTART(I2C1, ENABLE); while (!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_MODE_SELECT)) {} // 发送设备地址和写控制位 I2C_Send7bitAddress(I2C1, EEPROM_ADDRESS, I2C_Direction_Transmitter); while (!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED)) {} // 发送数据 I2C_SendData(I2C1, data); while (!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_BYTE_TRANSMITTED)) {} // 发送停止信号 I2C_GenerateSTOP(I2C1, ENABLE); } int main(void) { I2C1_Init(); uint8_t data = 0x55; I2C1_WriteByte(data); while (1) {} } ``` 需要注意的是,I2C通信的具体实现可能会因为设备地址、寄存器地址、数据长度、传输模式等参数的不同而有所变化。因此在实际应用中需要根据具体情况进行调整。
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值