传感器 Android
经过一段时间试用,决定将气压传感器应用到我们Android产品上,成为电子 产品的一个实用功能。
程序实现方面,Android系统内核是Linux Kernel,常规做法是写一个新I2C设备驱动用来驱动气压传感器,重新编译内核后内核就可以支持气压传感器。
不过,这次我决定尝试一种新的做法:从用户空间访问I2C总线,直接和I2C器件进行通讯。这样做的好处显而易见,不需要改kernel代码且无需编译和更新固件,应用程序里就能直接操作I2C设备:气压传感器,编程难度大为降低,代码灵活性大大提高。话不多说,直接上代码:
1. 在用户空间里进行I2C通讯
stati c char i2c_bus[] = "/dev/i2c-3"; //我的PCB A板子上接的是i2c-3这个总线
static jint i2c_write(uint8_t reg, uint8_t *data, int32_t size){
int fd;
int ret;
unsigned char buf[64];
fd = open(i2c_bus, O_RDWR);
LOGI("[i2c_write] Open i2c BUS: %sn", i2c_bus);
if (fd < 0) {
LOGI("open i2c dev error!n");
return -1;
}
ret = ioctl(fd, I2C_SLAVE_FORCE, BM1390GLV_DEVICE_ADDRESS);
if (ret < 0) {
LOGI("set dev error!n");
close(fd);
return ret;
}
memset(buf, 0, 64);
write(fd, buf, 10);
buf[0] = reg;
memcpy(&buf[1], data, size);
ret = write(fd, buf, size + 1);
if (ret < 0) {
LOGI("I2C write error!n");
close(fd);
return -1;
}
close(fd);
usleep(1000);
return 0;
}
static jint i2c_read(uint8_t reg, uint8_t *data, int32_t size)
{
int fd;
int ret;
unsigned char buf[64] = { 0 };
fd = open(i2c_bus, O_RDWR);
LOGI("[i2c_read] Open i2c BUS: %sn", i2c_bus);
if (fd < 0) {
LOGI("open i2c dev error!n");
return BM1390GLV_COMM_ERROR;
}
ret = ioctl(fd, I2C_SLAVE_FORCE, BM1390GLV_DEVICE_ADDRESS);
if (ret < 0) {
LOGI("set dev error!n");
close(fd);
return ret;
}
i2c_write(reg, data, size);
ret = read(fd, buf, size);
if (ret < 0) {
LOGI(" I2C read error!n");
close(fd);
return BM1390GLV_COMM_ERROR;
}
memcpy(data, buf, size);
close(fd);
usleep(1000);
return BM1390GLV_COMM_OK;
}
2. 初始化传感器
JNIEXPORT jint JNICALL Init(JNIEnv *env, jobject thiz) {
env->GetJavaVM(&gJavaVM);
gJavaObj = env->NewGlobalRef(thiz);
int32_t result;
result = init_check();
LOGI("Init %d",result);
if (result == BM1390GLV_COMM_OK) {
(void)init_setting();
}
return (result);
}
static jint init_check(void)
{
int32_t result;
uint8_t id[BM1390GLV_ALL_ID_SIZE];
result = i2c_read(BM1390GLV_MANUFACTURER_ID_PART_ID, id, BM1390GLV_ALL_ID_SIZE);
LOGI("init_check %d",result);
if (result == BM1390GLV_COMM_OK) {
LOGI("id1 %d", id[BM1390GLV_MANUFACTURER_ID]);
LOGI("id2 %d", id[BM1390GLV_PART_ID]);
if ((id[BM1390GLV_MANUFACTURER_ID] != BM1390GLV_MANUFACTURER_ID_VAL) || (id[BM1390GLV_PART_ID] != BM1390GLV_PART_ID_VAL)) {
result = BM1390GLV_WAI_ERROR;
}
}
return (result);
}
static jint init_setting(void)
{
int32_t result;
uint8_t val;
val = BM1390GLV_POWER_DOWN_VAL;
result = i2c_write(BM1390GLV_POWER_DOWN, &val, sizeof(val));
if (result == BM1390GLV_COMM_OK) {
val = BM1390GLV_RESET_VAL;
(void)i2c_write(BM1390GLV_RESET, &val, sizeof(val));
val = BM1390GLV_MODE_CONTROL_VAL;
(void)i2c_write(BM1390GLV_MODE_CONTROL, &val, sizeof(val));
ModeCtlVal = val;
}
return (result);
}
3. 读取传感器采集到的气压、温度
static float convert_hpa(uint32_t raw_press)
{
float press;
press = (float)raw_press / BM1390GLV_COUNTS_PER_HPA;
return (press);
}
static jfloat convert_degree_celsius(int16_t raw_temp)
{
float temp;
temp = (float)raw_temp / BM1390GLV_COUNTS_PER_DEGREE_CELSIUS;
return (temp);
}
static jint get_value(uint8_t *raw)
{
int32_t result;
result = i2c_read(BM1390GLV_PRESSURE_TEMPERATURE, raw, BM1390GLV_ALL_OUT_SIZE);
return (result);
}
JNIEXPORT jint JNICALL get_val(JNIEnv *env, jobject thi)
{
int32_t result;
uint8_t raw_data[BM1390GLV_ALL_OUT_SIZE];
uint32_t raw_press;
uint16_t raw_temp;
result = get_value(raw_data);
if (result == BM1390GLV_COMM_OK) {
raw_press = (uint32_t)raw_data[BM1390GLV_PRESS_OUT_MSB] << 16;
raw_press |= (uint32_t)raw_data[BM1390GLV_PRESS_OUT_LSB] << 8;
raw_press |= (uint32_t)raw_data[BM1390GLV_PRESS_OUT_XL];
raw_temp = (uint16_t)raw_data[BM1390GLV_TEMP_OUT_MSB] << 8;
raw_temp |= (uint16_t)raw_data[BM1390GLV_TEMP_OUT_LSB];
jfloat press = convert_hpa(raw_press);
jfloat temp = convert_degree_celsius(raw_temp);
LOGI("Pressure(hPa): %f", press);
LOGI("Temperature(degree Celsius): %f", temp);
};
return (result);
}
最后,来看一下效果。先来一张示波器量到的波形,证明用户空间直接通过I2C总线访问I2C器件是成功的:
接着看一下通过传感器读取到的气压值和温度值。没有在APK界面上显示,将就一下从PCBA板子的串口看打印信息吧:
更多回帖