You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
155 lines
5.3 KiB
C
155 lines
5.3 KiB
C
// En bmp180.c
|
|
#include <stdint.h>
|
|
#include <unistd.h>
|
|
#include <stdio.h>
|
|
#include "bmp180.h"
|
|
|
|
// --- FUNCIÓN AUXILIAR PARA LEER VLOQUES DE DATOS ---
|
|
int leer_bloque_i2c(int fd, uint8_t registro_inicial, uint8_t *buffer, int longitud) {
|
|
// Le decimos al sensor desde qué registro queremos empezar a leer
|
|
// (El sensor cuenta con un puntero autoincrementado que inicia del registro inicial enviando una rafaga)
|
|
if (write(fd, ®istro_inicial, 1) != 1) {
|
|
perror("Fallo al escribir el registro inicial");
|
|
return -1; // Falló
|
|
}
|
|
|
|
// Leemos la cantidad de bytes que pedimos
|
|
if (read(fd, buffer, longitud) != longitud) {
|
|
perror("Fallo al leer los bytes del sensor");
|
|
return -1; // Falló
|
|
}
|
|
|
|
return 0; // Éxito
|
|
}
|
|
|
|
// --- MÉTODO DE INICIALIZACIÓN PARA RECONSTRUIR LOS DATOS DE CALIBRACIÓN ---
|
|
int bmp180_init(int fd, BMP180_CalibData *calib) {
|
|
uint8_t buffer[22]; // Necesitamos un arreglo de 22 espacios para guardar los 22 bytes
|
|
|
|
printf("Iniciando BMP180 y leyendo calibracion...\n");
|
|
|
|
// Leemos los 22 bytes empezando desde el registro 0xAA
|
|
if (leer_bloque_i2c(fd, 0xAA, buffer, 22) < 0) {
|
|
printf("Error fatal: No se pudo leer la EEPROM.\n");
|
|
return -1; // Falló
|
|
}
|
|
|
|
// Unimos los bytes.
|
|
// El sensor envía primero el byte "pesado" (MSB) y luego el "ligero" (LSB).
|
|
// Tomamos el MSB, lo desplazamos 8 espacios a la izquierda (<< 8) y le sumamos el LSB (|).
|
|
calib->ac1 = (buffer[0] << 8) | buffer[1];
|
|
calib->ac2 = (buffer[2] << 8) | buffer[3];
|
|
calib->ac3 = (buffer[4] << 8) | buffer[5];
|
|
|
|
// Estos tres deben ser tratados como sin signo (unsigned) según el datasheet
|
|
calib->ac4 = (buffer[6] << 8) | buffer[7];
|
|
calib->ac5 = (buffer[8] << 8) | buffer[9];
|
|
calib->ac6 = (buffer[10] << 8) | buffer[11];
|
|
|
|
calib->b1 = (buffer[12] << 8) | buffer[13];
|
|
calib->b2 = (buffer[14] << 8) | buffer[15];
|
|
calib->mb = (buffer[16] << 8) | buffer[17];
|
|
calib->mc = (buffer[18] << 8) | buffer[19];
|
|
calib->md = (buffer[20] << 8) | buffer[21];
|
|
|
|
// Verificamos que se realizo correctamente
|
|
// Si la comunicación falla o el cable está mal, a veces el I2C devuelve puros ceros (0)
|
|
// o puros unos (0xFFFF = -1 en short)
|
|
if (calib->ac1 == 0 || calib->ac1 == -1) {
|
|
printf("Advertencia: Los datos de calibración parecen inválidos.\n");
|
|
return -1; // Falló
|
|
}
|
|
|
|
printf("Calibracion exitosa. Valor AC1: %d\n", calib->ac1);
|
|
|
|
return 0; // Todo listo para medir
|
|
}
|
|
|
|
// --- MÉTODO PARA LEER LA TEMPERATURA CRUDA ---
|
|
long bmp180_read_ut(int fd) {
|
|
// 0xF4 es el registro de control, 0x2E es el comando de temperatura
|
|
uint8_t cmd[2] = {BMP180_REG_CTRL_MEAS, BMP180_CMD_TEMP};
|
|
|
|
// Escribimos el comando
|
|
write(fd, cmd, 2);
|
|
|
|
// Esperamos a que el hardware del sensor termine la conversión (4.5ms)
|
|
usleep(4500);
|
|
|
|
uint8_t buf[2];
|
|
// Leemos los 2 bytes resultantes en el registro 0xF6
|
|
leer_bloque_i2c(fd, BMP180_REG_ADC_MSB, buf, 2);
|
|
|
|
// Ensamblamos los 16 bits
|
|
return (buf[0] << 8) | buf[1];
|
|
}
|
|
|
|
// --- MÉTODO PARA LEER LA PRESIÓN CRUDA ---
|
|
long bmp180_read_up(int fd, int oss) {
|
|
// Calculamos el comando exacto basado en la resolución (oss)
|
|
uint8_t cmd_val = 0x34 + (oss << 6);
|
|
uint8_t cmd[2] = {BMP180_REG_CTRL_MEAS, cmd_val};
|
|
|
|
write(fd, cmd, 2);
|
|
|
|
// El tiempo de espera del hardware aumenta si pides más resolución
|
|
switch(oss) {
|
|
case 0: usleep(4500); break;
|
|
case 1: usleep(7500); break;
|
|
case 2: usleep(13500); break;
|
|
case 3: usleep(25500); break;
|
|
}
|
|
|
|
uint8_t buf[3];
|
|
// Leemos 3 bytes (MSB, LSB, XLSB) desde 0xF6
|
|
leer_bloque_i2c(fd, BMP180_REG_ADC_MSB, buf, 3);
|
|
|
|
// Desplazamiento a nivel de bits exigido por el fabricante
|
|
return ((long)buf[0] << 16 | (long)buf[1] << 8 | (long)buf[2]) >> (8 - oss);
|
|
}
|
|
|
|
|
|
// --- MÉTODO PARA MEDIR LA PRESIÓN CALIBRADA ---
|
|
int bmp180_get_pressure(int fd, BMP180_CalibData *calib, int oss, double *pressure) {
|
|
// Medimos la temperatura y presión crudas
|
|
long UT = bmp180_read_ut(fd);
|
|
long UP = bmp180_read_up(fd, oss);
|
|
|
|
// Declaramos las variables locales según la hoja de datos (página 15)
|
|
long X1, X2, X3, B3, B5, B6, p;
|
|
unsigned long B4, B7;
|
|
|
|
// --- Ecuaciones de Temperatura (Para obtener B5) ---
|
|
X1 = ((UT - calib->ac6) * calib->ac5) >> 15;
|
|
X2 = ((long)calib->mc << 11) / (X1 + calib->md); // Cast a long obligatorio en mc
|
|
B5 = X1 + X2;
|
|
|
|
// --- Ecuaciones de Presión ---
|
|
B6 = B5 - 4000;
|
|
X1 = (calib->b2 * (B6 * B6 >> 12)) >> 11;
|
|
X2 = (calib->ac2 * B6) >> 11;
|
|
X3 = X1 + X2;
|
|
B3 = ((((long)calib->ac1 * 4 + X3) << oss) + 2) >> 2;
|
|
|
|
X1 = (calib->ac3 * B6) >> 13;
|
|
X2 = (calib->b1 * ((B6 * B6) >> 12)) >> 16;
|
|
X3 = ((X1 + X2) + 2) >> 2;
|
|
B4 = (calib->ac4 * (unsigned long)(X3 + 32768)) >> 15;
|
|
B7 = ((unsigned long)UP - B3) * (50000 >> oss);
|
|
|
|
if (B7 < 0x80000000) {
|
|
p = (B7 * 2) / B4;
|
|
} else {
|
|
p = (B7 / B4) * 2;
|
|
}
|
|
|
|
X1 = (p >> 8) * (p >> 8);
|
|
X1 = (X1 * 3038) >> 16;
|
|
X2 = (-7357 * p) >> 16;
|
|
p = p + ((X1 + X2 + 3791) >> 4);
|
|
|
|
// El cálculo arroja Pascales, dividimos entre 100 para Hectopascales (hPa)
|
|
*pressure = p / 100.0;
|
|
|
|
return 0; // Éxito
|
|
} |