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确定自身是发送方还是接收方。
读写操作
写:
读:
两者区别是数据帧格式中的读写位不同,数据流向不同。
底层抽象出的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]);
}