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

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

[复制链接]
STMCU小助手 发布时间:2022-10-8 16:57
简述

    IIC(Inter-Integrated Circuit)其实是IICBus简称,它是一种串行通信总线,使用多主从架构,在STM32开发中经常见到。
       使用面向对象的编程思想封装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. //根据GPIO组初始化GPIO时钟
  149. if(IIC_Type_t->GPIOx_SCL == GPIOA || IIC_Type_t->GPIOx_SDA == GPIOA)
  150.    {
  151.      __HAL_RCC_GPIOA_CLK_ENABLE();   //使能GPIOA时钟
  152.    }
  153. if(IIC_Type_t->GPIOx_SCL == GPIOB || IIC_Type_t->GPIOx_SDA == GPIOB)
  154.    {
  155.      __HAL_RCC_GPIOB_CLK_ENABLE();   //使能GPIOB时钟
  156.    }
  157. if(IIC_Type_t->GPIOx_SCL == GPIOC || IIC_Type_t->GPIOx_SDA == GPIOC)
  158.    {
  159.      __HAL_RCC_GPIOC_CLK_ENABLE();   //使能GPIOC时钟
  160.    }
  161. if(IIC_Type_t->GPIOx_SCL == GPIOD || IIC_Type_t->GPIOx_SDA == GPIOD)
  162.    {
  163.      __HAL_RCC_GPIOD_CLK_ENABLE();   //使能GPIOD时钟
  164.    }
  165. if(IIC_Type_t->GPIOx_SCL == GPIOE || IIC_Type_t->GPIOx_SDA == GPIOE)
  166.    {
  167.      __HAL_RCC_GPIOE_CLK_ENABLE();   //使能GPIOE时钟
  168.    }
  169. if(IIC_Type_t->GPIOx_SCL == GPIOH || IIC_Type_t->GPIOx_SDA == GPIOH)
  170.    {
  171.      __HAL_RCC_GPIOH_CLK_ENABLE();   //使能GPIOH时钟
  172.    }     

  173. //GPIO_SCL初始化设置
  174.      GPIO_Initure.Pin=IIC_Type_t->GPIO_SCL;
  175.      GPIO_Initure.Mode=GPIO_MODE_OUTPUT_PP;  //推挽输出
  176.      GPIO_Initure.Pull=GPIO_PULLUP;          //上拉
  177.      GPIO_Initure.Speed=GPIO_SPEED_FREQ_VERY_HIGH;    //快速
  178.      HAL_GPIO_Init(IIC_Type_t->GPIOx_SCL,&GPIO_Initure);
  179. //GPIO_SDA初始化设置
  180.      GPIO_Initure.Pin=IIC_Type_t->GPIO_SDA;
  181.      GPIO_Initure.Mode=GPIO_MODE_OUTPUT_PP;  //推挽输出
  182.      GPIO_Initure.Pull=GPIO_PULLUP;          //上拉
  183.      GPIO_Initure.Speed=GPIO_SPEED_FREQ_VERY_HIGH;    //快速
  184.      HAL_GPIO_Init(IIC_Type_t->GPIOx_SDA,&GPIO_Initure);

  185. //SCL与SDA的初始化均为高电平
  186.       IIC_SCL(IIC_Type_t,1);
  187.        IIC_SDA(IIC_Type_t,1);
  188. }
  189. //IIC Start
  190. static void IIC_Start_t(const struct IIC_Type* IIC_Type_t)
  191. {
  192.   SDA_OUT(IIC_Type_t);      //sda线输出
  193.   IIC_SDA(IIC_Type_t,1);      
  194.   IIC_SCL(IIC_Type_t,1);
  195.   IIC_Type_t->delay_us(4);
  196.    IIC_SDA(IIC_Type_t,0);  //START:when CLK is high,DATA change form high to low
  197.   IIC_Type_t->delay_us(4);
  198.   IIC_SCL(IIC_Type_t,0);  //钳住I2C总线,准备发送或接收数据
  199. }
  200. //IIC Stop
  201. static void IIC_Stop_t(const struct IIC_Type* IIC_Type_t)
  202. {
  203.   SDA_OUT(IIC_Type_t); //sda线输出
  204.   IIC_SCL(IIC_Type_t,0);
  205.   IIC_SDA(IIC_Type_t,0); //STOP:when CLK is high DATA change form low to high
  206.    IIC_Type_t->delay_us(4);
  207.   IIC_SCL(IIC_Type_t,1);
  208.   IIC_SDA(IIC_Type_t,1); //发送I2C总线结束信号
  209.   IIC_Type_t->delay_us(4);
  210. }
  211. //IIC_Wait_ack 返回HAL_OK表示wait成功,返回HAL_ERROR表示wait失败
  212. static uint8_t IIC_Wait_Ack_t(const struct IIC_Type* IIC_Type_t)   //IIC_Wait_ack,返回wait失败或是成功
  213. {
  214.   uint8_t ucErrTime = 0;
  215.   SDA_IN(IIC_Type_t);      //SDA设置为输入  
  216.   IIC_SDA(IIC_Type_t,1);IIC_Type_t->delay_us(1);   
  217.   IIC_SCL(IIC_Type_t,1);IIC_Type_t->delay_us(1);
  218. while(READ_SDA(IIC_Type_t))
  219.   {
  220.     ucErrTime++;
  221. if(ucErrTime>250)
  222.     {
  223.       IIC_Type_t->IIC_Stop(IIC_Type_t);
  224. return HAL_ERROR;
  225.     }
  226.   }
  227.   IIC_SCL(IIC_Type_t,0);//时钟输出0     
  228. return HAL_OK;  
  229. }
  230. //产生ACK应答
  231. static void IIC_Ack_t(const struct IIC_Type* IIC_Type_t)      
  232. {
  233.   IIC_SCL(IIC_Type_t,0);
  234.   SDA_OUT(IIC_Type_t);
  235.   IIC_SDA(IIC_Type_t,0);
  236.   IIC_Type_t->delay_us(2);  
  237.   IIC_SCL(IIC_Type_t,1);
  238.   IIC_Type_t->delay_us(2);  
  239.   IIC_SCL(IIC_Type_t,0);
  240. }
  241. //产生NACK应答
  242. static void IIC_NAck_t(const struct IIC_Type* IIC_Type_t)      
  243. {
  244.   IIC_SCL(IIC_Type_t,0);
  245.   SDA_OUT(IIC_Type_t);
  246.   IIC_SDA(IIC_Type_t,1);
  247.   IIC_Type_t->delay_us(2);  
  248.   IIC_SCL(IIC_Type_t,1);
  249.   IIC_Type_t->delay_us(2);  
  250.   IIC_SCL(IIC_Type_t,0);
  251. }
  252. //IIC_Send_Byte,入口参数为要发送的字节
  253. static void IIC_Send_Byte_t(const struct IIC_Type* IIC_Type_t,uint8_t txd)     
  254. {
  255.      uint8_t t = 0;   
  256.      SDA_OUT(IIC_Type_t);      
  257.      IIC_SCL(IIC_Type_t,0);//拉低时钟开始数据传输
  258. for(t=0;t<8;t++)
  259.      {              
  260.           IIC_SDA(IIC_Type_t,(txd&0x80)>>7);
  261.           txd <<= 1;   
  262.        IIC_Type_t->delay_us(2);     //对TEA5767这三个延时都是必须的
  263.        IIC_SCL(IIC_Type_t,1);
  264.        IIC_Type_t->delay_us(2);  
  265.        IIC_SCL(IIC_Type_t,0);
  266.        IIC_Type_t->delay_us(2);  
  267.      }  
  268. }
  269. //IIC_Send_Byte,入口参数为是否要发送ACK信号
  270. static uint8_t IIC_Read_Byte_t(const struct IIC_Type* IIC_Type_t,uint8_t ack)     
  271. {
  272.    uint8_t i,receive = 0;
  273.    SDA_IN(IIC_Type_t);//SDA设置为输入
  274. for(i=0;i<8;i++ )
  275.    {
  276.       IIC_SCL(IIC_Type_t,0);
  277.       IIC_Type_t->delay_us(2);
  278.       IIC_SCL(IIC_Type_t,1);
  279.       receive<<=1;
  280. if(READ_SDA(IIC_Type_t))receive++;   
  281.       IIC_Type_t->delay_us(1);
  282.    }      
  283. if (!ack)
  284.          IIC_Type_t->IIC_NAck(IIC_Type_t);//发送nACK
  285. else
  286.          IIC_Type_t->IIC_Ack(IIC_Type_t); //发送ACK   
  287. return receive;
  288. }
  289. //实例化一个IIC1外设,相当于一个结构体变量,可以直接在其他文件中使用
  290. IIC_TypeDef IIC1 = {
  291.   .GPIOx_SCL = GPIOA,   //GPIO组为GPIOA
  292.   .GPIOx_SDA = GPIOA,   //GPIO组为GPIOA
  293.   .GPIO_SCL = GPIO_PIN_5,   //GPIO为PIN5
  294.   .GPIO_SDA = GPIO_PIN_6,  //GPIO为PIN6
  295.   .IIC_Init = IIC_Init_t,
  296.   .IIC_Start = IIC_Start_t,
  297.   .IIC_Stop = IIC_Stop_t,
  298.   .IIC_Wait_Ack = IIC_Wait_Ack_t,
  299.   .IIC_Ack = IIC_Ack_t,
  300.   .IIC_NAck = IIC_NAck_t,
  301.   .IIC_Send_Byte = IIC_Send_Byte_t,
  302.   .IIC_Read_Byte = IIC_Read_Byte_t,
  303.   .delay_us = delay_us     //需自己外部实现delay_us函数
  304. };
复制代码



   上述就是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对象
  28.     at24cxx.c源文件主要是类模板具体操作函数的实现,具体如下:
  29. //在AT24CXX指定地址读出一个数据
  30. //ReadAddr:开始读数的地址  
  31. //返回值  :读到的数据
  32. static uint8_t AT24CXX_ReadOneByte_t(const struct AT24CXX_Type* AT24CXX_Type_t,uint16_t ReadAddr)
  33. {      
  34.   uint8_t temp=0;                          
  35.   AT24CXX_Type_t->IIC.IIC_Start(&AT24CXX_Type_t->IIC);  
  36. //根据AT的型号发送不同的地址
  37. if(AT24CXX_Type_t->EEP_TYPE > AT24C16)
  38.   {
  39.     AT24CXX_Type_t->IIC.IIC_Send_Byte(&AT24CXX_Type_t->IIC,0XA0);    //发送写命令
  40.     AT24CXX_Type_t->IIC.IIC_Wait_Ack(&AT24CXX_Type_t->IIC);
  41.     AT24CXX_Type_t->IIC.IIC_Send_Byte(&AT24CXX_Type_t->IIC,ReadAddr>>8);//发送高地址     
  42.   }else AT24CXX_Type_t->IIC.IIC_Send_Byte(&AT24CXX_Type_t->IIC,0XA0+((ReadAddr/256)<<1));   //发送器件地址0XA0,写数据     
  43.   AT24CXX_Type_t->IIC.IIC_Wait_Ack(&AT24CXX_Type_t->IIC);
  44.   AT24CXX_Type_t->IIC.IIC_Send_Byte(&AT24CXX_Type_t->IIC,ReadAddr%256);   //发送低地址
  45.   AT24CXX_Type_t->IIC.IIC_Wait_Ack(&AT24CXX_Type_t->IIC);     
  46.   AT24CXX_Type_t->IIC.IIC_Start(&AT24CXX_Type_t->IIC);        
  47.   AT24CXX_Type_t->IIC.IIC_Send_Byte(&AT24CXX_Type_t->IIC,0XA1);           //进入接收模式      
  48.   AT24CXX_Type_t->IIC.IIC_Wait_Ack(&AT24CXX_Type_t->IIC);  
  49.   temp=AT24CXX_Type_t->IIC.IIC_Read_Byte(&AT24CXX_Type_t->IIC,0);     
  50.   AT24CXX_Type_t->IIC.IIC_Stop(&AT24CXX_Type_t->IIC);//产生一个停止条件     
  51. return temp;
  52. }
  53. //在AT24CXX指定地址写入一个数据
  54. //WriteAddr  :写入数据的目的地址   
  55. //DataToWrite:要写入的数据
  56. static void AT24CXX_WriteOneByte_t(const struct AT24CXX_Type* AT24CXX_Type_t,uint16_t WriteAddr,uint8_t DataToWrite)
  57. {                                
  58.    AT24CXX_Type_t->IIC.IIC_Start(&AT24CXX_Type_t->IIC);   
  59. if(AT24CXX_Type_t->EEP_TYPE > AT24C16)
  60.   {
  61.     AT24CXX_Type_t->IIC.IIC_Send_Byte(&AT24CXX_Type_t->IIC,0XA0);    //发送写命令
  62.     AT24CXX_Type_t->IIC.IIC_Wait_Ack(&AT24CXX_Type_t->IIC);
  63.     AT24CXX_Type_t->IIC.IIC_Send_Byte(&AT24CXX_Type_t->IIC,WriteAddr>>8);//发送高地址     
  64.   }else AT24CXX_Type_t->IIC.IIC_Send_Byte(&AT24CXX_Type_t->IIC,0XA0+((WriteAddr/256)<<1));   //发送器件地址0XA0,写数据     
  65.   AT24CXX_Type_t->IIC.IIC_Wait_Ack(&AT24CXX_Type_t->IIC);
  66.    AT24CXX_Type_t->IIC.IIC_Send_Byte(&AT24CXX_Type_t->IIC,WriteAddr%256);   //发送低地址
  67.   AT24CXX_Type_t->IIC.IIC_Wait_Ack(&AT24CXX_Type_t->IIC);               
  68.   AT24CXX_Type_t->IIC.IIC_Send_Byte(&AT24CXX_Type_t->IIC,DataToWrite);     //发送字节         
  69.   AT24CXX_Type_t->IIC.IIC_Wait_Ack(&AT24CXX_Type_t->IIC);            
  70.    AT24CXX_Type_t->IIC.IIC_Stop(&AT24CXX_Type_t->IIC);//产生一个停止条件
  71. AT24CXX_Type_t->IIC.delay_us(10000);  
  72. }
  73. //在AT24CXX里面的指定地址开始写入长度为Len的数据
  74. //该函数用于写入16bit或者32bit的数据.
  75. //WriteAddr  :开始写入的地址  
  76. //DataToWrite:数据数组首地址
  77. //Len        :要写入数据的长度2,4
  78. static void AT24CXX_WriteLenByte_t(uint16_t WriteAddr,uint32_t DataToWrite,uint8_t Len)
  79. {   
  80.   uint8_t t;
  81. for(t=0;t<Len;t++)
  82.   {
  83.     AT24CXX_WriteOneByte(WriteAddr+t,(DataToWrite>>(8*t))&0xff);
  84.   }               
  85. }
  86. //在AT24CXX里面的指定地址开始读出长度为Len的数据
  87. //该函数用于读出16bit或者32bit的数据.
  88. //ReadAddr   :开始读出的地址
  89. //返回值     :数据
  90. //Len        :要读出数据的长度2,4
  91. static uint32_t AT24CXX_ReadLenByte_t(uint16_t ReadAddr,uint8_t Len)
  92. {   
  93.   uint8_t t;
  94.   uint32_t temp=0;
  95. for(t=0;t<Len;t++)
  96.   {
  97.     temp<<=8;
  98.      temp+=AT24CXX_ReadOneByte(ReadAddr+Len-t-1);         
  99.   }
  100. return temp;               
  101. }
  102. //在AT24CXX里面的指定地址开始写入指定个数的数据
  103. //WriteAddr :开始写入的地址 对24c64为0~8191
  104. //pBuffer   :数据数组首地址
  105. //NumToWrite:要写入数据的个数
  106. static void AT24CXX_Write_t(uint16_t WriteAddr,uint8_t *pBuffer,uint16_t NumToWrite)
  107. {
  108. while(NumToWrite--)
  109.   {
  110.    AT24CXX_WriteOneByte(WriteAddr,*pBuffer);
  111.     WriteAddr++;
  112.     pBuffer++;
  113.   }
  114. }
  115. //在AT24CXX里面的指定地址开始读出指定个数的数据
  116. //ReadAddr :开始读出的地址 对24c64为0~8191
  117. //pBuffer  :数据数组首地址
  118. //NumToRead:要读出数据的个数
  119. static void AT24CXX_Read_t(uint16_t ReadAddr,uint8_t *pBuffer,uint16_t NumToRead)
  120. {
  121. while(NumToRead)
  122.   {
  123.     *pBuffer++=AT24CXX_ReadOneByte(ReadAddr++);
  124.     NumToRead--;
  125.   }
  126. }
  127. //初始化IIC接口
  128. static void AT24CXX_Init_t(const struct AT24CXX_Type* AT24CXX_Type_t)
  129. {
  130.   AT24CXX_Type_t->IIC.IIC_Init(&AT24CXX_Type_t->IIC);//IIC初始化
  131. }
  132. //检查器件,返回0表示检测成功,返回1表示检测失败
  133. static uint8_t AT24CXX_Check_t(const struct AT24CXX_Type* AT24CXX_Type_t)   
  134. {
  135. uint8_t temp;
  136.   temp = AT24CXX_Type_t->AT24CXX_ReadOneByte(AT24CXX_Type_t,AT24CXX_Type_t->EEP_TYPE);//避免每次开机都写AT24CXX      
  137. if(temp == 0X33)return 0;     
  138. else//排除第一次初始化的情况
  139.   {
  140.       AT24CXX_Type_t->AT24CXX_WriteOneByte(AT24CXX_Type_t,AT24CXX_Type_t->EEP_TYPE,0X33);
  141.        temp = AT24CXX_Type_t->AT24CXX_ReadOneByte(AT24CXX_Type_t,AT24CXX_Type_t->EEP_TYPE);
  142. if(temp==0X33)return 0;
  143.   }
  144. return 1;  
  145. }
  146. //实例化AT24CXX对象
  147. AT24CXX_TypeDef AT24C_64={
  148. .EEP_TYPE = AT24C64,           //存储器类型(存储器容量)
  149. //操作
  150. .IIC={
  151.   .GPIOx_SCL = GPIOA,
  152.   .GPIOx_SDA = GPIOA,
  153.   .GPIO_SCL = GPIO_PIN_5,
  154.   .GPIO_SDA = GPIO_PIN_6,
  155.   .IIC_Init = IIC_Init_t,
  156.   .IIC_Start = IIC_Start_t,
  157.   .IIC_Stop = IIC_Stop_t,
  158.   .IIC_Wait_Ack = IIC_Wait_Ack_t,
  159.   .IIC_Ack = IIC_Ack_t,
  160.   .IIC_NAck = IIC_NAck_t,
  161.   .IIC_Send_Byte = IIC_Send_Byte_t,
  162.   .IIC_Read_Byte = IIC_Read_Byte_t,
  163.   .delay_us = delay_us
  164. },                   //IIC驱动
  165. .AT24CXX_ReadOneByte = AT24CXX_ReadOneByte_t,  //指定地址读取一个字节
  166. .AT24CXX_WriteOneByte = AT24CXX_WriteOneByte_t,//指定地址写入一个字节
  167. .AT24CXX_WriteLenByte = AT24CXX_WriteLenByte_t, //指定地址开始写入指定长度的数据
  168. .AT24CXX_ReadLenByte = AT24CXX_ReadLenByte_t,   //指定地址开始读取指定长度数据
  169. .AT24CXX_Write = AT24CXX_Write_t,  //指定地址开始写入指定长度的数据
  170. .AT24CXX_Read = AT24CXX_Read_t,   //指定地址开始读取指定长度的数据
  171. .AT24CXX_Init = AT24CXX_Init_t, //初始化IIC
  172. .AT24CXX_Check = AT24CXX_Check_t   //检查器件
  173. };
复制代码

    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存储器的封装,最终对外仅提供一个操作对象接口,大大提高了代码的复用性以及封装性。



转载自:EE时间

收藏 评论0 发布时间:2022-10-8 16:57

举报

0个回答

所属标签

相似分享

官网相关资源

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