你的浏览器版本过低,可能导致网站不能正常访问!
为了你能正常使用网站功能,请使用这些浏览器。

STM32开发中使用C语言实现IIC驱动

[复制链接]
STMCU-管管 发布时间:2021-6-21 14:39
简述
    IIC(Inter-Integrated Circuit)其实是IICBus简称,它是一种串行通信总线,使用多主从架构,在STM32开发中经常见到。


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


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


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


IIC驱动面向对象封装
    iic.h头文件主要是类模板的定义,具体如下:

  1. //定义IIC类
  2. typedef struct IIC_Type
  3. {
  4.    //属性
  5.    GPIO_TypeDef  *GPIOx_SCL;  //GPIO_SCL所属的GPIO组(如:GPIOA)
  6.    GPIO_TypeDef  *GPIOx_SDA;  //GPIO_SDA所属的GPIO组(如:GPIOA)
  7.    uint32_t GPIO_SCL;     //GPIO_SCL的IO引脚(如:GPIO_PIN_0)
  8.    uint32_t GPIO_SDA;     //GPIO_SDA的IO引脚(如:GPIO_PIN_0)
  9.    //操作
  10.    void (*IIC_Init)(const struct IIC_Type*);        //IIC_Init
  11.    void (*IIC_Start)(const struct IIC_Type*);       //IIC_Start
  12.    void (*IIC_Stop)(const struct IIC_Type*);        //IIC_Stop
  13.    uint8_t (*IIC_Wait_Ack)(const struct IIC_Type*);    //IIC_Wait_ack,返回wait失败或是成功
  14.    void (*IIC_Ack)(const struct IIC_Type*);       //IIC_Ack,IIC发送ACK信号
  15.    void (*IIC_NAck)(const struct IIC_Type*);       //IIC_NAck,IIC发送NACK信号
  16.    void (*IIC_Send_Byte)(const struct IIC_Type*,uint8_t);       //IIC_Send_Byte,入口参数为要发送的字节
  17.    uint8_t (*IIC_Read_Byte)(const struct IIC_Type*,uint8_t);     //IIC_Send_Byte,入口参数为是否要发送ACK信号
  18.    void (*delay_us)(uint32_t);              //us延时
  19. }IIC_TypeDef;
复制代码
  iic.c源文件主要是类模板具体操作函数的实现,具体如下:
  1. //设置SDA为输入模式
  2. static void SDA_IN(const struct IIC_Type* IIC_Type_t)
  3. {
  4.   uint8_t io_num = 0;  //定义io Num号
  5.   switch(IIC_Type_t->GPIO_SDA)
  6.   {
  7.    case GPIO_PIN_0:
  8.     io_num = 0;
  9.    break;
  10.    case GPIO_PIN_1:
  11.     io_num = 1;
  12.    break;
  13.    case GPIO_PIN_2:
  14.     io_num = 2;
  15.    break;
  16.    case GPIO_PIN_3:
  17.     io_num = 3;
  18.    break;
  19.    case GPIO_PIN_4:
  20.     io_num = 4;
  21.    break;
  22.     case GPIO_PIN_5:
  23.     io_num = 5;
  24.    break;
  25.    case GPIO_PIN_6:
  26.     io_num = 6;
  27.    break;
  28.    case GPIO_PIN_7:
  29.     io_num = 7;
  30.    break;
  31.    case GPIO_PIN_8:
  32.     io_num = 8;
  33.    break;
  34.    case GPIO_PIN_9:
  35.     io_num = 9;
  36.    break;
  37.    case GPIO_PIN_10:
  38.     io_num = 10;
  39.    break;
  40.    case GPIO_PIN_11:
  41.     io_num = 11;
  42.    break;
  43.    case GPIO_PIN_12:
  44.     io_num = 12;
  45.    break;
  46.    case GPIO_PIN_13:
  47.     io_num = 13;
  48.    break;
  49.    case GPIO_PIN_14:
  50.     io_num = 14;
  51.    break;
  52.    case GPIO_PIN_15:
  53.     io_num = 15;
  54.    break;
  55.   }
  56.   IIC_Type_t->GPIOx_SDA->MODER&=~(3<<(io_num*2)); //将GPIOx_SDA->GPIO_SDA清零
  57.   IIC_Type_t->GPIOx_SDA->MODER|=0<<(io_num*2);   //将GPIOx_SDA->GPIO_SDA设置为输入模式
  58. }

  59. //设置SDA为输出模式
  60. static void SDA_OUT(const struct IIC_Type* IIC_Type_t)
  61. {
  62.   uint8_t io_num = 0;  //定义io Num号
  63.   switch(IIC_Type_t->GPIO_SDA)
  64.   {
  65.    case GPIO_PIN_0:
  66.     io_num = 0;
  67.    break;
  68.    case GPIO_PIN_1:
  69.     io_num = 1;
  70.    break;
  71.    case GPIO_PIN_2:
  72.     io_num = 2;
  73.    break;
  74.    case GPIO_PIN_3:
  75.     io_num = 3;
  76.    break;
  77.    case GPIO_PIN_4:
  78.     io_num = 4;
  79.    break;
  80.     case GPIO_PIN_5:
  81.     io_num = 5;
  82.    break;
  83.    case GPIO_PIN_6:
  84.     io_num = 6;
  85.    break;
  86.    case GPIO_PIN_7:
  87.     io_num = 7;
  88.    break;
  89.    case GPIO_PIN_8:
  90.     io_num = 8;
  91.    break;
  92.    case GPIO_PIN_9:
  93.     io_num = 9;
  94.    break;
  95.    case GPIO_PIN_10:
  96.     io_num = 10;
  97.    break;
  98.    case GPIO_PIN_11:
  99.     io_num = 11;
  100.    break;
  101.    case GPIO_PIN_12:
  102.     io_num = 12;
  103.    break;
  104.    case GPIO_PIN_13:
  105.     io_num = 13;
  106.    break;
  107.    case GPIO_PIN_14:
  108.     io_num = 14;
  109.    break;
  110.    case GPIO_PIN_15:
  111.     io_num = 15;
  112.    break;
  113.   }
  114.   IIC_Type_t->GPIOx_SDA->MODER&=~(3<<(io_num*2)); //将GPIOx_SDA->GPIO_SDA清零
  115.   IIC_Type_t->GPIOx_SDA->MODER|=1<<(io_num*2);   //将GPIOx_SDA->GPIO_SDA设置为输出模式
  116. }
  117. //设置SCL电平
  118. static void IIC_SCL(const struct IIC_Type* IIC_Type_t,int n)
  119. {
  120.   if(n == 1)
  121.   {
  122.     HAL_GPIO_WritePin(IIC_Type_t->GPIOx_SCL,IIC_Type_t->GPIO_SCL,GPIO_PIN_SET);     //设置SCL为高电平
  123.   }
  124.   else{
  125.     HAL_GPIO_WritePin(IIC_Type_t->GPIOx_SCL,IIC_Type_t->GPIO_SCL,GPIO_PIN_RESET);     //设置SCL为低电平
  126.   }
  127. }
  128. //设置SDA电平
  129. static void IIC_SDA(const struct IIC_Type* IIC_Type_t,int n)
  130. {
  131.   if(n == 1)
  132.   {
  133.     HAL_GPIO_WritePin(IIC_Type_t->GPIOx_SDA,IIC_Type_t->GPIO_SDA,GPIO_PIN_SET);     //设置SDA为高电平
  134.   }
  135.   else{
  136.     HAL_GPIO_WritePin(IIC_Type_t->GPIOx_SDA,IIC_Type_t->GPIO_SDA,GPIO_PIN_RESET);     //设置SDA为低电平
  137.   }
  138. }
  139. //读取SDA电平
  140. static uint8_t READ_SDA(const struct IIC_Type* IIC_Type_t)
  141. {
  142.   return HAL_GPIO_ReadPin(IIC_Type_t->GPIOx_SDA,IIC_Type_t->GPIO_SDA);  //读取SDA电平
  143. }
  144. //IIC初始化
  145. static void IIC_Init_t(const struct IIC_Type* IIC_Type_t)
  146. {
  147.       GPIO_InitTypeDef GPIO_Initure;
  148.    
  149.    //根据GPIO组初始化GPIO时钟
  150.    if(IIC_Type_t->GPIOx_SCL == GPIOA || IIC_Type_t->GPIOx_SDA == GPIOA)
  151.    {
  152.      __HAL_RCC_GPIOA_CLK_ENABLE();   //使能GPIOA时钟
  153.    }
  154.    if(IIC_Type_t->GPIOx_SCL == GPIOB || IIC_Type_t->GPIOx_SDA == GPIOB)
  155.    {
  156.      __HAL_RCC_GPIOB_CLK_ENABLE();   //使能GPIOB时钟
  157.    }
  158.    if(IIC_Type_t->GPIOx_SCL == GPIOC || IIC_Type_t->GPIOx_SDA == GPIOC)
  159.    {
  160.      __HAL_RCC_GPIOC_CLK_ENABLE();   //使能GPIOC时钟
  161.    }
  162.    if(IIC_Type_t->GPIOx_SCL == GPIOD || IIC_Type_t->GPIOx_SDA == GPIOD)
  163.    {
  164.      __HAL_RCC_GPIOD_CLK_ENABLE();   //使能GPIOD时钟
  165.    }
  166.    if(IIC_Type_t->GPIOx_SCL == GPIOE || IIC_Type_t->GPIOx_SDA == GPIOE)
  167.    {
  168.      __HAL_RCC_GPIOE_CLK_ENABLE();   //使能GPIOE时钟
  169.    }
  170.    if(IIC_Type_t->GPIOx_SCL == GPIOH || IIC_Type_t->GPIOx_SDA == GPIOH)
  171.    {
  172.      __HAL_RCC_GPIOH_CLK_ENABLE();   //使能GPIOH时钟
  173.    }     
  174.    
  175.      //GPIO_SCL初始化设置
  176.      GPIO_Initure.Pin=IIC_Type_t->GPIO_SCL;
  177.      GPIO_Initure.Mode=GPIO_MODE_OUTPUT_PP;  //推挽输出
  178.      GPIO_Initure.Pull=GPIO_PULLUP;          //上拉
  179.      GPIO_Initure.Speed=GPIO_SPEED_FREQ_VERY_HIGH;    //快速
  180.      HAL_GPIO_Init(IIC_Type_t->GPIOx_SCL,&GPIO_Initure);
  181.      //GPIO_SDA初始化设置
  182.      GPIO_Initure.Pin=IIC_Type_t->GPIO_SDA;
  183.      GPIO_Initure.Mode=GPIO_MODE_OUTPUT_PP;  //推挽输出
  184.      GPIO_Initure.Pull=GPIO_PULLUP;          //上拉
  185.      GPIO_Initure.Speed=GPIO_SPEED_FREQ_VERY_HIGH;    //快速
  186.      HAL_GPIO_Init(IIC_Type_t->GPIOx_SDA,&GPIO_Initure);
  187.      
  188.             //SCL与SDA的初始化均为高电平
  189.       IIC_SCL(IIC_Type_t,1);
  190.        IIC_SDA(IIC_Type_t,1);
  191. }
  192. //IIC Start
  193. static void IIC_Start_t(const struct IIC_Type* IIC_Type_t)
  194. {
  195.   SDA_OUT(IIC_Type_t);      //sda线输出
  196.   IIC_SDA(IIC_Type_t,1);      
  197.   IIC_SCL(IIC_Type_t,1);
  198.   IIC_Type_t->delay_us(4);
  199.    IIC_SDA(IIC_Type_t,0);  //START:when CLK is high,DATA change form high to low
  200.   IIC_Type_t->delay_us(4);
  201.   IIC_SCL(IIC_Type_t,0);  //钳住I2C总线,准备发送或接收数据
  202. }
  203. //IIC Stop
  204. static void IIC_Stop_t(const struct IIC_Type* IIC_Type_t)
  205. {
  206.   SDA_OUT(IIC_Type_t); //sda线输出
  207.   IIC_SCL(IIC_Type_t,0);
  208.   IIC_SDA(IIC_Type_t,0); //STOP:when CLK is high DATA change form low to high
  209.    IIC_Type_t->delay_us(4);
  210.   IIC_SCL(IIC_Type_t,1);
  211.   IIC_SDA(IIC_Type_t,1); //发送I2C总线结束信号
  212.   IIC_Type_t->delay_us(4);
  213. }
  214. //IIC_Wait_ack 返回HAL_OK表示wait成功,返回HAL_ERROR表示wait失败
  215. static uint8_t IIC_Wait_Ack_t(const struct IIC_Type* IIC_Type_t)   //IIC_Wait_ack,返回wait失败或是成功
  216. {
  217.   uint8_t ucErrTime = 0;
  218.   SDA_IN(IIC_Type_t);      //SDA设置为输入  
  219.   IIC_SDA(IIC_Type_t,1);IIC_Type_t->delay_us(1);   
  220.   IIC_SCL(IIC_Type_t,1);IIC_Type_t->delay_us(1);
  221.   while(READ_SDA(IIC_Type_t))
  222.   {
  223.     ucErrTime++;
  224.     if(ucErrTime>250)
  225.     {
  226.       IIC_Type_t->IIC_Stop(IIC_Type_t);
  227.       return HAL_ERROR;
  228.     }
  229.   }
  230.   IIC_SCL(IIC_Type_t,0);//时钟输出0     
  231.   return HAL_OK;  
  232. }
  233. //产生ACK应答
  234. static void IIC_Ack_t(const struct IIC_Type* IIC_Type_t)      
  235. {
  236.   IIC_SCL(IIC_Type_t,0);
  237.   SDA_OUT(IIC_Type_t);
  238.   IIC_SDA(IIC_Type_t,0);
  239.   IIC_Type_t->delay_us(2);  
  240.   IIC_SCL(IIC_Type_t,1);
  241.   IIC_Type_t->delay_us(2);  
  242.   IIC_SCL(IIC_Type_t,0);
  243. }
  244. //产生NACK应答
  245. static void IIC_NAck_t(const struct IIC_Type* IIC_Type_t)      
  246. {
  247.   IIC_SCL(IIC_Type_t,0);
  248.   SDA_OUT(IIC_Type_t);
  249.   IIC_SDA(IIC_Type_t,1);
  250.   IIC_Type_t->delay_us(2);  
  251.   IIC_SCL(IIC_Type_t,1);
  252.   IIC_Type_t->delay_us(2);  
  253.   IIC_SCL(IIC_Type_t,0);
  254. }
  255. //IIC_Send_Byte,入口参数为要发送的字节
  256. static void IIC_Send_Byte_t(const struct IIC_Type* IIC_Type_t,uint8_t txd)     
  257. {
  258.      uint8_t t = 0;   
  259.      SDA_OUT(IIC_Type_t);      
  260.      IIC_SCL(IIC_Type_t,0);//拉低时钟开始数据传输
  261.      for(t=0;t<8;t++)
  262.      {              
  263.           IIC_SDA(IIC_Type_t,(txd&0x80)>>7);
  264.           txd <<= 1;   
  265.        IIC_Type_t->delay_us(2);     //对TEA5767这三个延时都是必须的
  266.        IIC_SCL(IIC_Type_t,1);
  267.        IIC_Type_t->delay_us(2);  
  268.        IIC_SCL(IIC_Type_t,0);
  269.        IIC_Type_t->delay_us(2);  
  270.      }  
  271. }
  272. //IIC_Send_Byte,入口参数为是否要发送ACK信号
  273. static uint8_t IIC_Read_Byte_t(const struct IIC_Type* IIC_Type_t,uint8_t ack)     
  274. {
  275.    uint8_t i,receive = 0;
  276.    SDA_IN(IIC_Type_t);//SDA设置为输入
  277.    for(i=0;i<8;i++ )
  278.    {
  279.       IIC_SCL(IIC_Type_t,0);
  280.       IIC_Type_t->delay_us(2);
  281.       IIC_SCL(IIC_Type_t,1);
  282.       receive<<=1;
  283.       if(READ_SDA(IIC_Type_t))receive++;   
  284.       IIC_Type_t->delay_us(1);
  285.    }      
  286.   if (!ack)
  287.          IIC_Type_t->IIC_NAck(IIC_Type_t);//发送nACK
  288.   else
  289.          IIC_Type_t->IIC_Ack(IIC_Type_t); //发送ACK   
  290.   return receive;
  291. }
  292. //实例化一个IIC1外设,相当于一个结构体变量,可以直接在其他文件中使用
  293. IIC_TypeDef IIC1 = {
  294.   .GPIOx_SCL = GPIOA,   //GPIO组为GPIOA
  295.   .GPIOx_SDA = GPIOA,   //GPIO组为GPIOA
  296.   .GPIO_SCL = GPIO_PIN_5,   //GPIO为PIN5
  297.   .GPIO_SDA = GPIO_PIN_6,  //GPIO为PIN6
  298.   .IIC_Init = IIC_Init_t,
  299.   .IIC_Start = IIC_Start_t,
  300.   .IIC_Stop = IIC_Stop_t,
  301.   .IIC_Wait_Ack = IIC_Wait_Ack_t,
  302.   .IIC_Ack = IIC_Ack_t,
  303.   .IIC_NAck = IIC_NAck_t,
  304.   .IIC_Send_Byte = IIC_Send_Byte_t,
  305.   .IIC_Read_Byte = IIC_Read_Byte_t,
  306.   .delay_us = delay_us     //需自己外部实现delay_us函数
  307. };
复制代码
上述就是IIC驱动的封装,由于没有应用场景暂不测试其实用性,待下面ATC64的驱动缝缝扎黄写完之后一起测试使用。


ATC64XX驱动封装实现
    at24cxx.h头文件主要是类模板的定义,具体如下:

  1. // 以下是共定义个具体容量存储器的容量
  2. #define AT24C01  127
  3. #define AT24C02  255
  4. #define AT24C04  511
  5. #define AT24C08  1023
  6. #define AT24C16  2047
  7. #define AT24C32  4095
  8. #define AT24C64   8191         //8KBytes
  9. #define AT24C128 16383
  10. #define AT24C256 32767  

  11. //定义AT24CXX类
  12. typedef struct AT24CXX_Type
  13. {
  14.   //属性
  15.   u32 EEP_TYPE;           //存储器类型(存储器容量)
  16.   //操作
  17.   IIC_TypeDef IIC;       //IIC驱动
  18.   uint8_t (*AT24CXX_ReadOneByte)(const struct AT24CXX_Type*,uint16_t);  //指定地址读取一个字节
  19.   void (*AT24CXX_WriteOneByte)(const struct AT24CXX_Type*,uint16_t,uint8_t); //指定地址写入一个字节
  20.   void (*AT24CXX_WriteLenByte)(uint16_t,uint32_t,uint8_t); //指定地址开始写入指定长度的数据
  21.   uint32_t (*AT24CXX_ReadLenByte)(uint16_t,uint8_t);   //指定地址开始读取指定长度数据
  22.   void (*AT24CXX_Write)(uint16_t,uint8_t *,uint16_t);  //指定地址开始写入指定长度的数据
  23.   void (*AT24CXX_Read)(uint16_t,uint8_t *,uint16_t);   //指定地址开始写入指定长度的数据
  24.   void (*AT24CXX_Init)(const struct AT24CXX_Type*); //初始化IIC
  25.   uint8_t (*AT24CXX_Check)(const struct AT24CXX_Type*);   //检查器件
  26. }AT24CXX_TypeDef;

  27. extern AT24CXX_TypeDef AT24C_64;     //外部声明实例化AT24CXX对象
复制代码
at24cxx.c源文件主要是类模板具体操作函数的实现,具体如下:
  1. //在AT24CXX指定地址读出一个数据
  2. //ReadAddr:开始读数的地址  
  3. //返回值  :读到的数据
  4. static uint8_t AT24CXX_ReadOneByte_t(const struct AT24CXX_Type* AT24CXX_Type_t,uint16_t ReadAddr)
  5. {      
  6.   uint8_t temp=0;                          
  7.   AT24CXX_Type_t->IIC.IIC_Start(&AT24CXX_Type_t->IIC);  
  8.   //根据AT的型号发送不同的地址
  9.   if(AT24CXX_Type_t->EEP_TYPE > AT24C16)
  10.   {
  11.     AT24CXX_Type_t->IIC.IIC_Send_Byte(&AT24CXX_Type_t->IIC,0XA0);    //发送写命令
  12.     AT24CXX_Type_t->IIC.IIC_Wait_Ack(&AT24CXX_Type_t->IIC);
  13.     AT24CXX_Type_t->IIC.IIC_Send_Byte(&AT24CXX_Type_t->IIC,ReadAddr>>8);//发送高地址     
  14.   }else AT24CXX_Type_t->IIC.IIC_Send_Byte(&AT24CXX_Type_t->IIC,0XA0+((ReadAddr/256)<<1));   //发送器件地址0XA0,写数据     
  15.   AT24CXX_Type_t->IIC.IIC_Wait_Ack(&AT24CXX_Type_t->IIC);
  16.   AT24CXX_Type_t->IIC.IIC_Send_Byte(&AT24CXX_Type_t->IIC,ReadAddr%256);   //发送低地址
  17.   AT24CXX_Type_t->IIC.IIC_Wait_Ack(&AT24CXX_Type_t->IIC);     
  18.   AT24CXX_Type_t->IIC.IIC_Start(&AT24CXX_Type_t->IIC);        
  19.   AT24CXX_Type_t->IIC.IIC_Send_Byte(&AT24CXX_Type_t->IIC,0XA1);           //进入接收模式      
  20.   AT24CXX_Type_t->IIC.IIC_Wait_Ack(&AT24CXX_Type_t->IIC);  
  21.   temp=AT24CXX_Type_t->IIC.IIC_Read_Byte(&AT24CXX_Type_t->IIC,0);     
  22.   AT24CXX_Type_t->IIC.IIC_Stop(&AT24CXX_Type_t->IIC);//产生一个停止条件     
  23.   return temp;
  24. }
  25. //在AT24CXX指定地址写入一个数据
  26. //WriteAddr  :写入数据的目的地址   
  27. //DataToWrite:要写入的数据
  28. static void AT24CXX_WriteOneByte_t(const struct AT24CXX_Type* AT24CXX_Type_t,uint16_t WriteAddr,uint8_t DataToWrite)
  29. {                                
  30.    AT24CXX_Type_t->IIC.IIC_Start(&AT24CXX_Type_t->IIC);   
  31.   if(AT24CXX_Type_t->EEP_TYPE > AT24C16)
  32.   {
  33.     AT24CXX_Type_t->IIC.IIC_Send_Byte(&AT24CXX_Type_t->IIC,0XA0);    //发送写命令
  34.     AT24CXX_Type_t->IIC.IIC_Wait_Ack(&AT24CXX_Type_t->IIC);
  35.     AT24CXX_Type_t->IIC.IIC_Send_Byte(&AT24CXX_Type_t->IIC,WriteAddr>>8);//发送高地址     
  36.   }else AT24CXX_Type_t->IIC.IIC_Send_Byte(&AT24CXX_Type_t->IIC,0XA0+((WriteAddr/256)<<1));   //发送器件地址0XA0,写数据     
  37.   AT24CXX_Type_t->IIC.IIC_Wait_Ack(&AT24CXX_Type_t->IIC);
  38.    AT24CXX_Type_t->IIC.IIC_Send_Byte(&AT24CXX_Type_t->IIC,WriteAddr%256);   //发送低地址
  39.   AT24CXX_Type_t->IIC.IIC_Wait_Ack(&AT24CXX_Type_t->IIC);               
  40.   AT24CXX_Type_t->IIC.IIC_Send_Byte(&AT24CXX_Type_t->IIC,DataToWrite);     //发送字节         
  41.   AT24CXX_Type_t->IIC.IIC_Wait_Ack(&AT24CXX_Type_t->IIC);            
  42.    AT24CXX_Type_t->IIC.IIC_Stop(&AT24CXX_Type_t->IIC);//产生一个停止条件
  43. AT24CXX_Type_t->IIC.delay_us(10000);  
  44. }
  45. //在AT24CXX里面的指定地址开始写入长度为Len的数据
  46. //该函数用于写入16bit或者32bit的数据.
  47. //WriteAddr  :开始写入的地址  
  48. //DataToWrite:数据数组首地址
  49. //Len        :要写入数据的长度2,4
  50. static void AT24CXX_WriteLenByte_t(uint16_t WriteAddr,uint32_t DataToWrite,uint8_t Len)
  51. {   
  52.   uint8_t t;
  53.   for(t=0;t<Len;t++)
  54.   {
  55.     AT24CXX_WriteOneByte(WriteAddr+t,(DataToWrite>>(8*t))&0xff);
  56.   }               
  57. }
  58. //在AT24CXX里面的指定地址开始读出长度为Len的数据
  59. //该函数用于读出16bit或者32bit的数据.
  60. //ReadAddr   :开始读出的地址
  61. //返回值     :数据
  62. //Len        :要读出数据的长度2,4
  63. static uint32_t AT24CXX_ReadLenByte_t(uint16_t ReadAddr,uint8_t Len)
  64. {   
  65.   uint8_t t;
  66.   uint32_t temp=0;
  67.   for(t=0;t<Len;t++)
  68.   {
  69.     temp<<=8;
  70.      temp+=AT24CXX_ReadOneByte(ReadAddr+Len-t-1);         
  71.   }
  72.   return temp;               
  73. }
  74. //在AT24CXX里面的指定地址开始写入指定个数的数据
  75. //WriteAddr :开始写入的地址 对24c64为0~8191
  76. //pBuffer   :数据数组首地址
  77. //NumToWrite:要写入数据的个数
  78. static void AT24CXX_Write_t(uint16_t WriteAddr,uint8_t *pBuffer,uint16_t NumToWrite)
  79. {
  80.   while(NumToWrite--)
  81.   {
  82.    AT24CXX_WriteOneByte(WriteAddr,*pBuffer);
  83.     WriteAddr++;
  84.     pBuffer++;
  85.   }
  86. }
  87. //在AT24CXX里面的指定地址开始读出指定个数的数据
  88. //ReadAddr :开始读出的地址 对24c64为0~8191
  89. //pBuffer  :数据数组首地址
  90. //NumToRead:要读出数据的个数
  91. static void AT24CXX_Read_t(uint16_t ReadAddr,uint8_t *pBuffer,uint16_t NumToRead)
  92. {
  93.   while(NumToRead)
  94.   {
  95.     *pBuffer++=AT24CXX_ReadOneByte(ReadAddr++);
  96.     NumToRead--;
  97.   }
  98. }
  99. //初始化IIC接口
  100. static void AT24CXX_Init_t(const struct AT24CXX_Type* AT24CXX_Type_t)
  101. {
  102.   AT24CXX_Type_t->IIC.IIC_Init(&AT24CXX_Type_t->IIC);//IIC初始化
  103. }
  104. //检查器件,返回0表示检测成功,返回1表示检测失败
  105. static uint8_t AT24CXX_Check_t(const struct AT24CXX_Type* AT24CXX_Type_t)   
  106. {
  107. uint8_t temp;
  108.   temp = AT24CXX_Type_t->AT24CXX_ReadOneByte(AT24CXX_Type_t,AT24CXX_Type_t->EEP_TYPE);//避免每次开机都写AT24CXX      
  109.   if(temp == 0X33)return 0;     
  110.   else//排除第一次初始化的情况
  111.   {
  112.       AT24CXX_Type_t->AT24CXX_WriteOneByte(AT24CXX_Type_t,AT24CXX_Type_t->EEP_TYPE,0X33);
  113.        temp = AT24CXX_Type_t->AT24CXX_ReadOneByte(AT24CXX_Type_t,AT24CXX_Type_t->EEP_TYPE);
  114.       if(temp==0X33)return 0;
  115.   }
  116.   return 1;  
  117. }
  118. //实例化AT24CXX对象
  119. AT24CXX_TypeDef AT24C_64={
  120. .EEP_TYPE = AT24C64,           //存储器类型(存储器容量)
  121. //操作
  122. .IIC={
  123.   .GPIOx_SCL = GPIOA,
  124.   .GPIOx_SDA = GPIOA,
  125.   .GPIO_SCL = GPIO_PIN_5,
  126.   .GPIO_SDA = GPIO_PIN_6,
  127.   .IIC_Init = IIC_Init_t,
  128.   .IIC_Start = IIC_Start_t,
  129.   .IIC_Stop = IIC_Stop_t,
  130.   .IIC_Wait_Ack = IIC_Wait_Ack_t,
  131.   .IIC_Ack = IIC_Ack_t,
  132.   .IIC_NAck = IIC_NAck_t,
  133.   .IIC_Send_Byte = IIC_Send_Byte_t,
  134.   .IIC_Read_Byte = IIC_Read_Byte_t,
  135.   .delay_us = delay_us
  136. },                   //IIC驱动
  137. .AT24CXX_ReadOneByte = AT24CXX_ReadOneByte_t,  //指定地址读取一个字节
  138. .AT24CXX_WriteOneByte = AT24CXX_WriteOneByte_t,//指定地址写入一个字节
  139. .AT24CXX_WriteLenByte = AT24CXX_WriteLenByte_t, //指定地址开始写入指定长度的数据
  140. .AT24CXX_ReadLenByte = AT24CXX_ReadLenByte_t,   //指定地址开始读取指定长度数据
  141. .AT24CXX_Write = AT24CXX_Write_t,  //指定地址开始写入指定长度的数据
  142. .AT24CXX_Read = AT24CXX_Read_t,   //指定地址开始读取指定长度的数据
  143. .AT24CXX_Init = AT24CXX_Init_t, //初始化IIC
  144. .AT24CXX_Check = AT24CXX_Check_t   //检查器件
  145. };
复制代码
简单分析:可以看出AT24CXX类中包含了IIC类的成员对象,这是一种包含关系,因为没有属性上的一致性因此谈不上继承。


    之所以将IIC的类对象作为AT24CXX类的成员是因为AT24CXX的实现需要调用IIC的成员方法,IIC相当于AT24CXX更下层的驱动,因此采用包含关系更合适。


    因此我们在使用AT24CXX的时候只需要实例化AT24CXX类对象就行了,因为IIC包含在AT24CXX类中间,因此不需要实例化IIC类对象,对外提供了较好的封装接口。下面我们看具体的调用方法。


主函数main调用测试
    在main函数中直接使用AT24C_64来完成所有操作,下面结合代码来看:


  1. #include "at24cxx.h"    //为了确定AT24C_64的成员方法和引用操作对象AT24C_64
  2. int main(void)
  3. {
  4.   /************省略其他初始化工作****************/
  5.   //第一步:调用对象初始化方法来初始化AT24C64
  6.   AT24C_64.AT24CXX_Init(&AT24C_64);
  7.   //第二步:调用对象检测方法来检测AT24C64           
  8.   if(AT24C_64.AT24CXX_Check(&AT24C_64) == 0)
  9.   {
  10.     printf("AT24C64检测成功\r\n");
  11.   }
  12.   else{
  13.     printf("AT24C64检测失败\r\n");
  14.   }
  15.   return 0;
  16. }
复制代码
可以看出所有的操作都是通过AT24C_64对象调用完成的,在我们初始化好AT24C_64对象之后就可以放心大胆的调用其成员方法,这样封装的好处就是一个设备对外只提供一个对象接口,简洁明了。


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



收藏 评论0 发布时间:2021-6-21 14:39

举报

0个回答

所属标签

关于
我们是谁
投资者关系
意法半导体可持续发展举措
创新与技术
意法半导体官网
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
官方最新发布
STM32N6 AI生态系统
STM32MCU,MPU高性能GUI
ST ACEPACK电源模块
意法半导体生物传感器
STM32Cube扩展软件包
关注我们
st-img 微信公众号
st-img 手机版