STM32裸机软件I2C驱动代码

前言

之前一个STM32项目使用的I2C的驱动代码,基于STM32标准库,当然,改为目前流行的HAL库,也不难。

 

代码

i2c_drv.h

#ifndef __I2C_DRV_H__
#define __I2C_DRV_H__

#include "stm32f10x.h"

#define I2C_ACK                 0 //应答
#define I2C_NOACK               1 //非应答

/* -- GPIO-I2C宏定义 ----*/
#define I2C_SCL_RCC             RCC_APB2Periph_GPIOB
#define I2C_SCL_GPIO_PORT       GPIOB
#define I2C_SCL_GPIO_PINS       GPIO_Pin_8

#define I2C_SDA_RCC             RCC_APB2Periph_GPIOB
#define I2C_SDA_GPIO_PORT       GPIOB
#define I2C_SDA_GPIO_PINS       GPIO_Pin_9

#define I2C_SCL_LOW             GPIO_ResetBits(I2C_SCL_GPIO_PORT, I2C_SCL_GPIO_PINS)
#define I2C_SCL_HIGH            GPIO_SetBits(I2C_SCL_GPIO_PORT, I2C_SCL_GPIO_PINS)
#define I2C_SDA_LOW             GPIO_ResetBits(I2C_SDA_GPIO_PORT, I2C_SDA_GPIO_PINS)
#define I2C_SDA_HIGH            GPIO_SetBits(I2C_SDA_GPIO_PORT, I2C_SDA_GPIO_PINS)
#define I2C_SDA_READ            GPIO_ReadInputDataBit(I2C_SDA_GPIO_PORT, I2C_SDA_GPIO_PINS)

void I2C_Initializes(void);
void I2C_GPIO_DeInit(void);
void I2C_GPIO_Config(void);
void I2C_Start(void);
void I2C_Stop(void);
void I2C_PutAck(uint8_t Ack);
uint8_t I2C_GetAck(void);

uint8_t I2C_WriteByte(uint8_t Data);
uint8_t I2C_ReadByte(uint8_t ack);

#endif

 

i2c_drv.c

#include "i2c_drv.h"

/************************************************
函数名称 : I2C_Delay
功    能 : I2C延时(非标准延时,请根据MCU速度 调节大小)
参    数 : 无
返 回 值 : 无
作    者 :
*************************************************/
static void I2C_Delay(void)
{
    uint16_t cnt = 10;
    while(cnt--)
    {
        __NOP();
    }
}

/************************************************
函数名称 : I2C_GPIO_Config
功    能 : I2C引脚配置(开漏输出)
参    数 : 无
返 回 值 : 无
作    者 :
*************************************************/
void I2C_GPIO_Config(void)
{
    GPIO_InitTypeDef GPIO_InitStructure;

    RCC_APB2PeriphClockCmd(I2C_SCL_RCC | I2C_SDA_RCC, ENABLE);//打开GPIO时钟

    GPIO_InitStructure.GPIO_Pin = I2C_SCL_GPIO_PINS;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_OD;    // OD输出
    GPIO_Init(I2C_SCL_GPIO_PORT, &GPIO_InitStructure);

    GPIO_InitStructure.GPIO_Pin = I2C_SDA_GPIO_PINS;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_OD;    // 推挽输出
    GPIO_Init(I2C_SDA_GPIO_PORT, &GPIO_InitStructure);
}

/************************************************
函数名称 : I2C_GPIO_DeInit
功    能 : I2C引脚配置(输出HIGH)
参    数 : 无
返 回 值 : 无
作    者 :
*************************************************/
void I2C_GPIO_DeInit(void)
{
    GPIO_InitTypeDef GPIO_InitStructure;

    RCC_APB2PeriphClockCmd(I2C_SCL_RCC | I2C_SDA_RCC, ENABLE);//打开GPIO时钟

    GPIO_InitStructure.GPIO_Pin = I2C_SCL_GPIO_PINS;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;    // PP输出
    GPIO_Init(I2C_SCL_GPIO_PORT, &GPIO_InitStructure);

    GPIO_InitStructure.GPIO_Pin = I2C_SDA_GPIO_PINS;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;    // 推挽输出
    GPIO_Init(I2C_SDA_GPIO_PORT, &GPIO_InitStructure);
    I2C_SCL_HIGH; //置位状态
    I2C_SDA_HIGH;
}

/************************************************
函数名称 : I2C_Initializes
功    能 : I2C初始化
参    数 : 无
返 回 值 : 无
作    者 :
*************************************************/
void I2C_Initializes(void)
{
    I2C_GPIO_Config();
    I2C_SCL_HIGH; //置位状态
    I2C_SDA_HIGH;
}

/************************************************
函数名称 : I2C_Start
功    能 : I2C开始
参    数 : 无
返 回 值 : 无
作    者 :
*************************************************/
void I2C_Start(void)
{
    I2C_SCL_HIGH;
    I2C_Delay();
    I2C_SDA_HIGH;
    I2C_Delay();
    I2C_SDA_LOW;
    I2C_Delay();
    I2C_SCL_LOW;
    I2C_Delay();
}

/************************************************
函数名称 : I2C_Stop
功    能 : I2C停止
参    数 : 无
返 回 值 : 无
作    者 :
*************************************************/
void I2C_Stop(void)
{
    I2C_SDA_LOW;
    I2C_Delay();
    I2C_SCL_HIGH;
    I2C_Delay();
    I2C_SDA_HIGH;
    I2C_Delay();
    I2C_Delay();
}

/************************************************
函数名称 : I2C_PutAck
功    能 : I2C主机产生应答(或非应答)位
参    数 : I2C_ACK ----- 应答
            I2C_NOACK --- 非应答
返 回 值 : 无
作    者 :
*************************************************/
void I2C_PutAck(uint8_t Ack)
{
    I2C_SCL_LOW;
    I2C_Delay();
    if(I2C_ACK == Ack)
        I2C_SDA_LOW;        //应答
    else
        I2C_SDA_HIGH;       //非应答
    I2C_Delay();

    I2C_SCL_HIGH; //SCL高 -> 低
    I2C_Delay();
    I2C_Delay();
    I2C_SCL_LOW;  //SCL低
    I2C_Delay();
    I2C_Delay();
}

/************************************************
函数名称 : I2C_GetAck
功    能 : I2C主机读取应答(或非应答)位
参    数 : 无
返 回 值 : I2C_ACK ----- 应答
            I2C_NOACK --- 非应答
作    者 :
*************************************************/
uint8_t I2C_GetAck(void)
{
    uint8_t ack = I2C_NOACK;

    I2C_SCL_LOW;
    I2C_Delay();
    I2C_Delay();
    I2C_SDA_HIGH;
    I2C_Delay();
    I2C_SCL_HIGH; //SCL高(读取应答位)
    I2C_Delay();
    I2C_Delay();
    if(I2C_SDA_READ)
        ack = I2C_NOACK;    //非应答
    else
        ack = I2C_ACK;      //应答

    I2C_SCL_LOW;  //SCL低
    I2C_Delay();
    I2C_Delay();

    return ack;
}

/************************************************
函数名称 : I2C_WriteByte
功    能 : I2C写一字节
参    数 : Data --- 数据
返 回 值 : 无
作    者 :
*************************************************/
uint8_t I2C_WriteByte(uint8_t Data)
{
    uint8_t cnt = 0;
    for(cnt=0; cnt<8; cnt++)
    {
        I2C_SCL_LOW;
        I2C_Delay();
        if(Data & 0x80)
            I2C_SDA_HIGH; //SDA高
        else
            I2C_SDA_LOW; //SDA低
        Data <<= 1;
        I2C_Delay();
        I2C_SCL_HIGH;
        I2C_Delay();
        I2C_Delay();
    }
    I2C_SCL_LOW;
    I2C_Delay();

    return I2C_GetAck();
}

/************************************************
函数名称 : I2C_ReadByte
功    能 : I2C读一字节
参    数 : ack --------- 产生应答(或者非应答)位
返 回 值 : data -------- 读取的一字节数据
作    者 :
*************************************************/
uint8_t I2C_ReadByte(uint8_t ack)
{
    uint8_t cnt = 0;
    uint8_t data = 0;

    I2C_SCL_LOW;
    I2C_Delay();
    I2C_Delay();
    I2C_SDA_HIGH;
    I2C_Delay();
    for(cnt=0; cnt<8; cnt++)
    {
        I2C_SCL_HIGH;
        I2C_Delay();
        I2C_Delay();
        data <<= 1;
        if(I2C_SDA_READ)
            data |= 0x01;

        I2C_SCL_LOW;
        I2C_Delay();
        I2C_Delay();
    }

    I2C_PutAck(ack);

    return data;
}

 

使用实例

 

三轴G-Sensor寄存器读写

#define G_SLAVEADDR_WR                0x4C
#define G_SLAVEADDR_RD                0x4D

/******************************************
    MSA300_WriteBytes
*******************************************/
void MSA300_WriteBytes(uint8_t RegAddr, uint8_t Data)
{
    I2C_Start();                    //开始数据传递
    I2C_WriteByte(G_SLAVEADDR_WR);  //写入从机地址
    I2C_WriteByte(RegAddr);         //写入读写命令
    I2C_WriteByte(Data);            //向当前地址(上面指定的地址)写入数据
    I2C_Stop();                     //停止数据传递
}

/******************************************
    BMA220 read bytes
*******************************************/
uint8_t MSA300_ReadBytes(uint8_t RegAddr)
{
    unsigned char ReadValue;
    I2C_Start();     //开始数据传递
    I2C_WriteByte(G_SLAVEADDR_WR);   //选择从机地址
    I2C_WriteByte(RegAddr);  //写入寄存器地址
    I2C_Start();     //开始数据传递
    I2C_WriteByte(G_SLAVEADDR_RD);   //选择从机地址
    ReadValue=I2C_ReadByte(I2C_NOACK);        //在指定地址读取数据
    I2C_Stop();       //停止数据传递
    return(ReadValue);    //将指定地址读出的数据返回
}

 

总结

 

  • 可以使用软件I2C,读取一些简单的如G-sensor之类的传感器。
  • 注意I2C总线,外部必须有上拉电阻,GPIO配置为OD。
  • I2C时序,速率,可以通过逻辑分析仪查看,修改GPIO 引脚速度、更改延时。

 

 

上一篇:linux常用命令


下一篇:INA226使用教程(IIC)