Sign in to follow this  
Followers 0
maelli01

micropower microvoltmeter with MSP430FR4133 and MCP3422 ADC

2 posts in this topic

Weekend-project:

Autoranging microvoltmeter based on the MSP430FR4133 launchpad.

ADC used: MIcrochip MCP3422, an 18bit, 3.75 sample/second Sigma Delta with 2 differential inputs. I2C interface

This nice little chip contains a programmable amplifier (x2,x4,x8) and a not-too-bad internal reference of 2.048V.

Max input range is +/-2.048V, resolution (8x amplified) is 2uV.

 

Hand-etched a single layer PCB which goes on top of Launchpad.

 

 

post-37272-0-47232300-1487772319_thumb.jpg

Type K cable in hot water: 2.93mV, 73Kelvin temp difference to ambient

 

 

post-37272-0-03080400-1487772415_thumb.jpg

compare with my Fluke 289, 0.06% (datasheet says 0.05% typical, 0.35% max)

Not too shabby for a chip that costs 3 bucks.

 

 

post-37272-0-60423900-1487772489_thumb.jpg

Current consumption: on average <40uA, the whole setup would run 5000hours from a CR2032

The ADC does 1 sample/second and sleeps the rest of the time, the MSP430 does what it likes the most: sleep in LPM3

 

 

Code is not a big deal, quick hack based on the FR4133 examples, for the LCD and for the I2C interface 

//microvolt meter with MCP3422 and MSP430FR413
//******************************************************************************
#include <msp430.h> 
#define LCDMEMW ((int*)LCDMEM)

#define pos1 4                                               // Digit A1 - L4
#define pos2 6                                               // Digit A2 - L6
#define pos3 8                                               // Digit A3 - L8
#define pos4 10                                              // Digit A4 - L10
#define pos5 2                                               // Digit A5 - L2
#define pos6 18                                              // Digit A6 - L18

const char digit[10] ={
    0xFC,                                                    // "0"
    0x60,                                                    // "1"
    0xDB,                                                    // "2"
    0xF3,                                                    // "3"
    0x67,                                                    // "4"
    0xB7,                                                    // "5"
    0xBF,                                                    // "6"
    0xE0,                                                    // "7"
    0xFF,                                                    // "8"
    0xF7                                                     // "9"
};

volatile long voltage;
unsigned long dvoltage;
unsigned char TXByteCtr;
unsigned char TXData;
unsigned char newgain,gain;

void Clear_LCD(){
	int i;
	for(i=5;i;i--) LCDMEMW[i]=0;
	LCDMEMW[9]=0;
}

int main( void )
{
    WDTCTL = WDTPW | WDTHOLD;                               // Stop watchdog timer

    P1OUT = 0x00;P2OUT = 0x00;P3OUT = 0x00;P4OUT = 0x00;
    P5OUT = 0x00;P6OUT = 0x00;P7OUT = 0x00;P8OUT = 0x00;

    P1DIR = 0xFF;P2DIR = 0xFF;P3DIR = 0xFF;P4DIR = 0xFF;
    P5DIR = 0xFF;P6DIR = 0xFF;P7DIR = 0xFF;P8DIR = 0xFF;

    P5SEL0 |= BIT2 | BIT3;                   // I2C pins

    // Configure XT1 oscillator
    P4SEL0 |= BIT1 | BIT2;                              // P4.2~P4.1: crystal pins
    do  {
        CSCTL7 &= ~(XT1OFFG | DCOFFG);                  // Clear XT1 and DCO fault flag
        SFRIFG1 &= ~OFIFG;
    }
    while (SFRIFG1 & OFIFG);                           // Test oscillator fault flag

    // Disable the GPIO power-on default high-impedance mode
    // to activate previously configured port settings
    PM5CTL0 &= ~LOCKLPM5;

    CSCTL4 = SELMS__DCOCLKDIV + SELA__XT1CLK;  			// MCLK=SMCLK=DCO; ACLK=XT1

    // Configure RTC
    RTCCTL |= RTCSS__XT1CLK | RTCIE;                    // Initialize RTC to use XT1 and enable RTC interrupt
    RTCMOD = 16384;                                     // Set RTC modulo to 16384 to trigger interrupt twice a second

    // Configure LCD pins
    SYSCFG2 |= LCDPCTL;                                 // R13/R23/R33/LCDCAP0/LCDCAP1 pins selected

    LCDPCTL0 = 0xFFFF;
    LCDPCTL1 = 0x07FF;
    LCDPCTL2 = 0x00F0;                                  // L0~L26 & L36~L39 pins selected

    LCDCTL0 = LCDSSEL_0 | LCDDIV_7;                     // flcd ref freq is xtclk

    // LCD Operation - Mode 3, internal 3.08v, charge pump 256Hz
    LCDVCTL = LCDCPEN | LCDREFEN | VLCD_5 | (LCDCPFSEL0 | LCDCPFSEL1 | LCDCPFSEL2 | LCDCPFSEL3);

    LCDMEMCTL |= LCDCLRM;                               // Clear LCD memory

    LCDCSSEL0 = 0x000F;                                 // Configure COMs and SEGs
    LCDCSSEL1 = 0x0000;                                 // L0, L1, L2, L3: COM pins
    LCDCSSEL2 = 0x0000;

    LCDM0 = 0x21;                                       // L0 = COM0, L1 = COM1
    LCDM1 = 0x84;                                       // L2 = COM2, L3 = COM3

    LCDCTL0 |= LCD4MUX | LCDON;                         // Turn on LCD, 4-mux selected (LCD4MUX also includes LCDSON)

    Clear_LCD();

    // Configure USCI_B0 for I2C mode
    UCB0CTLW0 |= UCSWRST;                   		// Software reset enabled
    UCB0CTLW0 |= UCMODE_3 | UCMST | UCSYNC; 		// I2C mode, Master mode, sync
    UCB0CTLW1 |= UCASTP_2;                  		// Automatic stop generated
                                              	  	// after UCB0TBCNT is reached
    UCB0BRW = 0x0008;                         		// baudrate = SMCLK / 8

    UCB0I2CSA = 0x0068;                     		// Slave address
    UCB0CTL1 &= ~UCSWRST;
    UCB0IE |= UCRXIE | UCNACKIE | UCBCNTIE | UCTXIE0;



    while(1){
   // 	P1OUT |= BIT0;
    	TXByteCtr = 1;                              // Load TX byte counter
    	TXData = 0x8C+gain;
    	while (UCB0CTLW0 & UCTXSTP);                // Ensure stop condition got sent
    	UCB0CTLW0 |= UCTR | UCTXSTT;                // I2C TX, start condition
   // 	P1OUT &= ~BIT0;
    	__bis_SR_register(LPM3_bits | GIE);         // timer will wake me up

  //  	P1OUT |= BIT0;
        UCB0TBCNT = 0x0003;                     	// 3 bytes to be received
    	voltage=0;
    	UCB0CTLW0 &= ~UCTR;
        while (UCB0CTL1 & UCTXSTP);         		// Ensure stop condition got sent
        UCB0CTL1 |= UCTXSTT;                		// I2C start condition
        __bis_SR_register(LPM3_bits | GIE);			// I2C irq will wake me up

        voltage<<=8;					// shift to left corner to do the sign correctly
        voltage/=32;				        // calibration is done here: 2048 in an ideal world
        if ((voltage<400000)&&(voltage>(-400000))){     // autoranging, downshift
        	if (newgain<3) newgain++;
        }
        if ((voltage>1000000)||(voltage<-1000000)){     // autoranging, upshift
        	if (newgain) newgain--;
        }
        voltage>>=gain;
        gain=newgain;
        if ((voltage<500000)&&(voltage>-500000)){
        	voltage*=10;							//low range
        	LCDMEM[11]&=~1;							//adjust decimal point
        	LCDMEM[9]|=1;
        }
        else{									//high range
        	LCDMEM[9]&=~1;							//adjust decimal point
        	LCDMEM[11]|=1;
        }
        voltage*=25;
        voltage/=128;

        if (voltage<0)	 {dvoltage=-voltage; LCDMEM[5]|=4 ;}	//negative
        else		 {dvoltage= voltage; LCDMEM[5]&=~4;}	//positive

      	LCDMEM[pos1] = digit[(dvoltage / 100000)%10];
       	LCDMEM[pos2] = digit[(dvoltage / 10000)%10];
   	LCDMEM[pos3] = digit[(dvoltage / 1000)%10];
        LCDMEM[pos4] = digit[(dvoltage / 100)%10];
   	LCDMEM[pos5] = digit[(dvoltage / 10)%10];
   	LCDMEM[pos6] = digit[dvoltage % 10];
   //	P1OUT &= ~BIT0;
   	__bis_SR_register(LPM3_bits | GIE);         // timer will wake me up
    }
}



#pragma vector = RTC_VECTOR
__interrupt void RTC_ISR(void){
    switch(__even_in_range(RTCIV, RTCIV_RTCIF)){
        case  RTCIV_NONE:   break;          // No interrupt
        case  RTCIV_RTCIF:                  // RTC Overflow
       __bic_SR_register_on_exit(LPM3_bits);
            				break;
        default: 			break;
    }
}

#pragma vector = USCI_B0_VECTOR
__interrupt void USCIB0_ISR(void){
  switch(__even_in_range(UCB0IV, USCI_I2C_UCBIT9IFG)){
    case USCI_NONE:          break;         // Vector 0: No interrupts
    case USCI_I2C_UCALIFG:   break;         // Vector 2: ALIFG
    case USCI_I2C_UCNACKIFG:                // Vector 4: NACKIFG
        UCB0CTL1 |= UCTXSTT;                  // I2C start condition
      	  	  	  	  	  	 break;
    case USCI_I2C_UCSTTIFG:  break;         // Vector 6: STTIFG
    case USCI_I2C_UCSTPIFG:  break;         // Vector 8: STPIFG
    case USCI_I2C_UCRXIFG3:  break;         // Vector 10: RXIFG3
    case USCI_I2C_UCTXIFG3:  break;         // Vector 14: TXIFG3
    case USCI_I2C_UCRXIFG2:  break;         // Vector 16: RXIFG2
    case USCI_I2C_UCTXIFG2:  break;         // Vector 18: TXIFG2
    case USCI_I2C_UCRXIFG1:  break;         // Vector 20: RXIFG1
    case USCI_I2C_UCTXIFG1:  break;         // Vector 22: TXIFG1
    case USCI_I2C_UCRXIFG0:                 // Vector 24: RXIFG0
        voltage=(voltage<<8)+UCB0RXBUF;
      	  	  	  	  	     break;
    case USCI_I2C_UCTXIFG0:		    // Vector 26: TXIFG0
        if (TXByteCtr){                     // Check TX byte counter
            UCB0TXBUF = TXData; // Load TX buffer
            TXByteCtr--;                    // Decrement TX byte counter
        }
        else{
            UCB0CTLW0 |= UCTXSTP;           // I2C stop condition
            UCB0IFG &= ~UCTXIFG;            // Clear USCI_B0 TX int flag
        }
        					 break;
    case USCI_I2C_UCBCNTIFG:                // Vector 28: BCNTIFG
        __bic_SR_register_on_exit(LPM3_bits);
        					 break;
    case USCI_I2C_UCCLTOIFG: break;         // Vector 30: clock low timeout
    case USCI_I2C_UCBIT9IFG: break;         // Vector 32: 9th bit
    default: break;
  }
}

Rei Vilo, chicken, Fmilburn and 4 others like this

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!


Register a new account

Sign in

Already have an account? Sign in here.


Sign In Now
Sign in to follow this  
Followers 0