STM32—IIC通信(软件实现底层函数)
阅读原文时间:2023年07月12日阅读:4

使用GPIO引脚模拟SDA和SCL总线实现软件模拟IIC通信,IIC的具体通信协议层和物理层链接:IIC

#ifndef __BSP_IIC_H
#define __BSP_IIC_H

#include "stm32f10x.h"

#define SCL_PORT         GPIOA
#define SCL_PIN          GPIO_Pin_2
#define SCL_MOOD         GPIO_Mode_Out_OD
#define SCL_SPEED        GPIO_Speed_50MHz

#define SDA_PORT         GPIOA
#define SDA_PIN          GPIO_Pin_3
#define SDA_MOOD         GPIO_Mode_Out_OD
#define SDA_SPEED        GPIO_Speed_50MHz

#define SDA_1()          GPIO_SetBits(SDA_PORT, SDA_PIN)
#define SDA_0()          GPIO_ResetBits(SDA_PORT, SDA_PIN)

#define SCL_1()          GPIO_SetBits(SCL_PORT, SCL_PIN)
#define SCL_0()          GPIO_ResetBits(SCL_PORT, SCL_PIN)

#define SDA_READ         GPIO_ReadInputDataBit(SDA_PORT, SDA_PIN)

 /* ACK原型为acknowledge,意为:报告已收到 */
void Delay(void);
void IIC_START(void);
void IIC_STOP(void);
void IIC_ACK(void);
void IIC_NACK(void);
uint8_t IIC_ReadACK(void);
void IIC_SendByte(uint8_t data)    ;
uint8_t IIC_ReadByte(void);
void IIC_GPIO_Config(void);

#endif     /* __BSP_IIC_H */





void Delay(void)
{
    uint8_t i;

    /* 
        下面的时间是通过逻辑分析仪测试得到的。
    工作条件:CPU主频72MHz ,MDK编译环境,1级优化

        循环次数为10时,SCL频率 = 205KHz
        循环次数为7时,SCL频率 = 347KHz, SCL高电平时间1.5us,SCL低电平时间2.87us
        循环次数为5时,SCL频率 = 421KHz, SCL高电平时间1.25us,SCL低电平时间2.375us
    */
    for (i = 0; i < 10; i++);
}

/* SCL高电平时期SDA产生下降沿表示起始信号 */
void IIC_START(void)
{
    SDA_1();
    SCL_1();
    Delay();

    SDA_0();
    Delay();
    SCL_0();
    Delay();
}

/* SCL高电平时期SDA产生上升沿表示停止信号 */
void IIC_STOP(void)
{
    SDA_0();
    SCL_1();
    Delay();

    SDA_1();
    Delay();
  /* 停止信号后SDA和SCL都为高电平 */
}

/* SCL高电平时期 SDA保持低电平代表 应答信号 */
void IIC_ACK(void)
{
    SDA_0();
    Delay();
    SCL_1();
    Delay();

    SCL_0();
    Delay();
    SDA_1();
/* 随后释放SDA总线 */
}

/* SCL高电平时期 SDA保持高电平代表 非应答信号 */
void IIC_NACK(void)
{
    SDA_1();
    Delay();
    SCL_1();
    Delay();

    SCL_0();
/* SDA已经为高电平,无需释放 */
}

/* CPU产生一个SCL时钟,读取应答  ACK:0   NACK:1*/
/* 因为SDA平时为高电平,所以没有收到应答时,SDA还为高电平,即读取到 1 为非应答 */
uint8_t IIC_ReadACK(void)
{
    uint8_t k;
    /* 释放SDA总线 */
    SDA_1();
    Delay();
    /* SCL为高电平时,才会读取有效数据 */
    SCL_1();
    Delay();

    /* 读取信号 */
    if(SDA_READ==1)
        k=1;
    else
        k=0;
    /* 收到信号后SCL要拉低 */
    SCL_0();
    Delay();
    return k;
}

/* 发送一个字节,先发送高位 */
void IIC_SendByte(uint8_t data)
{
    uint8_t n=0x01;
    int i;
    SCL_0();
    Delay();
    for(i=7;i>=0;i--)
    {
        if(n&(data>>i))
        {
            SDA_1();
            Delay();
            SCL_1();
            Delay();
        }
        else
        {
            SDA_0();
            Delay();
            SCL_1();
            Delay();
        }
        SCL_0();
        Delay();
    }
    /* 发送完一个字节后释放SDA总线 */
    SDA_1();
    Delay();

}

/* 读取一个字节,在CPU产生的SCL高电平时期读取 */
uint8_t IIC_ReadByte(void)
{
//    uint8_t i,data=0;
//    for(i=0;i<8;i++)
//    {
//        SCL_1();
//        Delay();
//        if(SDA_READ)
//        {
//            data++; /* 利用自增实现对最低位写 1  */
//        }
//        data<<=1;
//        SCL_0();
//        Delay();
//
//    }
//    return data;
//    

    uint8_t i;

    uint8_t temp = 0;

    for(i=0;i<8;i++)
    {
        temp<<=1;

        SCL_1();
        Delay();

        if( SDA_READ==1 )
        {
            temp += 1;
        }

        SCL_0();
        Delay();
    }       

    return temp;
}

/* 配置SCL和SDA对应的GPIO引脚,模式都为开漏输出 */
void IIC_GPIO_Config(void)
{
    GPIO_InitTypeDef GPIO_InitStruct;

    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);

    GPIO_InitStruct.GPIO_Pin = SCL_PIN;
    GPIO_InitStruct.GPIO_Mode = SCL_MOOD;
    GPIO_InitStruct.GPIO_Speed = SCL_SPEED;
    GPIO_Init(SCL_PORT, &GPIO_InitStruct);

  GPIO_InitStruct.GPIO_Pin = SDA_PIN;
    GPIO_InitStruct.GPIO_Mode = SDA_MOOD;
    GPIO_InitStruct.GPIO_Speed = SDA_SPEED;
    GPIO_Init(SDA_PORT, &GPIO_InitStruct);

/* 给一个停止信号,使IIC总线上所有设备处于复位 */
    IIC_STOP();

}