Jump to content


  • Content Count

  • Joined

  • Last visited

  • Days Won


Reputation Activity

  1. Like
    tripwire reacted to enl in Reversing the LED? i.e. sinking it to MCU?   
    See SLAS735 (MSP430G2X53 datasheet, or other device datasheets) for the graphs. SLAS735G, fig 6-9, "Typical characteristics, Outputs" shows the curent vs output potential vs supply potential.
    The MSP430 devices are pretty much symmetric with respect to sourcing and sinking. Driving at too high a current may not *immediately* damage an output, but driving an LED without a limiting resistor (or current limiting driver) will likely either smoke the LED or damage the I/O in the microcontroller eventually. The damage may not happen immediately, but the drive transistor will be dissipating power due to the potential drop, and will fail sooner or later due to the cumulative effect of the heat. A good rule is no more load than keeps you within the safe spec for logic signals, which is about 12mA at 3V for the MSP430G series. Half that is better. Many LEDs will fail very quickly even with fairly small overcurrent.
    Remember, the I-V curve for an LED, like any diode, is substantially exponential. Small increase in potential, large increase in current.
    Driving individual LEDs isn't a big deal these days. At 4mA (the drive current for the red LED on the launchpad), modern high efficiency devices are as bright as or brighter than the 20mA devices 20 years ago. Not lighting bright, but display and indicator bright, even outdoors during the day. Blinding in a poorly lit room.
    Any more current than this, it is best to use an external driver (NPN transistor to ground with limit resistor; current limiting LED driver IC; etc). When driving multiple LEDs from an output (multiplex display, etc) you pretty much need to use external drivers, as, even when PWM'd to low *average* current, the instantaneous current will be an issue.
    Also note that overloading the GPIO pins, for example with an LED, may effect UART by dropping the internal power bus or raising the internal ground bus: the UART output won't go full supply range. If all you are doing is lighting LEDs, not a big deal until the core can't run anymore (3V supply, that can be as low as 35 to 40mA total, depending on the temperature)
  2. Like
    tripwire reacted to rockets4kids in Reversing the LED? i.e. sinking it to MCU?   
    In the olden days -- long, long ago -- the final drive circuitry for many logic parts could source more current than it could sink.  In most cases with modern parts this is no longer the case.
    You can always find specifications in the datasheet.  If you cannot find them, you are looking in the wrong datasheet.  Always be careful when only a single value is specified -- pay attention to what that number means!  In some cases, that is the limit to what the chip can take without damage.  In others, it is the limit to which it can maintain the proper output voltage levels.
    For some parts (such as the msp430) you will not find per-pin limits.  When this is as case, you will find a graph that shows the output voltage with respect to current.  Pay particular attention to how little current the msp430 parts can source/sink before the voltage levels change.  While the msp430 can only supply about 6 mA per pin and maintain proper logic level voltages (depending on operating voltage) it does seem as if it is safe to drive parts such as LEDs at higher currents when you aren't concerned about output voltage.  That is to say you won't damage your msp430 pin by driving an LED at 20 mA.  However, just because you can do this without damaging the pin doesn't mean you should design a circuit this way.
    Finally, be sure to pay attention to the *total* amount of current the chip can supply to all pins.  It is often *much* less than the total current per pin multiplied by the number of pins.
  3. Like
    tripwire reacted to KatiePier in How to define a pin in Code composer studio   
    Hi @@morelius21,
    CCS doesn't really use the PxOUTbit.PxOUT format - I actually do not see many people use this format even with IAR because I think it is a bit inefficient if you want to set up a whole port at once.
    Instead I usually do something like this:
    P1OUT |= BIT0; //to set the bit or
    P1OUT &= ~BIT0; //to clear the bit If I want to make a mnemonic to make this easier to read what pins go with what functions on my board, I would do something like this:
    #define LCD_OUT   P1OUT #define LCD_4     BIT0 Then you can use statements like these, which will evaluate the same as the code I put before:
    LCD_OUT |= LCD_4; //set the bit LCD_OUT &= ~LCD_4; //clear the bit What's nice about using these kind of operations is that you can set or clear multiple bits at once too:
    #define LCD_OUT   P1OUT #define LCD_4     BIT0 #define LCD_5     BIT1 ... LCD_OUT |= LCD_4 + LCD_5; LCD_OUT &= ~(LCD_4 + LCD_5); There are a whole bunch of other ways you can do this too - it's all personal preference really.
    Hope this gives you some ideas!
  4. Like
    tripwire got a reaction from abc in Demographics of embedded development   
    Further to wasson65's reply, I'd suggest it's not helped by the move to higher-level languages in Computer Science courses.
    Embedded programming's emphasis on runtime performance, safety/reliability and minimal footprint encourages the use of lower-level languages. There's a gradual shift from assembly to C and C++ for embedded work, but it's behind the curve of programming in general.
    Although CS courses tend to focus on the theoretical side of Computer Science, specific languages are usually taught in the first year. For a lot of students these languages become their default choice when completing practical assignments. If you've spent 3 or 4 years of your life programming in Java or C# then the switch to assembly or even C can come as quite a culture shock.
    When I was at university the programming tuition had just switched from C to C++ with STL. The migration path back from C++ down through C and assembly is pretty clear; you just stop using the higher level features of C/C++/STL and treat the compiler like a more advanced assembler. Then you start reading the disassembly of your programs to see what's going on under the hood. I wouldn't even know where to begin that process if I had to start with a language that runs bytecode in a virtual machine.
    Something that may help to reverse the trend is the easy availability of cheap embedded development platforms (eg MSP430 Launchpad ). The information you need to get started is much more easily available these days, and there's better visibility of hobbyist embedded developers who can act as role models.
  5. Like
    tripwire reacted to pabigot in MSP430 vs Silicon Labs efm32   
    I think it'd depend on duty cycle and requirements, and how much effort gets put into the software design. My impression is MSP430 will be cheaper and EFM32 has more capability and is faster. The Wolverine (e.g. MSP430FR5969) has really low power, comparable to Zero Gecko; I'm seeing 250 nA for LPM3.5 with an EXP430FR5969 application that wakes periodically on RTC alarms. I expect to have numbers from an apples-to-apples comparison between the two in the next week or two.
  6. Like
    tripwire reacted to wasson65 in Demographics of embedded development   
    I'm no spring chicken (45) and this is my guess:  When I started programming (34 years ago...), being a computer user almost always meant being a computer programmer, and being a computer programmer almost always meant that you had to have knowledge of the systems you were programming on.
    In other words, I think 25 years ago, there was naturally more overlap between programmers and those interested in electronics and hardware.  I mean, people used to purchase ram chips and put them into ISA boards with sockets on them to expand their memory.
    Now, people's phones are multiple orders of magnitude more powerful than those early computers were.  And there are lots of computer programmers who have learned how to program in high level languages and never have had to actually worry about twiddling bits outside of some basic lab in college.
    So, a large percentage of the 'programming' population has never had to think much about memory usage, understand how data is actually stored, understand what 'endian' means.  The connection to any particular piece of hardware has been abstracted away under so many layers of layers, that somehow it's thought that actually touching hardware is somehow mysterious and difficult.  
    Additionally, there is another thing: VLSI.  When the circuit boards are just two layers, and the chips don't have hundreds of millions of gates, it was actually possible to look at a physical circuit board, look at the traces, look at the connectors, look at the chips, and almost extract the entire logical schematic.  Today's schematics are almost useless as they simply show 250 lines going into one big block marked U0.  How do you even begin to understand it by observation?  There's no way.  The bottom rungs of the ladder have been knocked off.
    Sad.  I'm so thankful I was born when I was, because any earlier, and I would not have had my own computer at a very young age, and any later and the computer would have been so powerful it would have been nothing more than a magic box.
  7. Like
    tripwire reacted to Rei Vilo in floating point - Math - and Trigonometry with msp430g2553   
    For intensive calculations, I'd recommend the Tiva C Series LaunchPad as the TM4C123 includes a FPU=Floating-Pont Unit , enabled by default by Energia. Because it is hardware, it runs very fast.
    On the MSP430, the float library takes 6KB out of 16KB, so you'll need to use approximations with integers to run fast and save memory.
  8. Like
    tripwire reacted to enl in MSP 430 Timer Logic?   
    For longer periods, where the divider still won't get you enough time, You can also use a counter in the interrupt routine. Initialize to the number interrupts you need in a cycle,, decrement each interrupt, and, if not zero, return. If it is zero, do your task and reset the counter.
    // counting interrupts example _interrupt void TIMER0_A0_ISR(void) { const unsigned cycle=24; // presuming 24 interrupts is a cycle static unsigned icount=cycle; // never reinitialized after program initialization if (--icount) return; P1OUT ^= 0x01; icount = cycle; return; } Your total period needs to be factored into two values: the timer cycle and the number of interrupts per operation cycle. With care, the overhead can be minimized
  9. Like
    tripwire reacted to spirilis in MSP 430 Timer Logic?   
    Adding to Peter's description, the TA0CTL register can take some ID_x bits, ranging from ID_0 to ID_3, which divide the timer's clock source by 2^(ID value) before using it.
    E.g. ID_3 would divide the 1MHz clock by 8.
  10. Like
    tripwire reacted to pabigot in MSP 430 Timer Logic?   
    Remember you're working with a 16-bit microcontroller: the maximum value you can assign to TA0CCR0 is 65535, which is about 65ms at 1MHz clock. Your 1045000 decimal is 0xFF208 hex which truncates to 0xF208 16-bit which is 61960 ticks or 59ms.
    You need to use a slower (or divided) clock source if you want alarms at longer periods. VLOCLK at about 8-12 kHz would work as you could delay up to about 5 seconds but with poor accuracy; preferably use a 32 kiHz crystal feeding ACLK.
  11. Like
    tripwire reacted to wasson65 in Analyzing blinky's disassembly   
    In a nutshell:
    Programming in C:  Now why did that stupid compiler do that in a wasteful and ultra-careful way?
    Programming in Assembly: Now why didn't that stupid programmer spend 4 extra cycles to make that routine generally safer?
    Once upon a time, there were people who coded PC applications in assembly because it made for faster programs.  Then the machines got faster, compliers got a little better, and assembly really wasn't worth it any more on PC's.
    Because libraries have to work (generally) on multiple architectures, and programs (generally) have to co-exist in memory and systems (generally) are multi-tasking, and interrupt routines (generally) must leave the environment exactly as they found them, there are things that the compliers are going to do, just to be 'good citizens'.  A good part of this 'good citizenship' can be dropped if you have intimate knowledge about the environment that the code is going to have to run in, which we (generally) have.
    And when the amount of application logic is large compared to the amount of hardware specific logic, these tradeoffs make a lot of sense.
    On our little '430s the hardware specific logic is probably 80% of our entire codebase, so the generalizations can begin to consume a non-trivial amount of space and cycles, which, as you know, we never have quite enough of....
    Viva la Assemblia!  - lol
  12. Like
    tripwire reacted to pabigot in Analyzing blinky's disassembly   
    BTW: The value in __watchdog_support() is not supposed to be WDTHOLD. If you choose to turn off the watchdog in main() (as your program does), that's fine, but the toolchain should not make that policy decision for you. Instead it preserves the power-up setting, and uses the value it saved on startup to ensure the watchdog does not fire during the startup code.
  13. Like
    tripwire reacted to PedroDaGr8 in Wera Screwdriver Sale   
    Was browsing GarageJournal and came across this. KC Tool Co is having a sale on some Wera Screwdrivers:

    Wera Precision Screwdriver Set A $20.99
    Wera Precision Screwdriver Set B $20.99

    Wera Kraftform PLUS 900 series Set $34.99

    Wera Kraftform PLUS 300 Series Set $26.99

    Plus some others:
    Sale Link

    These screwdrivers seem like the perfect sets for your usual electronics hobbyist. Wera is one of the top screwdriver makers in the world (Wiha, Wera, PB Swiss, etc. etc.) Note I emphasized the PLUS on two of the kits because they look the same as the non-plus versions. They Kraftform 300 sets have the laser etched tips that supposedly help the bits bite better into screws.
  14. Like
    tripwire reacted to pabigot in An exercise in advanced C semantics   
    Dealing with memory mapped registers, and ensuring the consistency of shared values in the face of interrupts, often requires use of the volatile qualifier in C.

    Given the following code:
    typedef struct sFIFO {   volatile uint16_t head;   volatile uint16_t tail;   const uint16_t length;   volatile uint8_t buffer[1]; } sFIFO; #define FIFO_ADJUST_OFFSET_NI(fp_,v_) ((v_) % (fp_)->length) static __inline__ int fifo_push_head_ni (sFIFO * fp, uint8_t v) {   uint16_t h = fp->head;   /* ... */ #if PICK_ONE   h = fp->head = FIFO_ADJUST_OFFSET_NI(fp, 1 + h); #else   fp->head = h = FIFO_ADJUST_OFFSET_NI(fp, 1 + h); #endif   /* ... */ } consider these questions:
    Does the value of PICK_ONE affect the code generated for the statement variant it selects? Does your answer hold for all C compilers? Can you find the part of the C11standard (draft N1570) that supports your answers to the first and second questions? If you're not interested in guessing, the answer for GCC is here, and text that supports that decision explicitly is in footnote 111 at section 6.5.16 paragraph 3 of N1570 (ISO/IEC 9899:2011), possibly supported by the last sentence of section 6.7.3 paragraph 7.

    I was surprised by the answer.
  15. Like
    tripwire got a reaction from jpnorair in Products using MSP430   
    KickSat is currently due to launch this weekend, on 30th March. Assuming all goes to plan there should be over a hundred MSP430s in low earth orbit, for a few days at least
    EDIT: Launch delayed again
    EDIT2: KickSat is in orbit! The individual sprites aren't deployed from the cubesat yet. I think the plan is to do that on April 30th (430 day )
  16. Like
    tripwire got a reaction from bluehash in Products using MSP430   
    KickSat is currently due to launch this weekend, on 30th March. Assuming all goes to plan there should be over a hundred MSP430s in low earth orbit, for a few days at least
    EDIT: Launch delayed again
    EDIT2: KickSat is in orbit! The individual sprites aren't deployed from the cubesat yet. I think the plan is to do that on April 30th (430 day )
  17. Like
    tripwire reacted to pabigot in const, infomem flash, and volatile keyword   
    "const volatile" is an odd combination, but as you found it's what's necessary there.  (Or neither const nor volatile, which would probably work too.)
    FWIW, read-only special-function registers like P1IN are marked const volatile.
  18. Like
    tripwire reacted to spirilis in const, infomem flash, and volatile keyword   
    Just thought I'd share a funny debugging session I just went through.  Reminds me of @@Rickta59 's post about the compiler "optimizing" things yesterday...
    So I've never really used the infomem in any of my projects, but, I have a specific need that would be good; Having my WS2811 LED strip water jug "remember" the last RGB color sent to it so it turns back on automatically after its power has been tripped.  Simple enough, I reserve space in Info_B for the R, G, B value and I decided to include a 16-bit "password" that is read to determine if there is valid information there.
    FYI all my projects use MSPGCC with -Os, so optimization is enabled.
    Seemed straightforward, I went ahead and declared my variables:
    const uint16_t _dmx_password __attribute__((section(".infob"))) = 0x0505; const uint16_t _dmx_red __attribute__((section(".infob"))) = 0x00FF; const uint16_t _dmx_green __attribute__((section(".infob"))) = 0x0050; const uint16_t _dmx_blue __attribute__((section(".infob"))) = 0x0080; As expected, when mspdebug writes the .elf to firmware, the Info_B segment actually gets initialized with those values correctly.  When writing, I cast those variables to (uint16_t *) to get rid of the const so they're writable.
    Inside main(), a function is called to load this info from flash, and if it's not valid, just reset the LEDs all to 0x000000:
    int dmx512_flash_load(uint8_t startcode) { if (startcode != DMX512_STARTCODE) return -1; if (_dmx_password != DMX_PASSWORD_VALID) return -1; dmx512_buffer[0] = (uint8_t) _dmx_red; dmx512_buffer[1] = (uint8_t) _dmx_green; dmx512_buffer[2] = (uint8_t) _dmx_blue; dmx512_update_commit(startcode); return 0; } DMX_PASSWORD_VALID is defined in my dmx512.h:
    /* Flash saving/retrieval of buffer */ int dmx512_flash_load(uint8_t startcode); void dmx512_flash_commit(uint8_t startcode); #define DMX_PASSWORD_VALID ((uint16_t) 0xFEF5) So the initial value is not the same as the intended value.  Next time the user updates the LED, after a short timeout period (during which more updates can happen; so we don't wear out the flash TOO quickly) it commits the new values, including the correct password.
    In mspdebug, I was seeing that happen correctly.  No errors in flash programming.  I even added some volatile uint16_t counters that incremented upon detecting any KEYV|ACCVIFG in FCTL3 during erase or unlock.
    But the "if (_dmx_password != DMX_PASSWORD_VALID)" kept failing.
    So I changed it around to something like this:
    volatile uint16_t temp_pw; int dmx512_flash_load(uint8_t startcode) { if (startcode != DMX512_STARTCODE) return -1; temp_pw = _dmx_password; if (temp_pw != DMX_PASSWORD_VALID) return -1; dmx512_buffer[0] = (uint8_t) _dmx_red; dmx512_buffer[1] = (uint8_t) _dmx_green; dmx512_buffer[2] = (uint8_t) _dmx_blue; dmx512_update_commit(startcode); return 0; } And it was still failing.  What's funny is, while mspdebug showed the contents of 0x1906 (_dmx_password) to be 0xFEF5, temp_pw was showing up as 0x0505 still.
    Couldn't figure that out.  Until I started looking at the ASM:
    00008d36 <dmx512_flash_load>: volatile uint16_t temp_pw; int dmx512_flash_load(uint8_t startcode) { if (startcode != DMX512_STARTCODE) 8d36: 4f 93 tst.b r15 8d38: 02 24 jz $+6 ;abs 0x8d3e return -1; 8d3a: 3f 43 mov #-1, r15 ;r3 As==11 8d3c: 30 41 ret temp_pw = _dmx_password; 8d3e: b2 40 05 05 mov #1285, &0x1c5e ;#0x0505 8d42: 5e 1c if (temp_pw != DMX_PASSWORD_VALID) 8d44: 1e 42 5e 1c mov &0x1c5e,r14 8d48: 3e 90 f5 fe cmp #-267, r14 ;#0xfef5 8d4c: f6 23 jnz $-18 ;abs 0x8d3a return -1; The darned compiler was optimizing away my flash "const" by including the const value in the instruction.
    Funny enough, something about it "feels" wrong when I read it but the solution is:
    volatile const uint16_t _dmx_password __attribute__((section(".infob"))) = 0x0505; volatile const uint16_t _dmx_red __attribute__((section(".infob"))) = 0x00FF; volatile const uint16_t _dmx_green __attribute__((section(".infob"))) = 0x0050; volatile const uint16_t _dmx_blue __attribute__((section(".infob"))) = 0x0080; "volatile const" lol...
    Does it right:
    int dmx512_flash_load(uint8_t startcode) { if (startcode != DMX512_STARTCODE) 8d36: 4f 93 tst.b r15 8d38: 15 20 jnz $+44 ;abs 0x8d64 return -1; if (_dmx_password != DMX_PASSWORD_VALID) 8d3a: 1e 42 06 19 mov &0x1906,r14 8d3e: 3e 90 f5 fe cmp #-267, r14 ;#0xfef5 8d42: 10 20 jnz $+34 ;abs 0x8d64 return -1; because 0x1906 is the correct Info_B address on the MSP430F5172 for _dmx_password, according to mspdebug.  Now my water jug lamp remembers its color setting across power cycles.
    Anyway, just sharing this moment of learning... as it only took me an hour to figure out
  19. Like
    tripwire reacted to rockets4kids in Energy use for interrupts   
    One thing to remember when using interrupts on pushbuttons is debouncing.  If you do not disable interrupts for the duration of the bounce period you will get hit with a chain of interrupts that will almost certainly cause problems.
  20. Like
    tripwire reacted to spirilis in Energy use for interrupts   
    Pullup resistors only draw current when the pin they're attached to is actively being driven somewhere else--like GND.  That only happens when you press the button.  When the button isn't pressed, the pullup resistor draws current only until the total capacitance of its circuit (pin capacitance + any residual capacitance on your board, or any debounce capacitors present if you add them for debouncing purposes) are charged.  Then it draws 0uA current.
    The registers on an MSP430 that configure this are called the PxREN registers, in conjunction with PxOUT.
    E.g. for Port 2.5:
    P2REN |= BIT5;  // Enable pullup/pulldown resistor for P2.5
    P2OUT &= ~BIT5;  // Clearing BIT5 means the PullDOWN resistor is enabled for P2.5, in conjunction with P2REN BIT5 being set.
    P2OUT |= BIT5;  // Setting BIT5 means the PullUP resistor is enabled for P2.5, in conjunction with P2REN BIT5 being set.
    Energia's pinMode() wraps those features using the INPUT_PULLUP keyword.
    There is nothing stopping you from triggering interrupts if pinMode() is set to INPUT, btw.
    It's just generally a good idea to have external circuitry on your board guaranteeing that the pin isn't "floating" where it may casually bounce back and forth between reading a '0' and a '1' (and inadvertently triggering an interrupt at an unpredictable rate).
  21. Like
    tripwire reacted to greeeg in 120 LED Ring Clock   
    I've been working on the code for the control board. Here is a quick demo.

    I've ordered a few more parts (accelerometer, regulator, levelshifter) which my board is running without currently.
    Here is a BOM (not including LEDs)
    Accel (MMA 8453) $1.31 level converter       $0.26 resistor/caps    14x $0.003 regulator         $0.60 32.768kHz crystal  $0.726 MSP430g2553  $2.73 Total: $5.40
    Since the BOM is quite low, I would have no issue populating some of these board for other members, (I would make this a donation to cover BOM+time.) but how about a flat $10USD
    If you'd like to DIY, I can ship the bare PCBs for free to members of the group-buy.
    Also, if you'd like a controller, but don't have any LEDs left, I have a few left.
    @@pyrosster @@dubnet @@t0mpr1c3 have already expressed interest, if you all drop me a PM, that would be much appreciated.
  22. Like
    tripwire reacted to enl in What are you doing right now..?   
    Grading papers and having a beer (or, more accurately, distracting myself from grading papers. Students are doing pretty good, but decoding vector calculus proofs takes a mental toll...)
    Oh, in general....
    Working on an articulated tail, neck, and head. Too many servos! If I ever get anywhere with it, I will post. Right now, still mocking up test system. I can't wait for next haloween......
  23. Like
    tripwire reacted to RobG in What are you doing right now..?   
    Playing Hay Day.                           And regenerating for new and exciting year.
    Oh, and trying to make enough money to buy this thing (you MUST read reviews.)
  24. Like
    tripwire reacted to enl in Compile questions - Routines within ISR   
    This warning comes about because it is generally (but not always) bad practice to call a function from within an interrupt handler. Interrupt handlers should generally be as short as possible and as fast as possible. The function call overhead can slow things down, increasing response time to other interrupts, and add to the stack burden, which is a significant thing when you have limited memory or time critical response.
    I would ask if you are sure that inlining it (or replacing the function with a #define macro) would increase code size. Incrementing a pointer isn't a big deal. Function calls take a bit of code space for setup and stack space for parameters. I presume that there is more than just incrementing a simple pointer, so.....
    That said, there are times when it is perfectly acceptable to call a function within an interrupt handler. For example, if your interrupt is the only one active, and there is no chance of missing the next one because the interrupt rate is known to be low enough the routine will finish before the next interrupt, it is ok. Not best practice, but ok.
    If other interrupts are active, and they can wait for this one to finish, and there is no chance of missing one, it is, again, ok, but not best practice.
    Things to consider: A good compiler can determine the needed stack depth by tracking the call chain. This is not as easy if there are function calls in an interrupt, and may, in fact, be impossible if interrupts are re-enabled within the routine (not recommended on MSP430, IMHO). I don't know off hand if the compiler you are using does this-- I use CCS and have no idea if it does, as it has never been an issue for me. This is important in many cases, as if allows the compiler to manage RAM usage appropriately based on the context.
    A better way to structure things, if you can, is have the interrupt routine do as little as possible, and handle everything else in your general code. The model that is commonly used is to have a main loop to do the work, and goes to sleep when the work is done. The interrupt does what it must, and resets the sleep on return (resets the low power mode bits on the MSP430), signalling what must be done for the main loop if needed. If this is not practical, and you can be sure that you won't lose due to memory or timing in the interrupt, go with it. Nothing says that the 'best' way is always the right way. I have shoved entirely too much into interrupt handlers at times, when it was the most practical solution for one reason or another.
    The pragma referenced is likely the FUNC_CANNOT_INLINE pragma. This will tell the compiler that the function is not inlinable, and is will stop yelling at you about it, or DIAG_SUPRESS for the given message (before your function) paired with DIAG_DEFAULT (after your function)
  25. Like
    tripwire reacted to mechg in Products using MSP430   
    Here is some shameless self-promotion:  I make and sell gauss meters that use MSP430G series chips:
    My customers include slot-car racers who use them to tune motors,  guitar makers who build magnetic pickups, and Model-T Ford owners who rebuild their own generators.   The design is not open source.

  • Create New...