7、i2c模块

i2c

i2c中发出时钟信号的是主机。主机通过地址来访问从机。

发送数据到总线的叫发送器,接受数据的器件叫接收器。

在物理结构上,i2c由一条串行总线SDA和一条串行时钟总线SCL构成。

I2C基本编程步骤:初始化时钟、配置引脚、起始信号、读、写、终止信号

使用到的中断:I2C2_EV_IRQHandler i2c的总线事务中断。
I2C1_ER_IRQHandler i2c的错误中断

起始信号(一般由主机产生)

  • 起始信号:当 SCL 为高电平期间,SDA 由高到低的跳变
  • 起始信号是一种电平跳变时序信号,而不是一个电平信号。

停止信号(一般由主机产生)

  • 停止信号:当 SCL 为高电平器件,SDA 由低到高的跳变
  • 停止信号也是一种电平跳变时序信号,而不是一个电平信号。

应答信号

  • 在接受数据的机器成功收到8位数据后,向发送数据的主机发送特定的低电平脉冲。
  • 每个字节后面都要跟一个应答信号,表示已经收到数据。

数据帧格式

​ IIC 总线上传输的数据信号是广义的,既包含地址信号,又包含真正的数据信号。在起始信号后必须传送一个从机的地址(7位),

第八位是数据的传输方向加上数据传送方向,正好一个字节,发出去后会收到一个应答位

  • 0:表示主机发数据
  • 1:表示主机接收数据

寻址约定

在i2c中,寻址字节由被控器的7位地址位和1位方向位组成。
方向位为0时,表示主控器将数据写到被控器,为1时表示主控器从被控器读取数据。
主控器发送起始信号后,立刻发送寻址字节,这时总线上面所有器件都将寻址字节中的7位地址与自己的地址进行比较。如果相同,则发送应答信号。
被控器根据数据方向位R/W确定自身是发送方还是接收方。

读写操作

写:

img

读:

在这里插入图片描述

两者区别是数据帧格式中的读写位不同,数据流向不同。

底层抽象出的i2c

typedef struct
{
  __IO uint32_t CR1;         /*!< I2C Control register 1,            Address offset: 0x00 */
  __IO uint32_t CR2;         /*!< I2C Control register 2,            Address offset: 0x04 */
  __IO uint32_t OAR1;        /*!< I2C Own address 1 register,        Address offset: 0x08 */
  __IO uint32_t OAR2;        /*!< I2C Own address 2 register,        Address offset: 0x0C */
  __IO uint32_t TIMINGR;     /*!< I2C Timing register,               Address offset: 0x10 */
  __IO uint32_t TIMEOUTR;    /*!< I2C Timeout register,              Address offset: 0x14 */
  __IO uint32_t ISR;         /*!< I2C Interrupt and status register, Address offset: 0x18 */
  __IO uint32_t ICR;         /*!< I2C Interrupt clear register,      Address offset: 0x1C */
  __IO uint32_t PECR;        /*!< I2C PEC register,                  Address offset: 0x20 */
  __IO uint32_t RXDR;        /*!< I2C Receive data register,         Address offset: 0x24 */
  __IO uint32_t TXDR;        /*!< I2C Transmit data register,        Address offset: 0x28 */
} I2C_TypeDef;
//=====================================================================
//文件名称:i2c.C
//功能概要:i2c底层驱动构件源文件
//制作单位:苏州大学嵌入式系统与物联网研究所(sumcu.suda.edu.cn)
//版    本:   2020-11-06  V2.0
//适用芯片:STM32
//=====================================================================
#include "i2c.h"
/*
使用到的一些宏定义
#define I2CA  2
#define I2CB  1

#define I2C1                ((I2C_TypeDef *) I2C1_BASE) // 后面这个Base就是地址
#define I2C2                ((I2C_TypeDef *) I2C2_BASE)
#define I2C3                ((I2C_TypeDef *) I2C3_BASE)

typedef enum
{
	I2C_OK = 0,
	I2C_ERROR,
}I2C_STATUS;
*/
const static I2C_TypeDef* I2C_BASE[3] = {I2C1,I2C2,I2C3};
const static IRQn_Type    I2C_IRQ[3] = {I2C1_EV_IRQn,I2C2_EV_IRQn,I2C3_EV_IRQn,};

const uint32_t TIME_OUT = 0XFFFFEU;   //时间溢出


//======================================================================
//函数名称:i2c_init
//函数功能:初始化
//函数参数:I2C_No:I2C号;mode:模式;slaveAddress:从机地址;frequence:波特率
//函数说明:slaveAddress地址范围为0~127;frequence:10kb/s、100kb/s、400kb/s
//======================================================================
void i2c_init(uint8_t I2C_No,uint8_t mode,uint8_t slaveAddress,uint16_t frequence)
{
	uint8_t i=0;
	uint32_t temp;
	I2C_TypeDef* ptr = (I2C_TypeDef*)I2C_BASE[I2C_No];
    //(1)打开时钟门和设置为I2C功能
	switch(I2C_No)
	{
	case 0:    //    PTA9--SCL   PTA10--SDA
		RCC->APB1ENR1 |= RCC_APB1ENR1_I2C1EN_Msk;  //使能I2C1的时钟门
		RCC->CCIPR |= (1<<RCC_CCIPR_I2C1SEL_Pos);
		RCC->APB1ENR1 |= RCC_APB1ENR1_I2C1EN_Msk;  //使能I2C2的时钟门
		RCC->AHB2ENR |= RCC_AHB2ENR_GPIOAEN_Msk;   //使能GPIOB的时钟门
        //设置GPIO模式
		GPIOA->MODER &= ~(GPIO_MODER_MODE10_Msk|GPIO_MODER_MODE9_Msk);
		GPIOA->MODER |= (2 << GPIO_MODER_MODE9_Pos) | (2 << GPIO_MODER_MODE10_Pos);
		//设置Open Drain
		GPIOA->OTYPER &= ~(GPIO_OTYPER_OT10_Msk | GPIO_OTYPER_OT9_Msk);
		GPIOA->OTYPER |= (1<<GPIO_OTYPER_OT10_Pos) | (1<<GPIO_OTYPER_OT9_Pos);
        //设置为上拉
		GPIOA->PUPDR &= ~(GPIO_PUPDR_PUPD10_Msk | GPIO_PUPDR_PUPD9_Msk);
		GPIOA->PUPDR |= (1<<GPIO_PUPDR_PUPD10_Pos) | (1<<GPIO_PUPDR_PUPD9_Pos);
		//设置速度
		GPIOA->OSPEEDR &= ~(GPIO_OSPEEDR_OSPEED10_Msk | GPIO_OSPEEDR_OSPEED9_Msk);
		GPIOA->OSPEEDR |= ((3<<GPIO_OSPEEDR_OSPEED10_Pos) | (3<<GPIO_OSPEEDR_OSPEED9_Pos));
		//设置为I2C功能
		GPIOA->AFR[1] &=  ~(GPIO_AFRH_AFSEL10_Msk | GPIO_AFRH_AFSEL9_Msk);
		GPIOA->AFR[1] |= (4<<GPIO_AFRH_AFSEL10_Pos) | (4<<GPIO_AFRH_AFSEL9_Pos);
		break;
	case 1:  //    PTB10--SCL   PTB11--SDA
		RCC->CCIPR |= (1<<RCC_CCIPR_I2C2SEL_Pos);
		RCC->APB1ENR1 |= RCC_APB1ENR1_I2C2EN_Msk;  //使能I2C2的时钟门
		RCC->AHB2ENR |= RCC_AHB2ENR_GPIOBEN_Msk;   //使能GPIOB的时钟门
        //设置GPIO模式
		GPIOB->MODER &= ~(GPIO_MODER_MODE10_Msk|GPIO_MODER_MODE11_Msk);
		GPIOB->MODER |= (2 << GPIO_MODER_MODE11_Pos) | (2 << GPIO_MODER_MODE10_Pos);
		//设置Open Drain
		GPIOB->OTYPER &= ~(GPIO_OTYPER_OT10_Msk | GPIO_OTYPER_OT11_Msk);
		GPIOB->OTYPER |= (1<<GPIO_OTYPER_OT10_Pos) | (1<<GPIO_OTYPER_OT11_Pos);
        //设置为上拉
		GPIOB->PUPDR &= ~(GPIO_PUPDR_PUPD10_Msk | GPIO_PUPDR_PUPD11_Msk);
		GPIOB->PUPDR |= (1<<GPIO_PUPDR_PUPD10_Pos) | (1<<GPIO_PUPDR_PUPD11_Pos);
		//设置速度
		GPIOB->OSPEEDR &= ~(GPIO_OSPEEDR_OSPEED10_Msk | GPIO_OSPEEDR_OSPEED11_Msk);
		GPIOB->OSPEEDR |= ((3<<GPIO_OSPEEDR_OSPEED10_Pos) | (3<<GPIO_OSPEEDR_OSPEED11_Pos));
		//设置为I2C功能
		GPIOB->AFR[1] &=  ~(GPIO_AFRH_AFSEL10_Msk | GPIO_AFRH_AFSEL11_Msk);
		GPIOB->AFR[1] |= (4<<GPIO_AFRH_AFSEL10_Pos) | (4<<GPIO_AFRH_AFSEL11_Pos);
		break;
	case 2:  //     PTC0--SCL  PTC1--SDA
		RCC->CCIPR |= (1<<RCC_CCIPR_I2C3SEL_Pos);      //选择系统时钟,system clock
		RCC->APB1ENR1 |= RCC_APB1ENR1_I2C3EN_Msk;      //使能I2C3的时钟门
		RCC->AHB2ENR |= RCC_AHB2ENR_GPIOCEN_Msk;       //使能GPIOB的时钟门
        //设置GPIO模式
		GPIOC->MODER &= ~(GPIO_MODER_MODE0_Msk|GPIO_MODER_MODE1_Msk);
		GPIOC->MODER |= (2 << GPIO_MODER_MODE0_Pos) | (2 << GPIO_MODER_MODE1_Pos);
		//设置Open Drain
		GPIOC->OTYPER &= ~(GPIO_OTYPER_OT0_Msk | GPIO_OTYPER_OT1_Msk);
		GPIOC->OTYPER |= (1<<GPIO_OTYPER_OT0_Pos) | (1<<GPIO_OTYPER_OT1_Pos);
        //设置为上拉
		GPIOC->PUPDR &= ~(GPIO_PUPDR_PUPD0_Msk | GPIO_PUPDR_PUPD1_Msk);
		GPIOC->PUPDR |= (1<<GPIO_PUPDR_PUPD0_Pos) | (1<<GPIO_PUPDR_PUPD1_Pos);
        //OSPEEDR设置为高速模式
		GPIOC->OSPEEDR &= ~(GPIO_OSPEEDR_OSPEED0_Msk | GPIO_OSPEEDR_OSPEED1_Msk);
		GPIOC->OSPEEDR |= ((3<<GPIO_OSPEEDR_OSPEED0_Pos) | (3<<GPIO_OSPEEDR_OSPEED1_Pos));
		//设置为I2C功能
		GPIOC->AFR[0] &=  ~(GPIO_AFRL_AFSEL0_Msk | GPIO_AFRL_AFSEL1_Msk);
		GPIOC->AFR[0] |= (4<<GPIO_AFRL_AFSEL0_Pos) | (4<<GPIO_AFRL_AFSEL1_Pos);
		break;
	}
    //(1)清寄存器,复位I2C的寄存器值
    ptr->CR1 &= ~I2C_CR1_PE_Msk;
    for(i=0;i<100;i++);  //延时
    //(3)设置主从机模式
    if(mode == 1)
    {
        //配置I2C时钟
    	switch(frequence)
    	{
    	case 10:   //标准模式  10Khz
        	temp = (0xb << I2C_TIMINGR_PRESC_Pos) | (0xc7 << I2C_TIMINGR_SCLL_Pos) |\
        			(0xc3 << I2C_TIMINGR_SCLH_Pos) | (0x2 << I2C_TIMINGR_SDADEL_Pos) |\
    				(0x4 << I2C_TIMINGR_SCLDEL_Pos);
    		break;
    	case 100:  //标准模式  100Khz
        	temp = (0xb << I2C_TIMINGR_PRESC_Pos) | (0x13 << I2C_TIMINGR_SCLL_Pos) |\
        			(0xf << I2C_TIMINGR_SCLH_Pos) | (0x2 << I2C_TIMINGR_SDADEL_Pos) |\
    				(0x4 << I2C_TIMINGR_SCLDEL_Pos);
    		break;
    	case 400:  //FAST模式 400Khz
        	temp = (0x5 << I2C_TIMINGR_PRESC_Pos) | (0x9 << I2C_TIMINGR_SCLL_Pos) |\
        			(0x3 << I2C_TIMINGR_SCLH_Pos) | (0x4 << I2C_TIMINGR_SDADEL_Pos) |\
    				(0x3 << I2C_TIMINGR_SCLDEL_Pos);
    		break;
    	default:   //标准模式 100Khz
        	temp = (0xb << I2C_TIMINGR_PRESC_Pos) | (0x13 << I2C_TIMINGR_SCLL_Pos) |\
        			(0xf << I2C_TIMINGR_SCLH_Pos) | (0x2 << I2C_TIMINGR_SDADEL_Pos) |\
    				(0x4 << I2C_TIMINGR_SCLDEL_Pos);
    		break;
    	}

        ptr->TIMINGR = temp;
        ptr->CR1 |= I2C_CR1_PE_Msk;
    }
    else
    {
        //使能地址寄存器1,设置从机地址为slaveAddress
        ptr->OAR1 &= ~I2C_OAR1_OA1EN_Msk;
        ptr->OAR1 = (slaveAddress<<1) | I2C_OAR1_OA1EN_Msk;
        ptr->CR2 |= (I2C_CR2_AUTOEND | I2C_CR2_NACK);
        ptr->CR1 |= I2C_CR1_PE_Msk;//使能外设
    }
    for(i=0;i<100;i++);   //延时
}

//======================================================================
//函数名称:i2c_master_send
//函数功能:主机数据向从机写入数据
//函数参数:I2C_No:I2C号;slaveAddress:从机地址;data:代写入数据首地址
//函数说明:slaveAddress地址范围为0~127;
//======================================================================
uint8_t i2c_master_send(uint8_t I2C_No,uint8_t slaveAddress,uint8_t *data)
{
	uint32_t temp=0;
	uint32_t time = 0;
	I2C_TypeDef* ptr = (I2C_TypeDef*)I2C_BASE[I2C_No];

    //(1)发送开始信号,
	temp = (slaveAddress<<1) | (1<<I2C_CR2_NBYTES_Pos);
	temp |= I2C_CR2_AUTOEND_Msk | I2C_CR2_START_Msk;
	ptr->CR2 = temp;  //配置发送从机地址,方向,字节数,自动结束
	//(2)接收数据
		ptr->ISR |= I2C_ISR_TXE_Msk;
		while((ptr->ISR & I2C_ISR_TXE_Msk) != I2C_ISR_TXE_Msk)
		{
			time++;
			if(time >= TIME_OUT)
			{
				return I2C_ERROR;
			}
		}
		ptr->TXDR = *data;
	//(3)等待结束信号
	time = 0;
	while((ptr->ISR & I2C_ISR_STOPF_Msk) != I2C_ISR_STOPF_Msk)
	{
		time++;
		if(time >= TIME_OUT)
		{
			return I2C_ERROR;
		}
	}
	ptr->ICR |= I2C_ICR_STOPCF_Msk;   //清停止标志位


	return I2C_OK; //发送成功
}

//======================================================================
//函数名称:i2c_master_receive
//函数功能:主机数据向从机读取数据
//函数参数:I2C_No:I2C号;slaveAddress:从机地址;data:数据存储区
//函数说明:slaveAddress地址范围为0~127;
//======================================================================
uint8_t i2c_master_receive(uint8_t I2C_No,uint8_t slaveAddress,uint8_t *data)
{
	uint32_t temp=0;
	uint32_t time = 0;
	I2C_TypeDef* ptr = (I2C_TypeDef*)I2C_BASE[I2C_No];
    //(1)发送开始信号,
	temp = (slaveAddress<<1) | (1<<I2C_CR2_NBYTES_Pos);
	temp |= I2C_CR2_RD_WRN_Msk | I2C_CR2_AUTOEND_Msk | I2C_CR2_START_Msk;
	ptr->CR2 = temp;
	//(2)发送数据
		while((ptr->ISR & I2C_ISR_RXNE_Msk) != I2C_ISR_RXNE_Msk)
		{
			time++;
			if(time >= TIME_OUT)
			{
				return I2C_ERROR;
			}
		}
		*data = ptr->RXDR;

	//(3)等待结束信号
	time = 0;
	while((ptr->ISR & I2C_ISR_STOPF_Msk) != I2C_ISR_STOPF_Msk)
	{
		time++;
		if(time >= TIME_OUT)
		{
			return I2C_ERROR;
		}
	}
	ptr->ICR |= I2C_ICR_STOPCF_Msk;   //清停止标志位
	return I2C_OK; //发送成功
}

//======================================================================
//函数名称:i2c_slave_send
//函数功能:从机向主机发送数据
//函数参数:I2C_No:I2C号;data:数据存储区
//函数说明:slaveAddress地址范围为0~127;
//======================================================================
uint8_t i2c_slave_send(uint8_t I2C_No,uint8_t *data)
{
	uint32_t time = 0;
	I2C_TypeDef* ptr = (I2C_TypeDef*)I2C_BASE[I2C_No];
	//(1)发送应答信号
	ptr->CR2 &= ~I2C_CR2_NACK_Msk;  //回复主机发送过来的地址信息
	//(2)消除地址接收标志位
	while((ptr->ISR & I2C_ISR_ADDR_Msk) != I2C_ISR_ADDR_Msk)
	{
		time++;
		if(time >= TIME_OUT)
		{
			return I2C_ERROR;
		}
	}
	ptr->ICR |= I2C_ICR_ADDRCF_Msk;  //清地址接收标志位
	//(3)等待为发送方向
	time = 0;
	while((ptr->ISR & I2C_ISR_DIR_Msk) != I2C_ISR_DIR_Msk)
	{
		time++;
		if(time >= TIME_OUT)
		{
			return I2C_ERROR;
		}
	}
	//(4)发送数据
	time=0;

		while((ptr->ISR & I2C_ISR_TXE_Msk) != I2C_ISR_TXE_Msk)
		{
			time++;
			if(time >= TIME_OUT)
			{
				return I2C_ERROR;
			}
		}
		ptr->TXDR = *data;

	//(5)等待停止信号
	time = 0;
	while((ptr->ISR & I2C_ISR_STOPF_Msk) != I2C_ISR_STOPF_Msk)
	{
		time++;
		if(time >= TIME_OUT)
		{
			return I2C_ERROR;
		}
	}
	ptr->ICR |= I2C_ICR_STOPCF_Msk;   //清停止标志位
	return I2C_OK;
}

//======================================================================
//函数名称:i2c_slave_receive
//函数功能:从机接收主机发送的数据
//函数参数:I2C_No:I2C号;data:数据存储区
//函数说明:slaveAddress地址范围为0~127;
//======================================================================
uint8_t i2c_slave_receive(uint8_t I2C_No,uint8_t *data)
{
	uint32_t time=0;
	I2C_TypeDef* ptr = (I2C_TypeDef*)I2C_BASE[I2C_No];

	//(1)发送应答信号
	ptr->CR2 &= ~I2C_CR2_NACK_Msk;  //回复主机发送过来的地址信息
	//(2)消除地址接收标志位
	while((ptr->ISR & I2C_ISR_ADDR_Msk) != I2C_ISR_ADDR_Msk)
	{
		time++;
		if(time >= TIME_OUT)
		{
			return I2C_ERROR;
		}
	}
	ptr->ICR |= I2C_ICR_ADDRCF_Msk;   //清地址接收标志位
	//(3)等待方向为接收
	time = 0;
	while((ptr->ISR & I2C_ISR_DIR_Msk) == I2C_ISR_DIR_Msk)
	{
		time++;
		if(time >= TIME_OUT)
		{
			return I2C_ERROR;
		}
	}
	//(4)发送数据
	time = 0;

		ptr->ISR |= I2C_ISR_RXNE_Msk;
		while((ptr->ISR & I2C_ISR_RXNE_Msk) != I2C_ISR_RXNE_Msk)
		{
			time++;
			if(time >= TIME_OUT)
			{
				return I2C_ERROR;
			}
		}
		*data = ptr->RXDR;

	ptr->ICR |= I2C_ICR_STOPCF_Msk;    //清停止标志位
	//(5)发送非应答信号
	ptr->CR2 |=	I2C_CR2_NACK_Msk;
	time = 0;
	while((ptr->ISR & I2C_ISR_STOPF_Msk) != I2C_ISR_STOPF_Msk)
	{
		time++;
		if(time >= TIME_OUT)
		{
			return I2C_ERROR;
		}
	}
	return I2C_OK;
}

//======================================================================
//函数名称:i2c_enableInterput
//函数功能:开启接收中断
//函数参数:I2C_No:I2C号
//函数说明:无
//======================================================================
void i2c_R_enableInterput(uint8_t I2C_No)
{
	I2C_TypeDef* ptr = (I2C_TypeDef*)I2C_BASE[I2C_No];
	ptr->ISR |= I2C_ISR_RXNE|I2C_ISR_STOPF|I2C_ISR_TC_Msk|I2C_ISR_TCR_Msk|I2C_ISR_NACKF|I2C_ISR_ADDR_Msk;				//-----20210128
	ptr->CR1 |= I2C_CR1_RXIE_Msk|I2C_CR1_STOPIE|I2C_CR1_TCIE|I2C_CR1_NACKIE|I2C_CR1_ADDRIE;
	NVIC_EnableIRQ(I2C_IRQ[I2C_No]);
}

//======================================================================
//函数名称:i2c_disableInterput
//函数功能:禁止接收中断
//函数参数:I2C_No:I2C号
//函数说明:无
//======================================================================
void i2c_R_disableInterput(uint8_t I2C_No)
{
	I2C_TypeDef* ptr = (I2C_TypeDef*)I2C_BASE[I2C_No];
	ptr->ISR &= ~I2C_ISR_ADDR_Msk;
	ptr->CR1 &= ~I2C_CR1_RXIE_Msk;
	ptr->CR1 &= ~I2C_CR1_ADDRIE;
	NVIC_DisableIRQ(I2C_IRQ[I2C_No]);
}

//======================================================================
//函数名称:i2c_T_enableInterput
//函数功能:开启发送中断
//函数参数:I2C_No:I2C号
//函数说明:无
//======================================================================
void i2c_T_enableInterput(uint8_t I2C_No)
{
	I2C_TypeDef* ptr = (I2C_TypeDef*)I2C_BASE[I2C_No];
	ptr->ISR |= I2C_ISR_TXIS_Msk;
	ptr->CR1 |= I2C_CR1_TXIE_Msk;
	NVIC_EnableIRQ(I2C_IRQ[I2C_No]);

}

//======================================================================
//函数名称:i2c_T_disableInterput
//函数功能:禁止发送中断
//函数参数:I2C_No:I2C号
//函数说明:无
//======================================================================
void i2c_T_disableInterput(uint8_t I2C_No)
{
	I2C_TypeDef* ptr = (I2C_TypeDef*)I2C_BASE[I2C_No];
	ptr->CR1 &= ~I2C_CR1_TXIE_Msk;
	NVIC_DisableIRQ(I2C_IRQ[I2C_No]);
}
posted @ 2023-12-09 15:53  深渊之巅  阅读(71)  评论(0编辑  收藏  举报