호타리
2025. 6. 24. 16:57
2025.06.24
BMP180
i2c라이브러리를 이용해서 기압 측정
bmp180_i2c.c
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <linux/i2c-dev.h>
#include <math.h>
#include <string.h>
#include <errno.h>
// BMP180 I2C 주소 및 레지스터
#define BMP180_ADDRESS 0x77
#define BMP180_REG_CAL_AC1 0xAA
#define BMP180_REG_CONTROL 0xF4
#define BMP180_REG_RESULT 0xF6
#define BMP180_CMD_READ_TEMP 0x2E
#define BMP180_CMD_READ_PRESSURE 0x34
// 보정 계수 전역 저장
static short ac1, ac2, ac3, b1, b2, mb, mc, md;
static unsigned short ac4, ac5, ac6;
static int i2c_fd;
// 함수 프로토타입
static void read_calibration_data(void);
static unsigned int read_ut(void);
static unsigned int read_up(int oss);
// 보정 데이터 읽기
static void read_calibration_data(void) {
unsigned char buf[22];
if (read(i2c_fd, buf, 22) != 22) {
perror("Failed to read calibration data");
exit(1);
}
ac1 = (buf[0] << 8) | buf[1];
ac2 = (buf[2] << 8) | buf[3];
ac3 = (buf[4] << 8) | buf[5];
ac4 = (buf[6] << 8) | buf[7];
ac5 = (buf[8] << 8) | buf[9];
ac6 = (buf[10] << 8) | buf[11];
b1 = (buf[12] << 8) | buf[13];
b2 = (buf[14] << 8) | buf[15];
mb = (buf[16] << 8) | buf[17];
mc = (buf[18] << 8) | buf[19];
md = (buf[20] << 8) | buf[21];
}
// 원시 온도 읽기
static unsigned int read_ut(void) {
unsigned char cmd[2] = { BMP180_REG_CONTROL, BMP180_CMD_READ_TEMP };
if (write(i2c_fd, cmd, 2) != 2) {
perror("Failed to write temp cmd");
exit(1);
}
usleep(5000);
unsigned char reg = BMP180_REG_RESULT;
if (write(i2c_fd, ®, 1) != 1) {
perror("Failed to set temp read ptr");
exit(1);
}
unsigned char buf[2];
if (read(i2c_fd, buf, 2) != 2) {
perror("Failed to read uncomp temp");
exit(1);
}
return (buf[0] << 8) | buf[1];
}
// 원시 압력 읽기
static unsigned int read_up(int oss) {
unsigned char cmd[2] = { BMP180_REG_CONTROL,
(unsigned char)(BMP180_CMD_READ_PRESSURE + (oss << 6)) };
if (write(i2c_fd, cmd, 2) != 2) {
perror("Failed to write press cmd");
exit(1);
}
// OSS에 따른 대기
switch (oss) {
case 0: usleep(5000); break;
case 1: usleep(8000); break;
case 2: usleep(14000); break;
case 3: usleep(26000); break;
}
unsigned char reg = BMP180_REG_RESULT;
if (write(i2c_fd, ®, 1) != 1) {
perror("Failed to set press read ptr");
exit(1);
}
unsigned char buf[3];
if (read(i2c_fd, buf, 3) != 3) {
perror("Failed to read uncomp press");
exit(1);
}
return (((buf[0] << 16) | (buf[1] << 8) | buf[2]) >> (8 - oss));
}
int main(void) {
const char *i2c_dev = "/dev/i2c-1";
// I2C 버스 오픈
if ((i2c_fd = open(i2c_dev, O_RDWR)) < 0) {
perror("Open i2c bus");
return 1;
}
if (ioctl(i2c_fd, I2C_SLAVE, BMP180_ADDRESS) < 0) {
perror("ioctl I2C_SLAVE");
return 1;
}
// 보정 데이터 읽기 위치 설정
unsigned char start = BMP180_REG_CAL_AC1;
if (write(i2c_fd, &start, 1) != 1) {
perror("Set cal data ptr");
return 1;
}
read_calibration_data();
printf("BMP180 실시간 측정 시작 (Ctrl+C 또는 Ctrl+Z 로 종료)\n\n");
while (1) {
// 원시값 읽기
unsigned int ut = read_ut();
unsigned int up = read_up(3); // 최고 해상도 OSS=3
// 온도 계산
long x1 = ((long)ut - ac6) * ac5 >> 15;
long x2 = ((long)mc << 11) / (x1 + md);
long b5 = x1 + x2;
double temperature = ((b5 + 8) >> 4) / 10.0; // °C
// 압력 계산
long b6 = b5 - 4000;
x1 = (b2 * (b6 * b6 >> 12)) >> 11;
x2 = (ac2 * b6) >> 11;
long x3 = x1 + x2;
long b3 = ((((long)ac1 * 4 + x3) << 3) + 2) >> 2;
x1 = (ac3 * b6) >> 13;
x2 = (b1 * (b6 * b6 >> 12)) >> 16;
x3 = ((x1 + x2) + 2) >> 2;
unsigned long b4 = (ac4 * (unsigned long)(x3 + 32768)) >> 15;
unsigned long b7 = ((unsigned long)up - b3) * (50000 >> 3);
long p;
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);
double pressure_hpa = p / 100.0;
printf("온도: %.2f °C 압력: %.2f hPa\n",
temperature, pressure_hpa);
sleep(1);
}
close(i2c_fd);
return 0;
}
디바이스 드라이버 개발
bmp180.c
// SPDX-License-Identifier: GPL-2.0
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/i2c.h>
#include <linux/delay.h>
#include <linux/mutex.h>
#include <linux/sysfs.h>
#include <linux/of.h>
#include <linux/of_device.h>
#define BMP180_REG_CALIB 0xAA
#define BMP180_REG_CTRL 0xF4
#define BMP180_REG_DATA 0xF6
#define BMP180_CMD_TEMP 0x2E
#define BMP180_CMD_PRESS 0x34
struct bmp180_data {
struct i2c_client *client;
struct mutex lock;
/* Calibration coefficients */
s16 ac1, ac2, ac3, b1, b2, mb, mc, md;
u16 ac4, ac5, ac6;
int oss;
};
static int bmp180_read_calib(struct bmp180_data *d)
{
u8 buf[22];
int ret;
ret = i2c_smbus_read_i2c_block_data(d->client,
BMP180_REG_CALIB,
sizeof(buf), buf);
if (ret < 0)
return ret;
d->ac1 = (buf[0] << 8) | buf[1];
d->ac2 = (buf[2] << 8) | buf[3];
d->ac3 = (buf[4] << 8) | buf[5];
d->ac4 = (buf[6] << 8) | buf[7];
d->ac5 = (buf[8] << 8) | buf[9];
d->ac6 = (buf[10] << 8) | buf[11];
d->b1 = (buf[12] << 8) | buf[13];
d->b2 = (buf[14] << 8) | buf[15];
d->mb = (buf[16] << 8) | buf[17];
d->mc = (buf[18] << 8) | buf[19];
d->md = (buf[20] << 8) | buf[21];
return 0;
}
static int bmp180_read_raw_temp(struct bmp180_data *d, int *ut)
{
int ret;
u8 msb, lsb;
/* Send temperature measurement command */
ret = i2c_smbus_write_byte_data(d->client,
BMP180_REG_CTRL,
BMP180_CMD_TEMP);
if (ret < 0)
return ret;
msleep(5);
/* Read MSB and LSB directly from DATA registers */
msb = i2c_smbus_read_byte_data(d->client, BMP180_REG_DATA);
lsb = i2c_smbus_read_byte_data(d->client, BMP180_REG_DATA + 1);
if ((int)msb < 0 || (int)lsb < 0)
return -EIO;
*ut = (msb << 8) | lsb;
return 0;
}
static int bmp180_read_raw_press(struct bmp180_data *d, int *up)
{
int ret;
u8 msb, lsb, xlsb;
ret = i2c_smbus_write_byte_data(d->client,
BMP180_REG_CTRL,
BMP180_CMD_PRESS + (d->oss << 6));
if (ret < 0)
return ret;
msleep(5 + (3 << d->oss));
msb = i2c_smbus_read_byte_data(d->client, BMP180_REG_DATA);
lsb = i2c_smbus_read_byte_data(d->client, BMP180_REG_DATA + 1);
xlsb = i2c_smbus_read_byte_data(d->client, BMP180_REG_DATA + 2);
if ((int)msb < 0 || (int)lsb < 0 || (int)xlsb < 0)
return -EIO;
*up = ((msb << 16) | (lsb << 8) | xlsb) >> (8 - d->oss);
return 0;
}
static long bmp180_calc_temp(struct bmp180_data *d, int ut, int *t)
{
long x1 = ((long)ut - d->ac6) * d->ac5 >> 15;
long x2 = ((long)d->mc << 11) / (x1 + d->md);
long b5 = x1 + x2;
*t = (b5 + 8) >> 4; /* 0.1°C unit */
return b5;
}
static int bmp180_calc_press(struct bmp180_data *d, int up,
long b5, int *p)
{
long b6 = b5 - 4000;
long x1 = (d->b2 * (b6 * b6 >> 12)) >> 11;
long x2 = (d->ac2 * b6) >> 11;
long x3 = x1 + x2;
long b3 = ((((long)d->ac1 * 4 + x3) << d->oss) + 2) >> 2;
x1 = (d->ac3 * b6) >> 13;
x2 = (d->b1 * (b6 * b6 >> 12)) >> 16;
x3 = ((x1 + x2) + 2) >> 2;
long b4 = (d->ac4 * (unsigned long)(x3 + 32768)) >> 15;
long b7 = ((unsigned long)up - b3) * (50000 >> d->oss);
long p_raw = (b7 < 0x80000000 ?
(b7 << 1) / b4 :
(b7 / b4) << 1);
x1 = (p_raw >> 8) * (p_raw >> 8);
x1 = (x1 * 3038) >> 16;
x2 = (-7357 * p_raw) >> 16;
*p = p_raw + ((x1 + x2 + 3791) >> 4);
return 0;
}
/* sysfs show functions */
static ssize_t show_temp(struct device *dev,
struct device_attribute *attr,
char *buf)
{
struct bmp180_data *d = dev_get_drvdata(dev);
int ut, t, ret;
long b5;
mutex_lock(&d->lock);
ret = bmp180_read_raw_temp(d, &ut);
if (ret < 0)
goto out;
b5 = bmp180_calc_temp(d, ut, &t);
out:
mutex_unlock(&d->lock);
if (ret < 0)
return ret;
return sprintf(buf, "%d\n", t);
}
static DEVICE_ATTR(temp, 0444, show_temp, NULL);
static ssize_t show_press(struct device *dev,
struct device_attribute *attr,
char *buf)
{
struct bmp180_data *d = dev_get_drvdata(dev);
int ut, up, p, ret;
long b5;
mutex_lock(&d->lock);
ret = bmp180_read_raw_temp(d, &ut);
if (ret < 0)
goto out;
b5 = bmp180_calc_temp(d, ut, &p);
ret = bmp180_read_raw_press(d, &up);
if (ret < 0)
goto out;
ret = bmp180_calc_press(d, up, b5, &p);
out:
mutex_unlock(&d->lock);
if (ret < 0)
return ret;
return sprintf(buf, "%d\n", p);
}
static DEVICE_ATTR(pressure, 0444, show_press, NULL);
static struct attribute *bmp180_attrs[] = {
&dev_attr_temp.attr,
&dev_attr_pressure.attr,
NULL,
};
static const struct attribute_group bmp180_group = {
.attrs = bmp180_attrs,
};
static const struct of_device_id bmp180_of_match[] = {
{ .compatible = "bosch,bmp180" },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, bmp180_of_match);
static const struct i2c_device_id bmp180_id[] = {
{ "bmp180", 0 },
{ }
};
MODULE_DEVICE_TABLE(i2c, bmp180_id);
static int bmp180_probe(struct i2c_client *client)
{
struct bmp180_data *d;
int ret;
d = devm_kzalloc(&client->dev,
sizeof(*d), GFP_KERNEL);
if (!d)
return -ENOMEM;
mutex_init(&d->lock);
d->client = client;
d->oss = 0;
i2c_set_clientdata(client, d);
ret = bmp180_read_calib(d);
if (ret < 0) {
dev_err(&client->dev,
"calibration read failed: %d\n", ret);
return ret;
}
ret = sysfs_create_group(&client->dev.kobj,
&bmp180_group);
if (ret)
dev_err(&client->dev,
"sysfs group create failed: %d\n", ret);
dev_info(&client->dev, "BMP180 sensor probed\n");
return ret;
}
static void bmp180_remove(struct i2c_client *client)
{
sysfs_remove_group(&client->dev.kobj,
&bmp180_group);
}
static struct i2c_driver bmp180_driver = {
.driver = {
.name = "bmp180",
.of_match_table = of_match_ptr(bmp180_of_match),
},
.probe = bmp180_probe,
.remove = bmp180_remove,
.id_table = bmp180_id,
};
module_i2c_driver(bmp180_driver);
MODULE_AUTHOR("Your Name");
MODULE_DESCRIPTION("BMP180 pressure/temperature sensor driver");
MODULE_LICENSE("GPL v2");
응용프로그램 개발
bmp180_read.c
// bmp180_read.c
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <dirent.h>
#include <string.h>
#include <errno.h>
#include <sys/stat.h>
#define I2C_DEV_DIR "/sys/bus/i2c/devices"
#define TEMP_FILE "temp"
#define PRESS_FILE "pressure"
// 디렉토리 안에 temp와 pressure 파일이 모두 있는 첫 번째 디렉토리 검색
static int find_bmp180_dir(char *out, size_t len) {
DIR *d = opendir(I2C_DEV_DIR);
struct dirent *ent;
if (!d) return -1;
while ((ent = readdir(d)) != NULL) {
// 디렉토리 이름이 "버스-주소" 형태인지 간단히 체크
if (strchr(ent->d_name, '-') == NULL)
continue;
char path_temp[256], path_press[256];
snprintf(path_temp, sizeof(path_temp),
I2C_DEV_DIR "/%s/" TEMP_FILE, ent->d_name);
snprintf(path_press, sizeof(path_press),
I2C_DEV_DIR "/%s/" PRESS_FILE, ent->d_name);
if (access(path_temp, R_OK) == 0 && access(path_press, R_OK) == 0) {
snprintf(out, len, I2C_DEV_DIR "/%s", ent->d_name);
closedir(d);
return 0;
}
}
closedir(d);
return -1;
}
// sysfs에서 정수 읽기
static int read_int(const char *path, int *out) {
char buf[32];
int fd = open(path, O_RDONLY);
if (fd < 0) return -1;
ssize_t n = read(fd, buf, sizeof(buf)-1);
close(fd);
if (n <= 0) return -1;
buf[n] = '\0';
*out = atoi(buf);
return 0;
}
int main(void) {
char devdir[128];
if (find_bmp180_dir(devdir, sizeof(devdir)) < 0) {
fprintf(stderr, "ERROR: bmp180 모듈 디렉토리를 찾을 수 없습니다.\n");
return 1;
}
char temp_path[256], press_path[256];
snprintf(temp_path, sizeof(temp_path),
"%s/" TEMP_FILE, devdir);
snprintf(press_path, sizeof(press_path),
"%s/" PRESS_FILE, devdir);
printf("BMP180 기압·온도 측정 (Ctrl+C로 종료)\n\n");
while (1) {
int t_raw, p_raw;
if (read_int(temp_path, &t_raw) < 0 ||
read_int(press_path, &p_raw) < 0) {
fprintf(stderr,
"센서 값 읽기 실패: %s\n",
strerror(errno));
return 1;
}
// 모듈에서 temp는 0.1°C 단위로, pressure는 Pa 단위로 리턴됩니다.
double temp_c = t_raw / 10.0;
double press_hpa = p_raw / 100.0;
printf("온도: %.1f °C 압력: %.2f hPa\n",
temp_c, press_hpa);
sleep(1);
}
return 0;
}
