IIC驱动?C语言使用面向对象来实现

admin 2025-05-07 94人围观 ,发现36个评论
一.简述

使用面向对象的编程思想封装IIC驱动,将IIC的属性和操作封装成一个库,在需要创建一个IIC设备时只需要实例化一个IIC对象即可,本文是基于STM32和HAL库做进一步封装的。

底层驱动方法不重要,封装的思想很重要。在完成对IIC驱动的封装之后借助继承特性实现AT24C64存储器的驱动开发,仍使用面向对象的思想封装AT24C64驱动。

二.IIC驱动面向对象封装

头文件主要是类模板的定义,具体如下:

//定义IIC类typedefstructIIC_Type{//属性GPIO_TypeDef*GPIOx_SCL;//GPIO_SCL所属的GPIO组(如:GPIOA)GPIO_TypeDef*GPIOx_SDA;//GPIO_SDA所属的GPIO组(如:GPIOA)uint32_tGPIO_SCL;//GPIO_SCL的IO引脚(如:GPIO_PIN_0)uint32_tGPIO_SDA;//GPIO_SDA的IO引脚(如:GPIO_PIN_0)//操作void(*IIC_Init)(conststructIIC_Type*);//IIC_Initvoid(*IIC_Start)(conststructIIC_Type*);//IIC_Startvoid(*IIC_Stop)(conststructIIC_Type*);//IIC_Stopuint8_t(*IIC_Wait_Ack)(conststructIIC_Type*);//IIC_Wait_ack,返回wait失败或是成功void(*IIC_Ack)(conststructIIC_Type*);//IIC_Ack,IIC发送ACK信号void(*IIC_NAck)(conststructIIC_Type*);//IIC_NAck,IIC发送NACK信号void(*IIC_S_Byte)(conststructIIC_Type*,uint8_t);//IIC_S_Byte,入口参数为要发送的字节uint8_t(*IIC_Read_Byte)(conststructIIC_Type*,uint8_t);//IIC_S_Byte,入口参数为是否要发送ACK信号void(*delay_us)(uint32_t);//us延时}IIC_TypeDef;

源文件主要是类模板具体操作函数的实现,具体如下:

//设置SDA为输入模式staticvoidSDA_IN(conststructIIC_Type*IIC_Type_t){uint8_tio_num=0;//定义ioNum号switch(IIC_Type_t-GPIO_SDA){caseGPIO_PIN_0:io_num=0;break;caseGPIO_PIN_1:io_num=1;break;caseGPIO_PIN_2:io_num=2;break;caseGPIO_PIN_3:io_num=3;break;caseGPIO_PIN_4:io_num=4;break;caseGPIO_PIN_5:io_num=5;break;caseGPIO_PIN_6:io_num=6;break;caseGPIO_PIN_7:io_num=7;break;caseGPIO_PIN_8:io_num=8;break;caseGPIO_PIN_9:io_num=9;break;caseGPIO_PIN_10:io_num=10;break;caseGPIO_PIN_11:io_num=11;break;caseGPIO_PIN_12:io_num=12;break;caseGPIO_PIN_13:io_num=13;break;caseGPIO_PIN_14:io_num=14;break;caseGPIO_PIN_15:io_num=15;break;}IIC_Type_t-GPIOx_SDA-MODER=~(3(io_num*2));//将GPIOx_SDA-GPIO_SDA清零IIC_Type_t-GPIOx_SDA-MODER|=0(io_num*2);//将GPIOx_SDA-GPIO_SDA设置为输入模式}//设置SDA为输出模式staticvoidSDA_OUT(conststructIIC_Type*IIC_Type_t){uint8_tio_num=0;//定义ioNum号switch(IIC_Type_t-GPIO_SDA){caseGPIO_PIN_0:io_num=0;break;caseGPIO_PIN_1:io_num=1;break;caseGPIO_PIN_2:io_num=2;break;caseGPIO_PIN_3:io_num=3;break;caseGPIO_PIN_4:io_num=4;break;caseGPIO_PIN_5:io_num=5;break;caseGPIO_PIN_6:io_num=6;break;caseGPIO_PIN_7:io_num=7;break;caseGPIO_PIN_8:io_num=8;break;caseGPIO_PIN_9:io_num=9;break;caseGPIO_PIN_10:io_num=10;break;caseGPIO_PIN_11:io_num=11;break;caseGPIO_PIN_12:io_num=12;break;caseGPIO_PIN_13:io_num=13;break;caseGPIO_PIN_14:io_num=14;break;caseGPIO_PIN_15:io_num=15;break;}IIC_Type_t-GPIOx_SDA-MODER=~(3(io_num*2));//将GPIOx_SDA-GPIO_SDA清零IIC_Type_t-GPIOx_SDA-MODER|=1(io_num*2);//将GPIOx_SDA-GPIO_SDA设置为输出模式}//设置SCL电平staticvoidIIC_SCL(conststructIIC_Type*IIC_Type_t,intn){if(n==1){HAL_GPIO_WritePin(IIC_Type_t-GPIOx_SCL,IIC_Type_t-GPIO_SCL,GPIO_PIN_SET);//设置SCL为高电平}else{HAL_GPIO_WritePin(IIC_Type_t-GPIOx_SCL,IIC_Type_t-GPIO_SCL,GPIO_PIN_RESET);//设置SCL为低电平}}//设置SDA电平staticvoidIIC_SDA(conststructIIC_Type*IIC_Type_t,intn){if(n==1){HAL_GPIO_WritePin(IIC_Type_t-GPIOx_SDA,IIC_Type_t-GPIO_SDA,GPIO_PIN_SET);//设置SDA为高电平}else{HAL_GPIO_WritePin(IIC_Type_t-GPIOx_SDA,IIC_Type_t-GPIO_SDA,GPIO_PIN_RESET);//设置SDA为低电平}}//读取SDA电平staticuint8_tREAD_SDA(conststructIIC_Type*IIC_Type_t){returnHAL_GPIO_ReadPin(IIC_Type_t-GPIOx_SDA,IIC_Type_t-GPIO_SDA);//读取SDA电平}//IIC初始化staticvoidIIC_Init_t(conststructIIC_Type*IIC_Type_t){GPIO_InitTypeDefGPIO_Initure;//根据GPIO组初始化GPIO时钟if(IIC_Type_t-GPIOx_SCL==GPIOA||IIC_Type_t-GPIOx_SDA==GPIOA){__HAL_RCC_GPIOA_CLK_ENABLE();//使能GPIOA时钟}if(IIC_Type_t-GPIOx_SCL==GPIOB||IIC_Type_t-GPIOx_SDA==GPIOB){__HAL_RCC_GPIOB_CLK_ENABLE();//使能GPIOB时钟}if(IIC_Type_t-GPIOx_SCL==GPIOC||IIC_Type_t-GPIOx_SDA==GPIOC){__HAL_RCC_GPIOC_CLK_ENABLE();//使能GPIOC时钟}if(IIC_Type_t-GPIOx_SCL==GPIOD||IIC_Type_t-GPIOx_SDA==GPIOD){__HAL_RCC_GPIOD_CLK_ENABLE();//使能GPIOD时钟}if(IIC_Type_t-GPIOx_SCL==GPIOE||IIC_Type_t-GPIOx_SDA==GPIOE){__HAL_RCC_GPIOE_CLK_ENABLE();//使能GPIOE时钟}if(IIC_Type_t-GPIOx_SCL==GPIOH||IIC_Type_t-GPIOx_SDA==GPIOH){__HAL_RCC_GPIOH_CLK_ENABLE();//使能GPIOH时钟}//GPIO_SCL初始化设置GPIO_=IIC_Type_t-GPIO_SCL;GPIO_=GPIO_MODE_OUTPUT_PP;//推挽输出GPIO_=GPIO_PULLUP;//上拉GPIO_=GPIO_SPEED_FREQ_VERY_HIGH;//快速HAL_GPIO_Init(IIC_Type_t-GPIOx_SCL,GPIO_Initure);//GPIO_SDA初始化设置GPIO_=IIC_Type_t-GPIO_SDA;GPIO_=GPIO_MODE_OUTPUT_PP;//推挽输出GPIO_=GPIO_PULLUP;//上拉GPIO_=GPIO_SPEED_FREQ_VERY_HIGH;//快速HAL_GPIO_Init(IIC_Type_t-GPIOx_SDA,GPIO_Initure);//SCL与SDA的初始化均为高电平IIC_SCL(IIC_Type_t,1);IIC_SDA(IIC_Type_t,1);}//IICStartstaticvoidIIC_Start_t(conststructIIC_Type*IIC_Type_t){SDA_OUT(IIC_Type_t);//sda线输出IIC_SDA(IIC_Type_t,1);IIC_SCL(IIC_Type_t,1);IIC_Type_t-delay_us(4);IIC_SDA(IIC_Type_t,0);//START:whenCLKishigh,DATAchangeformhightolowIIC_Type_t-delay_us(4);IIC_SCL(IIC_Type_t,0);//钳住I2C总线,准备发送或接收数据}//IICStopstaticvoidIIC_Stop_t(conststructIIC_Type*IIC_Type_t){SDA_OUT(IIC_Type_t);//sda线输出IIC_SCL(IIC_Type_t,0);IIC_SDA(IIC_Type_t,0);//STOP:whenCLKishighDATAchangeformlowtohighIIC_Type_t-delay_us(4);IIC_SCL(IIC_Type_t,1);IIC_SDA(IIC_Type_t,1);//发送I2C总线结束信号IIC_Type_t-delay_us(4);}//IIC_Wait_ack返回HAL_OK表示wait成功,返回HAL_ERROR表示wait失败staticuint8_tIIC_Wait_Ack_t(conststructIIC_Type*IIC_Type_t)//IIC_Wait_ack,返回wait失败或是成功{uint8_tucErrTime=0;SDA_IN(IIC_Type_t);//SDA设置为输入IIC_SDA(IIC_Type_t,1);IIC_Type_t-delay_us(1);IIC_SCL(IIC_Type_t,1);IIC_Type_t-delay_us(1);while(READ_SDA(IIC_Type_t)){ucErrTime++;if(ucErrTime250){IIC_Type_t-IIC_Stop(IIC_Type_t);returnHAL_ERROR;}}IIC_SCL(IIC_Type_t,0);//时钟输出0returnHAL_OK;}//产生ACK应答staticvoidIIC_Ack_t(conststructIIC_Type*IIC_Type_t){IIC_SCL(IIC_Type_t,0);SDA_OUT(IIC_Type_t);IIC_SDA(IIC_Type_t,0);IIC_Type_t-delay_us(2);IIC_SCL(IIC_Type_t,1);IIC_Type_t-delay_us(2);IIC_SCL(IIC_Type_t,0);}//产生NACK应答staticvoidIIC_NAck_t(conststructIIC_Type*IIC_Type_t){IIC_SCL(IIC_Type_t,0);SDA_OUT(IIC_Type_t);IIC_SDA(IIC_Type_t,1);IIC_Type_t-delay_us(2);IIC_SCL(IIC_Type_t,1);IIC_Type_t-delay_us(2);IIC_SCL(IIC_Type_t,0);}//IIC_S_Byte,入口参数为要发送的字节staticvoidIIC_S_Byte_t(conststructIIC_Type*IIC_Type_t,uint8_ttxd){uint8_tt=0;SDA_OUT(IIC_Type_t);IIC_SCL(IIC_Type_t,0);//拉低时钟开始数据传输for(t=0;t8;t++){IIC_SDA(IIC_Type_t,(txd0x80)7);txd=1;IIC_Type_t-delay_us(2);//对TEA5767这三个延时都是必须的IIC_SCL(IIC_Type_t,1);IIC_Type_t-delay_us(2);IIC_SCL(IIC_Type_t,0);IIC_Type_t-delay_us(2);}}//IIC_S_Byte,入口参数为是否要发送ACK信号staticuint8_tIIC_Read_Byte_t(conststructIIC_Type*IIC_Type_t,uint8_tack){uint8_ti,receive=0;SDA_IN(IIC_Type_t);//SDA设置为输入for(i=0;i8;i++){IIC_SCL(IIC_Type_t,0);IIC_Type_t-delay_us(2);IIC_SCL(IIC_Type_t,1);receive=1;if(READ_SDA(IIC_Type_t))receive++;IIC_Type_t-delay_us(1);}if(!ack)IIC_Type_t-IIC_NAck(IIC_Type_t);//发送nACKelseIIC_Type_t-IIC_Ack(IIC_Type_t);//发送ACKreturnreceive;}//实例化一个IIC1外设,相当于一个结构体变量,可以直接在其他文件中使用IIC_TypeDefIIC1={.GPIOx_SCL=GPIOA,//GPIO组为_SDA=GPIOA,//GPIO组为_SCL=GPIO_PIN_5,//GPIO为_SDA=GPIO_PIN_6,//GPIO为_Init=IIC_Init_t,.IIC_Start=IIC_Start_t,.IIC_Stop=IIC_Stop_t,.IIC_Wait_Ack=IIC_Wait_Ack_t,.IIC_Ack=IIC_Ack_t,.IIC_NAck=IIC_NAck_t,.IIC_S_Byte=IIC_S_Byte_t,.IIC_Read_Byte=IIC_Read_Byte_t,.delay_us=delay_us//需自己外部实现delay_us函数};

上述就是IIC驱动的封装,由于没有应用场景暂不测试其实用性,待下面ATC64的驱动缝缝扎黄写完之后一起测试使用。

三.ATC64XX驱动封装实现

头文件主要是类模板的定义,具体如下:

//以下是共定义个具体容量存储器的容量defineAT24C02255defineAT24C081023defineAT24C324095defineAT24C12816383include""//为了确定AT24C_64的成员方法和引用操作对象AT24C_64intmain(void){/************省略其他初始化工作****************///第一步:调用对象初始化方法来初始化AT24C64AT24C_64.AT24CXX_Init(AT24C_64);//第二步:调用对象检测方法来检测AT24C64if(AT24C_64.AT24CXX_Check(AT24C_64)==0){printf("AT24C64检测成功\r\n");}else{printf("AT24C64检测失败\r\n");}return0;}

可以看出所有的操作都是通过AT24C_64对象调用完成的,在我们初始化好AT24C_64对象之后就可以放心大胆的调用其成员方法,这样封装的好处就是一个设备对外只提供一个对象接口,简洁明了。

五.总结

本文详细介绍了面向对象方法实现IIC驱动封装以及AT24CXX存储器的封装,最终对外仅提供一个操作对象接口,大大提高了代码的复用性以及封装性。

猜你喜欢
    不容错过