分享一个基于stm32好用的模拟i2c框架,并驱动sth3x源码

/**********************************************************************
*file:模拟IIC文件
*author:残梦
*date:2023.5.20
*note:V2.0
本文件模拟IIC主机,函数使用的都是simIIC_StructDef结构体实体,方便多个外设使用

**********************************************************************/
#include "IIC_simulate.h"

#define dxSET_PIN(GPIOx,Pin) GPIOx->BSRR = Pin //pin set 1
#define dxRESET_PIN(GPIOx,Pin) GPIOx->BSRR =  ((uint32_t)Pin << 16u) //pin set 0
#define dxREAD_PIN(GPIOx,Pin) (GPIOx->IDR & Pin)?1:0
#define dxSET_LEVEL_PIN(gpio,pin,level) if(level){dxSET_PIN(gpio,pin);}else dxRESET_PIN(gpio,pin)

//IO 方向设置
#define dxPIN_MODE_IN(gpio,pin)     {gpio->MODER &= ~(3<<(pin*2));gpio->MODER |= 0<<(pin*2);}//输入模式
#define dxPIN_MODE_OUT(gpio,pin)    {gpio->MODER &= ~(3<<(pin*2));gpio->MODER |= 1<<(pin*2);}//输出模式

#define dIIC_SCL(simIIC,x)      dxSET_LEVEL_PIN(simIIC.SCL_GPIO,simIIC.SCL_Pin,x)
#define dIIC_SDA(simIIC,x)      dxSET_LEVEL_PIN(simIIC.SDA_GPIO,simIIC.SDA_Pin,x)
#define dIIC_SDA_IN(simIIC)     dxPIN_MODE_IN(simIIC.SDA_GPIO,simIIC.SDA_Pin)
#define dIIC_SDA_OUT(simIIC)    dxPIN_MODE_OUT(simIIC.SDA_GPIO,simIIC.SDA_Pin)
#define dIIC_SDA_READ(simIIC)   dxREAD_PIN(simIIC.SDA_GPIO,simIIC.SDA_Pin)

static void simIIC_DelayUs(simIIC_StructDef simIIC);

/******************************
@function:模拟IIC延时函数
@param:
@return:void
@note:
******************************/
static void simIIC_DelayUs(simIIC_StructDef simIIC)
{
    if(simIIC.DelayMicrosecond == 0)return;
    simIIC.Delay_us(simIIC.DelayMicrosecond);
}

/******************************
@function:模拟IIC初始化
@param:simIIC--待初始化的simIIC_StructDef
@return:void
@note:
******************************/
void simIIC_Init(simIIC_StructDef simIIC)
{
    GPIO_InitTypeDef GPIO_InitStruct = {0};

    simIIC.GPIO_EnableColock();
    GPIO_InitStruct.Pin = simIIC.SDA_Pin;
    GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_OD;
    GPIO_InitStruct.Pull = GPIO_NOPULL;
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
    HAL_GPIO_Init(simIIC.SDA_GPIO, &GPIO_InitStruct);
    GPIO_InitStruct.Pin = simIIC.SCL_Pin;
    HAL_GPIO_Init(simIIC.SCL_GPIO, &GPIO_InitStruct);
    dIIC_SCL(simIIC,1);
    dIIC_SDA(simIIC,1);
}

/******************************
@function:IIC起始信号
@param:simIIC--待初始化的simIIC_StructDef
@return:void
@note:
******************************/
void simIIC_Start(simIIC_StructDef simIIC)
{
    dIIC_SDA_OUT(simIIC);
    dIIC_SDA(simIIC,1);
    dIIC_SCL(simIIC,1);
    simIIC_DelayUs(simIIC);
    dIIC_SDA(simIIC,0);
    simIIC_DelayUs(simIIC);
    dIIC_SCL(simIIC,0);
}

/******************************
@function:IIC结束信号
@param:simIIC--待初始化的simIIC_StructDef
@return:void
@note:
******************************/
void simIIC_Stop(simIIC_StructDef simIIC)
{
    dIIC_SCL(simIIC,0);    
    dIIC_SDA(simIIC,0);
    simIIC_DelayUs(simIIC);

    dIIC_SCL(simIIC,1);
    simIIC_DelayUs(simIIC);
    dIIC_SDA(simIIC,1);
    simIIC_DelayUs(simIIC);
}

/******************************
@function:IIC写数据(请勿其他使用)
@param: data--待发送的数据
@return:0--写成功,-1--写失败(从机地址不存在)
@note:不含IIC起始,IIC结束
******************************/
signed int simIIC_WriteOneByte(simIIC_StructDef simIIC,unsigned char data)
{
    unsigned char ack = 0,mask = 0;

    dIIC_SDA_OUT(simIIC);
    for(mask=0x80;mask != 0;mask >>= 1)
    {
        dIIC_SDA(simIIC,((mask & data) ? 1 : 0));
        simIIC_DelayUs(simIIC);
        dIIC_SCL(simIIC,1);
        simIIC_DelayUs(simIIC);
        dIIC_SCL(simIIC,0);
    }
    dIIC_SDA(simIIC,1);
    dIIC_SDA_IN(simIIC);
    simIIC_DelayUs(simIIC);
    dIIC_SCL(simIIC,1);
    simIIC_DelayUs(simIIC);
    ack = dIIC_SDA_READ(simIIC);
    dIIC_SCL(simIIC,0);
    dIIC_SDA_OUT(simIIC);

    return (ack?-1:0);
}

/******************************
@function:IIC读数据(请勿其他使用)
@param: ack:0--应答,1--不应答
@return:读取的数据
@note:不含IIC起始,IIC结束
******************************/
unsigned char simIIC_ReadOneByte(simIIC_StructDef simIIC,simIIC_xACK_EnumDef ack)
{
    unsigned char mask = 0,data = 0;

    dIIC_SDA(simIIC,1);
    dIIC_SDA_IN(simIIC);
    for(mask=0x80;mask != 0;mask >>= 1)
    {
        simIIC_DelayUs(simIIC);
        dIIC_SCL(simIIC,1);
        simIIC_DelayUs(simIIC);
        data |= ((dIIC_SDA_READ(simIIC))?mask:0);
        dIIC_SCL(simIIC,0);
    }
    dIIC_SDA_OUT(simIIC);
    dIIC_SDA(simIIC,ack);
    simIIC_DelayUs(simIIC);
    dIIC_SCL(simIIC,1);
    simIIC_DelayUs(simIIC);
    dIIC_SCL(simIIC,0);

    return data;
}

/******************************
@function:IIC写数据
@param: data--待发送的数据
        byteNum--数据字节数,不含地址字节
        address--从机地址
@return:0--写成功,-1--写失败(从机地址不存在|数据字节数0)
@note:
******************************/
signed int simIIC_Write(simIIC_StructDef simIIC,unsigned char *data,unsigned int byteNum,unsigned char address)
{
    unsigned int pos = 0;
    if(byteNum == 0)return -1;

    simIIC_Start(simIIC);
    if(simIIC_WriteOneByte(simIIC,address << 1|0x00) < 0){simIIC_Stop(simIIC);return -1;}//地址访问:写
    for(pos=0;pos < byteNum;pos++){if(simIIC_WriteOneByte(simIIC,data[pos]) < 0){simIIC_Stop(simIIC);return -1;}}
    simIIC_Stop(simIIC);
    return 0;
}


/**************************************
@function: IIC写数据到寄存器
@param: simIIC -- 模拟I2C结构体
        data -- 存储读取到的数据的数组
        byteNum -- 待读取数据字节数
        address -- 从机地址
        regAddr -- 寄存器地址
@return: 0 -- 读成功, -1 -- 读失败

**************************************/
signed int simIIC_Write_Reg(simIIC_StructDef simIIC, unsigned char *data, unsigned int byteNum, unsigned char address, unsigned char regAddr)
{
    unsigned int pos = 0;
    if (byteNum == 0) return -1;

    // 发送起始信号
    simIIC_Start(simIIC);

    // 发送从机地址和寄存器地址(写入模式)
    if (simIIC_WriteOneByte(simIIC, address<< 1|0x00) < 0) {
        simIIC_Stop(simIIC);
        return -1;
    }

    // 发送寄存器地址
    if (simIIC_WriteOneByte(simIIC, regAddr) < 0) {
        simIIC_Stop(simIIC);
        return -1;
    }

    // 写入数据到寄存器
    for (pos = 0; pos < byteNum; pos++) {
        if (simIIC_WriteOneByte(simIIC, data[pos]) < 0) {
            simIIC_Stop(simIIC);
            return -1;
        }
    }

    // 发送停止信号
    simIIC_Stop(simIIC);

    return 0;
}

/******************************
@function:IIC读数据
@param: data--读取到的数据
        byteNum--待读取数据字节数,不含地址字节
        address--从机地址
@return:0--读成功,-1--读失败(从机地址不存在|读取错误)
@note:
******************************/
signed int simIIC_Read(simIIC_StructDef simIIC,unsigned char *data,unsigned int byteNum,unsigned char address)
{
    unsigned int pos = 0;
    if(byteNum == 0)return -1;
    address = (address << 1) | 0x01;

    simIIC_Start(simIIC);
    if(simIIC_WriteOneByte(simIIC,address) < 0){simIIC_Stop(simIIC);return -1;}//地址访问:读
    for(pos=0;pos < byteNum;pos++){data[pos] = simIIC_ReadOneByte(simIIC,(pos == (byteNum-1))?simIIC_NACK:simIIC_ACK);}
    simIIC_Stop(simIIC);
    return 0;
}



/**************************************
@function: IIC读数据
@param: simIIC -- 模拟I2C结构体
        data -- 存储读取到的数据的数组
        byteNum -- 待读取数据字节数
        address -- 从机地址
        regAddr -- 寄存器地址
@return: 0 -- 读成功, -1 -- 读失败
@note: 读取指定寄存器地址的数据
**************************************/
signed int simIIC_Read_Reg(simIIC_StructDef simIIC, unsigned char *data, unsigned int byteNum, unsigned char address, unsigned char regAddr)
{
    unsigned int pos = 0;
    if (byteNum == 0) return -1;

    // 发送起始信号
    simIIC_Start(simIIC);

    // 发送从机地址和寄存器地址(写入模式)
    if (simIIC_WriteOneByte(simIIC, address<< 1|0x00) < 0) {
        simIIC_Stop(simIIC);
        return -1;
    }

    // 发送寄存器地址
    if (simIIC_WriteOneByte(simIIC, regAddr) < 0) {
        simIIC_Stop(simIIC);
        return -1;
    }

    // 重新启动通信,发送从机地址和读取操作位(读取模式)
    simIIC_Start(simIIC);
    if (simIIC_WriteOneByte(simIIC, address << 1|0x001) < 0) {
        simIIC_Stop(simIIC);
        return -1;
    }

    // 读取数据
    for (pos = 0; pos < byteNum; pos++) {
        data[pos] = simIIC_ReadOneByte(simIIC, (pos == (byteNum - 1)) ? simIIC_NACK : simIIC_ACK);
    }

    // 发送停止信号
    simIIC_Stop(simIIC);

    return 0;
}

以上是.c源文件 

头文件如下:

#ifndef _IIC_simulate_H_
#define _IIC_simulate_H_
#include "gpio.h"

typedef struct
{
    uint32_t SDA_Pin;//SDA引脚
    uint32_t SCL_Pin;//SCL引脚
    GPIO_TypeDef  *SDA_GPIO;//SDA端口
    GPIO_TypeDef  *SCL_GPIO;//SCL端口
    void (*GPIO_EnableColock)(void);//使能SDA、SCL的GPIO时钟函数,用户定义:eg:void SHT3x_GPIO_EnableColock(void)
    unsigned short int DelayMicrosecond;//IIC延时时间,频率接近1/(2*DelayMicrosecond) * 1000000;为0时不予延时;注意IIC实际延时会稍大于此时间,因为还有引脚翻转时间消耗
    void (*Delay_us)(unsigned int us);//微秒延时精确函数,用户定义:eg:void Delay_us(unsigned int us)
}simIIC_StructDef;

typedef enum
{
    simIIC_ACK = 0, //IIC应答
    simIIC_NACK = 1 //IIC不应答
}simIIC_xACK_EnumDef;

void simIIC_Init(simIIC_StructDef simIIC);
void simIIC_Start(simIIC_StructDef simIIC);
void simIIC_Stop(simIIC_StructDef simIIC);
signed int simIIC_WriteOneByte(simIIC_StructDef simIIC,unsigned char data);
unsigned char simIIC_ReadOneByte(simIIC_StructDef simIIC,simIIC_xACK_EnumDef ack);
signed int simIIC_Write(simIIC_StructDef simIIC,unsigned char *data,unsigned int byteNum,unsigned char address);
signed int simIIC_Read(simIIC_StructDef simIIC,unsigned char *data,unsigned int byteNum,unsigned char address);
signed int simIIC_Read_Reg(simIIC_StructDef simIIC, unsigned char *data, unsigned int byteNum, unsigned char address, unsigned char regAddr);
#endif

驱动I2C传感器如sht35

如下放出了.c和.h文件,调用也是比较简单,在main.c直接调用ReadSHT3x(&Hum,&Temp);即可!

/**********************************************************************
*file:sht3x温湿度传感器驱动
*author:残梦
*date:2023.5.22
*note:
**********************************************************************/
#include "sht3x_Driver.h"
#include "IIC_simulate.h"
#include "Delay_Driver.h"
#include "stdio.h"
#define dSHT3X_SDA_CLOCK  __HAL_RCC_GPIOB_CLK_ENABLE()
#define dSHT3X_SDA_PORT   GPIOB
#define dSHT3X_SDA_PIN    GPIO_PIN_6
#define dSHT3X_SCL_CLOCK  __HAL_RCC_GPIOB_CLK_ENABLE()
#define dSHT3X_SCL_PORT   GPIOB
#define dSHT3X_SCL_PIN    GPIO_PIN_7

#define dSHT3X_IIC_ADDRESS 0x44

static simIIC_StructDef SHT3X_IIC;

static uint8_t SHT3x_CRC_CAL(uint16_t DAT);
static int SHT3x_WriteByte(uint8_t MSB,uint8_t LSB);
static void SHT3x_SoftReset(void);



/******************************
@function:发送两个字节数据
@param:MSB 高8位;LSB 低8位
@return:-1--写失败,0--成功
@note:
******************************/
static int SHT3x_WriteByte(uint8_t MSB,uint8_t LSB)
{
  uint8_t data[2] = {0};
  data[0] = MSB;data[1] = LSB;
  return (simIIC_Write(SHT3X_IIC,data,2,dSHT3X_IIC_ADDRESS) < 0) ? -1 : 0;
}

/*软件复位SHT3x*/
static void SHT3x_SoftReset(void)
{
  if( SHT3x_WriteByte(0x30,0xA2)!=0)
  {
		printf("sht3x reset error!\r\n");
	}	 //重新初始化SHT3x
}

/******************************
@function:sht3x 初始化IIC的SDA|SCL的GPIO时钟使能函数
@param:void
@return:void
@note:
******************************/
void SHT3x_GPIO_EnableColock(void)
{
  dSHT3X_SDA_CLOCK;
  dSHT3X_SCL_CLOCK;
}

/******************************
@function:sht3x初始化
@param:void
@return:-1--初始化失败,0--成功
@note:设定周期数据采集模式(每秒10次,Medium Repeatability)
******************************/
int SHT3x_Init(void)
{
  int xreturn = 0;

  SHT3X_IIC.SDA_Pin = dSHT3X_SDA_PIN;
  SHT3X_IIC.SCL_Pin = dSHT3X_SCL_PIN;
  SHT3X_IIC.SDA_GPIO = dSHT3X_SDA_PORT;
  SHT3X_IIC.SCL_GPIO = dSHT3X_SCL_PORT;
  SHT3X_IIC.GPIO_EnableColock = &SHT3x_GPIO_EnableColock;
  SHT3X_IIC.DelayMicrosecond = 1;//IIC速度:500KHz
  SHT3X_IIC.Delay_us = &Delay_us;
  simIIC_Init(SHT3X_IIC);
  SHT3x_SoftReset();
  xreturn = SHT3x_WriteByte(0X27,0X21);//周期数据采集模式(每秒10次,Medium Repeatability)

  float Hum = 0.0f,Temp = 0.0f;
  ReadSHT3x(&Hum,&Temp);
  return xreturn;
}

/******************************
@function:读取SHT3x数据
@param:*Hum 湿度,*Temp 温度
@return:-1--失败,0--成功
@note:读取周期小于设定周期时,读取会错误;当前设定周期100ms,首次读取会错误
******************************/
int ReadSHT3x(float *Hum,float *Temp)
{
  uint16_t HumData = 0,TempData = 0,HumCRC = 0,TempCRC = 0;//声明变量存放读取的数据
  uint8_t data[6] = {0};

  if(SHT3x_WriteByte(0XE0,0X00) < 0) return -1;   //发送指令,获取数据,周期数据采集模式用
  if(simIIC_Read(SHT3X_IIC,data,6,dSHT3X_IIC_ADDRESS) < 0)return -1;
  TempData = ((uint16_t)data[0] << 8) | (uint16_t)data[1];//温度高8位数据|温度低8位数据
  TempCRC = data[2];//温度CRC校验数据
  HumData = ((uint16_t)data[3] << 8) | (uint16_t)data[4];//湿度高8位数据|湿度低8位数据
  HumCRC = data[5];//湿度CRC校验数据

  if((SHT3x_CRC_CAL(HumData) != HumCRC) || (SHT3x_CRC_CAL(TempData) !=TempCRC)) return -1;//对接收到数据进行CRC校验
  *Hum = (float)HumData*100.0f/(65536-1);           //将接收的16位二进制数据转换为10进制湿度数据
  *Temp = (float)TempData*175.0f/(65536-1)-45.0f;   //将接收的16位二进制数据转换为10进制温度数据
  return 0;
}
/******************************
@function:CRC校验,CRC多项式为:x^8+x^5+x^4+1,即0x31
@param:DAT 要校验的数据
@return:校验码
@note:
******************************/
static uint8_t SHT3x_CRC_CAL(uint16_t DAT)
{
	uint8_t i,t,temp;
	uint8_t CRC_BYTE;

	CRC_BYTE = 0xFF;
	temp = (DAT>>8) & 0xFF;

	for(t = 0; t < 2; t++)
	{
		CRC_BYTE ^= temp;
		for(i = 0;i < 8;i ++)
		{
			if(CRC_BYTE & 0x80)
			{
				CRC_BYTE <<= 1;
				CRC_BYTE ^= 0x31;
			}
			else
			{
				CRC_BYTE <<= 1;
			}
		}
		if(t == 0)
		{
			temp = DAT & 0xFF;
		}
	}
	return CRC_BYTE;
}
#ifndef _sht3x_Driver_H_
#define _sht3x_Driver_H_
#include "main.h"

int SHT3x_Init(void);
int ReadSHT3x(float *Hum,float *Temp);

#endif

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: STM32模拟I2C驱动OLED可以通过软件模拟I2C总线来实现。使用STM32的GPIO引脚模拟I2C通信的时钟线(SCL)和数据线(SDA),通过编程控制引脚的输入输出状态来模拟I2C总线的通信。 首先,需要在STM32的开发环境中配置相关的GPIO引脚作为模拟I2C总线的SCL和SDA引脚。然后,在代码中初始化这些引脚的状态,并设置为输出模式。 在实现模拟I2C驱动之前,我们需要了解OLED的通信协议。通常,OLED使用I2C协议进行通信,包括启动信号、地址字节、数据字节等。根据OLED的数据手册,我们可以编写相应的代码来模拟这些通信步骤。 在驱动程序中,需要实现I2C的启动、发送字节、接收字节、停止等操作。这些操作可以通过控制SCL和SDA引脚的电平来实现。例如,启动信号可以通过将SCL引脚置高,然后将SDA引脚从高电平切换到低电平来发送。 然后,我们可以编写一系列函数来实现模拟I2C通信。例如,写入数据的函数可以使用引脚的输入输出状态来模拟发送数据字节,读取数据的函数可以根据引脚电平模拟接收数据字节。这些函数可以在程序中被调用以实现与OLED的通信。 最后,我们可以将模拟I2C驱动与OLED的显示代码结合起来,通过模拟I2C总线来控制OLED的初始化、显示内容等操作。这样,我们就能够使用STM32驱动模拟I2C的OLED显示屏了。 总之,通过在STM32模拟I2C总线的通信,我们可以实现驱动OLED的功能。这需要对STM32的GPIO引脚进行适当配置和编程,以模拟I2C通信的时序和协议。 ### 回答2: STM32是一款广泛应用于嵌入式系统开发的微控制器芯片。而模拟I2C驱动是一种基于软件实现的I2C通信协议的方式,可以用来驱动OLED显示屏。 在使用STM32模拟I2C驱动OLED时,首先需要配置GPIO引脚作为模拟I2C的SCL和SDA线。通常,SCL线连接到芯片的时钟输入引脚,SDA线连接到芯片的数据输入/输出引脚。 然后,需要实现一些软件函数来模拟I2C通信。对于起始信号的生成,可以通过将SCL和SDA引脚置为高电平,再将SDA引脚置为低电平来实现。发送数据时,将数据按照I2C协议的要求进行处理,依次将每个数据位发送出去,并等待接收端返回的ACK信号。接收数据时,根据I2C协议的要求,接收方会返回一个ACK信号,将接收到的数据保存起来。 在驱动OLED显示屏时,可以根据显示屏的数据手册,实现针对OLED的控制命令或数据的发送函数。通过模拟I2C驱动,可以将这些命令或数据发送到OLED中,实现对显示屏内容的控制。 需要注意的是,模拟I2C驱动与硬件I2C驱动相比,由于需要通过软件对时序进行精确的控制,所以在速度和准确度上可能会有所降低。因此,在实际应用中,需根据具体要求进行优化。 总之,通过STM32模拟I2C驱动OLED显示屏,可以实现对显示内容的控制和数据的发送。这种软件实现的方式,适用于一些不支持硬件I2C接口的情况,并且灵活度较高,可以满足不同应用的需求。 ### 回答3: STM32是一种微控制器系列,它具有强大的功能和广泛的应用领域。在实现模拟I2C驱动OLED时,我们可以使用STM32的GPIO和软件I2C库。 首先,我们需要配置STM32的GPIO引脚,用于连接到OLED的SCL(时钟)和SDA(数据)线。我们可以使用CubeMX或编写代码手动配置这些引脚。 接下来,我们需要实现软件I2C的功能。软件I2C是通过控制GPIO引脚的电平变化来模拟硬件I2C的数据传输。可以在STM32的库中找到适用于软件I2C的函数和宏。 在驱动OLED之前,我们需要确定OLED的通信协议和寄存器。通常,OLED使用I2C作为通信接口,并具有一组寄存器用于控制显示内容和属性。 为了驱动OLED,我们需要编写一些函数来发送和接收数据。发送数据时,我们将数据字节写入SDA引脚,并在SCL引脚上产生时钟脉冲。接收数据时,我们将SDA引脚配置为输入,并在SCL引脚上读取时钟脉冲。 最后,我们可以编写一些函数来初始化OLED并控制其显示内容。这些函数将使用软件I2C发送特定的命令和数据字节来设置OLED的显示模式、显示位置和显示内容。 总结起来,实现STM32模拟I2C驱动OLED需要配置GPIO引脚、实现软件I2C功能,编写发送和接收数据的函数,并控制OLED的显示内容。这样,我们就能够通过STM32来控制并显示内容在OLED上。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值