Jump to content

swampdonkeykami

Members
  • Content Count

    52
  • Joined

  • Last visited

  • Days Won

    2

Reputation Activity

  1. Like
    swampdonkeykami got a reaction from tripwire in [Group Buy-19][O] TMS0803/5 Emulating Calculator With Bubble Display   
    "@swampdonkeykami  Thank you for the review!"
     
    You may be mistaking me for somebody with an obscure youtube channel. 
  2. Like
    swampdonkeykami got a reaction from bluehash in [Group Buy-19][O] TMS0803/5 Emulating Calculator With Bubble Display   
    Had to wrassle with paypal, but it's paid now.
  3. Like
    swampdonkeykami got a reaction from Rickta59 in CHEAP air cylinder positioning with 43oh and flow meter   
    Hi Guys! I've been toying with an idea, that I  didn't think would work, and set up a test jig.
    Using an air compressor, a solenoid valve, a flow meter and a 43oh I found that we can get a repeatable cylinder position, like +-2.5 mm! 
    The flow meter is measuring the volume of air that we've fed to the cylinder and the 43oh opens or closes a solenoid valve depending on the cylinder position.http://arduinoforgoodnotevil.blogspot.ca/2013/10/accurate-ish-pneumatic-cylinder.html

  4. Like
    swampdonkeykami got a reaction from chibiace in CHEAP air cylinder positioning with 43oh and flow meter   
    Hi Guys! I've been toying with an idea, that I  didn't think would work, and set up a test jig.
    Using an air compressor, a solenoid valve, a flow meter and a 43oh I found that we can get a repeatable cylinder position, like +-2.5 mm! 
    The flow meter is measuring the volume of air that we've fed to the cylinder and the 43oh opens or closes a solenoid valve depending on the cylinder position.http://arduinoforgoodnotevil.blogspot.ca/2013/10/accurate-ish-pneumatic-cylinder.html

  5. Like
    swampdonkeykami got a reaction from bluehash in CHEAP air cylinder positioning with 43oh and flow meter   
    Hi Guys! I've been toying with an idea, that I  didn't think would work, and set up a test jig.
    Using an air compressor, a solenoid valve, a flow meter and a 43oh I found that we can get a repeatable cylinder position, like +-2.5 mm! 
    The flow meter is measuring the volume of air that we've fed to the cylinder and the 43oh opens or closes a solenoid valve depending on the cylinder position.http://arduinoforgoodnotevil.blogspot.ca/2013/10/accurate-ish-pneumatic-cylinder.html

  6. Like
    swampdonkeykami got a reaction from multivac in The Cramp! 430 powered desktop crane lamp   
    Even more impressive! Thanks for showing us what great projects can be accomplished even without all the tools. Too often people put up false barriers; "I could do that, if only I had a 3D printer. If only I had a CNC mill. If only I could program..."
     
    :thumbup: Well done sir!
  7. Like
    swampdonkeykami reacted to semicolo in Energia + LaunchPad MSP430G without on-chip UART?   
    I have to apologize about writing "but of course you can't use the communication examples."
    @@Rickta59 pointed out that TimerSerial.h implements a Timer/software driven UART. This works on the g2231 and g2452 and is automatically configure in Serial.h
     
    Yes you'd have better luck with TI's Code Composer studio for chips not supported in energia
  8. Like
    swampdonkeykami reacted to juani_c in Energia + LaunchPad MSP430G without on-chip UART?   
    A few days ago I was doing some testing and programed a MSP430F2013 using a ez430-f2013 but only uploaded a "blink" sketch. I select the board "LaunchPad w/ MSP430G2231 1Mhz".
    If you look in the devices datasheets you'll see that both have the configurations registers (at least the for Port1 and 2) at the same address. Also both chips have the same device ID, so I'm thinking that some code will be compatible.
    As semicolo said you program the chips using the Spy-By-Wire interface (that is RESET and TEST) it has nothing to do with the UART 
  9. Like
    swampdonkeykami got a reaction from multivac in The Cramp! 430 powered desktop crane lamp   
    Man! You must have a lot more patience than I do!
    When you build V. 2 :thumbup: ; I'll send you some copper coated steel rod. It's filler rod for tig welding, but the copper coating will make it easy to solder/braze. 
  10. Like
    swampdonkeykami reacted to multivac in The Cramp! 430 powered desktop crane lamp   
    hehe, too bad i dont have a pic of the wire rolls before. i straightened it by pulling it tight against a fixed metal tube(my workbench`s leg), which gave me a kink free, but slightly curved wire. I made a jig to put the three main booms together, so that went fast. the chassis, roof, and the two tiny thingies up top where made without support, and took some time.
  11. Like
    swampdonkeykami got a reaction from multivac in The Cramp! 430 powered desktop crane lamp   
    Wah?! That's great, you did a great job getting the wire straightened!
  12. Like
    swampdonkeykami reacted to nimblemotors in Tractor Engine Control   
    //******************************************************************************
    //
    //  Electronic Fuel Injection, Written by Jack Murray  2011
    //    NOTE, this is not cleaned up finished code, but decided to share it anyway.
    //
    //  We use a Geo Metro 1.0L Throttle Body Injector (TBI) for fuel
    //  The throttle is an electronic controlled from a 2004 Prius
    //  The water temp sensor is from a '87 Chevy
    //  The 02 sensor is a generic narrow band.
    //  We use the on-chip temp sensor to proxy the air temp
    //  The Fuel Pump is from an '89 Jeep Cherokee, and is turned on through
    //  a relay from a Geo Metro.
    //
    //  The EFI control is simply to determine how much time to turn on the injector,
    //  which is how long to make the pulse widths.
    //
    //  For the tractor, we also control the throttle, and try to maintain a
    //  given RPM that is determined by the RPM adjustment knob
    //
    //
    //  The controller is a repurposed MSP4301232 board that was a battery charger.
    //  It has a two line LCD, one darlington, two mosfets and 8 10bit ADC inputs
    //  We use the darlington to turn on the fuel pump relay, which is P1.1
    //  Mosfet A is used to for the fuel injector driver, P1.3 TA2
    //  Mosfet B is used to control the throttle, P1.2 TA1
    //  ADC inputs:
    //    A0   -- user interface selector
    //    A1   o2 sensor
    //    A2   -- lcd dial
    //    A3   water temp
    //    A4   rpm adjuster pot
    //    A5   -- connected to lcd
    //    A6   MAP sensor
    //    A7   throttle position sensor from prius
    //
    //    P2.5 is the tach input, that goes low on an ignition event
    // 
    //******************************************************************************
     
     
    #include  <signal.h>
    #include  <io.h>
     
     
    // efi settings, we need to limit this to 128 bytes if possible
    // so we can erase one segment and not lose everything if something goes wrong
    // during the update.
    struct ConfigEFI {
      unsigned char valid;   // whether containts are valid
      unsigned char airTable[32];   // PWM Adjustment for air temperature
      unsigned char watTable[32];   // PWM Adjustment for water temp  
      unsigned char rpmTable[32];   // PWM Adjustment for rpm
    };
     
     
    // Copy of current config parameters in Memory
    struct ConfigEFI Config, *FConfig; 
     
     
    unsigned short selectime;
    unsigned short selecting;
    unsigned short dstate, lastSeconds, seconds;
    unsigned short lastdial, dialpos;
     
     
     
     
    unsigned short throttleTimeOn, throttleTimeOff, throttleIsOn;
    unsigned short injectorIsOn, injectorStartTime, injectorPulseWidth;
    unsigned short injectorBaseWidth;
    unsigned short injectorTimeOff, injectorTimeOn;
     
     
    unsigned short dial, tps;
    unsigned short targetTPS = 500;
     
     
    unsigned short tach, rpm, lastTach;
     
     
    unsigned short airSensor;
    unsigned short waterSensor;
    unsigned short mapSensor;
    unsigned short o2Sensor;
     
     
    void startInjector();
    void updateInjectorWidth();
    void updateThrottle();
     
     
     
     
    #define Reboot()     WDTCTL = 0xFFFF        // cause reset from invalid WDT password
      
    flashcopy(char *mptr, char *fptr, int size)
    {
      int i;
      for (i=0; i<size; i++)
        {
          asm("dint");
          FCTL3 = FWKEY;                  // Clear Lock bit
          FCTL1 = FWKEY + WRT;            // Set WRT bit for write operation
          *fptr++ = *mptr++;         // Write value to flash
          FCTL1 = FWKEY;                  // Clear WRT bit
          FCTL3 = FWKEY + LOCK;           // Reset LOCK bit
          asm("eint");    
        }
    }
     
     
    // We save configs to flash.  We can only erase an entire segment,
    // so we first erase all of Segment A, copy the Segment B values + New values into A,
    // then erase B, and then copy A into B.  B now has the updated values.
    //
    SaveConfig()
    {
      int c,i;
      char *mptr;
      char *fptr;  
      int cfgsize = sizeof(Config);
      struct ConfigEFI *Aptr, *Bptr;
     
     
      Aptr = (struct ConfigEFI *)0x1080;
      Bptr = (struct ConfigEFI *)0x1000;  
     
     
      WDTCTL = WDTPW + WDTHOLD;       // Stop watchdog timer
      FCTL2 = FWKEY + FSSEL0 + FN4;   // 5Mhz MCLK/16 = 312Khz for Flash Timing Generator  
      //
      // Erase Segment A 
      fptr = (char *)Aptr;   // Initialize Flash segment B pointer
      asm("dint");
      FCTL1 = FWKEY + ERASE;          // Set Erase bit
      FCTL3 = FWKEY;                  // Clear Lock bit
      *fptr = 0;                // Dummy write to erase Flash segment A
      asm("eint");
      //
      // Write Segment A from memory
      mptr = (char *)&Config;
      flashcopy(mptr, (char *)Aptr, cfgsize);
      //
      // Now all of Erase B
      fptr = (char *)Bptr;   // Initialize Flash segment B pointer
      asm("dint");
      FCTL1 = FWKEY + ERASE;          // Set Erase bit
      FCTL3 = FWKEY;                  // Clear Lock bit
      *fptr = 0;                // Dummy write to erase Flash segment A
      asm("eint");
      //
      // Now copy all of A to B
      flashcopy((char *)Aptr, (char *)Bptr, cfgsize);
      // restart watchdog
      // WDTCTL = WDT_ADLY_1000;               // Set Watchdog Timer interval back to 1000ms (1-sec)
    }
     
     
    unsigned short adcRead(unsigned short channel)
    {
      unsigned short val; 
      ADC10CTL0 &= ~ENC;             // Disable while changing input channel
      ADC10CTL1 = (channel << 12);
      ADC10CTL0 |= ENC + ADC10SC;             // Sampling open
      while ((ADC10CTL1 & ADC10BUSY) == 1);   // ADC10BUSY?
      val = ADC10MEM;
      return val;
    }
     
     
    unsigned short readchannel(unsigned short channel)
    {
      unsigned short i, val;
      unsigned long avg;
      avg = 0;
      for(i=0; i<256; i++) {
        avg = avg + adcRead(channel);
      }
      val = avg >> 8;
      return val;
    }
     
     
    unsigned short waterSensor;
    unsigned short waterTemp;
     
     
     
     
    unsigned short readWaterTemp(unsigned short channel)
    {
      unsigned short val, tmp1, tmp2, tmp3, tmp4;
      //
      // The water temp is a GM water temp sensor
      // It has a log-scale of 10k at 0F down to 100 ohm at 250F
      // We use a divider circuit with a 330ohm resistor,
      // basically we just hardcode the readings to 32 levels 
      ADC10CTL0 =            REFON | REF2_5V | ADC10SHT_2 | SREF_1;   // turn OFF
      ADC10CTL0 =  ADC10ON | REFON | REF2_5V | ADC10SHT_2 | SREF_1;   // use 2.5v ref
      // ADC10CTL0 =  ADC10ON | ADC10SHT_3 | SREF_0;   // use VCC ref
      tmp1 = adcRead(3);
      tmp2 = adcRead(3);
      tmp3 = adcRead(3);
      tmp4 = readchannel(3);
      waterSensor = (tmp1 + tmp2 + tmp3 + tmp4) >> 2;
      // *=measured
      // temp    res      volts   1.5v   2.5v
      // 244 F : 110 ohm : 0.75v :  512 :  307 
      // 240 F : 120 ohm : 0.80v :  546 :  327 
      // 236 F : 130 ohm : 0.85v :  578 :  347 
      // 232 F : 140 ohm : 0.89v :  610 :  366 
      // 228 F : 150 ohm : 0.94v :  640 :  384 
      // 224 F : 160 ohm : 0.98v :  668 :  401 
      // 220 F : 170 ohm : 1.02v :  696 :  417 
      // 216 F : 180 ohm : 1.06v :  722 :  433 
      //*212 F : 190 ohm : 1.10v :  748 :  448 
      // 208 F : 200 ohm : 1.13v :  772 :  463 
      // 204 F : 210 ohm : 1.17v :  796 :  477 
      //*200 F : 220 ohm : 1.20v :  819 :  491 
      // 196 F : 236 ohm : 1.25v :  853 :  512 
      // 192 F : 252 ohm : 1.30v :  886 :  532 
      // 188 F : 268 ohm : 1.34v :  917 :  550 
      // 184 F : 284 ohm : 1.39v :  947 :  568 
      //*180 F : 300 ohm : 1.43v :  975 :  585 
      // 176 F : 324 ohm : 1.49v : 1014 :  608 
      // 172 F : 348 ohm : 1.54v : 1051 :  630 
      // 168 F : 372 ohm : 1.59v : 1085 :  651 
      // 164 F : 396 ohm : 1.64v : 1117 :  670 
      //*160 F : 420 ohm : 1.68v : 1146 :  688 
      // 156 F : 460 ohm : 1.75v : 1192 :  715 
      // 152 F : 500 ohm : 1.81v : 1233 :  740 
      // 148 F : 540 ohm : 1.86v : 1271 :  762 
      // 144 F : 580 ohm : 1.91v : 1305 :  783 
      //*140 F : 620 ohm : 1.96v : 1336 :  801 
      // 136 F : 660 ohm : 2.00v : 1365 :  819 
      // 132 F : 700 ohm : 2.04v : 1391 :  835 
      // 128 F : 740 ohm : 2.07v : 1416 :  849 
      // 124 F : 780 ohm : 2.11v : 1439 :  863 
      // 120 F : 820 ohm : 2.14v : 1460 :  876
      if (waterSensor < 600) {  // if temp is > 180, we switch to using the 1.5v reference to get better resolution
        ADC10CTL0 =            REFON | ADC10SHT_2 | SREF_1;   // use VCC ref, turn OFF
        ADC10CTL0 =  ADC10ON | REFON | ADC10SHT_2 | SREF_1;   // use 1.5v ref
        tmp1 = adcRead(3);
        tmp2 = adcRead(3);
        tmp3 = adcRead(3);
        tmp4 = adcRead(3);
        // switch it back to 2.5v
        ADC10CTL0 =            REFON | REF2_5V | ADC10SHT_2 | SREF_1;   // turn OFF
        ADC10CTL0 =  ADC10ON | REFON | REF2_5V | ADC10SHT_2 | SREF_1;   // use 2.5v ref
        waterSensor = (tmp1 + tmp2 + tmp3 + tmp4) >> 2;
        if (waterSensor <= 512) { waterTemp = 32; }
        else if (waterSensor <= 546) { waterTemp = 31; }
        else if (waterSensor <= 578) { waterTemp = 30; }
        else if (waterSensor <= 610) { waterTemp = 29; }
        else if (waterSensor <= 640) { waterTemp = 28; }
        else if (waterSensor <= 668) { waterTemp = 27; }
        else if (waterSensor <= 696) { waterTemp = 26; }
        else if (waterSensor <= 722) { waterTemp = 25; }
        else if (waterSensor <= 748) { waterTemp = 24; }
        else if (waterSensor <= 772) { waterTemp = 23; }
        else if (waterSensor <= 796) { waterTemp = 22; }
        else if (waterSensor <= 819) { waterTemp = 21; }
        else if (waterSensor <= 853) { waterTemp = 20; }
        else if (waterSensor <= 886) { waterTemp = 19; }
        else if (waterSensor <= 917) { waterTemp = 18; }
        else if (waterSensor <= 947) { waterTemp = 17; }
        else if (waterSensor <= 975) { waterTemp = 16; }
        else { waterTemp = 15; }
      }
      // 176 F : 324 ohm : 1.49v : 1014 :  608 
      // 172 F : 348 ohm : 1.54v : 1051 :  630 
      // 168 F : 372 ohm : 1.59v : 1085 :  651 
      // 164 F : 396 ohm : 1.64v : 1117 :  670 
      // 160 F : 420 ohm : 1.68v : 1146 :  688 
      // 156 F : 460 ohm : 1.75v : 1192 :  715 
      // 152 F : 500 ohm : 1.81v : 1233 :  740 
      // 148 F : 540 ohm : 1.86v : 1271 :  762 
      // 144 F : 580 ohm : 1.91v : 1305 :  783 
      // 140 F : 620 ohm : 1.96v : 1336 :  801 
      // 136 F : 660 ohm : 2.00v : 1365 :  819 
      // 132 F : 700 ohm : 2.04v : 1391 :  835 
      // 128 F : 740 ohm : 2.07v : 1416 :  849 
      // 124 F : 780 ohm : 2.11v : 1439 :  863 
      // 120 F : 820 ohm : 2.14v : 1460 :  876
      else if (waterSensor <= 608) { waterTemp = 15; }
      else if (waterSensor <= 630) { waterTemp = 14; }
      else if (waterSensor <= 651) { waterTemp = 13; }
      else if (waterSensor <= 670) { waterTemp = 12; }
      else if (waterSensor <= 688) { waterTemp = 11; }
      else if (waterSensor <= 715) { waterTemp = 10; }
      else if (waterSensor <= 740) { waterTemp = 9; }
      else if (waterSensor <= 762) { waterTemp = 8; }
      else if (waterSensor <= 783) { waterTemp = 7; }
      else if (waterSensor <= 801) { waterTemp = 6; }
      else if (waterSensor <= 819) { waterTemp = 5; }
      else if (waterSensor <= 835) { waterTemp = 4; }
      else if (waterSensor <= 849) { waterTemp = 3; }
      else if (waterSensor <= 863) { waterTemp = 2; }
      else if (waterSensor <= 1024) { waterTemp = 1; }
      return waterTemp;
    }
     
     
     
     
    __attribute__ ((naked)) void delay() {
        //first arg comes in register R15, loop uses 3 cycles per round
        //the eight cycles come from the call overhead and ret
        //delay_time = (1/MCLK)*(8+(3*n))
      asm("xloop: dec r15\n  jnz xloop\n ret");
    }
     
     
    //  To send command, Set Enable to High, set RS+DB command, then Clear Enable
    void
    LCDout(int rs, int data)
    {
      unsigned char high4 = (data >> 4) & 0x0F;
      unsigned char low4 = (data & 0x0F);
      unsigned char status = 0;
      unsigned char clear = 0;
      unsigned short max = 0;
      
      if (rs) {
        P3OUT =  0x30;  // Set Enable + RS, clear data bits 
        P3OUT |= high4; // Set high 4-bits
        P3OUT &= ~0x10;  // Clear Enable
        P3OUT = 0x30;  // Set Enable + RS
        P3OUT |= low4; // Set low 4-bits
        P3OUT &= ~0x10;  // Clear Enable    
      }
      else {
        P3OUT = 0x10;  // Set Enable, Clear RS, Clear data bits      
        P3OUT |= high4; // Set high 4-bits
        P3OUT &= ~0x10;  // Clear Enable
        // Now write low4 bits
        P3OUT = 0x10;  // Set Enable, Clear RS, Clear data bits      
        P3OUT |= low4; // Set low 4-bits
        P3OUT &= ~0x10;  // Clear Enable
      }
     
     
      delay(750*5);
     
     
      return;
    }
     
     
    void
    LCDouts(char *str)
    {
      while (*str != 0) {
        LCDout(1, *str++);        //write char and increment pointer, careful about 7-8 position!
      }
    }
     
     
    // initialize LCD.
    //  P3.0-1-2-3 connected to DB4-5-6-7
    //  P3.4 is Enable, P3.5 is RS(RegisterSelect, Read/Write is wired to Zero/Ground.
    void
    initLCD()
    {
      unsigned int wait;
      P3OUT = 0x00;    // Clear P3.0-7
      P3DIR = 0x3F;    // Set P3.0-5 to output direction
      
      P3OUT = 0x00;   // Clear Enable, RS, Data
     
     
      
      P3OUT = 0x03;   // Set Data Cmd for 8bit mode
      P3OUT |= 0x10;  // Set Enable.
      P3OUT &= ~0x10;  // Clear Enable.
      delay(750*20);
      
      P3OUT = 0x03;   // Set Data Cmd 
      P3OUT |= 0x10;  // Set Enable.
      P3OUT &= ~0x10;  // Clear Enable.
      delay(750*20);
      
      P3OUT = 0x03;   // Set Data Cmd 
      P3OUT |= 0x10;  // Set Enable.
      P3OUT &= ~0x10;  // Clear Enable.
      delay(750*20);
      
      P3OUT = 0x02;   // Now set Data Cmd to 0x20 for switch to 4bit mode
      P3OUT |= 0x10;  // Set Enable.
      P3OUT &= ~0x10;  // Clear Enable.
      delay(750*20);
      
      // Now we are in 4bit mode.  Do command again, now in 4bit mode.
     
     
      P3OUT = 0x02;   // Now send Function Set Data Cmd to 0x20 for switch to 4bit mode
      P3OUT |= 0x10;  // Set Enable.
      P3OUT &= ~0x10;  // Clear Enable.
      P3OUT = 0x0C;   // Send lower 4bit for 2-lines
      P3OUT |= 0x10;  // Set Enable.
      P3OUT &= ~0x10;  // Clear Enable.
      delay(750*20);
     
     
    }
     
     
    void
    LCDdigit(unsigned char d) {
      LCDout(1, '0'+ d);
    }
     
     
    unsigned short
    BCDecode(unsigned short val, unsigned char *d1, unsigned char *d2, unsigned char *d3)
    {
      *d1 = val % 10;
      val = val / 10;
      *d2 = val % 10;
      val = val / 10;
      if (d3)
        { *d3 = val % 10;
          val = val / 10;
        }
      return val;
    }
     
     
    // output charge time.  We increment only every 4 seconds, so 15 seconds is actually 60
    // we divide by 15 to get minutes.
    // 11.72v Ach 12:22
    // 11.72v Ach   81F
    LCDout_minutes (unsigned short ticks)
    {
      unsigned short val;
      char d1,d2;
     
     
      val = ticks/60;  
      BCDecode(val, &d1, &d2, 0);
      if (d2>0) { LCDdigit(d2); } else { LCDout(1,' '); }
      LCDdigit(d1);
     
     
      LCDout(1, ':');
      val = (ticks)%60;
      BCDecode(val, &d1, &d2, 0);
      LCDdigit(d2);
      LCDdigit(d1);
    }
     
     
     
     
    LCDout_temp (unsigned short temp)
    {
      unsigned short val = temp;
      char d1,d2,d3,d4;
     
     
      BCDecode(val, &d1, &d2, &d3);
      // d1 = val % 10;
      // val = val / 10;
      // d2 = val % 10;
      // val = val / 10;
      // d3 = val % 10;
      if (d3 > 0) {
        LCDdigit(d3);
      }
      else {
        LCDout(1, ' ');
      }
      LCDdigit(d2);
      LCDdigit(d1);
      LCDout(1, 'F');
    }
     
     
    LCDout_volts (unsigned short volts)
    {
      unsigned short val = volts;
      char d1,d2,d3,d4;
     
     
      val = BCDecode(val, &d1, &d2, &d3);  
      // d1 = val % 10;
      // val = val / 10;
      // d2 = val % 10;
      // val = val / 10;
      // d3 = val % 10;
      // val = val / 10;
      d4 = val % 10;
      if (d4 > 0) {
        LCDdigit(d4);
      }
      else {
        LCDout(1, ' ');
      }
      LCDdigit(d3);
      LCDout(1, '.');  
      LCDdigit(d2);
      LCDdigit(d1);
      LCDout(1, 'v');
    }
     
     
    LCDout_adc (unsigned short adc)
    {
      unsigned short val = adc;
      char d1,d2,d3,d4,d5;
     
     
      val = BCDecode(val, &d1, &d2, &d3);  
      d4 = val % 10;
      val = val / 10;
      if (d4 > 0) {
        d5 = val % 10;
        LCDdigit(d5);
      }
      else {
        LCDout(1, ' ');
      }
      LCDdigit(d4);
      LCDdigit(d3);
      LCDdigit(d2);
      LCDdigit(d1);
    }
     
     
     
     
    LCDout_Mamps (unsigned short ma)
    {
      unsigned short val = ma/8;
      char d1,d2,d3,d4;
     
     
      val = BCDecode(val, &d1, &d2, &d3);
      LCDdigit(val);  
      LCDdigit(d3);
      LCDdigit(d2);
      LCDdigit(d1);
      LCDout(1, 'm');
      LCDout(1, 'a');
      LCDout(1, ' ');
    }
     
     
     
     
    LCDout_amps (unsigned short amps)
    {
      unsigned short val = amps;
      char d1,d2,d3,d4;
     
     
      val = BCDecode(val, &d1, &d2, &d3);
      LCDdigit(d3);
      LCDout(1, '.');  
      LCDdigit(d2);
      LCDdigit(d1);
      LCDout(1, 'a');
      LCDout(1, ' ');
    }
     
     
     
     
    // Watchdog Timer interrupt service routine
    // Runs each second
    interrupt (WDT_VECTOR) 
    watchdog_timer(void)
    {
      unsigned short volts,temp;
     
     
      seconds++;
     
     
      _BIC_SR_IRQ(CPUOFF);             // Clear CPUOFF bits to wake up main loop
     
     
    }
     
     
    //
    // with two batteries charging, the ADC dial reading fluctuates badly,
    // so we stablize it by taking an average reading over 65,000 readings
    // and return that value, and then use hysteresis to have it snap
    // from one position to the next.
    //
    newDial()
    {
      unsigned short pos, ch1;
     
     
      while (selecting < 2) {  // no button pressed
        ch1 = readchannel(2); // read position
        pos = ch1/64; //  ndiv;
        if ((pos > dialpos) && (ch1-5 < lastdial))
          { pos = dialpos; }
        if ((pos < dialpos) && (ch1+5 > lastdial))
          { pos = dialpos; }  
        if (pos != dialpos) {
          // we have a new position, update globals and return
          lastdial = ch1;
          dialpos = pos;
          selecting = 1;     // reset watchdog timeout shutdown
          return dialpos;
        }
      }
      return dialpos;
    }
     
     
     
     
    int
    SelectInput(char *options)
    {
      LCDout(0, 0xC0); // position cursor at 2nd line
      LCDouts(options);
      LCDout(0, 0xC0); // position cursor at 2nd line
      LCDout(0, 0x0F);  // On, Cursor, Blink
     
     
      // init globals
      dialpos = 16;
      lastdial = 1060;
      
      selecting = 1;
      while (selecting < 2) {  // button not pressed (again)
        newDial();  // readchannel(INCH_2); // read position
        LCDout(0, 0xC0+dialpos); // update cursor position
      }
      return dialpos;  // return position when button pressed.
    }
     
     
    int
    buttonPress()
    {
      int val;
      unsigned int ch1, ch2, pos, cpos;
     
     
      while (1) {
        //
        // Offer selection of setting to edit
        //
        LCDout(0, 0x01);  // Clear Display
        LCDout(0, 0xC0);  // Move Cursor to position for this charger
        // LCDouts("X A:CtE B:CtE   ");
        LCDouts("XS A23E23 B23E23");
        // wait 1-2 sec to be sure button is fully back up
        // sec2 = seconds;
        // while (sec2 == seconds);
        LCDout(0, 0xC0); // position cursor at 2nd line
        LCDout(0, 0x0F);  // On, Cursor, Blink
     
     
        dialpos = 16;
        lastdial = 1060;
        selecting = 1;
        
        while (selecting < 2) {  // button not pressed (again)
          pos = newDial(); // readchannel(INCH_2); // read position
          LCDout(0, 0x80); // position cursor at 1st line, 3rd char "Px "
          switch (pos) {
          case 0:
    {
     LCDouts("Exit Menu   ");
     break;
    }
          case 1:
          case 2:
    {
     LCDouts("System Menu");
     break;
    }
          case 3:
          case 4:
          case 5:
    {
     LCDouts("A: Charge ");
     LCDdigit(pos-2);
     break;
    }
          case 6:
          case 7:
          case 8:
    {
     LCDouts("A: Edit   ");
     LCDdigit(pos-5);
     break;
    }
          case 9:
    {
     LCDouts("System Menu");
     break;
    }
          case 10:
          case 11:
          case 12:
    {
     LCDouts("B: Charge ");
     LCDdigit(pos-9);
     break;
    }
          case 13:
          case 14:
          case 15:
    {
     LCDouts("B: Edit   ");
     LCDdigit(pos-12);
     break;
    }
          }
          LCDouts("    ");    
          LCDout(0, 0xC0+pos); // position cursor at 2nd line pos
        }
        //
        // Execute Selected Action, and Return.
        // For Editing, because of stack overflow problems,
        // we return back to main to execute them.
        //
        switch (pos) {
          
        case 0: return 0;
        case 1:
        case 2:  return 1;
          
        case 3:
        case 4:
        case 5:      
          {
    return 0;
          }
        case 6:
        case 7:
        case 8:            
          {
    return pos-4;  // 2,3,4
          }
        case 9:  return 1;
          
        case 10:
        case 11:
        case 12:        
          {
    return 0;
          }
        case 13:
        case 14:
        case 15:
          {
    return pos-8;  // 5,6,7
          }
        }
        // return if not editing settings
        return 0;  // done
      }
    }
     
     
     
     
    //
    // Input Button interrupt occurs when button pressed
    // Button is in two states, up or down.
    // if it's up, we interrupt when it goes down,
    // but don't change state unless the button stays down
    // for a sufficient time, 30-50ms.
    // If it was down, and goes back up,
    // we acknowledge the button by incrementing the selecting variable.
    //
     
     
    interrupt (PORT1_VECTOR) p1int(void)
    {
      int in = P1IFG;
      if (in & 0x1) {   // P1.0 changed hardware state
        //
        // If button state was up, wait for a while to confirm it is now down
        //
        if ((P1IES & 0x01) == 0x01) { // int on high-low transition, so button is up and went down
          //
          // Aclock is running at 2-Mhz.  60,000 ticks would be 30msec.
          // we wait until it rolls through zero twice, that would be anywhere from 60k-120k
          // while (TAR > 4) {
          // in = P1IN;
          // }
          in = P1IN;      
          while (TAR > 4) {
    in = P1IN;
          }
          // now check if button is still down, if so, change interrupt to fire
          // when it goes up, and return from interrupt
     
     
          if ((in & 0x01)==0x00) {  // button is still down
    P1IES = 0x00; // now interrupt on button-up low-high transition
          }
        }
        // we have low-high interrupt, so button was down, now it is up. 
        else {
          // while (TAR > 4) {  // wait until TAR rolls to zero
          // in = P1IN;
          // }
          in = P1IN;      
          while (TAR > 4) {
    in = P1IN;
          }
          if ((in & 0x01)==0x01) {  // button is still up
    P1IES = 0x01; // now interrupt on button-down high-low transition
    // button is now considered pressed
    if (selecting==0) {
     selecting=1;
     _BIC_SR_IRQ(CPUOFF);             // Clear CPUOFF bits to wake up main loop
    }
    else {
     selecting++;  // increment to indicate button press
    }
          }
        }
      }
      P1IFG = 0;  // clear handled interrupt
    }
     
     
     
     
    unsigned short tar;
     
     
    //
    // Timer_A Interrupt Vector (TAIV) handler for CCR0 and CCR0 capture/compare,
    // and overflow interrupts
    interrupt (TIMERA0_VECTOR) a0timer(void)
    {
      unsigned short v;
      tar = TAR;
      if (TAIV == 10) {
      }
    }
     
     
    //
    // TACH signal went low
    // We get the rpm by measuring how long it was from
    // the last event to this one for the last 4 events (2 revolutions)
    // and generate a running average from them.
    // If we are in startup with less than 4 events, just use the last one.
    //
    // To get rpm, we get the rev per second.  we have a 32k clock,
    // so 32768 ticks per second, and thus 32768 / ticks = rev per second,
    // so if we get 16384 ticks, it would be 2 revolutions in a second.
    // We divide that in half since we have 2 events per revolution,
    // so 16384 / ticks = rev per second, if we multiply by 60 seconds per minute,
    // we get revolutions per minute (rpm).
    // We should not see less than 100rpm during cranking, which is 10,000 ticks
    // and thus times 4 = 40,000 which still fits into a 16bit unsigned short.
     
     
    unsigned short tachTimes[4];
     
     
    interrupt (PORT2_VECTOR) p2int(void)
    {
      unsigned short in = P2IFG;
      unsigned short tar = TAR;
      unsigned short tach0;
      // if (in & 0x20) { }  // P2.5 changed hardware state
      if (tar < lastTach) {  // timer rolled over
        tach0 = tar + (0xFFFF - lastTach);  // get up to here, plus the roll
      }
      else {
        tach0 = tar - lastTach;
      }
      lastTach = tar;
      tach++;  
      if (tach > 3) {
        tachTimes[ (tach & 0x3) ] = tach0;
        // now compute average of last four values
        tach0 = tachTimes[0] + tachTimes[1] + tachTimes[2] + tachTimes[3];
        tach0 = tach0 >> 2;
        // do injector spray after each ignition event once we've made at least one revolution
        rpm = (32768 / tach0) * 3;  // this gives us a better number
     
     
        startInjector();
        // wake up main loop to update injectorPulseWidth after we have started
        // nope, we must do it here, because if main is running the display (which takes 100ms)
        // wake up will do nothing, so we must force it now from interrupt.
        // we re-enable interrupts for pwm timers to execute.
        // if we get another tach interrupt before we're done here, we are toast!
        
        // _EINT();  // Now Enable interrupts 
        // updateInjectorWidth();
        // updateThrottle ();     
      }
      // rpm = (16384 / tach0) * 60;
      rpm = (32768 / tach0) * 3;  // this gives us a better number
      P2IFG = 0;       // clear handled interrupt
      _BIC_SR_IRQ(CPUOFF);             // Clear CPUOFF bits to wake up main loop
      return;
    }
     
     
    //
    // spray the fuel injector.
    // do a peak of full-on for 1.2 ms, then 25% pwm duty for remaining on-time
    // 
    void startInjector() {
      tar = TAR;
      injectorStartTime = tar;   // get timer value of when to leave it off
      CCR2 = tar + 39;   // 39 is 1.2ms for 32k timer 
      CCTL2 = OUT;  // turn ON injector
      CCTL2 = CCIE | OUTMOD_5;  // turn off at CCR2 after 1.2ms, and interrupt
      injectorIsOn = 1;         // peak period
      injectorPulseWidth = injectorBaseWidth;    
    }
     
     
    void updateInjectorWidth() {
      //
      // When we get tach input we wake up here
      // to read the sensors and initiate the injector spray
      //
      // For our 4cyl 159ci tractor engine, and a 45lb/hr injector, we have a base injector time of 9ms for
      // each ignition event.   It is adjusted by the o2sensor, airTemp, waterTemp, and MAP readings 
      // 
      // At our max of 2500 rpm = 41.666 RpS = 1/x = 24ms per revolution.
      // With two events per rev, that leaves us 12ms, so we are almost 100% at 2500
      // At 2200 rpm = 36.6 RpS = 1/x = 27ms per revolution, so we have 13.6ms
     
     
      // We use the previous sensor values to initiate the injector pulse width,
      // and then while that is going, we can do the 2-3ms needed to update the sensor readings
      // 
      // read all the sensors, this takes 
      //
      // read the MAP using 3v reference, 16x sample
      ADC10CTL0 =  0;
      ADC10CTL0 =  ADC10ON | ADC10SHT_2 | SREF_0; 
      mapSensor = adcRead(6);
      
      // read dial to get base pulse width adjustment
      // baseWidth is 9ms, which is 32*9 = 288 clock ticks
      // 
      dial = adcRead(2);
      injectorBaseWidth = dial >> 1;  // divide in half,  0 to 512 range 0 to 16ms
     
     
      //
      // read the O2 sensor using 1.5v reference
      ADC10CTL0 =  0;
      ADC10CTL0 =  ADC10ON | REFON | ADC10SHT_2 | SREF_1;   // use 1.5v ref
      o2Sensor = adcRead(1);
      //
      // read the chip temperature as proxy for outside air temp  using 1.5v ref
      airSensor = adcRead(0xA);
      //
      // read water temp, 
      waterTemp = readWaterTemp(3);
     
     
      //
      // adjust pulse width for MAP and temps until water temp is at operating temp
      // and then use o2 sensor for closed loop feedback
      //
      injectorPulseWidth = injectorBaseWidth;    
      if (waterTemp < 15) {  // doing open loop
        //
      }
      else if (o2Sensor < 500) {  // o2sensor is lean, slightly richen until o2 goes rich
        // injectorPulseWidth = injectorPulseWidth + 1;
      }
      else if (o2Sensor > 520) {
        // injectorPulseWidth = injectorPulseWidth - 1;
      }
    }
     
     
      unsigned short diff;
      unsigned short throttleHold     =   40;
      unsigned short throttleLowHold  =   64;
      unsigned short throttleOpen     =   10;
      unsigned short throttleClose    =  900;
      unsigned short throttleLowClose = 1600;
     
     
    void updateThrottle ()
    {
      //
      // adjust throttle position to maintain rpm setting
      //
      ADC10CTL0 =  0;
      ADC10CTL0 =  ADC10ON |  REFON | REF2_5V | ADC10SHT_2 | SREF_1;   // use 2.5v ref
      targetTPS = adcRead(4);
     
     
      throttleTimeOn = 8;  // constant
       
      // adjust the throttle PWM
      tps = adcRead(7);
      if (tps < targetTPS) {  // need to close it
        diff = targetTPS - tps;
        if (diff > 12) {
          // set pw to slow close
          // if tp is almost closed, we must use less force
          if (diff > 200) {
    throttleTimeOff = throttleLowClose;
          }
          else {
    throttleTimeOff = throttleClose;
          }
        }
        else {
          throttleTimeOff = throttleHold;
          if (tps > 800) {
    throttleTimeOff = throttleLowHold;
          }
        }
      }
      else {
        diff = tps - targetTPS;
        if (diff > 12) {   // need to open it
          if (diff > 200) {
    throttleTimeOff = throttleOpen; // fast open
          }
          else if (tps > 800) {
    throttleTimeOff = throttleOpen;
          }
          else {
    throttleTimeOff = throttleOpen;
          }
        }
        else {
          throttleTimeOff = throttleHold; 
        }
      }
    }
     
     
     
     
    //
    // Timer_A Interrupt Vector (TAIV) handler for CCR1 and CCR2 capture/compare,
    // and overflow interrupts
    interrupt (TIMERA1_VECTOR) axtimer(void)
    {
      unsigned short v;
      tar = TAR;
      v = TAIV;
      if (v == 2) {  // CCR1 compare
        // if throttle was ON,
        // then the output was turned off at compare,
        // and we must setup to turn it back on at next compare
        if (throttleIsOn) {
          CCR1 = tar + throttleTimeOff;
          // CCTL1 = 0;  // turn OFF throttle
          CCTL1 = CCIE | OUTMOD_1;  // turn on at CCR1
          throttleIsOn = 0;
        }
        else {
          CCR1 = tar + throttleTimeOn;
          // CCTL1 = OUT;  // turn ON throttle
          CCTL1 = CCIE | OUTMOD_5;  // turn off at CCR1
          throttleIsOn = 1;
     
     
        }
      }
      else if (v == 4) { // CCR2 compare
        // we initiate the full-on peak period,
        // we in the interrupt we are doing the pwm hold
        // use a on time of .125ms, offtime of .250ms
        // until the injectorOffTime is reached, then we leave it off
        if (injectorIsOn) {
          // if the on period has been reached, the spray is complete and we leave it off
          injectorIsOn = 0;
          unsigned short onTime = (tar - injectorStartTime);
          // turn back on if still not done with hold time.
          if (onTime < injectorPulseWidth) {
    CCR2 = tar + injectorTimeOff;
    CCTL2 = CCIE | OUTMOD_1;  // turn on at CCR2
          }
          else {
    // keep it off, done
    CCR2 = 0;
    CCTL2 = 0;
          }
        }
        else {
          CCR2 = tar + injectorTimeOn;
          CCTL2 = OUT;  // turn ON injector
          CCTL2 = CCIE | OUTMOD_5;  // turn off at CCR2
          injectorIsOn = 1;      
        }
      }
      else {  // overflow, timer hit 0xffff
     
     
      }
    }
     
     
    unsigned short tdel = 1;
    unsigned short t1 = 0;
    unsigned short t2 = 0;
     
     
    //
    // MAIN
    // 
    int main(void)
    {
      int x;
      unsigned short s;
      
      WDTCTL = WDTPW + WDTHOLD;             // Stop WDT
      DCOCTL = 0xE0;   // DCO=7, MOD=0
      BCSCTL1 = 0x07;  // RSEL=7    == 5Mhz for F1232
      // BCSCTL1 = (DIVA1 | DIVA0) | 0x07;  // Aclk/8 32k now 4k, RSEL=7,  4Mhz at 3v
     
     
      // check if we have a working 32k ACLK
      TACTL = TASSEL_ACLK + TACLR + MC_CONT;
      delay(1200);  // wait
      if (TAR == 0) {
        Reboot(); // can't run without working 32k clock, wait for it to warm up
      }
      P1OUT = 0x00;                     // Clear
      P2OUT = 0x00;                     // Clear
     
     
      //
      // copy from flash into memory sys config settings
      // check for bootstrap uninitialized values
      FConfig = (struct ConfigEFI *)0x1000;
      Config = *FConfig; 
      if (Config.valid == 0xFF) {
        Config.valid = 0;    
        SaveConfig();
        Reboot(); // can't run without working 32k clock, wait for it to warm up
      }
      
      // ADC10ON and 64x sample and hold time.
      // 2.5V reference
      // ADC10CTL0 =  REF2_5V | REFON | ADC10ON | ADC10SHT_3 | SREF_1;
      // ADC10ON and 16x sample and hold time.
      ADC10CTL0 =  REF2_5V | REFON | ADC10ON | ADC10SHT_2 | SREF_1;
      //
      // ADC option select
      // P2.0=A0 
      // P2.1=A1 O2
      // P2.2=A2 Dial
      // P2.3=A3 WaterTemp
      // P2.4=A4 RPM Dial
      // P3.6=A6 MAP
      // P3.7=A7 TPS Throttle Position
      //
      ADC10AE = 0xDF;
     
     
      // P2.5  tach input
      // P1.0  board button  input
      // P1.1  fuel pump relay output
      // P1.2  throttle 
      // P1.3  fuel injector  
     
     
      P1DIR = 0x0E;   // output for P1.1,2,3, input for P1.0
      P1OUT = 0x00;
      P2DIR = 0x00;
     
     
      P1IES = 0x00;  // init with high-low transition, so button must 1st go down
      P1IE  = 0x01;  // enable interrupts on P1.0
      
      P2IES = 0x00;  // init with high-low transition for TACH signal
      P2IE  = 0x20;  // enable interrupts on P2.5
     
     
      // PWM output for charge transistor control
      TACTL = TASSEL_ACLK + TACLR + MC_STOP;
      P1SEL = 0x0C;     // P1.3 is TimerA2 output for Throttle,  P1.2 is TimerA1 for Throttle
     
     
      // P1.0 is Button input, P2.3 is select dial
      initLCD();
     
     
      // Turn on LCD Display
      LCDout(0, 0x0C);  // Display On, no Cursor, no Blink
      delay(750*20);
      LCDout(0, 0x06);  // Move cursor to right
      LCDout(0, 0x01);  // Clear Display
      delay(750*20);
     
     
      //
      // initialize variables used by watchdog now before enable interrupts
      //
      tach = 0;
      selecting = 0;
      selectime = 0;
      seconds = 0;
     
     
      CCR1 = 0;
      CCR2 = 0;
      CCTL2 = 0x00; //init off
      CCTL1 = 0x00; //init off
      TACTL = TASSEL_ACLK + TACLR + TAIE + MC_CONT; // start timer
     
     
      //
      // see if button is down, if so we go into edit mode,
      // otherwise, get ready to run the engine
      //
      if (1) { // ((P1IN & 0x1)==0) {  // p1.0 is down
        LCDouts("   Nimble");
        LCDout(0, 0xC0); // position cursor at char 8  
        LCDouts("Motorsports, LLC");
        for(x=0; x<20; x++) {
          delay(25000);
        }
        LCDout(0, 0x01);  // Clear Display
        delay(750*20);  
        LCDouts("    Electronic      ");
        LCDout(0, 0xC0); // position cursor at char 8  
        LCDouts(" Fuel Injection 1.0 ");
        for(x=0; x<20; x++) {
          delay(25000);
        }
      }
     
     
      P1IFG = 0;  // clear button interrupt
      
      //
      // we are ready to run the engine, turn on the fuel pump
      //
      P1OUT = 0x02;
      
      WDTCTL = WDT_ADLY_1000;               // Set Watchdog Timer interval back to 1000 (1-sec)
      // WDTCTL = WDT_ADLY_250;
      IE1 |= WDTIE;                         // Enable WDT interrupt
      // _EINT();                              // Now Enable interrupts  
     
     
      // Init throttle PWM
      // CCR0 = 0xFFFF;
      // TACTL = TASSEL_SMCLK + TACLR + TAIE + MC_UPTO_CCR0; // start timer    
      TACTL = TASSEL_ACLK + TACLR + TAIE + MC_CONT; // start timer
      //
      // crack open the throttle for starting
      //
      throttleIsOn = 1;
      throttleTimeOn = 640;
      throttleTimeOff = 3200;
      CCR1 = throttleTimeOn;
      CCTL1 = OUT;  // turn ON throttle
      CCTL1 = CCIE | OUTMOD_5;  // turn off at CCR1 and interrupt
     
     
     
     
      injectorBaseWidth = 9*32;   // 9ms
      injectorPulseWidth = injectorBaseWidth;
      injectorTimeOff = 32;
      injectorTimeOn = 32;
      dstate = 0;
      
      _EINT();  // Now Enable interrupts
     
     
      while (1) {
        selecting = 0;      
        //  Sleep until next interval status update
        //_BIS_SR(CPUOFF);                 // Enter LPM3
        //_NOP();
     
     
        // we wake up when ignition event occurs,
        // recalculate the injector on time
        
        updateInjectorWidth();
     
     
        t1 = TAR;
     
     
        updateThrottle ();     
     
     
        t2 = TAR;
     
     
        dstate++;
        // do display in tiny steps
        if (dstate == 1) {    
          LCDout(0, 0x80); // A position
        }
        else if (dstate == 2) {
          //LCDouts("F:");
          //val = adcRead(0x0A);
          LCDouts("R:");
        }
        else if (dstate == 3) {
          LCDout_adc(rpm);
        }
        else if (dstate == 4) {
          LCDouts(" I:");
        }
        else if (dstate == 5) {
          LCDout_adc(injectorPulseWidth);
          // LCDout_adc(throttleTimeOff);      
        }
        else if (dstate == 6) {
          LCDout(0, 0xC0);  // Move Cursor to position for this charger
        }
        else if (dstate == 7) {
          LCDouts("T:");
        }
        else if (dstate == 8) {
          // LCDout_adc(waterTemp);
          LCDout_adc(tps);
        }
        else if (dstate == 9) {
          LCDouts(" D:");
        }
        else if (dstate == 10) {
          LCDout_adc(targetTPS);
        }
        else {
          dstate = 0;  // restart
        }
        t2 = TAR;
     
     
        // see if we woke up from button press interrupt
        if (selecting > 0) {
          selectime = 0;
          // because of stack overflow problem, we return from buttonPress
          // and execute any editing choice here.
          x = buttonPress();
          if (x == 0) {
    LCDout(0, 0x0C);  // On, no Cursor, no Blink
    selecting = 0;
          }
          else if (x == 1) {
    // sysSettings();
    // SaveConfig(0);
          }
          else if (x < 5 ) { // edit settings, 2,3,4
    // editSettings("A", x-2, &cset);
    // SaveConfig(); 
          }
          else if (x < 8) { // edit settings   5,6,7
    // editSettings("B", x-5, &cset);
    // SaveConfig(); 
          }
        }
      }
     
     
    }
  13. Like
    swampdonkeykami reacted to abecedarian in stepper position feedback options?   
    Okay, I know I'm not going to be explaining it properly (10 shots of whisky isn't helping), but maybe this wall of text might make sense.
     
    Hydraulic pumps are great, positive displacement, double-lip seals and thick packing helps leakage- measure the volume of fluid pumped and if you know the volume of the cylinder you can calculate position. I think you're trying to avoid pressure relief valves- that simplifies the plumbing and also prevents blowing out the cylinder packing.
     
    You will encounter problems with trying to determine position from flow: no matter how well the pump and metering device are sealed, there will be leakage and possible back-flow when the pump is not running and the cylinder is under stress from compression (being pushed back) or extension (being pulled out). This will make your calculations drift over time unless you recalibrate periodically: run the hydraulic cylinder all the way in, zero the count, run it out and measure the flow volume to reach full extension. You could then, maybe, scale things accordingly and have reasonable accuracy calculating the cylinder's position, given you know how far the cylinder will travel for a particular volume of fluid pumped in or out.
     
    To 'sort of' make my point:
    Cranes do not rely on how many times a pump has turned to determine how far the crane boom has extended. They use something like what hvontres suggested- a known, fixed-length spool of cable that unwinds as the boom extends and retracts with the boom and uses a rotary encoder to determine position based on the spool's rotation and knowing that the cable is under no significant tension and won't stretch, which if it did stretch that would be analogous to your pump leaking or such).
     
    Granted, that is an extreme case where the crane boom extension can be anywhere from a few meters to 100 meters or more, but I'm guessing that is probably far more than you need.
     
    My opinion is that a linear position encoder mounted to the hydraulic cylinder would probably be your best bet.
  14. Like
    swampdonkeykami reacted to hvontres in stepper position feedback options?   
    A DC motor (either brushed or brushless) will need some kind of feed back to control position. With a stepper, the encoder can be used for servo like control, or it can be used to detect missed steps after the motion is done. This way, the motor is run in open loop and if there were any missing steps during the move, you could command a couple of extra steps at the end. With a stepper, you are more likely to loose steps at higher speeds, since motor torque falls off pretty rapidly.
     
    Having worked around servo systems for the last 18 years, I would not reccomend them for the uninitiated. Proper tuning and hookups can be a real pain without the propper tools. Also, the effects of feedback failures can be very spectacular and dangerous (imagine an axis flying towards you with full torque applied after the encoder fails and then hitting the end stops with a loud bang... that'll wake you up) But for the really adventurous, they do offer the potential for more performance. One of these days I should set up some articles on how a servo system works and can be tuned.... but that would be a bit too much like work
  15. Like
    swampdonkeykami reacted to Lgbeno in stepper position feedback options?   
    In my mind a servo implies feedback.
     
    I think it's best to use some sensor to ensure a hydraulic valve position to guard against Mis steps and glitches like you mentioned. Even in a stepper based system. The control will need to be able to tolerate the resolution of the step size though otherwise it will oscillate if it is trying to reach a position between two steps.
     
    One other sensor type for linear position would be a lvdt transducer which is used in aerospace hydraulic actuators.
  16. Like
    swampdonkeykami reacted to hvontres in stepper position feedback options?   
    I would suggest a shaft encoder, especially once the new Tiva Launch pads get out. That one will have TWO quadarature counters on board, so interfacing to the encoder should be very simple. As far as controlling the position of a Hydraulic cylinder, you might be better off getting your feedback from the rod itself. One way that I played with years ago while working on the animation system for a Rose Parade Float (http://www.csupomona.edu/~library/specialcollections/rosefloat/floatimages/1993photo.html) is to use a 10 turn pot with a torstion spring, a large wheel and some steel cable. As the rod extends, the pot turns and you get absolute position feedback from the cylinder. I think if you try to just measure the flow, you will integrate up large errors.

  17. Like
    swampdonkeykami reacted to username in Ultrasound Garage Car Stopper   
    A Ultrasonic car stopper project I made for my garage to inform as to how far I should pull my car forward. This project uses a MSP430 launchpad and a HSC - 04 ultrasonic range module as well as a RGB LED and a photosensitive resistor.
     
    Youtube video: (uploading)


     
     
    Schematic here: garage_msp430.pdf
     
    Source here:
    (i'll clean it up in abit, was having trouble with my debugger hence all the globals)
    /*  * Author: Nathan Zimmerman  * Date: 6/22/13  *  * Description: A simple program to turn on a LED with the HC-SR04  * module to turn on a LED if the module detects a object closer  * than the trip point which is set by a pot  *  */ #include "msp430g2553.h" #include "stdint.h" //GPIO Pins #define trigger_pin           BIT2 #define echo_input_pin        BIT1 #define pot                   BIT0 #define bled                 BIT0 #define bled_setup            P2DIR #define bled_output        P2OUT #define bled_setup_on        bled_setup |= bled #define bled_on               bled_output |= bled #define bled_off            bled_output &=~bled #define rled                 BIT1 #define rled_setup            P2DIR #define rled_output        P2OUT #define rled_setup_on         rled_setup |= rled #define rled_on               rled_output |= rled #define rled_off            rled_output &=~rled #define gled                 BIT2 #define gled_setup            P2DIR #define gled_output           P2OUT #define gled_setup_on        gled_setup |= gled #define gled_on               gled_output |= gled #define gled_off             gled_output &=~gled //Ultrasound parameters #define timer_period        62500 // 4 * 62500 = 250ms #define trigger_pulse        (timer_period - 5) #define us_per_cm            58 #define time_to_trigger_us   450 #define distance_check        100 #define count_clk_divider    4 #define bad_measurement       40000 #define max_distance         400 //centimeters //Statics static uint16_t echo_pulse_counts = 0; static uint16_t distance_set_value =0; static uint16_t adc_val=0; static uint16_t distance=0; //Functions void clk_setup_1mhz(); void setup_trigger_pulse(); void setup_gpio_echo_interrupt(); void setup_adc_input(); uint16_t compare_distance_vs_threshold(); uint16_t get_adc_counts(); uint16_t get_distance_cm(); //Main void main(void) {     clk_setup_1mhz();     setup_trigger_pulse();     setup_gpio_echo_interrupt();     setup_adc_input();     bled_setup_on;     gled_setup_on;     rled_setup_on;     bled_off;     gled_off;     rled_off;     while(1)     {             distance = get_distance_cm();             if(distance)    //Check for out of range measurement             {                 if(distance<compare_distance_vs_threshold())                 {                     gled_off;                     rled_on;                 }                 else                 {                     gled_on;                     rled_off;                 }             }         }     } } // End of main void clk_setup_1mhz() {     BCSCTL1 = CALBC1_1MHZ;     DCOCTL = CALDCO_1MHZ;     WDTCTL = WDTPW + WDTHOLD; } void setup_trigger_pulse() {     P1DIR |= trigger_pin;     P1OUT &= ~trigger_pin;     P1SEL |= trigger_pin;     CCR0 = timer_period;     CCTL1 = OUTMOD_7;     CCR1 = trigger_pulse;     TACTL = TASSEL_2 + MC_1+ ID_2;     __enable_interrupt(); } void setup_gpio_echo_interrupt() {     P1DIR &= ~echo_input_pin;     P1OUT &= ~echo_input_pin;     P1IE |= echo_input_pin;     P1IES |= echo_input_pin;     P1IFG &= ~echo_input_pin; } void setup_adc_input() {     ADC10CTL1 |= CONSEQ1;     ADC10CTL0 |= ADC10SHT_2 + ADC10ON + MSC;     ADC10AE0 |= pot;     ADC10CTL0 |= ADC10SC + ENC; } uint16_t get_distance_cm() {     if(echo_pulse_counts>bad_measurement)         return 0;     else         return (echo_pulse_counts*count_clk_divider - (time_to_trigger_us))/ us_per_cm; } uint16_t get_adc_counts() {     return ADC10MEM; } uint16_t compare_distance_vs_threshold() {     uint32_t trip_threshold =0;     adc_val=0;     adc_val = get_adc_counts();     _delay_cycles(1);     trip_threshold = ((unsigned long)0x190*((unsigned long)adc_val))>>10;     _delay_cycles(1);     return (uint16_t)(trip_threshold & 0xFFFF); } #pragma vector=PORT1_VECTOR __interrupt void Port_1(void) {     echo_pulse_counts = TAR;     P1IFG &= ~echo_input_pin;     _delay_cycles(10); }  
  18. Like
    swampdonkeykami got a reaction from username in garden irrigation controller   
    Here's a trivial little controller that uses a Chinese soil moisture sensor, a relay and Adafruit solenoid valve to keep my wife's raised bed garden from dying.
     
    My thinking was that a timer is a stupid way to water a garden. What if it rains all spring (like it has been)? 
     
    The MSP430's analog pin is used to monitor the sensor and then fires the relay to open the solenoid valve if the reading is too low. There's a 30 second delay in the void loop to keep the solenoid from floating... 
     
    http://arduinoforgoodnotevil.blogspot.ca/2013/04/forget-to-water-your-garden-no-problem.html
     
    Unfortunately, after about a week the sensor cathode completely corroded!! I came home to a not so happy wife with a flooded garden! Ooops... it's on a timer until I make (and test) a stainless steel probe.
     
     
  19. Like
    swampdonkeykami reacted to igor in garden irrigation controller   
    This paper might be of interest, apropos capacitive moisture sensing. (Has RF communications also.)
     
    Boston University College of Engineering, Department of Electrical and Computer Engineering WiGreen Soil Moisture Sensing System, MCL Technical Report: TR-05-02-2006 Kurt Matarese Gregg Fischer Naman Gupta Philip Kim  Prof. Thomas Little (Sponsor)   December 15, 2006   http://hulk.bu.edu/pubs/papers/2007/TR-05-02-2007.doc     Aside about resistive sensing - why measure so frequently?  (one comment talked about every minute).   Can't you get by with measuring maybe once every hour, or every few hours? Things I have seen about using two nails talk about replacing the electrodes once a year.   (Of course this probably depends on your weather and soil properties.)
  20. Like
    swampdonkeykami reacted to spirilis in G2955 Game Pad   
    Ok so this is an idea I've had in my mind ever since the Arduino Esplora came out.  It's not an exact comparison since the G2955 doesn't have native USB (so you can't use this as a game controller necessarily) but it does have lots of pins and plenty of Flash + generous RAM to do the job IMO, also doesn't require anything super fancy like MSP430X support so the current stable release of mspgcc will work with it.
     
    The idea is a MSP430G2955-based game pad with a standard 20-pin BoosterPack pinout in the middle for the LCD display.  Other LCD boosterpacks would have to be sourced to add the display (similar to the Arduino Esplora, although I don't know if the headers in the middle of that one is remotely compatible with the TI BoosterPack standard... the pin count is oddly similar though).
     
    This allows easy expansion; sandwich other boosterpacks in between or maybe put them on the back-side, and have your LCD boosterpack of choice on top.  Easily upgrade your "game" by using a fancier color LCD or have a simple interactive monitoring system with a cheap B&W like the Nokia 1202.
     
    This is the general layout I had in mind:

     
    Features would include:
    MSP430G2955 with 32.768KHz XTAL BoosterPack layout similar to RobG's G2955 LaunchPad board, with the exception of P2.6/P2.7 in the upper right being replaced with other pins so P2.6/2.7 can be dedicated to the XTAL
    Port 1 dedicated to 8 HW-debounced pushbuttons; digital pad up/down/left/right, a "B" and "A" button on the right side, and "L" and "R" up top on the backside of the PCB.
    6-pin FTDI header for UART work, should also be capable of powering the board by feeding the Vcc line into an LDO regulator.
    USB connector for power only (feed into same LDO as the FTDI header).
    Some sort of connector, JST or whatever, for connecting LiPoly or AA battery pack (I bought some sort of 2xAA battery pack from Seeed that has a JST-style connector)
    Buzzer (connected to P4.1, Timer_B CCR1)
    Microchip 23LC512 (64KB SPI SRAM) for auxiliary storage of sprites or whatnot.  64KB is still addressable with 16-bits so it should be reasonably code efficient to use.
    WS2811 LED or 2 connected to P4.7 for miscellaneous use.
    On/Off switch connected to P2.2, an interrupt-capable pin that can be used by firmware to "turn off" the unit (put all HW into sleep/powerdown mode and enter LPM4 or LPM3 if the gamepad also intends to keep a clock)
    50-mil eZ430 header on the left side for easily plugging into an MSP430 LaunchPad and reprogramming.  Kit would include matching female & male 50-mil headers.  Will have to ponder location... might put it up on top instead so it's out of the way of your hands.
     
    The general layout for the LaunchPad pins in the center would be like this:
     
    (J1)
    Vcc
    P2.0 (A0)
    P3.5 (UCA0RXD)
    P3.4 (UCA0TXD)
    P4.6 (A15, CA6)
    P3.0 (UCB0STE, UCA0CLK, A5)
    P3.3 (UCB0CLK, UCA0STE)
    P4.5 (TB0.2, A14, CA5)
    P4.4 (TB0.1, A13, CA4)
    P4.3 (TB0.0, A12, CA3)
     
    (J2)
    GND
    P3.6 (TA1.1, A6)
    P2.1 (A1)
    TEST
    RESET
    P3.1 (UCB0SIMO, UCB0SDA)
    P3.2 (UCB0SOMI, UCB0SCL)
    P2.4 (VREF+/VeREF+, TA0.2, A4)
    P2.3 (VREF-/VeREF-, TA0.1, A3)
    P3.7 (TA1.2, A7)
     
    A lot of those pins aren't interrupt capable (only P1.x and P2.x are interrupt-capable), but this layout does follow TI's BYOB standard -- http://processors.wiki.ti.com/index.php/BYOB
     
    Other functions as listed:
    P4.0 - SPI SRAM HOLD
    P4.2 - SPI SRAM ChipSelect
    P2.2 - On/Off switch
    P4.7 - WS2811 LEDs
    P4.1 - Buzzer (TB0.1)
    P2.5 - RTS input from FTDI header
    P3.7 - General purpose 1206 SMD LED w/ 1K resistor inline (TA1.2) (could be used for power or on/off signal, simpler to interface than the WS2811's)
     
    While the obvious use-case of this is as a game pad, it's also useful as a practical control interface for other analog or digital sensor projects that you can configure "in the field" using the UI buttons and LCD.
     
    So whatcha think?
  21. Like
    swampdonkeykami reacted to JWoodrell in garden irrigation controller   
    You need to a capacitive sensor rather than a resistive one, otherwise it will always electroplate your sendor with crud. The capacitive ones change capacitance dependind on how much water is around them.
     
    You might be able to use a 555 timer to do the capacitance based pulsing in hardware the more water, the higher the capacitance, tge slower the 555 wil cycle
  22. Like
    swampdonkeykami reacted to roadrunner84 in garden irrigation controller   
    Here's my 2 cents: since the probe is symmetrical, you could bind both pins to analog input enabled pins, then once every minute or so you set one pin to digital high and measure the other pin using analog read. The next minute you switch the roles of the two pins. This way you don't have the probe powered all the time, plus you spread the load over the two pins. Also, most PCB fabs offer ENIG gold plating for your circuits, this would make the probe live virtually forever.
    //pseudo code bool forwardMeasure; void setup() { set up timer to fire every 30 seconds or so attach interrupt routine "Measure" to the timer pinMode(Probe1, INPUT); pinMode(Probe2, INPUT); LPM3; // Go to sleep, the timer will wake us up twice a minute } void loop() { // loop should not be executed at all LPM3; // if it does, go to sleep } void Measure() { int A, B; forwardMeasure = !forwardMeasure; // flip the forwardMeasure boolean if (forwardMeasure) { A = Probe1; B = Probe2; } else { B = Probe1; A = Probe2; } digitalWrite(A, HIGH); analogRead(; // Do this first to enable the ADC pin delay(20); // wait a little to measure the probe int x = analogRead(; // then actually measure pinMode(Probe1, INPUT); // Set the probe pins to floating again pinMode(Probe2, INPUT); // Set the probe pins to floating again if (x < WET) { digitalWrite(Solenoid, HIGH); } else { digitalWrite(Solenoid, LOW); } }
  23. Like
    swampdonkeykami got a reaction from yyrkoon in garden irrigation controller   
    Here's a trivial little controller that uses a Chinese soil moisture sensor, a relay and Adafruit solenoid valve to keep my wife's raised bed garden from dying.
     
    My thinking was that a timer is a stupid way to water a garden. What if it rains all spring (like it has been)? 
     
    The MSP430's analog pin is used to monitor the sensor and then fires the relay to open the solenoid valve if the reading is too low. There's a 30 second delay in the void loop to keep the solenoid from floating... 
     
    http://arduinoforgoodnotevil.blogspot.ca/2013/04/forget-to-water-your-garden-no-problem.html
     
    Unfortunately, after about a week the sensor cathode completely corroded!! I came home to a not so happy wife with a flooded garden! Ooops... it's on a timer until I make (and test) a stainless steel probe.
     
     
  24. Like
    swampdonkeykami reacted to yyrkoon in Yet another N00b programming question   
    if you want that to fire off 5 times though it should be more like
    while(retry >= 5) or while(retry > 6)
  25. Like
    swampdonkeykami reacted to yyrkoon in Yet another N00b programming question   
    Something like this . . .
    while(retry > 5){    digitalWrite(forwardPin, LOW);    //pin action delay here    digitalWrite(reversePin, LOW);    // pin action delay here        retry++; }
×
×
  • Create New...