仿照Arduino的SoftI2C库写一个适合STM32的软件I2C库
仿照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函数读出数据