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.

TIDA-00524: Implementation of the TIDA-00524 with additional humidity sensor

Part Number: TIDA-00524

Hello,

I'm trying to implement TIDA-00524 datalogger with additional humidity sensor. My humidity sensor is just a resistor whose resistance is proportional to the ambient humidity. If you measure  the resistance you basically measure the humidity. To do that I used a simple series RC circuit. I'm measuring the time constant of this circuit in exactly the same way that was described by J.Davies in his "MSP430 Microcontroller Basics" book. The code that I have written is just a modification of the code from the book but for MSP430 FR5969 microcontroller. I have tested it separately multiple times and its working. You can find the source code enclosed. 

When I integrated the code with the original Datalogger Cold Chain Code it still measures the resistance value but only till the moment when I try to read the data with my smartphone. After some troubleshooting I figured out that the execution stops at this line in the RF430_I2C.c file:

__bis_SR_register(LPM_MODE + GIE); 

You can find the integrated code enclosed as well.

Does anyone has an idea why the integrated code is not working any more? I would appreciate any advice because I have several assumptions why it doesn't work but I'm not sure in any of them. So my idea is that either it is the problem with the I2C communication or the problem with the Clock system setup. Originally the clock settings are initialized in the Cold Chain code, but then I use another settings for the clock in my code.

Thank you very much,

Vlad

RC_time_constant_book_pins 0 and 1.c
// RC CHARGE/DISCHARGE TIME CONSTANT MEASUREMENT

// R = 10k resistor driven from P1.1/OUT2 to capacitor
// C = 10n capacitor input to P1.0/C2, routed to comparator

//**************************************************************************************************************************************************************************************************
// INCLUDED HEADER FILES (LIBRARIES)
//**************************************************************************************************************************************************************************************************
#include <msp430fr5969.h>  // header files for our MCU
#include <driverlib.h> // driverlib functions
#include <math.h> // natural logarithm

// Standard input/output (for initialization of printf)
#include <stdio.h>  // File location: C:\ti\ccsv7\tools\compiler\ti-cgt-msp430_16.9.4.LTS\include


//Intrinsic functions
/*
 * Inline assembler can be used for inserting assembler instructions directly into a C or C++ function.
 * Typically, this can be useful if you need to access hardware resources that are not accessible in C or
 * you manually have to write a time-critical sequence of code that if written in C will not have the right timing.
 *
 * A better approach to control the lower levels of the hardware is to use intrinsic functions instead.
 *
 * An intrinsic function looks like a normal function call, but it is really a built-in function that the compiler recognizes.
 * The intrinsic functions compile into inline code, either as a single instruction, or as a short sequence of instructions,
 * and are marked with a double underscore (__).
 */
#include <intrinsics.h> // File location: C:\ti\ccsv7\tools\compiler\ti-cgt-msp430_16.9.4.LTS\include

// Standard integer types
/*
 * Example from the library:
 * typedef unsigned int   uint16_t;
 */
#include <stdint.h> // File location: C:\ti\ccsv7\tools\compiler\ti-cgt-msp430_16.9.4.LTS\include

#define CE_enable  0x0400 // Comparator enable bit (instead of CEON)
#define CE_disable  0x0000 // Comparator disable bit


int main(void)
{
	uint16_t N_charge, N_discharge; // measured charge and discharge times of the RC circuit (both times are proportional to the time constant of the circuit)
	uint16_t StatCharge[10]; // array containing statistical measurements of the timer counts for charge and discharge periods
	uint16_t StatChargeSize = sizeof(StatCharge) / sizeof(StatCharge[0]);

	float Average_N_charge = 0; // Average number of timer counts during the charging period
	float Average_N_discharge = 0; // Average number of timer counts during the discharging period

	uint16_t i,j,k;

	float Cap = 100; // [nF]; capacitance that is being charged and discharged
	//float Cap = 10; // [nF]; capacitance that is being charged and discharged
	//float Cap = 1; // [nF]; capacitance that is being charged and discharged
	float R_charge, R_discharge, R; // [kOhm]; Measured resistance

	float R_actual = 30; // [kOhm]; Actual resistance
	//float R_actual = 100; // [kOhm]; Actual resistance
	//float R_actual = 1000; // [kOhm]; Actual resistance
	//float R_actual = 10000; // [kOhm]; Actual resistance
	float RTol; // [%]; Measurement tolerance in comparison to actual resistor value

	//uint32_t f_timer = 4e6; // [Hz]; timer frequency
	uint32_t f_timer = 8e6; // [Hz]; timer frequency
	//uint32_t f_timer = 16e6; // [Hz]; timer frequency


    WDTCTL = WDTPW | WDTHOLD;	// stop watchdog timer

	// pin unlocking, only required for FRAM devices
	PM5CTL0 &= ~LOCKLPM5;       // Disable the GPIO power-on default high-impedance mode to activate previously configured port settings
	printf("Pins unlocked\n");

	//**************************************************************************************************************************************************************************************************
	//SETTING THE CLOCK
	//**************************************************************************************************************************************************************************************************

	// Calibrated 8MHz DCO, no xtl, no ACLK, power from JTAG

	// DCOCLK (DCO clock): Internal digitally controlled oscillator (DCO) with selectable frequencies
	// SMCLK: Subsystem master clock. SMCLK is software selectable as LFXTCLK, VLOCLK, LFMODCLK, DCOCLK, MODCLK, or HFXTCLK.
	// The CS (clock system) module can be configured or reconfigured by software at any time during program execution. The CS control registers are password protected to prevent inadvertent access.
	// The DCO has three frequency settings determined	by the DCOFSEL bits. Each frequency is trimmed at the factory. The DCO can be used as a source for MCLK or SMCLK.

	// The DCO frequency can be changed at any time, but care should be taken to ensure no other system	clock frequency constraints are exceeded with the new frequency selection. Any change in the DCOFSEL
	// or DCORSEL bits causes the DCOCLK to be held for four clock cycles before releasing the new value into
	// the system. This allows for the DCO to settle properly.

	// The CS module registers are listed on page 103 of the User's guide. There are in general 7 registers controlling the CS behaviour

	// The password defined in CSCTL0 controls access to the CS registers. After the correct password is written, the write access to the CS registers is enabled. Write access is disabled by writing an incorrect
	// password in byte mode to the CSCTL0 upper byte.
	//(i.e. to enable writing do not change default value in CSCTL0, to prohibit access to other CS registers change the value in CSCTL0 some other random bullshit value)

	//printf("Setting the clock...\n");

	CSCTL0 = (CSKEY); //accessing the registers
	CSCTL1 = (DCOFSEL_6); // DCOFSEL_6 = 0x000C; setting 8 MHz DCO reference clock
	//CSCTL1 = (DCORSEL|DCOFSEL_4); // DCOFSEL_4 = 0x0040; setting 16 MHz DCO reference clock
	//CSCTL1 = (DCOFSEL_3); // DCOFSEL_4 = 0x0040; setting 4 MHz DCO reference clock
	CSCTL2 = (SELS_3);    // SELS_3 = 0x0030; Choosing DCO as a source for SMCLK
	CSCTL3 = (DIVS__1);   // DIVS__1 = 0x0000; SMCLK Source Divider f(SMCLK)/1. Divides the frequency of the SMCLK clock source by 1 (i.e. in this case leaves it 8 MHz)
	CSCTL4 = (LFXTOFF|VLOOFF|HFXTOFF); // Switching off all the clock sources except for the SMCLK
	// LFXTOFF = 0x0001
	// VLOOFF = 0x0008
	// HFXTOFF = 0x0100

	//CSCTL5 = 0x0000 by default (this register is needed for LF/HF clock sources)
	//CSCTL6 = 0x0000 by default (conditional requests are disabled)

	printf("Clock is set\n");
	//**************************************************************************************************************************************************************************************************
	//SETTING THE GPIO PINS
	//**************************************************************************************************************************************************************************************************
	//printf("Setting the GPIO pins...\n");
	// Two pins are going to be used - pin P1.3 as an output and pin P1.0 as an input
	// To pick suitable pins - see Datasheet pages 12 and 68

	// PIN P1.1
	// The resistor is driven from pin P1.1, which is connected to OUT2 (bit of the TA0CCTL2 register) from Timer1_A3 (TA0R) by setting bits P1SEL0, P1SEL1 and P1DIR (see page 88 of the Datasheet row TA0.2).
	// To understand which GPIO register to switch on and off go to page 86 of the Datasheet and pages 365-366 of the User's guide
	P1SEL0 = BIT1; // BIT1 = 0x0002
	P1SEL1 = 0x00;
	P1DIR = BIT1;

	// PIN P1.0
	// The junction of the capacitor and resistor is connected to P1.0.
	// Thus, P1.0 has to be configured as a comparator input, because at this port the comparison between the input voltage and Vcc/2 reference level is going to be performed
	// It is said in the book on page 381 that there is no need to do anything with the registers for port P1.0 because comparator overrides the settings
	// However, if you go to page 86 of the Datasheet, you will see that bits P1SEL0, P1SEL1 and P1DIR have to be set again
	P1SEL0 |= BIT0; // BIT0 = 0x0001
	P1SEL1 |= BIT0;
	P1DIR |= 0x00;

	// OTHER PINS OF PORT P1
	//unused pins of port P1 are configured as outputs (P1DIR = 1) and driven low (P1OUT = 0)
	P1DIR |= (BIT7|BIT6|BIT5|BIT4|BIT3|BIT2); // = 0xFC
	P1OUT = 0x00;


	printf("GPIO pins are set\n");
    //**************************************************************************************************************************************************************************************************
    //SETTING THE COMPARATOR
    //**************************************************************************************************************************************************************************************************
	//printf("Setting the Comparator...\n");
	// There are in general 6 registers controlling the operation of the comparator see pages 899, 902 and 906 of the User's guide

	// Channel 2 (C2) is selected as an input for the positive terminal comparator because P1.2 (which was selected as an input port above) supports the Channel 2 Comparator input (See Datasheet page 12)
	CECTL0 = (CEIPEN_L|CEIPSEL_0); // CEIPEN_L = 0x0080; Comp. E Pos. Channel Input Enable (selected analog input channel for V+ is enabled)
	                               // CEIPSEL_0 = 0x0000; Comp. E V+ terminal Input Select: Channel 0 (channel input selected for the V+ terminal of the comparator)


	// The voltage reference generator is used to generate VREF, which can be applied to either comparator input terminal.
	// The CEREF1x (VREF1) and CEREF0x (VREF0) bits control the output of the voltage generator.
	// The CERSEL bit selects the comparator terminal to which VREF is applied.
	CECTL2 = (CERS_1|CERSEL|CEREF0_15); // CERS_1 = 0x0040; Comp. E Reference Source 1 : Vcc (VCC is chosen as a reference source and applied to the resistor ladder of the Reference Voltage Generator)
	                                              // CERSEL = 0x0020; When bit CEEX = 0 from the CECTL1 register, then VREF is applied to the V� terminal
                                                  // CEREF0_15 = 0x000F; Comp. E Int. Ref.0 Select 7 : 16/32 (VCC/2 is selected)

	// CECTL3 -- see pages 903 and 910 of the User's guide.
	// The comparator input and output functions are multiplexed with I/O port pins, which are digital CMOS 	gates. When analog signals are applied to digital CMOS gates, parasitic current can
	// flow from VCC to	GND. The comparator input and output functions are multiplexed with I/O port pins, which are digital CMOS gates. When analog signals are applied to digital CMOS gates,
	// parasitic current can flow from VCC to GND. The CEPDx bits, when set, disable the corresponding Px.y input buffer. When current consumption is critical, any Px.y pin connected to analog signals
	// should be disabled with the associated CEPDx bits.
	// Selecting an input pin to the comparator multiplexer with the CEIPSEL or CEIMSEL bits automatically disables the input buffer for that pin, regardless of the state of the associated CEPDx bit.

	// This means that after CEIPSEL_2 was selected in register CECTL0 above, the input buffer for that pin was immediately disabled and there is no need in configuring register CECTL3
	printf("Comparator is set\n");
    //**************************************************************************************************************************************************************************************************
    //SETTING THE TIMER
    //**************************************************************************************************************************************************************************************************
	//printf("Setting the Timer ...\n");
	// Timer1_A3 (or shortly TA0) is going to be used (see Datasheet page 12 for pins P1.3 and P1.2).
	// Description of the timer registers can be found on page 640 of the User's guide.
	// Block diagram can be found on page 627 of the User's guide.

	// One of the identical capture/compare blocks, TAxCCRn (where n = 0 to 7) is going to be used to capture the timer data.
	// Each of these blocks has corresponding control register TAxCCTLn which defines the behaviour of its capture/compare block TAxCCRn.
	// Each control register TAxCCTLn has 15 bits and thus can be represented in hexadecimal format as 0xZZZZ, where Z is a certain number corresponding to the group of 4 bits
	// However, for comfortable coding there are already created mnemonical abbreviations for such groups of bits. For example:
	// #define SCS                    (0x0800)       /* Capture synchronize */
	// #define SCCI                   (0x0400)       /* Latched capture signal (read) */
	// #define CAP                    (0x0100)       /* Capture mode: 1 /Compare mode : 0 */
	// These abbreviations can be found in here:  C:\ti\ccsv7\ccs_base\msp430\include\msp430fr5969.h
	// Hence, using the abbreviations the coding of the TA0CCTL0 register can be done in such simple manner:
	// TA0CCTL0 = (CCIS_1 | SCS| CAP| CCIE)
	// instead of direct usage of the hexadecimal numbers which are hard to read and understand

	// Timer_A: SMCLK, no division, continuous, no need to clear, no ints
	TA0CTL = (TASSEL__SMCLK|ID__1|MC__CONTINUOUS); // TASSEL__SMCLK = 2*0x100u; Timer A clock source select: 2 - SMCLK
                                                   // ID__1 = 0*0x40u
                                                   // MC__CONTINUOUS = 2*0x10u
	printf("Timer is set\n");
	//N_discharge = 10000; //Dummy value for the first delay
	N_discharge = 1000; //Dummy value for the first delay
	printf("N_discharge = %d (Dummy value, before the loop)\n",N_discharge);

	for (i = 0; i<StatChargeSize ;i++) //Loop forever taking measurements
        {
	         CECTL1 = (CEMRVS|CEF|CE_enable); // comparator on
	                                     // CEMRVS = 0x1000; Comp. E Output selects between VREF0 or VREF1 (register CEMRVL selects between VREF0 or VREF1). VREF0 was selected here as a reference voltage source
	                                     // CEF = 0x0004; Output filter. Available if CEPWRMD = 00 or 01. Here the comparator output is filtered with the delay of approximately 450 ns
	                  	                 // CE_enable = 0x0400,  Comparator enable bit (instead of CEON). This bit turns the comparator on. When the comparator is turned off the Comparator_E consumes no power,
	                                     // so it was turned off to save current between measurements

	     // Charging transient: discharge C fully, set output, wait for the capture event

	         TA0CCR2 = TA0R + 15*N_discharge; // Total delay for full discharge
	         // TA0R - delay based on the previous measurement
	         // A compare event on register CCR2 of timer TA0 is set up below to allow time for the capacitor to discharge before the new measurement

	         // Time constant Tau = R*C;
	         // Let t1 be the time at which the discharge started. Voltage at this initial time equals to V1 = Vcc;
	         // Let t2 be the time at which comparator will be switched. Voltage at this time moment equals to V2 = Vcc/2;
	         // The discharge time can be expressed as (see Book page 379):
	         // N_discharge = R*C*ln(V1/V2) = R*C*ln(2) = 0.693*R*C = 0.693*Tau;

	         // The interval N_discharge is calculated as N_discharge/f_timer, where  N_discharge is the number of counts of the timer and f_timer is the frequency of the clock
	         // Hence,
	         // ln(2)*Tau = N_discharge = N_discharge/f_timer;
	         // Tau = N_discharge/(f_timer*ln(2));

	         // A key assumption in the measurement procedure is that the capacitor has to be fully charged/discharged before the N_discharge/N_charge will be measured
	         // The time that required for the capacitor to become fully charged/discharged can be approximated as:
	         // exp( -PreN_charge/(RC) ) < TimerResolution, where TimerResolution = 1/N_charge (or 1/N_discharge);
	         // Hence
	         // PreN_charge > Tau*ln(N_charge) = Tau*ln(Tau*f_timer*ln(2));

	         // For R = 10k and C = 10n (f_timer = 8 MHz):
	         // Tau = 10^(-4) [s] = 0.1 ms;
	         // PreN_charge > 6.318*Tau = 9.117*N_discharge

	         // For R = 10k and C = 100n (f_timer = 8 MHz):
	         // Tau = 10^(-3) [s] = 1 ms;
	         // PreN_charge > 8.621*Tau = 12.440*N_discharge

	         // The resistor is driven from P1.3 which is connected to OUT2 of the TA0CCR2 of Timer_A (Port P1.3 supports OUT2 - see Datasheet page 12)
	         TA0CCTL2 = (OUTMOD_1); // OUTMOD_1 = 1*0x20u; Output mode PWM output mode: 1 - set; Set on compare to start charge
	         //The OUT2 signal is changed when theValue in TA0R reaches the TA0CCR2 value specified above.
	         //The output mode from port P1.3 is Set, which drives it to Vcc after the delay to start the measurement (the output mode starts after the TRUE compare)


	         // Comparator output COUT is provided to Timer_A CCI2A capture input (Port P1.3 supports CCI2A capture - see Datasheet page 12).
	         // That means there is internal connection from comparator COUT to CCI2A on Timer_A, synchronized, interrupt enabled
	         TA0CCTL1 = (CCIS_1|SCS|CAP|CCIE|CM_1); // Capture comparator rising
                                                    // CM_1 = 1*0x4000u; capture on rising edge
                                                    // CM_2 = 2*0x4000u; capture on falling edge
	                                                // CCIS_1 = 1*0x1000u; Capture input select: 1 - CCI1B
	                                                // SCS = 0x0800; synchronous capture
	                                                // CAP = 0x0100; selecting capture mode
	                                                // CCIE = 0x0010; Capture/compare interrupt enable
	         // The comparator input at port P1.3 goes high when Vc(t) rises to Vcc/2.
	         //TA0CCR2 -- register is in capture mode: the Timer_A Register, TA0R, is copied into the TA0CCR2 register when a capture is performed.

	         //Capture of the rising edge also requires interrupt for TA0CCR2.CCIFG; Code for interrupt see below the main function



	         //TA0CCR1 -- register is in compare mode: the TA0CCR1 holds the data for the comparison to the timer value in the Timer_A Register, TA0R.


	         //_bis_SR_register(GIE + LPM0_bits);  // LPM0 (low power mode) interrupts enabled

	         __low_power_mode_0(); // Wait for timer and comparator
	         // __low_power_mode_0 enters MSP430 low power mode 0. In this mode the CPU and MCLK are disabled; SMCLK and ACLK are active.
	         // __low_power_mode_0 is an intrinsic function and produces inline code.
	         // LPM0: CPU and MCLK are disabled, SMCLK and ACLK remain active, I = 85 uA. This is used when the CPU is not required but some modules require a fast clock from
	         // SMCLK and the DCO.

	         N_charge = TA0CCR1 - TA0CCR2; // Duration of charge
	         printf("N_charge = %d\n",N_charge);
	         //TauCharge = N_charge/log(2);  // Time constant while charging
	         //printf("TauCharge = %f\n",TauCharge);
	         StatCharge[2*i] = N_charge;
	     // Discharging transient: charge C fully, reset output, wait for the capture event

	         TA0CCR2 = TA0R + 15*N_charge; // Total delay for full discharge

	         // The resistor is driven from P1.3 which is connected to OUT2 of the TA0CCR2 of Timer_A (Port P1.3 supports OUT1 - see Datasheet page 12)
	         TA0CCTL2 = (OUTMOD_5); // OUTMOD_1 = 1*0x20u; Output mode PWM output mode: 1 - set; Set on compare to start charge
	                                // OUTMOD_5 = 5*0x20u; PWM output mode: 5 - Reset; Reset on compare to start discharge

	         TA0CCTL1 = (CCIS_1|SCS|CAP|CCIE|CM_2); // Capture comparator falling
	                                                             // CM_1 = 1*0x4000u; capture on rising edge
	                                                             // CM_2 = 2*0x4000u; capture on falling edge
	                                                             // CCIS_1 = 1*0x1000u; Capture input select: 1 - CCI1B
	                                                             // SCS = 0x0800; synchronous capture
	                                                             // CAP = 0x0100; selecting capture mode
	                                                             // CCIE = 0x0010; Capture/compare interrupt enable

	         //_bis_SR_register(GIE + LPM0_bits);  // LPM0 (low power mode) interrupts enabled
	         __low_power_mode_0(); // Wait for timer and comparator

	          N_discharge = TA0CCR1 - TA0CCR2; // Duration of charge
	          printf("N_discharge = %d\n",N_discharge);
	          //TauDischarge = N_discharge/log(2);     // Time constant while discharging
	          //printf("TauDischarge = %f\n",TauDischarge);
	          StatCharge[2*i + 1] = N_discharge;

	          CECTL1 = (CEMRVS|CEF|CE_disable); // comparator off (after the measurements are complete)
	                                                   // CEMRVS = 0x1000; Comp. E Output selects between VREF0 or VREF1 (register CEMRVL selects between VREF0 or VREF1). VREF0 was selected here as a reference voltage source
	                                                   // CEF = 0x0004; Output filter. Available if CEPWRMD = 00 or 01. Here the comparator output is filtered with the delay of approximately 450 ns
	                                                   // CE_disable = 0x0000,  Comparator disable bit

        }

	for (j = 1; j<StatChargeSize ;j = j + 2)
	        {
	            Average_N_discharge = Average_N_discharge + StatCharge[j];
	            printf("StatCharge[%d]=%d\n",j,StatCharge[j]);
	        }
	Average_N_discharge = 2*Average_N_discharge/StatChargeSize;
	printf("Average_N_discharge = %f\n",Average_N_discharge);

	for (k = 0; k<StatChargeSize ;k = k + 2)
	            {
	                Average_N_charge = Average_N_charge + StatCharge[k];
	                printf("StatCharge[%d]=%d\n",k,StatCharge[k]);
	            }
	Average_N_charge = 2*Average_N_charge/StatChargeSize;
	printf("Average_N_charge = %f\n",Average_N_charge);

	R_charge = (Average_N_charge * 1e6) / (f_timer*Cap*log(2));
	printf("R_charge = %f kOhm\n",R_charge);
	R_discharge = (Average_N_discharge * 1e6) / (f_timer*Cap*log(2));
	printf("R_discharge = %f kOhm\n",R_discharge);
    R = (R_charge + R_discharge) / 2;
    printf("Average value of R = %f kOhm\n",R);

    RTol = (R-R_actual)/R_actual;
    printf("Tolerance (in percent) : %f\n",RTol*100);


	return 0;
}

//**************************************************************************************************************************************************************************************************
    // INTERRUPT SERVICE ROUTINE (ISR) for TA0CCR2.CCIFG
    //**************************************************************************************************************************************************************************************************

    // disable further captures and return to active mode
    #pragma vector = TIMER0_A1_VECTOR // TIMER1_A1_VECTOR -- 0xFFE0 Timer1_A3 CC1-2, TA (see page 59 of the Datasheet Table 6.4 Interrupt sources, flags)
    __interrupt void TIMER0_A1_ISR(void)
        {
        TA0CCTL1 = (CCIS_1|SCS|CAP|CCIE|CM_0); // Disable further captures
                                                            // CM_0 = 0*0x4000u; no capture (Capture mode: 0 - disabled)
                                                            // CM_1 = 1*0x4000u; capture on rising edge
                                                            // CM_2 = 2*0x4000u; capture on falling edge
                                                            // CCIS_1 = 1*0x1000u; Capture input select: 1 - CCI1B
                                                            // SCS = 0x0800; synchronous capture
                                                            // CAP = 0x0100; selecting capture mode
                                                            // CCIE = 0x0010; Capture/compare interrupt enable

       // _bic_SR_register_on_exit(GIE + LPM0_bits);
            __low_power_mode_off_on_exit(); // Return to active mode on exit
            // The CPU is turned off by entering low-power mode 0 until awakened by a capture interrupt from TA0CCR2.CCIFG
            // The ISR disables further, unwanted captures and processor returns to active mode
        //printf("Rabotaet ebta!\n");
        }

Integrated RH sensor code.zip