STM32多IIC设备的软件IIC实现

您所在的位置:网站首页 i2c多个从机无法通讯 STM32多IIC设备的软件IIC实现

STM32多IIC设备的软件IIC实现

2024-07-10 17:05| 来源: 网络整理| 查看: 265

文章目录 前言一、Software_IIC.h二、起始时序三、停止时序四、发送数据时序五、读取时序六、应答时序程序代码

前言

在项目中遇到要驱动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 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 SCL); Delay_us(IIC_DELAY_TIME); HAL_GPIO_High(&pIIC->SCL); Delay_us(IIC_DELAY_TIME); dat = (dat 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 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); } 程序代码


【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3