cms仿站,茂名企业自助建站,郑州鹏之信网络科技有限公司,视频网站费用1.概念 I2C总线是PHLIPS公司在八十年代初推出的一种串行的半双工同步总线#xff0c;主要用于连接整体电路2.IIC总线硬件连接 1.IIC总线支持多主机多从机#xff0c;但是在实际开发过程中#xff0c;大多数采用单主机多从机模式
2.挂接到IIC总线上#xff0c;每个从机设备都…1.概念 I2C总线是PHLIPS公司在八十年代初推出的一种串行的半双工同步总线主要用于连接整体电路2.IIC总线硬件连接 1.IIC总线支持多主机多从机但是在实际开发过程中大多数采用单主机多从机模式
2.挂接到IIC总线上每个从机设备都有自己的7bit从机地址
3.在总线上发送数据的叫做发送器接收数据叫做接收器
4.主动发起数据的叫做主机只能被动接收数据的叫做从机
5.时钟信号由主机产生作用给从机为了IIC总线上传输数据同步3.IIC总线时序
3.1起始信号 在SCL为高电平期间SDA从高到低的变化(下降沿)属于起始信号
起始信号由主机产生起始信号产生之后总线占用状态3.2停止信号 在SCL为高电平期间SDA从低到高的变化(上升沿)属于终止信号
停止信号由主机产生停止信号产生之后总线空闲状态3.3数据传输信号(读写) 1.在SCL为高电平期间数据线上的数据保持稳定接收器从数据线上读取数据
2.在SCL为低电平期间数据线上的数据允许变化发送器向数据线上写入数据3.4应答信号 1.每一个字节必须保证是8位长度。数据传送时先传送高位在发送低位每一个被传送的字节后面都必须跟随一位应答位即一帧共有9位
2.发送器在发送完8位数据位之后接收器在第9个时钟周期返回一个应答信号(0)或者非应答信号(1)在第9个时钟周期,接收器向数据线上写入数据在第9个时钟周期,发送器从数据线上读取数据如果读取到0代表应答信号如果读取到1代表非应答信号3.5寻址 1.IIC总线上传输数据是广义的可以传输地址信号也可以传输数据信号
2.主机在产生起始信号之后必须传送7位从机地址加上读写位
3.用0表示写用1表示读4.IIC框图 5.IIC总线协议
5.1主机给从机发送一个字节 5.2主机给从机发送多个连续字节 5.3主机从从机读一个字节 5.4主机从从机读多个连续字节 6.GPIO模拟IIC协议 7.分析SI7006芯片手册
7.1思路
1.分析SI7006芯片手册内部实现框图
2.分析SI7006从机地址
3.分析SI7006芯片通信协议
4.分析找到采集温湿度传感器命令码
5.找到将采集的模拟量转换为数字量的公式
6.分析SI7006初始化的值7.2从机地址 通过以上分析可知si7006芯片从机地址0x40
从机地址 读0x40 1 | 1
从机地址 写0x40 1 | 07.3分析命令码 7.4协议 7.5公式 7.6初始化值 8.代码
// si7006.h
#ifndef __SI7006_H__
#define __SI7006_H__#include iic.h
#define SI7006_SLAVE 0x40void si7006_init(void);unsigned short si7006_read_hum_data(unsigned char slave_addr, unsigned char cmd_code);
short si7006_read_temp_data(unsigned char slave_addr, unsigned char cmd_code);#endif //__SI7006_H__
// si7006.c
#include iic.h
#include si7006.h
/** 函数名si7006_init* 函数功能SI7006芯片的初始化* 函数参数无* 函数返回值无*/
extern void delay(int ms);
// 往SI7006芯片0XE6写入0X3A
void si7006_init(void)
{// I2初始化i2c_init();// 发送起始信号i2c_start();// 主机发送7位从机地址1位写位i2c_write_byte(0X40 1 | 0);// 等待从机回应i2c_wait_ack();// 发送寄存器地址i2c_write_byte(0XE6);// 等待从机回应i2c_wait_ack();// 发送要写的数据i2c_write_byte(0X3A);// 等待从机回应i2c_wait_ack();// 发送终止信号i2c_stop();
}
/** 函数名si7006_read_hum_data* 函数功能读取SI7006的湿度转换结果* 函数参数* slave_addr 从机地址* cmd_code 命令码* 函数返回值湿度测量的数字量*/
unsigned short si7006_read_hum_data(unsigned char slave_addr,unsigned char cmd_code)
{unsigned short dat; // 保存读取到的湿度数据unsigned char dat_h, dat_l; // 保存读取到的数据的高八位和低八位// 发送起始信号i2c_start();// 主机发送7位从机地址1位写位i2c_write_byte(slave_addr 1 | 0);// 等待从机回应i2c_wait_ack();// 发送寄存器地址i2c_write_byte(cmd_code);// 等待从机回应i2c_wait_ack();// 发送第二次起始信号i2c_start();// 主机发送7位从机地址1位写位i2c_write_byte(slave_addr 1 | 1);// 等待从机回应i2c_wait_ack();// 延时等待从机测量数据delay(100);// 读取数据的高8位dat_h i2c_read_byte(0); // 读取完毕发送应答信号// 读取数据的低8位dat_l i2c_read_byte(1); // 读取完毕发送非应答信号// 发送停止信号i2c_stop();// 将读取到的数据整合到一起dat (dat_h 8) | dat_l;return dat;
}
/** 函数名si7006_read_temp_data* 函数功能读取SI7006的温度转换结果* 函数参数* slave_addr 从机地址* cmd_code 命令码* 函数返回值温度测量的数字量*/
short si7006_read_temp_data(unsigned char slave_addr,unsigned char cmd_code)
{short dat; // 保存读取到的温度数据char dat_h, dat_l; // 保存读取到的数据的高八位和低八位// 发送起始信号i2c_start();// 主机发送7位从机地址1位写位i2c_write_byte(slave_addr 1 | 0);// 等待从机回应i2c_wait_ack();// 发送寄存器地址i2c_write_byte(cmd_code);// 等待从机回应i2c_wait_ack();// 发送第二次起始信号i2c_start();// 主机发送7位从机地址1位写位i2c_write_byte(slave_addr 1 | 1);// 等待从机回应i2c_wait_ack();// 延时等待从机测量数据delay(100);// 读取数据的高8位dat_h i2c_read_byte(0); // 读取完毕发送应答信号// 读取数据的低8位dat_l i2c_read_byte(1); // 读取完毕发送非应答信号// 发送停止信号i2c_stop();// 将读取到的数据整合到一起dat (dat_h 8) | dat_l;return dat;
}
// iic.h
#ifndef __IIC_H__
#define __IIC_H__
#include stm32mp1xx_gpio.h
#include stm32mp1xx_rcc.h
// #include gpio.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 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
// icc.h
#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 (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; // 数值左移 0000 0000if (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; // 数据线处于占用状态发送器发送下一次数据
}
// main.c
#include si7006.hvoid delay(int ms)
{int i, j;for (i 0; i ms; i){for (j 0; j 2000; j);}
}
int main()
{unsigned short hum;short tem;// 进行si7006的初始化si7006_init();while (1){// 读取湿度hum si7006_read_hum_data(0X40, 0XE5);// 读取温度tem si7006_read_temp_data(0X40, 0XE3);// 将温度数据和湿度数据按照转换公式进行转换hum 125 * hum / 65536 - 6;tem 175.72 * tem / 65536 - 46.85;delay(1000); // 延时打印// 将获取到的数据打印到串口printf(hum:%d\n, hum);printf(tem:%d\n, tem);}return 0;
}