第五章、I2C总线接口设备及驱动

第一节、I2C通信原理及时序:

IIC通信协议用一句话来描述就是:主机呼叫从机,从机接受并反馈信号,传输数据后关闭传输。

1、I2C总线简述:(Inter-Integrated Circuit)

由于早期使用uart通信时,当进行多设备通信时,连接过于繁琐复杂,对于近距设备间通信,成本过高,所以在1982年由飞利浦公司开发出了一种基于总线的多设备通讯方式:I²C通信,总线由简洁的SCL时钟线与SDA数据线组成,多个设备都挂载到这两根上。I²C协议的设计初衷是通过减少连接线数量和简化硬件接口,实现低成本、高效的近距离设备通信。该协议允许多个主设备和从设备共享同一总线,使得系统设计更加简便和灵活。是低速设备通信的常用总线。

2、I2C总线通信协议:

下面介绍一下:使用从机E2PROM从机为例,通过时序简要介绍一下IIC通信协议:

1.写数据帧:

2.读数据帧:

3.时序图:

4.I2C控制器硬件框图:

5.使用CubMX快速配置i2C控制器初始化:

第二节、I2C接口通信接口函数分类:

1.HAL关于i2c操作模式介绍:

HAL库把对不同的i2c接口设备使用两种类型的接口,分别为Master主模与Memory模式:

1.Master模式

在I2C的Master模式下,STM32微控制器作为主设备,与从设备进行通信。主设备负责生成时钟信号并启动通信。HAL库中的I2C Master模式函数主要用于发送和接收数据。

主要函数包括:

  • HAL_I2C_Master_Transmit(): 主设备发送数据给从设备。
  • HAL_I2C_Master_Receive(): 主设备从从设备接收数据。
  • HAL_I2C_Master_Transmit_IT(): 主设备以中断方式发送数据。
  • HAL_I2C_Master_Receive_IT(): 主设备以中断方式接收数据。
  • HAL_I2C_Master_Transmit_DMA(): 主设备以DMA方式发送数据。
  • HAL_I2C_Master_Receive_DMA(): 主设备以DMA方式接收数据。

2.Memory模式(MeM模式)

在Memory模式下,STM32微控制器作为主设备,访问从设备的内存。这种模式通常用于读取或写入I2C EEPROM或其他具有内部寄存器的设备。

主要函数包括:

  • HAL_I2C_Mem_Write(): 向从设备的指定内存地址写入数据。
  • HAL_I2C_Mem_Read(): 从从设备的指定内存地址读取数据。
  • HAL_I2C_Mem_Write_IT(): 以中断方式向从设备的指定内存地址写入数据。
  • HAL_I2C_Mem_Read_IT(): 以中断方式从从设备的指定内存地址读取数据。
  • HAL_I2C_Mem_Write_DMA(): 以DMA方式向从设备的指定内存地址写入数据。
  • HAL_I2C_Mem_Read_DMA(): 以DMA方式从从设备的指定内存地址读取数据。

 3.i2c总线接口的从机设备的i2c设备地址,及相应的操作命令码:

1.陀螺仪加速度计:i2c地址7bit:0x68/0x69用于设置不同的i2c设备地址。默认是0x68

第三节.阻塞模式i2c接口函数API介绍:

1. Master模式:

  • HAL_I2C_Master_Transmit(): 主设备发送数据给从设备。

 

HAL_StatusTypeDef HAL_I2C_Master_Transmit(I2C_HandleTypeDef *hi2c, uint16_t DevAddress, 
                                            uint8_t *pData, uint16_t Size, uint32_t Timeout);
参数说明
I2C_HandleTypeDef *hi2c: I2C 句柄,包含了 I2C 配置信息。这个句柄是在初始化 I2C 外设时创建的。
uint16_t DevAddress: 从设备的 7 位或 10 位地址。该地址是左对齐的,即地址的最低有效位(LSB)应该是 0。
uint8_t *pData: 指向需要传输的数据缓冲区的指针。
uint16_t Size: 要传输的数据大小(字节数)。
uint32_t Timeout: 指定超时时间(毫秒)。如果在指定时间内传输未完成,则函数返回超时状态。
返回值
HAL_I2C_Master_Transmit 函数的返回值为 HAL_StatusTypeDef 类型,表示函数的执行状态。可能的返回值包括:
HAL_OK: 表示函数执行成功,数据传输完成。
HAL_ERROR: 表示发生错误。
HAL_BUSY: 表示 I2C 外设正忙,无法执行该操作。
HAL_TIMEOUT: 表示操作超时。                                                                               
  • HAL_I2C_Master_Receive(): 主设备从从设备接收数据。
    HAL_StatusTypeDef HAL_I2C_Master_Receive(I2C_HandleTypeDef *hi2c, uint16_t DevAddress, uint8_t *pData, 
                                                uint16_t Size, uint32_t Timeout);
    参数说明
    I2C_HandleTypeDef *hi2c: I2C 句柄,包含了 I2C 配置信息。这个句柄是在初始化 I2C 外设时创建的。
    uint16_t DevAddress: 从设备的 7 位或 10 位地址。该地址是左对齐的,即地址的最低有效位(LSB)应该是 0。
    uint8_t *pData: 指向存储接收数据的缓冲区的指针。
    uint16_t Size: 要接收的数据大小(字节数)。
    uint32_t Timeout: 指定超时时间(毫秒)。如果在指定时间内接收未完成,则函数返回超时状态。
    返回值
    HAL_I2C_Master_Receive 函数的返回值为 HAL_StatusTypeDef 类型,表示函数的执行状态。可能的返回值包括:
    HAL_OK: 表示函数执行成功,数据接收完成。
    HAL_ERROR: 表示发生错误。
    HAL_BUSY: 表示 I2C 外设正忙,无法执行该操作。
    HAL_TIMEOUT: 表示操作超时。

    2.Memory模式:

  • HAL_I2C_Mem_Write(): 向从设备的指定内存地址写入数据。
    HAL_StatusTypeDef HAL_I2C_Mem_Write(I2C_HandleTypeDef *hi2c, uint16_t DevAddress, uint16_t MemAddress, 
                            uint16_t MemAddSize, uint8_t *pData, uint16_t Size, uint32_t Timeout);
    参数说明
    I2C_HandleTypeDef *hi2c:I2C 句柄,包含了 I2C 配置信息。这个句柄是在初始化 I2C 外设时创建的。
    uint16_t DevAddress:从设备的地址(7 位或 10 位)。
    uint16_t MemAddress:从设备内存的地址,从这个地址开始写入数据。
    uint16_t MemAddSize:内存地址的大小,可以是 I2C_MEMADD_SIZE_8BIT 或 I2C_MEMADD_SIZE_16BIT。
    uint8_t *pData:指向要写入数据的缓冲区的指针。
    uint16_t Size:要写入的数据大小(字节数)。
    uint32_t Timeout:指定超时时间(毫秒)。如果在指定时间内操作未完成,则函数返回超时状态。
    返回值
    HAL_I2C_Mem_Write 函数的返回值为 HAL_StatusTypeDef 类型,表示函数的执行状态。可能的返回值包括:
    HAL_OK:表示函数执行成功,数据写入完成。
    HAL_ERROR:表示发生错误。
    HAL_BUSY:表示 I2C 外设正忙,无法执行该操作。
    HAL_TIMEOUT:表示操作超时。

  • HAL_I2C_Mem_Read(): 从从设备的指定内存地址读取数据。
    HAL_StatusTypeDef HAL_I2C_Mem_Read(I2C_HandleTypeDef *hi2c, uint16_t DevAddress, uint16_t MemAddress, 
                            uint16_t MemAddSize, uint8_t *pData, uint16_t Size, uint32_t Timeout);
    参数说明
    I2C_HandleTypeDef *hi2c:I2C 句柄,包含了 I2C 配置信息。这个句柄是在初始化 I2C 外设时创建的。
    uint16_t DevAddress:从设备的地址(7 位或 10 位)。
    uint16_t MemAddress:从设备内存的地址,从这个地址开始读取数据。
    uint16_t MemAddSize:内存地址的大小,可以是 I2C_MEMADD_SIZE_8BIT 或 I2C_MEMADD_SIZE_16BIT。
    uint8_t *pData:指向存储读取数据的缓冲区的指针。
    uint16_t Size:要读取的数据大小(字节数)。
    uint32_t Timeout:指定超时时间(毫秒)。如果在指定时间内操作未完成,则函数返回超时状态。
    返回值
    HAL_I2C_Mem_Read 函数的返回值为 HAL_StatusTypeDef 类型,表示函数的执行状态。可能的返回值包括:
    HAL_OK:表示函数执行成功,数据读取完成。
    HAL_ERROR:表示发生错误。
    HAL_BUSY:表示 I2C 外设正忙,无法执行该操作。
    HAL_TIMEOUT:表示操作超时。

    3.代码实例:

    mpu6050.h:

    #ifndef __MPU6050_DRIVER_H__
    #define __MPU6050_DRIVER_H__
    #include <stdint.h>
    #include "FreeRTOS.h"
    #include "main.h"
    #include "task.h"
    #include "usart.h"
    #include <stdbool.h>
    #include "semphr.h"
    #include <stdio.h>
    #include "i2c.h"
    extern I2C_HandleTypeDef hi2c1;
    
    //封装一个i2c从机设备对象:
    struct i2c_mpu6050
    {
        //i2c控制器指针:
        I2C_HandleTypeDef* i2c1_adpter;
        //i2c设备的命令码:
        uint8_t i2c_addr;
        //各个功能寄存器:
        uint8_t pow1_reg;
        uint8_t who_am_i_reg;
        uint8_t temperature_reg;
        uint8_t GYRO_x_reg;
        uint8_t GYRO_y_reg;
        uint8_t GYRO_z_reg;
        //i2c接收数据的缓冲2byte
        uint16_t data;
    };
    
    //获取MPU6050设备的who am i 中的值:
    uint8_t getWho_am_i_val(struct i2c_mpu6050* i2c_dev);
    
    #endif

    mpu6050.c

    #include "mpu6050_driver.h"
    #include "i2c.h"
    struct i2c_mpu6050 mpu6050 = {
        .data = 0,
        .i2c_addr = 0x68 << 1,
        .i2c1_adpter = &hi2c1,
        .pow1_reg = 0x6b,
        .who_am_i_reg = 0x75,
        .temperature_reg = 0x41,
        .GYRO_x_reg = 0x43,
        .GYRO_y_reg = 0x45,
        .GYRO_z_reg = 0x47,
    };
    // 获取MPU6050设备的who am i 中的值:
    uint8_t getWho_am_i_val(struct i2c_mpu6050 *i2c_dev)
    {
        HAL_I2C_Master_Transmit(i2c_dev->i2c1_adpter, i2c_dev->i2c_addr,
                                &i2c_dev->who_am_i_reg, 1, portMAX_DELAY);
        HAL_I2C_Master_Receive(i2c_dev->i2c1_adpter, i2c_dev->i2c_addr,
                               (uint8_t*)&i2c_dev->data, 1, portMAX_DELAY);
        return (uint8_t)i2c_dev->data;
    }

    在串口应用中测试:

    #include "uart_app.h"
    #include "uart_driver.h"
    #include <string.h>
    #include <stdbool.h>
    #include <stdio.h>
    #include "mpu6050_driver.h"
    extern struct i2c_mpu6050 mpu6050;
    //uart测试任务回调函数:
    void uart_task_function(void* arg)
    {
        const char* str = "Welcome uart_test,Please input CMD: \n";
        //uart_send_by_dma((void*)str,strlen(str));
        printf("%s",str);
        printf("%d \n",1000);
        float f = 3.1415926;
        printf("%.2f \n",f);
        //char buf[128] = {0};
        while (true)
        {
            // /* 测试uart串口收发数据 */
            // memset(buf, 0,sizeof(buf));
            // uart_recv_by_dmaToIdle(buf, sizeof(buf)-1);
            // //发给串口,在PC串口终端进行显示:
            // uart_send_by_dma(buf,strlen(buf));
            getWho_am_i_val(&mpu6050);
            vTaskDelay(1000);
            printf("mpu6050 who am i val = %#x\n",mpu6050.data);
        }
        
    }
    

    第三节、MPU6050陀螺仪芯片驱动开流程:

    1. 看手册,了解芯片配置:初始化配置:

2.MPU6050陀螺仪驱动代码及获取数据的接口函数:

#include "mpu6050_driver.h"
#include "i2c.h"
struct i2c_mpu6050 mpu6050 = {
    .data = 0,
    .i2c_addr = 0x68 << 1,
    .i2c1_adpter = &hi2c1,
    //功能寄存器:
    .who_am_i_reg = 0x75,
    .temperature_reg = 0x41,
    .GYRO_x_reg = 0x43,
    .GYRO_y_reg = 0x45,
    .GYRO_z_reg = 0x47,
    //电源寄存器:
    .pow1_reg = 0x6b,
    .pow2_reg = 0x6c,
    //配置寄存器:
    .smprt_reg = 0x19,//采样配置
    .dlpf_config_reg = 0x1a,//低通滤波配置
    .gyro_config_reg = 0x1b,//偏移角配置
    .accel_config_reg = 0x1c,//加速度配置

};
//1. 获取MPU6050设备的who am i 中的值:
uint8_t getWho_am_i_val(struct i2c_mpu6050 *i2c_dev)
{
    HAL_I2C_Master_Transmit(i2c_dev->i2c1_adpter, i2c_dev->i2c_addr,
                            &i2c_dev->who_am_i_reg, 1, portMAX_DELAY);
    HAL_I2C_Master_Receive(i2c_dev->i2c1_adpter, i2c_dev->i2c_addr,
                           (uint8_t *)&i2c_dev->data, 1, portMAX_DELAY);
    return (uint8_t)i2c_dev->data;
}

//2. mpu6050初始化函数:
int mpu6050_init(struct i2c_mpu6050* i2c_dev)
{
    HAL_StatusTypeDef status;
    //使用HAL库的memory方式:
    uint8_t write_data = 0x0;
    //对电源寄存器配置为唤醒:
    status = HAL_I2C_Mem_Write(i2c_dev->i2c1_adpter, i2c_dev->i2c_addr,mpu6050.pow1_reg, 1, &write_data,1,portMAX_DELAY);
    HAL_I2C_Mem_Write(i2c_dev->i2c1_adpter, i2c_dev->i2c_addr,mpu6050.pow2_reg, 1, &write_data,1,portMAX_DELAY);
    //配置采样:125Hz  
    write_data = 0x7;
    status = HAL_I2C_Mem_Write(i2c_dev->i2c1_adpter, i2c_dev->i2c_addr,mpu6050.smprt_reg, 1, &write_data,1,portMAX_DELAY);
    //配置低通滤波:
    write_data = 0x6;
    status = HAL_I2C_Mem_Write(i2c_dev->i2c1_adpter, i2c_dev->i2c_addr,mpu6050.dlpf_config_reg, 1, &write_data,1,portMAX_DELAY);
    //配置每秒检测的角度范围:
    write_data = 0x18;
    status = HAL_I2C_Mem_Write(i2c_dev->i2c1_adpter, i2c_dev->i2c_addr,mpu6050.gyro_config_reg, 1, &write_data,1,portMAX_DELAY);
    //配置加速度为+-4G:0x8:
    write_data = 0x8;
    status = HAL_I2C_Mem_Write(i2c_dev->i2c1_adpter, i2c_dev->i2c_addr,mpu6050.accel_config_reg, 1, &write_data,1,portMAX_DELAY);
    return status;
}

//1.获取mpu6050上的温度:
int getTemperature_val(struct i2c_mpu6050* i2c_dev)
{
    HAL_StatusTypeDef status;
    uint8_t buf[2] = {0};
    status = HAL_I2C_Mem_Read(i2c_dev->i2c1_adpter, i2c_dev->i2c_addr, i2c_dev->temperature_reg,
                                         1, buf, 2, portMAX_DELAY);
    //大端转成小端且合并成2字节无符号整形值:
    uint16_t tmp_data = buf[0] << 8 | buf[1];
    i2c_dev->data = tmp_data;
    return status;
}

 应用测试:获取温度:

#include "uart_app.h"
#include "uart_driver.h"
#include <string.h>
#include <stdbool.h>
#include <stdio.h>
#include "mpu6050_driver.h"
extern struct i2c_mpu6050 mpu6050;
//uart测试任务回调函数:
void uart_task_function(void* arg)
{
    const char* str = "Welcome uart_test,Please input CMD: \n";
    //uart_send_by_dma((void*)str,strlen(str));
    printf("%s",str);
    printf("%d \n",1000);
    float f = 3.1415926;
    printf("%.2f \n",f);
    //char buf[128] = {0};
    //初始化mpu6050:
    mpu6050_init(&mpu6050);
    while (true)
    {
        // /* 测试uart串口收发数据 */
        // memset(buf, 0,sizeof(buf));
        // uart_recv_by_dmaToIdle(buf, sizeof(buf)-1);
        // //发给串口,在PC串口终端进行显示:
        // uart_send_by_dma(buf,strlen(buf));
        // getWho_am_i_val(&mpu6050);
        // vTaskDelay(1000);
        // printf("mpu6050 who am i val = %#x\n",mpu6050.data);
        //获取:MPU6050上的温度传感器中的值:
        getTemperature_val(&mpu6050);
        //转为:有符号2字节整形值:
        int16_t tmp_val = mpu6050.data;
        vTaskDelay(1000);
        printf("Current Temperature val = %.2f \n", tmp_val / 340.0 + 36.53);
    }
    
}

3. 编写:获取角速度值的驱动接口:

角速度值驱动接口:

// 2.获取mpu6050上的GYRO_x, y, z三轴上的偏转角速度:
int getGYRO_xyz_val(struct i2c_mpu6050 *i2c_dev, float *GYRO_x, float *GYRO_y, float *GYRO_z)
{
    HAL_StatusTypeDef status;
    uint8_t buf[2] = {0};
    if (GYRO_x)
    {
        status = HAL_I2C_Mem_Read(i2c_dev->i2c1_adpter, i2c_dev->i2c_addr, i2c_dev->GYRO_x_reg,
                                  1, buf, 2, portMAX_DELAY);
        // 大端转成小端:
        int16_t tmp_data = buf[0] << 8 | buf[1];
        *GYRO_x = tmp_data / 16.4 + 0.79;
    }
    if (GYRO_y)
    {
        status = HAL_I2C_Mem_Read(i2c_dev->i2c1_adpter, i2c_dev->i2c_addr, i2c_dev->GYRO_y_reg,
                                  1, buf, 2, portMAX_DELAY);
        // 大端转成小端:
        int16_t tmp_data = buf[0] << 8 | buf[1];
        *GYRO_y = tmp_data / 16.4 + 4.27;
    }
    if (GYRO_z)
    {
        status = HAL_I2C_Mem_Read(i2c_dev->i2c1_adpter, i2c_dev->i2c_addr, i2c_dev->GYRO_z_reg,
                                  1, buf, 2, portMAX_DELAY);
        // 大端转成小端:
        int16_t tmp_data = buf[0] << 8 | buf[1];
        *GYRO_z = tmp_data / 16.4 + 0.43;
    }
    return status;
}

测试:

#include "uart_app.h"
#include "uart_driver.h"
#include <string.h>
#include <stdbool.h>
#include <stdio.h>
#include "mpu6050_driver.h"
extern struct i2c_mpu6050 mpu6050;
//uart测试任务回调函数:
void uart_task_function(void* arg)
{
    const char* str = "Welcome uart_test,Please input CMD: \n";
    //uart_send_by_dma((void*)str,strlen(str));
    printf("%s",str);
    printf("%d \n",1000);
    float f = 3.1415926;
    printf("%.2f \n",f);
    //char buf[128] = {0};
    //初始化mpu6050:
    mpu6050_init(&mpu6050);
    float x, y, z;
    x = y = z = 0;
    while (true)
    {
        // /* 测试uart串口收发数据 */
        // memset(buf, 0,sizeof(buf));
        // uart_recv_by_dmaToIdle(buf, sizeof(buf)-1);
        // //发给串口,在PC串口终端进行显示:
        // uart_send_by_dma(buf,strlen(buf));
        // getWho_am_i_val(&mpu6050);
        // vTaskDelay(1000);
        // printf("mpu6050 who am i val = %#x\n",mpu6050.data);
        getTemperature_val(&mpu6050);
        int16_t tmp_val = mpu6050.data;
        vTaskDelay(1000);
        printf("Current Temperature val = %.2f \n", tmp_val / 340.0 + 36.53);

        getGYRO_xyz_val(&mpu6050, &x, &y, &z);

        printf("GYRO x = %.2f, GYRO y = %.2f, GYRO z = %.2f,",x,y,z);
    }
}

4. 编写:获取加速度值的驱动接口:

// 3.获取mpu6050上的ACC_x,yz三轴上的加速度:
int getACC_xyz_val(struct i2c_mpu6050 *i2c_dev, float *ACC_x, float *ACC_y, float *ACC_z)
{
    HAL_StatusTypeDef status;
    uint8_t buf[2] = {0};
    if (ACC_x)
    {
        status = HAL_I2C_Mem_Read(i2c_dev->i2c1_adpter, i2c_dev->i2c_addr, i2c_dev->ACC_x_reg,
                                  1, buf, 2, portMAX_DELAY);
        // 大端转成小端:
        int16_t tmp_data = buf[0] << 8 | buf[1];
        *ACC_x = (float)tmp_data / 8192.0;
    }
    if (ACC_y)
    {
        status = HAL_I2C_Mem_Read(i2c_dev->i2c1_adpter, i2c_dev->i2c_addr, i2c_dev->ACC_y_reg,
                                  1, buf, 2, portMAX_DELAY);
        // 大端转成小端:
        int16_t tmp_data = buf[0] << 8 | buf[1];
        *ACC_y = (float)tmp_data / 8192.0;
    }
    if (ACC_z)
    {
        status = HAL_I2C_Mem_Read(i2c_dev->i2c1_adpter, i2c_dev->i2c_addr, i2c_dev->ACC_z_reg,
                                  1, buf, 2, portMAX_DELAY);
        // 大端转成小端:
        int16_t tmp_data = buf[0] << 8 | buf[1];
        *ACC_z = (float)tmp_data / 8192.0;
    }
    return status;
}

以上接口均为无人机飞控,及平衡车必用接口。以便以后的应用中使用!!!

5.融合角速度与加速度,获取趋近的实际的 roll pich yaw角度:(无技术点,纯数学算法)

#define PI 3.1415926
#define Dt 0.004
//alpha = 0.1 / (0.1 + 0.004) = 0.95238
#define ALPHA 0.95238
//4.获取mpu605三轴上的角度:roll_x, pitch_y, yaw_z;
int getRoll_Pitch_Yaw_val(struct i2c_mpu6050* i2c_dev, float* Roll_x,float* Pitch_y,float* Yaw_z)
{
    float gx, gy, gz,ax,ay,az;
    gx = gy = gz = ax = ay = az = 0;
    //获取角速度:
    getGYRO_xyz_val(i2c_dev, &gx, &gy, &gz);
    //获取加速度:
    getACC_xyz_val(i2c_dev, &ax, &ay, &az);

    //计算一下:使用加速度原值获取pitch与roll:
    float ACC_Pitch = -atan2(ax, az) * 180 / PI;
    float ACC_Roll = atan2(ay, az) * 180 / PI;

    //通过角速度计算pitch, roll, yaw;
    float GYRO_Pitch = gy * Dt;
    float GYRO_ROLL = gx * Dt;
    float GYRO_Yaw = gz * Dt;


    //互补滤波算法:
    *Roll_x = ALPHA * (*Roll_x + GYRO_ROLL) + (1 - ALPHA) * ACC_Roll;
    *Pitch_y = ALPHA * (*Pitch_y + GYRO_Pitch) + (1 - ALPHA) * ACC_Pitch;
    *Yaw_z = *Yaw_z + GYRO_Yaw *180 / PI;
    if(fabs(*Yaw_z) > 360)
    {
        *Yaw_z = 0;
    }

    return 0;

}

第四节、非阻塞中断与DMA方式

1. Master模式:

  • HAL_I2C_Master_Transmit_IT(): 主设备发送数据给从设备。
  • HAL_I2C_Master_Transmit_DMA(): 主设备发送数据给从设备。
  • 中断完成时的回调函数:
    1.中断的方式发送:
    HAL_StatusTypeDef HAL_I2C_Master_Transmit_IT(I2C_HandleTypeDef *hi2c, uint16_t DevAddress,
    uint8_t *pData, uint16_t Size);
    
    2.DMA的方式发送:
    HAL_StatusTypeDef HAL_I2C_Master_Transmit_DMA(I2C_HandleTypeDef *hi2c, uint16_t DevAddress,
    uint8_t *pData, uint16_t Size);
    
    /* 主设备发送完成回调函数 */
    void HAL_I2C_MasterTxCpltCallback(I2C_HandleTypeDef *hi2c);
  • HAL_I2C_Master_Receive_IT(): 主设备从从设备接收数据。
  • HAL_I2C_Master_Receive_DMA(): 主设备从从设备接收数据。
  • 中断完成时的回调函数:
    1.中断的方式接收:
    HAL_StatusTypeDef HAL_I2C_Master_Receive_IT(I2C_HandleTypeDef *hi2c, uint16_t DevAddress,
    uint8_t *pData, uint16_t Size);
    
    2.DMA的方式接收:
    HAL_StatusTypeDef HAL_I2C_Master_Receive_DMA(I2C_HandleTypeDef *hi2c, uint16_t DevAddress,
    uint8_t *pData, uint16_t Size);
    
    /* 主设备接收完成回调函数 */
    void HAL_I2C_MasterRxCpltCallback(I2C_HandleTypeDef *hi2c);

    2.Memory模式:

  • HAL_I2C_Mem_Write_IT(): 向从设备的指定内存地址写入数据。
  • HAL_I2C_Mem_Write_DMA(): 向从设备的指定内存地址写入数据。
  • 中断完成时的回调函数:
    1.Mem模式中断发送:
    HAL_StatusTypeDef HAL_I2C_Mem_Write_IT(I2C_HandleTypeDef *hi2c, uint16_t DevAddress,
    uint16_t MemAddress, uint16_t MemAddSize, uint8_t *pData, uint16_t Size);
    
    2.Mem模式DMA发送:
    HAL_StatusTypeDef HAL_I2C_Mem_Write_DMA(I2C_HandleTypeDef *hi2c, uint16_t DevAddress,
    uint16_t MemAddress, uint16_t MemAddSize, uint8_t *pData, uint16_t Size);
    
    /* MEM模式发送完成回调函数 */
    void HAL_I2C_MemTxCpltCallback(I2C_HandleTypeDef *hi2c);

    第五节、OLED显示屏原理及驱动的移植:

    1. 了解移植SSD1306厂家的显示驱动:

    OLED显示的坐标:

2. 编写汉字输出驱动:

1.显示屏点亮屏幕的原理:

2.汉字取模:把生成显示汉字的点阵

取模网站:LCD/OLED汉字字模提取软件,(HZK12宋体)GB2312中文12*12点阵字库 (23bei.com)

3.OLED汉字显示驱动:

// 编写OLED显示汉字的驱动:
void OLED_PrintChinaTxt(uint16_t x, uint16_t y, char *buf, uint16_t ChinaTxtSize)
{
    // 把坐标系设定为8*8为单位:
    uint16_t page = y;
    uint16_t col = x * 8;

    if (x > 15 || y > 7)
    {
        return;
    }

    for (int i = 0; i < ChinaTxtSize; i++)
    {
        uint16_t index = i * 32;
        OLED_SetPosition(page, col);
        OLED_WriteNBytes((uint8_t *)&buf[index], 16);

        OLED_SetPosition(page + 1, col);
        OLED_WriteNBytes((uint8_t *)&buf[index + 16], 16);
        col += 16;
    }
}

应用:

#include "oled1306_app.h"
#include <stdbool.h>
extern struct i2c_mpu6050 mpu6050;
char buf[] = {
    /*-- ID:0,字符:"湿",ASCII编码:CAAA,对应字:宽x高=16x16,画布:宽W=16 高H=16,共32字节*/
    0x10,0x22,0x64,0x0c,0x80,0xfe,0x92,0x92,0x92,0x92,0x92,0x92,0xff,0x02,0x00,0x00,
    0x04,0x04,0xfe,0x41,0x44,0x48,0x50,0x7f,0x40,0x40,0x7f,0x50,0x48,0x64,0x40,0x00,

/*-- ID:1,字符:"度",ASCII编码:B6C8,对应字:宽x高=16x16,画布:宽W=16 高H=16,共32字节*/
    0x00,0x00,0xfc,0x24,0x24,0x24,0xfc,0xa5,0xa6,0xa4,0xfc,0x24,0x34,0x26,0x04,0x00,
    0x40,0x20,0x9f,0x80,0x42,0x42,0x26,0x2a,0x12,0x2a,0x26,0x42,0x40,0xc0,0x40,0x00,

/*-- ID:2,字符:":",ASCII编码:A3BA,对应字:宽x高=16x16,画布:宽W=16 高H=16,共32字节*/
    0x00,0x00,0x00,0x00,0x80,0xc0,0xc0,0x80,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
    0x00,0x00,0x00,0x00,0x31,0x7b,0x7b,0x31,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00
};

void oled_task_function(void *arg)
{
    OLED_Init();
    OLED_Clear();
    mpu6050_init(&mpu6050);//初始化mpu6050
    int x , y;
    const char* str = "Hello world";
    char tmp[32] = {0};
    float f = 0;
    while (true)
    {
        /* oled相应的应用与测试: */
        //x,y坐标,偏移以8*8为基本单位。
        x = 0;
        y = 2;
        OLED_PrintString(x, y, str);
        OLED_PrintString(0, 0, str);
        OLED_PrintChinaTxt(0,4, buf, 3);
        vTaskDelay(100);
        getTemperature_val(&mpu6050);
        vTaskDelay(100);
        int16_t tmp_val = mpu6050.data;
        f =  tmp_val / 340.0 + 36.53;
        sprintf(tmp,"%.2f",f);
        OLED_PrintString(6,4,tmp);
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值