Jump to content

maelli01

Members
  • Content Count

    48
  • Joined

  • Last visited

  • Days Won

    16

Reputation Activity

  1. Like
    maelli01 got a reaction from MichelKohler in 3phase variable speed motor drive   
    Hi there
    Here is my 3phase variable speed motor drive booster pack

    This has been in my mind for some years, but I always thought that a 3phase variable speed inverter drive is
    beyond my humble hobbyist scope. Too complicated for my old 8-bit mind ;-)

    Such a inverter contains:
    6 high voltage FETs or IGBTs, 6 gatedrives, at least one DSP, a protection concept,
    all the software to create the 3-phase PWM, dead time control.....

    Still that was for quite some time on my long-term "to do" list, with no chance to actually materialize it,
    not enough time, too many other things to do.

    When playing around with the PWM module of the TM4C123 I found out that creating a 3phase PWM
    signal with this module is actually pretty easy.
    Combined that with an integrated Power Module such as the FSB50550 (Fairchild).



    So here it is: a booster pack for the Tiva Launchpad which drives big-ass 3phase motors.

    The booster pack contains the following:
    - the FSB50550 power module (6 FETs 500V 1.4Ohm, Gatedrivers, Bootstrap diodes, Temp sensor)
    - snubber capacitor
    Power supply: everything is powered from one DC source, 20V or (much) more.
    - 15V switchmode power supply from the high voltage side, built around a LNK304, for the FSB50550
    - 3.3V switchmode power supply from the 15V to power the Launchpad, built around a LT1376
    Measurement:
    - Passive voltage dividier to measure the input voltage
    - Sense resistor and LM339 comparator for overcurrent detection
    Display:
    - Nokia 5110 display
    Potentiometer for motor speed and direction


    The software is based on Energia using Tiva Ware function calls for all the PWM stuff.
    It is still work in progress, very basic and at the moment consists of:

    - calculate the sinwave lookup table at startup
    - PWM initialisation (PWM set to 15625 Hz, deadtime 1us, sync on)
    - a timer interrupt run every 10uSecs, do update the 3 PWD duty cycles
    - ADC measurement of temperature, voltage, current (moving average)
    - fault interrupt

    The main program is very short, the display is updated twice a second and the modulation factor is calculated
    out of the potentiometer speed setting and the applied DC voltage.
    Sudden changes in motor frequency are limited in the software, to prevent the motor to feed back energy and cause
    overvoltage.

    The motor on the picture is a 1/2hp, 900rpm, 6-pole motor, 12 kg of Italian steel and copper, probably 50 years old.
    For playing around, I apply about 50% of rated volt/hz, so current and maximum torque is reduced.
    Currently I use my dual 35V 4A lab supply, series connected, as a power source.
     
    here is the code:
    //simple 3phase frequency converter //27.9.2014 by maelli #define dots 192 //dots per halfhave, must be divisible with 3 #define period 5120 //80Mhz/5120 = 15625 switching frequency #define dt 80 //deadtime 80Mhz / 80 = 1uS #define PART_TM4C123GH6PM #include <stdint.h> #include <stdbool.h> #include "inc/hw_ints.h" #include "inc/hw_sysctl.h" #include "inc/hw_types.h" #include "driverlib/interrupt.h" #include "driverlib/sysctl.h" #include "driverlib/timer.h" #include "driverlib/pwm.h" #include "LCD_5110.h" #include "inc/tm4c123gh6pm.h" LCD_5110 myScreen (33,37,36,35,34,38,17); char celsius[3]={0x7f,'C',0x00}; uint16_t a,dire=0,modu,tensec; uint32_t timecount,sintable[dots]; volatile int32_t irqcount,timeset; volatile uint32_t temperature, voltage, current, poti; void setup(){ myScreen.begin(); myScreen.setBacklight(0); myScreen.text(0, 0, "3ph Converter"); for(int i=0;i<dots;i++) sintable[i]=sinf((i*3.14159)/dots)*(period/2-dt); unsigned long ulPeriod; unsigned int Hz = 10000; // interupt frequency in Hz ulPeriod = (SysCtlClockGet() / Hz); initTimer(); charge_gdu(); ROM_TimerLoadSet(TIMER0_BASE, TIMER_A,ulPeriod -1); initPWM(); } void loop(){ if (irqcount>499) { //20x per sec irqcount-=500; int32_t fsoll=732*(poti-16384); int32_t diff=fsoll-timeset; if (diff>0){ if (diff>150000) timeset+=150000; else timeset=fsoll; } else { if (diff<-150000) timeset-=150000; else timeset=fsoll; } modu=abs(timeset)/voltage/16; if (modu<(32000/voltage)) modu=32000/voltage; if (modu>256) modu=256; tensec++; if (tensec==10) { //2x per sec we display something tensec=0; myScreen.text(0, 1, mkstrg((temperature-325)/24,2)); myScreen.text(2, 1, celsius); myScreen.text(5, 1, mkstrg((voltage)/23,3)); myScreen.text(8, 1, "Volt"); myScreen.text(0, 2, mkstrg(abs(timeset)/322122,2)); myScreen.text(2, 2, "."); myScreen.text(3, 2, mkstrg(abs((timeset/32212)%10),1)); myScreen.text(4, 2, "Hz"); myScreen.text(7, 2, mkstrg(current,4)); myScreen.text(11, 2, "mA"); if (timeset<0) myScreen.text(0, 3, "links "); else myScreen.text(0, 3, "rechts"); } } } String mkstrg(int d,uint8_t l){ char display[l+1]; int q=1; display[l]=0; for (uint8_t a=l;a;a--){ display[a-1]=0x30+(d%(q*10))/q; q*=10; } return display; } void initTimer(){ ROM_SysCtlPeripheralEnable(SYSCTL_PERIPH_TIMER0); ROM_TimerConfigure(TIMER0_BASE, TIMER_CFG_PERIODIC); // 32 bits Timer TimerIntRegister(TIMER0_BASE, TIMER_A, Timer0Isr); // Registering isr ROM_TimerEnable(TIMER0_BASE, TIMER_A); ROM_IntEnable(INT_TIMER0A); ROM_TimerIntEnable(TIMER0_BASE, TIMER_TIMA_TIMEOUT); } void charge_gdu(){ ROM_SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOA); ROM_SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOD); ROM_SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOF); ROM_GPIOPinTypeGPIOOutput(GPIO_PORTF_BASE, GPIO_PIN_0 ); ROM_GPIOPadConfigSet(GPIO_PORTF_BASE,GPIO_PIN_0,GPIO_STRENGTH_4MA,GPIO_PIN_TYPE_STD); GPIOPinTypeGPIOOutput(GPIO_PORTA_BASE, GPIO_PIN_7); //alle 3 oberen ausschalten HWREG(GPIO_PORTA_BASE + (GPIO_PIN_7 << 2)) = 0; GPIOPinTypeGPIOOutput(GPIO_PORTD_BASE, GPIO_PIN_1); HWREG(GPIO_PORTD_BASE + (GPIO_PIN_1 << 2)) = 0; GPIOPinTypeGPIOOutput(GPIO_PORTF_BASE, GPIO_PIN_3); HWREG(GPIO_PORTF_BASE + (GPIO_PIN_3 << 2)) = 0; GPIOPinTypeGPIOOutput(GPIO_PORTD_BASE, GPIO_PIN_0); //auch die 2 letzten aus HWREG(GPIO_PORTD_BASE + (GPIO_PIN_0 << 2)) = 0; GPIOPinTypeGPIOOutput(GPIO_PORTF_BASE, GPIO_PIN_2); HWREG(GPIO_PORTF_BASE + (GPIO_PIN_2 << 2)) = 0; GPIOPinTypeGPIOOutput(GPIO_PORTA_BASE, GPIO_PIN_6); //den ersten unteren ein HWREG(GPIO_PORTA_BASE + (GPIO_PIN_6 << 2)) = GPIO_PIN_6; delay(1); HWREG(GPIO_PORTD_BASE + (GPIO_PIN_0 << 2)) = GPIO_PIN_0; delay(1); HWREG(GPIO_PORTF_BASE + (GPIO_PIN_2 << 2)) = GPIO_PIN_2; delay(1); } void initPWM(){ ROM_SysCtlPeripheralEnable(SYSCTL_PERIPH_PWM1); //The Tiva Launchpad has two PWM modules (0 and 1). We are using 1 ROM_GPIOPinConfigure(GPIO_PD0_M1PWM0); ROM_GPIOPinConfigure(GPIO_PD1_M1PWM1); ROM_GPIOPinConfigure(GPIO_PA6_M1PWM2); ROM_GPIOPinConfigure(GPIO_PA7_M1PWM3); ROM_GPIOPinConfigure(GPIO_PF2_M1PWM6); ROM_GPIOPinConfigure(GPIO_PF3_M1PWM7); ROM_GPIOPinConfigure(GPIO_PF4_M1FAULT0); ROM_GPIOPinTypePWM(GPIO_PORTD_BASE, GPIO_PIN_0 | GPIO_PIN_1 ); ROM_GPIOPinTypePWM(GPIO_PORTA_BASE, GPIO_PIN_6 | GPIO_PIN_7 ); ROM_GPIOPinTypePWM(GPIO_PORTF_BASE, GPIO_PIN_2 | GPIO_PIN_3 | GPIO_PIN_4); PWM1_0_FLTSEN_R =3; //PWM fault inverted see page 1169 GPIO_PORTF_PUR_R=0x10; //weak pullup for Pin 4 ROM_PWMGenConfigure(PWM1_BASE, PWM_GEN_0, PWM_GEN_MODE_UP_DOWN | PWM_GEN_MODE_SYNC | PWM_GEN_MODE_FAULT_LEGACY); ROM_PWMGenConfigure(PWM1_BASE, PWM_GEN_1, PWM_GEN_MODE_UP_DOWN | PWM_GEN_MODE_SYNC | PWM_GEN_MODE_FAULT_LEGACY); ROM_PWMGenConfigure(PWM1_BASE, PWM_GEN_3, PWM_GEN_MODE_UP_DOWN | PWM_GEN_MODE_SYNC | PWM_GEN_MODE_FAULT_LEGACY); ROM_PWMGenPeriodSet(PWM1_BASE, PWM_GEN_0, period); ROM_PWMGenPeriodSet(PWM1_BASE, PWM_GEN_1, period); ROM_PWMGenPeriodSet(PWM1_BASE, PWM_GEN_3, period); ROM_PWMDeadBandEnable(PWM1_BASE, PWM_GEN_0, dt,dt); ROM_PWMDeadBandEnable(PWM1_BASE, PWM_GEN_1, dt,dt); ROM_PWMDeadBandEnable(PWM1_BASE, PWM_GEN_3, dt,dt); ROM_PWMSyncTimeBase(PWM1_BASE,PWM_GEN_0_BIT |PWM_GEN_1_BIT|PWM_GEN_3_BIT); ROM_PWMGenEnable(PWM1_BASE, PWM_GEN_0); ROM_PWMGenEnable(PWM1_BASE, PWM_GEN_1); ROM_PWMGenEnable(PWM1_BASE, PWM_GEN_3); delay(1); PWMFaultIntRegister(PWM1_BASE, oh_shit); ROM_PWMIntEnable(PWM1_BASE,PWM_INT_FAULT0); ROM_PWMOutputState(PWM1_BASE, PWM_OUT_0_BIT | PWM_OUT_1_BIT | PWM1_BASE | PWM_OUT_2_BIT | PWM_OUT_3_BIT |PWM_OUT_6_BIT | PWM_OUT_7_BIT, true); } void Timer0Isr(void) { //10000x per second ROM_TimerIntClear(TIMER0_BASE, TIMER_TIMA_TIMEOUT); // Clear the timer interrupt irqcount++; timecount+=timeset; // 1 Hz is 192x256*256*256/10000=322122.5 if (timecount> 0xEFFFFFFF) timecount+=0xC0000000; if (timecount> 0xBFFFFFFF) timecount-=0xC0000000;; a=timecount>>16; a=a/(16384/(dots/3*2)); //a immer kleiner 2*dots: C000 *dots/3*2/ 4000= 12 *dots/3*2/4= 2*dots if (a<dots)ROM_PWMPulseWidthSet(PWM1_BASE, PWM_OUT_0,period/2+sintable[a]*modu/256); else ROM_PWMPulseWidthSet(PWM1_BASE, PWM_OUT_0,period/2-sintable[a-dots]*modu/256); a=a+dots*2/3; if (a>=2*dots) a-=2*dots; if (a<dots)ROM_PWMPulseWidthSet(PWM1_BASE, PWM_OUT_2,period/2+sintable[a]*modu/256); else ROM_PWMPulseWidthSet(PWM1_BASE, PWM_OUT_2,period/2-sintable[a-dots]*modu/256); a=a+dots*2/3; if (a>=2*dots) a-=2*dots; if (a<dots)ROM_PWMPulseWidthSet(PWM1_BASE, PWM_OUT_6,period/2+sintable[a]*modu/256); else ROM_PWMPulseWidthSet(PWM1_BASE, PWM_OUT_6,period/2-sintable[a-dots]*modu/256); ROM_PWMSyncUpdate(PWM1_BASE,PWM_GEN_0_BIT |PWM_GEN_1_BIT|PWM_GEN_3_BIT); switch(irqcount%10){ case 0: temperature=(temperature*127+analogRead(26))/128; break; case 1: voltage=(voltage*31+analogRead(27)*3)/32; break; case 2: current=(current*127+analogRead(25)*8)/128; break; case 3: poti=(poti*127+analogRead(28)*8)/128; break; } } void oh_shit(void) { //in case of severe overcurrent we shut down! ROM_PWMFaultIntClearExt(PWM1_BASE,PWM_INT_FAULT0); ROM_PWMOutputState(PWM1_BASE, PWM_OUT_0_BIT | PWM_OUT_1_BIT | PWM1_BASE | PWM_OUT_2_BIT | PWM_OUT_3_BIT |PWM_OUT_6_BIT | PWM_OUT_7_BIT, false); }  



  2. Like
    maelli01 got a reaction from Fmilburn in TM4C1294 ethernet library crashes   
    2018: warming up my old thread ;-)    
    good news: not a single crash in the last 4 months, 24/7 operation.

    I changed ISP and router box back in September. So I guess the TM4C1294 did not like my old router. 
    With the new Fritzbox 7360 it works like a charm.
  3. Like
    maelli01 got a reaction from dubnet in Solar power display   
    An output power display for my solar system.
    G2553 Launchpad,  Blue 4-digit LED display, RS485 Transceiver SN65HVD12P (a low power, 3.3V version of the standard SN75176), this is all there is.
    All pins are used,
    4 + 7 for the multiplexed LEDs (no resistors: Blue LED, 3.6V supply, output resistance of the pins limit the LED current)
    3 pins for UART and send/receive for the SN65.
    2 pins for 32768Hz  xtal (I had this one soldered in on the LP, so why not use it)
    The MSP asks the inverter over RS485/Modbus "what is your current output power". After less than half a sec, the  inverter aswers with the required value. This repeats every 2 seconds.
    The inverter is a Fronius Symo, with Datamanager 2 (which I guess is an embedded linux machine, covering LAN, Wifi, Modbus.....).
    The communication protocol can be downloaded from the Fronius website (after signing in), so no reverse engineering was required.
    Instead of only power, I could also display line voltage, frequency, total delivered energy....
     
    This is just a working prototype on Launchpad, I will do a PCB later, I also plan to power this directly from the inverter (which has a 12V solar powered output for such things).
    See the picture, almost 7.5kW :-)
     

  4. Like
    maelli01 got a reaction from zeke in Solar power display   
    An output power display for my solar system.
    G2553 Launchpad,  Blue 4-digit LED display, RS485 Transceiver SN65HVD12P (a low power, 3.3V version of the standard SN75176), this is all there is.
    All pins are used,
    4 + 7 for the multiplexed LEDs (no resistors: Blue LED, 3.6V supply, output resistance of the pins limit the LED current)
    3 pins for UART and send/receive for the SN65.
    2 pins for 32768Hz  xtal (I had this one soldered in on the LP, so why not use it)
    The MSP asks the inverter over RS485/Modbus "what is your current output power". After less than half a sec, the  inverter aswers with the required value. This repeats every 2 seconds.
    The inverter is a Fronius Symo, with Datamanager 2 (which I guess is an embedded linux machine, covering LAN, Wifi, Modbus.....).
    The communication protocol can be downloaded from the Fronius website (after signing in), so no reverse engineering was required.
    Instead of only power, I could also display line voltage, frequency, total delivered energy....
     
    This is just a working prototype on Launchpad, I will do a PCB later, I also plan to power this directly from the inverter (which has a 12V solar powered output for such things).
    See the picture, almost 7.5kW :-)
     

  5. Like
    maelli01 got a reaction from veryalive in Solar power display   
    An output power display for my solar system.
    G2553 Launchpad,  Blue 4-digit LED display, RS485 Transceiver SN65HVD12P (a low power, 3.3V version of the standard SN75176), this is all there is.
    All pins are used,
    4 + 7 for the multiplexed LEDs (no resistors: Blue LED, 3.6V supply, output resistance of the pins limit the LED current)
    3 pins for UART and send/receive for the SN65.
    2 pins for 32768Hz  xtal (I had this one soldered in on the LP, so why not use it)
    The MSP asks the inverter over RS485/Modbus "what is your current output power". After less than half a sec, the  inverter aswers with the required value. This repeats every 2 seconds.
    The inverter is a Fronius Symo, with Datamanager 2 (which I guess is an embedded linux machine, covering LAN, Wifi, Modbus.....).
    The communication protocol can be downloaded from the Fronius website (after signing in), so no reverse engineering was required.
    Instead of only power, I could also display line voltage, frequency, total delivered energy....
     
    This is just a working prototype on Launchpad, I will do a PCB later, I also plan to power this directly from the inverter (which has a 12V solar powered output for such things).
    See the picture, almost 7.5kW :-)
     

  6. Like
    maelli01 got a reaction from Fmilburn in Solar power display   
    An output power display for my solar system.
    G2553 Launchpad,  Blue 4-digit LED display, RS485 Transceiver SN65HVD12P (a low power, 3.3V version of the standard SN75176), this is all there is.
    All pins are used,
    4 + 7 for the multiplexed LEDs (no resistors: Blue LED, 3.6V supply, output resistance of the pins limit the LED current)
    3 pins for UART and send/receive for the SN65.
    2 pins for 32768Hz  xtal (I had this one soldered in on the LP, so why not use it)
    The MSP asks the inverter over RS485/Modbus "what is your current output power". After less than half a sec, the  inverter aswers with the required value. This repeats every 2 seconds.
    The inverter is a Fronius Symo, with Datamanager 2 (which I guess is an embedded linux machine, covering LAN, Wifi, Modbus.....).
    The communication protocol can be downloaded from the Fronius website (after signing in), so no reverse engineering was required.
    Instead of only power, I could also display line voltage, frequency, total delivered energy....
     
    This is just a working prototype on Launchpad, I will do a PCB later, I also plan to power this directly from the inverter (which has a 12V solar powered output for such things).
    See the picture, almost 7.5kW :-)
     

  7. Like
    maelli01 got a reaction from tripwire in lpm 4.5 current measurement, fun with low current   
    exactly, and the second diode to make it fool-proof
  8. Like
    maelli01 got a reaction from chicken in lpm 4.5 current measurement, fun with low current   
    exactly, and the second diode to make it fool-proof
  9. Like
    maelli01 got a reaction from tripwire in lpm 4.5 current measurement, fun with low current   
    The example msp430fr5994x_lpm4-5_02.c is supposed to show how little current is used in this mode.
    In the file it says:
    //   MSP430FR5x9x Demo - Entering and waking up from LPM4.5 via P1.3 interrupt
    //                       with SVS disabled
    //
    //   Description: Download and run the program. When entered LPM4.5, no LEDs
    //                should be on. Use a multimeter to measure current on JP1 and
    //                compare to the datasheet. When a positive voltage is applied
    //                to P1.3 the device should wake up from LPM4.5. This will enable
    //                the LFXT oscillator and blink the LED (on P1.0).
    Even for a high-end multimeter this current is too low to be accurately measured. 
    So I helped myself this way: 
    - power the processor from the supercap
    - a 10k resistor with two antiparallel diodes act as a shunt,  
    - connect the volt meter across the supercap, not across the processor
    0.43mV over a 10k resistor gives 43 Nanoamps. (!)   Yes, the datasheet (page 32) is right, typical value at 25°C is 45nA.  
    A  CR2032 (200mAh) cell would allow the processor to wait for an interrupt  for 530 years. 


  10. Like
    maelli01 got a reaction from chicken in lpm 4.5 current measurement, fun with low current   
    The example msp430fr5994x_lpm4-5_02.c is supposed to show how little current is used in this mode.
    In the file it says:
    //   MSP430FR5x9x Demo - Entering and waking up from LPM4.5 via P1.3 interrupt
    //                       with SVS disabled
    //
    //   Description: Download and run the program. When entered LPM4.5, no LEDs
    //                should be on. Use a multimeter to measure current on JP1 and
    //                compare to the datasheet. When a positive voltage is applied
    //                to P1.3 the device should wake up from LPM4.5. This will enable
    //                the LFXT oscillator and blink the LED (on P1.0).
    Even for a high-end multimeter this current is too low to be accurately measured. 
    So I helped myself this way: 
    - power the processor from the supercap
    - a 10k resistor with two antiparallel diodes act as a shunt,  
    - connect the volt meter across the supercap, not across the processor
    0.43mV over a 10k resistor gives 43 Nanoamps. (!)   Yes, the datasheet (page 32) is right, typical value at 25°C is 45nA.  
    A  CR2032 (200mAh) cell would allow the processor to wait for an interrupt  for 530 years. 


  11. Like
    maelli01 got a reaction from veryalive in lpm 4.5 current measurement, fun with low current   
    The example msp430fr5994x_lpm4-5_02.c is supposed to show how little current is used in this mode.
    In the file it says:
    //   MSP430FR5x9x Demo - Entering and waking up from LPM4.5 via P1.3 interrupt
    //                       with SVS disabled
    //
    //   Description: Download and run the program. When entered LPM4.5, no LEDs
    //                should be on. Use a multimeter to measure current on JP1 and
    //                compare to the datasheet. When a positive voltage is applied
    //                to P1.3 the device should wake up from LPM4.5. This will enable
    //                the LFXT oscillator and blink the LED (on P1.0).
    Even for a high-end multimeter this current is too low to be accurately measured. 
    So I helped myself this way: 
    - power the processor from the supercap
    - a 10k resistor with two antiparallel diodes act as a shunt,  
    - connect the volt meter across the supercap, not across the processor
    0.43mV over a 10k resistor gives 43 Nanoamps. (!)   Yes, the datasheet (page 32) is right, typical value at 25°C is 45nA.  
    A  CR2032 (200mAh) cell would allow the processor to wait for an interrupt  for 530 years. 


  12. Like
    maelli01 got a reaction from Fmilburn in lpm 4.5 current measurement, fun with low current   
    The example msp430fr5994x_lpm4-5_02.c is supposed to show how little current is used in this mode.
    In the file it says:
    //   MSP430FR5x9x Demo - Entering and waking up from LPM4.5 via P1.3 interrupt
    //                       with SVS disabled
    //
    //   Description: Download and run the program. When entered LPM4.5, no LEDs
    //                should be on. Use a multimeter to measure current on JP1 and
    //                compare to the datasheet. When a positive voltage is applied
    //                to P1.3 the device should wake up from LPM4.5. This will enable
    //                the LFXT oscillator and blink the LED (on P1.0).
    Even for a high-end multimeter this current is too low to be accurately measured. 
    So I helped myself this way: 
    - power the processor from the supercap
    - a 10k resistor with two antiparallel diodes act as a shunt,  
    - connect the volt meter across the supercap, not across the processor
    0.43mV over a 10k resistor gives 43 Nanoamps. (!)   Yes, the datasheet (page 32) is right, typical value at 25°C is 45nA.  
    A  CR2032 (200mAh) cell would allow the processor to wait for an interrupt  for 530 years. 


  13. Like
    maelli01 got a reaction from dubnet in lpm 4.5 current measurement, fun with low current   
    The example msp430fr5994x_lpm4-5_02.c is supposed to show how little current is used in this mode.
    In the file it says:
    //   MSP430FR5x9x Demo - Entering and waking up from LPM4.5 via P1.3 interrupt
    //                       with SVS disabled
    //
    //   Description: Download and run the program. When entered LPM4.5, no LEDs
    //                should be on. Use a multimeter to measure current on JP1 and
    //                compare to the datasheet. When a positive voltage is applied
    //                to P1.3 the device should wake up from LPM4.5. This will enable
    //                the LFXT oscillator and blink the LED (on P1.0).
    Even for a high-end multimeter this current is too low to be accurately measured. 
    So I helped myself this way: 
    - power the processor from the supercap
    - a 10k resistor with two antiparallel diodes act as a shunt,  
    - connect the volt meter across the supercap, not across the processor
    0.43mV over a 10k resistor gives 43 Nanoamps. (!)   Yes, the datasheet (page 32) is right, typical value at 25°C is 45nA.  
    A  CR2032 (200mAh) cell would allow the processor to wait for an interrupt  for 530 years. 


  14. Like
    maelli01 got a reaction from timotet in micropower microvoltmeter with MSP430FR4133 and MCP3422 ADC   
    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.
     
     

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

    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.
     
     

    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; } }
  15. Like
    maelli01 got a reaction from zeke in micropower microvoltmeter with MSP430FR4133 and MCP3422 ADC   
    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.
     
     

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

    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.
     
     

    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; } }
  16. Like
    maelli01 got a reaction from Fmilburn in micropower microvoltmeter with MSP430FR4133 and MCP3422 ADC   
    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.
     
     

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

    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.
     
     

    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; } }
  17. Like
    maelli01 got a reaction from Rei Vilo in micropower microvoltmeter with MSP430FR4133 and MCP3422 ADC   
    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.
     
     

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

    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.
     
     

    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; } }
  18. Like
    maelli01 got a reaction from jazz in micropower microvoltmeter with MSP430FR4133 and MCP3422 ADC   
    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.
     
     

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

    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.
     
     

    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; } }
  19. Like
    maelli01 got a reaction from veryalive in micropower microvoltmeter with MSP430FR4133 and MCP3422 ADC   
    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.
     
     

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

    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.
     
     

    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; } }
  20. Like
    maelli01 got a reaction from chicken in micropower microvoltmeter with MSP430FR4133 and MCP3422 ADC   
    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.
     
     

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

    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.
     
     

    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; } }
  21. Like
    maelli01 got a reaction from chicken in Products using MSP430   
    Hioki multimeter MSP430F449
     
     

     
    8:12
  22. Like
    maelli01 got a reaction from bluehash in Ultimate gibberish machine from the Art of Electronics   
    In the Horowitz/Hill Art of Electronics, third edition, design practices with discrete 74HCxxx, FPGA and Microprocessor are compared and discussed.
    As an example, the ultimate gibberish machine is described, a circuit that sends out a succession of pseudorandom bytes, as standard RS232 serial data, with 2 selectable speeds, 9600 / 1200 baud. 
     
    Independent of the processor type, the implementation with a small micro and program it in C looks like the clear winner here, smallest engineering effort, lowest hardware effort (have to admit that I do not have the faintest idea about FPGA.)
     
    The example in the book used some 8051ish device, so I was wondering on how this would look like on a MSP430/launchpad
     
    See my code below, based on the 8051 C code from the book, but adapted to run as low power as possible (LPM3)
    The power consuption of this thing is: 40uA at 9600baud, 6uA at 1200baud  
    //MSP430G2553LP implementation of the ultimate gibberish machine //from the art of electronics, third edition, Chapter 11.3. //Produces a pseudorandom datastream at 9600/1200baud, 8n1 //P1.0 output, sync, processor busy //P1.2 output, serial pseudorandom data //P1.3 input, data rate select (button,at start) //32768Hz crystal is required //2016-10-23 maelli01 #include <msp430.h> unsigned char d,c,b,a=1; int main(void) { WDTCTL = WDTPW + WDTHOLD; // Stop WDT P1DIR = ~BIT3; P1REN=BIT3; P1OUT=BIT3; // all output, except button P1SEL = BIT2; P1SEL2=BIT2; // P1.2=TXD P2REN = 0x3F; // all unused pins resistor enable P3REN = 0xFF; // all unused pins resistor enable UCA0CTL1 |= UCSSEL_1; // CLK = ACLK if(P1IN&8){ // button not pressed: 9600 baud UCA0BR0 = 3; // 32768Hz/9600 = 3.41 UCA0MCTL = UCBRS1 + UCBRS0; // Modulation UCBRSx = 3 } else{ // button pressed: 1200 baud UCA0BR0 = 27; // 32768Hz/1200 = 27.3 UCA0MCTL = UCBRS1; // Modulation UCBRSx = 2 } UCA0BR1 = 0x00; UCA0CTL1 &= ~UCSWRST; // **Initialize USCI state machine** IE2 |= UCA0TXIE; // Enable USCI_A0 TX interrupt __bis_SR_register(LPM3_bits + GIE); // Enter LPM3 w/ int } #pragma vector=USCIAB0TX_VECTOR __interrupt void USCI0TX_ISR(void){ P1OUT |=1; UCA0TXBUF=((d<<1)|(c>>7))^c; // 32 bit pseudorandom generator d=c; c=b; b=a; a=UCA0TXBUF; P1OUT &=~1; }
  23. Like
    maelli01 got a reaction from chicken in Ultimate gibberish machine from the Art of Electronics   
    In the Horowitz/Hill Art of Electronics, third edition, design practices with discrete 74HCxxx, FPGA and Microprocessor are compared and discussed.
    As an example, the ultimate gibberish machine is described, a circuit that sends out a succession of pseudorandom bytes, as standard RS232 serial data, with 2 selectable speeds, 9600 / 1200 baud. 
     
    Independent of the processor type, the implementation with a small micro and program it in C looks like the clear winner here, smallest engineering effort, lowest hardware effort (have to admit that I do not have the faintest idea about FPGA.)
     
    The example in the book used some 8051ish device, so I was wondering on how this would look like on a MSP430/launchpad
     
    See my code below, based on the 8051 C code from the book, but adapted to run as low power as possible (LPM3)
    The power consuption of this thing is: 40uA at 9600baud, 6uA at 1200baud  
    //MSP430G2553LP implementation of the ultimate gibberish machine //from the art of electronics, third edition, Chapter 11.3. //Produces a pseudorandom datastream at 9600/1200baud, 8n1 //P1.0 output, sync, processor busy //P1.2 output, serial pseudorandom data //P1.3 input, data rate select (button,at start) //32768Hz crystal is required //2016-10-23 maelli01 #include <msp430.h> unsigned char d,c,b,a=1; int main(void) { WDTCTL = WDTPW + WDTHOLD; // Stop WDT P1DIR = ~BIT3; P1REN=BIT3; P1OUT=BIT3; // all output, except button P1SEL = BIT2; P1SEL2=BIT2; // P1.2=TXD P2REN = 0x3F; // all unused pins resistor enable P3REN = 0xFF; // all unused pins resistor enable UCA0CTL1 |= UCSSEL_1; // CLK = ACLK if(P1IN&8){ // button not pressed: 9600 baud UCA0BR0 = 3; // 32768Hz/9600 = 3.41 UCA0MCTL = UCBRS1 + UCBRS0; // Modulation UCBRSx = 3 } else{ // button pressed: 1200 baud UCA0BR0 = 27; // 32768Hz/1200 = 27.3 UCA0MCTL = UCBRS1; // Modulation UCBRSx = 2 } UCA0BR1 = 0x00; UCA0CTL1 &= ~UCSWRST; // **Initialize USCI state machine** IE2 |= UCA0TXIE; // Enable USCI_A0 TX interrupt __bis_SR_register(LPM3_bits + GIE); // Enter LPM3 w/ int } #pragma vector=USCIAB0TX_VECTOR __interrupt void USCI0TX_ISR(void){ P1OUT |=1; UCA0TXBUF=((d<<1)|(c>>7))^c; // 32 bit pseudorandom generator d=c; c=b; b=a; a=UCA0TXBUF; P1OUT &=~1; }
  24. Like
    maelli01 got a reaction from spirilis in 3phase variable speed motor drive   
    Hi there
    Here is my 3phase variable speed motor drive booster pack

    This has been in my mind for some years, but I always thought that a 3phase variable speed inverter drive is
    beyond my humble hobbyist scope. Too complicated for my old 8-bit mind ;-)

    Such a inverter contains:
    6 high voltage FETs or IGBTs, 6 gatedrives, at least one DSP, a protection concept,
    all the software to create the 3-phase PWM, dead time control.....

    Still that was for quite some time on my long-term "to do" list, with no chance to actually materialize it,
    not enough time, too many other things to do.

    When playing around with the PWM module of the TM4C123 I found out that creating a 3phase PWM
    signal with this module is actually pretty easy.
    Combined that with an integrated Power Module such as the FSB50550 (Fairchild).



    So here it is: a booster pack for the Tiva Launchpad which drives big-ass 3phase motors.

    The booster pack contains the following:
    - the FSB50550 power module (6 FETs 500V 1.4Ohm, Gatedrivers, Bootstrap diodes, Temp sensor)
    - snubber capacitor
    Power supply: everything is powered from one DC source, 20V or (much) more.
    - 15V switchmode power supply from the high voltage side, built around a LNK304, for the FSB50550
    - 3.3V switchmode power supply from the 15V to power the Launchpad, built around a LT1376
    Measurement:
    - Passive voltage dividier to measure the input voltage
    - Sense resistor and LM339 comparator for overcurrent detection
    Display:
    - Nokia 5110 display
    Potentiometer for motor speed and direction


    The software is based on Energia using Tiva Ware function calls for all the PWM stuff.
    It is still work in progress, very basic and at the moment consists of:

    - calculate the sinwave lookup table at startup
    - PWM initialisation (PWM set to 15625 Hz, deadtime 1us, sync on)
    - a timer interrupt run every 10uSecs, do update the 3 PWD duty cycles
    - ADC measurement of temperature, voltage, current (moving average)
    - fault interrupt

    The main program is very short, the display is updated twice a second and the modulation factor is calculated
    out of the potentiometer speed setting and the applied DC voltage.
    Sudden changes in motor frequency are limited in the software, to prevent the motor to feed back energy and cause
    overvoltage.

    The motor on the picture is a 1/2hp, 900rpm, 6-pole motor, 12 kg of Italian steel and copper, probably 50 years old.
    For playing around, I apply about 50% of rated volt/hz, so current and maximum torque is reduced.
    Currently I use my dual 35V 4A lab supply, series connected, as a power source.
     
    here is the code:
    //simple 3phase frequency converter //27.9.2014 by maelli #define dots 192 //dots per halfhave, must be divisible with 3 #define period 5120 //80Mhz/5120 = 15625 switching frequency #define dt 80 //deadtime 80Mhz / 80 = 1uS #define PART_TM4C123GH6PM #include <stdint.h> #include <stdbool.h> #include "inc/hw_ints.h" #include "inc/hw_sysctl.h" #include "inc/hw_types.h" #include "driverlib/interrupt.h" #include "driverlib/sysctl.h" #include "driverlib/timer.h" #include "driverlib/pwm.h" #include "LCD_5110.h" #include "inc/tm4c123gh6pm.h" LCD_5110 myScreen (33,37,36,35,34,38,17); char celsius[3]={0x7f,'C',0x00}; uint16_t a,dire=0,modu,tensec; uint32_t timecount,sintable[dots]; volatile int32_t irqcount,timeset; volatile uint32_t temperature, voltage, current, poti; void setup(){ myScreen.begin(); myScreen.setBacklight(0); myScreen.text(0, 0, "3ph Converter"); for(int i=0;i<dots;i++) sintable[i]=sinf((i*3.14159)/dots)*(period/2-dt); unsigned long ulPeriod; unsigned int Hz = 10000; // interupt frequency in Hz ulPeriod = (SysCtlClockGet() / Hz); initTimer(); charge_gdu(); ROM_TimerLoadSet(TIMER0_BASE, TIMER_A,ulPeriod -1); initPWM(); } void loop(){ if (irqcount>499) { //20x per sec irqcount-=500; int32_t fsoll=732*(poti-16384); int32_t diff=fsoll-timeset; if (diff>0){ if (diff>150000) timeset+=150000; else timeset=fsoll; } else { if (diff<-150000) timeset-=150000; else timeset=fsoll; } modu=abs(timeset)/voltage/16; if (modu<(32000/voltage)) modu=32000/voltage; if (modu>256) modu=256; tensec++; if (tensec==10) { //2x per sec we display something tensec=0; myScreen.text(0, 1, mkstrg((temperature-325)/24,2)); myScreen.text(2, 1, celsius); myScreen.text(5, 1, mkstrg((voltage)/23,3)); myScreen.text(8, 1, "Volt"); myScreen.text(0, 2, mkstrg(abs(timeset)/322122,2)); myScreen.text(2, 2, "."); myScreen.text(3, 2, mkstrg(abs((timeset/32212)%10),1)); myScreen.text(4, 2, "Hz"); myScreen.text(7, 2, mkstrg(current,4)); myScreen.text(11, 2, "mA"); if (timeset<0) myScreen.text(0, 3, "links "); else myScreen.text(0, 3, "rechts"); } } } String mkstrg(int d,uint8_t l){ char display[l+1]; int q=1; display[l]=0; for (uint8_t a=l;a;a--){ display[a-1]=0x30+(d%(q*10))/q; q*=10; } return display; } void initTimer(){ ROM_SysCtlPeripheralEnable(SYSCTL_PERIPH_TIMER0); ROM_TimerConfigure(TIMER0_BASE, TIMER_CFG_PERIODIC); // 32 bits Timer TimerIntRegister(TIMER0_BASE, TIMER_A, Timer0Isr); // Registering isr ROM_TimerEnable(TIMER0_BASE, TIMER_A); ROM_IntEnable(INT_TIMER0A); ROM_TimerIntEnable(TIMER0_BASE, TIMER_TIMA_TIMEOUT); } void charge_gdu(){ ROM_SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOA); ROM_SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOD); ROM_SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOF); ROM_GPIOPinTypeGPIOOutput(GPIO_PORTF_BASE, GPIO_PIN_0 ); ROM_GPIOPadConfigSet(GPIO_PORTF_BASE,GPIO_PIN_0,GPIO_STRENGTH_4MA,GPIO_PIN_TYPE_STD); GPIOPinTypeGPIOOutput(GPIO_PORTA_BASE, GPIO_PIN_7); //alle 3 oberen ausschalten HWREG(GPIO_PORTA_BASE + (GPIO_PIN_7 << 2)) = 0; GPIOPinTypeGPIOOutput(GPIO_PORTD_BASE, GPIO_PIN_1); HWREG(GPIO_PORTD_BASE + (GPIO_PIN_1 << 2)) = 0; GPIOPinTypeGPIOOutput(GPIO_PORTF_BASE, GPIO_PIN_3); HWREG(GPIO_PORTF_BASE + (GPIO_PIN_3 << 2)) = 0; GPIOPinTypeGPIOOutput(GPIO_PORTD_BASE, GPIO_PIN_0); //auch die 2 letzten aus HWREG(GPIO_PORTD_BASE + (GPIO_PIN_0 << 2)) = 0; GPIOPinTypeGPIOOutput(GPIO_PORTF_BASE, GPIO_PIN_2); HWREG(GPIO_PORTF_BASE + (GPIO_PIN_2 << 2)) = 0; GPIOPinTypeGPIOOutput(GPIO_PORTA_BASE, GPIO_PIN_6); //den ersten unteren ein HWREG(GPIO_PORTA_BASE + (GPIO_PIN_6 << 2)) = GPIO_PIN_6; delay(1); HWREG(GPIO_PORTD_BASE + (GPIO_PIN_0 << 2)) = GPIO_PIN_0; delay(1); HWREG(GPIO_PORTF_BASE + (GPIO_PIN_2 << 2)) = GPIO_PIN_2; delay(1); } void initPWM(){ ROM_SysCtlPeripheralEnable(SYSCTL_PERIPH_PWM1); //The Tiva Launchpad has two PWM modules (0 and 1). We are using 1 ROM_GPIOPinConfigure(GPIO_PD0_M1PWM0); ROM_GPIOPinConfigure(GPIO_PD1_M1PWM1); ROM_GPIOPinConfigure(GPIO_PA6_M1PWM2); ROM_GPIOPinConfigure(GPIO_PA7_M1PWM3); ROM_GPIOPinConfigure(GPIO_PF2_M1PWM6); ROM_GPIOPinConfigure(GPIO_PF3_M1PWM7); ROM_GPIOPinConfigure(GPIO_PF4_M1FAULT0); ROM_GPIOPinTypePWM(GPIO_PORTD_BASE, GPIO_PIN_0 | GPIO_PIN_1 ); ROM_GPIOPinTypePWM(GPIO_PORTA_BASE, GPIO_PIN_6 | GPIO_PIN_7 ); ROM_GPIOPinTypePWM(GPIO_PORTF_BASE, GPIO_PIN_2 | GPIO_PIN_3 | GPIO_PIN_4); PWM1_0_FLTSEN_R =3; //PWM fault inverted see page 1169 GPIO_PORTF_PUR_R=0x10; //weak pullup for Pin 4 ROM_PWMGenConfigure(PWM1_BASE, PWM_GEN_0, PWM_GEN_MODE_UP_DOWN | PWM_GEN_MODE_SYNC | PWM_GEN_MODE_FAULT_LEGACY); ROM_PWMGenConfigure(PWM1_BASE, PWM_GEN_1, PWM_GEN_MODE_UP_DOWN | PWM_GEN_MODE_SYNC | PWM_GEN_MODE_FAULT_LEGACY); ROM_PWMGenConfigure(PWM1_BASE, PWM_GEN_3, PWM_GEN_MODE_UP_DOWN | PWM_GEN_MODE_SYNC | PWM_GEN_MODE_FAULT_LEGACY); ROM_PWMGenPeriodSet(PWM1_BASE, PWM_GEN_0, period); ROM_PWMGenPeriodSet(PWM1_BASE, PWM_GEN_1, period); ROM_PWMGenPeriodSet(PWM1_BASE, PWM_GEN_3, period); ROM_PWMDeadBandEnable(PWM1_BASE, PWM_GEN_0, dt,dt); ROM_PWMDeadBandEnable(PWM1_BASE, PWM_GEN_1, dt,dt); ROM_PWMDeadBandEnable(PWM1_BASE, PWM_GEN_3, dt,dt); ROM_PWMSyncTimeBase(PWM1_BASE,PWM_GEN_0_BIT |PWM_GEN_1_BIT|PWM_GEN_3_BIT); ROM_PWMGenEnable(PWM1_BASE, PWM_GEN_0); ROM_PWMGenEnable(PWM1_BASE, PWM_GEN_1); ROM_PWMGenEnable(PWM1_BASE, PWM_GEN_3); delay(1); PWMFaultIntRegister(PWM1_BASE, oh_shit); ROM_PWMIntEnable(PWM1_BASE,PWM_INT_FAULT0); ROM_PWMOutputState(PWM1_BASE, PWM_OUT_0_BIT | PWM_OUT_1_BIT | PWM1_BASE | PWM_OUT_2_BIT | PWM_OUT_3_BIT |PWM_OUT_6_BIT | PWM_OUT_7_BIT, true); } void Timer0Isr(void) { //10000x per second ROM_TimerIntClear(TIMER0_BASE, TIMER_TIMA_TIMEOUT); // Clear the timer interrupt irqcount++; timecount+=timeset; // 1 Hz is 192x256*256*256/10000=322122.5 if (timecount> 0xEFFFFFFF) timecount+=0xC0000000; if (timecount> 0xBFFFFFFF) timecount-=0xC0000000;; a=timecount>>16; a=a/(16384/(dots/3*2)); //a immer kleiner 2*dots: C000 *dots/3*2/ 4000= 12 *dots/3*2/4= 2*dots if (a<dots)ROM_PWMPulseWidthSet(PWM1_BASE, PWM_OUT_0,period/2+sintable[a]*modu/256); else ROM_PWMPulseWidthSet(PWM1_BASE, PWM_OUT_0,period/2-sintable[a-dots]*modu/256); a=a+dots*2/3; if (a>=2*dots) a-=2*dots; if (a<dots)ROM_PWMPulseWidthSet(PWM1_BASE, PWM_OUT_2,period/2+sintable[a]*modu/256); else ROM_PWMPulseWidthSet(PWM1_BASE, PWM_OUT_2,period/2-sintable[a-dots]*modu/256); a=a+dots*2/3; if (a>=2*dots) a-=2*dots; if (a<dots)ROM_PWMPulseWidthSet(PWM1_BASE, PWM_OUT_6,period/2+sintable[a]*modu/256); else ROM_PWMPulseWidthSet(PWM1_BASE, PWM_OUT_6,period/2-sintable[a-dots]*modu/256); ROM_PWMSyncUpdate(PWM1_BASE,PWM_GEN_0_BIT |PWM_GEN_1_BIT|PWM_GEN_3_BIT); switch(irqcount%10){ case 0: temperature=(temperature*127+analogRead(26))/128; break; case 1: voltage=(voltage*31+analogRead(27)*3)/32; break; case 2: current=(current*127+analogRead(25)*8)/128; break; case 3: poti=(poti*127+analogRead(28)*8)/128; break; } } void oh_shit(void) { //in case of severe overcurrent we shut down! ROM_PWMFaultIntClearExt(PWM1_BASE,PWM_INT_FAULT0); ROM_PWMOutputState(PWM1_BASE, PWM_OUT_0_BIT | PWM_OUT_1_BIT | PWM1_BASE | PWM_OUT_2_BIT | PWM_OUT_3_BIT |PWM_OUT_6_BIT | PWM_OUT_7_BIT, false); }  



  25. Like
    maelli01 got a reaction from gsutton in Products using MSP430   
    Bike light follow-up: reverse engineering time!
     
    here is my almost complete circuit diagram.
     
    IC1 is a voltage regulator: 2.5V, exact type I could not find out. Only the MSP runs on the regulated voltage, all the rest runs from the raw battery voltage.
     
    Voltage divider R2 1M / R3 330K measure the battery voltage (some microamps get lost here)
     
    LED2 and 3 are indicator (red/green)
     
    LED1 is the power led
     
    PWM is 20kHz, coming from pin 11 of the MSP.
    Main switch is a Si4562, N and P channel 20V 5A mosfet. Inductor is 100uH.
    Instead of using only the upper fet, they alternately switch on the upper / lower FET, avoiding one diode voltage drop, increasing effiency.
    The circuitry around IC3 (a weird CMOS 4572) creates a small dead time (less than a microsecond) to avoid cross-conduction.
    Note the resistors in the signal path ;-) 
     
    They managed to regulate the LED current without a shunt resistor. Took me some time to find out how:
    The voltage across the inductor is low-pass filtered, R14 390k / C4 0.1u, then fed into the MSP. Of course the DC-part of the inductor voltage depends on the current flow.
    Pretty clever. The regulation is rather slow (ramp-up of current is so slow it is actually visible).
     
    The circuitry around Q8 and Q9 takes care of the battery charge turn on/off. Input is from a wall-wart adaptor which is 500mA constant current type.
    R10/R11 tell the MSP that external voltage is present.
     
     

×
×
  • Create New...