2022-10-5 IIC实验(测温湿度)(复习)

1.I2C总线是PHLIPS公司在八十年代初推出的一种串行的半双工同步总线,主要用于连接整体电路。 1)同一个板子两个芯片之间的通信 stm32mp157a<---IIC--->SI7006 2)两个设备之间的通信 SOC<---UART--->Target 2.I2C总线为两线制,只有两根双向信号线。一根是数据线SDA,另一根是时钟线SCL。 3.I2C硬件结构简单,接口连接方便,成本较低。因此在各个领域得到了广泛的应用。 4.IIC总线传输速率 低速 100k 中速 400k 全速 3.4M 5.IIC总线需要接两个上拉电阻,这两个上拉电阻作用在IIC总线为空闲状态时保持高电平。

 

 IIC的协议

主机给从机发送一个字节

     主机给从机发送多个字节

  从机给主机发送一个字节

从机给主机发送多个字节

 

首先是头文件部分

#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 

然后是功能函数部分,包括初始化,开始,结束, 写字节,读字节,等待应答,还有应答包和非应答包

#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);
	// 设置PF14,PF15引脚为通用的输出功能
	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     \
	 *          --------
	 * */	
	SET_SDA_OUT;    // 设置SDA为输出,确保SDA一定为输出
	I2C_SCL_H;      // SCL拉高
	I2C_SDA_H;      // SDA拉高
	delay_us();
	I2C_SDA_L;      // SDA拉低
	delay_us();
	I2C_SCL_L;      // SCL拉低
	delay_us();
}

/*
 * 函数名:i2c_stop
 * 函数功能:模拟i2c停止信号的时序
 * 函数参数:无
 * 函数返回值:无
 * */

void i2c_stop(void)
{
	/*
	 * 停止信号 : 时钟在高电平期间,数据线从低到高的变化 
	 *             ----------
	 * SCL        /
	 *    --------
	 *    ---         -------
	 * SDA   X       /
	 *    --- -------
	 *    为了确保停止信号是一个上升沿,因此在时钟为低电平期间
	 *    将数据线拉低,确保可以产生上升沿。
	 * */
	SET_SDA_OUT;    // SDA输出
	I2C_SCL_L;      // SCL拉低 
	delay_us();
	I2C_SDA_L;      // SDA拉低
	delay_us();
	I2C_SCL_H;      // SCL拉高
	delay_us(); 
	I2C_SDA_H;      // SDA拉高
	delay_us();
}

/*
 * 函数名: i2c_write_byte
 * 函数功能:主机向i2c总线上的从设备写8bits数据
 * 函数参数:dat : 等待发送的字节数据
 * 函数返回值: 无
 * */

void i2c_write_byte(unsigned char dat)
{
	/*
	 * 数据信号:时钟在低电平期间,发送器向数据线上写入数据
	 * 			时钟在高电平期间,接收器从数据线上读取数据 
	 *      ----          --------
	 * 	SCL     \        /        \
	 *           --------          --------
	 *      -------- ------------------ ---
	 * 	SDA         X                  X
	 *      -------- ------------------ ---
	 *      先发送高位在发送低位 
	 * */
	unsigned int i;
	SET_SDA_OUT;     // SDA为输出
	for (i = 0; i < 8;i++) {
		I2C_SCL_L;    // SCL拉低
		delay_us();
		if (dat & 0x80) {
			I2C_SDA_H;   // 向数据线上写入高电平
		} else {
			I2C_SDA_L;   // 向数据线上写入低电平
		}
		delay_us();
		I2C_SCL_H;     // SCL拉高
		delay_us();
		delay_us();    // 等待从机将数据线上的数据读走

		dat <<= 1;     // 将次高位的数据移到最高位
	}
	
}

/*
 * 函数名:i2c_read_byte
 * 函数功能: 主机从i2c总线上的从设备读8bits数据, 
 *          主机发送一个应答或者非应答信号给从机
 * 函数参数: 0 : 应答信号   1 : 非应答信号
 * 函数返回值:读到的有效数据
 *
 * */
unsigned char i2c_read_byte(unsigned char ack)
{
	/*
	 * 数据信号:时钟在低电平期间,发送器向数据线上写入数据
	 * 			时钟在高电平期间,接收器从数据线上读取数据 
	 *      ----          --------
	 * 	SCL     \        /        \
	 *           --------          --------
	 *      -------- ------------------ ---
	 * 	SDA         X                  X
	 *      -------- ------------------ ---
	 *
	 *      先接收高位, 在接收低位 
	 * */
	unsigned char dat = 0;
	unsigned int i;
	SET_SDA_IN;      // 设置SDA为输入
	for (i = 0; i < 8; i++) {
		I2C_SCL_L;   // SCL拉低 
		delay_us();
		delay_us();  // 等待从机向数据线上写入数据
		I2C_SCL_H;   // SCL拉高
		delay_us();
		dat <<= 1;
		if (I2C_SDA_READ) {
			dat |= 1;
		} else {
			dat |= 0;
		}
		delay_us();
	}

	if (ack != 0) {
		i2c_nack();
	} else {
		i2c_ack();
	}
	return dat;
}
/*
	将dat中数据的左移1位写到接收数据的上边。

	为什么将dat中的数据左移1位,写道接收数据的下边不可以?
	假设接收的数据为0xFF,
                    先接收数据    在进行左移
	第1次循环        0000 0001    0000 0010
	第2次循环        0000 0011    0000 0110
	第3次循环        0000 0111    0000 1110
	第4次循环        0000 1111    0001 1110
	第5次循环        0001 1111    0011 1110
	第6次循环        0011 1111    0111 1110
	第7次循环        0111 1111    1111 1110
	第8次循环        1111 1111    1111 1110

*/

/*
 * 函数名: i2c_wait_ack
 * 函数功能: 主机作为发送器时,等待接收器返回的应答信号
 * 函数参数:无
 * 函数返回值:
 *					0:接收到的应答信号
 *                  1:接收到的非应答信号
 * */
unsigned char i2c_wait_ack(void)
{
	int ack;
	/*
	 * 主机发送一个字节之后,从机给主机返回一个应答信号,主机接收应答信号
	 *                       -----------
	 * SCL                  /   M:读    \
	 *     -----------------             --------
	 *     --- -------- --------------------
	 * SDA    X        X
	 *     ---          --------------------
	 *     主  释 设  从机    主机
	 *     机  放 置  向数据  读数据线
	 *         总 SDA 线写    上的数据
	 *         线 输  数据
	 *            入
	 * */	
	I2C_SCL_L;   // SCL拉低
	I2C_SDA_H;   // SDA拉高,主机释放数据线,
	delay_us();
	SET_SDA_IN;  // 设置数据线为输入
	delay_us();
	delay_us();  // 等待从机向数据线上写入应答信号(0)或者非应答信号(1)
	I2C_SCL_H;   // SCL拉高
	delay_us();
	if (I2C_SDA_READ) {
		ack = 1;   // 接收到的是非应答信号
	} else {
		ack = 0;   // 接收到的是应答信号
	}
	delay_us();
	I2C_SCL_L;     // SCL拉低
	return ack;
} 
/*
 * 函数名: iic_ack
 * 函数功能: 主机作为接收器时,给发送器发送应答信号
 * 函数参数:无
 * 函数返回值:无
 * */
void i2c_ack(void)
{
	/*            --------
	 * SCL       /        \
	 *    -------          ------
	 *    ---
	 * SDA   X 
	 *    --- -------------
	 *    在第九个时钟周期的低电平期间,接收器向数据线写入数据,
	 *    在第九个时钟周期的高电平期间,发送器从数据线上读取数据,
	 *    如果读到低电平表示应答信号
	 * */
	SET_SDA_OUT;   // SDA为输出
	I2C_SCL_L;     // SCL拉低
	delay_us();
	I2C_SDA_L;     // SDA拉低
	delay_us();
	I2C_SCL_H;     // SCL拉高
	delay_us();
	delay_us();    // 等待从机从数据线上读取应答信号
	I2C_SCL_L;     // SCL拉低
	
}
/*
 * 函数名: iic_nack
 * 函数功能: 主机作为接收器时,给发送器发送非应答信号
 * 函数参数:无
 * 函数返回值:无
 * */
void i2c_nack(void)
{
	/*            --------
	 * SCL       /        \
	 *    -------          ------
	 *    --- ---------------
	 * SDA   X 
	 *    --- 
	 *    在第九个时钟周期的低电平期间,接收器向数据线写入数据,
	 *    在第九个时钟周期的高电平期间,发送器从数据线上读取数据,
	 *    如果读到高电平表示非应答信号
	 * */
	SET_SDA_OUT;   // SDA为输出
	I2C_SCL_L;     // SCL拉低
	delay_us();
	I2C_SDA_H;     // SDA拉高,读取非应答信号
	delay_us();
	I2C_SCL_H;     // SCL拉高
	delay_us();
	delay_us();    // 等待从机从数据线上读取非应答信号
	I2C_SCL_L;     // SCL拉低
}

最后是函数的调用

#include "led.h"

#include "uart4.h"

#include "beep.h"

#include "command.h"

#include "key_IT.h"

#include "include/interrupt.h"

#include "timer-IT.h"

#include "si7006.h"



extern void printf(const char *fmt, ...);

void delay_ms(unsigned int ms)

{

	int i,j;

	for(i = 0; i < ms;i++)

		for (j = 0; j < 1800; j++);

}



extern char  buffer[LEN];

int main()

{

	unsigned short hum;

	short temp;

	si7006_init();



	while(1)

	{

		hum = si7006_read_hum_data(SI7006_SLAVE, MEASURE_HUM_CMD);

		temp = si7006_read_temp_data(SI7006_SLAVE, MEASURE_TEMP_CMD);



		hum = (hum * 125 / 65536 - 6)*10;

		temp = (temp * 175.72 / 65535 - 46.85)*10;



		printf("measure hum = %d.%d\n", hum / 10, hum % 10);

		printf("measure temp = %d.%d\n", temp / 10, temp % 10);



		delay_ms(1000);



	}

	return 0;

}

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值