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

// 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, &registro_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
}