Jump to content

pjkim

Members
  • Content Count

    63
  • Joined

  • Last visited

  • Days Won

    5

Reputation Activity

  1. Like
    pjkim reacted to veryalive in MSP430-GCC v4.0.1.0 with code composer studio   
    ......    and from the ccs wiki  .....
     
    http://processors.wiki.ti.com/index.php/Compiler_Installation_and_Selection
     
     
    MSP GCC 5.x not seen by CCSMSP GCC compilers have an installation issue with CCS v6.1.2 and earlier where it will not properly register the 5.x msp gcc release. The 4.9 release will properly register and show as an available compiler version.
    In order to fix this issue, save below xml file, MSP430_GNU_5.0.xml, to this location: <ccs>\ccsv6\eclipse\plugins\com.ti.ccstudio.buildDefinitions.GNU_6.1.1.201512151800\resources\buildDefinitions\msp430\
    File:MSP430 GNU 5.0.zip
    Restart CCS.
    From your CCS project, update the compiler on the General tab to point to the new 5.x msp gcc release. Click ok. You should now see both msp gcc compiler versions as available choices.
    The issue will be fixed in CCS 6.1.3 and CCS 6.2
  2. Like
    pjkim got a reaction from abecedarian in ESI Project: Servo motor controller   
    I have been interested in making a servo motor controller. I wanted a microcontroller with a hardware quadrature encoder so when TI had a call for projects utilizing the Extended Scan Interface of the MSP430FR6989, I thought this might be a good fit.
     
    Servo motors have several advantages over stepper motors including increased torque, lower power consumption, feedback control to eliminate missed steps. The problem is that servo motors are rather expensive compared to stepper motors. I obtained several surplus double-shafted, coreless DC motors with planetary gearboxes for $14. They turn easily with an old AA battery but can also be driven at 24V for increased speed and torque. I also purchased  slot photointerrupters to use as quadrature encoders.
     
    I cut an encoder wheel from a tin can, drilled a hole in the middle and cut out two 90 degree wedges. The wheel was mounted on a short nylon bolt that had a 1/16" hole drilled down the center and clamped between two nuts that were rounded to provide clearance against the photo-interrupters.

     
    The photo-interrupters were mounted 135 degrees from each other. To give a proper quadrate signal, the angle between the detectors needs to be an odd multiple of half the encoder disk wedge angle, i.e. n * 90/2 where n is an odd number. 45 degrees caused physical interference between the mounting lugs of the photo-interrupters so 135 degrees was chosen. This gives 8 pulses per rotation. I am not sure what the exact gear ratio is but somewhere around 80:1. This gives about 0.56 degree per step which is on the order of the gear backlash.

     
    Here is a picture of the entire servo motor assembly.

     
    Here is an oscilloscope trace of the quadrature signals.

     
    Hopefully I can have the MSP430FR6989s driving the servo motors and doing something interesting soon.
     
    Thanks once again to TI for sponsoring the contest.
  3. Like
    pjkim got a reaction from bluehash in ESI Project: Servo motor controller   
    Yes, I am using a L298N dual H bridge motor driver.
     
    From what I can gather from the datasheet, I can utilize the ESI to decode one quadrature encoder. I want to drive at least two axes so I can control X and Y. After sorting out the programming/ESI issues, I am planning to solder the MSP430FR6989 onto an LQFP100 adapter board so I can use two at a time. Something like
    http://www.dx.com/p/multi-function-v116-lqfp-tqfp32-44-48-64-80-100pin-to-dip-adapter-boards-green-2-pcs-217452#.VBCv_UtqJ6E
     
    Can't wait to try this out.
  4. Like
    pjkim got a reaction from bluehash in ESI Project: Servo motor controller   
    I have been interested in making a servo motor controller. I wanted a microcontroller with a hardware quadrature encoder so when TI had a call for projects utilizing the Extended Scan Interface of the MSP430FR6989, I thought this might be a good fit.
     
    Servo motors have several advantages over stepper motors including increased torque, lower power consumption, feedback control to eliminate missed steps. The problem is that servo motors are rather expensive compared to stepper motors. I obtained several surplus double-shafted, coreless DC motors with planetary gearboxes for $14. They turn easily with an old AA battery but can also be driven at 24V for increased speed and torque. I also purchased  slot photointerrupters to use as quadrature encoders.
     
    I cut an encoder wheel from a tin can, drilled a hole in the middle and cut out two 90 degree wedges. The wheel was mounted on a short nylon bolt that had a 1/16" hole drilled down the center and clamped between two nuts that were rounded to provide clearance against the photo-interrupters.

     
    The photo-interrupters were mounted 135 degrees from each other. To give a proper quadrate signal, the angle between the detectors needs to be an odd multiple of half the encoder disk wedge angle, i.e. n * 90/2 where n is an odd number. 45 degrees caused physical interference between the mounting lugs of the photo-interrupters so 135 degrees was chosen. This gives 8 pulses per rotation. I am not sure what the exact gear ratio is but somewhere around 80:1. This gives about 0.56 degree per step which is on the order of the gear backlash.

     
    Here is a picture of the entire servo motor assembly.

     
    Here is an oscilloscope trace of the quadrature signals.

     
    Hopefully I can have the MSP430FR6989s driving the servo motors and doing something interesting soon.
     
    Thanks once again to TI for sponsoring the contest.
  5. Like
    pjkim got a reaction from colotron in Complete Tiva IDE using Eclipse on Mac including flash and debug   
    I recently installed openOCD on my computer and was able to get command line debugging working. Reading the gdb tutorials, I started doubting whether this was something I wanted to learn and wanted to see if I could get a GUI debugger working instead. I started fiddling around with Eclipse on my Mac and wanted to get a complete Tiva IDE. I followed a great tutorial for installing Stellarisware on Linux at http://kernelhacks.blogspot.com/2012/11/the-complete-tutorial-for-stellaris_25.html
     
    Perhaps because of the changes from Stellarisware to Tivaware and/or Linux to Mac, there were many things that needed to be tweaked, adjusted, and fiddled with. But in the end, everything works!
     
    First, off, I have Energia.app in /Applications. I installed openOCD using macports and installed Eclipse IDE for C/C++ Developers. Make sure gdb and openOCD are working properly before trying to get it to work in eclipse. I will describe here the changes from the tutorial above-- do all the other steps in the tutorial above.
     
    I copied the TivaWare files extracted on a Windows machine (because TI distributes this as an EXE file!) to ~/Projects/TivaWare_C_Series-1.1 (you can choose elsewhere but will need to change paths accordingly). I also created a folder for openOCD in ~/Projects/openocdDir containing the file ek-lm4f230xl.cfg which contains
    # # TI Stellaris Launchpad ek-lm4f120xl Evaluation Kits # # http://www.ti.com/tool/ek-lm4f120xl # # # NOTE: using the bundled ICDI interface is optional! # This interface is not ftdi based as previous boards were # source [find interface/ti-icdi.cfg] set WORKAREASIZE 0x8000 #set CHIPNAME lm4f120h5qr set CHIPNAME lm4f230h5qr source [find target/stellaris_icdi.cfg] Step 6: in Cross Settings, change Prefix to arm-none-eabi- and set Path to /Applications/Energia.app/Contents/Resources/Java/hardware/tools/lm4f/bin/
     
    Step 7: Same except define PART_TM4C123GH6PM instead of PART_LM4F120H5QR. I don't know if this makes a difference but be consistent.
     
    Step 8: set include path to /Users/pjkim/Projects/TivaWare_C_Series-1.1
     
    Step 9: Misc section. Set other flags to "-c -fmessage-length=0 -mthumb -mcpu=cortex-m4 -mfpu=fpv4-sp-d16 -mfloat-abi=softfp -ffunction-sections -fdata-sections -MD -std=c99 -Wall -pedantic"
     
    Step 9.5: Change Cross Gcc Linker to ld. I think it defaulted to gcc for some reason.
     
    Step 10: Linker Libraries. Leave Libraries blank. Change search path to /Applications/Energia.app/Contents/Resources/Java/hardware/tools/lm4f/lib/gcc/arm-none-eabi/4.7.1
     
    Step 11: Linker Misc. Change linker flags to "--gc-sections -T../LM4F.ld"
     
    Once everything is done, check your flags. If you click on the "Cross GCC Compiler" item under Tool Settings, you can see "All options:" it should look like 
    -DARM_MATH_CM4 -DTARGET_IS_BLIZZARD_RA1 -DPART_TM4C123GH6PM -I/Users/pjkim/Projects/TivaWare_C_Series-1.1 -O0 -g3 -Wall -c -fmessage-length=0 -mthumb -mcpu=cortex-m4 -mfpu=fpv4-sp-d16 -mfloat-abi=softfp -ffunction-sections -fdata-sections -MD -std=c99 -Wall -pedantic Check the Cross GCC Linker options. They should be something like this.
    -L/Applications/Energia.app/Contents/Resources/Java/hardware/tools/lm4f/lib/gcc/arm-none-eabi/4.7.1 --gc-sections -T../LM4F.ld You will need to supply a linker description file LM4F.ld in the main folder of your project. You can copy any one of the .ld files from the TivaWare examples folder. You basically needs this in the file:
    * This is part of revision 1.1 of the EK-TM4C123GXL Firmware Package. MEMORY { FLASH (rx) : ORIGIN = 0x00000000, LENGTH = 0x00040000 SRAM (rwx) : ORIGIN = 0x20000000, LENGTH = 0x00008000 } SECTIONS { .text : { _text = .; KEEP(*(.isr_vector)) *(.text*) *(.rodata*) _etext = .; } > FLASH .data : AT(ADDR(.text) + SIZEOF(.text)) { _data = .; *(vtable) *(.data*) _edata = .; } > SRAM .bss : { _bss = .; *(.bss*) *(COMMON) _ebss = .; } > SRAM } Go back to the appropriate sections to fix any errors. My username is pjkim. Change to yours. This should give you a working IDE for compiling your projects into an .elf and .bin file. Get this working before going to the next step,  "Flashing Programs."
     
    Step 3: He was flashing the Release version which doesn't include the debug symbols in the .elf file. I chose to use the Debug version for this reason. Name "Debug flash." Location "/Applications/Energia.app/Contents/Resources/Java/hardware/tools/lm4f/bin/lm4flash" Working Directory: "${workspace_loc:/${project_name}/Debug}" and Arguments: "${project_name}.bin"
     
    This should give you compile and flashing capabilities.
    N.B. lm4flash in the current version of Energia (0101E0010) is broken. You will need to replace it from the prior version or as I did, build from source (https://github.com/utzig/lm4tools).
     
    The last part of the puzzle is in IDE debugging. This part works now but it was tricky to get working and I am not exactly why it is so fidgety. If it is not working properly, I suggest trying to isolate the problem by running openocd in a terminal window and gdb from eclipse. If this works, do it the other way around, run openocd in eclipse and connect to the board using gdb in a terminal window. That way you can narrow down where you need to fix things.
     
    Debuggin:
     
    Step 2: Location: "/opt/local/bin/openocd" Working Directory: "/Users/pjkim/Projects/openocdDir" and Arguments: "--file ek-lm4f230xl.cfg" You will need to create a openocdDir folder and put in the file ek-lm4f230xl.cfg. The contents are:
    # # Comments go here # source [find interface/ti-icdi.cfg] set WORKAREASIZE 0x8000 set CHIPNAME lm4f230h5qr source [find target/stellaris_icdi.cfg] Step 7: There is a known bug in gdb or openocd that can cause connection problems. The workaround is described in http://www.verious.com/tutorial/the-complete-tutorial-for-stellaris-launchpad-development-with-gnu-linux-ii
     
    Get the target.xml file from http://pastebin.com/download.php?i=0Lu3Bu0R and rename it. Put the file in your Project/Debug folder. If anyone knows how to do this better/automatically, I would appreciate it.
     
    Change the Initialization commands to
    set tdesc filename ../target.xml target extended-remote :3333 monitor reset halt And you should have a working IDE. Here is a screenshot of the IDE in action:

     
    Wouldn't it be nice if TI could package all of this into a .dmg file?
  6. Like
    pjkim got a reaction from OppaErich in MSP430 university project   
    Give a man a match, he stays warm for a day. Light him on fire, he stays warm for the rest of his life.
  7. Like
    pjkim got a reaction from Rickta59 in SubiCount: an improved tally counter   
    My day job is a rheumatologist and immunology researcher. An important but stupid-boring part of research involves an accurate counting of cells. Whether you work in  immunology, cancer biology, sperm counting, etc, an accurate cell count is essential for meaningful and reproducible results. There are electronic Coulter counters but they are expensive and unless you are constantly counting cells, it doesn't make sense because of the constant adjusting and cleaning involved.
     
    When I was first shown how to count cells, I thought they were taking the new guy on a snipe hunt! You take a suspension of cells and put 10 microliters (ul) to a specially calibrated slide called a hemocytometer. It has a calibrated grid of lines and a precision ground spacing between the slide and coverslip so that the volume of fluid being examined is precisely known. You typically count the number of cells in a 1 x 1 x 0.1 mm (0.1 ul) volume. So for example if you count 100 cells, you have 100/0.1ul = 1 million cells/ml.
     
    See http://en.wikipedia.org/wiki/Hemocytometer for pictures and an explanation. If you want accurate and reproducible results, you need to do a good job. Once you learn few rules, it is easy but at the same time mind numbing and tedious.
     
    A friend of mine did research on subtizing (https://en.wikipedia.org/wiki/Subitizing). Whats that you say? If I flash up fingers for a fraction of a second, you don't count the individual fingers-- you instantly recognize the number. Most people can subitize up to ~5 or 6 items and as you can imagine, it is much faster than counting. I wanted to see if I could subitize to speed up counting. Enter the SubiCount.
     
    The hardware is rather simple so I routed the board manually, i.e. without a schematic. Two transistors, two resistors and a bypass cap. The first version of the firmware was not particularly power efficient-- it was constantly in LPM3 with a timer driven interrupt routine. About 100 times per second, it would run a button debounce routine, take care of sending pulses to the tally counter, and fall back into LPM3. Pulses to the tally counter need to be sent at a controlled rate otherwise pulses get lost. The MSP430 keeps track of how many need to be streamed to the tally counter.
     
    Problem was, it would do this 24/7 regardless of whether it was being used or not. Despite this, the two button cell batteries lasted three and a half months. For my project entry, I wanted to really leverage the low power aspect of the MSP430. I haven't had a chance to see what the new battery life is but I reckon it will likely be several years because it spends the bulk of its time in LPM4.
     
    Here is a video of SubiCount in action:
    https://www.youtube.com/watch?v=_fc95bzSDj4
     
    My implementation was to stay in LPM4. From LPM4, pressing any of the buttons will trigger a PORT1 interrupt which puts the 430 into LPM3 and turns off the PORT1 interrupt. In LPM3, a timer driven interrupt routine is called 100 times per second to debounce buttons and send pulses to the tally counter. When all the pulses are sent and no button presses are active or pending, the PORT1 interrupt is turned back on and goes into LPM4.
     
    Here is the code for main. It does nothing other than turning on global interrupts and going into LPM3.
    int main(void) { Grace_init(); // Activate Grace-generated configuration // >>>>> Fill-in user code here <<<<< _BIS_SR(LPM3_bits + GIE); // never should get here } The meat is in two interrupt routines: one for PORT1 and another for TimerA0. I used GRACE so the code is in the automatically generated InterruptVectors_init.c file. Comments have been edited out for brevity:
    #include <msp430.h> /* SubiCount by Peter Kim 2013. Creative Commons License: Attribution-NonCommercial-ShareAlike 4.0 International */ /* USER CODE START (section: InterruptVectors_init_c_prologue) */ /* User defined includes, defines, global variables and functions */ #define MAXDEBOUNCE 2 //threshold for debounce #define MAXRESET 300 // threshold for three button reset int processKey(unsigned int pinValue, unsigned int *pkeyInteg, unsigned int *plastValue); int i; unsigned int oneKeyInteg = 0; unsigned int twoKeyInteg = 0; unsigned int threeKeyInteg = 0; unsigned int resetInteg = 0; unsigned int oneKeyLastVal = 0; unsigned int twoKeyLastVal = 0; unsigned int threeKeyLastVal = 0; volatile unsigned int pulses; int processKey(unsigned int pinValue, unsigned int *pkeyInteg, unsigned int *plastValue) { /* Debounce routine adapted from http://www.kennethkuhn.com/electronics/debounce.c */ int posEdge = 0; if (pinValue) //pinValue inverted { if (*pkeyInteg > 0) (*pkeyInteg)--; } else if (*pkeyInteg < MAXDEBOUNCE) (*pkeyInteg)++; if (*pkeyInteg == 0) *plastValue = 0; else if (*pkeyInteg >= MAXDEBOUNCE && *plastValue == 0) { posEdge = 1; *plastValue = 1; *pkeyInteg = MAXDEBOUNCE; /* defensive code if integrator got corrupted */ } return(posEdge); } void InterruptVectors_graceInit(void) { } #pragma vector=PORT1_VECTOR __interrupt void PORT1_ISR_HOOK(void) { /* USER CODE START (section: PORT1_ISR_HOOK) */ P1IFG &= ~(BIT3+BIT2+BIT1); //clear interrupt flag P1IE &= ~(BIT3+BIT2+BIT1); //clear P1.3 interrupt enable __bic_SR_register_on_exit(OSCOFF); //put into LPM3 /* USER CODE END (section: PORT1_ISR_HOOK) */ } /* * ======== Timer_A2 Interrupt Service Routine ======== */ #pragma vector=TIMERA0_VECTOR __interrupt void TIMERA0_ISR_HOOK(void) { /* USER CODE START (section: TIMERA0_ISR_HOOK) */ static int count =0; // to keep track of times here int P1; P1 = P1IN; if ( processKey(P1 & BIT1, &oneKeyInteg, &oneKeyLastVal) ) pulses++; if ( processKey(P1 & BIT2, &twoKeyInteg, &twoKeyLastVal) ) { pulses += 2; } if ( processKey(P1 & BIT3, &threeKeyInteg, &threeKeyLastVal) ) { pulses += 3; } if ( (P1 & (BIT1|BIT2|BIT3)) == 0) { resetInteg++; if (resetInteg > MAXRESET) { resetInteg = 0; P1OUT |= BIT6; //turn on bit6, reset _delay_cycles(40000); P1OUT = BIT1|BIT2|BIT3; //need pull ups on pulses = 0; //reset pulses count after reset return; } } else if (resetInteg) resetInteg--; if (count++ >7) { count = 0; if (P1OUT & BIT0) //count pin high? { P1OUT &= ~BIT0; //then turn off pulses--; //and decrement pulses } else if (pulses) { P1OUT |= BIT0; //turn on count pin } } if (pulses == 0) // done with pulses? { if ( (P1IN & BIT1+BIT2+BIT3) == BIT1+BIT2+BIT3 ) //are all buttons not pressed? { if (oneKeyLastVal==0 && twoKeyLastVal==0 && threeKeyLastVal==0) { P1IFG &= ~(BIT3+BIT2+BIT1); //clear P1.3 int flag P1IE |= BIT3+BIT2+BIT1; //enable P1.3 interrupt __bis_SR_register_on_exit(LPM4_bits + GIE); //go into LPM4 } } } /* USER CODE END (section: TIMERA0_ISR_HOOK) */ }   Here is the hand routed board:

     
    And a hand drawn schematic:

  8. Like
    pjkim got a reaction from Kcamp in Msp430 Launchpad to Msp430   
    It sounds like you are really trying to bite off more than you can chew. Interfacing two micro controllers can be somewhat challenging. Breadboarding a micro controller can be challenging. Can I suggest you start off simply?
     
    1) Get blink to work on the Launchpad alone. This makes sure that your computer and IDE (Energia or CodeComposer or etc) are set and working properly.
    2) Make sure you can communicate between a terminal window on your computer and the 430. I think the 2452 does not have hardware UART. So depending on how the software UART has its pins configured, you will need the Rx/Tx jumpers in J3 vertically or horizontally. I don't have a 2452 to confirm but this is fairly easy to figure out-- you will be able to communicate in one orientation and not the other.
    3) Once you have blink working on a Launchpad micro controller, take that programmed micro controller and get it to work on a breadboard. You need to supply power and gnd and probably need to keep RST high through a pull-up resistor.
    4) If you want to figure out how to program the chip on the breadboard, this would be a good time to sort that out.
    5) Inter-chip communication can be very challenging. If you are not experienced in programming and working with micro controllers, this will be a rather hard first project. You can try debugging the program on one chip by hooking it up to your computer and sending the appropriate commands/responses through a terminal window. Then debug the other micro controller. Once you have both working, hook them up to each other and then debug them together. And just in case, Tx of one chip goes to Rx of the other.
     
    Good luck.
  9. Like
    pjkim got a reaction from GeekDoc in Connect servo motor with msp430   
    Captain pedantic here.
     
    1) The link you provide is a regular motor, not a servo motor -- continuous rotation, no positional feedback. Your choice of driver will depend on how much current. The no load current of your motor is rather low so can be done with a "jelly bean" transistor but the stall current is 0.5 A. If you be driving your motors with this much current, need a power transistor. Your motor drive circuit will also depend on whether you want one directional rotation or bidirectional-- single transistor (or Darlington) or H bridge. Don't forget the flyback diodes to protect your transistors.
     
    2) True servo motors use a rotary encoder to provide positional feedback which is then used to position the motor. You need a dedicated servo motor driver to use one of these. Rather expensive but have great torque. Continuous rotation.
     
    3) Hobby servos (very different from true servo motors) take a pulse width modulated signal and turns through a range of ~180 degrees. They can be modified for continuous rotation. Inexpensive, not bad performance, easy to interface to micro controllers.
  10. Like
    pjkim got a reaction from joro1881 in MSP430 university project   
    Give a man a match, he stays warm for a day. Light him on fire, he stays warm for the rest of his life.
  11. Like
    pjkim got a reaction from tripwire in Online game based on MSP430 assembler: microcorruption   
    Saw this on Hackaday. There is a new online game based on hacking virtual MSP430 based electronic locks. In the game you have a debugger to analyze the lock code. Once you understand the code, you enter a password to unlock your prize.
     
    I program in C and have used the C level debugger but I have always wanted to be able to understand machine level code. This game includes a tutorial to get you started.
     
    You can find the site at https://microcorruption.com/login
     
    Could the virtual "Lockitall" be entered as another device that contains an MSP430?
  12. Like
    pjkim got a reaction from roadrunner84 in Online game based on MSP430 assembler: microcorruption   
    Saw this on Hackaday. There is a new online game based on hacking virtual MSP430 based electronic locks. In the game you have a debugger to analyze the lock code. Once you understand the code, you enter a password to unlock your prize.
     
    I program in C and have used the C level debugger but I have always wanted to be able to understand machine level code. This game includes a tutorial to get you started.
     
    You can find the site at https://microcorruption.com/login
     
    Could the virtual "Lockitall" be entered as another device that contains an MSP430?
  13. Like
    pjkim got a reaction from spirilis in Online game based on MSP430 assembler: microcorruption   
    Saw this on Hackaday. There is a new online game based on hacking virtual MSP430 based electronic locks. In the game you have a debugger to analyze the lock code. Once you understand the code, you enter a password to unlock your prize.
     
    I program in C and have used the C level debugger but I have always wanted to be able to understand machine level code. This game includes a tutorial to get you started.
     
    You can find the site at https://microcorruption.com/login
     
    Could the virtual "Lockitall" be entered as another device that contains an MSP430?
  14. Like
    pjkim got a reaction from bluehash in Online game based on MSP430 assembler: microcorruption   
    Saw this on Hackaday. There is a new online game based on hacking virtual MSP430 based electronic locks. In the game you have a debugger to analyze the lock code. Once you understand the code, you enter a password to unlock your prize.
     
    I program in C and have used the C level debugger but I have always wanted to be able to understand machine level code. This game includes a tutorial to get you started.
     
    You can find the site at https://microcorruption.com/login
     
    Could the virtual "Lockitall" be entered as another device that contains an MSP430?
  15. Like
    pjkim got a reaction from Aditya in Using MSP430 as a relay between a function generator and MATLAB   
    How you implement this will depend on the expected frequency of your square wave. You would need different implementations for 10 kHz signal vs a 0.01 Hz signal. A slower signal will be lot easier to do as the time jitter will be negligible.
     
    As for input signal, TTL is usually 0 to 5V which will nicely fry the GPIO pins on the MSP430 so you will need to do level shifting/converting.
     
    As for the program, you can use the attachInterrupt function. See http://energia.nu/AttachInterrupt.html
     
    Attach your interrupt routine to a GPIO pin (can pick rising or falling). This routine can send a message through the serial port using Serial.print().
     
    As for interfacing to MATLAB, I haven't used MATLAB in ages so I don't know. I would imagine you would need MATLAB to monitor the serial port (one of the COM ports in Windows or one of the /dev/tty* devices in linux/mac).
  16. Like
    pjkim got a reaction from tripwire in Members will now be warned for incorrect post content format.   
    I am in complete agreement regarding preservation of links. It is very frustrating to find a post on something you are working on to find that the links have been lost to "link rot." One thing that I don't agree with is websites requiring you to login to see attachments. I understand that will force people to register for an account but that inflates the userbase with people who have little interest otheriwse in the website. If they have something to contribute or want to post questions, they have other reasons to register and selects for a more active community of users. Just my two cents.
  17. Like
    pjkim got a reaction from petertux in SubiCount: an improved tally counter   
    According to the Polymorph page at Sparkfun, the melting temp is 60 deg C. https://www.sparkfun.com/products/10951
     
    As for using image processing, it takes a lot of fiddling to work correctly. Another thing I didn't mention is that you use trypan blue to mark the dead cells (the live cells actively pump out the dye and stay clear). You often also have debris mixed in. The image processing has to be able to detect correct size/shape, blue vs clear, and distinguish between cells and debris. If you use a program like ImageJ, it requires a fair amount of twiddling to get it right. And will likely require more twiddling from sample to sample.
     
    There are benchtop all in one models that include a microscope, CPU to do the image processing, and a display for the image and results. We demoed one and there were several shortcomings. First, I could count faster using a conventional tally counter. Second, it had a lot of difficulty when there was any debris in the sample-- isolating cells from skin produces a fair amount of debris. Third, they required that samples be loaded on a special single use plastic slide that cost something approaching a $1 per sample. For what we routinely do, the benchtop counter would have been slower, less accurate, and a whole lot more expensive.
  18. Like
    pjkim got a reaction from chicken in SubiCount: an improved tally counter   
    My day job is a rheumatologist and immunology researcher. An important but stupid-boring part of research involves an accurate counting of cells. Whether you work in  immunology, cancer biology, sperm counting, etc, an accurate cell count is essential for meaningful and reproducible results. There are electronic Coulter counters but they are expensive and unless you are constantly counting cells, it doesn't make sense because of the constant adjusting and cleaning involved.
     
    When I was first shown how to count cells, I thought they were taking the new guy on a snipe hunt! You take a suspension of cells and put 10 microliters (ul) to a specially calibrated slide called a hemocytometer. It has a calibrated grid of lines and a precision ground spacing between the slide and coverslip so that the volume of fluid being examined is precisely known. You typically count the number of cells in a 1 x 1 x 0.1 mm (0.1 ul) volume. So for example if you count 100 cells, you have 100/0.1ul = 1 million cells/ml.
     
    See http://en.wikipedia.org/wiki/Hemocytometer for pictures and an explanation. If you want accurate and reproducible results, you need to do a good job. Once you learn few rules, it is easy but at the same time mind numbing and tedious.
     
    A friend of mine did research on subtizing (https://en.wikipedia.org/wiki/Subitizing). Whats that you say? If I flash up fingers for a fraction of a second, you don't count the individual fingers-- you instantly recognize the number. Most people can subitize up to ~5 or 6 items and as you can imagine, it is much faster than counting. I wanted to see if I could subitize to speed up counting. Enter the SubiCount.
     
    The hardware is rather simple so I routed the board manually, i.e. without a schematic. Two transistors, two resistors and a bypass cap. The first version of the firmware was not particularly power efficient-- it was constantly in LPM3 with a timer driven interrupt routine. About 100 times per second, it would run a button debounce routine, take care of sending pulses to the tally counter, and fall back into LPM3. Pulses to the tally counter need to be sent at a controlled rate otherwise pulses get lost. The MSP430 keeps track of how many need to be streamed to the tally counter.
     
    Problem was, it would do this 24/7 regardless of whether it was being used or not. Despite this, the two button cell batteries lasted three and a half months. For my project entry, I wanted to really leverage the low power aspect of the MSP430. I haven't had a chance to see what the new battery life is but I reckon it will likely be several years because it spends the bulk of its time in LPM4.
     
    Here is a video of SubiCount in action:
    https://www.youtube.com/watch?v=_fc95bzSDj4
     
    My implementation was to stay in LPM4. From LPM4, pressing any of the buttons will trigger a PORT1 interrupt which puts the 430 into LPM3 and turns off the PORT1 interrupt. In LPM3, a timer driven interrupt routine is called 100 times per second to debounce buttons and send pulses to the tally counter. When all the pulses are sent and no button presses are active or pending, the PORT1 interrupt is turned back on and goes into LPM4.
     
    Here is the code for main. It does nothing other than turning on global interrupts and going into LPM3.
    int main(void) { Grace_init(); // Activate Grace-generated configuration // >>>>> Fill-in user code here <<<<< _BIS_SR(LPM3_bits + GIE); // never should get here } The meat is in two interrupt routines: one for PORT1 and another for TimerA0. I used GRACE so the code is in the automatically generated InterruptVectors_init.c file. Comments have been edited out for brevity:
    #include <msp430.h> /* SubiCount by Peter Kim 2013. Creative Commons License: Attribution-NonCommercial-ShareAlike 4.0 International */ /* USER CODE START (section: InterruptVectors_init_c_prologue) */ /* User defined includes, defines, global variables and functions */ #define MAXDEBOUNCE 2 //threshold for debounce #define MAXRESET 300 // threshold for three button reset int processKey(unsigned int pinValue, unsigned int *pkeyInteg, unsigned int *plastValue); int i; unsigned int oneKeyInteg = 0; unsigned int twoKeyInteg = 0; unsigned int threeKeyInteg = 0; unsigned int resetInteg = 0; unsigned int oneKeyLastVal = 0; unsigned int twoKeyLastVal = 0; unsigned int threeKeyLastVal = 0; volatile unsigned int pulses; int processKey(unsigned int pinValue, unsigned int *pkeyInteg, unsigned int *plastValue) { /* Debounce routine adapted from http://www.kennethkuhn.com/electronics/debounce.c */ int posEdge = 0; if (pinValue) //pinValue inverted { if (*pkeyInteg > 0) (*pkeyInteg)--; } else if (*pkeyInteg < MAXDEBOUNCE) (*pkeyInteg)++; if (*pkeyInteg == 0) *plastValue = 0; else if (*pkeyInteg >= MAXDEBOUNCE && *plastValue == 0) { posEdge = 1; *plastValue = 1; *pkeyInteg = MAXDEBOUNCE; /* defensive code if integrator got corrupted */ } return(posEdge); } void InterruptVectors_graceInit(void) { } #pragma vector=PORT1_VECTOR __interrupt void PORT1_ISR_HOOK(void) { /* USER CODE START (section: PORT1_ISR_HOOK) */ P1IFG &= ~(BIT3+BIT2+BIT1); //clear interrupt flag P1IE &= ~(BIT3+BIT2+BIT1); //clear P1.3 interrupt enable __bic_SR_register_on_exit(OSCOFF); //put into LPM3 /* USER CODE END (section: PORT1_ISR_HOOK) */ } /* * ======== Timer_A2 Interrupt Service Routine ======== */ #pragma vector=TIMERA0_VECTOR __interrupt void TIMERA0_ISR_HOOK(void) { /* USER CODE START (section: TIMERA0_ISR_HOOK) */ static int count =0; // to keep track of times here int P1; P1 = P1IN; if ( processKey(P1 & BIT1, &oneKeyInteg, &oneKeyLastVal) ) pulses++; if ( processKey(P1 & BIT2, &twoKeyInteg, &twoKeyLastVal) ) { pulses += 2; } if ( processKey(P1 & BIT3, &threeKeyInteg, &threeKeyLastVal) ) { pulses += 3; } if ( (P1 & (BIT1|BIT2|BIT3)) == 0) { resetInteg++; if (resetInteg > MAXRESET) { resetInteg = 0; P1OUT |= BIT6; //turn on bit6, reset _delay_cycles(40000); P1OUT = BIT1|BIT2|BIT3; //need pull ups on pulses = 0; //reset pulses count after reset return; } } else if (resetInteg) resetInteg--; if (count++ >7) { count = 0; if (P1OUT & BIT0) //count pin high? { P1OUT &= ~BIT0; //then turn off pulses--; //and decrement pulses } else if (pulses) { P1OUT |= BIT0; //turn on count pin } } if (pulses == 0) // done with pulses? { if ( (P1IN & BIT1+BIT2+BIT3) == BIT1+BIT2+BIT3 ) //are all buttons not pressed? { if (oneKeyLastVal==0 && twoKeyLastVal==0 && threeKeyLastVal==0) { P1IFG &= ~(BIT3+BIT2+BIT1); //clear P1.3 int flag P1IE |= BIT3+BIT2+BIT1; //enable P1.3 interrupt __bis_SR_register_on_exit(LPM4_bits + GIE); //go into LPM4 } } } /* USER CODE END (section: TIMERA0_ISR_HOOK) */ }   Here is the hand routed board:

     
    And a hand drawn schematic:

  19. Like
    pjkim got a reaction from petertux in SubiCount: an improved tally counter   
    My day job is a rheumatologist and immunology researcher. An important but stupid-boring part of research involves an accurate counting of cells. Whether you work in  immunology, cancer biology, sperm counting, etc, an accurate cell count is essential for meaningful and reproducible results. There are electronic Coulter counters but they are expensive and unless you are constantly counting cells, it doesn't make sense because of the constant adjusting and cleaning involved.
     
    When I was first shown how to count cells, I thought they were taking the new guy on a snipe hunt! You take a suspension of cells and put 10 microliters (ul) to a specially calibrated slide called a hemocytometer. It has a calibrated grid of lines and a precision ground spacing between the slide and coverslip so that the volume of fluid being examined is precisely known. You typically count the number of cells in a 1 x 1 x 0.1 mm (0.1 ul) volume. So for example if you count 100 cells, you have 100/0.1ul = 1 million cells/ml.
     
    See http://en.wikipedia.org/wiki/Hemocytometer for pictures and an explanation. If you want accurate and reproducible results, you need to do a good job. Once you learn few rules, it is easy but at the same time mind numbing and tedious.
     
    A friend of mine did research on subtizing (https://en.wikipedia.org/wiki/Subitizing). Whats that you say? If I flash up fingers for a fraction of a second, you don't count the individual fingers-- you instantly recognize the number. Most people can subitize up to ~5 or 6 items and as you can imagine, it is much faster than counting. I wanted to see if I could subitize to speed up counting. Enter the SubiCount.
     
    The hardware is rather simple so I routed the board manually, i.e. without a schematic. Two transistors, two resistors and a bypass cap. The first version of the firmware was not particularly power efficient-- it was constantly in LPM3 with a timer driven interrupt routine. About 100 times per second, it would run a button debounce routine, take care of sending pulses to the tally counter, and fall back into LPM3. Pulses to the tally counter need to be sent at a controlled rate otherwise pulses get lost. The MSP430 keeps track of how many need to be streamed to the tally counter.
     
    Problem was, it would do this 24/7 regardless of whether it was being used or not. Despite this, the two button cell batteries lasted three and a half months. For my project entry, I wanted to really leverage the low power aspect of the MSP430. I haven't had a chance to see what the new battery life is but I reckon it will likely be several years because it spends the bulk of its time in LPM4.
     
    Here is a video of SubiCount in action:
    https://www.youtube.com/watch?v=_fc95bzSDj4
     
    My implementation was to stay in LPM4. From LPM4, pressing any of the buttons will trigger a PORT1 interrupt which puts the 430 into LPM3 and turns off the PORT1 interrupt. In LPM3, a timer driven interrupt routine is called 100 times per second to debounce buttons and send pulses to the tally counter. When all the pulses are sent and no button presses are active or pending, the PORT1 interrupt is turned back on and goes into LPM4.
     
    Here is the code for main. It does nothing other than turning on global interrupts and going into LPM3.
    int main(void) { Grace_init(); // Activate Grace-generated configuration // >>>>> Fill-in user code here <<<<< _BIS_SR(LPM3_bits + GIE); // never should get here } The meat is in two interrupt routines: one for PORT1 and another for TimerA0. I used GRACE so the code is in the automatically generated InterruptVectors_init.c file. Comments have been edited out for brevity:
    #include <msp430.h> /* SubiCount by Peter Kim 2013. Creative Commons License: Attribution-NonCommercial-ShareAlike 4.0 International */ /* USER CODE START (section: InterruptVectors_init_c_prologue) */ /* User defined includes, defines, global variables and functions */ #define MAXDEBOUNCE 2 //threshold for debounce #define MAXRESET 300 // threshold for three button reset int processKey(unsigned int pinValue, unsigned int *pkeyInteg, unsigned int *plastValue); int i; unsigned int oneKeyInteg = 0; unsigned int twoKeyInteg = 0; unsigned int threeKeyInteg = 0; unsigned int resetInteg = 0; unsigned int oneKeyLastVal = 0; unsigned int twoKeyLastVal = 0; unsigned int threeKeyLastVal = 0; volatile unsigned int pulses; int processKey(unsigned int pinValue, unsigned int *pkeyInteg, unsigned int *plastValue) { /* Debounce routine adapted from http://www.kennethkuhn.com/electronics/debounce.c */ int posEdge = 0; if (pinValue) //pinValue inverted { if (*pkeyInteg > 0) (*pkeyInteg)--; } else if (*pkeyInteg < MAXDEBOUNCE) (*pkeyInteg)++; if (*pkeyInteg == 0) *plastValue = 0; else if (*pkeyInteg >= MAXDEBOUNCE && *plastValue == 0) { posEdge = 1; *plastValue = 1; *pkeyInteg = MAXDEBOUNCE; /* defensive code if integrator got corrupted */ } return(posEdge); } void InterruptVectors_graceInit(void) { } #pragma vector=PORT1_VECTOR __interrupt void PORT1_ISR_HOOK(void) { /* USER CODE START (section: PORT1_ISR_HOOK) */ P1IFG &= ~(BIT3+BIT2+BIT1); //clear interrupt flag P1IE &= ~(BIT3+BIT2+BIT1); //clear P1.3 interrupt enable __bic_SR_register_on_exit(OSCOFF); //put into LPM3 /* USER CODE END (section: PORT1_ISR_HOOK) */ } /* * ======== Timer_A2 Interrupt Service Routine ======== */ #pragma vector=TIMERA0_VECTOR __interrupt void TIMERA0_ISR_HOOK(void) { /* USER CODE START (section: TIMERA0_ISR_HOOK) */ static int count =0; // to keep track of times here int P1; P1 = P1IN; if ( processKey(P1 & BIT1, &oneKeyInteg, &oneKeyLastVal) ) pulses++; if ( processKey(P1 & BIT2, &twoKeyInteg, &twoKeyLastVal) ) { pulses += 2; } if ( processKey(P1 & BIT3, &threeKeyInteg, &threeKeyLastVal) ) { pulses += 3; } if ( (P1 & (BIT1|BIT2|BIT3)) == 0) { resetInteg++; if (resetInteg > MAXRESET) { resetInteg = 0; P1OUT |= BIT6; //turn on bit6, reset _delay_cycles(40000); P1OUT = BIT1|BIT2|BIT3; //need pull ups on pulses = 0; //reset pulses count after reset return; } } else if (resetInteg) resetInteg--; if (count++ >7) { count = 0; if (P1OUT & BIT0) //count pin high? { P1OUT &= ~BIT0; //then turn off pulses--; //and decrement pulses } else if (pulses) { P1OUT |= BIT0; //turn on count pin } } if (pulses == 0) // done with pulses? { if ( (P1IN & BIT1+BIT2+BIT3) == BIT1+BIT2+BIT3 ) //are all buttons not pressed? { if (oneKeyLastVal==0 && twoKeyLastVal==0 && threeKeyLastVal==0) { P1IFG &= ~(BIT3+BIT2+BIT1); //clear P1.3 int flag P1IE |= BIT3+BIT2+BIT1; //enable P1.3 interrupt __bis_SR_register_on_exit(LPM4_bits + GIE); //go into LPM4 } } } /* USER CODE END (section: TIMERA0_ISR_HOOK) */ }   Here is the hand routed board:

     
    And a hand drawn schematic:

  20. Like
    pjkim got a reaction from bluehash in SubiCount: an improved tally counter   
    My day job is a rheumatologist and immunology researcher. An important but stupid-boring part of research involves an accurate counting of cells. Whether you work in  immunology, cancer biology, sperm counting, etc, an accurate cell count is essential for meaningful and reproducible results. There are electronic Coulter counters but they are expensive and unless you are constantly counting cells, it doesn't make sense because of the constant adjusting and cleaning involved.
     
    When I was first shown how to count cells, I thought they were taking the new guy on a snipe hunt! You take a suspension of cells and put 10 microliters (ul) to a specially calibrated slide called a hemocytometer. It has a calibrated grid of lines and a precision ground spacing between the slide and coverslip so that the volume of fluid being examined is precisely known. You typically count the number of cells in a 1 x 1 x 0.1 mm (0.1 ul) volume. So for example if you count 100 cells, you have 100/0.1ul = 1 million cells/ml.
     
    See http://en.wikipedia.org/wiki/Hemocytometer for pictures and an explanation. If you want accurate and reproducible results, you need to do a good job. Once you learn few rules, it is easy but at the same time mind numbing and tedious.
     
    A friend of mine did research on subtizing (https://en.wikipedia.org/wiki/Subitizing). Whats that you say? If I flash up fingers for a fraction of a second, you don't count the individual fingers-- you instantly recognize the number. Most people can subitize up to ~5 or 6 items and as you can imagine, it is much faster than counting. I wanted to see if I could subitize to speed up counting. Enter the SubiCount.
     
    The hardware is rather simple so I routed the board manually, i.e. without a schematic. Two transistors, two resistors and a bypass cap. The first version of the firmware was not particularly power efficient-- it was constantly in LPM3 with a timer driven interrupt routine. About 100 times per second, it would run a button debounce routine, take care of sending pulses to the tally counter, and fall back into LPM3. Pulses to the tally counter need to be sent at a controlled rate otherwise pulses get lost. The MSP430 keeps track of how many need to be streamed to the tally counter.
     
    Problem was, it would do this 24/7 regardless of whether it was being used or not. Despite this, the two button cell batteries lasted three and a half months. For my project entry, I wanted to really leverage the low power aspect of the MSP430. I haven't had a chance to see what the new battery life is but I reckon it will likely be several years because it spends the bulk of its time in LPM4.
     
    Here is a video of SubiCount in action:
    https://www.youtube.com/watch?v=_fc95bzSDj4
     
    My implementation was to stay in LPM4. From LPM4, pressing any of the buttons will trigger a PORT1 interrupt which puts the 430 into LPM3 and turns off the PORT1 interrupt. In LPM3, a timer driven interrupt routine is called 100 times per second to debounce buttons and send pulses to the tally counter. When all the pulses are sent and no button presses are active or pending, the PORT1 interrupt is turned back on and goes into LPM4.
     
    Here is the code for main. It does nothing other than turning on global interrupts and going into LPM3.
    int main(void) { Grace_init(); // Activate Grace-generated configuration // >>>>> Fill-in user code here <<<<< _BIS_SR(LPM3_bits + GIE); // never should get here } The meat is in two interrupt routines: one for PORT1 and another for TimerA0. I used GRACE so the code is in the automatically generated InterruptVectors_init.c file. Comments have been edited out for brevity:
    #include <msp430.h> /* SubiCount by Peter Kim 2013. Creative Commons License: Attribution-NonCommercial-ShareAlike 4.0 International */ /* USER CODE START (section: InterruptVectors_init_c_prologue) */ /* User defined includes, defines, global variables and functions */ #define MAXDEBOUNCE 2 //threshold for debounce #define MAXRESET 300 // threshold for three button reset int processKey(unsigned int pinValue, unsigned int *pkeyInteg, unsigned int *plastValue); int i; unsigned int oneKeyInteg = 0; unsigned int twoKeyInteg = 0; unsigned int threeKeyInteg = 0; unsigned int resetInteg = 0; unsigned int oneKeyLastVal = 0; unsigned int twoKeyLastVal = 0; unsigned int threeKeyLastVal = 0; volatile unsigned int pulses; int processKey(unsigned int pinValue, unsigned int *pkeyInteg, unsigned int *plastValue) { /* Debounce routine adapted from http://www.kennethkuhn.com/electronics/debounce.c */ int posEdge = 0; if (pinValue) //pinValue inverted { if (*pkeyInteg > 0) (*pkeyInteg)--; } else if (*pkeyInteg < MAXDEBOUNCE) (*pkeyInteg)++; if (*pkeyInteg == 0) *plastValue = 0; else if (*pkeyInteg >= MAXDEBOUNCE && *plastValue == 0) { posEdge = 1; *plastValue = 1; *pkeyInteg = MAXDEBOUNCE; /* defensive code if integrator got corrupted */ } return(posEdge); } void InterruptVectors_graceInit(void) { } #pragma vector=PORT1_VECTOR __interrupt void PORT1_ISR_HOOK(void) { /* USER CODE START (section: PORT1_ISR_HOOK) */ P1IFG &= ~(BIT3+BIT2+BIT1); //clear interrupt flag P1IE &= ~(BIT3+BIT2+BIT1); //clear P1.3 interrupt enable __bic_SR_register_on_exit(OSCOFF); //put into LPM3 /* USER CODE END (section: PORT1_ISR_HOOK) */ } /* * ======== Timer_A2 Interrupt Service Routine ======== */ #pragma vector=TIMERA0_VECTOR __interrupt void TIMERA0_ISR_HOOK(void) { /* USER CODE START (section: TIMERA0_ISR_HOOK) */ static int count =0; // to keep track of times here int P1; P1 = P1IN; if ( processKey(P1 & BIT1, &oneKeyInteg, &oneKeyLastVal) ) pulses++; if ( processKey(P1 & BIT2, &twoKeyInteg, &twoKeyLastVal) ) { pulses += 2; } if ( processKey(P1 & BIT3, &threeKeyInteg, &threeKeyLastVal) ) { pulses += 3; } if ( (P1 & (BIT1|BIT2|BIT3)) == 0) { resetInteg++; if (resetInteg > MAXRESET) { resetInteg = 0; P1OUT |= BIT6; //turn on bit6, reset _delay_cycles(40000); P1OUT = BIT1|BIT2|BIT3; //need pull ups on pulses = 0; //reset pulses count after reset return; } } else if (resetInteg) resetInteg--; if (count++ >7) { count = 0; if (P1OUT & BIT0) //count pin high? { P1OUT &= ~BIT0; //then turn off pulses--; //and decrement pulses } else if (pulses) { P1OUT |= BIT0; //turn on count pin } } if (pulses == 0) // done with pulses? { if ( (P1IN & BIT1+BIT2+BIT3) == BIT1+BIT2+BIT3 ) //are all buttons not pressed? { if (oneKeyLastVal==0 && twoKeyLastVal==0 && threeKeyLastVal==0) { P1IFG &= ~(BIT3+BIT2+BIT1); //clear P1.3 int flag P1IE |= BIT3+BIT2+BIT1; //enable P1.3 interrupt __bis_SR_register_on_exit(LPM4_bits + GIE); //go into LPM4 } } } /* USER CODE END (section: TIMERA0_ISR_HOOK) */ }   Here is the hand routed board:

     
    And a hand drawn schematic:

  21. Like
    pjkim got a reaction from supamas in SubiCount: an improved tally counter   
    My day job is a rheumatologist and immunology researcher. An important but stupid-boring part of research involves an accurate counting of cells. Whether you work in  immunology, cancer biology, sperm counting, etc, an accurate cell count is essential for meaningful and reproducible results. There are electronic Coulter counters but they are expensive and unless you are constantly counting cells, it doesn't make sense because of the constant adjusting and cleaning involved.
     
    When I was first shown how to count cells, I thought they were taking the new guy on a snipe hunt! You take a suspension of cells and put 10 microliters (ul) to a specially calibrated slide called a hemocytometer. It has a calibrated grid of lines and a precision ground spacing between the slide and coverslip so that the volume of fluid being examined is precisely known. You typically count the number of cells in a 1 x 1 x 0.1 mm (0.1 ul) volume. So for example if you count 100 cells, you have 100/0.1ul = 1 million cells/ml.
     
    See http://en.wikipedia.org/wiki/Hemocytometer for pictures and an explanation. If you want accurate and reproducible results, you need to do a good job. Once you learn few rules, it is easy but at the same time mind numbing and tedious.
     
    A friend of mine did research on subtizing (https://en.wikipedia.org/wiki/Subitizing). Whats that you say? If I flash up fingers for a fraction of a second, you don't count the individual fingers-- you instantly recognize the number. Most people can subitize up to ~5 or 6 items and as you can imagine, it is much faster than counting. I wanted to see if I could subitize to speed up counting. Enter the SubiCount.
     
    The hardware is rather simple so I routed the board manually, i.e. without a schematic. Two transistors, two resistors and a bypass cap. The first version of the firmware was not particularly power efficient-- it was constantly in LPM3 with a timer driven interrupt routine. About 100 times per second, it would run a button debounce routine, take care of sending pulses to the tally counter, and fall back into LPM3. Pulses to the tally counter need to be sent at a controlled rate otherwise pulses get lost. The MSP430 keeps track of how many need to be streamed to the tally counter.
     
    Problem was, it would do this 24/7 regardless of whether it was being used or not. Despite this, the two button cell batteries lasted three and a half months. For my project entry, I wanted to really leverage the low power aspect of the MSP430. I haven't had a chance to see what the new battery life is but I reckon it will likely be several years because it spends the bulk of its time in LPM4.
     
    Here is a video of SubiCount in action:
    https://www.youtube.com/watch?v=_fc95bzSDj4
     
    My implementation was to stay in LPM4. From LPM4, pressing any of the buttons will trigger a PORT1 interrupt which puts the 430 into LPM3 and turns off the PORT1 interrupt. In LPM3, a timer driven interrupt routine is called 100 times per second to debounce buttons and send pulses to the tally counter. When all the pulses are sent and no button presses are active or pending, the PORT1 interrupt is turned back on and goes into LPM4.
     
    Here is the code for main. It does nothing other than turning on global interrupts and going into LPM3.
    int main(void) { Grace_init(); // Activate Grace-generated configuration // >>>>> Fill-in user code here <<<<< _BIS_SR(LPM3_bits + GIE); // never should get here } The meat is in two interrupt routines: one for PORT1 and another for TimerA0. I used GRACE so the code is in the automatically generated InterruptVectors_init.c file. Comments have been edited out for brevity:
    #include <msp430.h> /* SubiCount by Peter Kim 2013. Creative Commons License: Attribution-NonCommercial-ShareAlike 4.0 International */ /* USER CODE START (section: InterruptVectors_init_c_prologue) */ /* User defined includes, defines, global variables and functions */ #define MAXDEBOUNCE 2 //threshold for debounce #define MAXRESET 300 // threshold for three button reset int processKey(unsigned int pinValue, unsigned int *pkeyInteg, unsigned int *plastValue); int i; unsigned int oneKeyInteg = 0; unsigned int twoKeyInteg = 0; unsigned int threeKeyInteg = 0; unsigned int resetInteg = 0; unsigned int oneKeyLastVal = 0; unsigned int twoKeyLastVal = 0; unsigned int threeKeyLastVal = 0; volatile unsigned int pulses; int processKey(unsigned int pinValue, unsigned int *pkeyInteg, unsigned int *plastValue) { /* Debounce routine adapted from http://www.kennethkuhn.com/electronics/debounce.c */ int posEdge = 0; if (pinValue) //pinValue inverted { if (*pkeyInteg > 0) (*pkeyInteg)--; } else if (*pkeyInteg < MAXDEBOUNCE) (*pkeyInteg)++; if (*pkeyInteg == 0) *plastValue = 0; else if (*pkeyInteg >= MAXDEBOUNCE && *plastValue == 0) { posEdge = 1; *plastValue = 1; *pkeyInteg = MAXDEBOUNCE; /* defensive code if integrator got corrupted */ } return(posEdge); } void InterruptVectors_graceInit(void) { } #pragma vector=PORT1_VECTOR __interrupt void PORT1_ISR_HOOK(void) { /* USER CODE START (section: PORT1_ISR_HOOK) */ P1IFG &= ~(BIT3+BIT2+BIT1); //clear interrupt flag P1IE &= ~(BIT3+BIT2+BIT1); //clear P1.3 interrupt enable __bic_SR_register_on_exit(OSCOFF); //put into LPM3 /* USER CODE END (section: PORT1_ISR_HOOK) */ } /* * ======== Timer_A2 Interrupt Service Routine ======== */ #pragma vector=TIMERA0_VECTOR __interrupt void TIMERA0_ISR_HOOK(void) { /* USER CODE START (section: TIMERA0_ISR_HOOK) */ static int count =0; // to keep track of times here int P1; P1 = P1IN; if ( processKey(P1 & BIT1, &oneKeyInteg, &oneKeyLastVal) ) pulses++; if ( processKey(P1 & BIT2, &twoKeyInteg, &twoKeyLastVal) ) { pulses += 2; } if ( processKey(P1 & BIT3, &threeKeyInteg, &threeKeyLastVal) ) { pulses += 3; } if ( (P1 & (BIT1|BIT2|BIT3)) == 0) { resetInteg++; if (resetInteg > MAXRESET) { resetInteg = 0; P1OUT |= BIT6; //turn on bit6, reset _delay_cycles(40000); P1OUT = BIT1|BIT2|BIT3; //need pull ups on pulses = 0; //reset pulses count after reset return; } } else if (resetInteg) resetInteg--; if (count++ >7) { count = 0; if (P1OUT & BIT0) //count pin high? { P1OUT &= ~BIT0; //then turn off pulses--; //and decrement pulses } else if (pulses) { P1OUT |= BIT0; //turn on count pin } } if (pulses == 0) // done with pulses? { if ( (P1IN & BIT1+BIT2+BIT3) == BIT1+BIT2+BIT3 ) //are all buttons not pressed? { if (oneKeyLastVal==0 && twoKeyLastVal==0 && threeKeyLastVal==0) { P1IFG &= ~(BIT3+BIT2+BIT1); //clear P1.3 int flag P1IE |= BIT3+BIT2+BIT1; //enable P1.3 interrupt __bis_SR_register_on_exit(LPM4_bits + GIE); //go into LPM4 } } } /* USER CODE END (section: TIMERA0_ISR_HOOK) */ }   Here is the hand routed board:

     
    And a hand drawn schematic:

  22. Like
    pjkim reacted to Rei Vilo in New Energia release 0101E0011 - 12/17/2013   
    This is the standard error message because Energia isn't signed.
     
    Go to systems preferences, select security and allow apps from any source.
     
    Launch Energia: it will work.
  23. Like
    pjkim reacted to energia in New Energia release 0101E0011 - 12/17/2013   
    @@pjkim were you able to get Energia to run with @@Rei Vilo's instructions?
  24. Like
    pjkim got a reaction from spirilis in Please add a basic tutorial for C2000 Launchpad   
    First off, I want to say I really appreciate what you (and TI) are doing in terms of reaching out to the hobbyist community. Thank you. I think that you have so much experience with the tools you work with that it becomes second nature and you don't have to think about it. I have some experience with CCS5 for the 430 Launchpad but I found ControlSuite rather foreign and unfamiliar. A tutorial to explain how to go through the demos on ControlSuite would be helpful.
     
    Perhaps you can get the woman in the product launch video to do the tutorial--call me biased but she really seemed to know what she was doing. You, not so much.
     
    Once again, thank you and congratulations on releasing your new creation to the world.
  25. Like
    pjkim got a reaction from mattanonymous in Need help with simple 'once every hour' program   
    Your circuit diagram shows the input to the MCU but not the output circuit. Are you by chance driving the buzzer/relay directly from a GPIO pin? If you are, two things.
     
    1) This would explain the buzzer working once and not the relay. Buzzers need a lot less current and can be driven directly depending on the buzzer. Relays need more than a GPIO pin can provide.
    2) Relays are inductive so if you drive them with a GPIO without a protection diode, the voltage kick at turn off will fry your output pin. This might explain why you said it worked once. Is the buzzer no longer working? You might have fried that pin.
×
×
  • Create New...