第一节、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);
}
}