STM32开发(九)STM32F103 通信 —— I2C通信编程详解
文章目录
- 一、基础知识点
- 二、开发环境
- 三、STM32CubeMX相关配置
- 四、Vscode代码讲解
- GPIO模拟I2C代码
- SHT30相关代码
- main函数中循环代码
- 五、结果演示
- 方式一、示波器分析I2C数据
- 方式2、通过Modbus将获取到的数据传到PC上
一、基础知识点
本实验通过I2C通信获取SHT30温湿度值,显示数码管以及通过modbus协议传送给PC端。
本实验内容知识点:
1、 I2C通信协议
2、温湿度传感器 SHT3x-DIS 手册 解析 、TM1620芯片手册 解析
3、数码管显示 详解
4、RS485 Modbus通信编程详解
5、定时器中断 详解
6、I2C引脚SDA既要输出也要输入,因此要配置为准双向口。先来看下STM32普通GPIO口内部图,了解下准双向口。
将IO口配置为开漏输出时,输出控制器中P-MOS管断开,输出通过N-MOS管决定。
只有输出高电平的时候,N-MOS管截止,这时IO口就能作为输入使用,获取IO口的状态。(P-MOS和N-MOS都截止)
这样I2C SDA引脚就不用重新初始化为输入模式。这也就是准双向口的原理。
准备好了吗?开始实战show time。
二、开发环境
1、硬件开发准备
主控:STM32F103ZET6
温湿度传感器:SHT3X
2、软件开发准备
软件开发使用虚拟机 + VScode + STM32Cube 开发STM32,在虚拟机中直接完成编译下载。
该部分可参考:软件开发环境构建
三、STM32CubeMX相关配置
1、STM32CubeMX基本配置
本实验基于CubeMX详解构建基本框架 进行开发。
2、STM32CubeMX I2C相关配置
(1)GPIO配置
配置I2C两个引脚默认高电平:I2C处于空闲状态
配置I2C两个引脚为开漏输出。
四、Vscode代码讲解
GPIO模拟I2C代码
I2C时序由空闲时序、开始时序、停止时序、发送一个字节时序、接收一个字节时序,这四种时序部分组成。GPIO只要模拟出这四种时序就能满足所有的完整I2C时序。因此制作一个结构体来实现所有I2C时序部分接口。其他函数就能直接调用这几个接口构成完整的I2C时序通信。
typedef struct
{void (*Init)(void); //I2C初始化——I2C空闲时序void (*start)(void); //I2C开始时序void (*stop)(void); //I2C停止时序 ACK_Value_t (*Write_Byte)(uint8_t); //I2C写字节时序uint8_t (*Read_Byte) (ACK_Value_t); //I2C读字节时序
} Myi2c_t;Myi2c_t Myi2c =
{Init,start,stop,Write_Byte,Read_Byte
};
实现具体的I2C时序
初始化I2C信号 —— 空闲信号:SCL和SDA信号都为高电平
static void Init(void)
{I2C_SCL_SET; // SCL置高 I2C_SDA_SET; // SDA置高
}
开始信号:SCL为高电平时,SDA由高电平向低电平跳变,开始传送数据。
static void start(void)
{I2C_SCL_SET; // SCL置高I2C_SDA_SET; // SDA置高I2C_Delay_us(1);I2C_SDA_RESET; // SDA清零I2C_Delay_us(10);I2C_SCL_RESET; // SCL清零I2C_Delay_us(1);
}
结束信号:SCL为高电平时,SDA由低电平向高电平跳变,结束传送数据。
static void stop(void)
{I2C_SDA_RESET; // SDA清零I2C_SCL_SET; // SCL置高I2C_Delay_us(1);I2C_SDA_SET; // SDA置高I2C_Delay_us(10);
}
I2C写字节时序:1、主机发送1个字节;2、接收从机发来的ACK信号; 3、释放信号线
注:在SCL低变高的时候数据变化,在SCL高电平时保持数据变化
static ACK_Value_t Write_Byte(uint8_t data)
{uint8_t i;ACK_Value_t ACK_Rspond;// When SCL is in low power hours, SDA changes its state. // When SCL is in high power hours, SDA state must be stable.for(i=0;i<8;i++){I2C_SCL_RESET; // SCL清零,准备发送数据位I2C_Delay_us(1);if((data&BIT7) == BIT7) // 要发送的数据最高位,高位先发I2C_SDA_SET;elseI2C_SDA_RESET;I2C_Delay_us(1);I2C_SCL_SET; // SCL置高,发送数据I2C_Delay_us(10);data <<= 1; // 移位,准备下个数据位发送}I2C_SCL_RESET; // SCL清零,准备接收从机发来的ACK信号I2C_SDA_SET; // 将SDA释放信号I2C_Delay_us(1);I2C_SCL_SET; // 接收从机发来的信号I2C_Delay_us(10);ACK_Rspond = (ACK_Value_t)I2C_READ_SDA; // 将信号保存到ACK_RspondI2C_SCL_RESET; // 释放SCLI2C_Delay_us(1);return ACK_Rspond; // 返回ACK信号
}
I2C读字节时序:1、主机接收1个字节;2、接收完成,主机发ACK信号回复从机; 3、释放信号线
注:在SCL低变高的时候数据变化,在SCL高电平时保持数据变化,可采到接收值
static uint8_t Read_Byte(ACK_Value_t ack)
{uint8_t RD_Byte=0,i;for(i=0; i<8; i++) // 循环接收一个字节数据{RD_Byte <<= 1; // 准备接收下一位数据I2C_SCL_RESET; // SCL清零,从机SDA准备数据I2C_Delay_us(10);I2C_SCL_SET; // SCL置高,获取数据I2C_Delay_us(10);RD_Byte |= I2C_READ_SDA; // 将数据保存在RD_Byte缓存里}I2C_SCL_RESET; //SCL清零,主机准备应答信号I2C_Delay_us(1);if(ack == ACK){I2C_SDA_RESET; // 主机SDA清零,回应从机ACK,接收成功}else{I2C_SDA_SET; // 主机SDA置高,回应从机NACK,接收失败}I2C_SCL_SET; // SCL置高,发送ACKI2C_Delay_us(10);//释放SDA数据线//SCL先清零,再释放SDA,防止连续传输数据时,从机错将SDA释放信号当成NACk信号I2C_SCL_RESET; I2C_SDA_SET; I2C_Delay_us(1);return RD_Byte; // 返回接收到的字节
}static void I2C_Delay_us(uint8_t us)
{uint8_t i = 0;//通过示波器测量进行校准while(us--){for(i=0;i<7;i++);}
}
SHT30相关代码
构建一个SHT30结构体并初始化
typedef struct
{float fTemperature_Value; // 保存温度值uint8_t ucHumidity_Value; // 保存湿度值void (*Measure_Period_Mode)(void); // 测量函数
} SHT3X_t;extern SHT3X_t SHT3X;SHT3X_t SHT3X=
{0.0,0,Measure_Period_Mode
};
调用I2C接口实现SHT30时序,获取当前温湿度值
#define SHT3X_ADDR (uint8_t)(0x44 << 1) // SHT30地址,I2C地址位为7位,因此一定要向左移一位。第8位为读写位
#define Write_CMD 0xFE
#define Read_CMD 0x01static void Measure_Period_Mode(void)
{uint8_t SHT32X_RD_array[6] = {0};uint16_t temp_uint = 0;float temp_float = 0;// 1、设备芯片检测模式:启动周期性测量 High repeat , mps = 10Myi2c.start();Myi2c.Write_Byte(SHT3X_ADDR&Write_CMD);Myi2c.Write_Byte(0x27);Myi2c.Write_Byte(0x37);// 2、读当前温湿度值,寄存器0xE000Timer6.SHT30_Measure_Timeout =0; // 使用定时器6 定时测量时间do{if(Timer6.SHT30_Measure_Timeout >= TIMER6_2S) //2s内没获取到数据,退出等待break;Myi2c.start();Myi2c.Write_Byte(SHT3X_ADDR&Write_CMD);Myi2c.Write_Byte(0xE0);Myi2c.Write_Byte(0x00);Myi2c.start();} while (Myi2c.Write_Byte(SHT3X_ADDR | Read_CMD) == NACK);if(Timer6.SHT30_Measure_Timeout < TIMER6_2S){SHT32X_RD_array[0]=Myi2c.Read_Byte(ACK);SHT32X_RD_array[1]=Myi2c.Read_Byte(ACK);SHT32X_RD_array[2]=Myi2c.Read_Byte(ACK);SHT32X_RD_array[3]=Myi2c.Read_Byte(ACK);SHT32X_RD_array[4]=Myi2c.Read_Byte(ACK);SHT32X_RD_array[5]=Myi2c.Read_Byte(NACK);Myi2c.stop();// 3、CRC校验以及温度计算,见手册讲解if(CRC_8(SHT32X_RD_array,2) == SHT32X_RD_array[2]) //CRC-8 校验{temp_uint = SHT32X_RD_array[0]*256+SHT32X_RD_array[1];temp_float = ((float)temp_uint)*0.267032-4500;SHT3X.fTemperature_Value = temp_float*0.01;}// 3、CRC校验以及温度计算,见手册讲解if(CRC_8(&SHT32X_RD_array[3],2) == SHT32X_RD_array[5]) //CRC-8 校验{temp_uint = SHT32X_RD_array[3]*256+SHT32X_RD_array[4];temp_float = ((float)temp_uint)*0.152590;temp_float = temp_float*0.01;SHT3X.ucHumidity_Value = (unsigned char)temp_float; }}
}
main函数中循环代码
while (1)
{float Temp_float = 0;uint16_t Temp_uint = 0;//获取SHT30的温湿度SHT3X.Measure_Period_Mode();//数码管显示//温度if(SHT3X.fTemperature_Value < 0) //负温{Temp_float = 0 - SHT3X.fTemperature_Value;Display.Disp_Other(Disp_NUM_GRID3,0x40,Disp_DP_OFF); //4号数码管显示负号}else{Temp_float = SHT3X.fTemperature_Value;Display.Disp_Other(Disp_NUM_GRID3,0x00,Disp_DP_OFF); //4号数码管关闭}Temp_uint = (uint16_t)(Temp_float*10);Display.Disp(Disp_NUM_GRID4,Temp_uint/100,Disp_DP_OFF);Display.Disp(Disp_NUM_GRID5,Temp_uint%100/10,Disp_DP_ON);Display.Disp(Disp_NUM_GRID6,Temp_uint%10,Disp_DP_OFF);//湿度Display.Disp(Disp_NUM_GRID1,SHT3X.ucHumidity_Value/10,Disp_DP_OFF);Display.Disp(Disp_NUM_GRID2,SHT3X.ucHumidity_Value%10,Disp_DP_OFF);HAL_Delay(500);
}
五、结果演示
方式一、示波器分析I2C数据
实验结果:
温度:21.4℃ 湿度:47
方式2、通过Modbus将获取到的数据传到PC上
修改Modbus相关代码
diff --git a/MyApplication/Src/Modbus.c b/MyApplication/Src/Modbus.c
index 7665eee..b6caf10 100755
--- a/MyApplication/Src/Modbus.c
+++ b/MyApplication/Src/Modbus.c
@@ -106,24 +106,26 @@ static void Modbus_Read_Register(UART_t* UART)//功能码*(COM_UART->pucSend_Buffer+1) = FunctionCode_Read_Register;//数据长度(字节)
- *(COM_UART->pucSend_Buffer+2) = 2;
+ *(COM_UART->pucSend_Buffer+2) = 6;//发送数据// deep status*(COM_UART->pucSend_Buffer+3) = 0;*(COM_UART->pucSend_Buffer+4) = Deep.Read_Deep();
- *(COM_UART->pucSend_Buffer+5) = 0;
- *(COM_UART->pucSend_Buffer+6) = 0x66;
+ *(COM_UART->pucSend_Buffer+5) = ((uint16_t)(SHT3X.fTemperature_Value*10))/256;
+ *(COM_UART->pucSend_Buffer+6) = ((uint16_t)(SHT3X.fTemperature_Value*10))%256;
+ *(COM_UART->pucSend_Buffer+7) = 0;
+ *(COM_UART->pucSend_Buffer+8) = SHT3X.ucHumidity_Value;//插入CRC
- CRC_16.CRC_Value = CRC_16.CRC_Check(COM_UART->pucSend_Buffer,7); //计算CRC值
+ CRC_16.CRC_Value = CRC_16.CRC_Check(COM_UART->pucSend_Buffer,9); //计算CRC值CRC_16.CRC_H = (uint8_t)(CRC_16.CRC_Value >> 8);CRC_16.CRC_L = (uint8_t)CRC_16.CRC_Value;- *(COM_UART->pucSend_Buffer+7) = CRC_16.CRC_L;
- *(COM_UART->pucSend_Buffer+8) = CRC_16.CRC_H;
+ *(COM_UART->pucSend_Buffer+9) = CRC_16.CRC_L;^M
+ *(COM_UART->pucSend_Buffer+10) = CRC_16.CRC_H;^M//发送数据
- UART3.SendArray(COM_UART->pucSend_Buffer,9);
+ UART3.SendArray(COM_UART->pucSend_Buffer,11);}}
主设备为PC端安装的MThings进行Modbus收发数据。 MThings软件具体操作可参考Modbus通信详解中的结果演示以及报文解析
[2023-03-18 15:45:34-919]COM38-发送:01 03 9c 41 00 03 7b 8f
0x01:主机要查询的从设备地址
0x03:功能码 查询读操作
0x9c 0x41:寄存器地址0x9c41转十进制地址为40,001
0x00 0x02:读取三个数据(一个数据3字节)
0x7b 0x8f:CRC校验码
[2023-03-18 15:45:34-941]COM38-接收:01 03 06 00 00 00 d6 00 2e 91 40
0x01:告诉主机自己从设备地址
0x03:功能码 读操作
0x00 0x00:读出第一个数据为0x01,当前蜂鸣器关闭状态
0x00 0xd6:读取第二个数据为0xd6,温度值=214/10=21.4℃ (发送代码将值乘以10,这里需要除以10获取真实温度值)
0x00 0x2e:读取第二个数据为0x2e,湿度值=46
0x91 0x40:CRC校验码
相关文章:

STM32开发(九)STM32F103 通信 —— I2C通信编程详解
文章目录一、基础知识点二、开发环境三、STM32CubeMX相关配置四、Vscode代码讲解GPIO模拟I2C代码SHT30相关代码main函数中循环代码五、结果演示方式一、示波器分析I2C数据方式2、通过Modbus将获取到的数据传到PC上一、基础知识点 本实验通过I2C通信获取SHT30温湿度值ÿ…...

手撕数据结构—栈
Tips不得不再次提一下这个语法问题,当数组创建的时候,进行初始化的时候,分为全部初始化或者说部分初始化,对于不完全初始化而言,剩下的部分就全部默认为零。现在比如说你想对整型数组的1万个元素把它全部变成-1&#x…...
【java刷题】排序子序列
这里写目录标题问题描述解决思路实现代码问题描述 牛牛定义排序子序列为一个数组中一段连续的子序列,并且这段子序列是非递增或者非递减排序的。牛牛有一个长度为n的整数数组A,他现在有一个任务是把数组A分为若干段排序子序列,牛牛想知道他最少可以把这个数组分为几段排序子序…...

Springboot怎么快速集成Mybatis和thymeleaf?
前言有时候做方案,需要模拟一些业务上的一些场景来验证方案的可行性,基本上每次都是到处百度如何集成springbootmybatisthymeleaf这些东西的集成平时基本上一年也用不了一次,虽然比较简单,奈何我真得记不住详细的每一步࿰…...
shell常见面试题一
(1)、set //查看系统变量 (2)、chsh -s /bin/zsh test //修改用户登录shell (3)、2>&1 //标准错误重定向到标准输出 &> //同样可以将标准错误重定向到标准输出 如下: ls test.…...

python如何快速采集美~女视频?无反爬
人生苦短 我用python~ 这次康康能给大家整点好看的不~ 环境使用: Python 3.8 Pycharm mou歌浏览器 mou歌驱动 —> 驱动版本要和浏览器版本最相近 <大版本一样, 小版本最相近> 模块使用: requests >>> pip install requests selenium >>> pip …...

kali内置超好用的代理工具proxychains
作者:Eason_LYC 悲观者预言失败,十言九中。 乐观者创造奇迹,一次即可。 一个人的价值,在于他所拥有的。所以可以不学无术,但不能一无所有! 技术领域:WEB安全、网络攻防 关注WEB安全、网络攻防。…...

Java栈和队列·下
Java栈和队列下2. 队列(Queue)2.1 概念2.2 实现2.3 相似方法的区别2.4 循环队列3. 双端队列 (Deque)3.1 概念4.java中的栈和队列5. 栈和队列面试题大家好,我是晓星航。今天为大家带来的是 Java栈和队列下 的讲解!😀 继上一个讲完的栈后&…...

b01lers CTF web 复现
warmup 按照提示依次 base64 加密后访问,可以访问 ./flag.txt,也就是 Li9mbGFnLnR4dA 。 from base64 import b64decode import flaskapp flask.Flask(__name__)app.route(/<name>) def index2(name):name b64decode(name)if (validate(name))…...

三月份跳槽了,历经字节测开岗4轮面试,不出意外,被刷了...
大多数情况下,测试员的个人技能成长速度,远远大于公司规模或业务的成长速度。所以,跳槽成为了这个行业里最常见的一个词汇。 前几天,我看到有朋友留言说,他在面试字节的测试开发工程师的时候,灵魂拷问三小…...

springboot+vue驾校管理系统 idea科目一四预约考试,练车
加大了对从事道路运输经营活动驾驶员的培训管理力度,但在实际的管理过程中,仍然存在以下问题:(1)管理部门内部人员在实际管理过程中存在人情管理,不进行培训、考试直接进行发证。(2)从业驾驶员培训机构不能严格执行管理部门的大纲…...

【pytorch】使用deepsort算法进行目标跟踪,原理+pytorch实现
目录deepsort流程一、匈牙利算法二、卡尔曼滤波车速预测例子动态模型的概念卡尔曼滤波在deepsort中的动态模型三、预测值及测量值的含义deepsort在pytorch中的运行deepsort流程 DeepSORT是一种常用的目标跟踪算法,它结合了深度学习和传统的目标跟踪方法。DeepSORT的…...

Python 基础教程【3】:字符串、列表、元组
本文已收录于专栏🌻《Python 基础》文章目录🌕1、字符串🥝1.1 字符串基本操作🍊1.1.1 字符串创建🍊1.1.2 字符串元素读取🍊1.1.3 字符串分片🍊1.1.4 连接和重复🍊1.1.5 关系运算&…...

(数据结构)八大排序算法
目录一、常见排序算法二、实现1. 直接插入排序2.🌟希尔排序3. 选择排序4.🌟堆排序5. 冒泡排序7. 🌟快速排序7.1 其他版本的快排7.2 优化7.3 ⭐非递归7. 🌟归并排序7.1 ⭐非递归8. 计数排序三、总结1. 分析排序 (Sorting) 是计算机…...

构建GRE隧道打通不同云商的云主机内网
文章目录1. 环境介绍2 GRE隧道搭建2.1 华为云 GRE 隧道安装2.2 阿里云 GRE 隧道安装3. 设置安全组4. 验证GRE隧道4.1 在华为云上 ping 阿里云云主机内网IP4.2 在阿里云上 ping 华为云云主机内网IP5. 总结1. 环境介绍 华为云上有三台云主机,内网 CIDR 是 192.168.0.0…...

48天C++笔试强训 001
作者:小萌新 专栏:笔试强训 作者简介:大二学生 希望能和大家一起进步! 本篇博客简介:讲解48天笔试强训第一天的题目 笔试强训 day1选择题12345678910编程题12选择题 1 以下for循环的执行次数是(ÿ…...

Android 11新增系统服务
1.编写.aidl文件存放位置:frameworks/base/core/java/android/ospackage android.os;interface ISystemVoiceServer {void setHeightVoice(int flag);void setBassVoice(int flag);void setReverbVoice(int flag);}2.将.aidl文件添加到frameworks/base/Android.bp f…...
“你要多弄弄算法”
开始瞎掰 ▽ 2月的第一天,猎头Luna给我推荐了字节的机会,菜鸡我呀,还是有自知之明的,赶忙婉拒:能力有限,抱歉抱歉。 根据我为数不多的和猎头交流的经验,一般猎头都会稍微客套一下:…...

【数据结构】千字深入浅出讲解队列(附原码 | 超详解)
🚀write in front🚀 📝个人主页:认真写博客的夏目浅石. 🎁欢迎各位→点赞👍 收藏⭐️ 留言📝 📣系列专栏:C语言实现数据结构 💬总结:希望你看完…...
vue面试题(day04)
vue面试题vue插槽?vue3中如何获取refs,dom对象的方式?vue3中生命周期的和vue2中的区别?说说vue中的diff算法?说说 Vue 中 CSS scoped 的原理?vue3中怎么设置全局变量?Vue中给对象添加新属性时&a…...

ESP32读取DHT11温湿度数据
芯片:ESP32 环境:Arduino 一、安装DHT11传感器库 红框的库,别安装错了 二、代码 注意,DATA口要连接在D15上 #include "DHT.h" // 包含DHT库#define DHTPIN 15 // 定义DHT11数据引脚连接到ESP32的GPIO15 #define D…...

Python实现prophet 理论及参数优化
文章目录 Prophet理论及模型参数介绍Python代码完整实现prophet 添加外部数据进行模型优化 之前初步学习prophet的时候,写过一篇简单实现,后期随着对该模型的深入研究,本次记录涉及到prophet 的公式以及参数调优,从公式可以更直观…...

Cloudflare 从 Nginx 到 Pingora:性能、效率与安全的全面升级
在互联网的快速发展中,高性能、高效率和高安全性的网络服务成为了各大互联网基础设施提供商的核心追求。Cloudflare 作为全球领先的互联网安全和基础设施公司,近期做出了一个重大技术决策:弃用长期使用的 Nginx,转而采用其内部开发…...
Robots.txt 文件
什么是robots.txt? robots.txt 是一个位于网站根目录下的文本文件(如:https://example.com/robots.txt),它用于指导网络爬虫(如搜索引擎的蜘蛛程序)如何抓取该网站的内容。这个文件遵循 Robots…...
Axios请求超时重发机制
Axios 超时重新请求实现方案 在 Axios 中实现超时重新请求可以通过以下几种方式: 1. 使用拦截器实现自动重试 import axios from axios;// 创建axios实例 const instance axios.create();// 设置超时时间 instance.defaults.timeout 5000;// 最大重试次数 cons…...
【HTTP三个基础问题】
面试官您好!HTTP是超文本传输协议,是互联网上客户端和服务器之间传输超文本数据(比如文字、图片、音频、视频等)的核心协议,当前互联网应用最广泛的版本是HTTP1.1,它基于经典的C/S模型,也就是客…...

html-<abbr> 缩写或首字母缩略词
定义与作用 <abbr> 标签用于表示缩写或首字母缩略词,它可以帮助用户更好地理解缩写的含义,尤其是对于那些不熟悉该缩写的用户。 title 属性的内容提供了缩写的详细说明。当用户将鼠标悬停在缩写上时,会显示一个提示框。 示例&#x…...
JS设计模式(4):观察者模式
JS设计模式(4):观察者模式 一、引入 在开发中,我们经常会遇到这样的场景:一个对象的状态变化需要自动通知其他对象,比如: 电商平台中,商品库存变化时需要通知所有订阅该商品的用户;新闻网站中࿰…...

视觉slam十四讲实践部分记录——ch2、ch3
ch2 一、使用g++编译.cpp为可执行文件并运行(P30) g++ helloSLAM.cpp ./a.out运行 二、使用cmake编译 mkdir build cd build cmake .. makeCMakeCache.txt 文件仍然指向旧的目录。这表明在源代码目录中可能还存在旧的 CMakeCache.txt 文件,或者在构建过程中仍然引用了旧的路…...

springboot整合VUE之在线教育管理系统简介
可以学习到的技能 学会常用技术栈的使用 独立开发项目 学会前端的开发流程 学会后端的开发流程 学会数据库的设计 学会前后端接口调用方式 学会多模块之间的关联 学会数据的处理 适用人群 在校学生,小白用户,想学习知识的 有点基础,想要通过项…...