ARM_day8:温湿度数据采集应用

2024-04-18 03:28

本文主要是介绍ARM_day8:温湿度数据采集应用,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

1、IIC通信过程

主机发送起始信号、主机发送8位(7位从机地址+1位传送方向(0W,1R))、从机应答、发数据、应答、数据传输完,主机发送停止信号

2、起始信号和终止信号

SCL时钟线,SDA数据线

SCL高电平,SDA由高到低——起始信号

SCL高电平,SDA由低到高——终止信号

均由主机发出

3、应答信号和非应答信号

8位数据+1位应答位——一帧9位

8位数据传输完,第九个时钟周期,数据线(SDA)低电平,接收方还想接收数据,回复应答信号

                                                       数据线(SDA)高电平,接收方不想接收数据,回复非应信号

4、数据传输时机

时钟信号(SCL)高电平,数据线(SDA)稳定时,读取

时钟信号(SCL)低电平,数据线(SDA)高电平或低电平,写入

5、IIC从机选择及读写选择

传输信号包括地址信号、数据信号

起始信号后必须跟一个8位数据(7位从机地址+1位传送方向位(R/W))(0——W、1——R)

6、IIC读写时序

主机向从机发送:

主机发送起始信号、主机发送8位(7位从机地址+1位写标志)、从机应答、主机发送8位从机寄存器地址、从机应答、主机发送8位数据、从机应答、主机发起终止信号

主机读取从机数据:

主机发送起始信号、主机发送8位(7位从机地址+1位写标志)、从机应答、主机发起重复起始信号、主机发送8位(7位从机地址+1位读标志)、从机应答、从机发送8位数据、主机非应答信号、主机发起终止信号

实验:I2C读取温湿度传感器数据

1、SCL时钟线——PF14,SDA数据线——PF15        引脚连接

        I2C总线引脚初始化:(PF14、PF15共同初始化)使能GPIOF外设时钟、设输出功能、推挽输出、输出速度、上拉下拉、空闲状态下的SCL和SDA状态(拉高)——起始信号要求SCL高,SDA从高到低

2、模拟I2C开始信号时序

        SDA数据线保持输出状态  PF15输出;(PF15管脚设为输出)

        空闲状态SCL、SDA拉高(起始信号要求SCL高,SDA从高到低);(选用ODR(设置)、BSRR(复位)、BRR(清空)使输出高低电平)

        延迟一段时间,保持稳定后,拉低SDA数据线完成起始信号;

        延迟一段时间,拉低SCL时钟线,才能进行写入数据;

3、主机向从机写数据(高位到低位)

        SDA数据线保持输出状态  PF15输出;(PF15管脚设为输出)

        时钟线拉低,才能写数据;

        循环发送数据8位(0-7),延迟时间,保持时钟线稳定,开始发送数据;(要发送的数据 dat & 0x80(10000000),判断其真假,真则拉高数据线为高电平1,否则拉低数据线为低电平0)

        每次发送一位数据,延时后拉高时钟线(SCL),接收器才能读数据;

        延时等待接收器接收数据,再延时后将数据左移一位再&0x80进行循环

程序代码:

main.c:

#include "si7006.h"
extern void printf(const char* fmt, ...);
int main()
{//si7006初始化si7006_init();unsigned short hum;short tem;while(1){//读取温湿度hum=si70006_read_hum();tem=si70006_read_tem();//计算温湿度数据hum=hum*125/65536-6;tem=tem*175.72/65536-46.85;printf("hum:%d\n",hum);printf("tem:%d\n",tem);delay_ms(1000);//湿度大于65开启马达if(hum>65){GPIOF->ODR |= (0x1<<6);}else if(hum<=60)  //湿度小于60关闭马达{GPIOF->ODR &= (~(0x1<<6)); }//温度大于25开启风扇if(tem>=25){GPIOE->ODR |= (0x1<<9);}else if(tem<25)  //湿度小于25关闭风扇{GPIOE->ODR &= (~(0x1<<9)); }}return 0;
}

si7006.h:

#ifndef __SI7006_H__
#define __SI7006_H__
#include "iic.h"
void delay_ms(int ms);
void si7006_init();
unsigned short si70006_read_hum();
short si70006_read_tem();
#endif

si7006.c:

#include "si7006.h"
extern void printf(const char* fmt, ...);
void delay_ms(int ms)
{int i,j;for(i=0;i<ms;i++){for(j=0;j<2000;j++){}}
}
void si7006_init()
{//发起起始信号i2c_init();//I2C总线引脚初始化i2c_start();//模拟i2c开始信号的时序//发送7bit从机地址和写标志位 0x80i2c_write_byte(0x40<<1|0);//主机向从机写8bit数据//等待从机应答i2c_wait_ack();//等待接收器应答//发送寄存器地址 0XE6i2c_write_byte(0xE6);//等待从机应答i2c_wait_ack();//等待接收器应答//向从机发送数据 0x3Ai2c_write_byte(0x3A);//等待从机应答i2c_wait_ack();//发送终止信号i2c_stop();
}
unsigned short si70006_read_hum()//湿度读取
{unsigned char hum_l,hum_h;unsigned short hum;//主机发送起始信号i2c_init();i2c_start();//主机发送7bit从机地址+1bit写标志i2c_write_byte(0x40<<1|0);//等待从机应答i2c_wait_ack();//主机发送8bit寄存器地址i2c_write_byte(0xE5);//等待从机应答i2c_wait_ack();//主机发起重复起始信号i2c_start();//主机发送7bit从机地址+1bit 读 0x81i2c_write_byte(0x40<<1|1);//等待从机应答i2c_wait_ack();//延时等待从机测量数据delay_ms(100);//读取湿度的高8bit数据 hum_h//发送应答信号hum_h=i2c_read_byte(0);//读取湿度的低8位数据 hum_l//发送非应答信号hum_l=i2c_read_byte(1);//发送终止信号//将读取到的数据的低8位和高8位合成一个完整数据hum=hum_h<<8 | hum_l;return hum;
}
short si70006_read_tem()//温度读取
{unsigned char tem_l,tem_h;unsigned short tem;//主机发送起始信号i2c_init();i2c_start();//主机发送7bit从机地址+1bit写标志i2c_write_byte(0x40<<1|0);//等待从机应答i2c_wait_ack();//主机发送8bit寄存器地址i2c_write_byte(0xE3);//等待从机应答i2c_wait_ack();//主机发起重复起始信号i2c_start();//主机发送7bit从机地址+1bit 读 0x81i2c_write_byte(0x40<<1|1);//等待从机应答i2c_wait_ack();//延时等待从机测量数据delay_ms(100);//读取温度的高8bit数据 tem_h//发送应答信号tem_h=i2c_read_byte(0);//读取温度的低8位数据 tem_l//发送非应答信号tem_l=i2c_read_byte(1);//发送终止信号//将读取到的数据的低8位和高8位合成一个完整数据tem=tem_h<<8 | tem_l;return tem;
}

iic.h:

#ifndef __IIC_H__
#define __IIC_H__
#include "stm32mp1xx_gpio.h"
#include "stm32mp1xx_rcc.h"/* 通过程序模拟实现I2C总线的时序和协议* GPIOF ---> AHB4* I2C1_SCL ---> PF14* I2C1_SDA ---> PF15** */#define SET_SDA_OUT     do{GPIOF->MODER &= (~(0x3 << 30)); \GPIOF->MODER |= (0x1 << 30);}while(0)#define SET_SDA_IN      do{GPIOF->MODER &= (~(0x3 << 30));}while(0)#define I2C_SCL_H       do{GPIOF->BSRR |= (0x1 << 14);}while(0)
#define I2C_SCL_L       do{GPIOF->BRR |= (0x1 << 14);}while(0)#define I2C_SDA_H       do{GPIOF->BSRR |= (0x1 << 15);}while(0)
#define I2C_SDA_L       do{GPIOF->BRR |= (0x1 << 15);}while(0)#define I2C_SDA_READ    (GPIOF->IDR & (0x1 << 15))void delay_us(void);//微秒延时
void delay(int ms);
void i2c_init(void);//初始化
void i2c_start(void);//起始信号
void i2c_stop(void);//终止信号
void i2c_write_byte(unsigned char  dat);//写一个字节数据
unsigned char i2c_read_byte(unsigned char ack);//读取一个字节数据
unsigned char i2c_wait_ack(void);       //等待应答信号
void i2c_ack(void);//发送应答信号
void i2c_nack(void);//发送非应答信号#endif 

iic.c:

#include "iic.h"extern void printf(const char* fmt, ...);
/** 函数名 : delay_us* 函数功能:延时函数* 函数参数:无* 函数返回值:无* */
void delay_us(void)  //微秒级延时
{unsigned int i = 2000;while(i--);
}
/** 函数名 : i2c_init* 函数功能: i2C总线引脚的初始化, 通用输出,推挽输出,输出速度,* 函数参数:无* 函数返回值:无* */
void i2c_init(void)
{// 使能GPIOF端口的时钟RCC->MP_AHB4ENSETR |= (0x1 << 5);//使能风扇的时钟RCC->MP_APB2ENSETR |= 0x1;//使能马达的时钟RCC->MP_APB2ENSETR |= (0x1<<3);// 设置PF14,PF15引脚为通用的输出功能GPIOF->MODER &= (~(0xF << 28));GPIOF->MODER |= (0x5 << 28);//设置PE9为输出GPIOE->MODER &= (~(0x3<<18));GPIOE->MODER |= (0x1<<18);//设置PF6为输出GPIOF->MODER &= (~(0x3<<12));GPIOF->MODER |= (0x1<<12);// 设置PF14, PF15引脚为推挽输出GPIOF->OTYPER &= (~(0x3 << 14));//设置PE9为推挽输出GPIOE->OTYPER &= (~(0x1<<9));//设置PF6为推挽输出GPIOF->OTYPER &= (~(0x1<<6));// 设置PF14, PF15引脚为高速输出GPIOF->OSPEEDR |= (0xF << 28);//设置PE9为低速输出GPIOE->OSPEEDR &= (~(0x3<<18));//设置PF6为低速输出GPIOF->OSPEEDR &= (~(0x3<<12));// 设置PF14, PF15引脚的禁止上拉和下拉GPIOF->PUPDR &= (~(0xF << 28));//设置PE9没有上拉下拉电阻GPIOE->PUPDR &= (~(0x3<<18));//设置PF6没有上拉下拉电阻GPIOF->PUPDR &= (~(0x3<<12));// 空闲状态SDA和SCL拉高 I2C_SCL_H;I2C_SDA_H;
}/** 函数名:i2c_start* 函数功能:模拟i2c开始信号的时序* 函数参数:无* 函数返回值:无* */
void i2c_start(void)
{/** 开始信号:时钟在高电平期间,数据线从高到低的变化*     --------* SCL         \*              --------*     ----* SDA     \*          --------* */   //确保SDA是输出状态 PF15输出SET_SDA_OUT;// 空闲状态SDA和SCL拉高 I2C_SCL_H;I2C_SDA_H;delay_us();//延时等待一段时间I2C_SDA_L;//数据线拉低delay_us();//延时等待一段时间I2C_SCL_L;//时钟线拉低,让总线处于占用状态
}/** 函数名:i2c_stop* 函数功能:模拟i2c停止信号的时序* 函数参数:无* 函数返回值:无* */void i2c_stop(void)
{/** 停止信号 : 时钟在高电平期间,数据线从低到高的变化 *             ----------* SCL        /*    --------*    ---         -------* SDA   X       /*    --- -------* *///确保SDA是输出状态 PF15输出SET_SDA_OUT;//时钟线拉低I2C_SCL_L;//为了修改数据线的电平delay_us();//延时等待一段时间I2C_SDA_L;//数据线拉低delay_us();//延时等待一段时间//时钟线拉高I2C_SCL_H;delay_us();//延时等待一段时间I2C_SDA_H;//数据线拉高}/** 函数名: i2c_write_byte* 函数功能:主机向i2c总线上的从设备写8bits数据* 函数参数:dat : 等待发送的字节数据* 函数返回值: 无* */void i2c_write_byte(unsigned char dat)
{  /** 数据信号:时钟在低电平期间,发送器向数据线上写入数据*          时钟在高电平期间,接收器从数据线上读取数据 *      ----          --------*  SCL     \        /        \*           --------          --------*      -------- ------------------ ---*  SDA         X                  X*      -------- ------------------ ---**      先发送高位在发送低位 * *///确保SDA是输出状态 PF15输出SET_SDA_OUT;unsigned int i;for(i=0;i<8;i++){//时钟线拉低I2C_SCL_L;delay_us();//延时//0X3A->0011 1010   0X80->10000000if(dat&0X80)//最高位为1{//发送1I2C_SDA_H;}else  //最高位为0{I2C_SDA_L;//发送0}delay_us();//延时//时钟线拉高,接收器接收I2C_SCL_H;delay_us();//延时,用于等待接收器接收数据delay_us();//延时//将数据左移一位,让原来第6位变为第7位dat = dat<<1;}}/** 函数名:i2c_read_byte* 函数功能: 主机从i2c总线上的从设备读8bits数据, *          主机发送一个应答或者非应答信号* 函数参数: 0 : 应答信号   1 : 非应答信号* 函数返回值:读到的有效数据** */
unsigned char i2c_read_byte(unsigned char ack)
{/** 数据信号:时钟在低电平期间,发送器向数据线上写入数据*          时钟在高电平期间,接收器从数据线上读取数据 *      ----          --------*  SCL     \        /        \*           --------          --------*      -------- ------------------ ---*  SDA         X                  X*      -------- ------------------ ---**      先接收高位, 在接收低位 * */unsigned int i;unsigned char dat;//保存接受的数据//将数据线设置为输入SET_SDA_IN;for(i=0;i<8;i++){//先把时钟线拉低,等一段时间,保证发送器发送完毕数据I2C_SCL_L;delay_us();delay_us();//保证发送器发送完数据//时钟线拉高,读取数据I2C_SCL_H;delay_us();dat=dat<<1;//数值左移 一定要先左移在赋值,不然数据会溢出if(I2C_SDA_READ)//pf15管脚得到了一个高电平输入{dat |=1; //0000 0110}else{dat &=(~0X1);}delay_us();}if(ack){i2c_nack();//发送非应答信号,不再接收下一次数据}else{i2c_ack();//发送应答信号 }return dat;//将读取到的数据返回
}
/** 函数名: i2c_wait_ack* 函数功能: 主机作为发送器时,等待接收器返回的应答信号* 函数参数:无* 函数返回值:*                  0:接收到的应答信号*                  1:接收到的非应答信号* */
unsigned char i2c_wait_ack(void)
{/** 主机发送一个字节之后,从机给主机返回一个应答信号**                   -----------* SCL              /   M:读    \*     -------------             --------*     --- ---- --------------------* SDA    X    X*     ---      --------------------*     主  释   从机    主机*     机  放   向数据  读数据线*         总   线写    上的数据*         线   数据* */   //时钟线拉低,接收器可以发送信号I2C_SCL_L;I2C_SDA_H;//先把数据线拉高,当接收器回应应答信号时,数据线会拉低delay_us();SET_SDA_IN;//设置数据线为输入delay_us();//等待从机响应delay_us();I2C_SCL_H;//用于读取数据线数据if(I2C_SDA_READ)//PF15得到一个高电平输入,收到非应答信号return 1;I2C_SCL_L;//时钟线拉低,让数据线处于占用状态return 0;} 
/** 函数名: iic_ack* 函数功能: 主机作为接收器时,给发送器发送应答信号* 函数参数:无* 函数返回值:无* */
void i2c_ack(void)
{/*            --------* SCL       /        \*    -------          ------*    ---* SDA   X *    --- -------------* *///保证数据线是输出SET_SDA_OUT;I2C_SCL_L;//拉低时钟线delay_us();I2C_SDA_L;//数据线拉低,表示应答信号delay_us();I2C_SCL_H;//时钟线拉高,等待发送器读取应答信号delay_us();//让从机读取我们当前的回应delay_us();I2C_SCL_L;//数据线处于占用状态,发送器发送下一次数据}
/** 函数名: iic_nack* 函数功能: 主机作为接收器时,给发送器发送非应答信号* 函数参数:无* 函数返回值:无* */
void i2c_nack(void)
{/*            --------* SCL       /        \*    -------          ------*    --- ---------------* SDA   X *    --- * */   //保证数据线是输出SET_SDA_OUT;I2C_SCL_L;//拉低时钟线delay_us();I2C_SDA_H;//数据线拉高,表示非应答信号delay_us();I2C_SCL_H;//时钟线拉高,等待发送器读取应答信号delay_us();delay_us();I2C_SCL_L;//数据线处于占用状态,发送器发送下一次数据
}

运行结果:

 

这篇关于ARM_day8:温湿度数据采集应用的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/913647

相关文章

大模型研发全揭秘:客服工单数据标注的完整攻略

在人工智能(AI)领域,数据标注是模型训练过程中至关重要的一步。无论你是新手还是有经验的从业者,掌握数据标注的技术细节和常见问题的解决方案都能为你的AI项目增添不少价值。在电信运营商的客服系统中,工单数据是客户问题和解决方案的重要记录。通过对这些工单数据进行有效标注,不仅能够帮助提升客服自动化系统的智能化水平,还能优化客户服务流程,提高客户满意度。本文将详细介绍如何在电信运营商客服工单的背景下进行

基于MySQL Binlog的Elasticsearch数据同步实践

一、为什么要做 随着马蜂窝的逐渐发展,我们的业务数据越来越多,单纯使用 MySQL 已经不能满足我们的数据查询需求,例如对于商品、订单等数据的多维度检索。 使用 Elasticsearch 存储业务数据可以很好的解决我们业务中的搜索需求。而数据进行异构存储后,随之而来的就是数据同步的问题。 二、现有方法及问题 对于数据同步,我们目前的解决方案是建立数据中间表。把需要检索的业务数据,统一放到一张M

关于数据埋点,你需要了解这些基本知识

产品汪每天都在和数据打交道,你知道数据来自哪里吗? 移动app端内的用户行为数据大多来自埋点,了解一些埋点知识,能和数据分析师、技术侃大山,参与到前期的数据采集,更重要是让最终的埋点数据能为我所用,否则可怜巴巴等上几个月是常有的事。   埋点类型 根据埋点方式,可以区分为: 手动埋点半自动埋点全自动埋点 秉承“任何事物都有两面性”的道理:自动程度高的,能解决通用统计,便于统一化管理,但个性化定

中文分词jieba库的使用与实景应用(一)

知识星球:https://articles.zsxq.com/id_fxvgc803qmr2.html 目录 一.定义: 精确模式(默认模式): 全模式: 搜索引擎模式: paddle 模式(基于深度学习的分词模式): 二 自定义词典 三.文本解析   调整词出现的频率 四. 关键词提取 A. 基于TF-IDF算法的关键词提取 B. 基于TextRank算法的关键词提取

水位雨量在线监测系统概述及应用介绍

在当今社会,随着科技的飞速发展,各种智能监测系统已成为保障公共安全、促进资源管理和环境保护的重要工具。其中,水位雨量在线监测系统作为自然灾害预警、水资源管理及水利工程运行的关键技术,其重要性不言而喻。 一、水位雨量在线监测系统的基本原理 水位雨量在线监测系统主要由数据采集单元、数据传输网络、数据处理中心及用户终端四大部分构成,形成了一个完整的闭环系统。 数据采集单元:这是系统的“眼睛”,

使用SecondaryNameNode恢复NameNode的数据

1)需求: NameNode进程挂了并且存储的数据也丢失了,如何恢复NameNode 此种方式恢复的数据可能存在小部分数据的丢失。 2)故障模拟 (1)kill -9 NameNode进程 [lytfly@hadoop102 current]$ kill -9 19886 (2)删除NameNode存储的数据(/opt/module/hadoop-3.1.4/data/tmp/dfs/na

异构存储(冷热数据分离)

异构存储主要解决不同的数据,存储在不同类型的硬盘中,达到最佳性能的问题。 异构存储Shell操作 (1)查看当前有哪些存储策略可以用 [lytfly@hadoop102 hadoop-3.1.4]$ hdfs storagepolicies -listPolicies (2)为指定路径(数据存储目录)设置指定的存储策略 hdfs storagepolicies -setStoragePo

Hadoop集群数据均衡之磁盘间数据均衡

生产环境,由于硬盘空间不足,往往需要增加一块硬盘。刚加载的硬盘没有数据时,可以执行磁盘数据均衡命令。(Hadoop3.x新特性) plan后面带的节点的名字必须是已经存在的,并且是需要均衡的节点。 如果节点不存在,会报如下错误: 如果节点只有一个硬盘的话,不会创建均衡计划: (1)生成均衡计划 hdfs diskbalancer -plan hadoop102 (2)执行均衡计划 hd

csu 1446 Problem J Modified LCS (扩展欧几里得算法的简单应用)

这是一道扩展欧几里得算法的简单应用题,这题是在湖南多校训练赛中队友ac的一道题,在比赛之后请教了队友,然后自己把它a掉 这也是自己独自做扩展欧几里得算法的题目 题意:把题意转变下就变成了:求d1*x - d2*y = f2 - f1的解,很明显用exgcd来解 下面介绍一下exgcd的一些知识点:求ax + by = c的解 一、首先求ax + by = gcd(a,b)的解 这个

hdu1394(线段树点更新的应用)

题意:求一个序列经过一定的操作得到的序列的最小逆序数 这题会用到逆序数的一个性质,在0到n-1这些数字组成的乱序排列,将第一个数字A移到最后一位,得到的逆序数为res-a+(n-a-1) 知道上面的知识点后,可以用暴力来解 代码如下: #include<iostream>#include<algorithm>#include<cstring>#include<stack>#in