Iic.c
#include "../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为输出模式,确保一定为输出模式
I2C_SDA_H; // SDA拉高
I2C_SCL_H; // SCL拉高
delay_us();
I2C_SDA_L; // SDA拉低
delay_us();
I2C_SCL_L; // SCL拉低,I2C总线就处于占用态
}
/*
* 函数名: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拉高
}
/*
* 函数名: 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;
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)
i2c_ack(); // 发送应答信号
else
i2c_nack(); // 发送非应答信号
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拉低
delay_us();
I2C_SDA_H; // SDA拉高, 释放总线
SET_SDA_IN; // 设置SDA为输入
delay_us();
delay_us(); // 等待从机向数据线上写入应答信号
I2C_SCL_H; // SCL拉高
delay_us();
if (I2C_SDA_READ)
ack = 1; // 非应答信号
else
ack = 0; // 应答信号
delay_us();
I2C_SCL_L;
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拉低
}
si7006.c
#include "../include/iic.h"
#include "../include/si7006.h"
extern void delay_ms(unsigned int ms);
/*
* 函数名:si7006_init
* 函数功能:SI7006芯片的初始化
* 函数参数:无
* 函数返回值:无
*/
void si7006_init(void)
{
i2c_init();
// 向用户寄存器中写入数据
i2c_start();
i2c_write_byte(SI7006_SLAVE<<1);
i2c_wait_ack();
i2c_write_byte(USER_REG_CMD);
i2c_wait_ack();
i2c_write_byte(USER_REG_VALUE);
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();
i2c_write_byte(slave_addr << 1);
i2c_wait_ack();
i2c_write_byte(cmd_code);
i2c_wait_ack();
i2c_start();
i2c_write_byte((slave_addr << 1) | 1);
i2c_wait_ack();
delay_ms(100); // 等待转换结束之后在读取测量的结果
dat_h = i2c_read_byte(0);
dat_l = i2c_read_byte(1);
i2c_stop();
dat = dat_h;
dat = (dat << 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;
unsigned char dat_h, dat_l;
i2c_start();
i2c_write_byte(slave_addr << 1);
i2c_wait_ack();
i2c_write_byte(cmd_code);
i2c_wait_ack();
i2c_start();
i2c_write_byte((slave_addr << 1) | 1);
i2c_wait_ack();
delay_ms(100); // 等待转换结束之后在读取测量的结果
dat_h = i2c_read_byte(0);
dat_l = i2c_read_byte(1);
i2c_stop();
dat = dat_h;
dat = (dat << 8) | dat_l;
return dat;
}
main.c
#include "led.h"
#include "uart4.h"
#include "command.h"
#include "beep.h"
#include "fan.h"
#include "motor.h"
#include "interrupt.h"
#include "timer2_it.h"
#include "./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 = (125 * hum / 65536 - 6) * 10;
temp = (175.72 * temp / 65536 - 46.85) * 10;
printf("hum = %d.%d\n", hum / 10, hum % 10);
printf("temp = %d.%d\n", temp / 10, temp % 10);
delay_ms(1000);
}
return 0;
}