ARM开发(stm32 cortex-A7核IIC实验)

news/2024/6/3 19:27:40 标签: arm开发, stm32, 嵌入式硬件, c语言

1.实验目标:采集温湿度传感器值;

2.分析框图(模拟IIC控制器);

 

3.代码;

---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 i2c_init(void);   //模拟I2C总线初始化
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);
	// 设置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     \
	 *          --------
	 * */	
	//1.设置数据线为输出模式
	SET_SDA_OUT;

	//2.在SCL为高电平
	I2C_SCL_H;
	delay_us();

	//3.SDA高电平
	I2C_SDA_H;
	delay_us();

	//4.SDA低电平
	I2C_SDA_L;

	//5.起始信号产生之后,总线处于占用状态
	I2C_SCL_L;
}

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

void i2c_stop(void)
{
	/*
	 * 停止信号 : 时钟在高电平期间,数据线从低到高的变化 
	 *             ----------
	 * SCL        /
	 *    --------
	 *    ---         -------
	 * SDA   X       /
	 *    --- -------
	 * */

	//1.设置数据线输出模式
	SET_SDA_OUT;

	//2.SCL为低电平,改变数据线上数据
	I2C_SCL_L;
	delay_us();

	//3.SDA为低电平
	I2C_SDA_L;
	delay_us();

	//4.SCL为高电平
	I2C_SCL_H;
	delay_us();

	//5.SDA为高电平
	I2C_SDA_H;
	delay_us();
}

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

void i2c_write_byte(unsigned char dat)
{
	/*
	 * 数据信号:时钟在低电平期间,发送器向数据线上写入数据
	 * 			时钟在高电平期间,接收器从数据线上读取数据 
	 *      ----          --------
	 * 	SCL     \        /        \
	 *           --------          --------
	 *      -------- ------------------ ---
	 * 	SDA         X                  X
	 *      -------- ------------------ ---
	 *
	 *      先发送高位在发送低位 
	 * */
	unsigned int i;

	//1.设置数据线为输出模式
	SET_SDA_OUT;

	//2.for循环,先发高位再发低位
	
	for(i=0; i<8; i++)
	{
		I2C_SCL_L;  //SCL低电平
		delay_us();

		if(dat & 0x80)  //先发高位
			I2C_SDA_H;  //3.向数据线上写入高电平
		else
			I2C_SDA_L;  //4.向数据线上写入低电平
		delay_us();

		I2C_SCL_H;  //5.等待从机从数据线上读取数据
		delay_us();
		delay_us();
		
		dat <<= 1;  //6.移位
	}
}

/*
 * 函数名: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;  //1.设置数据线输入模式
	
	for(i=0; i<8; i++)
	{
		I2C_SCL_L;  //2保证发送器向数据线上写入数据完成
		delay_us();
		delay_us();

		I2C_SCL_H;  //3从数据线上读取数据
		delay_us();
		delay_us();
		dat <<= 1;  //4移位放if前面

		if(I2C_SDA_READ)
			dat |= 1;  //读取的数据为1
		else
			dat |= 0;  //读取的数据为0
		delay_us();
	}
		if(!ack)
			i2c_ack();  //主机给从机返回应答信号
		else
			i2c_nack();  //主机给从机返回非应答信号

		return dat;
}
/*
 * 函数名: i2c_wait_ack
 * 函数功能: 主机作为发送器时,等待接收器返回的应答信号
 * 函数参数:无
 * 函数返回值:
 *					0:接收到的应答信号
 *                  1:接收到的非应答信号
 * */
unsigned char i2c_wait_ack(void)
{
	/*
	 * 主机发送一个字节之后,从机给主机返回一个应答信号
	 *
	 *                   -----------
	 * SCL              /   M:读    \
	 *     -------------             --------
	 *     --- ---- --------------------
	 * SDA    X    X
	 *     ---      --------------------
	 *     主  释   从机    主机
	 *     机  放   向数据  读数据线
	 *         总   线写    上的数据
	 *         线   数据
	 * */	

	I2C_SCL_L;  //1设置SCL为低电平
	delay_us();

	I2C_SDA_H;  //2.释放数据线
	delay_us();
	SET_SDA_IN;  //3.设置数据线输入模式
	delay_us();

	I2C_SCL_H;  //SCL为高电平期间
	delay_us();

	//从总线上读取数据
	if(I2C_SDA_READ)
		return 1;  //读1 非应答信号
	
	I2C_SCL_L;  //SCL为低电平,总线处于占用状态
	return 0;  //读0 应答信号
} 
/*
 * 函数名: iic_ack
 * 函数功能: 主机作为接收器时,给发送器发送应答信号
 * 函数参数:无
 * 函数返回值:无
 * */
void i2c_ack(void)
{
	/*            --------
	 * SCL       /        \
	 *    -------          ------
	 *    ---
	 * SDA   X 
	 *    --- -------------
	 * */
	SET_SDA_OUT;  //1.数据线输出 
	I2C_SCL_L;   //2.SCL低电平,改变数据线上数据
	delay_us();

	I2C_SDA_L;  //3.应答信号==0
	delay_us();

	I2C_SCL_H;  //4.等待从机读取应答号
	delay_us();
	delay_us();
	I2C_SCL_L;  //5.总线处于占用状态
}
/*
 * 函数名: iic_nack
 * 函数功能: 主机作为接收器时,给发送器发送非应答信号
 * 函数参数:无
 * 函数返回值:无
 * */
void i2c_nack(void)
{
	/*            --------
	 * SCL       /        \
	 *    -------          ------
	 *    --- ---------------
	 * SDA   X 
	 *    --- 
	 * */		
	SET_SDA_OUT;  //1.数据线输出 
	I2C_SCL_L;   //2.SCL低电平,改变数据线上数据
	delay_us();

	I2C_SDA_H;  //3.非应答信号==1
	delay_us();

	I2C_SCL_H;  //4.等待从机读取应答号
	delay_us();
	delay_us();
	I2C_SCL_L;  //5.总线处于占用状态
}

---si7006.h读取温湿度头文件---
#ifndef __SI7006_H__
#define __SI7006_H__

#include "iic.h"
#define        SI7006_SLAVE      0x40

//初始化
void si7006_init(void);

//读取湿度
unsigned short si7006_read_hum_data(unsigned char slave_addr, unsigned char reg_addr);
//读取温度
short si7006_read_temp_data(unsigned char slave_addr, unsigned char reg_addr);


#endif //__SI7006_H__
---si7006.c读取温湿度函数---
#include "iic.h"
#include "si7006.h"
extern void delay_ms(unsigned int ms);
/*
 * 函数名:si7006_init
 * 函数功能:SI7006芯片的初始化
 * 函数参数:无
 * 函数返回值:无
*/
void si7006_init(void)
{
	i2c_init();  //模拟I2C总线初始化
	i2c_start();   //开始信号

	i2c_write_byte(SI7006_SLAVE << 1);  //发送从机地址+写0
	i2c_wait_ack();  //主机等待从机应答信号

	i2c_write_byte(0xE6);  //发送写用户寄存器1
	i2c_wait_ack(); 

	i2c_write_byte(0x3A);  //发送写用户寄存器1
	i2c_wait_ack(); 
	
	i2c_stop();  //停止信号
}

/*
 * 函数名:si7006_read_data
 * 函数功能:读取SI7006的转换结果
 * 函数参数:
 *     slave_addr : 从机地址
 *     reg_addr : 寄存器地址
 * 函数返回值:无
*/
unsigned short si7006_read_hum_data(unsigned char slave_addr, 
		unsigned char reg_addr)
{
	unsigned short dat;
	unsigned char dat_h;
	unsigned char dat_l;

	i2c_start();  //起始信号
	i2c_write_byte(slave_addr << 1 | 0);  //发送从机地址+写0
	i2c_wait_ack();  //主机等待从机应答信号

	i2c_write_byte(reg_addr);  //发送寄存器地址
	i2c_wait_ack();

	i2c_start();  //起始信号
	i2c_write_byte(slave_addr << 1 | 1);  //发送从机地址+读1
	i2c_wait_ack();

	delay_ms(5000);  //延时函数

	dat_h = i2c_read_byte(0);  //参数ack
	dat_l = i2c_read_byte(1);  //nack

	//高八位低八位拼接
	dat = dat_h;
	dat <<= 8;
	dat |= dat_l;

	return dat;
}

//温度
short si7006_read_temp_data(unsigned char slave_addr, 
		unsigned char reg_addr)
{
	short dat;
	unsigned char dat_h;
	unsigned char dat_l;

	i2c_start();  //起始信号
	i2c_write_byte(slave_addr << 1 | 0);  //发送从机地址+写0
	i2c_wait_ack();  //主机等待从机应答信号

	i2c_write_byte(reg_addr);  //发送寄存器地址
	i2c_wait_ack();

	i2c_start();  //起始信号
	i2c_write_byte(slave_addr << 1 | 1);  //发送从机地址+读1
	i2c_wait_ack();
	delay_ms(5000); //延时函数

	dat_h = i2c_read_byte(0);  //参数ack
	dat_l = i2c_read_byte(1);  //nack

	//高八位低八位拼接
	dat = dat_h;
	dat <<= 8;
	dat |= dat_l;

	return dat;

}

---main.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 < 1800; j++);
}

int main()
{
	short temp;
	unsigned short hum;
	si7006_init();

	while(1)
	{
		hum = si7006_read_hum_data(SI7006_SLAVE,0xE5);
		temp = si7006_read_temp_data(SI7006_SLAVE,0xE3);

		printf("hum = %d\n",hum*125/65536-6);
		printf("temp = %d\n",temp*176/65536-47);
	}

	return 0;

}

4.运行结果;

 


http://www.niftyadmin.cn/n/4964493.html

相关文章

优化Python代理爬虫的应用

当我们在资源受限的环境中使用Python代理爬虫时&#xff0c;我们需要采取一些优化措施&#xff0c;以确保程序的高效性和稳定性。在本文中&#xff0c;我将分享一些关于如何优化Python代理爬虫在资源受限环境下的应用的实用技巧。 首先我们来了解&#xff0c;哪些情况算是资源…

java中多个list怎么用List表示?

如果你有多个List对象&#xff0c;想要将它们合并成一个List对象&#xff0c;可以使用addAll()方法来实现。addAll()方法将会把一个List中的元素逐个添加到另一个List中。 以下是一个示例&#xff0c;展示了如何将多个List对象合并为一个List对象&#xff1a; import java.ut…

10-案例: 注册登录

项目思路: 1. 首页展示 将数据传递给前端模板渲染 2. 注册用户 接收前端传递的数据,保存后,重定向到首页 3. 删除用户 接收前端传递的用户名,进行删除 4. 修改用户 接收前端传递的数据,老名字进行判断,新数据修改数据 项目结构: 构建蓝图: (1). apps / user / model.py 增…

Golang Gorm 高级查询之where查询

插入测试数据 package mainimport ("fmt""gorm.io/driver/mysql""gorm.io/gorm" )type Student struct {ID int64Name string gorm:"size:6"Age intEmail *string }func (*Student) TableName() string {return "student&q…

剑指Offer41,数据流中的中位数 C++

1、题目描述 如何得到一个数据流中的中位数&#xff1f;如果从数据流中读出奇数个数值&#xff0c;那么中位数就是所有数值排序之后位于中间的数值。如果从数据流中读出偶数个数值&#xff0c;那么中位数就是所有数值排序之后中间两个数的平均值。 例如&#xff0c; [2,3,4] 的…

requests模板成功下载,但是不能在pycharm中运行

在做实验的过程中&#xff0c;需要用到requests&#xff0c;但是在pycharm中成功下载&#xff0c;仍然无法使用&#xff0c;找了很久&#xff0c;解决方法如下&#xff1a; 进入win中的命令提示符 下载requests模块 pip install requests输入python显示你的python的基本信息&…

Microsoft GitHub产品线梳理

目录 前言 1. GitHub网站 2. GitHub Desktop 3. GitHub CLI 4. GitHub Actions 5. GitHub Packages 总结 前言 Microsoft GitHub是微软公司于2018年收购的一个基于云的软件开发平台,提供代

Jetpack Compose UI架构

Jetpack Compose UI架构 引言 Jetpack Compose是我职业生涯中最激动人心的事。它改变了我工作和问题思考的方式&#xff0c;引入了易用且灵活的工具&#xff0c;几乎可轻松实现各种功能。 早期在生产项目中尝试了Jetpack Compose后&#xff0c;我迅速着迷。尽管我已有使用Co…