请人帮忙做淘宝网站多少钱,网站名称及网址,兰州网站怎么建设,wordpress建站实例视频一、IIC总线的基本概念#xff1a; iic总线是一种带应答的同步的、串行、半双工的通信方式#xff0c;支持一个主机对应多个从机。它有一根SCL#xff08;时钟线#xff09;和一根SDA#xff08;数据线#xff09;组成#xff0c;由于只有一根数据线#xff0c;所以它是…一、IIC总线的基本概念 iic总线是一种带应答的同步的、串行、半双工的通信方式支持一个主机对应多个从机。它有一根SCL时钟线和一根SDA数据线组成由于只有一根数据线所以它是半双工的通信方式。
首先需要了解 主机和从机之间是如何通过 iic 总线进行数据的读写的。
二、IIC总线的通信流程 当主机向从机发送数据时
1.主机需要发送一位起始位时钟线为高电平数据线产生下降沿标志着要开始发送数据
2.然后主机发送 7位 的从机地址和 1位 的写标志0
3.此时从机需要回应一个应答信号表示收到了主机发送数据的请求
4.主机发送 8位 从机的寄存器地址告诉从机发送的数据保存在哪里
5.从机再次回应一个应答信号
6.主机发送 8位 的数据
7.从机回应一个应答信号
8.此时发送完一个字节的数据如果需要继续发送数据重复6、7步
9.当不需要发送数据时主机发起终止信号时钟线为高电平数据线产生上升沿表示停止发送数据 当主机向从机读取数据时
1.主机需要发送一位起始位标志着要开始发送数据
2.然后主机发送 7位 的从机地址和 1位 的写标志0
3.此时从机需要回应一个应答信号表示收到了主机发送数据的请求
4.主机发送 8位 从机的寄存器地址告诉从机发送的数据保存在哪里
5.从机再次回应一个应答信号
6.主机发起一个重复的起始信号
7.然后主机发送 7位 的从机地址和 1位 的读标志1
8.从机回应一个应答信号
9.从机可以发送 8位 数据
10.此时主机可以回应一个应答信号或者非应答信号表示读取多少数据
11.当主机不再读取数据时上一步回应非应答信号后再次发起终止信号表示停止读取数据 三、GPIO模拟IIC实现温湿度传感器数据的读取
1.首先需要查看原理图找到对应的引脚进行相关初始化的设置。
1查询RCC寄存器使能GPIOF外设时钟
2查询GPIO数据手册根据需要将相关引脚进行初始化设置
2.了解SI7006温湿度传感器的内部结构以及工作原理
查询SI7006数据手册需要了解到以下几个方面的内容
1如何初始化芯片 通过数据手册了解到对应的寄存器地址向寄存器地址中写入初始化的数值。
2芯片内部的工作原理 SI7006内部有两个传感器分别是温度传感器和湿度传感器通过模数转换器可以将二者采集到的模拟信号转换为数字信号再由相关的寄存器存储通过控制单元交给IIC接口再通过IIC总线与外界进行交互
3查询作为IIC从机时的从机地址 SI7006数据手册中已经写明但要注意它是原本的从机地址还是加上了R/W标志位后的数值
4采集温度和湿度的芯片寄存器 根据数据手册查询到相关的寄存器命令码主IIC设备可以使用命令结构与Si7006通信
5查询传感器采集的数据如何进行计算 根据给定的公式可以将采集到的温湿度转换成通用的温湿度打印出来 相关代码如下
main.c:
#include si7006.hint main()
{//si7006初始化si7006_init();//风扇/马达的初始化led_init();unsigned short hum;short tem;while(1){//读取温度和湿度humsi7006_read_hum();temsi7006_read_tem();//计算温湿度数据humhum*125/65536-6;temtem*175.72/65536-46.85;printf(hum:%d\n,hum);printf(tem:%d\n,tem);delay_ms(1000);if(tem26){CH1_CTRL(1);//开启风扇}else if(tem26){CH1_CTRL(0);//关闭风扇}if(hum 65){//开启震动马达//Motor_CTRL(1);GPIOF-ODR | (0x16);}else if(hum 65){//关闭震动马达//Motor_CTRL(0);GPIOF-ODR (~(0x16));}}return 0;
}si7006.c:
#include si7006.h//手动封装延时函数
void delay_ms(int ms)
{int i,j;for(i0;ims;i){for(j0;j2000;j){}}
}void si7006_init()
{i2c_init();//1.发起起始信号i2c_start();//2.发送7bit从机地址和写标志位 0X80i2c_write_byte(0x80);//3.等待从机应答i2c_wait_ack();//4.发送寄存器地址 0XE6i2c_write_byte(0xe6);//5.等待从机应答i2c_wait_ack();//6.向从机发送数据 0X3Ai2c_write_byte(0x3a);//7.等待从机应答i2c_wait_ack();//8.发送终止信号i2c_stop();
}unsigned short si7006_read_hum()
{unsigned char hum_l,hum_h;unsigned short hum;// 1.主机发起起始信号i2c_start();// 2.主机发送7bit从机地址1bit写标志i2c_write_byte(0x80);// 3.等待从机应答i2c_wait_ack();// 4.主机发送8bit寄存器地址i2c_write_byte(0xe5);// 5.等待从机应答i2c_wait_ack();// 6.主机发起重复起始信号i2c_start();// 7.主机发送7bit从机地址1bit 读 0X81i2c_write_byte(0x81);// 8.等待从机应答i2c_wait_ack();// 9.延时等待从机测量数据delay_ms(1000);// 10.读取湿度的高8bit数据 hum_h// 11.发送应答信号hum_h i2c_read_byte(0);// 12.读取湿度的低8位数据 hum_l // 13.发送非应答信号hum_l i2c_read_byte(1);// 14.发起终止信号i2c_stop();// 15.将读取到的数据的低8位和高8bit合成一个完整的数据humhum_h8|hum_l;return hum;}short si7006_read_tem()
{unsigned char tem_l,tem_h;unsigned short tem;// 1.主机发起起始信号i2c_start();// 2.主机发送7bit从机地址1bit写标志i2c_write_byte(0x80);// 3.等待从机应答i2c_wait_ack();// 4.主机发送8bit寄存器地址i2c_write_byte(0xe3);// 5.等待从机应答i2c_wait_ack();// 6.主机发起重复起始信号i2c_start();// 7.主机发送7bit从机地址1bit 读 0X81i2c_write_byte(0x81);// 8.等待从机应答i2c_wait_ack();// 9.延时等待从机测量数据delay_ms(1000);// 10.读取温度的高8bit数据 tem_h// 11.发送应答信号tem_h i2c_read_byte(0);// 12.读取温度的低8位数据 tem_l// 13.发送非应答信号tem_l i2c_read_byte(1);// 14.发起终止信号i2c_stop();// 15.将读取到的数据的低8位和高8bit合成一个完整的数据temtem_h8|tem_l;return tem;}
si7006.h:
#ifndef __SI7006_H__
#define __SI7006_H__
#includeiic.h
#include uart4.hvoid delay_ms(int ms);
void si7006_init();
unsigned short si7006_read_hum();
short si7006_read_tem();#endifi2c.c:
#include iic.hextern 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);// 设置PF14PF15引脚为通用的输出功能GPIOF-MODER (~(0xF 28));GPIOF-MODER | (0x5 28);// 设置PF14, PF15引脚为推挽输出GPIOF-OTYPER (~(0x3 14));// 设置PF14, PF15引脚为高速输出GPIOF-OSPEEDR | (0xF 28);// 设置PF14, PF15引脚的禁止上拉和下拉GPIOF-PUPDR (~(0xF 28));// 空闲状态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(i0;i8;i){//时钟线拉低I2C_SCL_L;delay_us();//延时//0X3A-0011 1010 0X80-10000000if(dat0X80)//最高位为1{//发送1I2C_SDA_H;}else //最高位为0{I2C_SDA_L;//发送0}delay_us();//延时//时钟线拉高接收器接收I2C_SCL_H;delay_us();//延时,用于等待接收器接收数据delay_us();//延时//将数据左移一位让原来第6位变为第7位dat dat1;}}/** 函数名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(i0;i8;i){//先把时钟线拉低等一段时间保证发送器发送完毕数据I2C_SCL_L;delay_us();delay_us();//保证发送器发送完数据//时钟线拉高读取数据I2C_SCL_H;delay_us();datdat1;//数值左移 一定要先左移在赋值不然数据会溢出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;//数据线处于占用状态发送器发送下一次数据
}
i2c.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