如何实现IIC驱动封装以及AT24CXX存储器的封装

发布时间:2024-02-22  

简述


IIC(Inter-Integrated Circuit)其实是IICBus简称,它是一种串行通信总线,使用多主从架构,在STM32开发中经常见到。



关于IIC可以参考之前发的一篇文章:《通信协议 IIC 与 SPI 最全对比》来了解。


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


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


IIC驱动面向对象封装

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


//定义IIC类

typedef struct IIC_Type

{

//属性

   GPIO_TypeDef  *GPIOx_SCL;  //GPIO_SCL所属的GPIO组(如:GPIOA)

   GPIO_TypeDef  *GPIOx_SDA;  //GPIO_SDA所属的GPIO组(如:GPIOA)

uint32_t GPIO_SCL;     //GPIO_SCL的IO引脚(如:GPIO_PIN_0)

uint32_t GPIO_SDA;     //GPIO_SDA的IO引脚(如:GPIO_PIN_0)

//操作

void (*IIC_Init)(const struct IIC_Type*);        //IIC_Init

void (*IIC_Start)(const struct IIC_Type*);       //IIC_Start

void (*IIC_Stop)(const struct IIC_Type*);        //IIC_Stop

uint8_t (*IIC_Wait_Ack)(const struct IIC_Type*);    //IIC_Wait_ack,返回wait失败或是成功

void (*IIC_Ack)(const struct IIC_Type*);       //IIC_Ack,IIC发送ACK信号

void (*IIC_NAck)(const struct IIC_Type*);       //IIC_NAck,IIC发送NACK信号

void (*IIC_Send_Byte)(const struct IIC_Type*,uint8_t);       //IIC_Send_Byte,入口参数为要发送的字节

uint8_t (*IIC_Read_Byte)(const struct IIC_Type*,uint8_t);     //IIC_Send_Byte,入口参数为是否要发送ACK信号

void (*delay_us)(uint32_t);              //us延时

}IIC_TypeDef;

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

//设置SDA为输入模式

static void SDA_IN(const struct IIC_Type* IIC_Type_t)

{

  uint8_t io_num = 0;  //定义io Num号

switch(IIC_Type_t->GPIO_SDA)

  {

case GPIO_PIN_0:

    io_num = 0;

break;

case GPIO_PIN_1:

    io_num = 1;

break; 

case GPIO_PIN_2:

    io_num = 2;

break; 

case GPIO_PIN_3:

    io_num = 3;

break;

case GPIO_PIN_4:

    io_num = 4;

break; 

case GPIO_PIN_5:

    io_num = 5;

break; 

case GPIO_PIN_6:

    io_num = 6;

break; 

case GPIO_PIN_7:

    io_num = 7;

break;

case GPIO_PIN_8:

    io_num = 8;

break; 

case GPIO_PIN_9:

    io_num = 9;

break;

case GPIO_PIN_10:

    io_num = 10;

break;

case GPIO_PIN_11:

    io_num = 11;

break; 

case GPIO_PIN_12:

    io_num = 12;

break;

case GPIO_PIN_13:

    io_num = 13;

break;

case GPIO_PIN_14:

    io_num = 14;

break; 

case GPIO_PIN_15:

    io_num = 15;

break;

  }

  IIC_Type_t->GPIOx_SDA->MODER&=~(3<GPIO_SDA清零

  IIC_Type_t->GPIOx_SDA->MODER|=0<GPIO_SDA设置为输入模式

}


//设置SDA为输出模式

static void SDA_OUT(const struct IIC_Type* IIC_Type_t)

{

  uint8_t io_num = 0;  //定义io Num号

switch(IIC_Type_t->GPIO_SDA)

  {

case GPIO_PIN_0:

    io_num = 0;

break;

case GPIO_PIN_1:

    io_num = 1;

break; 

case GPIO_PIN_2:

    io_num = 2;

break; 

case GPIO_PIN_3:

    io_num = 3;

break;

case GPIO_PIN_4:

    io_num = 4;

break; 

case GPIO_PIN_5:

    io_num = 5;

break; 

case GPIO_PIN_6:

    io_num = 6;

break; 

case GPIO_PIN_7:

    io_num = 7;

break;

case GPIO_PIN_8:

    io_num = 8;

break; 

case GPIO_PIN_9:

    io_num = 9;

break;

case GPIO_PIN_10:

    io_num = 10;

break;

case GPIO_PIN_11:

    io_num = 11;

break; 

case GPIO_PIN_12:

    io_num = 12;

break;

case GPIO_PIN_13:

    io_num = 13;

break;

case GPIO_PIN_14:

    io_num = 14;

break; 

case GPIO_PIN_15:

    io_num = 15;

break;

  }

  IIC_Type_t->GPIOx_SDA->MODER&=~(3<GPIO_SDA清零

  IIC_Type_t->GPIOx_SDA->MODER|=1<GPIO_SDA设置为输出模式

}

//设置SCL电平

static void IIC_SCL(const struct IIC_Type* IIC_Type_t,int n)

{

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电平

static void IIC_SDA(const struct IIC_Type* IIC_Type_t,int n)

{

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电平

static uint8_t READ_SDA(const struct IIC_Type* IIC_Type_t)

{

return HAL_GPIO_ReadPin(IIC_Type_t->GPIOx_SDA,IIC_Type_t->GPIO_SDA);  //读取SDA电平

}

//IIC初始化

static void IIC_Init_t(const struct IIC_Type* IIC_Type_t)

{

      GPIO_InitTypeDef GPIO_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_Initure.Pin=IIC_Type_t->GPIO_SCL;

     GPIO_Initure.Mode=GPIO_MODE_OUTPUT_PP;  //推挽输出

     GPIO_Initure.Pull=GPIO_PULLUP;          //上拉

     GPIO_Initure.Speed=GPIO_SPEED_FREQ_VERY_HIGH;    //快速

     HAL_GPIO_Init(IIC_Type_t->GPIOx_SCL,&GPIO_Initure);

//GPIO_SDA初始化设置

     GPIO_Initure.Pin=IIC_Type_t->GPIO_SDA;

     GPIO_Initure.Mode=GPIO_MODE_OUTPUT_PP;  //推挽输出

     GPIO_Initure.Pull=GPIO_PULLUP;          //上拉

     GPIO_Initure.Speed=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);

}

//IIC Start

static void IIC_Start_t(const struct IIC_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:when CLK is high,DATA change form high to low 

  IIC_Type_t->delay_us(4);

  IIC_SCL(IIC_Type_t,0);  //钳住I2C总线,准备发送或接收数据 

}

//IIC Stop

static void IIC_Stop_t(const struct IIC_Type* IIC_Type_t)

{

  SDA_OUT(IIC_Type_t); //sda线输出

  IIC_SCL(IIC_Type_t,0);

  IIC_SDA(IIC_Type_t,0); //STOP:when CLK is high DATA change form low to high

   IIC_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失败

static uint8_t IIC_Wait_Ack_t(const struct IIC_Type* IIC_Type_t)   //IIC_Wait_ack,返回wait失败或是成功

{

  uint8_t ucErrTime = 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(ucErrTime>250)

    {

      IIC_Type_t->IIC_Stop(IIC_Type_t);

return HAL_ERROR;

    }

  }

  IIC_SCL(IIC_Type_t,0);//时钟输出0     

return HAL_OK;  

}

//产生ACK应答

static void IIC_Ack_t(const struct IIC_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应答

static void IIC_NAck_t(const struct IIC_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_Send_Byte,入口参数为要发送的字节

static void IIC_Send_Byte_t(const struct IIC_Type* IIC_Type_t,uint8_t txd)     

{

     uint8_t t = 0;   

     SDA_OUT(IIC_Type_t);      

     IIC_SCL(IIC_Type_t,0);//拉低时钟开始数据传输

for(t=0;t<8;t++)

     {              

          IIC_SDA(IIC_Type_t,(txd&0x80)>>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_Send_Byte,入口参数为是否要发送ACK信号

static uint8_t IIC_Read_Byte_t(const struct IIC_Type* IIC_Type_t,uint8_t ack)     

文章来源于:电子工程世界    原文链接
本站所有转载文章系出于传递更多信息之目的,且明确注明来源,不希望被转载的媒体或个人可与我们联系,我们将立即进行删除处理。

我们与500+贴片厂合作,完美满足客户的定制需求。为品牌提供定制化的推广方案、专属产品特色页,多渠道推广,SEM/SEO精准营销以及与公众号的联合推广...详细>>

利用葫芦芯平台的卓越技术服务和新产品推广能力,原厂代理能轻松打入消费物联网(IOT)、信息与通信(ICT)、汽车及新能源汽车、工业自动化及工业物联网、装备及功率电子...详细>>

充分利用其强大的电子元器件采购流量,创新性地为这些物料提供了一个全新的窗口。我们的高效数字营销技术,不仅可以助你轻松识别与连接到需求方,更能够极大地提高“闲置物料”的处理能力,通过葫芦芯平台...详细>>

我们的目标很明确:构建一个全方位的半导体产业生态系统。成为一家全球领先的半导体互联网生态公司。目前,我们已成功打造了智能汽车、智能家居、大健康医疗、机器人和材料等五大生态领域。更为重要的是...详细>>

我们深知加工与定制类服务商的价值和重要性,因此,我们倾力为您提供最顶尖的营销资源。在我们的平台上,您可以直接接触到100万的研发工程师和采购工程师,以及10万的活跃客户群体...详细>>

凭借我们强大的专业流量和尖端的互联网数字营销技术,我们承诺为原厂提供免费的产品资料推广服务。无论是最新的资讯、技术动态还是创新产品,都可以通过我们的平台迅速传达给目标客户...详细>>

我们不止于将线索转化为潜在客户。葫芦芯平台致力于形成业务闭环,从引流、宣传到最终销售,全程跟进,确保每一个potential lead都得到妥善处理,从而大幅提高转化率。不仅如此...详细>>