16:【stm32】I2C的使用一:I2C片上外设的使用

news2024/9/30 15:25:06

I2C

  • 1、片上外设
    • 1.1:寄存器与内部结构
  • 2、通过I2C向外发送数据
    • 2.1:I2C的初始化
      • 2.1.1:初始化SCL和SDA
      • 2.1.2:使能时钟PCLK1(APB1)
      • 2.1.3:配置I2C1的参数
    • 2.2:发送数据
      • 2.2.1:等待总线空闲(BUSY标志位)
      • 2.2.2:发送起始位(START,SB标志位)
      • 2.2.3:发送地址(AF,ADDR标志位)
      • 2.2.4:发送数据

具体的通过I2C通信协议传输数据帧的相关信息参考《C51单片机》里面的第13节OLED屏的使用

1、片上外设

如图:I2C片上外设挂载在APB1总线上面,并且I2C1由复用重映射,I2C2没有重映射。
在这里插入图片描述
在这里插入图片描述

1.1:寄存器与内部结构

在这里插入图片描述

TDR -Transmit Data Register -发送数据寄存器
RDR -Receive Data Register -接收数据寄存器
CR  -Control Register -控制寄存器
SR1 -Status Register 1 -状态寄存器1
SR2 -Status Register 2 -状态寄存器2
CCR -Clock Control Register -时钟控制寄存器
  • SR1:状态寄存器1

    SB:起始位发送完成,0代表没有起始位产生,1代表检测到了起始位。
    AF/ADDR:AF代表接收到NAK,寻址失败AF=1;ADDR代表选址成功,寻址成功-ADDR=1。
    TXE:发送数据寄存器TDR空,1代表空,0代表非空。
    RXNE:接收数据寄存器RDR非空,1代表非空,0代表空。
    BTF:发送完成。1代表发送完成
    
  • SR2:状态寄存器2

    TRA
    BUSY:用于查询总线的空闲状态,0为空闲,1为忙。
    MSL
    
  • CR:控制寄存器

    PE:SCL的开关。
    START:用于产生一个起始位,写入1代表发送一个起始位。
    ACK:用于产生一个应答信号。
    

2、通过I2C向外发送数据

2.1:I2C的初始化

在这里插入图片描述

2.1.1:初始化SCL和SDA

我们使用I2C1,初始化SCL和SDA所连接的IO口,把他们配置为复用开漏模式。

//1 .开启GPIOB挂载的总线时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);
//初始化IO引脚
GPIO_InitTypeDef GPIOInitStruct;
GPIOInitStruct.GPIO_Mode = GPIO_Mode_AF_OD;//复用输出开漏模式
GPIOInitStruct.GPIO_Pin = GPIO_Pin_6 |GPIO_Pin_7;
GPIOInitStruct.GPIO_Speed = GPIO_Speed_2MHz;
GPIO_Init(GPIOB,&GPIOInitStruct);

2.1.2:使能时钟PCLK1(APB1)

//2 .开启I2C挂载APB1总线的时钟
RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C1,ENABLE);//开启时钟
RCC_APB1PeriphResetCmd(RCC_APB1Periph_I2C1,ENABLE);//对I2C1进行复位
RCC_APB1PeriphResetCmd(RCC_APB1Periph_I2C1,DISABLE);//对I2C1进行释放

2.1.3:配置I2C1的参数

在这里插入图片描述
标志模式下:波特率max = 100K,当波特率>=100K时,就按照快速模式进行。快速模式下:波特率max = 400K。
I2C_DutyCycle:在快速模式下,配置时钟信号的形状,即时钟信号的占空比。2:1 = 低电平:高电平/16:9 = 低电平:高电平
下面灰色是工作在从机模式下才需要进行配置的。

//3 .配置I2C1的参数
I2C_InitTypeDef I2CInitStruct;
I2CInitStruct.I2C_ClockSpeed = 400000;//配置波特率,最高400K
I2CInitStruct.I2C_Mode = I2C_Mode_I2C;//标准的I2C模式,一般不使用SMBus
I2CInitStruct.I2C_DutyCycle = I2C_DutyCycle_2;//2:1
I2C_Init(I2C1,&I2CInitStruct);
//4 .使能I2C
I2C_Cmd(I2C1,ENABLE);//闭合开关,PE

2.2:发送数据

在这里插入图片描述在这里插入图片描述

2.2.1:等待总线空闲(BUSY标志位)

在这里插入图片描述

//1 .等待总线空闲 
while(I2C_GetFlagStatus(I2C1,I2C_FLAG_BUSY) == SET);

2.2.2:发送起始位(START,SB标志位)

在这里插入图片描述

//2 .发送起始位
I2C_GenerateSTART(I2C1,ENABLE);
while(I2C_GetFlagStatus(I2C1,I2C_FLAG_SB) == RESET);//起始位发送完成

2.2.3:发送地址(AF,ADDR标志位)

在这里插入图片描述
发送地址前先清除AF标志位,

//3 .发送地址
  //3.1 清除标志位AF
I2C_ClearFlag(I2C1,I2C_FLAG_AF);
  //3.2 发送8为数据,7+1
I2C_SendData(I2C1,SlaveADDr & 0xfe);
  //3.3 等待发送成功
while(I2C_GetFlagStatus(I2C1,I2C_FLAG_ADDR) == RESET)
{
	if(I2C_GetFlagStatus(I2C1,I2C_FLAG_AF) == SET)//地址发送失败了
	{
		ret = ERROR;
		goto STOP;
	}
}
STOP:
	I2C_GenerateSTOP(I2C1,ENABLE);//发送停止位
	while(I2C_GetFlagStatus(I2C1,I2C_FLAG_BUSY) == SET);//等待总线空闲

2.2.4:发送数据

在这里插入图片描述
发送数据前先清除ADDR标志位,清除ADDR(先读SR1寄存器,再度SR2寄存器)。

//4 .发送数据
  //4.1 清除ADDR标志位
I2C_ReadRegister(I2C1,I2C_Register_SR1);//读取SR1
I2C_ReadRegister(I2C1,I2C_Register_SR2);//读取SR2

  //4.2 向TDR寄存器写入数据
uint32_t i;
for(i = 0; i < Size ;i++)//Size是数据数组的个数
{
	while(I2C_GetFlagStatus(I2C1,I2C_FLAG_TXE) == RESET)//等待TXE为1
	{
		if(I2C_GetFlagStatus(I2C1,I2C_FLAG_AF) == SET)
		{
			ret = ERROR;
			goto STOP;
		}
	}
	I2C_SendData(I2C1,pData[i]);//向TDR写入数据
}

while(I2C_GetFlagStatus(I2C1,I2C_FLAG_BTF) == RESET)//等待发送完成
{
	if(I2C_GetFlagStatus(I2C1,I2C_FLAG_AF) == SET)
	{
		ret = ERROR;
		goto STOP;
	}
}

最终代码①:

	//1 .等待总线空闲 
	while(I2C_GetFlagStatus(I2C1,I2C_FLAG_BUSY) == SET);
	
	//2 .发送起始位
	I2C_GenerateSTART(I2C1,ENABLE);
	while(I2C_GetFlagStatus(I2C1,I2C_FLAG_SB) == RESET);//起始位发送完成
	
	//3 .发送地址
	  //3.1 清除标志位AF
	I2C_ClearFlag(I2C1,I2C_FLAG_AF);
	  //3.2 发送8为数据,7+1
	I2C_SendData(I2C1,SlaveADDr & 0xfe);
	  //3.3 等待发送成功
	while(I2C_GetFlagStatus(I2C1,I2C_FLAG_ADDR) == RESET)
	{
		if(I2C_GetFlagStatus(I2C1,I2C_FLAG_AF) == SET)//地址发送失败了
		{
			ret = ERROR;
			goto STOP;
		}
	}

	//4 .发送数据
	  //4.1 清除ADDR标志位
	I2C_ReadRegister(I2C1,I2C_Register_SR1);//读取SR1
	I2C_ReadRegister(I2C1,I2C_Register_SR2);//读取SR2
	
	  //4.2 向TDR寄存器写入数据
	uint32_t i;
	for(i = 0; i < Size ;i++)
	{
		while(I2C_GetFlagStatus(I2C1,I2C_FLAG_TXE) == RESET)//等待TXE为1
		{
			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
STOP:
	I2C_GenerateSTOP(I2C1,ENABLE);
	while(I2C_GetFlagStatus(I2C1,I2C_FLAG_BUSY) == SET);

代码测试:通过向OLED模式发送指令将其屏幕点亮

#include "stm32f10x.h"

void App_I2C_Init(void);
void App_I2C_Send(uint8_t SlaveADDr,uint8_t* pData,uint16_t Size);

int main(void)
{
	//OLED指令
  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
	};
	App_I2C_Init();
	App_I2C_Send(0x78, oled_init_command, sizeof(oled_init_command)/sizeof(uint8_t));
	
	while(1)
	{
		
	}
}

void App_I2C_Init(void)
{
	//1 .开启GPIOB挂载的总线时钟
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);
	//初始化IO引脚
	GPIO_InitTypeDef GPIOInitStruct;
	GPIOInitStruct.GPIO_Mode = GPIO_Mode_AF_OD;//复用输出开漏模式
	GPIOInitStruct.GPIO_Pin = GPIO_Pin_6 |GPIO_Pin_7;
	GPIOInitStruct.GPIO_Speed = GPIO_Speed_2MHz;
	GPIO_Init(GPIOB,&GPIOInitStruct);
	
	//2 .开启I2C挂载APB1总线的时钟
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C1,ENABLE);//开启时钟
	RCC_APB1PeriphResetCmd(RCC_APB1Periph_I2C1,ENABLE);//对I2C1进行复位
	RCC_APB1PeriphResetCmd(RCC_APB1Periph_I2C1,DISABLE);//对I2C1进行释放
	
	//3 .配置I2C1的参数
	I2C_InitTypeDef I2CInitStruct;
	I2CInitStruct.I2C_ClockSpeed = 400000;//配置波特率,最高400K
	I2CInitStruct.I2C_Mode = I2C_Mode_I2C;//一般不使用SMBus
	I2CInitStruct.I2C_DutyCycle = I2C_DutyCycle_2;//2:1
	I2C_Init(I2C1,&I2CInitStruct);
	
	//4 .使能I2C
	I2C_Cmd(I2C1,ENABLE);
}

void App_I2C_Send(uint8_t SlaveADDr,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 .发送地址
	  //3.1 清除标志位AF
	I2C_ClearFlag(I2C1,I2C_FLAG_AF);
	  //3.2 发送8为数据,7+1
	I2C_SendData(I2C1,SlaveADDr & 0xfe);
	  //3.3 等待发送成功
	while(I2C_GetFlagStatus(I2C1,I2C_FLAG_ADDR) == RESET)
	{
		if(I2C_GetFlagStatus(I2C1,I2C_FLAG_AF) == SET)//地址发送失败了
		{
			ret = ERROR;
			goto STOP;
		}
	}

	//4 .发送数据
	  //4.1 清除ADDR标志位
	I2C_ReadRegister(I2C1,I2C_Register_SR1);//读取SR1
	I2C_ReadRegister(I2C1,I2C_Register_SR2);//读取SR2
	
	  //4.2 向TDR寄存器写入数据
	uint32_t i;
	for(i = 0; i < Size ;i++)
	{
		while(I2C_GetFlagStatus(I2C1,I2C_FLAG_TXE) == RESET)//等待TXE为1
		{
			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
STOP:
	I2C_GenerateSTOP(I2C1,ENABLE);
	while(I2C_GetFlagStatus(I2C1,I2C_FLAG_BUSY) == SET);
}

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

P2P 文件共享:现代网络中的高效文件传输

在互联网的世界中&#xff0c;不同应用程序的数据传输方法各异。P2P文件共享&#xff08;Peer-to-Peer File Sharing&#xff09; 作为一种高效的文件传输方式&#xff0c;使得用户可以在没有中央服务器的情况下直接进行文件交换。本文将详细介绍P2P文件共享的基本原理、优势及…

【通俗理解】CNN复杂度——卷积神经网络的计算成本解析

【通俗理解】CNN复杂度——卷积神经网络的计算成本解析 关键词提炼 #CNN复杂度 #卷积神经网络 #计算成本 #输入数据尺寸 #卷积核大小 #卷积核数量 #复杂度公式 第一节&#xff1a;CNN复杂度的类比与核心概念【尽可能通俗】 1.1 CNN复杂度的类比 CNN的复杂度就像是烹饪一道大…

引擎切换pdf识别简历分析

文章目录 1.EasyCode生成interview_history的crud1.在模板设置中手动指定逻辑删除的值2.生成代码&#xff0c;进行测试 2.PDF识别关键字1.引入依赖2.代码概览3.PDFUtil.java4.keyword1.EndType.java2.FlagIndex.java3.WordType.java4.KeyWordUtil.java 3.策略模式实现引擎切换&…

QT-window记事本

QT-window记事本 一、演示效果二、核心代码三、下载连接 一、演示效果 二、核心代码 #include <QMessageBox> #include <QFileDialog> #include <QDebug> #include <QProcess> #include <QDesktopServices> #include <QDateTime> #includ…

孙宇晨:区块链领域的坚韧领航者,以智慧铸就行业基石

​ 在区块链领域&#xff0c;孙宇晨以其卓越的智慧与不懈的韧性&#xff0c;成为行业内备受瞩目的领军人物。从创立波场 TRON 到引领去中心化的变革&#xff0c;孙宇晨始终以坚定的信念和独特的战略眼光推动着区块链技术的发展。 孙宇晨的成功不仅仅是因为他对技术的深入…

叉车驾驶员状态监控系统,司机身份安全识别,强化监管能力建设!

人脸识别技术作为人工智能领域的一个重要分支&#xff0c;已经广泛应用于安全识别、个人化推荐、社交网络等多个领域。其基于计算机视觉、图像处理、人脸检测、特征提取和人脸识别等先进技术&#xff0c;能够实现对人脸图像的精准分析和识别。在叉车驾驶场景中&#xff0c;AI人…

windows mfc webview2 接收html信息

webview2导入到mfc参考&#xff1a; windows vs2022 MFC使用webview2嵌入网页-CSDN博客 webview2与js交互参考&#xff1a;WebView2教程(基于C)【四】JS与C互访&#xff08;上&#xff09;_window.chrome.webview.postmessage-CSDN博客 一、JS端发送和接收 JS中&#xff0c;…

12代装win7影响性能吗?12代酷睿装win7关闭小核提高性能方法

12代酷睿装win7影响性能吗&#xff1f;12代酷睿装win7在性能上有点损耗&#xff0c;可以关闭小核提高性能。有些小朋友不知道怎么关闭上核来提高性能&#xff0c;下面电脑系统城小编就教大家12代酷睿装win7关闭核提高性能方法。 12代酷睿装win7说明&#xff1a;关闭小核解锁更多…

【分布式】分布式Session共享

这里通过SpringSession来实现Session的共享&#xff0c;Session数据存储在Redis中 SpringSession的操作指南&#xff1a; https://docs.spring.io/spring-session/docs/2.5.6/reference/html5/guides/boot-redis.html 导入相关的依赖 <dependency><groupId>org.s…

算法入门-递归3

第四部分&#xff1a;递归 143.重排链表&#xff08;中等&#xff09; 题目&#xff1a;给定一个单链表 L 的头节点 head &#xff0c;单链表 L 表示为&#xff1a; L0 → L1 → … → Ln - 1 → Ln 请将其重新排列后变为&#xff1a; L0 → Ln → L1 → Ln - 1 → L2 → …

问题-小技巧-win11状态栏卡住

目前我只知道治标不治本的办法&#xff0c;打开任务管理器&#xff0c;找到Windows资源管理器右键重新启动&#xff0c;就可以解决这个问题。 这个问题我觉得是Win11自己的问题&#xff0c;等有新的发现&#xff0c;会进行补充。

使用Hutool工具类轻松生成验证码

效果图&#xff1a; 引入依赖&#xff1a; <!--hutool工具包--> <dependency><groupId>cn.hutool</groupId><artifactId>hutool-all</artifactId><version>5.7.15</version> </dependency>核心代码 import cn.hutool.…

使用docker-compose运行kafka及验证(无需zookpeer)

前言&#xff1a;要求安装docker-compose kafka镜像版本&#xff1a;apache/kafka:3.8.0 可能存在镜像拉不下来的情况&#xff1a; 1、vim /etc/docker/daemon.json {"data-root":"/data/docker","registry-mirrors": ["https://docker.m…

高性价比百元蓝牙耳机如何选择?四款首选高性价比蓝牙耳机推荐

不知道什么时候开始&#xff0c;有线耳机悄悄的淡出了我们的视线。现在几乎都是蓝牙耳机首当前冲&#xff0c;因为比起有线耳机&#xff0c;蓝牙耳机更携带方便&#xff0c;拿出来就是秒连&#xff0c;体验感也不差。而且随着蓝牙耳机的价格不断下降&#xff0c;同时&#xff0…

【图文并茂】ant design pro 如何优雅奇妙地添加修改密码的功能

如上图所示&#xff0c;我们要加这样的一个功能&#xff0c;如何做呢&#xff1f; 首先要写好前端页面&#xff0c;再对接好后端&#xff0c;然后我们要判断当前密码是否正确&#xff0c;如果正确才能新密码修改好。 前端页面 src/pages/account/change-password/index.tsx …

【直观表格】常见神经网络计算复杂度对比 ——从时间复杂度和空间复杂度角度剖析

常见神经网络计算复杂度对比 ——从时间复杂度和空间复杂度角度剖析 【表格】常见神经网络计算复杂度对比 神经网络类型时间复杂度空间复杂度关键参数备注多层感知机&#xff08;MLP&#xff09; O ( n ⋅ d ⋅ h ) O(n \cdot d \cdot h) O(n⋅d⋅h) O ( d ⋅ h h ) O(d \c…

helm安装jenkins保姆级别

一、创建nfs服务器 这一步跳过、自行百度 注意&#xff1a;要给共享目录赋予权限chmod一下&#xff0c;不然到时候容器没办法在目录里面创建文件&#xff0c;初始化时候会报错误代码2 二、添加Jenkins的Helm仓库 helm repo add jenkinsci https://charts.jenkins.io helm re…

护眼台灯真的有用吗?学生护眼台灯十大牌子推荐

在当前近视患者剧增&#xff0c;我们发现近视还与青光眼的发生有关联&#xff0c;这是一种可能导致永久性视力丧失的眼病。青光眼通常是由于眼内压力过高造成的视神经损伤。高度近视患者的眼球结构变化可能增加眼压&#xff0c;从而提高患青光眼的风险。预防近视变得非常重要&a…

html js弹幕功能

效果如上 html <!DOCTYPE html> <html><head><meta charset"utf-8" /><title></title><script charset"utf-8" src"https://unpkg.com/vue2.6.14/dist/vue.min.js" type"text/javascript">…

[C语言]-基础知识点梳理-编译、链接、预处理

前言 各位师傅大家好&#xff0c;我是qmx_07,今天来给大家讲解以下程序运行会经历哪些事情 翻译环境和运⾏环境 在ANSIC的任何⼀种实现中&#xff0c;存在两个不同的环境 第1种是翻译环境&#xff0c;在这个环境中源代码被转换为可执⾏的机器指令&#xff08;⼆进制指令&a…