使用手机BLE连接VEML6075的可穿戴平台紫外检测功能开发
文档编号 |
TN_TEMPLATE0101_A0 |
关键字 |
BLE,VEML6075,可穿戴,紫外检测 |
摘要 |
本技术笔记对使用手机BLE连接VEML6075的可穿戴平台紫外检测功能开发进行说明 |
Mars4zhu
目 录
1 总述 1
2 紫外线检测及其传感器VEML6075介绍 1
2.1 紫外线及其检测介绍 1
2.2 VEML6075介绍 1
3 VEML6075在FRDM-KW41Z上的移植 2
3.1 Driver例程修改 2
3.1.1 原例程运行结果与代码分析 3
3.1.2 i2c_polling_transfer例程修改适配VEML6075 4
3.2 BLE例程修改 8
4 版本历史(Revision History) 14
插图索引
图 21 紫外UVA与UVB频谱分布 1
图 22 VEML6075的命令协议格式 2
图 23 VEML6075的命令代码与寄存器描述 2
图 31 i2c_polling_transfer例程硬件连线 3
图 32 不连接I2C1(master)和I2C0(slave)接线的i2c_polling_transfer运行结果 3
图 33 连接I2C1(master)和I2C0(slave)接线的i2c_polling_transfer运行结果 4
图 34 i2c_polling_transfer例程修改后读取VEML6075紫外检测结果 8
图 35 FRDM-KW41Z和VEML6075的连线 9
图 36 BLE例程blood_pressure_sensor修改后的截图界面 12
图 37 blood_pressure_sensor例程修改后读取VEML6075并在手机App显示紫外指数截图 13
表格索引
错误!未找到目录项。
为了评估可穿戴设备的紫外检测,使用Vishay出品的VEML6075紫外检测传感器芯片,并利用具备低功耗蓝牙BLE连接功能的开发板FRDM-KW41Z,实现从手机、平板等便携终端的BLE连接到可穿戴平台,显示紫外线指数检测结果的功能。
紫外线主要包括365nm波长(320-400nm,3dB带宽为350--375nm)的UVA区域和330nm波长(280--4430nm,3dB带宽为315--340nm)的UVB区域,
图 21 紫外UVA与UVB频谱分布
UVB波长较短伤害较大,不过仅占太阳能的0.1%,而UVA虽然波长长单个光子伤害低,但是能量丰富,占了太阳能的4.5%。
VEML6075是一个紫外检测传感器芯片,提供标准的I2C接口,并提供多种命令来设置或读取传感器参数。其读写格式和命令列表如下:
图 22 VEML6075的命令协议格式
图 23 VEML6075的命令代码与寄存器描述
FRDM-KW41Z是具备多协议无线功能的开发板,特别支持低功耗蓝牙BLE 4.2。为了实现在FRDM-KW41Z上移植VEML6075传感器,需要参照LPC824_VEML6075例程,对FRDM-KW41Z的例程进行修改。
出于简单起见,先使用i2c-river例程修改,这里选择i2c_polling_transfer例程。
首先运行i2c-polling_transfer例程,注意例程运行过程前,需要分别连接I2C0和I2C1的SCL和SDA引脚。连接线如下图:
图 31 i2c_polling_transfer例程硬件连线
运行结果如下:
图 32 不连接I2C1(master)和I2C0(slave)接线的i2c_polling_transfer运行结果
图 33 连接I2C1(master)和I2C0(slave)接线的i2c_polling_transfer运行结果
根据VEML6075的数据手册,采用I2C1作为I2C-Master来设置和读取VEML6075的参数。
原有的LPC824_VEML6075例程的代码主体结构不需要变化,只需要修改适配最终的I2C通讯相关的代码部分,即SetupXferRecAndExecute函数。并且复制delay和delay_Long函数。
#define VEML6075_I2C_ADDR_7BIT (0x10)
#define I2C_ADDR_7BIT VEML6075_I2C_ADDR_7BIT
static uint8_t txData[16];
static uint8_t rxData[16];
static int txSize, rxSize;
void delay(unsigned int n)
{
while(n!=0){n--;}
}
void delay_Long(unsigned long n)
{
unsigned int i;
for(i=0;i<n;i++)
delay(100);
}
/* Function to setup and execute I2C transfer request */
static void SetupXferRecAndExecute(uint8_t devAddr,
uint8_t *txBuffPtr,
uint16_t txSize,
uint8_t *rxBuffPtr,
uint16_t rxSize)
{
memset(&masterXfer, 0, sizeof(masterXfer));
masterXfer.slaveAddress = VEML6075_I2C_ADDR_7BIT;
masterXfer.direction = kI2C_Write;
masterXfer.subaddress = 0;
masterXfer.subaddressSize = 0;
masterXfer.data = txBuffPtr;
masterXfer.dataSize = txSize;
masterXfer.flags = kI2C_TransferNoStopFlag;
I2C_MasterTransferBlocking(EXAMPLE_I2C_MASTER_BASEADDR, &masterXfer);
memset(&masterXfer, 0, sizeof(masterXfer));
masterXfer.slaveAddress = VEML6075_I2C_ADDR_7BIT;
masterXfer.direction = kI2C_Read;
masterXfer.subaddress = 0;
masterXfer.subaddressSize = 0;
masterXfer.data = rxBuffPtr;
masterXfer.dataSize = rxSize;
masterXfer.flags = kI2C_TransferRepeatedStartFlag;
I2C_MasterTransferBlocking(EXAMPLE_I2C_MASTER_BASEADDR, &masterXfer);
}
需要注意的是在读参数的时候,整个I2C协议帧包括先写命令和再读参数两部分,两部分的I2C中间不需要STOP,即第一部分的写命令的I2C为 kI2C_TransferNoStopFlag,第二个的flags为kI2C_TransferRepeatedStartFlag。否则会出现一直停留在I2C协议帧的第二部分代码里。
Main函数的代码修改为如下:
#define DEBUGOUT PRINTF
#define printf PRINTF
int main(void)
{
BOARD_InitPins();
BOARD_BootClockRUN();
BOARD_InitDebugConsole();
PRINTF("\r\nI2C example -- MasterPolling_VEML6075\r\n");
/*2.Set up i2c master to send data to slave*/
/*
* masterConfig.baudRate_Bps = 100000U;
* masterConfig.enableStopHold = false;
* masterConfig.glitchFilterWidth = 0U;
* masterConfig.enableMaster = true;
*/
I2C_MasterGetDefaultConfig(&masterConfig);
masterConfig.baudRate_Bps = I2C_BAUDRATE;
sourceClock = I2C_MASTER_CLK_FREQ;
I2C_MasterInit(EXAMPLE_I2C_MASTER_BASEADDR, &masterConfig, sourceClock);
// 初始化VEML6075,发送0x00命令,参数为0x40
txSize = 3;
rxSize = 0;
txData[0]=0x00;
txData[1]=0x40;//0x10;
txData[3]=0x00;//0x10;
SetupXferRecAndExecute(I2C_ADDR_7BIT, txData,txSize, rxData, rxSize);
txSize = 1;
rxSize = 2;
txData[0]=0x0C;
SetupXferRecAndExecute(I2C_ADDR_7BIT, txData,txSize, rxData, rxSize);
/* Enter the task loop */
DEBUGOUT("Initialization OK\n");
float a = 2.22, b = 1.33, c = 2.95, d = 1.74;
while(1) {
uint16_t UVA_data, UVB_data, UVcomp1_data, UVcomp2_data, UV_id;
float UVAcalc, UVBcalc;
// 发送0x07命令,读取UVA_data寄存器的数值
txData[0]=0x07;
SetupXferRecAndExecute(I2C_ADDR_7BIT, txData,txSize, rxData, rxSize);
UVA_data = (rxData[1] << 8) + rxData[0];
delay(100);
// 发送0x09命令,读取UVB_data寄存器的数值
txData[0]=0x09;
SetupXferRecAndExecute(I2C_ADDR_7BIT, txData,txSize, rxData, rxSize);
UVB_data = (rxData[1] << 8) + rxData[0];
delay(100);
// 发送0x0A命令,读取UVcomp1_data寄存器的数值
txData[0]=0x0A;
SetupXferRecAndExecute(I2C_ADDR_7BIT, txData,txSize, rxData, rxSize);
UVcomp1_data = (rxData[1] << 8) + rxData[0];
delay(100);
// 发送0x0B命令,读取UVcomp2_data寄存器的数值
txData[0]=0x0B;
SetupXferRecAndExecute(I2C_ADDR_7BIT, txData,txSize, rxData, rxSize);
UVcomp2_data = (rxData[1] << 8) + rxData[0];
delay(100);
// 发送0x0C命令,读取UV_id寄存器的数值
txData[0]=0x0C;
SetupXferRecAndExecute(I2C_ADDR_7BIT, txData,txSize, rxData, rxSize);
UV_id = (rxData[1] << 8) + rxData[0];
delay(100);
printf("UVA_data = %d, UVB_data = %d, UVcomp1_data = %d, UVcomp2_data = %d, UV_id = %x\n",
UVA_data, UVB_data, UVcomp1_data, UVcomp2_data, UV_id);
UVAcalc = (float)UVA_data - a*(float)UVcomp1_data - b*(float)UVcomp2_data;
UVBcalc = (float)UVB_data - c*(float)UVcomp1_data - d*(float)UVcomp2_data;
printf("UVAcalc = %f, UVBcalc = %f\n\n", UVAcalc, UVBcalc);
delay_Long(20000);
}
}
最终运行结果为:
图 34 i2c_polling_transfer例程修改后读取VEML6075紫外检测结果
项目要求实现BLE连接功能,最终在手机上显示紫外检测结果,因此在BLE例程上进行修改。为了方便显示,选择blood_pressure_sensor例程。将原例程中的模拟的心率传感数据替换为读取的紫外显示数据,最终在App中可以直观的看到数据结果(虽然App显示为心率,但是实际明白是紫外指数就行。
首先硬件连线,将VEML6075的VCC、GND以及I2C接线SCL、SDA分别于FRDM-KW41Z连接即可。
图 35 FRDM-KW41Z和VEML6075的连线
基本同样的代码修改,在blood_pressure_sensor.c文件中添加如下代码:
// Mars4zhu Add for VEML6075
#include "fsl_i2c.h"
// #include "pinmux.h"
#define EXAMPLE_I2C_MASTER_BASEADDR I2C1
#define I2C_MASTER_CLK_SRC I2C1_CLK_SRC
#define I2C_MASTER_CLK_FREQ CLOCK_GetFreq(I2C1_CLK_SRC)
#define I2C_BAUDRATE 100000U
#define I2C_DATA_LENGTH 32U
i2c_master_config_t masterConfig;
uint32_t sourceClock;
i2c_master_transfer_t masterXfer;
#define VEML6075_I2C_ADDR_7BIT (0x10)
#define I2C_ADDR_7BIT VEML6075_I2C_ADDR_7BIT
static uint8_t txData[16];
static uint8_t rxData[16];
static int txSize, rxSize;
void delay(unsigned int n)
{
while(n!=0){n--;}
}
void delay_Long(unsigned long n)
{
unsigned int i;
for(i=0;i<n;i++)
delay(100);
}
/* Function to setup and execute I2C transfer request */
static void SetupXferRecAndExecute(uint8_t devAddr,
uint8_t *txBuffPtr,
uint16_t txSize,
uint8_t *rxBuffPtr,
uint16_t rxSize)
{
memset(&masterXfer, 0, sizeof(masterXfer));
masterXfer.slaveAddress = VEML6075_I2C_ADDR_7BIT;
masterXfer.direction = kI2C_Write;
masterXfer.subaddress = 0;
masterXfer.subaddressSize = 0;
masterXfer.data = txBuffPtr;
masterXfer.dataSize = txSize;
masterXfer.flags = kI2C_TransferNoStopFlag;
I2C_MasterTransferBlocking(EXAMPLE_I2C_MASTER_BASEADDR, &masterXfer);
memset(&masterXfer, 0, sizeof(masterXfer));
masterXfer.slaveAddress = VEML6075_I2C_ADDR_7BIT;
masterXfer.direction = kI2C_Read;
masterXfer.subaddress = 0;
masterXfer.subaddressSize = 0;
masterXfer.data = rxBuffPtr;
masterXfer.dataSize = rxSize;
masterXfer.flags = kI2C_TransferRepeatedStartFlag;
I2C_MasterTransferBlocking(EXAMPLE_I2C_MASTER_BASEADDR, &masterXfer);
}
void VEML6075_Init()
{
// 修改开发板引脚设置,I2C1功能引出到PTC2/PTC3
BOARD_InitI2C();
// 初始化VEML6075,发送0x00命令,参数为0x40
I2C_MasterGetDefaultConfig(&masterConfig);
masterConfig.baudRate_Bps = I2C_BAUDRATE;
sourceClock = I2C_MASTER_CLK_FREQ;
I2C_MasterInit(EXAMPLE_I2C_MASTER_BASEADDR, &masterConfig, sourceClock);
txSize = 3;
rxSize = 0;
txData[0]=0x00;
txData[1]=0x40; // 800ms
txData[3]=0x00; ;
SetupXferRecAndExecute(I2C_ADDR_7BIT, txData,txSize, rxData, rxSize);
txSize = 1;
rxSize = 2;
txData[0]=0x0C;
SetupXferRecAndExecute(I2C_ADDR_7BIT, txData,txSize, rxData, rxSize);
}
void VEML6075_GetUV_data(uint16_t *UVA_data, uint16_t *UVB_data)
{
txSize = 1;
rxSize = 2;
// 发送0x07命令,读取UVA_data寄存器的数值
txData[0]=0x07;
SetupXferRecAndExecute(I2C_ADDR_7BIT, txData,txSize, rxData, rxSize);
*UVA_data = (rxData[1] << 8) + rxData[0];
delay(100);
// 发送0x09命令,读取UVB_data寄存器的数值
txData[0]=0x09;
SetupXferRecAndExecute(I2C_ADDR_7BIT, txData,txSize, rxData, rxSize);
*UVB_data = (rxData[1] << 8) + rxData[0];
delay(100);
}
然后在BleApp_Init()函数中添加VEML6075_Init();
void BleApp_Init(void)
{
/* Initialize application support for drivers */
BOARD_InitAdc();
VEML6075_Init();
}
最后将TimerMeasurementCallback()函数修改为读取VEML6075的紫外检测指数。
static void TimerMeasurementCallback(void * pParam)
{
uint16_t UVA_data, UVB_data;
VEML6075_GetUV_data(&UVA_data, &UVB_data);
bpsMeasurement_t bp;
bp.unit = gBps_UnitInkPa_c;
bp.systolicValue = UVA_data;
bp.diastolicValue = UVB_data;
bp.meanArterialPressure = (bp.systolicValue + bp.diastolicValue * 2)/3;
Bps_RecordBloodPressureMeasurement(service_blood_pressure, &bp);
}
最后编译运行,在App中得到如下功能。必须认清App中的心率参数systolic和diastolic两个值现在分别是VEML6075传感器检测到的UVA和UVB紫外指数。
图 36 BLE例程blood_pressure_sensor修改后的截图界面
使用手机的相机LED曝光灯(手电筒功能)照射VEML6075,可以看到紫外线指数急剧增加。因此手机App成功通过BLE连接读取到了紫外检测结果。
图 37 blood_pressure_sensor例程修改后读取VEML6075并在手机App显示紫外指数截图
版本号 |
发布时间 |
内容 |
A0 |
2017-06-30 |
初次编写 |