I2C(一):存储器模式:stm32作为主机对AT24C02写读数据

存储器模式:在HAL库中,I2C有专门对存储器外设设置的库函数

I2C(一):存储器模式的使用

  • 1、I2C轮询式写读AT24C02一页数据
  • 2、I2C轮询式写读AT24C02多页数据
  • 3、I2C中断式写读AT24C02一页数据
  • 4、I2C使用DMA式写读AT24C02一页数据
  • 1、I2C轮询式写读AT24C02一页数据

    AT24C02 是一款常见的 2 Kbit(256 字节)I²C 接口的 EEPROM(电可擦可编程只读存储器)。它的存储结构如下:

    存储容量:AT24C02 的总存储容量为 2 Kbit(即 256 字节)
    页(Page)大小:AT24C02 的每一页(Page)的大小为 8 字节。
    写数据时,最多一次型写入1页(8字节),超过即会从页的开头覆盖
    读数据时,没有限定字节数。
    

    ①I2C.c文件的代码如下

    #include "I2C.h"
    
    /**
     * @brief:I2C1初始化函数
     */
    I2C_HandleTypeDef hi2c1;                            //I2C初始化结构体
    void I2C1_Init(void)
    {
        /* 1、对I2C1进行初始化 */
        __HAL_RCC_I2C1_CLK_ENABLE();                    //使能I2C1的时钟
        hi2c1.Instance = I2C1;                          //选择I2C1
        hi2c1.Init.ClockSpeed = 100000;                 //通信速率100K,标志模式
        HAL_I2C_Init(&hi2c1);
    }
    
    /**
     * @brief:HAL_I2C_Init()调用此函数
     */
    void HAL_I2C_MspInit(I2C_HandleTypeDef *hi2c)
    {
        GPIO_InitTypeDef GPIO_Init;                     //IO口初始化结构体
        if(hi2c->Instance == I2C1)
        {
            /* I2C1引脚的初始化:PB6 = SCL,PB7 = SDA*/
            __HAL_RCC_GPIOB_CLK_ENABLE();              //使能GPIOB的时钟
            GPIO_Init.Mode = GPIO_MODE_AF_OD;           //复用开漏输出
            GPIO_Init.Pin = GPIO_PIN_6 | GPIO_PIN_7;    //PB6和PB7
            GPIO_Init.Speed = GPIO_SPEED_FREQ_LOW;      //最大输出速度:低
            HAL_GPIO_Init(GPIOB, &GPIO_Init);
        }else if(hi2c->Instance == I2C2)
        {
            
        }
    }
    

    ②I2C.h文件的代码如下

    #ifndef __I2C_H
    #define __I2C_H
    
    #include "stm32f1xx_hal.h"
    extern I2C_HandleTypeDef hi2c1;                            //I2C初始化结构体
    void I2C1_Init(void);
    
    #endif
    

    ③main.c文件的代码如下

    #include "stm32f1xx_hal.h"
    #include "STM32_RCC_Init.h"
    #include "UART.h"
    #include "I2C.h"
    
    #define AT24C02_Address 0xA0
    
    uint8_t WriteData[8] = {1,2,3,4,5,6,7,88};
    uint8_t ReadData[8];
    
    int main(void){
        
    	HAL_Init();
    	HSE_RCC_Init(); 
    	UART1_Init(115200);
        I2C1_Init();
    	printf("启动判断!\r\n");
        
        /* 存储器模式:对AT24C02第一页进行写 */
        HAL_I2C_Mem_Write(&hi2c1, AT24C02_Address, 0, I2C_MEMADD_SIZE_8BIT, WriteData, 8, 1000);
                         //I2C1,从机地址,从机内部地址,每次传输的数据大小,需要写入的数据缓冲区,需要写入多少个数据
        
        /* 查询从机知否准备就绪 */
        if(HAL_I2C_IsDeviceReady(&hi2c1, AT24C02_Address, 20, 1000) == HAL_OK)//I2C1,从机地址,查询次数,超时时间
        {
            /* 存储器模式:向从机第一页读取数据 */
            HAL_I2C_Mem_Read(&hi2c1, AT24C02_Address, 0, I2C_MEMADD_SIZE_8BIT, ReadData, 8, 1000);
        }
        else
        {
             printf("芯片忙碌!\r\n");
        }
        
        /* 将读取到的数据通过串口打印 */
        for(uint8_t i = 0; i<8; i++)
        {
            printf("ReadData[%d] = %d\r\n",i,ReadData[i]);
        }
            
    	while(1){
           
    	}	
    }
    

    2、I2C轮询式写读AT24C02多页数据

    ①I2C.c文件的代码如下

    #include "I2C.h"
    
    /**
     * @brief:I2C1初始化函数
     */
    I2C_HandleTypeDef hi2c1;                            //I2C初始化结构体
    void I2C1_Init(void)
    {
        /* 1、对I2C1进行初始化 */
        __HAL_RCC_I2C1_CLK_ENABLE();                    //使能I2C1的时钟
        hi2c1.Instance = I2C1;                          //选择I2C1
        hi2c1.Init.ClockSpeed = 100000;                 //通信速率100K,标志模式
        HAL_I2C_Init(&hi2c1);
    }
    
    /**
     * @brief:HAL_I2C_Init()调用此函数
     */
    void HAL_I2C_MspInit(I2C_HandleTypeDef *hi2c)
    {
        GPIO_InitTypeDef GPIO_Init;                     //IO口初始化结构体
        if(hi2c->Instance == I2C1)
        {
            /* I2C1引脚的初始化:PB6 = SCL,PB7 = SDA*/
            __HAL_RCC_GPIOB_CLK_ENABLE();              //使能GPIOB的时钟
            GPIO_Init.Mode = GPIO_MODE_AF_OD;           //复用开漏输出
            GPIO_Init.Pin = GPIO_PIN_6 | GPIO_PIN_7;    //PB6和PB7
            GPIO_Init.Speed = GPIO_SPEED_FREQ_LOW;      //最大输出速度:低
            HAL_GPIO_Init(GPIOB, &GPIO_Init);
        }else if(hi2c->Instance == I2C2)
        {
            
        }
    }
    

    ②I2C.h文件的代码如下

    #ifndef __I2C_H
    #define __I2C_H
    
    #include "stm32f1xx_hal.h"
    extern I2C_HandleTypeDef hi2c1;                            //I2C初始化结构体
    void I2C1_Init(void);
    
    #endif
    

    ③main.c文件的代码如下

    #include "stm32f1xx_hal.h"
    #include "STM32_RCC_Init.h"
    #include "UART.h"
    #include "I2C.h"
    
    #define AT24C02_Address 0xA0
    
    uint8_t WriteData[16] = {1,2,3,4,5,6,7,8,
                             8,7,6,5,4,3,2,1};
    uint8_t ReadData[16];
    
    int main(void){
        
    	HAL_Init();
    	HSE_RCC_Init(); 
    	UART1_Init(115200);
        I2C1_Init();
    	printf("启动判断!\r\n");
        
        uint8_t i = 0;
        
        for(i = 0; i<2; i++)
        {
            /* 存储器模式:对AT24C02的页进行写 */
            HAL_I2C_Mem_Write(&hi2c1, AT24C02_Address, i*8, I2C_MEMADD_SIZE_8BIT, &WriteData[i*8], 8, 1000);
            //while(HAL_I2C_IsDeviceReady(&hi2c1, AT24C02_Address, 20, 1000) != HAL_OK);
            HAL_Delay(10);
        }
        
        /* 查询从机知否准备就绪 */
        if(HAL_I2C_IsDeviceReady(&hi2c1, AT24C02_Address, 20, 1000) == HAL_OK)//I2C1,从机地址,查询次数,超时时间
        {
            /* 存储器模式:向从机读取数据 */
            HAL_I2C_Mem_Read(&hi2c1, AT24C02_Address, 0, I2C_MEMADD_SIZE_8BIT, ReadData, 16, 1000);
        }
        else
        {
             printf("芯片忙碌!\r\n");
        }
        
        /* 将读取到的数据通过串口打印 */
        for(i = 0; i<16; i++)
        {
            printf("ReadData[%d] = %d\r\n",i,ReadData[i]);
        }
            
    	while(1){
           
    	}	
    }
    

    3、I2C中断式写读AT24C02一页数据

    实验要求:连接PB0的按键按下后,单片机主机向从机AT24C02写入数据。连接PB1的按键按下后,主机向从机AT24C02读取数据,然后通过串口打印。

    ①Key.c文件的代码如下

    #include "Key.h"
    
    /**
     * 按键引脚的初始化,使用PB0和PB1
     */
    void Key_GPIO_Init(void)
    {
        /* 1、开启GPIOB的时钟 */
       __HAL_RCC_GPIOB_CLK_ENABLE();
        
        /* 2、对GPIOB0进行配置 */
        GPIO_InitTypeDef GPIO_Init;
        GPIO_Init.Pin = GPIO_PIN_0|GPIO_PIN_1;      //选择PB0
        GPIO_Init.Mode = GPIO_MODE_INPUT;           //选择输入模式
        GPIO_Init.Pull = GPIO_PULLUP;               //选择上拉模式
        HAL_GPIO_Init(GPIOB, &GPIO_Init);
    }
    
    /**
     * @brief:判断是按键1按下还是按键2按下
     */
    uint8_t Key_Scan(void)
    {
        uint8_t Key_Num = 0;
        if(PB0_IN == GPIO_PIN_RESET)        //PB0按下
        {
            HAL_Delay(10);                  //延时消抖
            while(PB0_IN == GPIO_PIN_RESET);
            Key_Num =  1;
        }
        if(PB1_IN == GPIO_PIN_RESET)        //PB1按下
        {
            HAL_Delay(10);                  //延时消抖
            while(PB1_IN == GPIO_PIN_RESET);
            Key_Num =  2;
        }  
        return Key_Num;
    }
    

    ①I2C.c文件的代码如下

    #include "I2C.h"
    
    /**
     * @brief:I2C1初始化函数
     */
    I2C_HandleTypeDef hi2c1;                            //I2C初始化结构体
    void I2C1_Init(void)
    {
        /* 1、对I2C1进行初始化 */
        __HAL_RCC_I2C1_CLK_ENABLE();                    //使能I2C1的时钟
        hi2c1.Instance = I2C1;                          //选择I2C1
        hi2c1.Init.ClockSpeed = 100000;                 //通信速率100K,标志模式
        HAL_I2C_Init(&hi2c1);
        
        /* 配置I2C1的NVIC */
        HAL_NVIC_SetPriority(I2C1_EV_IRQn,3,0);
        HAL_NVIC_EnableIRQ(I2C1_EV_IRQn);
    }
    
    /**
     * @brief:HAL_I2C_Init()调用此函数
     */
    void HAL_I2C_MspInit(I2C_HandleTypeDef *hi2c)
    {
        GPIO_InitTypeDef GPIO_Init;                     //IO口初始化结构体
        if(hi2c->Instance == I2C1)
        {
            /* I2C1引脚的初始化:PB6 = SCL,PB7 = SDA*/
            __HAL_RCC_GPIOB_CLK_ENABLE();              //使能GPIOB的时钟
            GPIO_Init.Mode = GPIO_MODE_AF_OD;           //复用开漏输出
            GPIO_Init.Pin = GPIO_PIN_6 | GPIO_PIN_7;    //PB6和PB7
            GPIO_Init.Speed = GPIO_SPEED_FREQ_LOW;      //最大输出速度:低
            HAL_GPIO_Init(GPIOB, &GPIO_Init);
        }else if(hi2c->Instance == I2C2)
        {
            
        }
    }
    

    ②I2C.h文件的代码如下

    #ifndef __I2C_H
    #define __I2C_H
    
    #include "stm32f1xx_hal.h"
    extern I2C_HandleTypeDef hi2c1;                            //I2C初始化结构体
    void I2C1_Init(void);
    
    #endif
    

    ③main.c文件的代码如下

    #include "stm32f1xx_hal.h"
    #include "STM32_RCC_Init.h"
    #include "UART.h"
    #include "I2C.h"
    #include "Key.h"
    
    #define AT24C02_Address 0xA0
    
    uint8_t WriteData[8] = {1,2,3,4,5,6,7,8};
    uint8_t ReadData[8];
    
    int main(void){
        
    	HAL_Init();
    	HSE_RCC_Init(); 
    	UART1_Init(115200);
        I2C1_Init();
        Key_GPIO_Init();
    	printf("启动判断!\r\n");
        
    	while(1){
            switch(Key_Scan())
            {
                case 1://按键PB0按下
                    if(HAL_I2C_IsDeviceReady(&hi2c1, AT24C02_Address, 20, 1000) == HAL_OK)
                    {
                        /* 存储器模式:对AT24C02的页进行写,且开启写入完成中断 */
                        HAL_I2C_Mem_Write_IT(&hi2c1, AT24C02_Address, 0, I2C_MEMADD_SIZE_8BIT, WriteData, 8);
                    }
                    break;
                case 2://按键PB1按下
                    if(HAL_I2C_IsDeviceReady(&hi2c1, AT24C02_Address, 20, 1000) == HAL_OK)
                    {
                        /* 存储器模式:对AT24C02的页进行读,且开启读取完成中断 */
                        HAL_I2C_Mem_Read_IT(&hi2c1, AT24C02_Address, 0, I2C_MEMADD_SIZE_8BIT, ReadData, 8);
                    }
                break;      
            }
    	}	
    }
    

    ④stm32f1xx_it.c文件的代码如下

    #include "stm32f1xx_hal.h"   
    #include "stm32f1xx_it.h" 
    #include "I2C.h" 
    #include "UART.h"
    
    /**
     * I2C1中断服务函数 
     */
    void I2C1_EV_IRQHandler(void)
    {
        HAL_I2C_EV_IRQHandler(&hi2c1);//中断服务总函数
    }
    
    /******************* 下面的中断的回调函数 ***************************/
    /**
     * 存储器模式:I2C1主机发送完成中断回调函数
     */
    void HAL_I2C_MemTxCpltCallback(I2C_HandleTypeDef *hi2c)
    {
      if(hi2c->Instance == I2C1)
      {
          printf("发送完成了!\r\n");
      }else if(hi2c->Instance == I2C2)
      {
      
      }
    }
    
    /**
     * 存储器模式:I2C1主机读取完成中断回调函数
     */
    extern uint8_t ReadData[8];
    void HAL_I2C_MemRxCpltCallback(I2C_HandleTypeDef *hi2c)
    {
      if(hi2c->Instance == I2C1)
      {
          printf("读取完成了!\r\n");
          for(uint8_t i = 0; i<8; i++)
          {
              printf("ReadData[%d] = %d\r\n",i,ReadData[i]);
          }
      }else if(hi2c->Instance == I2C2)
      {
      
      }
    }
    

    4、I2C使用DMA式写读AT24C02一页数据

    实验要求:连接PB0的按键按下后,单片机主机向从机AT24C02写入数据。连接PB1的按键按下后,主机向从机AT24C02读取数据,然后通过串口打印。
    I2C的DMA非常的难用,需要配置I2C的NVIC中断和配置I2C的中断函数。才会调用I2C的DMA中断。还不知直接使用I2C的中断回调函数。
    ①I2C.c文件的代码如下

    #include "I2C.h"
    
    /**
     * @brief:I2C1初始化函数
     */
    I2C_HandleTypeDef hi2c1;                            //I2C初始化结构体
    void I2C1_Init(void)
    {
        /* 1、对I2C1进行初始化 */
        __HAL_RCC_I2C1_CLK_ENABLE();                    //使能I2C1的时钟
        hi2c1.Instance = I2C1;                          //选择I2C1
        hi2c1.Init.ClockSpeed = 100000;                 //通信速率100K,标志模式
        HAL_I2C_Init(&hi2c1);
        
        /* 配置I2C1的NVIC */
        HAL_NVIC_SetPriority(I2C1_EV_IRQn,3,0);
        HAL_NVIC_EnableIRQ(I2C1_EV_IRQn);
    }
    
    /**
     * @brief:HAL_I2C_Init()调用此函数
     */
    DMA_HandleTypeDef hdma1_I2C_Tx;                     //DMA1配置结构体
    DMA_HandleTypeDef hdma1_I2C_Rx;                     //DMA1配置结构体
    void HAL_I2C_MspInit(I2C_HandleTypeDef *hi2c)
    {
        GPIO_InitTypeDef GPIO_Init;                     //IO口初始化结构体
        if(hi2c->Instance == I2C1)
        {
            /* I2C1引脚的初始化:PB6 = SCL,PB7 = SDA*/
            __HAL_RCC_GPIOB_CLK_ENABLE();               //使能GPIOB的时钟
            GPIO_Init.Mode = GPIO_MODE_AF_OD;           //复用开漏输出
            GPIO_Init.Pin = GPIO_PIN_6 | GPIO_PIN_7;    //PB6和PB7
            GPIO_Init.Speed = GPIO_SPEED_FREQ_LOW;      //最大输出速度:低
            HAL_GPIO_Init(GPIOB, &GPIO_Init);
            
            /* 2、初始化DMA1的通道6 */
            __HAL_RCC_DMA1_CLK_ENABLE();                                   //使能DMA1的时钟
            hdma1_I2C_Tx.Instance = DMA1_Channel6;                         //选择DMA1的通道6
            hdma1_I2C_Tx.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE;   //外设站点数据宽度,8位
            hdma1_I2C_Tx.Init.PeriphInc = DMA_PINC_DISABLE;                //外设地址是否递增,选择不自增
            hdma1_I2C_Tx.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE;      //内存站点数据宽度,8位
            hdma1_I2C_Tx.Init.MemInc = DMA_MINC_ENABLE;                    //内存地址是否递增,选择自增
            hdma1_I2C_Tx.Init.Direction = DMA_MEMORY_TO_PERIPH;            //传输方向:这里选择内存---->寄存器
            hdma1_I2C_Tx.Init.Mode = DMA_NORMAL;                           //计数器传输模式:选择不自动重装
            hdma1_I2C_Tx.Init.Priority = DMA_PRIORITY_MEDIUM;              //通道1传输优先级,选择中等
            __HAL_LINKDMA(&hi2c1,hdmatx,hdma1_I2C_Tx);                     //将I2C1_TX和DMA1通道6连接起来
            HAL_DMA_Init(&hdma1_I2C_Tx);
            
            /* 配置DMA1通道6的NVIC */
            HAL_NVIC_SetPriority(DMA1_Channel6_IRQn,3,0);
            HAL_NVIC_EnableIRQ(DMA1_Channel6_IRQn);
            
            /* 3、配置DMA1的通道7 */
            hdma1_I2C_Rx.Instance = DMA1_Channel7;                         //选择DMA1的通道7
            hdma1_I2C_Rx.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE;   //外设站点数据宽度,8位
            hdma1_I2C_Rx.Init.PeriphInc = DMA_PINC_DISABLE;                //外设地址是否递增,选择不自增
            hdma1_I2C_Rx.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE;      //内存站点数据宽度,8位
            hdma1_I2C_Rx.Init.MemInc = DMA_MINC_ENABLE;                    //内存地址是否递增,选择自增
            hdma1_I2C_Rx.Init.Direction = DMA_PERIPH_TO_MEMORY;            //传输方向:这里选择寄存器---->内存
            hdma1_I2C_Rx.Init.Mode = DMA_NORMAL;                           //计数器传输模式:选择不自动重装
            hdma1_I2C_Rx.Init.Priority = DMA_PRIORITY_MEDIUM;              //通道1传输优先级,选择中等
            __HAL_LINKDMA(&hi2c1,hdmarx,hdma1_I2C_Rx);                     //将I2C1_RX和DMA1通道7连接起来
            HAL_DMA_Init(&hdma1_I2C_Rx);
            
            /* 配置DMA1通道6的NVIC */
            HAL_NVIC_SetPriority(DMA1_Channel7_IRQn,3,0);
            HAL_NVIC_EnableIRQ(DMA1_Channel7_IRQn);
        }else if(hi2c->Instance == I2C2)
        {
            
        }
    }
    

    ②I2C.h文件的代码如下

    #ifndef __I2C_H
    #define __I2C_H
    
    #include "stm32f1xx_hal.h"
    extern I2C_HandleTypeDef hi2c1;                            //I2C初始化结构体
    extern DMA_HandleTypeDef hdma1_I2C_Tx;                     //DMA1配置结构体
    extern DMA_HandleTypeDef hdma1_I2C_Rx;                     //DMA1配置结构体
    void I2C1_Init(void);
    
    #endif
    

    ③main.c文件的代码如下

    #include "stm32f1xx_hal.h"
    #include "STM32_RCC_Init.h"
    #include "UART.h"
    #include "I2C.h"
    #include "Key.h"
    
    #define AT24C02_Address 0xA0
    
    uint8_t WriteData[8] = {1,2,3,4,5,6,7,8};
    uint8_t ReadData[8];
    
    int main(void){
        
    	HAL_Init();
    	HSE_RCC_Init(); 
    	UART1_Init(115200);
        I2C1_Init();
        Key_GPIO_Init();
    	printf("启动判断!\r\n");
        
    	while(1){
            switch(Key_Scan())
            {
                case 1://按键PB0按下
                    if(HAL_I2C_IsDeviceReady(&hi2c1, AT24C02_Address, 20, 1000) == HAL_OK)
                    {
                        /* 存储器模式:对AT24C02的页进行写,且开启DMA写入完成中断 */
                        HAL_I2C_Mem_Write_DMA(&hi2c1, AT24C02_Address, 0, I2C_MEMADD_SIZE_8BIT, WriteData, 8);
                    }
                    break;
                case 2://按键PB1按下
                    if(HAL_I2C_IsDeviceReady(&hi2c1, AT24C02_Address, 20, 1000) == HAL_OK)
                    {
                        /* 存储器模式:对AT24C02的页进行读,且开启DMA读取完成中断 */
                        HAL_I2C_Mem_Read_DMA(&hi2c1, AT24C02_Address, 0, I2C_MEMADD_SIZE_8BIT, ReadData, 8);
                    }
                break;      
            }
    	}	
    }
    

    ④stm32f1xx_it.c文件的代码如下

    #include "stm32f1xx_hal.h"   
    #include "stm32f1xx_it.h" 
    #include "I2C.h" 
    #include "UART.h"
    
    /**
     * I2C1中断服务函数 
     */
    void I2C1_EV_IRQHandler(void)
    {
        HAL_I2C_EV_IRQHandler(&hi2c1);//中断服务总函数
    }
    /**
     * DMA1通道6的中断服务函数 
     */
    void DMA1_Channel6_IRQHandler(void)
    {
        HAL_DMA_IRQHandler(&hdma1_I2C_Tx);
    }
    
    /**
     * DMA1通道7的中断服务函数 
     */
    void DMA1_Channel7_IRQHandler(void)
    {
        HAL_DMA_IRQHandler(&hdma1_I2C_Rx);
    }
    /******************* 下面的中断的回调函数 ***************************/
    /**
     * 存储器模式:I2C1的DMA发送完成中断回调函数
     */
    void HAL_I2C_MemTxCpltCallback(I2C_HandleTypeDef *hi2c)
    {
      if(hi2c->Instance == I2C1)
      {
          printf("发送完成了!\r\n");
      }else if(hi2c->Instance == I2C2)
      {
      
      }
    }
    
    /**
     * 存储器模式:I2C1的DMA读取完成中断回调函数
     */
    extern uint8_t ReadData[8];
    void HAL_I2C_MemRxCpltCallback(I2C_HandleTypeDef *hi2c)
    {
      if(hi2c->Instance == I2C1)
      {
          printf("读取完成了!\r\n");
          for(uint8_t i = 0; i<8; i++)
          {
              printf("ReadData[%d] = %d\r\n",i,ReadData[i]);
          }
      }else if(hi2c->Instance == I2C2)
      {
      
      }
    }
    

    作者:浅陌pa

    物联沃分享整理
    物联沃-IOTWORD物联网 » I2C(一):存储器模式:stm32作为主机对AT24C02写读数据

    发表回复