I2C—基于铁头山羊的STM32标准库教程

I2C是Inter-Integrated Circuit (内部集成电路)的简称。

初始化I2C的步骤:(可以分步配置,也可以直接复制修改最后的初始化总例程)

第一步:给SCL和SDA配置引脚(都要配置成复用输出开漏模式,即GPIO_MODE_AF_OD)

RCC_APB2PeriphclockCmd(RCC_APB2Periph_GPIOB, ENABLE);

GPIO_InitTypeDef GPIO_Initstruct;
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_值1|GPI0_Pin_值2;//值1和值2是数字,选择引脚
GPIO_Initstruct.GPIO_Mode = GPIO_Mode_AF_OD;//复用开漏输出,不要改
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_10MHz;//2MHz或10MHz,一般选10MHz
GPIO_Init(GPIO值3,&GPIO_Initstruct);//值3可以是A,B,C,D,E,F,G

第二步:开启I2C的时钟并复位(不复位也可,但复位是好习惯)

RCC_APB1Periphclockcmd(RCC_APB1Periph_I2C值1,ENABLE);/*使能,此处三个值1都是数字,用于选择一个I2C通道*/
RCC_APBlPeriphResetcmd(RCC_APB1Periph_I2c值1,ENABLE);//复位的第一步
RCC_APBlPeriphResetCmd(RCC_APB1Periph_I2C值1, DISABLE);//复位的第二步

第三步:配置I2C参数

I2C_InitTypeDef I2C_InitStruct;

I2C_InitStruct.I2C_ClockSpeed = 400000;// 波特率,最高400k	
I2C_InitStruct.I2C_Mode = I2C_Mode_I2C;/*选择速度模式,可以是I2C_Mode_SMBusDevice或I2C_Mode_SMBusHost*/
I2C_InitStruct.I2C_DutyCycle = I2C_DutyCycle_2;/*可以是I2C_DutyCycle_2,即2:1,低电平占两份高电平占一份,或12C_DutyCycle_16_9,即16:9,低电平占16份,高电平占9份*/
	
I2C_Init(I2C1, &I2C_InitStruct);

第四步:使能I2C

I2C_Cmd(I2C值1, ENABLE);//值1是数字,用于选择一个I2C通道

初始化总例程:

static void App_I2C_Init(void)
{
	// #1. 初始化SCL和SDA引脚
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
	
	GPIO_InitTypeDef GPIO_InitStruct;
	GPIO_InitStruct.GPIO_Pin = GPIO_Pin_6 | GPIO_Pin_7;
	GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF_OD;
	GPIO_InitStruct.GPIO_Speed = GPIO_Speed_10MHz;
	GPIO_Init(GPIOB, &GPIO_InitStruct);
	
	// #2. 开启I2C的时钟并复位
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C1, ENABLE);
	RCC_APB1PeriphResetCmd(RCC_APB1Periph_I2C1, ENABLE);
	RCC_APB1PeriphResetCmd(RCC_APB1Periph_I2C1, DISABLE);
	
	// #3. 设置I2C的参数
	I2C_InitTypeDef I2C_InitStruct;
	
	I2C_InitStruct.I2C_Mode = I2C_Mode_I2C;
	I2C_InitStruct.I2C_ClockSpeed = 400000;
	I2C_InitStruct.I2C_DutyCycle = I2C_DutyCycle_2;
	
	I2C_Init(I2C1, &I2C_InitStruct);
	
	// #4. 使能I2C
	I2C_Cmd(I2C1, ENABLE);
}

数据发送流程概述:(可以复制修改最后的总例程)

等待空闲:

函数:查询标志位


I2C_GetFlagStatus(I2C值1,I2CFLAG_值2);/*此处值1是数字,用于选择一个I2C通道,值2为所要查询的标志位名称,可以是TRA,BUSY,MSL,SB。此函数返回值为RESET -标志位等于0,SET-标志位等于1*/
示例代码:
while(I2C_GetFlagStatus(I2C1,I2C_FLAG_BUSY)==SET);// 等待空闲

发送起始位:

函数:发送起始位


I2C_GenerateSTART(值1,值2);/*值1为数字,选择I2C通道,值2为:ENABLE-向START写1,DISABLE -向START写0*/

示例代码:
I2C_GenerateSTART(I2C1,ENABLE);//发送起始位
while(I2C_GetFlagStatus(I2C1,I2C_FLAG_SB)==RESET);// 起始位已发送

发送地址:

函数:清除标志位

I2C_ClearFlag(I2C1值1, I2C_FLAG_值2);/*此处值1是数字,用于选择一个I2C通道,值2为所要清除的标志位名称,可以是TRA,BUSY,MSL,SB。

示例:I2C_ClearFlag(I2C1,12C_FLAG_AF);// 清除AF标志位

函数:发送数据(向TDR写值)

I2C_SendData(I2C值1,值2);//此处值1是数字,用于选择一个I2C通道,值2为要发送的数据

示例:I2C_SendData(I2C1,SlaveAddr &0xfe);//发送地址

函数:发送停止位

I2C_GenerateStop(I2C值1);//此处值1是数字,用于选择一个I2C通道

示例:I2C_GenerateStop(12C1);//发送停止位

发送数据:

函数:读取寄存器

I2C_ReadRegister(I2C值1,I2C_Register_值2);/*此处值1是数字,用于选择一个I2C通道,值2可以是CR1,CR2,SR1,SR2等

例程:

I2C_ReadRegister(I2C1,I2C_Register_SR1);// 清除ADDR
I2C_ReadRegister(I2C1,I2C_Register_SR2);
for(i=0;i<Size;i++){
// 等待TXE
while(I2C_GetFlagStatus(I2C1,I2C_FLAG_TXE)==RESET){
if(120 GetFlagStatus(I2C1,I2C_FLAG_AF)==SET){
ret = ERROR:
goto STOP;
}}
I2C_SendData(I2C1,pData[i]);//发送数据
}
//等待BTF
while(I2C_GetFlagStatus(I2C1,I2C_FLAG_BTF)==RESET){
if(I2C_GetFlagStatus(12C1,I2C_FLAG_AF)==SET){
ret = ERROR;
goto STOP;
}}
//STOP:
I2C_GenerateSTOP(I2C1);
while(I2C_GetFlagStatus(I2C1,I2C_FLAG_BUSY)==SET);
return ret:

总例程:

ErrorStatus ret =SUCCESS;
I2C_ClearFlag(I2C1,I2C_FLAG_AF);// 清除AF
I2C_SendData(I2C1,SlaveAddr &0xfe);//发送地址
while(I2C_GetFlagStatus(I2C1,I2C_FLAG_ADDR)==RESET){// 等待数据发送完成
 if(I2C_GetFlagStatus(I2C1,I2C_FLAG_AF)==SET){
  ret = ERROR;
  goto STOP;
//发送数据
STOP:
I2C_GenerateSTOP(12C1);//发送停止位
while(I2C_GetFlagStatus(I2C1,I2C_FLAG_BUSY)==SET):// 等待总线空闲
return ret;

实验:

#include "stm32f10x.h"

#include "stm32f10x_pal_i2c.h"

static void App_I2C_Init(void);
static ErrorStatus App_I2C_MasterTransmit(uint8_t SlaveAddr, const uint8_t *pData, uint16_t Size);
static ErrorStatus App_I2C_MasterReceive(uint8_t SlaveAddr, uint8_t *pDataOut, uint16_t Size);

static PalI2C_HandleTypeDef hi2c1;

int main(void)
{
	uint8_t oled_init_command[] = {
		0x00, // Command Stream
		0xa8, 0x3f, // Set MUX Ratio
		0xd3, 0x00, // Set Display Offset
		0x40, // Set Display Start Line
		0xa0, // Set Segment re-map
		0xc0, // Set COM Output Scan Direction
		0xda, 0x02, // Set COM Pins hardware configuration
		0x81, 0x7f, // Set Contrast Control
		0xa5, // Enable Entire Display On
		0xa6, // Set Normal Display
		0xd5, 0x80, // Set OSC Frequency
		0x8d, 0x14, // Enable charge pump regulator
		0xaf, // Display on
	};
	
	uint8_t oled_off_command[] = { 0x00, 0xae};
	uint8_t oled_on_command[] = { 0x00, 0xaf};
	
	uint8_t buffer[8];
	
	

	
	hi2c1.Init.I2Cx = I2C1;
	hi2c1.Init.I2C_ClockSpeed = 400000;
	hi2c1.Init.I2C_DutyCycle = I2C_DutyCycle_2;
	PAL_I2C_Init(&hi2c1);
	
	// App_I2C_Init();
	
	PAL_I2C_MasterTransmit(&hi2c1, 0x78, oled_init_command, sizeof(oled_init_command)/sizeof(uint8_t));
	//App_I2C_MasterTransmit(0x78, oled_init_command, sizeof(oled_init_command)/sizeof(uint8_t));
	
	// 熄灭显示器
	PAL_I2C_MasterTransmit(&hi2c1, 0x78, oled_off_command, sizeof(oled_off_command)/sizeof(uint8_t));
	PAL_I2C_MasterReceive(&hi2c1, 0x78, buffer, 1);
	PAL_I2C_MasterReceive(&hi2c1, 0x78, buffer, 2);
	PAL_I2C_MasterReceive(&hi2c1, 0x78, buffer, 8);
	
	// 点亮显示器
	PAL_I2C_MasterTransmit(&hi2c1, 0x78, oled_on_command, sizeof(oled_off_command)/sizeof(uint8_t));
	PAL_I2C_MasterReceive(&hi2c1, 0x78, buffer, 1);
	PAL_I2C_MasterReceive(&hi2c1, 0x78, buffer, 2);
	PAL_I2C_MasterReceive(&hi2c1, 0x78, buffer, 8);
	
	while(1)
	{
	}
}

static void App_I2C_Init(void)
{
	// #1. 初始化SCL和SDA引脚
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
	
	GPIO_InitTypeDef GPIO_InitStruct;
	GPIO_InitStruct.GPIO_Pin = GPIO_Pin_6 | GPIO_Pin_7;
	GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF_OD;
	GPIO_InitStruct.GPIO_Speed = GPIO_Speed_10MHz;
	GPIO_Init(GPIOB, &GPIO_InitStruct);
	
	// #2. 开启I2C的时钟并复位
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C1, ENABLE);
	RCC_APB1PeriphResetCmd(RCC_APB1Periph_I2C1, ENABLE);
	RCC_APB1PeriphResetCmd(RCC_APB1Periph_I2C1, DISABLE);
	
	// #3. 设置I2C的参数
	I2C_InitTypeDef I2C_InitStruct;
	
	I2C_InitStruct.I2C_Mode = I2C_Mode_I2C;
	I2C_InitStruct.I2C_ClockSpeed = 400000;
	I2C_InitStruct.I2C_DutyCycle = I2C_DutyCycle_2;
	
	I2C_Init(I2C1, &I2C_InitStruct);
	
	// #4. 使能I2C
	I2C_Cmd(I2C1, ENABLE);
}

static ErrorStatus App_I2C_MasterTransmit(uint8_t SlaveAddr, const uint8_t *pData, uint16_t Size)
{
	ErrorStatus ret = SUCCESS;
	
	// #1. 等待总线空闲
	while(I2C_GetFlagStatus(I2C1, I2C_FLAG_BUSY) == SET);
	// #2. 发送起始位
	I2C_GenerateSTART(I2C1, ENABLE);
	while(I2C_GetFlagStatus(I2C1, I2C_FLAG_SB) == RESET);
	// #3. 发送地址
	I2C_ClearFlag(I2C1, I2C_FLAG_AF);
	I2C_SendData(I2C1, SlaveAddr & 0xfe);
	
	while(I2C_GetFlagStatus(I2C1, I2C_FLAG_ADDR) == RESET)
	{
		if(I2C_GetFlagStatus(I2C1, I2C_FLAG_AF) == SET)
		{
			ret = ERROR;
			goto STOP;
		}
	}
	
	// #4. 发送数据
	I2C_ReadRegister(I2C1, I2C_Register_SR1); // 清除ADDR
	I2C_ReadRegister(I2C1, I2C_Register_SR2);
	
	uint32_t i;
	
	for(i=0;i<Size;i++)
	{
		while(I2C_GetFlagStatus(I2C1, I2C_FLAG_TXE) == RESET)
		{
			if(I2C_GetFlagStatus(I2C1, I2C_FLAG_AF) == SET)
			{
				ret = ERROR;
				goto STOP;
			}
		}
		I2C_SendData(I2C1, pData[i]);
	}
	
	while(I2C_GetFlagStatus(I2C1, I2C_FLAG_BTF) == RESET)
	{
		if(I2C_GetFlagStatus(I2C1, I2C_FLAG_AF) == SET)
		{
			ret = ERROR;
			goto STOP;
		}
	}
	
	// #5. 发送停止位
STOP:
	I2C_GenerateSTOP(I2C1, ENABLE);
	while(I2C_GetFlagStatus(I2C1, I2C_FLAG_BUSY) == SET);
	
	return ret;
}

static ErrorStatus App_I2C_MasterReceive(uint8_t SlaveAddr, uint8_t *pDataOut, uint16_t Size)
{
	// #1. 等待总线空闲
	while(I2C_GetFlagStatus(I2C1, I2C_FLAG_BUSY) == SET);
	
	// #2. 发送起始位
	I2C_GenerateSTART(I2C1, ENABLE);
	
	while(I2C_GetFlagStatus(I2C1, I2C_FLAG_SB) == RESET);
	
	// #3. 发送从机地址
	I2C_ClearFlag(I2C1, I2C_FLAG_AF);
	
	I2C_SendData(I2C1, SlaveAddr | 0x01);
	
	while(I2C_GetFlagStatus(I2C1, I2C_FLAG_ADDR) == RESET)
	{
		if(I2C_GetFlagStatus(I2C1, I2C_FLAG_AF) == SET)
		{
			I2C_GenerateSTOP(I2C1, ENABLE);
			while(I2C_GetFlagStatus(I2C1, I2C_FLAG_BUSY) == SET);
			
			return ERROR;
		}
	}
	
	// #4. 接收数据
	if(Size == 1)
	{
		// 4.1. 清除ADDR标志位
		I2C_ReadRegister(I2C1, I2C_Register_SR1);
		I2C_ReadRegister(I2C1, I2C_Register_SR2);
		
		// 4.2. ACK=0, STOP=1
		I2C_AcknowledgeConfig(I2C1, DISABLE);
		I2C_GenerateSTOP(I2C1, ENABLE);
		
		// 4.3. 等待RXNE,然后把数据读出来
		while(I2C_GetFlagStatus(I2C1, I2C_FLAG_RXNE) == RESET);
		
		pDataOut[0] = I2C_ReceiveData(I2C1);
		
		while(I2C_GetFlagStatus(I2C1, I2C_FLAG_BUSY) == SET);
		
		return SUCCESS;
	}
	else if(Size == 2)
	{
		I2C_AcknowledgeConfig(I2C1, ENABLE);
		
		// 4.1. 清除ADDR标志位
		I2C_ReadRegister(I2C1, I2C_Register_SR1);
		I2C_ReadRegister(I2C1, I2C_Register_SR2);
		
		// 4.2. 等待RXNE=1
		while(I2C_GetFlagStatus(I2C1, I2C_FLAG_RXNE) == RESET);
		I2C_AcknowledgeConfig(I2C1, DISABLE);
		I2C_GenerateSTOP(I2C1, ENABLE);
		pDataOut[0] = I2C_ReceiveData(I2C1);
		
		// 4.3. 接收第2个字节
		while(I2C_GetFlagStatus(I2C1, I2C_FLAG_RXNE) == RESET);
		pDataOut[1] = I2C_ReceiveData(I2C1);
		
		// 4.4. 等待总线空闲
		while(I2C_GetFlagStatus(I2C1, I2C_FLAG_BUSY) == SET);
		
		return SUCCESS;
	}
	else if(Size > 2)
	{
		// ACK = 1 N-1 ACK NAK
		I2C_AcknowledgeConfig(I2C1, ENABLE);
		
		// 4.1. 清除ADDR标志位
		I2C_ReadRegister(I2C1, I2C_Register_SR1);
		I2C_ReadRegister(I2C1, I2C_Register_SR2);
		
		// 4.2. 
		uint32_t i;
		for(i=0;i<Size-3;i++)
		{
			while(I2C_GetFlagStatus(I2C1, I2C_FLAG_RXNE) == RESET);
			
			pDataOut[i] = I2C_ReceiveData(I2C1);
		}
		
		// 4.3. 等待BTF标志位置位
		while(I2C_GetFlagStatus(I2C1, I2C_FLAG_BTF) == RESET);
		
		pDataOut[Size-3] = I2C_ReceiveData(I2C1);
		pDataOut[Size-2] = I2C_ReceiveData(I2C1);
		
		// 4.4. ACK=0,STOP=1
		I2C_AcknowledgeConfig(I2C1, DISABLE);
		I2C_GenerateSTOP(I2C1, ENABLE);
		
		// 4.5. 等待RXNE标志位置位
		pDataOut[Size-1] = I2C_ReceiveData(I2C1);
		
		// 4.6. 等待总线空闲
		while(I2C_GetFlagStatus(I2C1, I2C_FLAG_BUSY) == SET);
		
		return SUCCESS;
	}
	else
	{
		return ERROR;
	}
}

  • 9
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值