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.

Simulating TIDA with CC1310 and BOOSTXL-TLV8544PIR issues

Part Number: TIDA-00489
Other Parts Discussed in Thread: CC1310, BOOSTXL-TLV8544PIR

Tool/software: TI-RTOS

Hey guys,

I use the CC1310 Launchpad and BOOSTXL-TLV8544PIR Sensor with the TIDA-00489 Firmware and it basically works. But I am facing some issues and a I have a few questions, since I want to make a few changes to the code. As a sniffer, I am ussing SmartRF Studio with another CC1310 Launchpad.

Issues:

  1. I think there is a loose connection between the PIR-shield and the launchpad, making the device sometimes detect and send motions. I will make a few tests on the cpnnection when I have access to some testing devices, but has this been noticed more frequently before? (it only works, if windows gives me the sound like if I have connected some USB device)
  2. I use "puts" to write some stuff to the console. This only works once in the code. You got any idea why? (the code is below)
  3. I am facing some really strange behaviour regarding when a motion is detected and transmitted via sub GHz.
    1. Sometimes the movement is indicated immediately by sending AA, than FF and then 55 (55 is send after the comparators are both low again) and then further package/s is/are send 8-10s later indicating a movement which does not happen.
    2. Sometimes while I make movements no packages are send, but again 8-10s later they come, although there was no further movement.

--> Any idea, why there is sometimes such a big delay?

4. When I set PIR_QUIET_TIME to 5s, and I constantly make movements 15s long, I see that FF is send like ~20s after AA, like it should. But I don't understand why there are no further AA-packages send after the first detected movement?!

Software changes:

  1. I want to change the program flow, counting the movements and send them after 1min. Therefore, I want to keep the Hwi for detecting the movements and I want to add the Swi to send the #movements each minute. Can you help me create another clock to realize the Swi?
  2. Do you think a Task is better than a Swi for my purpose?
  3. Regarding issue #4 I hope you can help me, because I guess, that the counter won't work, if send_package(AA) does not work either...
  4. What happens to my "move" variable, which I use for counting, when the system goes to sleep, will it stay 0 or will there be some randoom number if I dont initialize it after every wakeup?

This are a lot of questions, but I really want to understand the setup, so that I will be able to use the TIDA-Board properly when I will get it.

Here is the code:

/*
 * Copyright (c) 2015, Texas Instruments Incorporated
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * *  Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *
 * *  Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * *  Neither the name of Texas Instruments Incorporated nor the names of
 *    its contributors may be used to endorse or promote products derived
 *    from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

/***** Includes *****/
#include <stdio.h>
#include <stdlib.h>
#include <xdc/std.h>
#include <xdc/cfg/global.h>
#include <xdc/runtime/System.h>

#include <ti/sysbios/BIOS.h>
#include <ti/sysbios/knl/Task.h>
#include <ti/sysbios/knl/Clock.h>
#include <ti/sysbios/knl/Semaphore.h>

/* Drivers */
#include <ti/drivers/rf/RF.h>
#include <ti/drivers/PIN.h>
#include <ti/sysbios/family/arm/cc26xx/Power.h>
#include <ti/sysbios/family/arm/cc26xx/PowerCC2650.h>
#include <ti/drivers/I2C.h>
#include <ti/drivers/PIN.h>
#include <ti/drivers/pin/PINCC26XX.h>
#include <driverlib/sys_ctrl.h>
#include <TIDA-00489_Board.h>

/* Board Header files */
#include "smartrf_settings/cc13xx_smartrf_settings.h"


/***** Defines *****/
#define PIR_SETTLE_TIME  	 5000000	//  5 seconds
//#define PIR_SETTLE_TIME  	30000000    // 30 seconds (2 minutes) , sollte man so lassen, Einschwingzeit beim Einschalten
//#define PIR_QUIET_TIME	10000000    // 10 seconds
#define PIR_QUIET_TIME	    5000000    // 20 seconds (1 minutes), das hier müsste wohl ganz runter auf 1s
#define PIR_SHUTDOWN_TIME    1000000    //   1 seconds (5 Seconds), beschreibt wie lange er wartet bevor er nach dem Senden in den Sleep geht

#define TX_TASK_STACK_SIZE 	1024 // Each task stack must be large enough to handle both its normal function calls and two full interrupting Hwi contexts.
#define TX_TASK_PRIORITY   	2    // Defines the priority of the task. Higher priority (3>2) causes preference.

/* TX Configuration */
#define PAYLOAD_LENGTH		3               // Vermutlich Angabe in Bytes und betrifft Packetgröße beim Senden
#define PACKET_INTERVAL 	4000000*0.001f  // Setzt das Intervall auf 1ms, denn f = die Frequenz des Prozessors (=4MHz=250ns)

/* Move limit*/
#define MOVE_LIMIT          10          //Anzahl ab wann der Sensor die Daten schicken soll;

/***** Prototypes *****/ //<-- sagen dem Compiler welche Funktionen ihn im Programm erwarten
static void txTaskFunction(UArg arg0, UArg arg1); // Diese Funktion beinhaltet den kompletten Sende- und Interruptprozess
static void initializeTask(void);               // Die Clock für die Übertragung und die Semaphore werden initialisiert
static void radioSyncTxClockCallback(UArg a0);  // Die Semaphore wird um einen Wert inkrementiert
static void sendPacket(uint8_t data);           // Behandelt explizit den Sendevorgang
void intr_HwiFxn(PIN_Handle hPin, PIN_Id pinId); // Behandelt den Interrupt Pin


/***** Variable declarations *****/ // Variablen werden deklariert
static Task_Params txTaskParams;
static Task_Struct txTask;
static uint8_t txTaskStack[TX_TASK_STACK_SIZE];

static RF_Object rfObject;
static RF_Handle rfHandle;
static RF_Params rfParams;

static Semaphore_Struct radioSyncTxSem;
static Semaphore_Handle radioSyncTxSemHandle;
static Clock_Struct radioSyncTxClock;
static Clock_Handle radioSyncTxClockHandle;

static uint8_t packet[PAYLOAD_LENGTH];
static uint8_t move;            // von mir definiert

/* Wake-up pin table */ // Die genannten Pins werden Input enabled, pulldown wird enabled, und aufwachen bei positiver Flanke
PIN_Config PinTableWakeUp[] = {
	Board_PIR_Out_Lo   | PIN_INPUT_EN | PIN_PULLDOWN | PINCC26XX_WAKEUP_POSEDGE,
	Board_PIR_Out_Hi   | PIN_INPUT_EN | PIN_PULLDOWN | PINCC26XX_WAKEUP_POSEDGE,
    PIN_TERMINATE                                 /* Terminate list */
};

/* Pin interrupt */ // Die genannten Pins werden Input enabled, kein Pull Up oder Down, und aufwachen bei positiver Flanke
PIN_Config intrPinTable[] = {
	Board_PIR_Out_Lo   | PIN_INPUT_EN | PIN_NOPULL | PIN_HYSTERESIS,
	Board_PIR_Out_Hi   | PIN_INPUT_EN | PIN_NOPULL | PIN_HYSTERESIS,
    PIN_TERMINATE
};
PIN_Handle intr_handle; // Pin_State benötigt ein "Handle" und dieses wird hier definiert
PIN_State  intr_state;



/***** Function definitions *****/
void TxTask_init()  // kann man wohl so lassen
{
    Task_Params_init(&txTaskParams);    // "Task_Params_init" Cluster wird befüllt
    txTaskParams.stackSize = TX_TASK_STACK_SIZE; // Stack Size
    txTaskParams.priority = TX_TASK_PRIORITY;   // Task Priorität
    txTaskParams.stack = &txTaskStack;          // Stack Bezeichnung
    txTaskParams.arg0 = (UInt)1000000;          // Check ich nicht, steht aber überall

    Task_construct(&txTask, txTaskFunction, &txTaskParams, NULL); // Ein Objekt wird gebildet und nicht erstellt. Um es zu benutzen muss ein Handle erstellt werden.
}

static void txTaskFunction(UArg arg0, UArg arg1)
{
	/* Init task TI-RTOS objects */
	initializeTask();       // Der Task wird initialisiert

	/* Backdoor access */
	uint8_t modeValue = PIN_getInputValue(Board_Mode); // Der Wert am Pin wird eingelesen, ist er 1 = high, so wird das Modul in den Shutdown Modus gefahren
	if(modeValue == 1)
	{
		Power_shutdown(NULL); // Energiesparmodus oder?

		//Trap code here
		while(1);
	}


	/* Get the reason for reset */
	uint32_t rSrc = SysCtrlResetSourceGet();    // Der Grund für den Reset wird ermittelt
	if(rSrc == RSTSRC_WAKEUP_FROM_SHUTDOWN)     // Falls Interrupt ausgelöst wurde wird diese Schleife ausgeführt
	{
		/*** Pin interrupt ***/

		// Configure GPIO interrupt
		intr_handle = PIN_open(&intr_state, intrPinTable);  // Nicht durch den "Test Debug" sondern durch den Interrupt definiert das Handle die Pins
		PIN_registerIntCb(intr_handle, &intr_HwiFxn);       // Registriert eine Callback Funktion wenn ein HW-Interrupt aufgetreten ist
		PIN_setConfig(intr_handle, PIN_BM_IRQ, Board_PIR_Out_Lo | PIN_IRQ_POSEDGE); // IRQ bei positiver Flanke ausgelöst
		PIN_setConfig(intr_handle, PIN_BM_IRQ, Board_PIR_Out_Hi | PIN_IRQ_POSEDGE); // IRQ bei positiver Flanke ausgelöst

		//Send ON packet
		sendPacket(0xAA);
		move = move + 1;

		//Wait to make sure there are no more additional movements.
		//If any movement detected, counter is restarted in the ISR
		Clock_setTimeout(radioSyncTxClockHandle, PIR_QUIET_TIME / Clock_tickPeriod);    // Warte bis counter abgelaufen und geht wieder auf inaktiv
		Clock_start(radioSyncTxClockHandle);

		Semaphore_pend(radioSyncTxSemHandle, BIOS_WAIT_FOREVER); // Task wird geblockt

		//Send OFF packet
		sendPacket(0xFF);


		//Wait for sensor to settle
		while(1)    // Die Komparatorwerte werden getestet
		{
			uint8_t pirLowValue  = PIN_getInputValue(Board_PIR_Out_Lo); // Checke Pin Wert vom einen Pin
			uint8_t pirHighValue = PIN_getInputValue(Board_PIR_Out_Hi); // Checke Pin Wert vom anderen Pin
			if(pirLowValue == 0 && pirHighValue == 0)                   // Sind beide Pins low, ist der Sensor "gesettelt"
			{
			    sendPacket(0x55);
			    break;
			}
		}
	}
	else        // Falls der Reset anders ausgelöst wurde als durch Interrupt, also durch Einschalten
	{           // Den Start sollte man so lassen!
		/*** Power on reset wakeup ***/

		//Send initial packet to show system is up
		sendPacket(0x00);
		puts("System is up!\n");
		move = 0;               // reset move variable always at beginning

		while(1)
		{

			//Wait for sensor to start up and settle
			Clock_setTimeout(radioSyncTxClockHandle, PIR_SETTLE_TIME / Clock_tickPeriod); // Genauso wie im case zuvor
			Clock_start(radioSyncTxClockHandle);
			Semaphore_pend(radioSyncTxSemHandle, BIOS_WAIT_FOREVER);


			//Check value of comparators. If not low, wait for more time
			uint8_t pirLowValue  = PIN_getInputValue(Board_PIR_Out_Lo);
			uint8_t pirHighValue = PIN_getInputValue(Board_PIR_Out_Hi);
			if(pirLowValue == 0 && pirHighValue == 0)   // Wenn beide low sind, sende das "Testpacket"
			{
				break;
			}
			else    // Wenn nicht beide Low sind, sende Error packet
			{
				//Send error packet
				sendPacket(0xEE);
			}
		}

		//Send packet to show system is up and running
		sendPacket(0x11);
		puts("System is running!");

			}   // Nach Interrupt oder Power up Routine geht es hier weiter!


	/* Wait for sensor to settle after RF transmission */
	Task_sleep(PIR_SHUTDOWN_TIME / Clock_tickPeriod);   // Der aktuelle Task wird geblockt

	/* Go to shutdown to wait for next PIR interrupt */
	PINCC26XX_setWakeup(PinTableWakeUp);    // Es werden die Pins nach dem "PinTableWakeUp" gesetzt und er geht in den Shutdown Modus
	Power_shutdown(NULL);


	/* Should never get here, since shutdown will reset */
	while(1);
}

static void initializeTask(void) // sollte man so lassen können
{
	/* Setup clock used for sync TX */ // Clock Parameter wurden gesetzt und Handles initialisiert
	Clock_Params radioSyncTxClockParams;
	Clock_Params_init(&radioSyncTxClockParams);
	radioSyncTxClockParams.startFlag = 0;
	radioSyncTxClockParams.period = 0; /* One-off clock */
	Clock_construct(&radioSyncTxClock, radioSyncTxClockCallback, 0, &radioSyncTxClockParams);
	radioSyncTxClockHandle = Clock_handle(&radioSyncTxClock);

	/* Create semaphore to do sync TX */ // Semaphor und zugehöriges Handle wird initialisiert
	Semaphore_Params radioSyncTxParams;
	Semaphore_Params_init(&radioSyncTxParams);
	Semaphore_construct(&radioSyncTxSem, 0, &radioSyncTxParams);
	radioSyncTxSemHandle = Semaphore_handle(&radioSyncTxSem);
}

static void radioSyncTxClockCallback(UArg a0) // semaphore_post führt dazu, dass das SemaphoreHandle um 1 erhöht wird
{
	Semaphore_post(radioSyncTxSemHandle);
}

void intr_HwiFxn(PIN_Handle hPin, PIN_Id pinId) // Wenn die pinId dem Hi und Lo Pin entspricht, wird die Clock gestoppt, ein TO gesetzt und wieder gestartet
{
	if(pinId == Board_PIR_Out_Lo || pinId == Board_PIR_Out_Hi) // Sollte nach einer gemessenen Bewegung zutreffen
	{
		Clock_stop(radioSyncTxClockHandle);
		Clock_setTimeout(radioSyncTxClockHandle, PIR_QUIET_TIME / Clock_tickPeriod); // Warte bis Modul wieder inaktiv wird
		Clock_start(radioSyncTxClockHandle);
	}
}

static void sendPacket(uint8_t data)
{
	uint32_t time;

	/* Init Radio parameters */
	RF_Params_init(&rfParams);

	/* Construct payload */
	packet[0] = 04;
	packet[1] = 89;
	packet[2] = data;

	/* Configure radio */
	RF_cmdPropTx.pktLen = PAYLOAD_LENGTH; // Paketlänge in Bytes
	RF_cmdPropTx.pPkt = packet; // Paketarray, dass gesendet werden soll und max. der gesetzten Paketlänge entspricht
	RF_cmdPropTx.startTrigger.triggerType = TRIG_ABSTIME; // Muss man noch nachlesen was das soll
	RF_cmdPropTx.startTrigger.pastTrig = 1; // Muss man noch nachlesen was das soll

	/* Request access to the radio */
	rfHandle = RF_open(&rfObject, &RF_prop, (RF_RadioSetup*)&RF_cmdPropRadioDivSetup, &rfParams);

	/* Set the frequency */
	RF_postCmd(rfHandle, (RF_Op*)&RF_cmdFs, RF_PriorityNormal, NULL);

	/* Send packet */ // Nach jedem abgelaufenem Interval wird ein Paket gesendet
	time = RF_getCurrentTime();
	time += PACKET_INTERVAL;
	RF_cmdPropTx.startTime = time;

	RF_Event result = RF_runCmd(rfHandle, (RF_Op*)&RF_cmdPropTx, RF_PriorityNormal);
	if (result != RF_EventCmdDone)
	{
		// Error
		while(1);
	}

	/* Close radio to save power */
	RF_close(rfHandle);

	/* Power down the radio */
	RF_yield(rfHandle);
}

/*
 *  ======== main ========
 */
int main(void) // Hier geschieht nur die Initialisierung, der eigentliche Code wird in der StartUp und Interrupt Routine abgerufen
{
    /* Call board init functions. */ // Hier werden die Boardfunktionen initialisiert
    Board_initGeneral();

    /* Initialize task */   // Hier wird der Task initialisiert
    TxTask_init();

    /* Start BIOS */ // Hier wird das TI-RTOS gestartet
    BIOS_start();

    return (0);
}

Kind regards

Stefan

  • Hi Stefan,

    On 1) the PIR sensor is very sensitive. We've seen in some cases it can detect the slightest movement. We usually cover it with a box and try to stay as still as possible.

    On 2) not sure on this. It may have to do with the fact that the CC1310 is going into shutdown mode (basically reset) after all motion is stopped. This action likely affects the connection with CCS.

    On 3) see comment on 1). Also we have noticed in the past that the sniffer software will display a packet only after it receives a new one. This could be what you are seeing.

    On 4) this could again be due to the issue with the sniffer software I mentioned before. Do you see *any* packets after the last 0xFF/0x55 is received?

    Regarding the software changes.

    On 1) try to follow the code which sets up the existing clocks.

    On 2) shouldn't matter much given that you don't have any other tasks competing for execution time.

    On 4) you will lose all variables after you go into shutdown. You also incur power penalty & latency penalty when you come out of shutdown given that the code has to be re-loaded from flash each time. It might make sense to stay in active mode (add while loop to existing task and count interrupts) for the one minute. Standby mode may also be an option.
  • Stefan,

    Below are some additional comments to Gus' reply above.

    3. I am facing some really strange behavior...

    For this one, you can try modifying the PACKET_INTERVAL field.  If you look at the sendPacket function, you'll see that RF_cmdPropTx.startTime is set to the current time with PACKET_INTERVAL added.  This is essentially scheduling when the packet will be transmitted "in the future".  Whenever the clock matches the startTime, the radio will transmit the packet. You can try reducing the number to have a smaller offset.  However, do not make this offset too small, or else the system clock might pass the startTime before the command gets executed.

    4. When I set PIR_QUIET_TIME to 5s...

    This is the current implementation and is expected.  On line 161, you will see that the 0xAA packet is transmitted after the CC1310 wakes up.  In lines 155-158, the HWI (intr_HwiFxn) is setup to trigger whenever the PIR lines goes high.  On line 166-167, the clock is started.  On line 169, the task will wait here forever until a Semaphore gets posted.  Whenever you move, atleast one of the PIR lines goes high, so intr_HwiFxn gets triggered.  In that function, the only thing that happens is the clock gets restarted with a new PIR_QUIET_TIME countdown. Whenever the clock expires, radioSyncTxClockCallback gets called, which will post a Semaphore.  By posting a Semaphore, the task can continue executing after line 169.  That's when you see the 0xFF/0x55 get transmitted. 

    Software changes

    I am assuming that you never plan to shutdown and that you want to send the number of movements every minute.  If that is the case, you can re-purpose the PIR_QUIET_TIME clock.  Below are some suggestion on how to modify the code (add while code after sending 0xAA).  You will also need to modify the Hwi to count the movements.  I have not tested this code, so please make the necessary modification. 

    while(1)
    {

       move = 0;    // Reset move counter

       Clock_setTimeout(radioSyncTxClockHandle, PIR_QUIET_TIME / Clock_tickPeriod);    //Assume PIR_QUIET_TIME is set to 1 minute
       Clock_start(radioSyncTxClockHandle);      //Start the clock

       Semaphore_pend(radioSyncTxSemHandle, BIOS_WAIT_FOREVER);     //Wait here until the clock expires

       sendPacket(move);            //Send the number of moves.  This assumes the number will be less than 2 bytes
    }

    void intr_HwiFxn(PIN_Handle hPin, PIN_Id pinId)
    {
         move = move + 1;   //Instead of resetting the clock, add to the move variable
    }
  • Hey Gus,

    thank you for your answers. Regarding your question: There is sometimes a delay after AA is send, but after FF usually immediately 55 is send and sometimes AA-FF-55 packages will arrive delayed 8-10s or not at all anymore. 55 is always the last package.

    Regarding the software changes:
    I will try to implement my own Swi using a clock_construct function. But any issues related to that will be discussed in another thread if I will face them (which I am sure I will...).
    Since I will use the code for the TIDA-00489 module, which is battery powered I actually wanna go into sleep or at least standby, but not that often. I will now try to stay in standby using clams approach first, but later on I wanna optimize the code and go to sleep if there is no movement within 5 min. I still wanna wake Up and send 0 as amount of movements, but go back to sleep immediately. But I will try this in the future...
  • Hey Clam,

    thank you for your further information.

    Let me try to tell you how I think the code is executed. When the interrupt causes a wakeup, the code execution starts in line 123 at the "txTaskFunction". In line 169, the task is blocked due to the pending semaphore. Now, a movement causes an interrupt and now only the callback function is executed, causing to reset the clock timeout in "void intr_HwiFxn(PIN_Handle hPin, PIN_Id pinId)", am I right?

    1. Can you tell me why only the callback function is activated, due to an interrupt when the system is awake?! Is this a standard principle?
    2. I was using "sendPacket(XxXX)" as a probe and tried to put such a probe in the intr_HwiFxn function declaration, but this did not work, is this function also blocked by the semaphore?
    3. And when the timeout expires, the semaphore is posted and thus unblocking the further code execution? During this time, only the "intr_HwiFxn" is executed when an Hwi occurs?!

    In this case your code looks good, I will try this code prior to a more elegant solution including sleep to save power after a long time without movements. I will post feedback here, when the code works.

    Kind regards

    Stefan

     

     

     

  • You're understanding is correct.

    1. This is just an example. You can modify it as needed. We mainly structured the firmware this way to minimize the amount of time the device is awake. Whenever the task is in semaphore pend and there is no activity, the device will automatically enter standby mode. The Hwi allows the device to quickly wake up, do whatever it needs to do, and go back to standby.
    2. You can only send packets in the the task, not HWI
    3. There are two interrupts running: HWI (for the pins) and clock. In lines 157-158, it configures the polarity of the pin interrupt. In line 156, it defines what function (intr_HwiFxn) to call when the pin interrupt occurs. In lines 166, Clock_setTimeout says what function (radioSyncTxClockHandle) to call when the time (PIR_QUIET_TIME) expires. If either the clock expires or a pin toggles, it will call the respective function, even though the task is in semaphore pend.
    If you want to save power and go to deep sleep, you can add a global variable to count how many times you receive 0 movements. For example, if you receive 0 movements 5 times, then that means 5 minutes has occurred with no movement. You can then break out of the while(1) loop, and continue running the rest of the code (send 0xFF, 0x55, then shutdown).
  • I just wanted to give an update.

    The code is working so far. I count the movements for 1minute and then send the value. 5 minutes without movement cause the board to go sleeping.
    Thank you all for your help so far, i guess won't be the last time I have to ask.

    kind regards

    Stefan