Example Device: DEBIX Model A + I/O Board
Overview
The BMP180 is a high-precision barometric pressure sensor with low power consumption and low noise. It comes with an internal temperature sensor to compensate for barometric pressure measurements and uses I2C communication.
Hardware installation
- Barometric Pressure Sensor module's VCC terminal is connected to J2 Pin2 of DEBIX Model A I/O board
- Barometric Pressure Sensor module's SDA terminal is connected to J2 Pin3 of DEBIX Model A I/O board
- Barometric Pressure Sensor module's SCL terminal is connected to J2 Pin5 of DEBIX Model A I/O board
- Barometric Pressure Sensor module's GND terminal is connected to J2 Pin39 of DEBIX Model A I/O board
- Barometric Pressure Sensor module's 3.3V terminal is connected to J2 Pin1 of DEBIX Model A I/O board
Modify the device tree imx8mp-evk.dts
1.Add a BMP180 node under i2c4 node:
&i2c4 {
status = "okay";
BMP180: BMP180@77 {
compatible = "BMP180";
reg = <0x77>;
};
2.Compile and replace the device tree file
Compile the device tree in the source directory: make ARCH=arm64 CROSS_COMPILE=aarch64-none-linux-gnu- dtbs
Copy the compiled new device tree file arch/arm64/boot/dts/freescale/imx8mp-evk.dtb to the /boot directory of DEBIX and restart DEBIX.
3.Check whether dts contains the modified device tree node.
Run the command ls /sys/bus/i2c/devices/
Write and Load Drivers
The driver code is as follows:
#include <linux/init.h>
#include <linux/module.h>
#include <linux/i2c.h>
#include <linux/miscdevice.h>
#include <linux/fs.h>
#include <linux/delay.h>
static struct i2c_client* fxs_i2c_client;
typedef struct {
s16 AC1;
s16 AC2;
s16 AC3;
u16 AC4;
u16 AC5;
u16 AC6;
s16 B1;
s16 B2;
s16 MB;
s16 MC;
s16 MD;
}BMP180_CalcParam_t;
typedef struct {
s32 T;
s32 P;
}BMP180_Result;
BMP180_CalcParam_t BMP180_CalcParams;
static s32 i2c_write_reg(u8 reg_addr, u8 *data, u8 len)
{
u8 buff[256];
struct i2c_msg msgs[] = {
[0] = {
.addr = fxs_i2c_client->addr,
.flags = 0,
.len = len + 1,
.buf = buff,
},
};
buff[0] = reg_addr;
memcpy(&buff[1], data, len);
return i2c_transfer(fxs_i2c_client->adapter, msgs, 1);
}
static s32 i2c_read_reg(u8 reg,void* data, int len){
int ret;
struct i2c_msg msgs[] = {
[0]={
.addr = fxs_i2c_client->addr,
.flags = 0,
.buf = ®,
.len = sizeof(reg),
},
[1]={
.addr = fxs_i2c_client->addr,
.flags = I2C_M_RD,
.buf = data,
.len = len,
},
};
ret = i2c_transfer(fxs_i2c_client->adapter,msgs,2);
if(ret == 2) {
return ret;
}else{
printk("read:i2c_transfer err..\n");
return -1;
}
}
static s16 BMP180_Reads16Reg(u8 regh,u8 regl){
u8 datah,datal;
s16 val;
i2c_read_reg(regh,&datah,1);
i2c_read_reg(regl,&datal,1);
val = (datah & 0x00ff) << 8;
val |= datal & 0xff;
return val;
}
static u16 BMP180_Readu16Reg(u8 regh,u8 regl){
u8 datah,datal;
u16 val;
i2c_read_reg(regh,&datah,1);
i2c_read_reg(regl,&datal,1);
val = (datah & 0x00ff) << 8;
val |= datal & 0xff;
return val;
}
static s32 BMP180_Read32Reg(u8 regh,u8 regm,u8 regl){
u8 datah,datam,datal;
s32 val;
i2c_read_reg(regh,&datah,1);
i2c_read_reg(regm,&datam,1);
i2c_read_reg(regl,&datal,1);
val = (datah & 0x0000ff) << 16;
val |= (datam & 0x0000ff) << 8;
val |= (datal & 0x0000ff) ;
return val;
}
static void BMP180_Init(void){
BMP180_CalcParam_t* pBMP180_CP = &BMP180_CalcParams;
pBMP180_CP->AC1 = BMP180_Reads16Reg(0xaa,0xab);
pBMP180_CP->AC2 = BMP180_Reads16Reg(0xac,0xad);
pBMP180_CP->AC3 = BMP180_Reads16Reg(0xae,0xaf);
pBMP180_CP->AC4 = BMP180_Readu16Reg(0xb0,0xb1);
pBMP180_CP->AC5 = BMP180_Readu16Reg(0xb2,0xb3);
pBMP180_CP->AC6 = BMP180_Readu16Reg(0xb4,0xb4);
pBMP180_CP->B1 = BMP180_Reads16Reg(0xb6,0xb7);
pBMP180_CP->B2 = BMP180_Reads16Reg(0xb8,0xb9);
pBMP180_CP->MB = BMP180_Reads16Reg(0xba,0xbb);
pBMP180_CP->MC = BMP180_Reads16Reg(0xbc,0xbd);
pBMP180_CP->MD = BMP180_Reads16Reg(0xbe,0xbf);
}
static void BMP180_ReadT_P(s32* T,s32* P){
BMP180_CalcParam_t* pBMP180_CP = &BMP180_CalcParams;
u8 data = 0x2e;
s32 UT,X1,X2,X3,B3,B6,UP,p;
u32 B4,B7;
i2c_write_reg(0xf4,&data,1);
mdelay(20);
UT = BMP180_Read32Reg(0xf6,0xf7,0xf8) >> 8;
X1 = (UT-pBMP180_CP->AC6)* pBMP180_CP->AC5 >> 15;
X2 = (pBMP180_CP->MC << 11) / (X1 + pBMP180_CP->MD);
*T = (X1 + X2 +8) >> 4 ;
data = 0x34;
i2c_write_reg(0xf4,&data,1);
mdelay(20);
UP = BMP180_Read32Reg(0xf6,0xf7,0xf8) >> 8;
B6 = X1 + X2 - 4000;
X1 = (B6 * B6 >> 12) * pBMP180_CP->B2 >> 11;
X2 = pBMP180_CP->AC2 * B6 >> 11;
X3 = X1 + X2;
B3 = (((pBMP180_CP->AC1 << 2) + X3) + 2) >> 2;
X1 = pBMP180_CP->AC3 * B6 >> 13;
X2 = (B6 * B6 >> 12) * pBMP180_CP->B1 >> 16;
X3 = (X1 + X2 + 2) >> 2;
B4 = pBMP180_CP->AC4 * (u32)(X3 + 32768) >> 15;
B7 = ((u32)UP - B3) * 50000;
if(B7 < 0x80000000){
p = (B7 << 1) / B4;
}else{
p = B7/B4 << 1;
}
X1 = (p >> 8) * (p >> 8);
X1 = (X1 * 3038) >> 16;
X2 = (-7375 * p) >> 16;
p = p + ((X1 + X2 + 3791) >> 4);
*P = p;
}
int misc_open (struct inode *inode, struct file *file){
printk("misc_open..\n");
BMP180_Init();
return 0;
}
int misc_release (struct inode *inode, struct file *file){
printk("misc_release..\n");
return 0;
}
ssize_t misc_read (struct file *file, char __user *ubuf, size_t size, loff_t *of){
int ret;
BMP180_Result BMP180_Result;
BMP180_ReadT_P(&BMP180_Result.T,&BMP180_Result.P);
ret = copy_to_user(ubuf,&BMP180_Result,sizeof(BMP180_Result));
if(ret!=0){
printk("copy_to_user err..\n");
return -1;
}
return sizeof(BMP180_Result);
}
static const struct file_operations misc_fops={
.owner = THIS_MODULE,
.open = misc_open,
.release = misc_release,
.read = misc_read,
};
static struct miscdevice misc_test={
.minor = MISC_DYNAMIC_MINOR,
.name = "BMP180",
.fops = &misc_fops
};
static int BMP180_i2c_probe(struct i2c_client *client, const struct i2c_device_id *id){
int ret;
fxs_i2c_client = client;
ret = misc_register(&misc_test);
if(ret<0){
printk("misc_register err..\n");
return -1;
}
printk("i2c probe success..\n");
return 0;
}
static int BMP180_i2c_remove(struct i2c_client *client){
misc_deregister(&misc_test);
return 0;
}
static const struct i2c_device_id id_BMP180_i2c_driver[] = {
{"BMP180",0,},
{}
};
static const struct of_device_id of_BMP180_i2c_driver[] = {
{.compatible = "BMP180",0,},
{},
};
static struct i2c_driver BMP180_i2c_driver = {
.driver = {
.owner = THIS_MODULE,
.name = "BMP180",
.of_match_table = of_BMP180_i2c_driver,
},
.probe = BMP180_i2c_probe,
.remove = BMP180_i2c_remove,
.id_table = id_BMP180_i2c_driver,
};
static int __init i2c_init(void){
int ret ;
ret = i2c_add_driver(&BMP180_i2c_driver);
if(ret < 0){
printk("i2c_add_driver error..\n");
return ret;
}
printk("i2c_init..\n");
return 0;
}
static void __exit i2c_exit(void){
i2c_del_driver(&BMP180_i2c_driver);
printk("i2c_exit..\n");
}
module_init(i2c_init);
module_exit(i2c_exit);
MODULE_LICENSE("GPL");
1.Run the above command to compile the driver make
will get the .ko file
2.Copy the .ko file to DEBIX, run the command sudo insmod BMP180.ko
3.Run the command sudo dmesg | tail -2
, check the driver print information:
4.You can see that the probe
function matches the device tree successfully.
Write Application Test Code
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/ioctl.h>
typedef struct {
int T;
int P;
}BMP180_Result;
int main(int argc,char *argv[])
{
int fd,ret;
int value;
BMP180_Result BMP180_Result;
fd = open("/dev/BMP180", O_RDWR);
if (fd < 0){
perror("open error \n");
return fd;
}
while (1){
read(fd,&BMP180_Result,sizeof(BMP180_Result)); printf("temperature: %d.%dC\n",BMP180_Result.T/10,
BMP180_Result.T%10);
printf("pressure: %dpa\n",BMP180_Result.P);
printf("-----------------------\n");
sleep(1);
}
close(fd);
return 0;
}
Compile the application file, run the command aarch64-none-linux-gnu-gcc app.c -o app
, copy the compiled executable file to DEBIX.
Test result
Execute the application test code, and run the command ./app
.
Top comments (0)