仿照Arduino的SoftI2C库写一个适合STM32的软件I2C库

发布时间 2023-12-25 22:54:19作者: HAOstudio

仿照Arduino的SoftI2C库写一个适合STM32的软件I2C库

Arduino的SoftI2C库的相关链接:github链接

同时我写了一篇关于IIC通信原理的博客,链接:IIC通信的相关知识

当使用了Arduino的SoftI2C库后,发现这个库非常好用,用来软件模拟IIC通信。也想要在STM32上使用这个库,所以就有了移植SoftI2C库到STM32上的想法。

移植了相关代码,可以很方便的在HAL库基础上使用该库。

相关源代码如下:

SoftI2C.c文件:

#include "SoftI2C.h"

static void setSdaMode_OUT(SoftI2C_HandleTypeDef *SoftI2C_s_ptr);
static void setSclMode_OUT(SoftI2C_HandleTypeDef *SoftI2C_s_ptr);
static void setSdaMode_IN(SoftI2C_HandleTypeDef *SoftI2C_s_ptr);

static void setSdaLevel(SoftI2C_HandleTypeDef *SoftI2C_s_ptr, uint8_t level);
static void setSclLevel(SoftI2C_HandleTypeDef *SoftI2C_s_ptr, uint8_t level);

static bool i2cInit(SoftI2C_HandleTypeDef *SoftI2C_s_ptr);
static bool i2cStart(SoftI2C_HandleTypeDef *SoftI2C_s_ptr, uint8_t addr);
//static bool i2cStartWait(SoftI2C_HandleTypeDef *SoftI2C_s_ptr, uint8_t addr); //not used
static bool i2cRepStart(SoftI2C_HandleTypeDef *SoftI2C_s_ptr, uint8_t addr);
static void i2cStop(SoftI2C_HandleTypeDef *SoftI2C_s_ptr);
static bool i2cWrite(SoftI2C_HandleTypeDef *SoftI2C_s_ptr, uint8_t value);
static uint8_t i2cRead(SoftI2C_HandleTypeDef *SoftI2C_s_ptr, bool last);


// soft iic init func
void SoftI2C_begin(SoftI2C_HandleTypeDef *SoftI2C_s_ptr, GPIO_TypeDef * sda_io_port, uint16_t sda_io_pin, GPIO_TypeDef * scl_io_port, uint16_t scl_io_pin, bool pullup)
{
	SoftI2C_s_ptr->sda_io_port = sda_io_port;
	SoftI2C_s_ptr->sda_io_pin = sda_io_pin;
	SoftI2C_s_ptr->scl_io_port = scl_io_port;
	SoftI2C_s_ptr->scl_io_pin = scl_io_pin;
	SoftI2C_s_ptr->pullup = pullup;
	
	SoftI2C_s_ptr->rxBufferIndex = 0;
	SoftI2C_s_ptr->rxBufferLength = 0;
	SoftI2C_s_ptr->error = 0;
	SoftI2C_s_ptr->isTransmitting = false;
	
	i2cInit(SoftI2C_s_ptr);
	
}

// soft iic end func
void SoftI2C_end(SoftI2C_HandleTypeDef *SoftI2C_s_ptr)
{}


void SoftI2C_beginTransmission(SoftI2C_HandleTypeDef *SoftI2C_s_ptr, uint8_t address)
{
	if(SoftI2C_s_ptr->isTransmitting)
	{
    SoftI2C_s_ptr->error = (i2cRepStart(SoftI2C_s_ptr ,(address<<1)|I2C_WRITE) ? 0 : 2);
  }
	else
	{
    SoftI2C_s_ptr->error = (i2cStart(SoftI2C_s_ptr ,(address<<1)|I2C_WRITE) ? 0 : 2);
  }
    // indicate that we are isTransmitting
  SoftI2C_s_ptr->isTransmitting = 1;
}


uint8_t SoftI2C_endTransmission(SoftI2C_HandleTypeDef *SoftI2C_s_ptr, uint8_t sendStop)
{
	uint8_t transferError = SoftI2C_s_ptr->error;
  if(sendStop)
	{
    i2cStop(SoftI2C_s_ptr);
    SoftI2C_s_ptr->isTransmitting = 0;
  }
  SoftI2C_s_ptr->error = 0;
  return transferError;
}


size_t SoftI2C_writeByte(SoftI2C_HandleTypeDef *SoftI2C_s_ptr, uint8_t data)
{
	if(i2cWrite(SoftI2C_s_ptr, data))
	{
    return 1;
  }
	else
	{
    if(SoftI2C_s_ptr->error == 0)
			SoftI2C_s_ptr->error = 3;
    return 0;
  }
}


size_t SoftI2C_writeBytes(SoftI2C_HandleTypeDef *SoftI2C_s_ptr, const uint8_t *data, size_t quantity)
{
	size_t progress = 0;
	
  for(size_t i = 0; i < quantity; ++i)
	{
    progress += SoftI2C_writeByte(SoftI2C_s_ptr, data[i]);
  }
  return progress;
}


uint8_t SoftI2C_requestFrom(SoftI2C_HandleTypeDef *SoftI2C_s_ptr, uint8_t address, uint8_t quantity,
                    uint32_t iaddress, uint8_t isize, uint8_t sendStop)
{
	uint8_t localerror = 0;
	uint8_t count;
	
	SoftI2C_s_ptr->error = 0;
	if(isize > 0)
	{
		SoftI2C_beginTransmission(SoftI2C_s_ptr, address);
		// the maximum size of internal address is 3 bytes
		if (isize > 3){
			isize = 3;
		}
		// write internal register address - most significant byte first
		while (isize-- > 0) {
			SoftI2C_writeByte(SoftI2C_s_ptr, (uint8_t)(iaddress >> (isize*8)));
		}
		SoftI2C_endTransmission(SoftI2C_s_ptr, false);
	}
	
	// clamp to buffer length
	if(quantity > I2C_BUFFER_LENGTH)
	{
		quantity = I2C_BUFFER_LENGTH;
	}
	if(SoftI2C_s_ptr->isTransmitting)
	{
		localerror = !i2cRepStart(SoftI2C_s_ptr, (address<<1) | I2C_READ);
	}
	else
	{
		localerror = !i2cStart(SoftI2C_s_ptr, (address<<1) | I2C_READ);
	}
	
	if(SoftI2C_s_ptr->error == 0 && localerror)
		SoftI2C_s_ptr->error = 2;
	// perform blocking read into buffer
	for(count=0; count < quantity; count++)
	{
		SoftI2C_s_ptr->rxBuffer[count] = i2cRead(SoftI2C_s_ptr, count == quantity-1);
	}
	// set rx buffer iterator vars
	SoftI2C_s_ptr->rxBufferIndex = 0;
	SoftI2C_s_ptr->rxBufferLength = SoftI2C_s_ptr->error ? 0 : quantity;
	if(sendStop) 
	{
		SoftI2C_s_ptr->isTransmitting = 0;
		i2cStop(SoftI2C_s_ptr);
	}
	return SoftI2C_s_ptr->rxBufferLength;
}

int SoftI2C_available(SoftI2C_HandleTypeDef *SoftI2C_s_ptr)
{
	return SoftI2C_s_ptr->rxBufferLength - SoftI2C_s_ptr->rxBufferIndex;
}

int SoftI2C_read(SoftI2C_HandleTypeDef *SoftI2C_s_ptr)
{
	int value = -1;
  if(SoftI2C_s_ptr->rxBufferIndex < SoftI2C_s_ptr->rxBufferLength)
	{
    value = SoftI2C_s_ptr->rxBuffer[SoftI2C_s_ptr->rxBufferIndex];
    ++(SoftI2C_s_ptr->rxBufferIndex);
  }
  return value;
}

int SoftI2C_peek(SoftI2C_HandleTypeDef *SoftI2C_s_ptr)
{
	int value = -1;

  if(SoftI2C_s_ptr->rxBufferIndex < SoftI2C_s_ptr->rxBufferLength)
	{
    value = SoftI2C_s_ptr->rxBuffer[SoftI2C_s_ptr->rxBufferIndex];
  }
  return value;
}


void SoftI2C_flush(SoftI2C_HandleTypeDef *SoftI2C_s_ptr)
{}



/*********************************static func*********************************/
static void setSdaMode_OUT(SoftI2C_HandleTypeDef *SoftI2C_s_ptr)
{
	GPIO_InitTypeDef GPIO_InitStruct = {0};
	
	HAL_GPIO_WritePin(SoftI2C_s_ptr->sda_io_port, SoftI2C_s_ptr->sda_io_pin, GPIO_PIN_SET);
	
	GPIO_InitStruct.Pin = SoftI2C_s_ptr->sda_io_pin;
  GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
  if(SoftI2C_s_ptr->pullup == true)
		GPIO_InitStruct.Pull = GPIO_PULLUP;
	else
		GPIO_InitStruct.Pull = GPIO_NOPULL;
  GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
  HAL_GPIO_Init(SoftI2C_s_ptr->sda_io_port, &GPIO_InitStruct);
}

static void setSdaMode_IN(SoftI2C_HandleTypeDef *SoftI2C_s_ptr)
{
	GPIO_InitTypeDef GPIO_InitStruct = {0};
	
	HAL_GPIO_WritePin(SoftI2C_s_ptr->sda_io_port, SoftI2C_s_ptr->sda_io_pin, GPIO_PIN_SET);
	
	GPIO_InitStruct.Pin = SoftI2C_s_ptr->sda_io_pin;
  GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
  if(SoftI2C_s_ptr->pullup == true)
		GPIO_InitStruct.Pull = GPIO_PULLUP;
	else
		GPIO_InitStruct.Pull = GPIO_NOPULL;
  GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
  HAL_GPIO_Init(SoftI2C_s_ptr->sda_io_port, &GPIO_InitStruct);
}

static void setSclMode_OUT(SoftI2C_HandleTypeDef *SoftI2C_s_ptr)
{
	GPIO_InitTypeDef GPIO_InitStruct = {0};
	
	HAL_GPIO_WritePin(SoftI2C_s_ptr->scl_io_port, SoftI2C_s_ptr->scl_io_pin, GPIO_PIN_SET);
	
	GPIO_InitStruct.Pin = SoftI2C_s_ptr->scl_io_pin;
  GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
  if(SoftI2C_s_ptr->pullup == true)
		GPIO_InitStruct.Pull = GPIO_PULLUP;
	else
		GPIO_InitStruct.Pull = GPIO_NOPULL;
  GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
  HAL_GPIO_Init(SoftI2C_s_ptr->scl_io_port, &GPIO_InitStruct);
}

void setSdaLevel(SoftI2C_HandleTypeDef *SoftI2C_s_ptr, uint8_t level)
{
	if(level)
		HAL_GPIO_WritePin(SoftI2C_s_ptr->sda_io_port,SoftI2C_s_ptr->sda_io_pin, GPIO_PIN_SET);
	else
		HAL_GPIO_WritePin(SoftI2C_s_ptr->sda_io_port,SoftI2C_s_ptr->sda_io_pin, GPIO_PIN_RESET);
}

void setSclLevel(SoftI2C_HandleTypeDef *SoftI2C_s_ptr, uint8_t level)
{
	if(level)
		HAL_GPIO_WritePin(SoftI2C_s_ptr->scl_io_port,SoftI2C_s_ptr->scl_io_pin, GPIO_PIN_SET);
	else
		HAL_GPIO_WritePin(SoftI2C_s_ptr->scl_io_port,SoftI2C_s_ptr->scl_io_pin, GPIO_PIN_RESET);
}

static bool i2cInit(SoftI2C_HandleTypeDef *SoftI2C_s_ptr)
{	
	//sda
//	__HAL_RCC_GPIOB_CLK_ENABLE();
	if(SoftI2C_s_ptr->sda_io_port == GPIOA)
		__HAL_RCC_GPIOA_CLK_ENABLE();
	else if(SoftI2C_s_ptr->sda_io_port == GPIOB)
		__HAL_RCC_GPIOB_CLK_ENABLE();
	else if(SoftI2C_s_ptr->sda_io_port == GPIOC)
		__HAL_RCC_GPIOC_CLK_ENABLE();
	else if(SoftI2C_s_ptr->sda_io_port == GPIOD)
		__HAL_RCC_GPIOD_CLK_ENABLE();
	else if(SoftI2C_s_ptr->sda_io_port == GPIOE)
		__HAL_RCC_GPIOE_CLK_ENABLE();
	setSdaMode_OUT(SoftI2C_s_ptr);
	
	//scl
//	__HAL_RCC_GPIOA_CLK_ENABLE();
	if(SoftI2C_s_ptr->scl_io_port == GPIOA)
		__HAL_RCC_GPIOA_CLK_ENABLE();
	else if(SoftI2C_s_ptr->scl_io_port == GPIOB)
		__HAL_RCC_GPIOB_CLK_ENABLE();
	else if(SoftI2C_s_ptr->scl_io_port == GPIOC)
		__HAL_RCC_GPIOC_CLK_ENABLE();
	else if(SoftI2C_s_ptr->scl_io_port == GPIOD)
		__HAL_RCC_GPIOD_CLK_ENABLE();
	else if(SoftI2C_s_ptr->scl_io_port == GPIOE)
		__HAL_RCC_GPIOE_CLK_ENABLE();
	setSclMode_OUT(SoftI2C_s_ptr);
	
	setSdaLevel(SoftI2C_s_ptr, 1);//setPinHigh(sda);
	setSclLevel(SoftI2C_s_ptr, 1);//setPinHigh(scl);
	return true;
}

static bool i2cStart(SoftI2C_HandleTypeDef *SoftI2C_s_ptr, uint8_t addr)
{
	setSdaMode_OUT(SoftI2C_s_ptr);

	setSdaLevel(SoftI2C_s_ptr, 0);//setPinLow(sda);
	siic_delay_us(DELAY);
	setSclLevel(SoftI2C_s_ptr, 0);//setPinLow(scl);
	return i2cWrite(SoftI2C_s_ptr, addr);
}

//static bool i2cStartWait(SoftI2C_HandleTypeDef *SoftI2C_s_ptr, uint8_t addr)
//{
//	uint32_t retry = I2C_MAXWAIT;
//	while(!i2cStart(SoftI2C_s_ptr, addr))
//	{
//    i2cStop(SoftI2C_s_ptr);
//    if (--retry == 0) 
//			return false;
//  }
//  return true;
//}

static bool i2cRepStart(SoftI2C_HandleTypeDef *SoftI2C_s_ptr,uint8_t addr)
{
	setSdaLevel(SoftI2C_s_ptr, 1);//setPinHigh(sda);
  setSclLevel(SoftI2C_s_ptr, 1);//setPinHigh(scl);
	siic_delay_us(DELAY);
	return i2cStart(SoftI2C_s_ptr, addr);
}

static void i2cStop(SoftI2C_HandleTypeDef *SoftI2C_s_ptr)
{
	setSdaMode_OUT(SoftI2C_s_ptr);
	
	setSclLevel(SoftI2C_s_ptr, 0);
	setSdaLevel(SoftI2C_s_ptr, 0);
  siic_delay_us(DELAY);
  setSclLevel(SoftI2C_s_ptr, 1);
  siic_delay_us(DELAY);
  setSdaLevel(SoftI2C_s_ptr, 1);
  siic_delay_us(DELAY);
}
	
static bool i2cWrite(SoftI2C_HandleTypeDef *SoftI2C_s_ptr, uint8_t value)
{
	uint8_t curr, ack;
	
	siic_delay_us(DELAY/2);
	setSdaMode_OUT(SoftI2C_s_ptr);
	for(curr = 0X80; curr != 0; curr >>= 1)
	{
    if(curr & value)
			setSdaLevel(SoftI2C_s_ptr, 1);
		else
			setSdaLevel(SoftI2C_s_ptr, 0);
		
		siic_delay_us(DELAY/2);
    setSclLevel(SoftI2C_s_ptr, 1);
    siic_delay_us(DELAY);
		setSclLevel(SoftI2C_s_ptr, 0);
		siic_delay_us(DELAY/2);
  }
	//ack
	setSdaMode_IN(SoftI2C_s_ptr);
	siic_delay_us(1);
	ack = HAL_GPIO_ReadPin(SoftI2C_s_ptr->sda_io_port,SoftI2C_s_ptr->sda_io_pin);  
  setSclLevel(SoftI2C_s_ptr, 1);
	siic_delay_us(DELAY);
	setSclLevel(SoftI2C_s_ptr, 0);
  return ack == 0;
}

static uint8_t i2cRead(SoftI2C_HandleTypeDef *SoftI2C_s_ptr, bool last)
{
	uint8_t receivedByte  = 0;
	
	setSdaMode_IN(SoftI2C_s_ptr);
	
  for (uint8_t i = 0; i < 8; i++)
	{
    receivedByte <<= 1;
    siic_delay_us(DELAY);
    setSclLevel(SoftI2C_s_ptr, 1);
    if (HAL_GPIO_ReadPin(SoftI2C_s_ptr->sda_io_port,SoftI2C_s_ptr->sda_io_pin))
			receivedByte |= 1;
    setSclLevel(SoftI2C_s_ptr, 0);
  }
	
	setSdaMode_OUT(SoftI2C_s_ptr);
  if(last)
		setSdaLevel(SoftI2C_s_ptr, 1);
	else
		setSdaLevel(SoftI2C_s_ptr, 0);
  
	setSclLevel(SoftI2C_s_ptr, 1);
  siic_delay_us(DELAY/2);
  setSclLevel(SoftI2C_s_ptr, 0);
  siic_delay_us(DELAY/2);
  setSdaLevel(SoftI2C_s_ptr, 0);
  
	return receivedByte ;
}

//delay us function
void siic_delay_us(uint32_t us)
{
	uint32_t load_before = SysTick->LOAD;
	uint32_t tmp;
	
	if(us <= 0)
		return;
	
	SysTick->CTRL &= ~SysTick_CTRL_TICKINT_Msk;		//关闭滴答定时器中断
	
	SysTick->LOAD = us*(load_before+1)/1000 - 1;	//设置重装载值 61
	SysTick->VAL = 0x00;													//将定时器归零
	
	//这里通过循环判断定时器的状态位值来确认定时器是否已归零
	do{
		tmp = SysTick->CTRL;											//获取定时器的状态值
	}while(tmp & 0x01 && !(tmp & (1 << 16)));
	SysTick->CTRL &= ~SysTick_CTRL_ENABLE_Msk;	//关闭定时器
	
	//恢复
	SysTick->LOAD = load_before;								//设置重装载值
	SysTick->VAL = 0x00;												//将定时器归零
	SysTick->CTRL |= SysTick_CTRL_TICKINT_Msk;	//开启滴答定时器中断
	SysTick->CTRL |= SysTick_CTRL_ENABLE_Msk;		//开启定时器
}

SoftI2C.h文件:

#ifndef __SOFTI2C_H_
#define __SOFTI2C_H_

#include <stdint.h>
#include <stdbool.h>

#include "main.h"



//define
#ifndef I2C_BUFFER_LENGTH
  #define I2C_BUFFER_LENGTH 32
#endif

#define WIRE_HAS_END 	    1
#define I2C_READ 			1
#define I2C_WRITE 		    0
#define DELAY 				4 		//delay us
#define I2C_MAXWAIT 	    5000

typedef struct
{
	GPIO_TypeDef * sda_io_port;
  uint16_t sda_io_pin;
  GPIO_TypeDef * scl_io_port;
	uint16_t scl_io_pin;
  bool pullup;
	
	uint8_t rxBuffer[I2C_BUFFER_LENGTH];
  uint8_t rxBufferIndex;
  uint8_t rxBufferLength;
  uint8_t isTransmitting;
  uint8_t error;
} SoftI2C_HandleTypeDef;


void SoftI2C_begin(SoftI2C_HandleTypeDef *SoftI2C_s_ptr, GPIO_TypeDef * sda_io_port, uint16_t sda_io_pin, GPIO_TypeDef * scl_io_port, uint16_t scl_io_pin, bool pullup);
void SoftI2C_end(SoftI2C_HandleTypeDef *SoftI2C_s_ptr);

void SoftI2C_beginTransmission(SoftI2C_HandleTypeDef *SoftI2C_s_ptr, uint8_t address);
uint8_t SoftI2C_endTransmission(SoftI2C_HandleTypeDef *SoftI2C_s_ptr, uint8_t sendStop);

size_t SoftI2C_writeByte(SoftI2C_HandleTypeDef *SoftI2C_s_ptr, uint8_t data);
size_t SoftI2C_writeBytes(SoftI2C_HandleTypeDef *SoftI2C_s_ptr, const uint8_t *data, size_t quantity);

uint8_t SoftI2C_requestFrom(SoftI2C_HandleTypeDef *SoftI2C_s_ptr, uint8_t address, uint8_t quantity,
                    uint32_t iaddress, uint8_t isize, uint8_t sendStop);

int SoftI2C_available(SoftI2C_HandleTypeDef *SoftI2C_s_ptr);
int SoftI2C_read(SoftI2C_HandleTypeDef *SoftI2C_s_ptr);
int SoftI2C_peek(SoftI2C_HandleTypeDef *SoftI2C_s_ptr);
void SoftI2C_flush(SoftI2C_HandleTypeDef *SoftI2C_s_ptr);

void siic_delay_us(uint32_t us);

#endif

使用实例:

/*首先要向工程中加入相关源文件*/

#include "SoftI2C.h"    //包含头文件

SoftI2C_HandleTypeDef hsiic1;   //定义软件iic结构体对象

SoftI2C_begin(&hsiic1, GPIOB, GPIO_PIN_9, GPIOA, GPIO_PIN_15, true); //初始化,sda使用PB9,scl使用PA15,true代表使能引脚内部上拉电阻

//写一个byte数据
SoftI2C_beginTransmission(&hsiic1, 0x36);   //1 向器件地址为0x36的从设备通信
SoftI2C_writeByte(&hsiic1, 0x10);           //2 向寄存器地址0x10写入数据
SoftI2C_writeByte(&hsiic1, 0x3F);           //3 写如数据0x3f
SoftI2C_endTransmission(&hsiic1, true);     //4 结束通信,发送停止信号

//读一个byte数据
SoftI2C_beginTransmission(&hsiic1, 0x36);           //1 向器件地址为0x36的从设备通信
SoftI2C_writeByte(&hsiic1, 0x10);                   //2 从寄存器地址0x10读数据
SoftI2C_requestFrom(&hsiic1, 0x36, 1, 0, 0, true);  //3 请求一个字节的数据,并且停止通信
uint8_t mdata = SoftI2C_read(&hsiic1);              //4 使用read函数读出数据