STM32多IIC设备的软件IIC实现

本文介绍了在C语言中通过面向对象的方式实现软件IIC驱动的方法,以应对多个IIC设备地址相同的情况。作者通过定义IIC_TypeDef结构体封装了设备的相关信息,并提供了IIC的初始化、起始、停止、发送、读取和应答等时序操作。这种方法提高了代码的复用性和可维护性。
摘要由CSDN通过智能技术生成


前言

在项目中遇到要驱动10个IIC设备其中有8个地址相同并且无法更改的情况,使用宏定义形式的IO口操作不能很好的满足需求,于是想到了面向对象的写法,在C语言中虽然没有对象这一概念,但对象也只是相关的数据结构和一组方法的封装,在C程序中可以提过将相关数据结构封装到结构体中,函数传入应该结构体指针的方式来实现类似的面向对象写法。

一、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);
}

程序代码

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值