打印
[STM32F1]

软件IIC实现

[复制链接]
482|0
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
deliahouse887|  楼主 | 2024-8-31 18:54 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
一、Software_IIC.h
将IIC协议信息抽象到IIC_TypeDef结构体中,包括一个SDA、SCL引脚以及一个地址信息,这样在使用软件IIC时只需要定义IIC_TypeDef的结构体就可以了,并且可以很好的复用IIC操作的代码。
如我这种8个地址相同的情况就可以定义如下IIC设备:

IIC_Typedef IIC1 = {
        {RCC_AHB1Periph_GPIOH, GPIOH, 4},
        {RCC_AHB1Periph_GPIOH, GPIOH, 5},
        0xA0,
};
IIC_Typedef IIC2 = {
        {RCC_AHB1Periph_GPIOH, GPIOH, 4},
        {RCC_AHB1Periph_GPIOH, GPIOH, 6},
        0xA0,
};
IIC_Typedef IIC3 = {
        {RCC_AHB1Periph_GPIOH, GPIOH, 4},
        {RCC_AHB1Periph_GPIOH, GPIOH, 7},
        0xA0,
};
IIC_Typedef IIC4 = {
        {RCC_AHB1Periph_GPIOH, GPIOH, 4},
        {RCC_AHB1Periph_GPIOH, GPIOH, 5},
        0xA8,
};
/**
* @Author: 时间世纪
* @Date: 2021-08-06 13:35:00
* @Description: 软件IIC驱动程序
*/
#ifndef _SOFTWARE_IIC_H_
#define _SOFTWARE_IIC_H_

#include "HAL_Driver.h"

#define IIC_DELAY_TIME 1 //延时时间

//一个软件IIC设备
typedef        struct
{
        HAL_GPIO_TypeDef        SCL;//包含操作IO口的信息
        HAL_GPIO_TypeDef        SDA;
        uint8_t ADD;//设备地址,内部使用时没有移位,只设置了读写位
} IIC_TypeDef;

/**
* @brief 初始化一个IIC设备
* @param IIC_TypeDef *pIIC
* @retval:
*/
extern void IIC_Init(IIC_TypeDef * const pIIC);
/**
* @brief 开始一次IIC通信
* @param IIC_TypeDef *pIIC
* @retval
*/
extern void IIC_Start(IIC_TypeDef *const pIIC);
/**
* @brief 停止一次IIC通信
* @param IIC_TypeDef *pIIC
* @retval
*/
extern void IIC_Stop(IIC_TypeDef *const pIIC);
/**
* @brief 等待从机应答
* @param IIC_TypeDef *pIIC
* @retval 0:有应答, 1:无应答
*/
extern uint8_t IIC_Sack(IIC_TypeDef *const pIIC);
/**
* @brief 发送一字节数据
* @param IIC_TypeDef *pIIC
* @param uint8_t dat
* @retval
*/
extern void IIC_SendByte(IIC_TypeDef *const pIIC, uint8_t dat);
/**
* @brief 发送多个数据
* @param IIC_TypeDef *pIIC
* @param uint8_t * dat
* @param uint8_t len 长度
* @retval 0:发送成功,1: 失败
*/
extern uint8_t IIC_Send(IIC_TypeDef *const pIIC, uint8_t *dat, uint8_t len);
/**
* @brief 读取一字节数据
* @param IIC_TypeDef *pIIC
* @retval
*/
extern uint8_t IIC_ReadByte(IIC_TypeDef *const pIIC);
/**
* @brief 读取多个数据
* @param IIC_TypeDef *pIIC
* @param uint8_t * dat
* @param uint8_t len 长度
*/
extern void IIC_Read(IIC_TypeDef *const pIIC, uint8_t *dat, uint8_t len);
/**
* @brief 发送写地址
* @param IIC_TypeDef *pIIC
* @retval 0:有应答,1:无应答
*/
__STATIC_FORCEINLINE uint8_t IIC_SendWriteAddress(IIC_TypeDef *const pIIC)
{
    IIC_SendByte(pIIC, pIIC->ADD);
    return IIC_Sack(pIIC);
}

/**
* @brief 发送读地址
* @param IIC_TypeDef *pIIC
* @retval 0:有应答,1:无应答
*/
__STATIC_FORCEINLINE uint8_t IIC_SendReadAddress(IIC_TypeDef *const pIIC)
{
    IIC_SendByte(pIIC, pIIC->ADD | 0x01);
    return IIC_Sack(pIIC);
}

/**
* @brief 发送一个字节并检查从机应答
* @param IIC_TypeDef *pIIC
* @param uint8_t dat
* @retval 0:有应答,1:无应答
*/
__STATIC_FORCEINLINE uint8_t IIC_SendAndSack(IIC_TypeDef *const pIIC, uint8_t dat)
{
    IIC_SendByte(pIIC, dat);
    return IIC_Sack(pIIC);
}

/**
* @brief 读取一字节数据并进行应答操作
* @param IIC_TypeDef *pIIC
* @param uint8_t ack 是否对从机进行应答继续读取,0:否停止读取,1:继续读取
* @retval
*/
__STATIC_FORCEINLINE  uint8_t IIC_ReadAndAck(IIC_TypeDef *const pIIC, uint8_t ack)
{
        uint8_t dat = 0;
        dat = IIC_ReadByte(pIIC);
        IIC_Ack(IIC, ack);
        return dat;
}
#endif

二、起始时序
注意:程序未采用STM32的HAL库,程序中HAL_GPIO_High,HAL_GPIO_Low函数仅代表当前这款单片机IO口的操作的封装,并向其传递控制IO口的必要参数。
起始时序为在SCL为高时SDA拉低启动一次发送。

/**
* @brief 启动一次传输,在SCL高电平时SDA高变低开始传输
* @param IIC_TypeDef *pIIC
* @retval
*/
void IIC_Start(IIC_TypeDef * const pIIC)
{
    HAL_GPIO_SetOutput(&pIIC->SDA);
    HAL_GPIO_High(&pIIC->SDA);
    HAL_GPIO_High(&pIIC->SCL);//拉高SCL
    Delay_us(IIC_DELAY_TIME);
    HAL_GPIO_Low(&pIIC->SDA);//拉低SDA启动一次传输
    Delay_us(IIC_DELAY_TIME);
    HAL_GPIO_Low(&pIIC->SCL);//拉低SCL准备传输
    Delay_us(IIC_DELAY_TIME);
}
三、停止时序
停止时序为SCL为高时SDA由低变高结束一次发送。

/**
* @brief 停止本次传输,在SCL高电平时SDA低变高停止传输
* @param IIC_TypeDef *pIIC
* @retval
*/
void IIC_Stop(IIC_TypeDef *const pIIC)
{
    HAL_GPIO_SetOutput(&pIIC->SDA);
    HAL_GPIO_Low(&pIIC->SDA);//拉低SDA
    Delay_us(IIC_DELAY_TIME);
    HAL_GPIO_High(&pIIC->SCL);//拉高SCL
    Delay_us(IIC_DELAY_TIME);
    HAL_GPIO_High(&pIIC->SDA);//拉高SDA结束一次传输
    Delay_us(IIC_DELAY_TIME);
    HAL_GPIO_Low(&pIIC->SCL);
    Delay_us(IIC_DELAY_TIME);
}
四、发送数据时序
发送时序为在SCL为低时SDA可变换,SCL拉高从机读取SDA。

/**
* @brief 发送一字节数据
* @param IIC_TypeDef *pIIC
* @param uint8_t dat
*/
void IIC_SendByte(IIC_TypeDef *const pIIC, uint8_t dat)
{
    HAL_GPIO_SetOutput(&pIIC->SDA);
    for (uint8_t i = 0; i < 8; i++)
    {
        HAL_GPIO_Low(&pIIC->SCL);//拉低SCL
        Delay_us(IIC_DELAY_TIME);
        if (dat & (0x80 >> i))//传输数据
            HAL_GPIO_High(&pIIC->SDA);
        else
            HAL_GPIO_Low(&pIIC->SDA);
        Delay_us(IIC_DELAY_TIME);
        HAL_GPIO_High(&pIIC->SCL);//拉高SCL从机读取数据
        Delay_us(IIC_DELAY_TIME);
    }
    HAL_GPIO_Low(&pIIC->SCL);
    Delay_us(IIC_DELAY_TIME);
}

五、读取时序
在SCL为低时从机将数据放在SDA上。

/**
* @brief 接收一字节数据
* @param IIC_TypeDef *pIIC
* @retval
*/
uint8_t IIC_ReadByte(IIC_TypeDef *const pIIC)
{
    uint8_t dat = 0;

    HAL_GPIO_SetInput(&pIIC->SDA);
    for (uint8_t i = 0; i < 8; i++)
    {
        HAL_GPIO_Low(&pIIC->SCL);
        Delay_us(IIC_DELAY_TIME);
        HAL_GPIO_High(&pIIC->SCL);
        Delay_us(IIC_DELAY_TIME);
        dat = (dat << 1) | HAL_GPIO_Read(&pIIC->SDA);
    }
    HAL_GPIO_Low(&pIIC->SCL);
    return dat;
}

六、应答时序
/**
* @brief 等待从机应答,在SCL高时从机会拉低SDA进行应答
* @param IIC_TypeDef *pIIC
* @retval 0:有应答,1:无应答
*/
uint8_t IIC_Sack(IIC_TypeDef *const pIIC)
{
    uint8_t result;

    HAL_GPIO_SetInput(&pIIC->SDA);//设置SDA为输入
    Delay_us(IIC_DELAY_TIME);
    HAL_GPIO_High(&pIIC->SCL);
    Delay_us(IIC_DELAY_TIME);
    for (result = 0; result <= 10; result++)
    {
        if (!HAL_GPIO_Read(&pIIC->SDA))
            break;
        Delay_us(IIC_DELAY_TIME);
    }
    HAL_GPIO_Low(&pIIC->SCL);
    Delay_us(IIC_DELAY_TIME);
    return result >= 10 ? 1 : 0;
}

/**
* @brief 产生一个应答信号对从机应答
* @param IIC_TypeDef *pIIC
* @param uint8_t ack 0: 结束不应答,1:应答
* @retval:
*/
static void IIC_Ack(IIC_TypeDef *const pIIC, uint8_t ack)
{
    HAL_GPIO_Low(&pIIC->SCL);
    Delay_us(IIC_DELAY_TIME);
    HAL_GPIO_SetOutput(&pIIC->SDA);
    if (ack)
        HAL_GPIO_Low(&pIIC->SDA);
    else
        HAL_GPIO_High(&pIIC->SDA);
    Delay_us(IIC_DELAY_TIME);
    HAL_GPIO_High(&pIIC->SCL);
    Delay_us(IIC_DELAY_TIME);
    HAL_GPIO_Low(&pIIC->SCL);
    Delay_us(IIC_DELAY_TIME);
}


使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

29

主题

1199

帖子

0

粉丝