This thread has been locked.

If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.

CCS/AFE4400: CC2640R2F Interfacing with two AFE4400

Part Number: AFE4400
Other Parts Discussed in Thread: CC2640R2F, ADS1292

Tool/software: Code Composer Studio

Hello,

I have been developing a system that consists of CC2640R2F (5x5) and two AFE4400 ICs.

In the case of the optical sensor,  SFH7050 has been used.

I have interfaced with the SPI with Project zero.

I need to read the Two AFE4400 simultaneously and send the data wirelessly over BLE (Bluetooth Low Energy).

But i am not receiving proper readings of PPG from either of the AFE4400.

The graph below shows the reading in plotted in python.

This is at one instance when i read 1000 samples from the device.

"AFE_upper" and "AFE_Bottom" corresponds to the two AFE4400 being used.

I am using SaBLE-X-R2 which consists of CC2640R2F (5x5) and BLED112 at the receiving end and plotting using Python PyGatt library.

Can anyone tell me what's wrong?

Y am I receiving such readings?

  • Hello Mohammad Ehshan Khan,

        I would suggest you to use AFE4400EVM to get the register settings. Once you have the appropriate register settings, you can use that to program AFE4400 on your custom board. Also can you share me the portion of code used to convert ADC codes to float (Voltage).

    Regards,

    Midhun Raveendran

  • I have used AFE4400EVM to get the register values already. And using that to program my custom board.
    Please have a look at the code below.


    /* * 05/11/2019 *Acquire PPG Signal and Transmit over BLE. *Flow: /* * INCLUDES */ #include <string.h> #include <stdint.h> #include <stddef.h> #include <math.h> //#define xdc_runtime_Log_DISABLE_ALL 1 // Add to disable logs from this file #include <ti/sysbios/knl/Task.h> #include <ti/sysbios/knl/Event.h> #include <ti/sysbios/knl/Queue.h> #include <ti/sysbios/knl/Clock.h> #include <ti/sysbios/knl/Semaphore.h> #include <ti/drivers/PIN.h> #include <ti/drivers/SPI.h> #include <ti/display/Display.h> #include <xdc/std.h> #include <xdc/runtime/Error.h> #include <xdc/runtime/System.h> #include <xdc/runtime/Log.h> #include <xdc/runtime/Diags.h> #include <xdc/cfg/global.h> #include <uartlog/UartLog.h> /* This Header file contains all BLE API and icall structure definition */ #include "icall_ble_api.h" #include <icall.h> #include <osal_snv.h> #include <peripheral.h> #include <devinfoservice.h> #include "util.h" #include "Board.h" #include "project_zero.h" #include "SABLEXR2_DEV_BOARD.h" #include "spi.h" // Bluetooth Developer Studio services #include "led_service.h" #include "sunlightService.h" // SOLUTION /* ----------------------------------------------------------------------------------------- */ /* Defines */ /* ----------------------------------------------------------------------------------------- */ #define data_length 4 /* ----------------------------------------------------------------------------------------- */ /* Variables */ /* ----------------------------------------------------------------------------------------- */ uint32_t array[data_length]; uint32_t j = 0, i = 0, k = 0; uint32_t flag = 1; /* ----------------------------------------------------------------------------------------- */ /* CONSTANTS */ /* ----------------------------------------------------------------------------------------- */ // Advertising interval when device is discoverable (units of 625us, 160=100ms) #define DEFAULT_ADVERTISING_INTERVAL 160 // Limited discoverable mode advertises for 30.72s, and then stops // General discoverable mode advertises indefinitely #define DEFAULT_DISCOVERABLE_MODE GAP_ADTYPE_FLAGS_GENERAL // Default pass-code used for pairing. #define DEFAULT_PASSCODE 000000 #define DEFAULT_SUNLIGHT_TIMEOUT 10 //SOLUTION msec // Task configuration #define PRZ_TASK_PRIORITY 1 #ifndef PRZ_TASK_STACK_SIZE #define PRZ_TASK_STACK_SIZE 1024 #endif // Internal Events for RTOS application #define PRZ_ICALL_EVT ICALL_MSG_EVENT_ID // Event_Id_31 #define PRZ_QUEUE_EVT UTIL_QUEUE_EVENT_ID // Event_Id_30 #define PRZ_STATE_CHANGE_EVT Event_Id_00 #define PRZ_CHAR_CHANGE_EVT Event_Id_01 #define PRZ_PERIODIC_EVT Event_Id_02 #define PRZ_APP_MSG_EVT Event_Id_03 #define PRZ_ALL_EVENTS (PRZ_ICALL_EVT | \ PRZ_QUEUE_EVT | \ PRZ_STATE_CHANGE_EVT | \ PRZ_CHAR_CHANGE_EVT | \ PRZ_PERIODIC_EVT | \ PRZ_APP_MSG_EVT) // Set the register cause to the registration bit-mask #define CONNECTION_EVENT_REGISTER_BIT_SET(registerCause) (connectionEventRegisterCauseBitMap |= registerCause ) // Remove the register cause from the registration bit-mask #define CONNECTION_EVENT_REGISTER_BIT_REMOVE(registerCause) (connectionEventRegisterCauseBitMap &= (~registerCause) ) // Gets whether the current App is registered to the receive connection events #define CONNECTION_EVENT_IS_REGISTERED (connectionEventRegisterCauseBitMap > 0) // Gets whether the registerCause was registered to recieve connection event #define CONNECTION_EVENT_REGISTRATION_CAUSE(registerCause) (connectionEventRegisterCauseBitMap & registerCause ) /********************************************************************* * TYPEDEFS */ // Types of messages that can be sent to the user application task from other // tasks or interrupts. Note: Messages from BLE Stack are sent differently. typedef enum { APP_MSG_SERVICE_WRITE = 0, /* A characteristic value has been written */ APP_MSG_SERVICE_CFG, /* A characteristic configuration has changed */ APP_MSG_UPDATE_CHARVAL, /* Request from ourselves to update a value */ APP_MSG_GAP_STATE_CHANGE, /* The GAP / connection state has changed */ APP_MSG_SEND_PASSCODE, /* A pass-code/PIN is requested during pairing */ APP_MSG_PRZ_CONN_EVT, /* Connection Event finished report */ APP_MSG_PERIODIC_TIMER, /* Timer has expired, set characteristic value */ APP_MSG_AFE, } app_msg_types_t; // Struct for messages sent to the application task typedef struct { Queue_Elem _elem; app_msg_types_t type; uint8_t pdu[]; } app_msg_t; // Struct for messages about characteristic data typedef struct { uint16_t svcUUID; // UUID of the service uint16_t dataLen; // uint8_t paramID; // Index of the characteristic uint8_t data[]; // Flexible array member, extended to malloc - sizeof(.) } char_data_t; // Struct for message about sending/requesting passcode from peer. typedef struct { uint16_t connHandle; uint8_t uiInputs; uint8_t uiOutputs; uint32 numComparison; } passcode_req_t; // Struct for message about button state typedef struct { PIN_Id pinId; uint8_t state; } button_state_t; /********************************************************************* * LOCAL VARIABLES */ // Entity ID globally used to check for source and/or destination of messages static ICall_EntityID selfEntity; // Event globally used to post local events and pend on system and // local events. static ICall_SyncHandle syncEvent; // Queue object used for application messages. static Queue_Struct applicationMsgQ; static Queue_Handle hApplicationMsgQ; // Task configuration Task_Struct przTask; Char przTaskStack[PRZ_TASK_STACK_SIZE]; // GAP - SCAN RSP data (max size = 31 bytes) static uint8_t scanRspData[] = { // No scan response data provided. 0x00 // Placeholder to keep the compiler happy. }; // GAP - Advertisement data (max size = 31 bytes, though this is // best kept short to conserve power while advertisting) static uint8_t advertData[] = { // Flags; this sets the device to use limited discoverable // mode (advertises for 30 seconds at a time) or general // discoverable mode (advertises indefinitely), depending // on the DEFAULT_DISCOVERY_MODE define. 0x02, // length of this data GAP_ADTYPE_FLAGS, DEFAULT_DISCOVERABLE_MODE | GAP_ADTYPE_FLAGS_BREDR_NOT_SUPPORTED, // complete name 12, GAP_ADTYPE_LOCAL_NAME_COMPLETE, 'A', 'F', 'E', '4', '4', '0', '0', ' ', 'B', 'L', 'E', }; // GAP GATT Attributes static uint8_t attDeviceName[GAP_DEVICE_NAME_LEN] = "AFE4490 BLE"; // Globals used for ATT Response retransmission static gattMsgEvent_t *pAttRsp = NULL; static uint8_t rspTxRetry = 0; // Global display handle Display_Handle dispHandle; //Clock struct for Periodic Notifications static Clock_Struct myClock; // SOLUTION /********************************************************************* * LOCAL FUNCTIONS */ static void ProjectZero_init( void ); static void ProjectZero_taskFxn(UArg a0, UArg a1); static void user_processApplicationMessage(app_msg_t *pMsg); static uint8_t ProjectZero_processStackMsg(ICall_Hdr *pMsg); static uint8_t ProjectZero_processGATTMsg(gattMsgEvent_t *pMsg); static void ProjectZero_sendAttRsp(void); static uint8_t ProjectZero_processGATTMsg(gattMsgEvent_t *pMsg); static void ProjectZero_freeAttRsp(uint8_t status); static void ProjectZero_connEvtCB(Gap_ConnEventRpt_t *pReport); static void ProjectZero_processConnEvt(Gap_ConnEventRpt_t *pReport); static void user_processGapStateChangeEvt(gaprole_States_t newState); static void user_gapStateChangeCB(gaprole_States_t newState); static void user_gapBondMgr_passcodeCB(uint8_t *deviceAddr, uint16_t connHandle, uint8_t uiInputs, uint8_t uiOutputs, uint32 numComparison); static void user_gapBondMgr_pairStateCB(uint16_t connHandle, uint8_t state, uint8_t status); // Generic callback handlers for value changes in services. static void user_service_ValueChangeCB( uint16_t connHandle, uint16_t svcUuid, uint8_t paramID, uint8_t *pValue, uint16_t len ); //static void user_service_CfgChangeCB( uint16_t connHandle, uint16_t svcUuid, uint8_t paramID, uint8_t *pValue, uint16_t len ); // Task context handlers for generated services. static void user_LedService_ValueChangeHandler(char_data_t *pCharData); // Task handler for sending notifications. static void user_updateCharVal(char_data_t *pCharData); // Utility functions static void user_enqueueRawAppMsg(app_msg_types_t appMsgType, uint8_t *pData, uint16_t len ); static void user_enqueueCharDataMsg(app_msg_types_t appMsgType, uint16_t connHandle, uint16_t serviceUUID, uint8_t paramID, uint8_t *pValue, uint16_t len); static char *Util_convertArrayToHexString(uint8_t const *src, uint8_t src_len, uint8_t *dst, uint8_t dst_len); #if defined(UARTLOG_ENABLE) static char *Util_getLocalNameStr(const uint8_t *data); #endif // SOLUTION BEGIN static void sunCharChanged(uint16_t connHandle, uint8_t paramID, uint16_t len, uint8_t *pValue); static void myClockSwiFxn(void); // SOLUTION END void afeCallbackFxn(PIN_Handle handle, PIN_Id pinId); void enable_pin_interrupt(void); void disable_pin_interrupt(void); /********************************************************************* * EXTERN FUNCTIONS */ extern void AssertHandler(uint8 assertCause, uint8 assertSubcause); /********************************************************************* * PROFILE CALLBACKS */ // GAP Role Callbacks static gapRolesCBs_t user_gapRoleCBs = { user_gapStateChangeCB // Profile State Change Callbacks }; // GAP Bond Manager Callbacks static gapBondCBs_t user_bondMgrCBs = { user_gapBondMgr_passcodeCB, // Passcode callback user_gapBondMgr_pairStateCB // Pairing / Bonding state Callback }; /* * Callbacks in the user application for events originating from BLE services. */ // LED Service callback handler. // The type LED_ServiceCBs_t is defined in led_service.h static LedServiceCBs_t user_LED_ServiceCBs = { .pfnChangeCb = user_service_ValueChangeCB, // Characteristic value change callback handler .pfnCfgChangeCb = NULL, // No notification-/indication enabled chars in LED Service }; // SOLUTION: Sunlight Service callback handler. // The type Data_ServiceCBs_t is defined in sunlightService.h static sunlightServiceCBs_t user_Sunlight_ServiceCBs = { .pfnChangeCb = sunCharChanged, .pfnCfgChangeCb = NULL }; /********************************************************************* * The following typedef and global handle the registration to connection event */ typedef enum { NONE_REGISTERED = 0, FOR_ATT_RSP = 1, } connectionEventRegisterCause_u; // Handle the registration and un-registration for the connection event, since only one can be registered. uint32_t connectionEventRegisterCauseBitMap = NONE_REGISTERED; // See connectionEventRegisterCause_u /* * @brief Register to receive connection event reports for all the connections * * @param connectionEventRegisterCause Represents the reason for registration * * @return @ref SUCCESS */ bStatus_t ProjectZero_RegistertToAllConnectionEvent(connectionEventRegisterCause_u connectionEventRegisterCause) { bStatus_t status = SUCCESS; // In case there is no registration for the connection event, register for report if (!CONNECTION_EVENT_IS_REGISTERED) { status = GAP_RegisterConnEventCb(ProjectZero_connEvtCB, GAP_CB_REGISTER, LINKDB_CONNHANDLE_ALL); } if(status == SUCCESS) { // Add the reason bit to the bitamap. CONNECTION_EVENT_REGISTER_BIT_SET(connectionEventRegisterCause); } return(status); } /* * @brief Unregister connection events * * @param connectionEventRegisterCause represents the reason for registration * * @return @ref SUCCESS * */ bStatus_t ProjectZero_UnRegistertToAllConnectionEvent (connectionEventRegisterCause_u connectionEventRegisterCause) { bStatus_t status = SUCCESS; CONNECTION_EVENT_REGISTER_BIT_REMOVE(connectionEventRegisterCause); // In case there are no more subscribers for the connection event then unregister for report if (!CONNECTION_EVENT_IS_REGISTERED) { GAP_RegisterConnEventCb(ProjectZero_connEvtCB, GAP_CB_UNREGISTER, LINKDB_CONNHANDLE_ALL); } return(status); } /********************************************************************* * PUBLIC FUNCTIONS */ /* * @brief Task creation function for the user task. * * @param None. * * @return None. */ void ProjectZero_createTask(void) { Task_Params taskParams; // Configure task Task_Params_init(&taskParams); taskParams.stack = przTaskStack; taskParams.stackSize = PRZ_TASK_STACK_SIZE; taskParams.priority = PRZ_TASK_PRIORITY; Task_construct(&przTask, ProjectZero_taskFxn, &taskParams, NULL); } /* * @brief Called before the task loop and contains application-specific * initialization of the BLE stack, hardware setup, power-state * notification if used, and BLE profile/service initialization. * * @param None. * * @return None. */ static void ProjectZero_init(void) { // ****************************************************************** // NO STACK API CALLS CAN OCCUR BEFORE THIS CALL TO ICall_registerApp // ****************************************************************** // Register the current thread as an ICall dispatcher application // so that the application can send and receive messages via ICall to Stack. ICall_registerApp(&selfEntity, &syncEvent); Log_info0("Initializing the user task, hardware, BLE stack and services."); // Open display. By default this is disabled via the predefined symbol Display_DISABLE_ALL. dispHandle = Display_open(Display_Type_UART, NULL); // Initialize queue for application messages. // Note: Used to transfer control to application thread from e.g. interrupts. Queue_construct(&applicationMsgQ, NULL); hApplicationMsgQ = Queue_handle(&applicationMsgQ); // clockParams is only used during init and can be on the stack. Clock_Params myClockParams; // Insert default params Clock_Params_init(&myClockParams); // Set a period, so it times out periodically without jitter myClockParams.period = DEFAULT_SUNLIGHT_TIMEOUT * (1000/Clock_tickPeriod), // Initialize the clock object / Clock_Struct previously added globally. Clock_construct(&myClock, myClockSwiFxn, 0, // Initial delay before first timeout &myClockParams); // ****************************************************************** // BLE Stack initialization // ****************************************************************** // Setup the GAP Peripheral Role Profile uint8_t initialAdvertEnable = TRUE; // Advertise on power-up // By setting this to zero, the device will go into the waiting state after // being discoverable. Otherwise wait this long [ms] before advertising again. uint16_t advertOffTime = 0; // miliseconds // Set advertisement enabled. GAPRole_SetParameter(GAPROLE_ADVERT_ENABLED, sizeof(uint8_t), &initialAdvertEnable); // Configure the wait-time before restarting advertisement automatically GAPRole_SetParameter(GAPROLE_ADVERT_OFF_TIME, sizeof(uint16_t), &advertOffTime); // Initialize Scan Response data GAPRole_SetParameter(GAPROLE_SCAN_RSP_DATA, sizeof(scanRspData), scanRspData); // Initialize Advertisement data GAPRole_SetParameter(GAPROLE_ADVERT_DATA, sizeof(advertData), advertData); Log_info1("Name in advertData array: \x1b[33m%s\x1b[0m", (IArg)Util_getLocalNameStr(advertData)); // Set advertising interval uint16_t advInt = DEFAULT_ADVERTISING_INTERVAL; GAP_SetParamValue(TGAP_LIM_DISC_ADV_INT_MIN, advInt); GAP_SetParamValue(TGAP_LIM_DISC_ADV_INT_MAX, advInt); GAP_SetParamValue(TGAP_GEN_DISC_ADV_INT_MIN, advInt); GAP_SetParamValue(TGAP_GEN_DISC_ADV_INT_MAX, advInt); // Set duration of advertisement before stopping in Limited adv mode. GAP_SetParamValue(TGAP_LIM_ADV_TIMEOUT, 30); // Seconds // ****************************************************************** // BLE Bond Manager initialization // ****************************************************************** uint32_t passkey = 0; // passkey "000000" uint8_t pairMode = GAPBOND_PAIRING_MODE_WAIT_FOR_REQ; uint8_t mitm = TRUE; uint8_t ioCap = GAPBOND_IO_CAP_DISPLAY_ONLY; uint8_t bonding = TRUE; GAPBondMgr_SetParameter(GAPBOND_DEFAULT_PASSCODE, sizeof(uint32_t), &passkey); GAPBondMgr_SetParameter(GAPBOND_PAIRING_MODE, sizeof(uint8_t), &pairMode); GAPBondMgr_SetParameter(GAPBOND_MITM_PROTECTION, sizeof(uint8_t), &mitm); GAPBondMgr_SetParameter(GAPBOND_IO_CAPABILITIES, sizeof(uint8_t), &ioCap); GAPBondMgr_SetParameter(GAPBOND_BONDING_ENABLED, sizeof(uint8_t), &bonding); // ****************************************************************** // BLE Service initialization // ****************************************************************** // Add services to GATT server GGS_AddService(GATT_ALL_SERVICES); // GAP GATTServApp_AddService(GATT_ALL_SERVICES); // GATT attributes DevInfo_AddService(); // Device Information Service // Set the device name characteristic in the GAP Profile GGS_SetParameter(GGS_DEVICE_NAME_ATT, GAP_DEVICE_NAME_LEN, attDeviceName); // Add services to GATT server and give ID of this task for Indication acks. LedService_AddService( selfEntity ); SunlightService_AddService( selfEntity ); // SOLUTION // Register callbacks with the generated services that // can generate events (writes received) to the application LedService_RegisterAppCBs( &user_LED_ServiceCBs ); SunlightService_RegisterAppCBs( &user_Sunlight_ServiceCBs); // SOLUTION // Placeholder variable for characteristic intialization uint8_t initVal[40] = {0}; // Initalization of characteristics in LED_Service that can provide data. LedService_SetParameter(LS_LED0_ID, LS_LED0_LEN, initVal); // SOLUTION BEGIN // Initalization of characteristics in sunlightService that are readable. uint8_t sunlightService_sunlightValue_initVal[SUNLIGHTSERVICE_SUNLIGHTVALUE_LEN] = {0}; SunlightService_SetParameter(SUNLIGHTSERVICE_SUNLIGHTVALUE_ID, SUNLIGHTSERVICE_SUNLIGHTVALUE_LEN, sunlightService_sunlightValue_initVal); uint16_t sunlightService_sunlightPeriod_initVal = DEFAULT_SUNLIGHT_TIMEOUT; SunlightService_SetParameter(SUNLIGHTSERVICE_UPDATEPERIOD_ID, SUNLIGHTSERVICE_UPDATEPERIOD_LEN, &sunlightService_sunlightPeriod_initVal); // SOLUTION END // Start the stack in Peripheral mode. VOID GAPRole_StartDevice(&user_gapRoleCBs); // Start Bond Manager VOID GAPBondMgr_Register(&user_bondMgrCBs); // Register with GAP for HCI/Host messages GAP_RegisterForMsgs(selfEntity); // Register for GATT local events and ATT Responses pending for transmission GATT_RegisterForMsgs(selfEntity); } /* * @brief Application task entry point. * * Invoked by TI-RTOS when BIOS_start is called. Calls an init function * and enters an infinite loop waiting for messages. * * Messages can be either directly from the BLE stack or from user code * like Hardware Interrupt (Hwi) or a callback function. * * The reason for sending messages to this task from e.g. Hwi's is that * some RTOS and Stack APIs are not available in callbacks and so the * actions that may need to be taken is dispatched to this Task. * * @param a0, a1 - not used. * * @return None. */ static void ProjectZero_taskFxn(UArg a0, UArg a1) { // Initialize application SPI_init(); ProjectZero_init(); PIN_Config pinTable[] = { AFE_CS | PIN_GPIO_OUTPUT_EN | PIN_GPIO_HIGH | PIN_PUSHPULL | PIN_DRVSTR_MAX, AFE_PDN | PIN_GPIO_OUTPUT_EN | PIN_GPIO_HIGH | PIN_PUSHPULL | PIN_DRVSTR_MAX, AFE_RST | PIN_GPIO_OUTPUT_EN | PIN_GPIO_HIGH | PIN_PUSHPULL | PIN_DRVSTR_MAX, AFE_RDY | PIN_INPUT_EN | PIN_PULLUP | PIN_IRQ_DIS, //PIN_IRQ_POSEDGE, AFE_CS1 | PIN_GPIO_OUTPUT_EN | PIN_GPIO_HIGH | PIN_PUSHPULL | PIN_DRVSTR_MAX, AFE_PDN1 | PIN_GPIO_OUTPUT_EN | PIN_GPIO_HIGH | PIN_PUSHPULL | PIN_DRVSTR_MAX, AFE_RST1 | PIN_GPIO_OUTPUT_EN | PIN_GPIO_HIGH | PIN_PUSHPULL | PIN_DRVSTR_MAX, AFE_RDY1 | PIN_INPUT_EN | PIN_PULLUP | PIN_IRQ_DIS, //PIN_IRQ_POSEDGE, PIN_TERMINATE }; /* Open pin */ pinHandle = PIN_open(&pinState, pinTable); if (pinHandle == NULL) { while(1); /* Error Initializing the Pins Defined in "pintable" */ } /* ----------------------------------------------------------------------------------------- */ spi_init(); spi_init1(); /* ----------------------------------------------------------------------------------------- */ PIN_setOutputValue(pinHandle, AFE_RST, 1); PIN_setOutputValue(pinHandle, AFE_PDN, 1); PIN_setOutputValue(pinHandle, AFE_RST, 0); PIN_setOutputValue(pinHandle, AFE_RST, 1); PIN_setOutputValue(pinHandle, AFE_CS, 1); // Set CSn high PIN_setOutputValue(pinHandle, AFE_RST1, 1); PIN_setOutputValue(pinHandle, AFE_PDN1, 1); PIN_setOutputValue(pinHandle, AFE_RST1, 0); PIN_setOutputValue(pinHandle, AFE_RST1, 1); PIN_setOutputValue(pinHandle, AFE_CS1, 1); // Set CSn high /* Setup callback for DRDY pin */ if (PIN_registerIntCb(pinHandle, &afeCallbackFxn) != 0) { /* Error registering button callback function */ while(1); } AFE4490_Reg_Init(AFE_CS); // upper read_enable(AFE_CS); AFE4490_Reg_Init(AFE_CS1); read_enable(AFE_CS1); // lower // Application main loop for (;;) { uint32_t events; // Waits for an event to be posted associated with the calling thread. // Note that an event associated with a thread is posted when a // message is queued to the message receive queue of the thread events = Event_pend(syncEvent, Event_Id_NONE, PRZ_ALL_EVENTS, ICALL_TIMEOUT_FOREVER); if (events) { ICall_EntityID dest; ICall_ServiceEnum src; ICall_HciExtEvt *pMsg = NULL; // Check if we got a signal because of a stack message if (ICall_fetchServiceMsg(&src, &dest, (void **)&pMsg) == ICALL_ERRNO_SUCCESS) { uint8 safeToDealloc = TRUE; if ((src == ICALL_SERVICE_CLASS_BLE) && (dest == selfEntity)) { ICall_Stack_Event *pEvt = (ICall_Stack_Event *)pMsg; if (pEvt->signature != 0xffff) { // Process inter-task message safeToDealloc = ProjectZero_processStackMsg((ICall_Hdr *)pMsg); } } if (pMsg && safeToDealloc) { ICall_freeMsg(pMsg); } } // Process messages sent from another task or another context. while (!Queue_empty(hApplicationMsgQ)) { app_msg_t *pMsg = Queue_dequeue(hApplicationMsgQ); // Process application-layer message probably sent from ourselves. user_processApplicationMessage(pMsg); // Free the received message. ICall_free(pMsg); } } } } /* * @brief Handle application messages * * These are messages not from the BLE stack, but from the * application itself. * * For example, in a Software Interrupt (Swi) it is not possible to * call any BLE APIs, so instead the Swi function must send a message * to the application Task for processing in Task context. * * @param pMsg Pointer to the message of type app_msg_t. * * @return None. */ static void user_processApplicationMessage(app_msg_t *pMsg) { char_data_t *pCharData = (char_data_t *)pMsg->pdu; switch (pMsg->type) { case APP_MSG_SERVICE_WRITE: /* Message about received value write */ /* Call different handler per service */ switch(pCharData->svcUUID) { case LED_SERVICE_SERV_UUID: user_LedService_ValueChangeHandler(pCharData); break; } break; case APP_MSG_SERVICE_CFG: /* Message about received CCCD write */ /* Call different handler per service */ break; case HCI_BLE_HARDWARE_ERROR_EVENT_CODE: AssertHandler(HAL_ASSERT_CAUSE_HARDWARE_ERROR,0); break; case APP_MSG_UPDATE_CHARVAL: /* Message from ourselves to send */ user_updateCharVal(pCharData); break; case APP_MSG_GAP_STATE_CHANGE: /* Message that GAP state changed */ user_processGapStateChangeEvt( *(gaprole_States_t *)pMsg->pdu ); break; case APP_MSG_SEND_PASSCODE: /* Message about pairing PIN request */ { passcode_req_t *pReq = (passcode_req_t *)pMsg->pdu; Log_info2("BondMgr Requested passcode. We are %s passcode %06d", (IArg)(pReq->uiInputs?"Sending":"Displaying"), DEFAULT_PASSCODE); // Send passcode response. GAPBondMgr_PasscodeRsp(pReq->connHandle, SUCCESS, DEFAULT_PASSCODE); } break; case APP_MSG_PRZ_CONN_EVT: { ProjectZero_processConnEvt((Gap_ConnEventRpt_t *)pMsg->pdu); break; } // SOLUTION BEGIN case APP_MSG_PERIODIC_TIMER: { SunlightService_SetParameter(SUNLIGHTSERVICE_SUNLIGHTVALUE_ID, SUNLIGHTSERVICE_SUNLIGHTVALUE_LEN, &array); break; } case APP_MSG_AFE: { if(flag == 1) { array[j] = AFE4490_reg_read(0x2F, AFE_CS); // Red upper j++; array[j] = AFE4490_reg_read(0x2E, AFE_CS); // IR upper j++; array[j] = AFE4490_reg_read(0x2F, AFE_CS1); // Red lower j++; array[j] = AFE4490_reg_read(0x2E, AFE_CS1); // IR lower j++; flag = 0; } else { if(flag == 0) { flag = 1; j=0; user_enqueueRawAppMsg(APP_MSG_PERIODIC_TIMER, NULL, 0); // Not sending any data here, just a signal } } break; } // SOLUTION END } } /****************************************************************************** ***************************************************************************** * * Handlers of system/application events deferred to the user Task context. * Invoked from the application Task function above. * * Further down you can find the callback handler section containing the * functions that defer their actions via messages to the application task. * **************************************************************************** *****************************************************************************/ /* * @brief Process a pending GAP Role state change event. * * @param newState - new state * * @return None. */ static void user_processGapStateChangeEvt(gaprole_States_t newState) { switch ( newState ) { case GAPROLE_STARTED: { uint8_t ownAddress[B_ADDR_LEN]; uint8_t systemId[DEVINFO_SYSTEM_ID_LEN]; GAPRole_GetParameter(GAPROLE_BD_ADDR, ownAddress); // use 6 bytes of device address for 8 bytes of system ID value systemId[0] = ownAddress[0]; systemId[1] = ownAddress[1]; systemId[2] = ownAddress[2]; // set middle bytes to zero systemId[4] = 0x00; systemId[3] = 0x00; // shift three bytes up systemId[7] = ownAddress[5]; systemId[6] = ownAddress[4]; systemId[5] = ownAddress[3]; DevInfo_SetParameter(DEVINFO_SYSTEM_ID, DEVINFO_SYSTEM_ID_LEN, systemId); // Display device address char *cstr_ownAddress = Util_convertBdAddr2Str(ownAddress); Log_info1("GAP is started. Our address: \x1b[32m%s\x1b[0m", (IArg)cstr_ownAddress); } break; case GAPROLE_ADVERTISING: Log_info0("Advertising"); break; case GAPROLE_CONNECTED: { uint8_t peerAddress[B_ADDR_LEN]; GAPRole_GetParameter(GAPROLE_CONN_BD_ADDR, peerAddress); char *cstr_peerAddress = Util_convertBdAddr2Str(peerAddress); Log_info1("Connected. Peer address: \x1b[32m%s\x1b[0m", (IArg)cstr_peerAddress); } break; case GAPROLE_CONNECTED_ADV: Log_info0("Connected and advertising"); break; case GAPROLE_WAITING: Log_info0("Disconnected / Idle"); Clock_stop(Clock_handle(&myClock)); //SOLUTION break; case GAPROLE_WAITING_AFTER_TIMEOUT: Log_info0("Connection timed out"); Clock_stop(Clock_handle(&myClock)); //SOLUTION break; case GAPROLE_ERROR: Log_info0("Error"); break; default: break; } } /* * @brief Handle a write request sent from a peer device. * * Invoked by the Task based on a message received from a callback. * * When we get here, the request has already been accepted by the * service and is valid from a BLE protocol perspective as well as * having the correct length as defined in the service implementation. * * @param pCharData pointer to malloc'd char write data * * @return None. */ void user_LedService_ValueChangeHandler(char_data_t *pCharData) { static uint8_t pretty_data_holder[16]; // 5 bytes as hex string "AA:BB:CC:DD:EE" Util_convertArrayToHexString(pCharData->data, pCharData->dataLen, pretty_data_holder, sizeof(pretty_data_holder)); switch (pCharData->paramID) { case LS_LED0_ID: Log_info3("Value Change msg: %s %s: %s", (IArg)"LED Service", (IArg)"LED1", (IArg)pretty_data_holder); // Do something useful with pCharData->data here // ------------------------- // Set the output value equal to the received value. 0 is off, not 0 is on if (pCharData->data[0]) { enable_pin_interrupt(); } else { disable_pin_interrupt(); } Log_info2("Turning %s %s", (IArg)"\x1b[32mLED1\x1b[0m", (IArg)(pCharData->data[0]?"on":"off")); break; default: return; } } /* * @brief Process an incoming BLE stack message. * * This could be a GATT message from a peer device like acknowledgement * of an Indication we sent, or it could be a response from the stack * to an HCI message that the user application sent. * * @param pMsg - message to process * * @return TRUE if safe to deallocate incoming message, FALSE otherwise. */ static uint8_t ProjectZero_processStackMsg(ICall_Hdr *pMsg) { uint8_t safeToDealloc = TRUE; switch (pMsg->event) { case GATT_MSG_EVENT: // Process GATT message safeToDealloc = ProjectZero_processGATTMsg((gattMsgEvent_t *)pMsg); break; case HCI_GAP_EVENT_EVENT: { // Process HCI message switch(pMsg->status) { case HCI_COMMAND_COMPLETE_EVENT_CODE: // Process HCI Command Complete Event Log_info0("HCI Command Complete Event received"); break; default: break; } } break; default: // do nothing break; } return (safeToDealloc); } /* * @brief Process GATT messages and events. * * @return TRUE if safe to deallocate incoming message, FALSE otherwise. */ static uint8_t ProjectZero_processGATTMsg(gattMsgEvent_t *pMsg) { // See if GATT server was unable to transmit an ATT response if (pMsg->hdr.status == blePending) { Log_warning1("Outgoing RF FIFO full. Re-schedule transmission of msg with opcode 0x%02x", pMsg->method); // No HCI buffer was available. Let's try to retransmit the response // on the next connection event. if(ProjectZero_RegistertToAllConnectionEvent(FOR_ATT_RSP) == SUCCESS) { // First free any pending response ProjectZero_freeAttRsp(FAILURE); // Hold on to the response message for retransmission pAttRsp = pMsg; // Don't free the response message yet return (FALSE); } } else if (pMsg->method == ATT_FLOW_CTRL_VIOLATED_EVENT) { // ATT request-response or indication-confirmation flow control is // violated. All subsequent ATT requests or indications will be dropped. // The app is informed in case it wants to drop the connection. // Log the opcode of the message that caused the violation. Log_error1("Flow control violated. Opcode of offending ATT msg: 0x%02x", pMsg->msg.flowCtrlEvt.opcode); } else if (pMsg->method == ATT_MTU_UPDATED_EVENT) { // MTU size updated Log_info1("MTU Size change: %d bytes", pMsg->msg.mtuEvt.MTU); } else { // Got an expected GATT message from a peer. Log_info1("Recevied GATT Message. Opcode: 0x%02x", pMsg->method); } // Free message payload. Needed only for ATT Protocol messages GATT_bm_free(&pMsg->msg, pMsg->method); // It's safe to free the incoming message return (TRUE); } /********************************************************************* * @fn ProjectZero_processConnEvt * * @brief Process connection event. * * @param pReport pointer to connection event report */ static void ProjectZero_processConnEvt(Gap_ConnEventRpt_t *pReport) { if( CONNECTION_EVENT_REGISTRATION_CAUSE(FOR_ATT_RSP)) { // The GATT server might have returned a blePending as it was trying // to process an ATT Response. Now that we finished with this // connection event, let's try sending any remaining ATT Responses // on the next connection event. // Try to retransmit pending ATT Response (if any) ProjectZero_sendAttRsp(); } } /* * Application error handling functions *****************************************************************************/ /* * @brief Send a pending ATT response message. * * The message is one that the stack was trying to send based on a * peer request, but the response couldn't be sent because the * user application had filled the TX queue with other data. * * @param none * * @return none */ static void ProjectZero_sendAttRsp(void) { // See if there's a pending ATT Response to be transmitted if (pAttRsp != NULL) { uint8_t status; // Increment retransmission count rspTxRetry++; // Try to retransmit ATT response till either we're successful or // the ATT Client times out (after 30s) and drops the connection. status = GATT_SendRsp(pAttRsp->connHandle, pAttRsp->method, &(pAttRsp->msg)); if ((status != blePending) && (status != MSG_BUFFER_NOT_AVAIL)) { // Disable connection event end notice ProjectZero_UnRegistertToAllConnectionEvent (FOR_ATT_RSP); // We're done with the response message ProjectZero_freeAttRsp(status); } else { // Continue retrying Log_warning2("Retrying message with opcode 0x%02x. Attempt %d", pAttRsp->method, rspTxRetry); } } } /* * @brief Free ATT response message. * * @param status - response transmit status * * @return none */ static void ProjectZero_freeAttRsp(uint8_t status) { // See if there's a pending ATT response message if (pAttRsp != NULL) { // See if the response was sent out successfully if (status == SUCCESS) { Log_info2("Sent message with opcode 0x%02x. Attempt %d", pAttRsp->method, rspTxRetry); } else { Log_error2("Gave up message with opcode 0x%02x. Status: %d", pAttRsp->method, status); // Free response payload GATT_bm_free(&pAttRsp->msg, pAttRsp->method); } // Free response message ICall_freeMsg(pAttRsp); // Reset our globals pAttRsp = NULL; rspTxRetry = 0; } } /****************************************************************************** ***************************************************************************** * * Handlers of direct system callbacks. * * Typically enqueue the information or request as a message for the * application Task for handling. * **************************************************************************** *****************************************************************************/ /********************************************************************* * @fn ProjectZero_connEvtCB * * @brief Connection event callback. * * @param pReport pointer to connection event report */ static void ProjectZero_connEvtCB(Gap_ConnEventRpt_t *pReport) { // Enqueue the event for processing in the app context. user_enqueueRawAppMsg(APP_MSG_PRZ_CONN_EVT, (uint8_t *)pReport, sizeof(pReport)); ICall_free(pReport); } /* * Callbacks from the Stack Task context (GAP or Service changes) *****************************************************************************/ /** * Callback from GAP Role indicating a role state change. */ static void user_gapStateChangeCB(gaprole_States_t newState) { Log_info1("(CB) GAP State change: %d, Sending msg to app.", (IArg)newState); user_enqueueRawAppMsg( APP_MSG_GAP_STATE_CHANGE, (uint8_t *)&newState, sizeof(newState) ); } /* * @brief Passcode callback. * * @param connHandle - connection handle * @param uiInputs - input passcode? * @param uiOutputs - display passcode? * @param numComparison - numeric comparison value * * @return none */ static void user_gapBondMgr_passcodeCB(uint8_t *deviceAddr, uint16_t connHandle, uint8_t uiInputs, uint8_t uiOutputs, uint32 numComparison) { passcode_req_t req = { .connHandle = connHandle, .uiInputs = uiInputs, .uiOutputs = uiOutputs, .numComparison = numComparison }; // Defer handling of the passcode request to the application, in case // user input is required, and because a BLE API must be used from Task. user_enqueueRawAppMsg(APP_MSG_SEND_PASSCODE, (uint8_t *)&req, sizeof(req)); } /* * @brief Pairing state callback. * * @param connHandle - connection handle * @param state - pairing state * @param status - pairing status * * @return none */ static void user_gapBondMgr_pairStateCB(uint16_t connHandle, uint8_t state, uint8_t status) { if (state == GAPBOND_PAIRING_STATE_STARTED) { Log_info0("Pairing started"); } else if (state == GAPBOND_PAIRING_STATE_COMPLETE) { if (status == SUCCESS) { Log_info0("Pairing completed successfully."); } else { Log_error1("Pairing failed. Error: %02x", status); } } else if (state == GAPBOND_PAIRING_STATE_BONDED) { if (status == SUCCESS) { Log_info0("Re-established pairing from stored bond info."); } } } /** * Callback handler for characteristic value changes in services. */ static void user_service_ValueChangeCB( uint16_t connHandle, uint16_t svcUuid, uint8_t paramID, uint8_t *pValue, uint16_t len ) { // See the service header file to compare paramID with characteristic. Log_info2("(CB) Characteristic value change: svc(0x%04x) paramID(%d). " "Sending msg to app.", (IArg)svcUuid, (IArg)paramID); user_enqueueCharDataMsg(APP_MSG_SERVICE_WRITE, connHandle, svcUuid, paramID, pValue, len); } /****************************************************************************** ***************************************************************************** * * Utility functions * **************************************************************************** *****************************************************************************/ /* * @brief Generic message constructor for characteristic data. * * Sends a message to the application for handling in Task context where * the message payload is a char_data_t struct. * * From service callbacks the appMsgType is APP_MSG_SERVICE_WRITE or * APP_MSG_SERVICE_CFG, and functions running in another context than * the Task itself, can set the type to APP_MSG_UPDATE_CHARVAL to * make the user Task loop invoke user_updateCharVal function for them. * * @param appMsgType Enumerated type of message being sent. * @param connHandle GAP Connection handle of the relevant connection * @param serviceUUID 16-bit part of the relevant service UUID * @param paramID Index of the characteristic in the service * @oaram *pValue Pointer to characteristic value * @param len Length of characteristic data */ static void user_enqueueCharDataMsg( app_msg_types_t appMsgType, uint16_t connHandle, uint16_t serviceUUID, uint8_t paramID, uint8_t *pValue, uint16_t len ) { // Called in Stack's Task context, so can't do processing here. // Send message to application message queue about received data. uint16_t readLen = len; // How much data was written to the attribute // Allocate memory for the message. // Note: The pCharData message doesn't have to contain the data itself, as // that's stored in a variable in the service implementation. // // However, to prevent data loss if a new value is received before the // service's container is read out via the GetParameter API is called, // we copy the characteristic's data now. app_msg_t *pMsg = ICall_malloc( sizeof(app_msg_t) + sizeof(char_data_t) + readLen ); if (pMsg != NULL) { pMsg->type = appMsgType; char_data_t *pCharData = (char_data_t *)pMsg->pdu; pCharData->svcUUID = serviceUUID; // Use 16-bit part of UUID. pCharData->paramID = paramID; // Copy data from service now. memcpy(pCharData->data, pValue, readLen); // Update pCharData with how much data we received. pCharData->dataLen = readLen; // Enqueue the message using pointer to queue node element. Queue_enqueue(hApplicationMsgQ, &pMsg->_elem); // Let application know there's a message. Event_post(syncEvent, PRZ_APP_MSG_EVT); } } /* * @brief Generic message constructor for application messages. * * Sends a message to the application for handling in Task context. * * @param appMsgType Enumerated type of message being sent. * @oaram *pValue Pointer to characteristic value * @param len Length of characteristic data */ static void user_enqueueRawAppMsg(app_msg_types_t appMsgType, uint8_t *pData, uint16_t len) { // Allocate memory for the message. app_msg_t *pMsg = ICall_malloc( sizeof(app_msg_t) + len ); if (pMsg != NULL) { pMsg->type = appMsgType; // Copy data into message memcpy(pMsg->pdu, pData, len); // Enqueue the message using pointer to queue node element. Queue_enqueue(hApplicationMsgQ, &pMsg->_elem); // // Let application know there's a message. Event_post(syncEvent, PRZ_APP_MSG_EVT); } } /* * @brief Convenience function for updating characteristic data via char_data_t * structured message. * * @note Must run in Task context in case BLE Stack APIs are invoked. * * @param *pCharData Pointer to struct with value to update. */ static void user_updateCharVal(char_data_t *pCharData) { switch(pCharData->svcUUID) { case LED_SERVICE_SERV_UUID: LedService_SetParameter(pCharData->paramID, pCharData->dataLen, pCharData->data); break; } } /* * @brief Convert {0x01, 0x02} to "01:02" * * @param src - source byte-array * @param src_len - length of array * @param dst - destination string-array * @param dst_len - length of array * * @return array as string */ static char *Util_convertArrayToHexString(uint8_t const *src, uint8_t src_len, uint8_t *dst, uint8_t dst_len) { char hex[] = "0123456789ABCDEF"; uint8_t *pStr = dst; uint8_t avail = dst_len-1; memset(dst, 0, avail); while (src_len && avail > 3) { if (avail < dst_len-1) { *pStr++ = ':'; avail -= 1; }; *pStr++ = hex[*src >> 4]; *pStr++ = hex[*src++ & 0x0F]; avail -= 2; src_len--; } if (src_len && avail) *pStr++ = ':'; // Indicate not all data fit on line. return (char *)dst; } #if defined(UARTLOG_ENABLE) /* * @brief Extract the LOCALNAME from Scan/AdvData * * @param data - Pointer to the advertisement or scan response data * * @return Pointer to null-terminated string with the adv local name. */ static char *Util_getLocalNameStr(const uint8_t *data) { uint8_t nuggetLen = 0; uint8_t nuggetType = 0; uint8_t advIdx = 0; static char localNameStr[32] = { 0 }; memset(localNameStr, 0, sizeof(localNameStr)); for (advIdx = 0; advIdx < 32;) { nuggetLen = data[advIdx++]; nuggetType = data[advIdx]; if ( (nuggetType == GAP_ADTYPE_LOCAL_NAME_COMPLETE || nuggetType == GAP_ADTYPE_LOCAL_NAME_SHORT) && nuggetLen < 31) { memcpy(localNameStr, &data[advIdx + 1], nuggetLen - 1); break; } else { advIdx += nuggetLen; } } return localNameStr; } #endif // SOLUTION BEGIN /* * @brief SWI handler function for periodic clock expiry * * @param arg0 - Passed by TI-RTOS clock module */ static void myClockSwiFxn() { // Can't call blocking TI-RTOS calls or BLE APIs from here. // .. Send a message to the Task that something is afoot. user_enqueueRawAppMsg(APP_MSG_PERIODIC_TIMER, NULL, 0); // Not sending any data here, just a signal } /* * @brief Callback from Sunlight service to app * Invoked on characteristic value changed * * @param connHandle - connection handle on which the transaction occurred * @param paramID - ID of the char that is written to * @param len - Length of data written * @param pValue - Pointer to new data that is written */ static void sunCharChanged(uint16_t connHandle, uint8_t paramID, uint16_t len, uint8_t *pValue) { if((paramID == SUNLIGHTSERVICE_UPDATEPERIOD_ID) && (len == SUNLIGHTSERVICE_UPDATEPERIOD_LEN)) { uint16_t myTimeoutInMs = BUILD_UINT16(pValue[0], pValue[1]); Clock_stop(Clock_handle(&myClock)); Clock_setPeriod(Clock_handle(&myClock), myTimeoutInMs * (1000/Clock_tickPeriod)); Clock_start(Clock_handle(&myClock)); } } // SOLUTION END /* ----------------------------------------------------------------------------------------- */ /* AFE Callback Function */ /* ----------------------------------------------------------------------------------------- */ void afeCallbackFxn(PIN_Handle handle, PIN_Id pinId) { user_enqueueRawAppMsg(APP_MSG_AFE, NULL, 0); } /* ----------------------------------------------------------------------------------------- */ /* Interrupt Function */ /* ----------------------------------------------------------------------------------------- */ void enable_pin_interrupt() { PIN_setInterrupt(pinHandle, AFE_RDY1 | PIN_IRQ_POSEDGE); PIN_setInterrupt(pinHandle, AFE_RDY | PIN_IRQ_POSEDGE); // Clock_start(Clock_handle(&myClock)); } void disable_pin_interrupt() { PIN_setInterrupt(pinHandle, AFE_RDY1 | PIN_IRQ_DIS); PIN_setInterrupt(pinHandle, AFE_RDY | PIN_IRQ_DIS); // Clock_stop(Clock_handle(&myClock)); } /* ----------------------------------------------------------------------------------------- */ /********************************************************************* *********************************************************************/

    Here the above code is Projectzero.c

  • Hello Mohammad Ehshan Khan,

    Can you share the FFT of the captured data?

    Regards,

    Midhun Raveendran

  • I think i figured out the issue.

    I'm using two AFE440 to get PPG signal, respectively. However, I'm encountered the synchronization issue. AFE 4400 does not have the START register like ADS1292 for multiple device connection, i am unable to solve this synchronization issue. Therefore, please let me know if have a solution to this problem.

  • Anyone has any solution for synchronization of two AFE4400?

    Regards,

    Ehshan

  • Hello Mohammad Ehshan Khan,

        You can sync two AFE4400 by using common external clock and by writing the TIM_CNT_RST bit to 1 and 0. Even if you do this there can be an error of 1CLK.

    Regards,

    Midhun Raveendran

  • Hello Midhun,

    I will do as you have adviced.

    But can you elaborate on "writing the TIM_CNT_RST bit to 1 and 0" in detail.

    I have provided the code above. Could you tell me where i am supposed make the changes?

    Regards, 

    Ehshan Khan

  • Hello Mohammad Ehshan Khan,

    Here is a generic way of doing it. On power up and every time you stop the clock, Set TIM_COUNT_RST bit to keep the counter in reset state.

    When you want to start data acquisition, Set TIM_COUNT_RST to 0 and set TIMEREN bit. Also make sure you are writing both AFE4400 simultaneously.

    Regards,

    Midhun Raveendran