SPL06 基于stm32F103 HAL库驱动(软件模拟IIC)
talk is cheap, show you my code
SPL06.c
#include "SPL06.h"//*************全局变量*************//
Factor_List* b_list; //存储过采样率对应的系数KP,KT
COEF_ValueStruct Coefficient = { 0 }; //存储校准系数
TEMP_InitTypedef TEMP_InitStructure = { 0 }; //温度测量初始化配置结构体
PSR_InitTypedef PSR_InitStructure = { 0 }; //大气压强测量初始化配置结构体//*************1.初始化相关函数*************//
//*************1.1 链表初始化相关函数*******//
/*** @name Factor_List* initList(void)* @brief 初始化链表头节点,将头节点也利用起来,存储信息* @param [NONE]* @return [p] 返回创建的链表头节点地址*/
Factor_List* initList(void)
{Factor_List *p = (Factor_List*)malloc(sizeof(Factor_List));p->OverSamplingRate = _SINGLE_OVERSAMPLING;p->FACTOR = _SINGLE_SCALE_FACTOR;p->next = NULL;if (p == NULL) {// 处理内存分配失败的情况return NULL;}return p;
}/*** @name Factor_List* insertTail(Factor_List *l, uint8_t val, uint32_t Factor)* @brief 向链表的末尾添加一个节点,即尾插法* @param [Factor_List *list] 链表的起始节点* [uint8_t val] 链表OverSamplingRate部分的值,对应过采样率的寄存器值* [uint32_t Factor] 过采样率对应的比例因子* @return 尾节点指针,由于数据量只有八个,且链表添加数据之后固定,为了简化代码,没有利用尾节点指针*/
Factor_List* insertTail(Factor_List *l, uint8_t val, uint32_t Factor)
{//先创建一个新节点Factor_List *p = (Factor_List*)malloc(sizeof(Factor_List));p->OverSamplingRate = val;p->FACTOR = Factor;p->next = NULL;while(l->next != NULL) l = l->next;l->next = p;return p;
}/*** @name void Init_FactorList(void)* @brief 初始化链表的所有数据,将过采样率及其对应的比例因子插入到链表中* @param [NONE]* @return [NONE]*/
void Init_FactorList(void)
{b_list = initList(); //_SINGLE_OVERSAMPLING的数据已经放入insertTail(b_list, _2TIMES_OVERSAMPLING, _2TIMES_SCALE_FACTOR);insertTail(b_list, _4TIMES_OVERSAMPLING, _4TIMES_SCALE_FACTOR);insertTail(b_list, _8TIMES_OVERSAMPLING, _8TIMES_SCALE_FACTOR);insertTail(b_list, _16TIMES_OVERSAMPLING, _16TIMES_SCALE_FACTOR);insertTail(b_list, _32TIMES_OVERSAMPLING, _32TIMES_SCALE_FACTOR);insertTail(b_list, _64TIMES_OVERSAMPLING, _64TIMES_SCALE_FACTOR);insertTail(b_list, _128TIMES_OVERSAMPLING, _128TIMES_SCALE_FACTOR);
}/*** @brief 根据对应的过采样率寻找对应的比例因子* @param [Factor_List *list] 要查找的链表* [val] 用于查询的过采样率* @return 返回的比例因子数值*/
uint32_t FindFactor(Factor_List* l, uint8_t val)
{while(l->OverSamplingRate != val){l = l->next;}return l->FACTOR;
}//*************1.2 SPL06初始化相关函数******//
/*** @name uint8_t SPL06_Init(void)* @brief SPL06初始化,包含采样模式,温度采样配置,大气压强采样配置,可以通过修改对应结构体成员来修改配置* @return 0 配置成功* 1 I2C通讯异常* 2 配置采样模式失败,总线无应答* 3 配置大气压强采样失败,总线无应答* 4 配置温度采样失败,总线无应答* 5 获取校正系数失败,可能是校正系数没有准备好,也可能是I2C通讯异常*/
uint8_t SPL06_Init(void)
{//1. 拉高SCL、SDA确保起始条件能够被正确发送MyI2C_W_SCL(1);MyI2C_W_SDA(1);//2. 进行读写校验,验证I2C通讯uint8_t write_buf[2] = {0x11, 0x13};uint8_t read_buf[2];MyI2C_WriteMultiRegister(SPL06_ADDRESS, PRS_CFG, 3, write_buf);MyI2C_ReadMultiRegister(SPL06_ADDRESS, PRS_CFG, 3, read_buf);for(uint8_t i = 0; i < 2; i++){if(read_buf[i] != write_buf[i]){return 1;}}//3. 确认I2C通信正常后,软复位芯片MyI2C_WriteRegister(RESET, 0x89);for(uint16_t i=0; i<1000; i++){for(uint16_t j=0; j<2000; j++);}//4. 配置采样模式:连续采样大气压强和温度if(SPL_OperatingModeInit(STRAT_CONTINUOUS_PSR_TEMP))return 2;//5. 配置大气压强采样频率,过采样率PSR_InitStructure.MEASURE_RATE = _4HZ_MEASUREMENT; //采样频率PSR_InitStructure.OVER_SAMPLING_TIMES = _64TIMES_OVERSAMPLING; //过采样率if(SPL06_PSRInitStruct(&PSR_InitStructure))return 3;//6. 配置温度采样频率,过采样率TEMP_InitStructure.MEASURE_RATE = _4HZ_MEASUREMENT; //采样频率TEMP_InitStructure.OVER_SAMPLING_TIMES = _SINGLE_OVERSAMPLING; //过采样率TEMP_InitStructure.SENSOR_SOURCE = _EXTERNAL_SENSOR;if(SPL06_TEMPInitStruct(&TEMP_InitStructure))return 2;//7. 当大气压强过采样率>8时,必须启用P ShiftMyI2C_WriteRegister(CFG_REG, 0x04); // 启动P位移,0x04,禁用FIFO 0x06,启用FIFO//8. 初始化KP、KT链表Init_FactorList();//9. 读取矫正系数,保存在Coefficient结构体中if(GetCoefVal(&Coefficient) != 0)return 5;//10. 正常返回0return 0;
}/*** @name uint8_t SPL06_PSRInitStruct(PSR_InitTypedef* PSR_InitStructure)* @brief 配置大气压强测量控制寄存器,可配置采样频率,过采样率* @param [PSR_InitStructure] 传入的结构体指针,包含配置信息* @return 0 成功* 1 失败*/
uint8_t SPL06_PSRInitStruct(PSR_InitTypedef* PSR_InitStructure)
{uint8_t config = 0x00;config = PSR_InitStructure->MEASURE_RATE + PSR_InitStructure->OVER_SAMPLING_TIMES;if(MyI2C_WriteRegister(PRS_CFG, (uint8_t)config)){return 1;}return 0;
}/*** @name uint8_t SPL06_TEMPInitStruct(TEMP_InitTypedef* TEMP_InitStructure)* @brief 配置温度测量控制寄存器,可配置采样频率,过采样率* @param [TEMP_InitStructure] 传入的结构体指针,包含配置信息* @return 0 成功* 1 失败*/
uint8_t SPL06_TEMPInitStruct(TEMP_InitTypedef* TEMP_InitStructure)
{uint8_t config = 0x00;config = TEMP_InitStructure->SENSOR_SOURCE + TEMP_InitStructure->MEASURE_RATE \+ TEMP_InitStructure->OVER_SAMPLING_TIMES;if(MyI2C_WriteRegister(TMP_CFG, config)){return 1;}return 0;
}/*** @name uint8_t SPL_OperatingModeInit(MeasureModeConfig config)* @brief 设置SPL06的测量模式,可以在@<! MeasureModeConfig >中查看可配置的测量模式* @param [config] 具体见@<! MeasureModeConfig >* @return 0 成功* 1 失败*/
uint8_t SPL_OperatingModeInit(MeasureModeConfig config)
{if(MyI2C_WriteRegister(MEAS_CFG, config)){return 1;}return 0;
}/*** @name uint8_t COEF_CheckStatus(void)* @brief 查询SPL06的矫正系数是否可读* @return 0 可读* 1 不可读*/
uint8_t COEF_CheckStatus(void)
{if((MyI2C_ReadRegister(MEAS_CFG) & 0x80) == 0x80) //如果bit[7]COFE_RDY为高,说明矫正系数可读{return 0;}return 1;
}/*** @brief 读取SPL06的ID* @param [void]* @return [ID] ID默认值*/
uint8_t SPL06_ReadID(void)
{return MyI2C_ReadRegister(ID);
}/*** @brief 获得出厂校准系数* @param COEF_ValueStruct* COEF,系数结构体指针,用于存储系数* @return 0 获取成功* 1 获取失败,读取过程出错,但矫正系数本身可以被读* 2 获取失败,矫正系数没有准备就绪*/
uint8_t GetCoefVal(COEF_ValueStruct* COEF)
{uint8_t buffer[18] = { 0 };if(COEF_CheckStatus() == 0){//开始读取矫正系数if(MyI2C_ReadMultiRegister(SPL06_ADDRESS, COEF_C0, 18, buffer)){return 1;}}else{return 2;}//将校正系数正确存放到变量中COEF->C0 = COEF->raw_C0 = (uint16_t)((buffer[0] << 4) | ((buffer[1] & 0xF0) >> 4)); // 12-bit valueCOEF->C1 = COEF->raw_C1 = (uint16_t)(((buffer[1] & 0x0F) << 8) | buffer[2]); // 12-bit valueCOEF->C00 = COEF->raw_C00 = (uint32_t)((buffer[3] << 12) | (buffer[4] << 4) | ((buffer[5] & 0xF0) >> 4)); // 20-bit valueCOEF->C10 = COEF->raw_C10 = (uint32_t)((buffer[5] & 0x0F) << 16 | buffer[6] << 8 | buffer[7]); // 20-bit valueCOEF->C01 = COEF->raw_C01 = (uint16_t)(buffer[8] << 8 | buffer[9]);COEF->C11 = COEF->raw_C11 = (uint16_t)(buffer[10] << 8 | buffer[11]);COEF->C20 = COEF->raw_C20 = (uint16_t)(buffer[12] << 8 | buffer[13]);COEF->C21 = COEF->raw_C21 = (uint16_t)(buffer[14] << 8 | buffer[15]);COEF->C30 = COEF->raw_C30 = (uint16_t)(buffer[16] << 8 | buffer[17]);//这些数据都是以补码形式存放,如果为负数,应该将其转换if(COEF->raw_C0 & 0x800) COEF->C0 = (COEF->raw_C0 - Total_Number_12);if(COEF->raw_C1 & 0x800) COEF->C1 = (COEF->raw_C1 - Total_Number_12);if(COEF->raw_C00 & 0x80000) COEF->C00 = COEF->raw_C00 - Total_Number_20;if(COEF->raw_C10 & 0x80000) COEF->C10 = COEF->raw_C10 - Total_Number_20;if(COEF->raw_C01 & 0x8000) COEF->C01 = COEF->raw_C01 - Total_Number_16;if(COEF->raw_C11 & 0x8000) COEF->C11 = COEF->raw_C11 - Total_Number_16;if(COEF->raw_C20 & 0x8000) COEF->C20 = COEF->raw_C20 - Total_Number_16;if(COEF->raw_C21 & 0x8000) COEF->C21 = COEF->raw_C21 - Total_Number_16;if(COEF->raw_C30 & 0x8000) COEF->C30 = COEF->raw_C30 - Total_Number_16;return 0;
}/*** @brief 获取经过出厂校准系数补偿过的大气压强值,温度值
* @param [float* baroValue] 接收大气压强值的浮点数,单位: 百帕(hpa)
* [float* tempValue] 接收温度值的浮点数,单位: 摄氏度(℃)* [COEF_ValueStruct* COEF] 校准系数结构体指针* @return 0 获取成功* 1 获取失败,I2C通讯异常*/
uint8_t GetCompensatedVal(float* baroValue, float* tempValue, COEF_ValueStruct* COEF)
{//1. 获取raw数据uint8_t arr_temp[6] = { 0 };uint32_t rawBaroValue = 0, rawTempValue = 0;int32_t Baro, Temp;if(MyI2C_ReadMultiRegister(SPL06_ADDRESS, PSR_B2, 6, arr_temp))//一次性读取大气压强值、温度值的6个寄存器{return 1;}rawBaroValue = (arr_temp[0] << 16) + (arr_temp[1] << 8) + (arr_temp[2]); //24bit有符号数rawTempValue = (arr_temp[3] << 16) + (arr_temp[4] << 8) + (arr_temp[5]);Baro = rawBaroValue & 0x00FFFFFF;Temp = rawTempValue & 0x00FFFFFF;if(rawBaroValue & 0x80000) Baro = rawBaroValue - Total_Number_24; //如果最高位为1,转化为负数if(rawTempValue & 0x80000) Temp = rawTempValue - Total_Number_24;//2. 根据过采样率选择对应的KP,KT系数volatile uint32_t KP, KT;float Praw_Sc, Traw_Sc;KP = FindFactor(b_list, PSR_InitStructure.OVER_SAMPLING_TIMES); //在初始化的链表中寻找对应的比例因子KT = FindFactor(b_list, TEMP_InitStructure.OVER_SAMPLING_TIMES);//3. 带入公式求得校准后的数据Praw_Sc = (float)Baro / KP;Traw_Sc = (float)Temp / KT;//4. 将数据传递到地址中*baroValue = COEF->C00 + Praw_Sc * (COEF->C10 + Praw_Sc * ( COEF->C20 + Praw_Sc * COEF->C30)) + Traw_Sc * COEF->C01 + Traw_Sc * Praw_Sc * (COEF->C11 + Praw_Sc * COEF->C21);*baroValue /= 100; //将压强值转化为hpa*tempValue = COEF->C0 * 0.5f + COEF->C1 * Traw_Sc;return 0;
}/*** @brief 将大气压强值转化为高度值* @param [float P] 大气压强值,单位hpa* @return -1 输入大气压强值错误* Altitude 单精度浮点数,返回海拔高度值,单位m*/
float getAltitude(float P)
{P *= 100;float Altitude;if(P > 30000 && P < 200000){Altitude = 44330.0f * (1.0f - (float)pow(P / P0, 1.0/5.255));}else{return 0;}return Altitude;
}//*************快速排序算法*************//
void swap(float* a, float* b) {float t = *a;*a = *b;*b = t;
}int partition(float arr[], int low, int high) {float pivot = arr[high]; // 选择最后一个元素作为基准int i = (low - 1); // 小于基准的元素的索引for (int j = low; j < high; j++) {// 如果当前元素小于或等于基准if (arr[j] <= pivot) {i++; // 增加小于基准的元素的索引swap(&arr[i], &arr[j]);}}swap(&arr[i + 1], &arr[high]);return (i + 1);
}void quickSort(float arr[], int low, int high) {if (low < high) {// pi 是 partitioning index, arr[p] 现在位于正确位置int pi = partition(arr, low, high);// 分别对基准左右两边的子数组进行递归排序quickSort(arr, low, pi - 1);quickSort(arr, pi + 1, high);}
}/*** @name float AltitudeFilter(float newAltitude)* @brief 对计算得到校准后的高度值进一步滤波,采用快速排序+中值均值滤波* @param [float newAltitude] 采集到的新数据* @return [filterdAltitude] 滤波后的数据*/
float AltitudeFilter(float newAltitude)
{static float AltitudeRawArray[FILTER_MAX_SIZE] = { 0 }; //原始数据static float AltitudeBuffer[FILTER_MAX_SIZE] = { 0 }; //排序缓冲数组float filterdAltitude; //滤波后数据//把原始数组里的数据前移for(uint8_t i = 0; i < FILTER_MAX_SIZE - 1; i++){AltitudeRawArray[i] = AltitudeRawArray[i + 1];}//更新数据AltitudeRawArray[FILTER_MAX_SIZE - 1] = newAltitude;//将更新的数据拷贝给缓冲排序数组memcpy(&AltitudeBuffer, &AltitudeRawArray, sizeof(AltitudeRawArray));//快速排序quickSort(AltitudeBuffer, 0, FILTER_MAX_SIZE - 1);//掐头去尾取中间,去掉前三分之一和后三分之一,取中间平均值float bufferSum = 0;for(uint8_t i = FILTER_MAX_SIZE / 3; i < FILTER_MAX_SIZE * 2/3; i++){bufferSum += AltitudeBuffer[i];}filterdAltitude = bufferSum / (FILTER_MAX_SIZE / 3);return filterdAltitude;
}
SPL06.h
#ifndef __SPL06_H
#define __SPL06_H#include <stdlib.h>
#include <string.h>
#include <math.h>#include "MyI2C.h"//************自然值************//
#define P0 101325.0f //标准大气压强值//************用于负数二补数转换************//
#define Total_Number_24 16777216.0
#define Total_Number_20 1048576.0
#define Total_Number_16 65536.0
#define Total_Number_12 4096.0//************REGISTER ADDRESS************//
//仅列出了部分寄存器组,通过读写多个寄存器实现对所有寄存器的操作,如果需要更多的寄存器请参考datasheet
#define PSR_B2 0x00
#define PSR_B1 0x01
#define PSR_B0 0x02
#define TMP_B2 0x03
#define TMP_B1 0x04
#define TMP_B0 0x05
#define PRS_CFG 0x06
#define TMP_CFG 0x07
#define MEAS_CFG 0x08
#define CFG_REG 0x09
#define INT_STS 0x0A
#define FIFO_STS 0x0B
#define RESET 0x0C
#define ID 0x0D
#define COEF_C0 0x10
#define COEF_C00 0x13
#define COEF_C10 0x17
#define COEF_C01 0x18
#define COEF_C11 0x1A
#define COEF_C20 0x1C
#define COEF_C21 0x1E
#define COEF_C30 0x20//************SPL06 I2C ADDRESS INITIALIZE************//
#define SPL06_ADDRESS 0x77
#define SPL06_ADDRESS_W (SPL06_ADDRESS<<1)|0x00
#define SPL06_ADDRESS_R (SPL06_ADDRESS<<1)|0x01//************PRESSSURE & TEMPERATURE CONFIG************//
//bit[7] only accessiable for temperature config
#define _INTERNAL_SENSOR 0x00
#define _EXTERNAL_SENSOR 0x80
//bit[6:4] MEASURE_RATE for all
#define _1HZ_MEASUREMENT 0x00
#define _2HZ_MEASUREMENT 0x10
#define _4HZ_MEASUREMENT 0x20
#define _8HZ_MEASUREMENT 0x30
#define _16HZ_MEASUREMENT 0x40
#define _32HZ_MEASUREMENT 0x50
#define _64HZ_MEASUREMENT 0x60
#define _128HZ_MEASUREMENT 0x70
//bit[3:0] OVER_SAMPLING_TIMES for all
#define _SINGLE_OVERSAMPLING 0x00
#define _2TIMES_OVERSAMPLING 0x01
#define _4TIMES_OVERSAMPLING 0x02
#define _8TIMES_OVERSAMPLING 0x03
#define _16TIMES_OVERSAMPLING 0x04
#define _32TIMES_OVERSAMPLING 0x05
#define _64TIMES_OVERSAMPLING 0x06
#define _128TIMES_OVERSAMPLING 0x07//************SCALE FACTOR************//
//存放KP、KT的数据集合,该数值与过采样率(OVER_SAMPLING_TIMES)相关
#define _SINGLE_SCALE_FACTOR 524288 //单次过采样对应scaleFactor
#define _2TIMES_SCALE_FACTOR 1572864 //2次过采样对应scaleFactor
#define _4TIMES_SCALE_FACTOR 3670016 //4次过采样对应scaleFactor
#define _8TIMES_SCALE_FACTOR 7864320 //8次过采样对应scaleFactor
#define _16TIMES_SCALE_FACTOR 253952 //16次过采样对应scaleFactor
#define _32TIMES_SCALE_FACTOR 516096 //32次过采样对应scaleFactor
#define _64TIMES_SCALE_FACTOR 1040384 //64次过采样对应scaleFactor
#define _128TIMES_SCALE_FACTOR 2088960 //128次过采样对应scaleFactor//************滤波最大缓冲数************//
#define FILTER_MAX_SIZE 32typedef struct
{uint8_t MEASURE_RATE; //see @MEASURE_RATEuint8_t OVER_SAMPLING_TIMES; //see @OVER_SAMPLING_TIMES
} PSR_InitTypedef;typedef struct
{uint8_t MEASURE_RATE;uint8_t OVER_SAMPLING_TIMES;uint8_t SENSOR_SOURCE;
} TEMP_InitTypedef;//校准系数: coefficient
typedef struct
{uint16_t raw_C0;uint16_t raw_C1;uint32_t raw_C00;uint32_t raw_C10;uint16_t raw_C01;uint16_t raw_C11;uint16_t raw_C20;uint16_t raw_C21;uint16_t raw_C30;int16_t C0;int16_t C1;int32_t C00;int32_t C10;int16_t C01;int16_t C11;int16_t C20;int16_t C21;int16_t C30;} COEF_ValueStruct;//传感器测量模式
typedef enum
{STANDBY = 0x00, //休眠START_SINGLE_PSR, //单次转换大气压强值START_SINGLE_TEMP, //单词转换温度值STRAT_CONTINUOUS_PSR = 0x05, //连续转换大气压强值STRAT_CONTINUOUS_TEMP, //连续转换温度值STRAT_CONTINUOUS_PSR_TEMP = 0x07, //连续转换大气压强值和温度值
} MeasureModeConfig;//定义过采样率对应的比例系数链表
typedef struct node
{/* data */uint8_t OverSamplingRate;uint32_t FACTOR;struct node* next;
} Factor_List;extern Factor_List* b_list; //声明比例系数链表存在
extern COEF_ValueStruct Coefficient; //声明校准系数存在
extern TEMP_InitTypedef TEMP_InitStructure; //声明温度测量初始化配置结构体
extern PSR_InitTypedef PSR_InitStructure; //声明大气压强测量初始化配置结构体//*********************链表初始化*********************//
Factor_List* initList(void);
Factor_List* insertTail(Factor_List *l, uint8_t val, uint32_t Factor);
void Init_FactorList(void);
uint32_t FindFactor(Factor_List* l, uint8_t val);//*********************传感器初始化*********************//
uint8_t SPL06_PSRInitStruct(PSR_InitTypedef* PSR_InitStructure);
uint8_t SPL06_TEMPInitStruct(TEMP_InitTypedef* TEMP_InitStructure);
uint8_t SPL_OperatingModeInit(MeasureModeConfig config);//*********************传感器状态获取*******************//
uint8_t COEF_CheckStatus(void);//*********************传感器内部数据读取***************//
uint8_t GetCoefVal(COEF_ValueStruct* COEF);uint8_t SPL06_ReadID(void);//**********************<对外API,重要!>*********************//
uint8_t SPL06_Init(void);
uint8_t GetCompensatedVal(float* baroValue, float* tempValue, COEF_ValueStruct* COEF);//计算出气压计的单位为hpa
float getAltitude(float P);
float AltitudeFilter(float newAltitude);#endif
MyI2C.c(基于江协科技)
#include "main.h" // Device header
#include "Delay.h"
#include "MyI2C.h"void MyI2C_W_SCL(uint8_t BitValue)
{HAL_GPIO_WritePin(BARO_SCL_GPIO_Port, BARO_SCL_Pin, (GPIO_PinState)BitValue);}void MyI2C_W_SDA(uint8_t BitValue)
{HAL_GPIO_WritePin(BARO_SDA_GPIO_Port, BARO_SDA_Pin, (GPIO_PinState)BitValue);}uint8_t MyI2C_R_SDA(void)
{uint8_t BitValue;BitValue = HAL_GPIO_ReadPin(BARO_SDA_GPIO_Port, BARO_SDA_Pin);return BitValue;
}void MyI2C_Start(void)
{MyI2C_W_SDA(1);MyI2C_W_SCL(1);MyI2C_W_SDA(0);MyI2C_W_SCL(0);
}void MyI2C_Stop(void)
{MyI2C_W_SDA(0);MyI2C_W_SCL(1);MyI2C_W_SDA(1);
}void MyI2C_SendByte(uint8_t Byte)
{uint8_t i;for (i = 0; i < 8; i ++){MyI2C_W_SDA(!!(Byte & (0x80 >> i)));MyI2C_W_SCL(1);MyI2C_W_SCL(0);}
}uint8_t MyI2C_ReceiveByte(void)
{uint8_t i, Byte = 0x00;MyI2C_W_SDA(1);for (i = 0; i < 8; i ++){MyI2C_W_SCL(1);if (MyI2C_R_SDA()){Byte |= (0x80 >> i);}MyI2C_W_SCL(0);}return Byte;
}void MyI2C_SendAck(uint8_t AckBit)
{MyI2C_W_SDA(AckBit);MyI2C_W_SCL(1);MyI2C_W_SCL(0);
}uint8_t MyI2C_ReceiveAck(void)
{uint8_t AckBit;MyI2C_W_SDA(1);MyI2C_W_SCL(1);AckBit = MyI2C_R_SDA();MyI2C_W_SCL(0);return AckBit;
}uint8_t MyI2C_WriteRegister(uint8_t RegAddr, uint8_t byte)
{MyI2C_Start(); //启动总线MyI2C_SendByte(SPL06_ADDRESS_W); //发送写地址if(MyI2C_ReceiveAck() == 1) //未应答return 1;MyI2C_SendByte(RegAddr); //要写入的寄存器地址if(MyI2C_ReceiveAck() == 1) //未应答return 1;MyI2C_SendByte(byte);if(MyI2C_ReceiveAck() == 1) //未应答return 1;MyI2C_Stop();return 0;
}uint8_t MyI2C_ReadRegister(uint8_t RegAddr)
{MyI2C_Start(); //启动总线MyI2C_SendByte(SPL06_ADDRESS_W); //发送写地址if(MyI2C_ReceiveAck() == 1) //未应答return 1;MyI2C_SendByte(RegAddr); //要写入的寄存器地址if(MyI2C_ReceiveAck() == 1) //未应答return 1;MyI2C_Start(); //启动总线MyI2C_SendByte(SPL06_ADDRESS_R); //发送读地址if(MyI2C_ReceiveAck() == 1) //未应答return 1;uint8_t byte = MyI2C_ReceiveByte();MyI2C_SendAck(1);MyI2C_Stop();return byte;
}uint8_t MyI2C_ReadMultiRegister(uint8_t I2CAddr, uint8_t RegAddr, uint8_t length, uint8_t* temp)
{uint8_t AddrW,AddrR;AddrW = I2CAddr<<1;AddrR = (I2CAddr<<1) + 1;MyI2C_Start(); //启动总线MyI2C_SendByte(AddrW); //发送写地址if(MyI2C_ReceiveAck() == 1) //未应答return 1;MyI2C_SendByte(RegAddr); //要写入的寄存器地址if(MyI2C_ReceiveAck() == 1) //未应答return 1;MyI2C_Start(); //启动总线MyI2C_SendByte(AddrR); //发送读地址if(MyI2C_ReceiveAck() == 1) //未应答return 1;for(uint8_t i = 0; i < length; i++){*(temp + i) = MyI2C_ReceiveByte();if(i < length - 1){MyI2C_SendAck(0);}else{MyI2C_SendAck(1);}}MyI2C_Stop();return 0;
}uint8_t MyI2C_WriteMultiRegister(uint8_t I2CAddr, uint8_t RegAddr, uint8_t length, uint8_t* temp)
{uint8_t AddrW;AddrW = I2CAddr<<1;MyI2C_Start(); //启动总线MyI2C_SendByte(AddrW); //发送写地址if(MyI2C_ReceiveAck() == 1) //未应答return 1;MyI2C_SendByte(RegAddr); //要写入的寄存器地址if(MyI2C_ReceiveAck() == 1) //未应答return 1;for(uint8_t i = 0; i < length; i++){MyI2C_SendByte(*(temp + i));if(i < length - 1){MyI2C_SendAck(0);}else{MyI2C_SendAck(1);}}MyI2C_Stop();return 0;
}
MyI2C.h
#ifndef __MYI2C_H
#define __MYI2C_H#include "main.h"
#include "Delay.h"
#include "SPL06.h"void MyI2C_W_SCL(uint8_t BitValue);
void MyI2C_W_SDA(uint8_t BitValue);
uint8_t MyI2C_R_SDA(void);void MyI2C_Start(void);
void MyI2C_Stop(void);
void MyI2C_SendByte(uint8_t Byte);
uint8_t MyI2C_ReceiveByte(void);
void MyI2C_SendAck(uint8_t AckBit);
uint8_t MyI2C_ReceiveAck(void);uint8_t MyI2C_ReadRegister(uint8_t RegAddr);
uint8_t MyI2C_WriteRegister(uint8_t RegAddr, uint8_t byte);
uint8_t MyI2C_ReadMultiRegister(uint8_t I2CAddr, uint8_t RegAddr, uint8_t length, uint8_t* temp);
uint8_t MyI2C_WriteMultiRegister(uint8_t I2CAddr, uint8_t RegAddr, uint8_t length, uint8_t* temp);
#endif
main.c
#include "main.h"
#include "SPL06.h"int main(void)
{HAL_Init();SystemClock_Config();MX_GPIO_Init();uint8_t ack = SPL06_Init();float height, filtredHeight;float baro, temp;while(1){//获取补偿后的值GetCompensatedVal(&baro, &temp, &Coefficient);//转化为高度值height = getAltitude(baro);//排序,中值平均滤波,可以在SPL06.h中修改FILTER_MAX_SIZE来调整滤波filtredHeight = AltitudeFilter(height);rt_thread_delay(250);}
}
相关文章:
SPL06 基于stm32F103 HAL库驱动(软件模拟IIC)
talk is cheap, show you my code SPL06.c #include "SPL06.h"//*************全局变量*************// Factor_List* b_list; //存储过采样率对应的系数KP,KT COEF_ValueStruct Coefficient { 0 }; //存储校准系数…...

【C#】List求并集、交集、差集
值类型List List<int> intList1 new List<int>() { 1, 2, 3 };List<int> intList2 new List<int>() { 3, 4, 5 };var result intList1.Union(intList2);Console.WriteLine($"并 {string.Join(,,result)}");result intList1.Intersect(in…...

YOLOv8目标检测——详细记录使用ONNX Runtime进行推理部署C++/Python实现
概述 在之前博客中有介绍YOLOv8从环境安装到训练的完整过程,本节主要介绍ONNX Runtime的原理以及使用其进行推理加速,使用Python、C两种编程语言来实现。 https://blog.csdn.net/MariLN/article/details/143924548?spm1001.2014.3001.5501 1. ONNX Ru…...

mfc140u.dll是什么文件?如何解决mfc140u.dll丢失的相关问题
遇到“mfc140u.dll文件丢失”的错误通常影响应用程序的运行,这个问题主要出现在使用Microsoft Visual C环境开发的软件中。mfc140u.dll是一个重要的系统文件,如果它丢失或损坏,会导致相关程序无法启动。本文将简要介绍几种快速有效的方法来恢…...

Redis篇-19--运维篇1-主从复制(主从复制,读写分离,配置实现,实战案例)
1、概述 Redis的主从复制(Master-Slave Replication)是一种数据冗余机制,它允许将一台Redis服务器的数据复制到其他Redis服务器。在主从复制中,有一台主服务器(Master)和一个或多个从服务器(Sl…...

【Elasticsearch入门到落地】4、Elasticsearch的安装
接上篇《3、es与mysql的概念对比》 上一篇我们学习了Elasticsearch与Mysql的概念与区别。本篇我们来进行Elasticsearch的环境准备及软件安装。 一、环境准备 如果我们没有自己的Linux服务器,且现在正在使用的是Windows操作系统的电脑,那么首先我们需要安…...

计算无人机俯拍图像的地面采样距离(GSD)矩阵
引言 在无人机遥感、测绘和精细农业等领域,地面采样距离(Ground Sampling Distance,简称 GSD)是一个非常重要的指标。GSD 是指图像中每个像素在地面上实际代表的物理距离,通常以米或厘米为单位。GSD 决定了图像的空间…...
牛客网 SQL37查找多列排序
SQL37查找多列排序 select device_id,gpa,age from user_profile order by gpa asc,age asc#select [字段1,字段2] from [表名] order by [字段1] [升序(asc)/降序(desc)],[字段2] [升序(asc)/降序(desc)] #select:查询 #order by 排序 每日问题 如何处理对象的状…...

el-tabs标签过多
tab-position:top情况 .el-tabs__nav-wrap{overflow-x: auto ;width: 86% ;margin-left: 10px ; } 效果: tab-position:left情况 .el-tabs__nav-wrap{overflow-x: auto ;height: 高度 ;margin-top: 10px ; } 效果: 注意&…...

如何制作搞笑配音视频?操作方法
在数字娱乐盛行的今天,搞笑配音视频凭借其独特的幽默感和创意,在网络上赢得了大量观众的喜爱。如果你也想尝试制作一部让人捧腹的搞笑配音视频,那么请跟随以下步骤,从撰写搞笑文案到视频配音剪辑,一步步打造你的作品。…...

[Unity]Unity跨平台开发之针对Android开发
用户手册的这一部分包含Android平台关于输入(input)、资产管理(asset management)和调试(debugging)等相关主题的开发信息。 Android移动脚本编写 注意:安卓可以在C#中使用UNITY_ANDROID来进行…...
ELK部署
背景 很多公司还是在单体项目中苦苦挣扎,没有必要上elk系统,大家都懂的一个原则系统的技术栈越多系统越复杂,维护起来也越麻烦,在没有大流量高并发的情况下我们就用单体服务挺舒服。我们行业的特殊性做的都是BTB的项目࿰…...

ELK系列-(四)轻量级的日志收集助手-Beat家族
一、前文回顾 ELK系列-(一)Docker部署ELK核心组件 ELK系列-(二)LogStash数据处理的瑞士军刀 ELK系列-(三)Kibana 数据可视化的艺术家 关于部署的整体架构欢迎大家回到前面的文章观看,此处&a…...
NodeJs-包管理工具
包英文单词是 package ,代表了一组特定功能的源码集合 管理包的应用软件,可以对包进行 下载安装 , 更新 , 删除 , 上传 等操作 借助包管理工具,可以快速开发项目,提升开发效率 前端常用的包管理…...

AWR microwave office 仿真学习(二)使用多层结构天线/超表面的S参数确定层间距
引言 如果大家有看过一些多层天线或超表面的论文,有两种比较常用的分析方法,等效电路法和传输线分析法,这两种方法都是三维结构的电磁问题转换为二维/集总的电路问题。本文就介绍根据这种思想进行多层结构优化的一种方法:在AWR软件中根据单层结构的S参数,确定最佳层间距。…...

【zlm】 webrtc源码讲解三(总结)
目录 setsdp onwrite 编辑 play 参考 setsdp onwrite play 参考 【zlm】 webrtc源码讲解_zlm webrtc-CSDN博客 【zlm】 webrtc源码讲解(二)_webrtc 源码-CSDN博客...
Springboot+Druid(可切换Hikari)+Mybatis-plus+mysql+hive的多数据源项目配置
1.搭建一个springboot项目,不会的搜一下,很简单这里不做赘述。 2.首先你搭建的springboot能正常启动之后,pom文件添加如下依赖: <dependency><groupId>com.alibaba</groupId><artifactId>druid</arti…...
Git使用步骤
Git 是一个分布式版本控制系统,广泛用于软件开发和其他需要跟踪文件变更的项目。以下是 Git 的基本使用方法和一些常用命令的详细说明。 安装 Git 在大多数操作系统上,你可以通过包管理器安装 Git: Windows: 下载并安装 Git for Windows。…...

Python+OpenCV系列:AI看图识人、识车、识万物
在人工智能风靡全球的今天,用 Python 和 OpenCV 结合机器学习实现物体识别,不仅是酷炫技能,更是掌握未来的敲门砖。本篇博文手把手教你如何通过摄像头或图片输入,识别人、动物、车辆及其他物品,让你的程序瞬间具备 AI …...

springboot449教学资源共享平台(论文+源码)_kaic
摘 要 如今社会上各行各业,都喜欢用自己行业的专属软件工作,互联网发展到这个时候,人们已经发现离不开了互联网。新技术的产生,往往能解决一些老技术的弊端问题。因为传统教学资源共享平台信息管理难度大,容错率低&am…...
vscode里如何用git
打开vs终端执行如下: 1 初始化 Git 仓库(如果尚未初始化) git init 2 添加文件到 Git 仓库 git add . 3 使用 git commit 命令来提交你的更改。确保在提交时加上一个有用的消息。 git commit -m "备注信息" 4 …...
椭圆曲线密码学(ECC)
一、ECC算法概述 椭圆曲线密码学(Elliptic Curve Cryptography)是基于椭圆曲线数学理论的公钥密码系统,由Neal Koblitz和Victor Miller在1985年独立提出。相比RSA,ECC在相同安全强度下密钥更短(256位ECC ≈ 3072位RSA…...
《Playwright:微软的自动化测试工具详解》
Playwright 简介:声明内容来自网络,将内容拼接整理出来的文档 Playwright 是微软开发的自动化测试工具,支持 Chrome、Firefox、Safari 等主流浏览器,提供多语言 API(Python、JavaScript、Java、.NET)。它的特点包括&a…...

Opencv中的addweighted函数
一.addweighted函数作用 addweighted()是OpenCV库中用于图像处理的函数,主要功能是将两个输入图像(尺寸和类型相同)按照指定的权重进行加权叠加(图像融合),并添加一个标量值&#x…...

MODBUS TCP转CANopen 技术赋能高效协同作业
在现代工业自动化领域,MODBUS TCP和CANopen两种通讯协议因其稳定性和高效性被广泛应用于各种设备和系统中。而随着科技的不断进步,这两种通讯协议也正在被逐步融合,形成了一种新型的通讯方式——开疆智能MODBUS TCP转CANopen网关KJ-TCPC-CANP…...

MySQL 8.0 OCP 英文题库解析(十三)
Oracle 为庆祝 MySQL 30 周年,截止到 2025.07.31 之前。所有人均可以免费考取原价245美元的MySQL OCP 认证。 从今天开始,将英文题库免费公布出来,并进行解析,帮助大家在一个月之内轻松通过OCP认证。 本期公布试题111~120 试题1…...

【JavaWeb】Docker项目部署
引言 之前学习了Linux操作系统的常见命令,在Linux上安装软件,以及如何在Linux上部署一个单体项目,大多数同学都会有相同的感受,那就是麻烦。 核心体现在三点: 命令太多了,记不住 软件安装包名字复杂&…...
Spring AI与Spring Modulith核心技术解析
Spring AI核心架构解析 Spring AI(https://spring.io/projects/spring-ai)作为Spring生态中的AI集成框架,其核心设计理念是通过模块化架构降低AI应用的开发复杂度。与Python生态中的LangChain/LlamaIndex等工具类似,但特别为多语…...
《C++ 模板》
目录 函数模板 类模板 非类型模板参数 模板特化 函数模板特化 类模板的特化 模板,就像一个模具,里面可以将不同类型的材料做成一个形状,其分为函数模板和类模板。 函数模板 函数模板可以简化函数重载的代码。格式:templa…...
Python Einops库:深度学习中的张量操作革命
Einops(爱因斯坦操作库)就像给张量操作戴上了一副"语义眼镜"——让你用人类能理解的方式告诉计算机如何操作多维数组。这个基于爱因斯坦求和约定的库,用类似自然语言的表达式替代了晦涩的API调用,彻底改变了深度学习工程…...