Forked from Lonnie

Dependencies:   BLE_API mbed nRF51822 paw8001motion32_5_m0_keil_split

Fork of Pixart by Lonnie Zhang

main.cpp

Committer:
lonnie80236
Date:
2015-12-11
Revision:
0:56543e77233d
Child:
2:7dc466962683

File content as of revision 0:56543e77233d:

 /* mbed Microcontroller Library
 * Copyright (c) 2006-2013 ARM Limited
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "mbed.h"
#include "BLE.h"
#include "HeartRateService.h"
#include "BatteryService.h"
#include "DeviceInformationService.h"

BLEDevice  ble;
DigitalOut led1(LED1);
/* ---------------------------------------------------------------
 * Below for PAH8001 code
*///--------------------------------------------------------------
#include "app_util_platform.h"
#include "nrf_soc.h"
#include "app_util.h"
#include "PAH8001Set.h"
extern "C"
{
    #include "pxialg.h"
}
Serial  pc(USBTX, USBRX);
I2C i2c(I2C_SDA0, I2C_SCL0);
Ticker  ticker;
/* Power optimized, 1.0mA @disconnection, 4.5mA @connection */
#define MIN_CONN_INTERVAL   MSEC_TO_UNITS(379, UNIT_1_25_MS)              /**< Minimum connection interval (379 ms) */
#define MAX_CONN_INTERVAL   MSEC_TO_UNITS(399, UNIT_1_25_MS)              /**< Maximum connection interval (399 ms). */
#define SLAVE_LATENCY       4                                             /**< Slave latency. */
#define CONN_SUP_TIMEOUT    MSEC_TO_UNITS(6000, UNIT_10_MS)               /**< Connection supervisory timeout (6 seconds). */
void rs232talk(void);
/* ---------------------------------------------------------------
 * End of PAH8001 code
*///--------------------------------------------------------------

const static char     DEVICE_NAME[]        = "PixArt_HRmbed";
static const uint16_t uuid16_list[]        = {GattService::UUID_HEART_RATE_SERVICE,
                                              GattService::UUID_BATTERY_SERVICE,
                                              GattService::UUID_DEVICE_INFORMATION_SERVICE};
 /* ---------------------------------------------------------------
 * Below for PAH8001 code
*///--------------------------------------------------------------
static volatile bool  triggerSensorPolling = false;
void disconnectionCallback(const Gap::DisconnectionCallbackParams_t *params)
{
    ble.gap().startAdvertising();
    triggerSensorPolling = false;
    PxiAlg_Close();//close and reset algorithm
}

void onconnectionCallback(const Gap::ConnectionCallbackParams_t *p_conn_param)
//void onconnectionCallback(Gap::Handle_t handle, Gap::addr_type_t peerAddrType, const Gap::address_t peerAddr, const Gap::ConnectionParams_t *p_conn_param)
{
    triggerSensorPolling = true;
    Gap::ConnectionParams_t gap_conn_params;
    gap_conn_params.minConnectionInterval = MIN_CONN_INTERVAL;
    gap_conn_params.maxConnectionInterval = MAX_CONN_INTERVAL;
    gap_conn_params.slaveLatency = SLAVE_LATENCY;
    gap_conn_params.connectionSupervisionTimeout = CONN_SUP_TIMEOUT;
    ble.gap().updateConnectionParams(p_conn_param->handle, &gap_conn_params);
}

void periodicCallback(void)
{uint8_t tmp;
    if(triggerSensorPolling == true){
        HR_Cnt++;
        CRITICAL_REGION_ENTER();
        Pixart_HRD();
        CRITICAL_REGION_EXIT();
        
        led1 = 1;
        if(Pop(&ppg_mems_data)) //Get data from FIFO
        {
            MEMS_Data[0] = ppg_mems_data.MEMS_Data[0];
            MEMS_Data[1] = ppg_mems_data.MEMS_Data[1];
            MEMS_Data[2] = ppg_mems_data.MEMS_Data[2];
            
            tmp = PxiAlg_Process(ppg_mems_data.HRD_Data, MEMS_Data);
            if( tmp != FLAG_DATA_READY)
            {
                pc.printf("AlgoProcssRtn: %d\n\r", tmp);
                pc.printf("PPG[8]: %d\n\r", ppg_mems_data.HRD_Data[8]);
            }
            else{
                PxiAlg_HrGet(&myHR);
                PxiAlg_GetSigGrade(&grade);
            }
        }
        led1 = 0;
        
        
    }
    else
        HR_Cnt = 0;
}

int main(void)
{
    pc.baud (115200);
    pc.printf("\n\rStart initialization\n\r");  
    i2c.frequency(400000);  
    
    led1 = 1;
    PAH8001_init(); //PAH8001 initialization

    ble.init();
    ble.gap().onDisconnection(disconnectionCallback);
    ble.gap().onConnection(onconnectionCallback);
    /* Setup primary service. */
    uint8_t hrmCounter = 100;
    HeartRateService hrService(ble, hrmCounter, HeartRateService::LOCATION_FINGER);

    /* Setup auxiliary services. */
    BatteryService           battery(ble);
    DeviceInformationService deviceInfo(ble, "ARM", "Model1", "SN1", "hw-rev1", "fw-rev1", "soft-rev1");

    /* Setup advertising. */
    ble.accumulateAdvertisingPayload(GapAdvertisingData::BREDR_NOT_SUPPORTED | GapAdvertisingData::LE_GENERAL_DISCOVERABLE);
    ble.accumulateAdvertisingPayload(GapAdvertisingData::COMPLETE_LIST_16BIT_SERVICE_IDS, (uint8_t *)uuid16_list, sizeof(uuid16_list));
    ble.accumulateAdvertisingPayload(GapAdvertisingData::GENERIC_HEART_RATE_SENSOR);
    ble.accumulateAdvertisingPayload(GapAdvertisingData::COMPLETE_LOCAL_NAME, (uint8_t *)DEVICE_NAME, sizeof(DEVICE_NAME));
    ble.setAdvertisingType(GapAdvertisingParams::ADV_CONNECTABLE_UNDIRECTED);
    ble.setAdvertisingInterval(1600); /* 1000ms; in multiples of 0.625ms. */
    ble.startAdvertising();
    
    pc.printf("Before while 1\n\r");
    while (true) {
        rs232talk();
        
        if(triggerSensorPolling == true)
        {
            if( (HR_Cnt>HR_Rpt) && (ppg_mems_data.HRD_Data[11]==0x80) )
            {
                HR_Cnt = 0;
                
                pc.printf("SG: %d   ", (uint8_t)grade);
                pc.printf("HR: %d\n\r", (uint8_t)myHR);
    
                hrmCounter = (uint8_t)myHR;
                hrService.updateHeartRate(hrmCounter);
            }
        }
        else
            ble.waitForEvent();
    }
}



void PAH8001_init()
{   uint16_t q;
    uint8_t bank=0, temp; float grade=0;

    if(readRegister(0x00) == 0x30)
        pc.printf("PAH8001 I2C Link Successful!\n\r");
    else
        pc.printf("PAH8001 I2C Link Fail!\n\r");
        
    alg_version = PxiAlg_Version();
    pc.printf("Algo Ver: %d\n\r", alg_version);
    

#ifdef DEBUG_8001
    writeRegister(0x09, 0x5A); writeRegister(0x54, 0xEE);
    pc.printf("\n\r~~~Start Test Pattern~~~ \n\r");
    pc.printf("Reg0x09: %d\n\r", readRegister(0x09));
    pc.printf("Reg0x54: %d\n\r", readRegister(0x54));
    pc.printf("Reg0x1E: %d\n\r", readRegister(0x1E));
    
    float MEMS_Data[3] = {0 ,0, 0}; //apply test pattern
    float myHR = 0 ;
    /*for(q=0;q<PPG_PATTERN_SIZE;q++)
        PxiAlg_Process((unsigned char*)PPG_Data[q], MEMS_Data);
    PxiAlg_HrGet(&myHR);
    pc.printf("HR: %f\n\r", myHR);*/
    
    PxiAlg_SetMemsScale(1);
    for(q=0;q<PPG_PATTERN_SIZE;q++)
    {
        PxiAlg_Process((unsigned char*)PPG_Data[q], (float*)iMEMS_Data[q]);
        PxiAlg_HrGet(&myHR);    PxiAlg_GetSigGrade(&grade);
        pc.printf("SG: %f   ", grade);
        pc.printf("HR: %f\n\r", myHR);
    }
    pc.printf("~~~End of Test Pattern~~~ \n\r");
    while(1);
#endif
    
#ifndef DEBUG_8001
    PxiAlg_SetMemsScale(1);
    //PxiAlg_EnableFastOutput(true);
    PxiAlg_EnableFastOutput(false);
    PxiAlg_EnableAutoMode(true);
    PxiAlg_EnableMotionMode(false);
    ticker.attach_us(&periodicCallback, PAH8001_Poll*1000);
    
    pc.printf("\n\r~~~Start Real-time HRM~~~ \n\r");
    //Initialization settings
    writeRegister(0x06, 0x82);  //Reset sensor
    wait_ms(10);    //make a delay
    
    for(q=0;q<INIT_PPG_REG_ARRAY_SIZE;q++){
        if(init_ppg_register_array[q][0] == 0x7F)
            bank = init_ppg_register_array[q][1];

        if((bank == 0) && (init_ppg_register_array[q][0] == 0x17) )
        {
            //read and write bit7=1
            temp = readRegister(0x17);
            temp |= 0x80 ;
            writeRegister(0x17, temp) ;
        }
        else
            writeRegister(init_ppg_register_array[q][0], init_ppg_register_array[q][1]);
    }
#endif
}

void writeRegister(uint8_t addr, uint8_t data)
{
    char data_write[2];
    
    data_write[0] = addr;
    data_write[1] = data;
    i2c.write(I2C_ADDR, data_write, 2, 0);
}
uint8_t readRegister(uint8_t addr)
{
    char data_write[2];
    char data_read[2];
    
    data_write[0] = addr;
    i2c.write(I2C_ADDR, data_write, 1, 0);
    i2c.read(I2C_ADDR, data_read, 1, 0);
    return data_read[0];
}

bool Pixart_HRD(void)
{
    uint8_t tmp=0;
    char data_write[2];
    char data_read[4];
    ppg_mems_data_t ppg_mems_data;
    //Check Touch Status for power saving
    writeRegister(0x7F,0x00); //bank0
    tmp = readRegister(0x59)&0x80;
    led_ctrl(tmp);

    //writeRegister(0x7F,0x01); //bank1
    ppg_mems_data.HRD_Data[0]=readRegister(0x68)&0x0f; //check status: 0 is not ready, 1 is ready, 2 is loss one data?

    if(ppg_mems_data.HRD_Data[0] ==0)
    {
        writeRegister(0x7F,0x00); //bank0
        return false;
    }
    else
    {
        //Only support burst read (0x64~0x67), when using I2C interface
        data_write[0] = 0x64;
        i2c.write(PAH8001_ADDR, data_write, 1, 1);
        i2c.read(PAH8001_ADDR, data_read, 4, 0);
        ppg_mems_data.HRD_Data[1]=data_read[0]&0xff;
        ppg_mems_data.HRD_Data[2]=data_read[1]&0xff;
        ppg_mems_data.HRD_Data[3]=data_read[2]&0xff;
        ppg_mems_data.HRD_Data[4]=data_read[3]&0xff;

        //Only support burst read (0x1A~0x1C), when using I2C interface
        data_write[0] = 0x1A;
        i2c.write(PAH8001_ADDR, data_write, 1, 1);
        i2c.read(PAH8001_ADDR, data_read, 3, 0);
        ppg_mems_data.HRD_Data[5]=data_read[0]&0xff;
        ppg_mems_data.HRD_Data[6]=data_read[1]&0xff;
        ppg_mems_data.HRD_Data[7]=data_read[2]&0xff;

        ppg_mems_data.HRD_Data[8]=Frame_Count++;
        ppg_mems_data.HRD_Data[9]=0;
        ppg_mems_data.HRD_Data[10]=_led_current_change_flag;
        writeRegister(0x7F,0x00); //bank0
        //bit7 is Touch Flag (bit7=1 is meant Touch, and bit7=0 is meant No Touch)
        ppg_mems_data.HRD_Data[11]=(readRegister(0x59)&0x80); //Check Touch Flag
        ppg_mems_data.HRD_Data[12]= ppg_mems_data.HRD_Data[6];
        
        //If no G sensor, please set G_Sensor_Data[3] = {0};
        ppg_mems_data.MEMS_Data[0] = 0;//ReadGSensorX();
        ppg_mems_data.MEMS_Data[1] = 0;//ReadGSensorY();
        ppg_mems_data.MEMS_Data[2] = 0;//ReadGSensorZ();
        Push(&ppg_mems_data); //Save data into FIFO
        
        return true;
    }
}

bool isFIFOEmpty(void)
{
    return (_write_index == _read_index);
}

bool Push(ppg_mems_data_t *data)
{
    int tmp = _write_index;
    tmp++;
    if(tmp >= FIFO_SIZE)
        tmp = 0;
    if(tmp == _read_index)
        return false;
    _ppg_mems_data[tmp] = *data;
    _write_index = tmp;
    
    return true;
}

bool Pop(ppg_mems_data_t *data)
{
    int tmp;
    if(isFIFOEmpty())
        return false;
    *data = _ppg_mems_data[_read_index];
    tmp = _read_index + 1;
    if(tmp >= FIFO_SIZE)
        tmp = 0;
    _read_index = tmp;
    
    return true;
}

/***********************LED Control Start***********************************/
void led_ctrl(uint8_t touch)    {
    if(touch == 0x80)   {
        uint8_t data;
        //uint16_t Frame_Average, EP_L, EP_H, Exposure_Line;
        uint16_t EP_L, EP_H, Exposure_Line;
        writeRegister(0x7f,0x00);
        writeRegister(0x05,0x98);
        writeRegister(0x7f,0x01);
        //writeRegister(0x42,0xA4);
        writeRegister(0x7f,0x00);
        data = readRegister(0x33);
        EP_H=data&0x03;
        data = readRegister(0x32);
        EP_L=data;
        Exposure_Line=(EP_H<<8)+EP_L;
        writeRegister(0x7f,0x01);
        if(_sleepflag==1)   {
            writeRegister(0x38, (0xE0|DEFAULT_LED_STEP));
            _sleepflag = 0 ;
        }

        if (_state_count <= STATE_COUNT_TH) {
            _state_count++;
            _led_current_change_flag = 0;
        }
        else {
            _state_count = 0;
            if(_state == 0) {
                if( (Exposure_Line>=LED_CTRL_EXPO_TIME_HI_BOUND) || (Exposure_Line<=LED_CTRL_EXPO_TIME_LOW_BOUND) ) {
                    //writeRegister(0x7f,0x01);
                    data = readRegister(0x38);
                    _led_step=data&0x1f;
                    if( (Exposure_Line>=LED_CTRL_EXPO_TIME_HI_BOUND) && (_led_step < LED_CURRENT_HI) )  {
                        _state = 1 ;
                        _led_step=_led_step+LED_INC_DEC_STEP;

                        if(_led_step>LED_CURRENT_HI)
                            _led_step=LED_CURRENT_HI;
                        writeRegister(0x38, (_led_step|0xE0));
                        _led_current_change_flag = 1;
                    }
                    else if((Exposure_Line<=LED_CTRL_EXPO_TIME_LOW_BOUND) && (_led_step > LED_CURRENT_LOW) )    {
                        _state = 2 ;
                        if(_led_step<=(LED_CURRENT_LOW+LED_INC_DEC_STEP))
                            _led_step=LED_CURRENT_LOW;
                        else
                            _led_step=_led_step-LED_INC_DEC_STEP;
                        writeRegister(0x38, (_led_step|0xE0));
                        _led_current_change_flag = 1;
                    }
                    else    {
                        _state = 0 ;
                        _led_current_change_flag = 0;
                    }
                }
                else {
                    _led_current_change_flag = 0;
                }
            }
            else if(_state == 1)    {
                if(Exposure_Line > LED_CTRL_EXPO_TIME_HI)   {
                    _state = 1 ;
                    _led_step=_led_step+LED_INC_DEC_STEP;
                    if(_led_step>=LED_CURRENT_HI)   {
                        _state = 0 ;
                        _led_step=LED_CURRENT_HI;
                    }
                    writeRegister(0x38, (_led_step|0xE0));
                    _led_current_change_flag = 1;
                }
                else    {
                    _state = 0 ;
                    _led_current_change_flag = 0;
                }
            }
            else    {
                if(Exposure_Line < LED_CTRL_EXPO_TIME_LOW)  {
                    _state = 2 ;
                    if(_led_step<=(LED_CURRENT_LOW+LED_INC_DEC_STEP))   {
                        _state = 0 ;
                        _led_step=LED_CURRENT_LOW;
                    }
                    else
                        _led_step=_led_step-LED_INC_DEC_STEP;
                    writeRegister(0x38, (_led_step|0xE0));
                    _led_current_change_flag = 1;
                }
                else    {
                    _state = 0;
                    _led_current_change_flag = 0;
                }
            }
        }
    }
    else    {
        writeRegister(0x7f,0x00);
        writeRegister(0x05,0xB8);
        writeRegister(0x7F,0x01);
        //writeRegister(0x42,0xA0);
        _led_step = DEFAULT_LED_STEP;
        writeRegister(0x38, 0xFF);
        _sleepflag = 1;
        _led_current_change_flag = 0;
    }
}
/***********************LED Control End ***********************************/


void rs232talk(void)
{uint8_t tmp, reg_tmp[5], reg[2];
    if(pc.readable()) {
    //pc.putc(pc.getc());
        tmp = pc.getc();
        if(tmp == 's')//for start polling
        {
            pc.printf("\n\rForce Start Polling\n\r");
            triggerSensorPolling = true;
        }
        if(tmp == 'p')//for stop polling
        {
            pc.printf("\n\rForce Stop Polling\n\r");
            triggerSensorPolling = false;
            PxiAlg_Close();//close and reset algorithm
        }
        
        if(tmp == 'w')//write register
        {
            //writeRegister(0x06, 0x08);
        }
        if(tmp == 'r')//read register
        {
            //writeRegister(0x06, 0x00);
        }
    }
}
/* ---------------------------------------------------------------
 * End of PAH8001 code
*///--------------------------------------------------------------