avatar

松果工作室

欢迎光临

  • 首页
  • freeRTOS
  • ESP
  • 开发手册
  • 快速笔记
  • 个人收藏
  • 工具
Home 软件I2C
文章

软件I2C

Posted 2024-01-17 Updated 2024-02- 17
By YCP
46~60 min read

硬件IIC读取写入函数

HAL_I2C_Mem_Read(&hi2cx, DEV_ADD, REG_ADD, ADD_SIZE, &DATA, DATA_SIZE, HAL_MAX_DELAY);
HAL_I2C_Mem_Write(&hi2c, DEV_ADD, REG_ADD, ADD_SIZE, &DATA, DATA_SIZE, HAL_MAX_DELAY);
HAL_I2C_Master_Transmit(&hi2c, DEV_ADD, &DATA, DATA_SIZE, HAL_MAX_DELAY);
HAL_I2C_Master_Receive(&hi2c, DEV_ADD, &DATA, DATA_SIZE, HAL_MAX_DELAY);

模拟IIC读取写入函数及其实现

uint8_t Soft_IIC_Read_Register(uint8_t deviceAddress, uint8_t regAddr, uint8_t *pData) {
	Soft_IIC_Start();
	if (!Soft_IIC_Write_Byte((deviceAddress << 1) | 0x00)) {
		Soft_IIC_Stop();
		return 0;
	}
	if (!Soft_IIC_Write_Byte(regAddr)) {
		Soft_IIC_Stop();
		return 0;
	}
	Soft_IIC_Start();
	if (!Soft_IIC_Write_Byte((deviceAddress << 1) | 0x01)) {
		Soft_IIC_Stop();
		return 0;
	}
	*pData = Soft_IIC_Recv_Byte(NACK);
	Soft_IIC_Stop();
	return 1;
}
uint8_t Soft_IIC_Write_Register(uint8_t deviceAddress, uint8_t regAddr, uint8_t data) {
	Soft_IIC_Start();
	if (!Soft_IIC_Write_Byte((deviceAddress << 1) | 0x00)) {
		Soft_IIC_Stop();
		return 0;
	}
	if (!Soft_IIC_Write_Byte(regAddr)) {
		Soft_IIC_Stop();
		return 0;
	}
	if (!Soft_IIC_Write_Byte(data)) {
		Soft_IIC_Stop();
		return 0;
	}
	Soft_IIC_Stop();
	return 1;
}

IIC写入数据,在初始化传感器的时候使用,比如配置MPU6050的精度,量程范围等等。

IIC写入流程:
IIC开始。
IIC发送传感器地址并检测是否响应。
IIC发送寄存器地址并检测是否响应。
IIC发送寄存器地址参数数据。
IIC停止。

IIC读取流程:
IIC开始。
IIC发送传感器地址并检测是否响应。
IIC发送寄存器地址并检测是否响应。
IIC开始。
IIC发送接收请求并检测是否响应。(从代码里面可以看见接收请求就是传感器地址&&0x01)
IIC接收数据。
IIC停止。

逐步剖析

IIC启动
R.6fd6eae154a8750bf2693d2e4fe01605.jpeg

void Soft_IIC_Start(void)
{
	Soft_IIC_Output();
	IIC_SCL_L();//至于这个为什么要加,暂时不清楚
  
	IIC_SDA_H();
	IIC_SCL_H();
	IIC_Delay(IIC_DELAY_TIME);
	IIC_SDA_L();
	IIC_Delay(IIC_DELAY_TIME);
	IIC_SCL_L();
}

IIC写入
设置IO口为输出模式
把8位字节按高低电平发送
等待回应(等待回应函数中设置了IO为输入模式,等待的是数据口的电平变化)

uint8_t Soft_IIC_Write_Byte(uint8_t Byte)
{
	uint8_t i;
	Soft_IIC_Output();
	for (i = 0x80; i != 0; i >>= 1)
	{
		if (Byte & i)
		{
			Soft_IIC_Write_High();
		}
		else
		{
			Soft_IIC_Write_Low();
		}
	}
	return (Soft_IIC_Wait_ACK());
}

等待回应函数
这里是 模拟IIC 的重点。
在源代码中,Soft_IIC_Input()实际上我把 IO 配置为输入模式这个代码注释了,仅仅把 IO 配置为高电平。
当然了,这个SDA引脚默认是输出模式,为什么把 IO 输出模式依然能读取外界电平,实现输出又输入呢。
https://blog.csdn.net/qq_35629563/article/details/87710101
其实 很多模拟IIC教程会让你把引脚配置为开漏输出,却不说为什么。
这是原因:

  1. 引脚配置成开漏输出
  2. 外部上拉
  3. 在输入的时候,要先将SDA的引脚置高
    经过这三样配置,IIC 就能实现输入又输出。如果SDA 引脚置低可能就只能输出模式了吧。
uint8_t Soft_IIC_Wait_ACK(void)
{
	uint8_t wait;
	Soft_IIC_Output();
	IIC_SDA_H();
  
	Soft_IIC_Input();
	IIC_SCL_H();
	IIC_Delay(IIC_DELAY_TIME);
	while (HAL_GPIO_ReadPin(IIC_SDA_PORT, IIC_SDA_PIN))
	{
		wait++;
		if (wait > 200)
		{
			Soft_IIC_Stop();
			return 0;
		}
	}
	IIC_SCL_L();
	return 1;
}

IIC停止函数

void Soft_IIC_Stop(void)
{
	Soft_IIC_Output();
	IIC_SCL_L();
  
    IIC_SCL_H();
	IIC_SDA_L();
	IIC_Delay(IIC_DELAY_TIME);
	IIC_SDA_H();
	IIC_Delay(IIC_DELAY_TIME);
}

总代码

H文件

#ifndef _SOFTWAREIIC_H_
#define _SOFTWAREIIC_H_

#include "main.h"

typedef enum{
		NACK = 0,
		ACK  = 1
}ACK_STATUS;

#define IIC_DELAY_TIME 10

// 软件IIC引脚定义
#define IIC_SCL_PIN GPIO_PIN_2
#define IIC_SCL_PORT GPIOB
#define IIC_SDA_PIN GPIO_PIN_1
#define IIC_SDA_PORT GPIOB

// #define IIC_SDA_H() GPIOB->BSRR = 0x010
// #define IIC_SDA_L() GPIOB->BRR = 0x010
// #define IIC_SCL_H() GPIOB->BSRR = 0x100
// #define IIC_SCL_L() GPIOB->BRR = 0x100

#define IIC_SCL_H()     HAL_GPIO_WritePin(GPIOB,GPIO_PIN_2,GPIO_PIN_SET)
#define IIC_SCL_L()     HAL_GPIO_WritePin(GPIOB,GPIO_PIN_2,GPIO_PIN_RESET)
#define IIC_SDA_H()     HAL_GPIO_WritePin(GPIOB,GPIO_PIN_1,GPIO_PIN_SET)
#define IIC_SDA_L()     HAL_GPIO_WritePin(GPIOB,GPIO_PIN_1,GPIO_PIN_RESET)


void Soft_IIC_Init(void);
void Soft_IIC_Start(void);
void Soft_IIC_Stop(void);
void Soft_IIC_ACK(void);
void Soft_IIC_NACK(void);
uint8_t Soft_IIC_Wait_ACK(void);
uint8_t Soft_IIC_Write_Byte(uint8_t Byte);
uint8_t Soft_IIC_Recv_Byte(ACK_STATUS ack_sta);


uint8_t Soft_IIC_Read_Register(uint8_t deviceAddress, uint8_t regAddr, uint8_t *pData);
uint8_t Soft_IIC_Write_Register(uint8_t deviceAddress, uint8_t regAddr, uint8_t data);
#endif

C文件

/**
 * @file     softwareiic.c
 * @brief    适用于STM32 HAL库及CUBEMX生成工程的软件模拟IIC
 * @version  V1.0.0
 * @author   Sundea
 * @date     2023/5/8
 */
#include "softwareiic.h"
#include "sys_delay_us.h"
/**
 * @brief IIC延时
 * @param  无
 * @return 无
 */
void IIC_Delay(uint8_t time)
{
	delay_us(time);
}

/**
 * @brief IIC初始化
 * @param  无
 * @return 无
 */
void Soft_IIC_Init(void)
{
	IIC_SDA_H();
	IIC_SCL_H();
}

/**
 * @brief SDA引脚设置输出模式
 * @param  无
 * @return 无
 */
static void Soft_IIC_Output(void)
{
	GPIO_InitTypeDef SOFT_IIC_GPIO_STRUCT;
	SOFT_IIC_GPIO_STRUCT.Mode = GPIO_MODE_OUTPUT_PP;
	SOFT_IIC_GPIO_STRUCT.Pin = IIC_SDA_PIN;
	SOFT_IIC_GPIO_STRUCT.Speed = GPIO_SPEED_FREQ_HIGH;

	HAL_GPIO_Init(IIC_SDA_PORT, &SOFT_IIC_GPIO_STRUCT);
}

/**
 * @brief SDA引脚设置输入模式
 * @param  无
 * @return 无
 */
static void Soft_IIC_Input(void)
{
	GPIO_InitTypeDef SOFT_IIC_GPIO_STRUCT;
	SOFT_IIC_GPIO_STRUCT.Mode = GPIO_MODE_INPUT;
	SOFT_IIC_GPIO_STRUCT.Pin = IIC_SDA_PIN;
	SOFT_IIC_GPIO_STRUCT.Speed = GPIO_SPEED_FREQ_HIGH;

	HAL_GPIO_Init(IIC_SDA_PORT, &SOFT_IIC_GPIO_STRUCT);
}

/**
 * @brief IIC起始信号
 * @param  无
 * @return 无
 */
void Soft_IIC_Start(void)
{
	Soft_IIC_Output();
	IIC_SCL_L();
	IIC_SDA_H();
	IIC_SCL_H();
	IIC_Delay(IIC_DELAY_TIME);
	IIC_SDA_L();
	IIC_Delay(IIC_DELAY_TIME);
	IIC_SCL_L();
}

/**
 * @brief IIC停止信号
 * @param  无
 * @return 无
 */
void Soft_IIC_Stop(void)
{
	Soft_IIC_Output();
	IIC_SCL_L();
	IIC_SDA_L();
	IIC_SCL_H();
	IIC_Delay(IIC_DELAY_TIME);
	IIC_SDA_H();
	IIC_Delay(IIC_DELAY_TIME);
}

/**
 * @brief IIC应答信号
 * @param  无
 * @return 无
 */
void Soft_IIC_ACK(void)
{
	Soft_IIC_Output();
	IIC_SCL_L();
	IIC_SDA_L();
	IIC_Delay(IIC_DELAY_TIME);
	IIC_SCL_H();
	IIC_Delay(IIC_DELAY_TIME);
	IIC_SCL_L();
}

/**
 * @brief IIC无应答信号
 * @param  无
 * @return 无
 */
void Soft_IIC_NACK(void)
{
	Soft_IIC_Output();
	IIC_SCL_L();
	IIC_SDA_H();
	IIC_Delay(IIC_DELAY_TIME);
	IIC_SCL_H();
	IIC_Delay(IIC_DELAY_TIME);
}

/**
 * @brief IIC等待应答信号
 * @param  无
 * @return 0无应答  1有应答
 */
uint8_t Soft_IIC_Wait_ACK(void)
{
	uint8_t wait;
	Soft_IIC_Output();
	IIC_SDA_H();
	Soft_IIC_Input();
	IIC_SCL_H();
	IIC_Delay(IIC_DELAY_TIME);
	while (HAL_GPIO_ReadPin(IIC_SDA_PORT, IIC_SDA_PIN))
	{
		wait++;
		if (wait > 200)
		{
			Soft_IIC_Stop();
			return 0;
		}
	}
	IIC_SCL_L();
	return 1;
}

/**
 * @brief IIC写数据1
 * @param  无
 * @return 无
 */
void Soft_IIC_Write_High(void)
{
	IIC_SCL_L();
	IIC_SDA_H();
	IIC_Delay(IIC_DELAY_TIME);
	IIC_SCL_H();
	IIC_Delay(IIC_DELAY_TIME);
	IIC_SCL_L();
}

/**
 * @brief IIC写数据0
 * @param  无
 * @return 无
 */
void Soft_IIC_Write_Low(void)
{
	IIC_SCL_L();
	IIC_SDA_L();
	IIC_Delay(IIC_DELAY_TIME);
	IIC_SCL_H();
	IIC_Delay(IIC_DELAY_TIME);
	IIC_SCL_L();
}

/**
 * @brief IIC写入单个数据
 * @param  无
 * @return 应答信号, 0无应答 1有应答
 */
uint8_t Soft_IIC_Write_Byte(uint8_t Byte)
{
	uint8_t i;
	Soft_IIC_Output();
	for (i = 0x80; i != 0; i >>= 1)
	{
		if (Byte & i)
		{
			Soft_IIC_Write_High();
		}
		else
		{
			Soft_IIC_Write_Low();
		}
	}
	return (Soft_IIC_Wait_ACK());
}

/**
 * @brief IIC读一个数据
 * @param  ACK:应答 NACK:不应答
 * @return 返回读到的数据
 */
uint8_t Soft_IIC_Recv_Byte(ACK_STATUS ack_sta)
{
	uint8_t data = 0, i;
	Soft_IIC_Input();
	IIC_SCL_H();
	IIC_Delay(IIC_DELAY_TIME);
	for (i = 0x80; i != 0; i >>= 1)
	{
		if (HAL_GPIO_ReadPin(IIC_SDA_PORT, IIC_SDA_PIN) == 1)
		{
			data |= i;
		}
		IIC_Delay(IIC_DELAY_TIME);
		IIC_SCL_L();
		IIC_Delay(IIC_DELAY_TIME);
		IIC_SCL_H();
		IIC_Delay(IIC_DELAY_TIME);
	}
	if (ack_sta == ACK)
	{
		Soft_IIC_ACK();
	}
	else
	{
		Soft_IIC_NACK();
	}
	return data;
}

uint8_t Soft_IIC_Read_Register(uint8_t deviceAddress, uint8_t regAddr, uint8_t *pData) {
	// Generate I2C start signal
	Soft_IIC_Start();

	// Write device address (shifted left by 1, indicating write operation)
	if (!Soft_IIC_Write_Byte((deviceAddress << 1) | 0x00)) {
		// Handle error if necessary
		Soft_IIC_Stop();
		return 0;
	}
	// Write register address
	if (!Soft_IIC_Write_Byte(regAddr)) {
		// Handle error if necessary
		Soft_IIC_Stop();
		return 0;
	}
	// Generate repeated start signal
	Soft_IIC_Start();
	// Write device address (shifted left by 1, indicating read operation)
	if (!Soft_IIC_Write_Byte((deviceAddress << 1) | 0x01)) {
		// Handle error if necessary
		Soft_IIC_Stop();
		return 0;
	}
	// Read data from the specified register
	*pData = Soft_IIC_Recv_Byte(NACK); // For the last byte, send NACK
	// Generate I2C stop signal
	Soft_IIC_Stop();
	return 1; // Successful read operation
}


uint8_t Soft_IIC_Write_Register(uint8_t deviceAddress, uint8_t regAddr, uint8_t data) {
	// Generate I2C start signal
	Soft_IIC_Start();

	// Write device address (shifted left by 1, indicating write operation)
	if (!Soft_IIC_Write_Byte((deviceAddress << 1) | 0x00)) {
		// Handle error if necessary
		Soft_IIC_Stop();
		return 0;
	}
	// Write register address
	if (!Soft_IIC_Write_Byte(regAddr)) {
		// Handle error if necessary
		Soft_IIC_Stop();
		return 0;
	}
	// Write data to the specified register
	if (!Soft_IIC_Write_Byte(data)) {
		// Handle error if necessary
		Soft_IIC_Stop();
		return 0;
	}
	// Generate I2C stop signal
	Soft_IIC_Stop();
	return 1; // Successful write operation
}
坑和笔记
Others
License:  CC BY 4.0
Share

Further Reading

Dec 23, 2024

其他笔记

EC800K AT连接移远云 配置过程 # 配置产品信息(初次连接需配置) AT+QIOTCFG="productinfo","pxxxxt","cDVTxxxxxxxxWGVB" # 连接开发者中心 AT+QIOTREG=1 # 查询当前连接状态(+QIOTSTATE: 8为正常) AT+QI

Jun 21, 2024

环形滤波算法

#include <stdio.h> #include <stdlib.h> #define BUFFER_SIZE 10 // 缓冲区大小 #define THRESHOLD 180

Jun 17, 2024

STM32 ADC采集的三种方式

采样周期 单个采集模式 ADC_Settings: 程序使用 uint16_t ADC_Read(

OLDER

ESP-ADF 播放MP3音乐

NEWER

Linux FFMPEG 哔哩哔哩推流直播

Recently Updated

  • ESP32(八) 简单的webserver
  • ESP32(七) NVS
  • ESP32(四) STA & AP
  • 多级菜单
  • ESP32(五) ESP32 OTA

Trending Tags

WCH Linux Elec freeRTOS STM ESP Flutter Others SwiftUI

Contents

©2025 松果工作室. Some rights reserved.

Using the Halo theme Chirpy