Jump to content
43oh

xmob

Members
  • Content Count

    44
  • Joined

  • Last visited

  • Days Won

    1

Reputation Activity

  1. Like
    xmob got a reaction from shluzzzoid in Camera Intervalometer With LCD   
    This is the updated version of my camera intervalometer posted here.
     
    The first version was a proof of concept. This is a more feature rich version.
     

     
    What is a camera intervalometer? It's a device that makes your camera take a photo at set intervals. These photos can then be stitched together into a movie. This is often referred to as time lapse photography. A sample video created using this intervalometer is this:


     
    The hardware is what I would consider feature complete for this revision. The software is paused for now. It is functional, but there are several features/improvements that I will be adding in the future.
     
    Compatible cameras
    Currently, the intervalomter works with any Nikon camera that uses the Nikon ML-L3 remote. Support for Canon, Sony and Pentax cameras should be possible. However, I cannot test these so have not spent time on them. Shout up if you have one of the other makes of camera and can test for me!
     
    Operation
    The intervalometer has three buttons: "Menu", "Down" and "Up/Shoot"
     
    On start up the intervalometer is ready to shart shooting at a default interval of 60s. Pressing the "Up/Shoot" button will trigger the camera shutter then start the countdown to the next frame.
     
    At any time the "Menu" button can be pressed to cycle through the various menu options. There are only 2 at the moment; Interval count and interval period.
     
    Interval count is the number of seconds or minutes between frames. Use the "Down" and "Up/Shoot" button to adjust. The interval count can be set to any integer value between 1 and 99. The interval period can be configured to be either seconds or minutes.
     
    Entering the menu will cause any shooting to pause and will wait for you to restart it.
     
    During operation the LCD will display the current interval setting along with how long to go before the next shutter release. It also displays the total number of frames shot during the current session. An on board LED will flash every second to indicate that the intervalometer is currently operating.
     
    Hardware
    The hardware is built around the MSG430G2231. This particular device was chosen simply because it is supplied with the launchpad. Later versions may use a different device with more code space. The 2231 is configured to use the internal DCO clocked in such a way that we are able to get SMCLK to ~38KHz. This frequency is required for the carrier of the IR. SMCLK is connected to P1.4, which drives the IR LED via a 2N2222 transistor.
     
    To save on IO space, the LCD is driven in 4-bit mode via a 74HC595 shift register. This uses only 3 IO instead of 7 (or even 11!).
     
    The input buttons are configured to interrupt on edge changes. A really crafty debounce is possible by adding a delay after entering the interrupt then reading the input level. It's dirty, but it works in this application.
     
    To do
    The hardware supports external triggering (motion sensor, lightning sensor etc) but the software doesn't.
    The hardware supports controlling the LCD backlight but the software doesn't.
    Other camera support.
     
    Resources
    I'll add all the resources here as I've prepared them.
     
    Schematic

     
    Code

    #include "msp430g2231.h" #include #define SHUTTERBUTTON BIT5 #define DOWNBUTTON BIT3 #define MENUBUTTON BIT0 #define IROUT BIT4 #define STATUS BIT6 #define SCLOCK BIT6 #define SDATA BIT7 #define SLATCH BIT7 #define LCDRS 0x02 #define LCDRW 0x04 #define LCDE 0x08 #define SHUTTER nikonShutter() //#define SHUTTER canonShutter() //#define SHUTTER pentaxShutter() //#define SHUTTER sonyShutter() volatile int i; volatile unsigned int shutterInterval = 60; volatile unsigned int intervalCounter; volatile unsigned int delayAcc = 0; volatile unsigned int minAcc = 0; volatile char clockRunning = 0; volatile char period = 's'; volatile unsigned int shutterCount = 0; volatile unsigned int menu = 0; char buf[9] = {0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; void nikonShutter(); void shiftOut(char w, char latch); void LCDdelay(); void resetLCD(); void LCDOut (char b, char d); void LCDString (); void itoa (unsigned int val, char *str); void showMenu(); void main(void) { intervalCounter = shutterInterval; WDTCTL = WDTPW + WDTHOLD; // stop WDT DCOCTL |= DCO0 + DCO1; // DCO = ~300kHz BCSCTL1 |= RSEL0 + RSEL1; BCSCTL1 &= ~(RSEL2 + RSEL3); BCSCTL2 |= DIVS0 + DIVS1; // SMCLK / 8 is ~38kHz P1DIR |= IROUT; // Connect 1.4 to SMCLK which give us our carrier P1OUT &= ~IROUT; P1SEL &= ~IROUT; P1DIR |= STATUS; P1OUT &= ~STATUS; P1REN |= SHUTTERBUTTON + MENUBUTTON + DOWNBUTTON; // pull-up P1OUT |= SHUTTERBUTTON + MENUBUTTON + DOWNBUTTON; // pull-up P1IE |= SHUTTERBUTTON + MENUBUTTON + DOWNBUTTON; // interrupt enabled P1IES |= SHUTTERBUTTON + MENUBUTTON + DOWNBUTTON; // hi/lo edge P1IFG &= ~SHUTTERBUTTON + MENUBUTTON + DOWNBUTTON; P2SEL = 0x00; // User XIN/XOUT as output (P2.6, P2.7) P2DIR |= SCLOCK + SDATA; P1DIR |= SLATCH; // Define timer interrupt TACCR0 = 46; TACTL = TASSEL_2 + MC_1 + ID_0; TACCTL0 = CCIE; __delay_cycles(5000); resetLCD(); showMenu(); _EINT(); } void nikonShutter() { // 628, 8798, 119, 496, 125, 1128, 122, 19973 for (i = 0; i < 2; i++) { P1SEL |= IROUT; __delay_cycles(628); P1SEL &= ~IROUT; __delay_cycles(8798); P1SEL |= IROUT; __delay_cycles(119); P1SEL &= ~IROUT; __delay_cycles(496); P1SEL |= IROUT; __delay_cycles(125); P1SEL &= ~IROUT; __delay_cycles(1128); P1SEL |= IROUT; __delay_cycles(122); P1SEL &= ~IROUT; __delay_cycles(19973); } shutterCount++; clockRunning = 1; } void shiftOut(char w, char latch) { P1OUT &= ~SLATCH; for (i = 0; i < 8; i++) { if ((w & 0x80) == 0x80) { P2OUT |= SDATA; } else { P2OUT &= ~SDATA; } P2OUT |= SCLOCK; w <<= 1; P2OUT &= ~SCLOCK; } if (latch) { P1OUT |= SLATCH; P1OUT &= ~SLATCH; } } // This should be enough for most LCDs. Technically, there should be // at least three different delays but we're trying to save code space. void LCDdelay() { __delay_cycles(150); } void resetLCD() { shiftOut(0x20, 1); // Not in 4 bit mode yet so we have to do it by shifting LCDdelay(); shiftOut(0x20 + LCDE, 1); LCDdelay(); shiftOut(0x20, 1); LCDdelay(); LCDOut(0x28, 0); // 4 bit, 2 lines, 5x7 font LCDOut(0x08, 0); // Enable display, cursor off LCDOut(0x01, 0); // Clear display, move cursor home LCDOut(0x06, 0); // Move cursor LCDOut(0x0c, 0); // Display on } void LCDOut(char b, char d) { char btosend = 0; btosend = (b & 0xf0); // High nibble if (d) { btosend += LCDRS; } shiftOut(btosend, 1); LCDdelay(); shiftOut(btosend + LCDE, 1); LCDdelay(); shiftOut(btosend, 1); LCDdelay(); btosend = (b & 0x0f); // Low nibble btosend <<= 4; // Move it to the high nibble if (d) { btosend += LCDRS; } shiftOut(btosend, 1); LCDdelay(); shiftOut(btosend + LCDE, 1); LCDdelay(); shiftOut(btosend, 1); LCDdelay(); } // Takes whatever is in buf[] and sends it to the LCD void LCDString() { int dp = 0; while (buf[dp] != 0) { LCDOut(buf[dp], 1); dp++; } } void itoa(unsigned int val, char *str) { int temploc = 0; int digit = 0; int strloc = 0; char tempstr[5]; do { digit = val % 10; tempstr[temploc++] = digit + '0'; val /= 10; } while (val > 0); // The digits are now reversed. Let's fix that. while(temploc>0) str[strloc++] = tempstr[--temploc]; str[strloc]=0; } void showMenu() { LCDOut(0x02, 0); if (menu == 0) { strcpy(buf, "Shoot to\0"); LCDString(); LCDOut(0xc0, 0); // Line 2 strcpy(buf, "start. \0"); LCDString(); } if (menu == 1) { strcpy(buf, "Interval\0"); LCDString(); LCDOut(0xc0, 0); // Line 2 itoa(shutterInterval, buf); LCDString(); strcpy(buf, " \n"); LCDString(); } if (menu == 2) { strcpy(buf, "Period \0"); LCDString(); LCDOut(0xc0, 0); // Line 2 if (period == 's') { strcpy(buf, "Seconds \0"); } else { strcpy(buf, "Minutes \0"); } LCDString(); } } #pragma vector=PORT1_VECTOR __interrupt void Port_1(void) { // REALLY cheap debounce. // We can get away with this because we're edge triggering but reading the level. __delay_cycles(10000); if ((~P1IN & SHUTTERBUTTON) == SHUTTERBUTTON) { if (menu == 0) { SHUTTER; intervalCounter = shutterInterval; delayAcc = 0; minAcc = 0; } if (menu == 1) { shutterInterval++; if (shutterInterval == 100) { shutterInterval = 99; } } if (menu == 2) { if (period == 's') { period = 'm'; } else { period = 's'; } } } if ((~P1IN & MENUBUTTON) == MENUBUTTON) { clockRunning = 0; shutterCount = 0; menu++; if (menu == 3) { menu = 0; } } if ((~P1IN & DOWNBUTTON) == DOWNBUTTON) { if (menu == 1) { if (shutterInterval != 1) { shutterInterval--; } } if (menu == 2) { if (period == 's') { period = 'm'; } else { period = 's'; } } } if (!clockRunning) { showMenu(); } P1IFG &= ~(SHUTTERBUTTON + MENUBUTTON + DOWNBUTTON); } #pragma vector=TIMERA0_VECTOR __interrupt void Timer_A (void) { if (clockRunning) { delayAcc++; if (delayAcc == 836) { // Near as damn it 1Hz P1OUT ^= STATUS; LCDOut(0x02, 0); itoa(intervalCounter, buf); LCDString(); LCDOut(0x2f, 1); itoa(shutterInterval, buf); LCDString(); LCDOut(period, 1); strcpy(buf, " \0"); LCDString(); if (period == 's') { intervalCounter--; } else { // Not seconds so must be minutes minAcc++; if (minAcc == 60) { intervalCounter--; minAcc = 0; } } if (intervalCounter == 0) { intervalCounter = shutterInterval; SHUTTER; } itoa(shutterCount, buf); LCDOut(0xc0, 0); // Line 2 LCDString(); strcpy(buf, " \0"); LCDString(); delayAcc = 0; } } }
     
    Board layout

    If there's enough interest, I'll be getting several boards made up for sale. Possibly even a kit of parts.
     
    Archive of all files
    Gerber are included in this file.
    LCDIntervalometer.zip
  2. Like
    xmob got a reaction from RobG in Arduino Quadcopter   
    I know what it's like to lose a prop (motor, actually) from height. :oops:
     


  3. Like
    xmob reacted to MarkoeZ in Arduino Quadcopter   
    Aaaaaaannnnnnddddd...... A flight video!
     
    I was trying to stay low because i was testing new rubber bands for the props, at one point you see one snapping. Also trying to stay low for the camera ofc.
    But it gives a nice impression of what it can already do.
     


     
    Cheers!
  4. Like
    xmob got a reaction from MarkoeZ in Arduino Quadcopter   
    Nah. A motor was damaged (caused the crash anyway), one arm was bent/fractured and 3 props with missing blades. All the important stuff survived. It was flying again later that weekend.
  5. Like
    xmob got a reaction from MarkoeZ in Arduino Quadcopter   
    Brilliant work! That's nice stable flight you have there.
     
    I would forget using prop savers. You are hanging the weight of the quadcopter (/4) on those rubber bands. Use fixed hubs and buy shares in a prop manufacturer. Trust me, I have. :?
     
    And be confident and get some height! You'd be surprised at how much ground effect there can be.
  6. Like
    xmob got a reaction from MattTheGeek in EasyMSP   
    I've contacted HackADay to see if they'll post an article calling for help.
  7. Like
    xmob got a reaction from bolton in Cheap 4-layer PCB fabrication   
    The DorkbotPDX PCB service does 4 layer PCBs.
  8. Like
    xmob reacted to cubeberg in Maxim - FREE DS8007 Sample Kit   
    Found this while browsing on the Maxim IC website yesterday. I can't find it anymore, but I found a page yesterday listing the value of the board at ~$135. Not sure if I'll actually get one - I'll post if my order gets approved.
     
    http://www.maxim-ic.com/landing/index.mvp?lpk=215
     
    **Edit** zborgerd pointed out that this doesn't include the development board - any ideas for uses?
  9. Like
    xmob reacted to gordon in Camera Intervalometer With LCD   
    You have a bad problem with the PCB.
     

     
    (Do use DRC generously )
  10. Like
    xmob got a reaction from AaronInSpace in RC Car Electronic Speed Controller...control   
    Have you tried connecting your output to a servo?
     
    If the servo moves predictably, at least you have some confirmation that your code is on the right track.
  11. Like
    xmob got a reaction from tekdoc in Camera Intervalometer With LCD   
    This is the updated version of my camera intervalometer posted here.
     
    The first version was a proof of concept. This is a more feature rich version.
     

     
    What is a camera intervalometer? It's a device that makes your camera take a photo at set intervals. These photos can then be stitched together into a movie. This is often referred to as time lapse photography. A sample video created using this intervalometer is this:


     
    The hardware is what I would consider feature complete for this revision. The software is paused for now. It is functional, but there are several features/improvements that I will be adding in the future.
     
    Compatible cameras
    Currently, the intervalomter works with any Nikon camera that uses the Nikon ML-L3 remote. Support for Canon, Sony and Pentax cameras should be possible. However, I cannot test these so have not spent time on them. Shout up if you have one of the other makes of camera and can test for me!
     
    Operation
    The intervalometer has three buttons: "Menu", "Down" and "Up/Shoot"
     
    On start up the intervalometer is ready to shart shooting at a default interval of 60s. Pressing the "Up/Shoot" button will trigger the camera shutter then start the countdown to the next frame.
     
    At any time the "Menu" button can be pressed to cycle through the various menu options. There are only 2 at the moment; Interval count and interval period.
     
    Interval count is the number of seconds or minutes between frames. Use the "Down" and "Up/Shoot" button to adjust. The interval count can be set to any integer value between 1 and 99. The interval period can be configured to be either seconds or minutes.
     
    Entering the menu will cause any shooting to pause and will wait for you to restart it.
     
    During operation the LCD will display the current interval setting along with how long to go before the next shutter release. It also displays the total number of frames shot during the current session. An on board LED will flash every second to indicate that the intervalometer is currently operating.
     
    Hardware
    The hardware is built around the MSG430G2231. This particular device was chosen simply because it is supplied with the launchpad. Later versions may use a different device with more code space. The 2231 is configured to use the internal DCO clocked in such a way that we are able to get SMCLK to ~38KHz. This frequency is required for the carrier of the IR. SMCLK is connected to P1.4, which drives the IR LED via a 2N2222 transistor.
     
    To save on IO space, the LCD is driven in 4-bit mode via a 74HC595 shift register. This uses only 3 IO instead of 7 (or even 11!).
     
    The input buttons are configured to interrupt on edge changes. A really crafty debounce is possible by adding a delay after entering the interrupt then reading the input level. It's dirty, but it works in this application.
     
    To do
    The hardware supports external triggering (motion sensor, lightning sensor etc) but the software doesn't.
    The hardware supports controlling the LCD backlight but the software doesn't.
    Other camera support.
     
    Resources
    I'll add all the resources here as I've prepared them.
     
    Schematic

     
    Code

    #include "msp430g2231.h" #include #define SHUTTERBUTTON BIT5 #define DOWNBUTTON BIT3 #define MENUBUTTON BIT0 #define IROUT BIT4 #define STATUS BIT6 #define SCLOCK BIT6 #define SDATA BIT7 #define SLATCH BIT7 #define LCDRS 0x02 #define LCDRW 0x04 #define LCDE 0x08 #define SHUTTER nikonShutter() //#define SHUTTER canonShutter() //#define SHUTTER pentaxShutter() //#define SHUTTER sonyShutter() volatile int i; volatile unsigned int shutterInterval = 60; volatile unsigned int intervalCounter; volatile unsigned int delayAcc = 0; volatile unsigned int minAcc = 0; volatile char clockRunning = 0; volatile char period = 's'; volatile unsigned int shutterCount = 0; volatile unsigned int menu = 0; char buf[9] = {0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; void nikonShutter(); void shiftOut(char w, char latch); void LCDdelay(); void resetLCD(); void LCDOut (char b, char d); void LCDString (); void itoa (unsigned int val, char *str); void showMenu(); void main(void) { intervalCounter = shutterInterval; WDTCTL = WDTPW + WDTHOLD; // stop WDT DCOCTL |= DCO0 + DCO1; // DCO = ~300kHz BCSCTL1 |= RSEL0 + RSEL1; BCSCTL1 &= ~(RSEL2 + RSEL3); BCSCTL2 |= DIVS0 + DIVS1; // SMCLK / 8 is ~38kHz P1DIR |= IROUT; // Connect 1.4 to SMCLK which give us our carrier P1OUT &= ~IROUT; P1SEL &= ~IROUT; P1DIR |= STATUS; P1OUT &= ~STATUS; P1REN |= SHUTTERBUTTON + MENUBUTTON + DOWNBUTTON; // pull-up P1OUT |= SHUTTERBUTTON + MENUBUTTON + DOWNBUTTON; // pull-up P1IE |= SHUTTERBUTTON + MENUBUTTON + DOWNBUTTON; // interrupt enabled P1IES |= SHUTTERBUTTON + MENUBUTTON + DOWNBUTTON; // hi/lo edge P1IFG &= ~SHUTTERBUTTON + MENUBUTTON + DOWNBUTTON; P2SEL = 0x00; // User XIN/XOUT as output (P2.6, P2.7) P2DIR |= SCLOCK + SDATA; P1DIR |= SLATCH; // Define timer interrupt TACCR0 = 46; TACTL = TASSEL_2 + MC_1 + ID_0; TACCTL0 = CCIE; __delay_cycles(5000); resetLCD(); showMenu(); _EINT(); } void nikonShutter() { // 628, 8798, 119, 496, 125, 1128, 122, 19973 for (i = 0; i < 2; i++) { P1SEL |= IROUT; __delay_cycles(628); P1SEL &= ~IROUT; __delay_cycles(8798); P1SEL |= IROUT; __delay_cycles(119); P1SEL &= ~IROUT; __delay_cycles(496); P1SEL |= IROUT; __delay_cycles(125); P1SEL &= ~IROUT; __delay_cycles(1128); P1SEL |= IROUT; __delay_cycles(122); P1SEL &= ~IROUT; __delay_cycles(19973); } shutterCount++; clockRunning = 1; } void shiftOut(char w, char latch) { P1OUT &= ~SLATCH; for (i = 0; i < 8; i++) { if ((w & 0x80) == 0x80) { P2OUT |= SDATA; } else { P2OUT &= ~SDATA; } P2OUT |= SCLOCK; w <<= 1; P2OUT &= ~SCLOCK; } if (latch) { P1OUT |= SLATCH; P1OUT &= ~SLATCH; } } // This should be enough for most LCDs. Technically, there should be // at least three different delays but we're trying to save code space. void LCDdelay() { __delay_cycles(150); } void resetLCD() { shiftOut(0x20, 1); // Not in 4 bit mode yet so we have to do it by shifting LCDdelay(); shiftOut(0x20 + LCDE, 1); LCDdelay(); shiftOut(0x20, 1); LCDdelay(); LCDOut(0x28, 0); // 4 bit, 2 lines, 5x7 font LCDOut(0x08, 0); // Enable display, cursor off LCDOut(0x01, 0); // Clear display, move cursor home LCDOut(0x06, 0); // Move cursor LCDOut(0x0c, 0); // Display on } void LCDOut(char b, char d) { char btosend = 0; btosend = (b & 0xf0); // High nibble if (d) { btosend += LCDRS; } shiftOut(btosend, 1); LCDdelay(); shiftOut(btosend + LCDE, 1); LCDdelay(); shiftOut(btosend, 1); LCDdelay(); btosend = (b & 0x0f); // Low nibble btosend <<= 4; // Move it to the high nibble if (d) { btosend += LCDRS; } shiftOut(btosend, 1); LCDdelay(); shiftOut(btosend + LCDE, 1); LCDdelay(); shiftOut(btosend, 1); LCDdelay(); } // Takes whatever is in buf[] and sends it to the LCD void LCDString() { int dp = 0; while (buf[dp] != 0) { LCDOut(buf[dp], 1); dp++; } } void itoa(unsigned int val, char *str) { int temploc = 0; int digit = 0; int strloc = 0; char tempstr[5]; do { digit = val % 10; tempstr[temploc++] = digit + '0'; val /= 10; } while (val > 0); // The digits are now reversed. Let's fix that. while(temploc>0) str[strloc++] = tempstr[--temploc]; str[strloc]=0; } void showMenu() { LCDOut(0x02, 0); if (menu == 0) { strcpy(buf, "Shoot to\0"); LCDString(); LCDOut(0xc0, 0); // Line 2 strcpy(buf, "start. \0"); LCDString(); } if (menu == 1) { strcpy(buf, "Interval\0"); LCDString(); LCDOut(0xc0, 0); // Line 2 itoa(shutterInterval, buf); LCDString(); strcpy(buf, " \n"); LCDString(); } if (menu == 2) { strcpy(buf, "Period \0"); LCDString(); LCDOut(0xc0, 0); // Line 2 if (period == 's') { strcpy(buf, "Seconds \0"); } else { strcpy(buf, "Minutes \0"); } LCDString(); } } #pragma vector=PORT1_VECTOR __interrupt void Port_1(void) { // REALLY cheap debounce. // We can get away with this because we're edge triggering but reading the level. __delay_cycles(10000); if ((~P1IN & SHUTTERBUTTON) == SHUTTERBUTTON) { if (menu == 0) { SHUTTER; intervalCounter = shutterInterval; delayAcc = 0; minAcc = 0; } if (menu == 1) { shutterInterval++; if (shutterInterval == 100) { shutterInterval = 99; } } if (menu == 2) { if (period == 's') { period = 'm'; } else { period = 's'; } } } if ((~P1IN & MENUBUTTON) == MENUBUTTON) { clockRunning = 0; shutterCount = 0; menu++; if (menu == 3) { menu = 0; } } if ((~P1IN & DOWNBUTTON) == DOWNBUTTON) { if (menu == 1) { if (shutterInterval != 1) { shutterInterval--; } } if (menu == 2) { if (period == 's') { period = 'm'; } else { period = 's'; } } } if (!clockRunning) { showMenu(); } P1IFG &= ~(SHUTTERBUTTON + MENUBUTTON + DOWNBUTTON); } #pragma vector=TIMERA0_VECTOR __interrupt void Timer_A (void) { if (clockRunning) { delayAcc++; if (delayAcc == 836) { // Near as damn it 1Hz P1OUT ^= STATUS; LCDOut(0x02, 0); itoa(intervalCounter, buf); LCDString(); LCDOut(0x2f, 1); itoa(shutterInterval, buf); LCDString(); LCDOut(period, 1); strcpy(buf, " \0"); LCDString(); if (period == 's') { intervalCounter--; } else { // Not seconds so must be minutes minAcc++; if (minAcc == 60) { intervalCounter--; minAcc = 0; } } if (intervalCounter == 0) { intervalCounter = shutterInterval; SHUTTER; } itoa(shutterCount, buf); LCDOut(0xc0, 0); // Line 2 LCDString(); strcpy(buf, " \0"); LCDString(); delayAcc = 0; } } }
     
    Board layout

    If there's enough interest, I'll be getting several boards made up for sale. Possibly even a kit of parts.
     
    Archive of all files
    Gerber are included in this file.
    LCDIntervalometer.zip
  12. Like
    xmob reacted to roger430 in [ ENDED ] Aug-Sep 2011 - 43oh Project of the Month Contest   
    Magic Eight Ball
    Here's my first attempt at having the TI Launchpad do something a little more than flashing LEDs. I used this project to learn more about the MSP430 interrupts, GPIO, low power mode and timers. It operates similar to the old "Magic Eight Ball" in that you ask a question, press S2 (P1.3) on the Launchpad, watch an animated "<>" and look at the answer in the LCD display. The random seed code is adapted from a translation of TI assembler code to C by NatureTM in the "Code Vault" section of this forum. I used the same LCD Display and code that is used in the "Using 3 wires to control parallel LCD display" by RobG. I had to use different ports for the display to accommodate the random number generator code.
    This project won't change the world, however, it keeps kids busy for a while. :D
     
    ** Added delay toward beginning of main(void) to allow settling of MSP430. Prior to this change the reset button had to
    be pressed to start the program.
     
    Here are some pictures as well as the schematic and code:
     

     

     

     

    //****************************************************************************** // Magic Eight Ball Project // // Description: Simple Magic Eight Ball adaptation utilizing the MSP430 Launchpad. // To use, press S1, "Magic Eight Ball v0.03 - READY" should appear on the LCD display. // Ask your question, press S2, watch "<>" animation and an answer will appear // in the LCD display. // // 08/14/11 Improved randomness of answers. Used NatureTM's code from the 43oh // forum to generate a random seed. Requires that P1.4 and P1.5 be in the Input // mode (floating). Thanks NatureTM! // // 09/17/11 Adapted output to LCD Display. Modified code from 43oh forum "Using // 3 wires to control parallel LCD display" by RobG. Thanks Robg! Also modified // nuetron's code modification to break long lines up. Thanks nuetron! // // MSP430G2xx1 // ----------------- // /|\| XIN|- // | | | 32kHz // --|RST XOUT|- // <---|P1.3 Switch | // | CCI0B/TXD/P1.1|--------> Pin 6 of Display (E) // | P1.7|--------> Pin 1&2 of 74HC164 (AB) // | CCI0A/RXD/P1.2|--------> Pin 8 of 74HC164 (CLK) // // R.G. Rioux // September 2011 // Built with CCS Version 4.2.4.00033 //****************************************************************************** #include "msp430g2231.h" #include // rand() & srand() #include // strlen() //------------------------------------------------------------------------------ // Display related definitions //------------------------------------------------------------------------------ #define sendData(data) send(data, 1) #define sendInstruction(data) send(data, 0) #define initDisplay() sendInstruction(0x3C); sendInstruction(0x0C); clearDisplay(); sendInstruction(0x06) #define clearDisplay() sendInstruction(0x01); _delay_cycles(2000) #define DATAPIN BIT7 // P1.7 #define CLOCKPIN BIT2 // P1.2 #define ENABLEPIN BIT1 // P1.1 #define BUTTON BIT3 // Button on P1.3 //------------------------------------------------------------------------------ // Display related functions and variables //------------------------------------------------------------------------------ void send(char data, char registerSelect); void sendStr(char data[], int length); char charIndex = 0; char bitCounter = 0; //------------------------------------------------------------------------------ // Global variables in program //------------------------------------------------------------------------------ unsigned int seed; unsigned int randnum = 0; int i; int j; // indexes char msgflag = 0; // Used comma in string as a delimiter to break string up into two lines static char *answer[11] = { "Yes,in due time.", "My sources say,no.", "Definitely not.", "Yes.", "Probably.", "I have my,doubts.", "Who knows?", "Looking good!", "Go for it!", "Forget about it!", "Are you kidding?" }; // <> animation array static char *anim[8] = { " <> ", " < > ", " < > ", " < > ", " < > ", " < > ", " < > ", "< >" }; int getRandomBit(); int length = 0; int lFlag = 0; // line flag //------------------------------------------------------------------------------ // main() //------------------------------------------------------------------------------ void main(void) { WDTCTL = WDTPW + WDTHOLD; // Stop watchdog timer _delay_cycles(100000); DCOCTL = 0x00; // Set DCOCLK to 1MHz BCSCTL1 = CALBC1_1MHZ; DCOCTL = CALDCO_1MHZ; P1OUT &= ~(CLOCKPIN + DATAPIN); P1OUT &= ~(BIT0 + BIT6); P1OUT |= ENABLEPIN; // Set all pins to output except BUTTON, P1.4 and P1.5 P1DIR = 0xFF & ~(BUTTON + BIT4 + BIT5); P2OUT = 0x00; P2SEL = 0x00; P2DIR = 0xFF; P1IE |= BUTTON; //P1.3 interrupt enabled P1IFG = 0x00; //Clear all interrupt flags __enable_interrupt(); initDisplay(); sendStr("Magic Eight Ball,v0.03 - READY", 30); srand(getRandomBit()); //Random number generator seed while (1) { _bis_SR_register(LPM0_bits + GIE); if (msgflag == 1) { for (i=1;i<=5;i++) // Waste time display animated "<>" { clearDisplay(); for (j=0;j<8;j++){ sendStr(anim[j],16); __delay_cycles(150000); } lFlag ^= 1; } clearDisplay(); lFlag = 0; sendStr(answer[randnum], strlen(answer[randnum])); // Send message msgflag = 0; // Reset message flag } } } // Switch interrupt service routine #pragma vector=PORT1_VECTOR __interrupt void Port_1(void) { randnum = rand() % 11; // random number 0 to 10 // Not very random, however, adequate for // this project. msgflag = 1; // message flag P1IFG &= ~BUTTON; // reset interrupt flag _bic_SR_register_on_exit(LPM0_bits); } int getRandomBit(){ ADC10CTL1 |= INCH_5; ADC10CTL0 |= SREF_1 + ADC10SHT_1 + REFON + ADC10ON; ADC10CTL0 |= ENC + ADC10SC; while(ADC10CTL1 & ADC10BUSY); return ADC10MEM; } void send(char data, char registerSelect) { bitCounter = 0; while(bitCounter < 8) { (data & BIT7) ? (P1OUT |= DATAPIN) : (P1OUT &= ~DATAPIN); data <<= 1; P1OUT |= CLOCKPIN; P1OUT &= ~CLOCKPIN; bitCounter++; } registerSelect ? (P1OUT |= DATAPIN) : (P1OUT &= ~DATAPIN); P1OUT &= ~ENABLEPIN; P1OUT |= ENABLEPIN; } void sendStr(char string[], int size) { int charIndex = 0; lFlag == 0 ? sendInstruction(0x80) : sendInstruction(0xC0); // Select line while(charIndex < size) { // If "," appears in string use line 2 if (string[charIndex] == 0x2C) { sendInstruction(0xC0); // Second line charIndex++; // Bypass displaying comma } sendData(string[charIndex]); charIndex++; } } //------------------------------------------------------------------------------
  13. Like
    xmob got a reaction from bluehash in Camera Intervalometer With LCD   
    This is the updated version of my camera intervalometer posted here.
     
    The first version was a proof of concept. This is a more feature rich version.
     

     
    What is a camera intervalometer? It's a device that makes your camera take a photo at set intervals. These photos can then be stitched together into a movie. This is often referred to as time lapse photography. A sample video created using this intervalometer is this:


     
    The hardware is what I would consider feature complete for this revision. The software is paused for now. It is functional, but there are several features/improvements that I will be adding in the future.
     
    Compatible cameras
    Currently, the intervalomter works with any Nikon camera that uses the Nikon ML-L3 remote. Support for Canon, Sony and Pentax cameras should be possible. However, I cannot test these so have not spent time on them. Shout up if you have one of the other makes of camera and can test for me!
     
    Operation
    The intervalometer has three buttons: "Menu", "Down" and "Up/Shoot"
     
    On start up the intervalometer is ready to shart shooting at a default interval of 60s. Pressing the "Up/Shoot" button will trigger the camera shutter then start the countdown to the next frame.
     
    At any time the "Menu" button can be pressed to cycle through the various menu options. There are only 2 at the moment; Interval count and interval period.
     
    Interval count is the number of seconds or minutes between frames. Use the "Down" and "Up/Shoot" button to adjust. The interval count can be set to any integer value between 1 and 99. The interval period can be configured to be either seconds or minutes.
     
    Entering the menu will cause any shooting to pause and will wait for you to restart it.
     
    During operation the LCD will display the current interval setting along with how long to go before the next shutter release. It also displays the total number of frames shot during the current session. An on board LED will flash every second to indicate that the intervalometer is currently operating.
     
    Hardware
    The hardware is built around the MSG430G2231. This particular device was chosen simply because it is supplied with the launchpad. Later versions may use a different device with more code space. The 2231 is configured to use the internal DCO clocked in such a way that we are able to get SMCLK to ~38KHz. This frequency is required for the carrier of the IR. SMCLK is connected to P1.4, which drives the IR LED via a 2N2222 transistor.
     
    To save on IO space, the LCD is driven in 4-bit mode via a 74HC595 shift register. This uses only 3 IO instead of 7 (or even 11!).
     
    The input buttons are configured to interrupt on edge changes. A really crafty debounce is possible by adding a delay after entering the interrupt then reading the input level. It's dirty, but it works in this application.
     
    To do
    The hardware supports external triggering (motion sensor, lightning sensor etc) but the software doesn't.
    The hardware supports controlling the LCD backlight but the software doesn't.
    Other camera support.
     
    Resources
    I'll add all the resources here as I've prepared them.
     
    Schematic

     
    Code

    #include "msp430g2231.h" #include #define SHUTTERBUTTON BIT5 #define DOWNBUTTON BIT3 #define MENUBUTTON BIT0 #define IROUT BIT4 #define STATUS BIT6 #define SCLOCK BIT6 #define SDATA BIT7 #define SLATCH BIT7 #define LCDRS 0x02 #define LCDRW 0x04 #define LCDE 0x08 #define SHUTTER nikonShutter() //#define SHUTTER canonShutter() //#define SHUTTER pentaxShutter() //#define SHUTTER sonyShutter() volatile int i; volatile unsigned int shutterInterval = 60; volatile unsigned int intervalCounter; volatile unsigned int delayAcc = 0; volatile unsigned int minAcc = 0; volatile char clockRunning = 0; volatile char period = 's'; volatile unsigned int shutterCount = 0; volatile unsigned int menu = 0; char buf[9] = {0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; void nikonShutter(); void shiftOut(char w, char latch); void LCDdelay(); void resetLCD(); void LCDOut (char b, char d); void LCDString (); void itoa (unsigned int val, char *str); void showMenu(); void main(void) { intervalCounter = shutterInterval; WDTCTL = WDTPW + WDTHOLD; // stop WDT DCOCTL |= DCO0 + DCO1; // DCO = ~300kHz BCSCTL1 |= RSEL0 + RSEL1; BCSCTL1 &= ~(RSEL2 + RSEL3); BCSCTL2 |= DIVS0 + DIVS1; // SMCLK / 8 is ~38kHz P1DIR |= IROUT; // Connect 1.4 to SMCLK which give us our carrier P1OUT &= ~IROUT; P1SEL &= ~IROUT; P1DIR |= STATUS; P1OUT &= ~STATUS; P1REN |= SHUTTERBUTTON + MENUBUTTON + DOWNBUTTON; // pull-up P1OUT |= SHUTTERBUTTON + MENUBUTTON + DOWNBUTTON; // pull-up P1IE |= SHUTTERBUTTON + MENUBUTTON + DOWNBUTTON; // interrupt enabled P1IES |= SHUTTERBUTTON + MENUBUTTON + DOWNBUTTON; // hi/lo edge P1IFG &= ~SHUTTERBUTTON + MENUBUTTON + DOWNBUTTON; P2SEL = 0x00; // User XIN/XOUT as output (P2.6, P2.7) P2DIR |= SCLOCK + SDATA; P1DIR |= SLATCH; // Define timer interrupt TACCR0 = 46; TACTL = TASSEL_2 + MC_1 + ID_0; TACCTL0 = CCIE; __delay_cycles(5000); resetLCD(); showMenu(); _EINT(); } void nikonShutter() { // 628, 8798, 119, 496, 125, 1128, 122, 19973 for (i = 0; i < 2; i++) { P1SEL |= IROUT; __delay_cycles(628); P1SEL &= ~IROUT; __delay_cycles(8798); P1SEL |= IROUT; __delay_cycles(119); P1SEL &= ~IROUT; __delay_cycles(496); P1SEL |= IROUT; __delay_cycles(125); P1SEL &= ~IROUT; __delay_cycles(1128); P1SEL |= IROUT; __delay_cycles(122); P1SEL &= ~IROUT; __delay_cycles(19973); } shutterCount++; clockRunning = 1; } void shiftOut(char w, char latch) { P1OUT &= ~SLATCH; for (i = 0; i < 8; i++) { if ((w & 0x80) == 0x80) { P2OUT |= SDATA; } else { P2OUT &= ~SDATA; } P2OUT |= SCLOCK; w <<= 1; P2OUT &= ~SCLOCK; } if (latch) { P1OUT |= SLATCH; P1OUT &= ~SLATCH; } } // This should be enough for most LCDs. Technically, there should be // at least three different delays but we're trying to save code space. void LCDdelay() { __delay_cycles(150); } void resetLCD() { shiftOut(0x20, 1); // Not in 4 bit mode yet so we have to do it by shifting LCDdelay(); shiftOut(0x20 + LCDE, 1); LCDdelay(); shiftOut(0x20, 1); LCDdelay(); LCDOut(0x28, 0); // 4 bit, 2 lines, 5x7 font LCDOut(0x08, 0); // Enable display, cursor off LCDOut(0x01, 0); // Clear display, move cursor home LCDOut(0x06, 0); // Move cursor LCDOut(0x0c, 0); // Display on } void LCDOut(char b, char d) { char btosend = 0; btosend = (b & 0xf0); // High nibble if (d) { btosend += LCDRS; } shiftOut(btosend, 1); LCDdelay(); shiftOut(btosend + LCDE, 1); LCDdelay(); shiftOut(btosend, 1); LCDdelay(); btosend = (b & 0x0f); // Low nibble btosend <<= 4; // Move it to the high nibble if (d) { btosend += LCDRS; } shiftOut(btosend, 1); LCDdelay(); shiftOut(btosend + LCDE, 1); LCDdelay(); shiftOut(btosend, 1); LCDdelay(); } // Takes whatever is in buf[] and sends it to the LCD void LCDString() { int dp = 0; while (buf[dp] != 0) { LCDOut(buf[dp], 1); dp++; } } void itoa(unsigned int val, char *str) { int temploc = 0; int digit = 0; int strloc = 0; char tempstr[5]; do { digit = val % 10; tempstr[temploc++] = digit + '0'; val /= 10; } while (val > 0); // The digits are now reversed. Let's fix that. while(temploc>0) str[strloc++] = tempstr[--temploc]; str[strloc]=0; } void showMenu() { LCDOut(0x02, 0); if (menu == 0) { strcpy(buf, "Shoot to\0"); LCDString(); LCDOut(0xc0, 0); // Line 2 strcpy(buf, "start. \0"); LCDString(); } if (menu == 1) { strcpy(buf, "Interval\0"); LCDString(); LCDOut(0xc0, 0); // Line 2 itoa(shutterInterval, buf); LCDString(); strcpy(buf, " \n"); LCDString(); } if (menu == 2) { strcpy(buf, "Period \0"); LCDString(); LCDOut(0xc0, 0); // Line 2 if (period == 's') { strcpy(buf, "Seconds \0"); } else { strcpy(buf, "Minutes \0"); } LCDString(); } } #pragma vector=PORT1_VECTOR __interrupt void Port_1(void) { // REALLY cheap debounce. // We can get away with this because we're edge triggering but reading the level. __delay_cycles(10000); if ((~P1IN & SHUTTERBUTTON) == SHUTTERBUTTON) { if (menu == 0) { SHUTTER; intervalCounter = shutterInterval; delayAcc = 0; minAcc = 0; } if (menu == 1) { shutterInterval++; if (shutterInterval == 100) { shutterInterval = 99; } } if (menu == 2) { if (period == 's') { period = 'm'; } else { period = 's'; } } } if ((~P1IN & MENUBUTTON) == MENUBUTTON) { clockRunning = 0; shutterCount = 0; menu++; if (menu == 3) { menu = 0; } } if ((~P1IN & DOWNBUTTON) == DOWNBUTTON) { if (menu == 1) { if (shutterInterval != 1) { shutterInterval--; } } if (menu == 2) { if (period == 's') { period = 'm'; } else { period = 's'; } } } if (!clockRunning) { showMenu(); } P1IFG &= ~(SHUTTERBUTTON + MENUBUTTON + DOWNBUTTON); } #pragma vector=TIMERA0_VECTOR __interrupt void Timer_A (void) { if (clockRunning) { delayAcc++; if (delayAcc == 836) { // Near as damn it 1Hz P1OUT ^= STATUS; LCDOut(0x02, 0); itoa(intervalCounter, buf); LCDString(); LCDOut(0x2f, 1); itoa(shutterInterval, buf); LCDString(); LCDOut(period, 1); strcpy(buf, " \0"); LCDString(); if (period == 's') { intervalCounter--; } else { // Not seconds so must be minutes minAcc++; if (minAcc == 60) { intervalCounter--; minAcc = 0; } } if (intervalCounter == 0) { intervalCounter = shutterInterval; SHUTTER; } itoa(shutterCount, buf); LCDOut(0xc0, 0); // Line 2 LCDString(); strcpy(buf, " \0"); LCDString(); delayAcc = 0; } } }
     
    Board layout

    If there's enough interest, I'll be getting several boards made up for sale. Possibly even a kit of parts.
     
    Archive of all files
    Gerber are included in this file.
    LCDIntervalometer.zip
  14. Like
    xmob got a reaction from bluehash in [ ENDED ] Aug-Sep 2011 - 43oh Project of the Month Contest   
    My entry, a camera intervalomter.
     
    For automatically taking photos at set intervals. This allows the creation of time lapse videos.
     

     
    It uses an LCD for user feedback which is connected using a shift register. The shutter release is triggered by IR.
     

     
    Code:

    #include "msp430g2231.h" #include #define SHUTTERBUTTON BIT5 #define DOWNBUTTON BIT3 #define MENUBUTTON BIT0 #define IROUT BIT4 #define STATUS BIT6 #define SCLOCK BIT6 #define SDATA BIT7 #define SLATCH BIT7 #define LCDRS 0x02 #define LCDRW 0x04 #define LCDE 0x08 #define SHUTTER nikonShutter() //#define SHUTTER canonShutter() //#define SHUTTER pentaxShutter() //#define SHUTTER sonyShutter() volatile int i; volatile unsigned int shutterInterval = 60; volatile unsigned int intervalCounter; volatile unsigned int delayAcc = 0; volatile unsigned int minAcc = 0; volatile char clockRunning = 0; volatile char period = 's'; volatile unsigned int shutterCount = 0; volatile unsigned int menu = 0; char buf[9] = {0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; void nikonShutter(); void shiftOut(char w, char latch); void LCDdelay(); void resetLCD(); void LCDOut (char b, char d); void LCDString (); void itoa (unsigned int val, char *str); void showMenu(); void main(void) { intervalCounter = shutterInterval; WDTCTL = WDTPW + WDTHOLD; // stop WDT DCOCTL |= DCO0 + DCO1; // DCO = ~300kHz BCSCTL1 |= RSEL0 + RSEL1; BCSCTL1 &= ~(RSEL2 + RSEL3); BCSCTL2 |= DIVS0 + DIVS1; // SMCLK / 8 is ~38kHz P1DIR |= IROUT; // Connect 1.4 to SMCLK which give us our carrier P1OUT &= ~IROUT; P1SEL &= ~IROUT; P1DIR |= STATUS; P1OUT &= ~STATUS; P1REN |= SHUTTERBUTTON + MENUBUTTON + DOWNBUTTON; // pull-up P1OUT |= SHUTTERBUTTON + MENUBUTTON + DOWNBUTTON; // pull-up P1IE |= SHUTTERBUTTON + MENUBUTTON + DOWNBUTTON; // interrupt enabled P1IES |= SHUTTERBUTTON + MENUBUTTON + DOWNBUTTON; // hi/lo edge P1IFG &= ~SHUTTERBUTTON + MENUBUTTON + DOWNBUTTON; P2SEL = 0x00; // User XIN/XOUT as output (P2.6, P2.7) P2DIR |= SCLOCK + SDATA; P1DIR |= SLATCH; // Define timer interrupt TACCR0 = 46; TACTL = TASSEL_2 + MC_1 + ID_0; TACCTL0 = CCIE; __delay_cycles(5000); resetLCD(); showMenu(); _EINT(); } void nikonShutter() { // 628, 8798, 119, 496, 125, 1128, 122, 19973 for (i = 0; i < 2; i++) { P1SEL |= IROUT; __delay_cycles(628); P1SEL &= ~IROUT; __delay_cycles(8798); P1SEL |= IROUT; __delay_cycles(119); P1SEL &= ~IROUT; __delay_cycles(496); P1SEL |= IROUT; __delay_cycles(125); P1SEL &= ~IROUT; __delay_cycles(1128); P1SEL |= IROUT; __delay_cycles(122); P1SEL &= ~IROUT; __delay_cycles(19973); } shutterCount++; clockRunning = 1; } void shiftOut(char w, char latch) { P1OUT &= ~SLATCH; for (i = 0; i < 8; i++) { if ((w & 0x80) == 0x80) { P2OUT |= SDATA; } else { P2OUT &= ~SDATA; } P2OUT |= SCLOCK; w <<= 1; P2OUT &= ~SCLOCK; } if (latch) { P1OUT |= SLATCH; P1OUT &= ~SLATCH; } } // This should be enough for most LCDs. Technically, there should be // at least three different delays but we're trying to save code space. void LCDdelay() { __delay_cycles(150); } void resetLCD() { shiftOut(0x20, 1); // Not in 4 bit mode yet so we have to do it by shifting LCDdelay(); shiftOut(0x20 + LCDE, 1); LCDdelay(); shiftOut(0x20, 1); LCDdelay(); LCDOut(0x28, 0); // 4 bit, 2 lines, 5x7 font LCDOut(0x08, 0); // Enable display, cursor off LCDOut(0x01, 0); // Clear display, move cursor home LCDOut(0x06, 0); // Move cursor LCDOut(0x0c, 0); // Display on } void LCDOut(char b, char d) { char btosend = 0; btosend = (b & 0xf0); // High nibble if (d) { btosend += LCDRS; } shiftOut(btosend, 1); LCDdelay(); shiftOut(btosend + LCDE, 1); LCDdelay(); shiftOut(btosend, 1); LCDdelay(); btosend = (b & 0x0f); // Low nibble btosend <<= 4; // Move it to the high nibble if (d) { btosend += LCDRS; } shiftOut(btosend, 1); LCDdelay(); shiftOut(btosend + LCDE, 1); LCDdelay(); shiftOut(btosend, 1); LCDdelay(); } // Takes whatever is in buf[] and sends it to the LCD void LCDString() { int dp = 0; while (buf[dp] != 0) { LCDOut(buf[dp], 1); dp++; } } void itoa(unsigned int val, char *str) { int temploc = 0; int digit = 0; int strloc = 0; char tempstr[5]; do { digit = val % 10; tempstr[temploc++] = digit + '0'; val /= 10; } while (val > 0); // The digits are now reversed. Let's fix that. while(temploc>0) str[strloc++] = tempstr[--temploc]; str[strloc]=0; } void showMenu() { LCDOut(0x02, 0); if (menu == 0) { strcpy(buf, "Shoot to\0"); LCDString(); LCDOut(0xc0, 0); // Line 2 strcpy(buf, "start. \0"); LCDString(); } if (menu == 1) { strcpy(buf, "Interval\0"); LCDString(); LCDOut(0xc0, 0); // Line 2 itoa(shutterInterval, buf); LCDString(); strcpy(buf, " \n"); LCDString(); } if (menu == 2) { strcpy(buf, "Period \0"); LCDString(); LCDOut(0xc0, 0); // Line 2 if (period == 's') { strcpy(buf, "Seconds \0"); } else { strcpy(buf, "Minutes \0"); } LCDString(); } } #pragma vector=PORT1_VECTOR __interrupt void Port_1(void) { // REALLY cheap debounce. // We can get away with this because we're edge triggering but reading the level. __delay_cycles(10000); if ((~P1IN & SHUTTERBUTTON) == SHUTTERBUTTON) { if (menu == 0) { SHUTTER; intervalCounter = shutterInterval; delayAcc = 0; minAcc = 0; } if (menu == 1) { shutterInterval++; if (shutterInterval == 100) { shutterInterval = 99; } } if (menu == 2) { if (period == 's') { period = 'm'; } else { period = 's'; } } } if ((~P1IN & MENUBUTTON) == MENUBUTTON) { clockRunning = 0; shutterCount = 0; menu++; if (menu == 3) { menu = 0; } } if ((~P1IN & DOWNBUTTON) == DOWNBUTTON) { if (menu == 1) { if (shutterInterval != 1) { shutterInterval--; } } if (menu == 2) { if (period == 's') { period = 'm'; } else { period = 's'; } } } if (!clockRunning) { showMenu(); } P1IFG &= ~(SHUTTERBUTTON + MENUBUTTON + DOWNBUTTON); } #pragma vector=TIMERA0_VECTOR __interrupt void Timer_A (void) { if (clockRunning) { delayAcc++; if (delayAcc == 836) { // Near as damn it 1Hz P1OUT ^= STATUS; LCDOut(0x02, 0); itoa(intervalCounter, buf); LCDString(); LCDOut(0x2f, 1); itoa(shutterInterval, buf); LCDString(); LCDOut(period, 1); strcpy(buf, " \0"); LCDString(); if (period == 's') { intervalCounter--; } else { // Not seconds so must be minutes minAcc++; if (minAcc == 60) { intervalCounter--; minAcc = 0; } } if (intervalCounter == 0) { intervalCounter = shutterInterval; SHUTTER; } itoa(shutterCount, buf); LCDOut(0xc0, 0); // Line 2 LCDString(); strcpy(buf, " \0"); LCDString(); delayAcc = 0; } } }
     
    Full project details can be found in this thread.
  15. Like
    xmob got a reaction from gwdeveloper in Camera Intervalometer With LCD   
    This is the updated version of my camera intervalometer posted here.
     
    The first version was a proof of concept. This is a more feature rich version.
     

     
    What is a camera intervalometer? It's a device that makes your camera take a photo at set intervals. These photos can then be stitched together into a movie. This is often referred to as time lapse photography. A sample video created using this intervalometer is this:


     
    The hardware is what I would consider feature complete for this revision. The software is paused for now. It is functional, but there are several features/improvements that I will be adding in the future.
     
    Compatible cameras
    Currently, the intervalomter works with any Nikon camera that uses the Nikon ML-L3 remote. Support for Canon, Sony and Pentax cameras should be possible. However, I cannot test these so have not spent time on them. Shout up if you have one of the other makes of camera and can test for me!
     
    Operation
    The intervalometer has three buttons: "Menu", "Down" and "Up/Shoot"
     
    On start up the intervalometer is ready to shart shooting at a default interval of 60s. Pressing the "Up/Shoot" button will trigger the camera shutter then start the countdown to the next frame.
     
    At any time the "Menu" button can be pressed to cycle through the various menu options. There are only 2 at the moment; Interval count and interval period.
     
    Interval count is the number of seconds or minutes between frames. Use the "Down" and "Up/Shoot" button to adjust. The interval count can be set to any integer value between 1 and 99. The interval period can be configured to be either seconds or minutes.
     
    Entering the menu will cause any shooting to pause and will wait for you to restart it.
     
    During operation the LCD will display the current interval setting along with how long to go before the next shutter release. It also displays the total number of frames shot during the current session. An on board LED will flash every second to indicate that the intervalometer is currently operating.
     
    Hardware
    The hardware is built around the MSG430G2231. This particular device was chosen simply because it is supplied with the launchpad. Later versions may use a different device with more code space. The 2231 is configured to use the internal DCO clocked in such a way that we are able to get SMCLK to ~38KHz. This frequency is required for the carrier of the IR. SMCLK is connected to P1.4, which drives the IR LED via a 2N2222 transistor.
     
    To save on IO space, the LCD is driven in 4-bit mode via a 74HC595 shift register. This uses only 3 IO instead of 7 (or even 11!).
     
    The input buttons are configured to interrupt on edge changes. A really crafty debounce is possible by adding a delay after entering the interrupt then reading the input level. It's dirty, but it works in this application.
     
    To do
    The hardware supports external triggering (motion sensor, lightning sensor etc) but the software doesn't.
    The hardware supports controlling the LCD backlight but the software doesn't.
    Other camera support.
     
    Resources
    I'll add all the resources here as I've prepared them.
     
    Schematic

     
    Code

    #include "msp430g2231.h" #include #define SHUTTERBUTTON BIT5 #define DOWNBUTTON BIT3 #define MENUBUTTON BIT0 #define IROUT BIT4 #define STATUS BIT6 #define SCLOCK BIT6 #define SDATA BIT7 #define SLATCH BIT7 #define LCDRS 0x02 #define LCDRW 0x04 #define LCDE 0x08 #define SHUTTER nikonShutter() //#define SHUTTER canonShutter() //#define SHUTTER pentaxShutter() //#define SHUTTER sonyShutter() volatile int i; volatile unsigned int shutterInterval = 60; volatile unsigned int intervalCounter; volatile unsigned int delayAcc = 0; volatile unsigned int minAcc = 0; volatile char clockRunning = 0; volatile char period = 's'; volatile unsigned int shutterCount = 0; volatile unsigned int menu = 0; char buf[9] = {0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; void nikonShutter(); void shiftOut(char w, char latch); void LCDdelay(); void resetLCD(); void LCDOut (char b, char d); void LCDString (); void itoa (unsigned int val, char *str); void showMenu(); void main(void) { intervalCounter = shutterInterval; WDTCTL = WDTPW + WDTHOLD; // stop WDT DCOCTL |= DCO0 + DCO1; // DCO = ~300kHz BCSCTL1 |= RSEL0 + RSEL1; BCSCTL1 &= ~(RSEL2 + RSEL3); BCSCTL2 |= DIVS0 + DIVS1; // SMCLK / 8 is ~38kHz P1DIR |= IROUT; // Connect 1.4 to SMCLK which give us our carrier P1OUT &= ~IROUT; P1SEL &= ~IROUT; P1DIR |= STATUS; P1OUT &= ~STATUS; P1REN |= SHUTTERBUTTON + MENUBUTTON + DOWNBUTTON; // pull-up P1OUT |= SHUTTERBUTTON + MENUBUTTON + DOWNBUTTON; // pull-up P1IE |= SHUTTERBUTTON + MENUBUTTON + DOWNBUTTON; // interrupt enabled P1IES |= SHUTTERBUTTON + MENUBUTTON + DOWNBUTTON; // hi/lo edge P1IFG &= ~SHUTTERBUTTON + MENUBUTTON + DOWNBUTTON; P2SEL = 0x00; // User XIN/XOUT as output (P2.6, P2.7) P2DIR |= SCLOCK + SDATA; P1DIR |= SLATCH; // Define timer interrupt TACCR0 = 46; TACTL = TASSEL_2 + MC_1 + ID_0; TACCTL0 = CCIE; __delay_cycles(5000); resetLCD(); showMenu(); _EINT(); } void nikonShutter() { // 628, 8798, 119, 496, 125, 1128, 122, 19973 for (i = 0; i < 2; i++) { P1SEL |= IROUT; __delay_cycles(628); P1SEL &= ~IROUT; __delay_cycles(8798); P1SEL |= IROUT; __delay_cycles(119); P1SEL &= ~IROUT; __delay_cycles(496); P1SEL |= IROUT; __delay_cycles(125); P1SEL &= ~IROUT; __delay_cycles(1128); P1SEL |= IROUT; __delay_cycles(122); P1SEL &= ~IROUT; __delay_cycles(19973); } shutterCount++; clockRunning = 1; } void shiftOut(char w, char latch) { P1OUT &= ~SLATCH; for (i = 0; i < 8; i++) { if ((w & 0x80) == 0x80) { P2OUT |= SDATA; } else { P2OUT &= ~SDATA; } P2OUT |= SCLOCK; w <<= 1; P2OUT &= ~SCLOCK; } if (latch) { P1OUT |= SLATCH; P1OUT &= ~SLATCH; } } // This should be enough for most LCDs. Technically, there should be // at least three different delays but we're trying to save code space. void LCDdelay() { __delay_cycles(150); } void resetLCD() { shiftOut(0x20, 1); // Not in 4 bit mode yet so we have to do it by shifting LCDdelay(); shiftOut(0x20 + LCDE, 1); LCDdelay(); shiftOut(0x20, 1); LCDdelay(); LCDOut(0x28, 0); // 4 bit, 2 lines, 5x7 font LCDOut(0x08, 0); // Enable display, cursor off LCDOut(0x01, 0); // Clear display, move cursor home LCDOut(0x06, 0); // Move cursor LCDOut(0x0c, 0); // Display on } void LCDOut(char b, char d) { char btosend = 0; btosend = (b & 0xf0); // High nibble if (d) { btosend += LCDRS; } shiftOut(btosend, 1); LCDdelay(); shiftOut(btosend + LCDE, 1); LCDdelay(); shiftOut(btosend, 1); LCDdelay(); btosend = (b & 0x0f); // Low nibble btosend <<= 4; // Move it to the high nibble if (d) { btosend += LCDRS; } shiftOut(btosend, 1); LCDdelay(); shiftOut(btosend + LCDE, 1); LCDdelay(); shiftOut(btosend, 1); LCDdelay(); } // Takes whatever is in buf[] and sends it to the LCD void LCDString() { int dp = 0; while (buf[dp] != 0) { LCDOut(buf[dp], 1); dp++; } } void itoa(unsigned int val, char *str) { int temploc = 0; int digit = 0; int strloc = 0; char tempstr[5]; do { digit = val % 10; tempstr[temploc++] = digit + '0'; val /= 10; } while (val > 0); // The digits are now reversed. Let's fix that. while(temploc>0) str[strloc++] = tempstr[--temploc]; str[strloc]=0; } void showMenu() { LCDOut(0x02, 0); if (menu == 0) { strcpy(buf, "Shoot to\0"); LCDString(); LCDOut(0xc0, 0); // Line 2 strcpy(buf, "start. \0"); LCDString(); } if (menu == 1) { strcpy(buf, "Interval\0"); LCDString(); LCDOut(0xc0, 0); // Line 2 itoa(shutterInterval, buf); LCDString(); strcpy(buf, " \n"); LCDString(); } if (menu == 2) { strcpy(buf, "Period \0"); LCDString(); LCDOut(0xc0, 0); // Line 2 if (period == 's') { strcpy(buf, "Seconds \0"); } else { strcpy(buf, "Minutes \0"); } LCDString(); } } #pragma vector=PORT1_VECTOR __interrupt void Port_1(void) { // REALLY cheap debounce. // We can get away with this because we're edge triggering but reading the level. __delay_cycles(10000); if ((~P1IN & SHUTTERBUTTON) == SHUTTERBUTTON) { if (menu == 0) { SHUTTER; intervalCounter = shutterInterval; delayAcc = 0; minAcc = 0; } if (menu == 1) { shutterInterval++; if (shutterInterval == 100) { shutterInterval = 99; } } if (menu == 2) { if (period == 's') { period = 'm'; } else { period = 's'; } } } if ((~P1IN & MENUBUTTON) == MENUBUTTON) { clockRunning = 0; shutterCount = 0; menu++; if (menu == 3) { menu = 0; } } if ((~P1IN & DOWNBUTTON) == DOWNBUTTON) { if (menu == 1) { if (shutterInterval != 1) { shutterInterval--; } } if (menu == 2) { if (period == 's') { period = 'm'; } else { period = 's'; } } } if (!clockRunning) { showMenu(); } P1IFG &= ~(SHUTTERBUTTON + MENUBUTTON + DOWNBUTTON); } #pragma vector=TIMERA0_VECTOR __interrupt void Timer_A (void) { if (clockRunning) { delayAcc++; if (delayAcc == 836) { // Near as damn it 1Hz P1OUT ^= STATUS; LCDOut(0x02, 0); itoa(intervalCounter, buf); LCDString(); LCDOut(0x2f, 1); itoa(shutterInterval, buf); LCDString(); LCDOut(period, 1); strcpy(buf, " \0"); LCDString(); if (period == 's') { intervalCounter--; } else { // Not seconds so must be minutes minAcc++; if (minAcc == 60) { intervalCounter--; minAcc = 0; } } if (intervalCounter == 0) { intervalCounter = shutterInterval; SHUTTER; } itoa(shutterCount, buf); LCDOut(0xc0, 0); // Line 2 LCDString(); strcpy(buf, " \0"); LCDString(); delayAcc = 0; } } }
     
    Board layout

    If there's enough interest, I'll be getting several boards made up for sale. Possibly even a kit of parts.
     
    Archive of all files
    Gerber are included in this file.
    LCDIntervalometer.zip
  16. Like
    xmob got a reaction from xpg in Camera Intervalometer With LCD   
    This is the updated version of my camera intervalometer posted here.
     
    The first version was a proof of concept. This is a more feature rich version.
     

     
    What is a camera intervalometer? It's a device that makes your camera take a photo at set intervals. These photos can then be stitched together into a movie. This is often referred to as time lapse photography. A sample video created using this intervalometer is this:


     
    The hardware is what I would consider feature complete for this revision. The software is paused for now. It is functional, but there are several features/improvements that I will be adding in the future.
     
    Compatible cameras
    Currently, the intervalomter works with any Nikon camera that uses the Nikon ML-L3 remote. Support for Canon, Sony and Pentax cameras should be possible. However, I cannot test these so have not spent time on them. Shout up if you have one of the other makes of camera and can test for me!
     
    Operation
    The intervalometer has three buttons: "Menu", "Down" and "Up/Shoot"
     
    On start up the intervalometer is ready to shart shooting at a default interval of 60s. Pressing the "Up/Shoot" button will trigger the camera shutter then start the countdown to the next frame.
     
    At any time the "Menu" button can be pressed to cycle through the various menu options. There are only 2 at the moment; Interval count and interval period.
     
    Interval count is the number of seconds or minutes between frames. Use the "Down" and "Up/Shoot" button to adjust. The interval count can be set to any integer value between 1 and 99. The interval period can be configured to be either seconds or minutes.
     
    Entering the menu will cause any shooting to pause and will wait for you to restart it.
     
    During operation the LCD will display the current interval setting along with how long to go before the next shutter release. It also displays the total number of frames shot during the current session. An on board LED will flash every second to indicate that the intervalometer is currently operating.
     
    Hardware
    The hardware is built around the MSG430G2231. This particular device was chosen simply because it is supplied with the launchpad. Later versions may use a different device with more code space. The 2231 is configured to use the internal DCO clocked in such a way that we are able to get SMCLK to ~38KHz. This frequency is required for the carrier of the IR. SMCLK is connected to P1.4, which drives the IR LED via a 2N2222 transistor.
     
    To save on IO space, the LCD is driven in 4-bit mode via a 74HC595 shift register. This uses only 3 IO instead of 7 (or even 11!).
     
    The input buttons are configured to interrupt on edge changes. A really crafty debounce is possible by adding a delay after entering the interrupt then reading the input level. It's dirty, but it works in this application.
     
    To do
    The hardware supports external triggering (motion sensor, lightning sensor etc) but the software doesn't.
    The hardware supports controlling the LCD backlight but the software doesn't.
    Other camera support.
     
    Resources
    I'll add all the resources here as I've prepared them.
     
    Schematic

     
    Code

    #include "msp430g2231.h" #include #define SHUTTERBUTTON BIT5 #define DOWNBUTTON BIT3 #define MENUBUTTON BIT0 #define IROUT BIT4 #define STATUS BIT6 #define SCLOCK BIT6 #define SDATA BIT7 #define SLATCH BIT7 #define LCDRS 0x02 #define LCDRW 0x04 #define LCDE 0x08 #define SHUTTER nikonShutter() //#define SHUTTER canonShutter() //#define SHUTTER pentaxShutter() //#define SHUTTER sonyShutter() volatile int i; volatile unsigned int shutterInterval = 60; volatile unsigned int intervalCounter; volatile unsigned int delayAcc = 0; volatile unsigned int minAcc = 0; volatile char clockRunning = 0; volatile char period = 's'; volatile unsigned int shutterCount = 0; volatile unsigned int menu = 0; char buf[9] = {0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; void nikonShutter(); void shiftOut(char w, char latch); void LCDdelay(); void resetLCD(); void LCDOut (char b, char d); void LCDString (); void itoa (unsigned int val, char *str); void showMenu(); void main(void) { intervalCounter = shutterInterval; WDTCTL = WDTPW + WDTHOLD; // stop WDT DCOCTL |= DCO0 + DCO1; // DCO = ~300kHz BCSCTL1 |= RSEL0 + RSEL1; BCSCTL1 &= ~(RSEL2 + RSEL3); BCSCTL2 |= DIVS0 + DIVS1; // SMCLK / 8 is ~38kHz P1DIR |= IROUT; // Connect 1.4 to SMCLK which give us our carrier P1OUT &= ~IROUT; P1SEL &= ~IROUT; P1DIR |= STATUS; P1OUT &= ~STATUS; P1REN |= SHUTTERBUTTON + MENUBUTTON + DOWNBUTTON; // pull-up P1OUT |= SHUTTERBUTTON + MENUBUTTON + DOWNBUTTON; // pull-up P1IE |= SHUTTERBUTTON + MENUBUTTON + DOWNBUTTON; // interrupt enabled P1IES |= SHUTTERBUTTON + MENUBUTTON + DOWNBUTTON; // hi/lo edge P1IFG &= ~SHUTTERBUTTON + MENUBUTTON + DOWNBUTTON; P2SEL = 0x00; // User XIN/XOUT as output (P2.6, P2.7) P2DIR |= SCLOCK + SDATA; P1DIR |= SLATCH; // Define timer interrupt TACCR0 = 46; TACTL = TASSEL_2 + MC_1 + ID_0; TACCTL0 = CCIE; __delay_cycles(5000); resetLCD(); showMenu(); _EINT(); } void nikonShutter() { // 628, 8798, 119, 496, 125, 1128, 122, 19973 for (i = 0; i < 2; i++) { P1SEL |= IROUT; __delay_cycles(628); P1SEL &= ~IROUT; __delay_cycles(8798); P1SEL |= IROUT; __delay_cycles(119); P1SEL &= ~IROUT; __delay_cycles(496); P1SEL |= IROUT; __delay_cycles(125); P1SEL &= ~IROUT; __delay_cycles(1128); P1SEL |= IROUT; __delay_cycles(122); P1SEL &= ~IROUT; __delay_cycles(19973); } shutterCount++; clockRunning = 1; } void shiftOut(char w, char latch) { P1OUT &= ~SLATCH; for (i = 0; i < 8; i++) { if ((w & 0x80) == 0x80) { P2OUT |= SDATA; } else { P2OUT &= ~SDATA; } P2OUT |= SCLOCK; w <<= 1; P2OUT &= ~SCLOCK; } if (latch) { P1OUT |= SLATCH; P1OUT &= ~SLATCH; } } // This should be enough for most LCDs. Technically, there should be // at least three different delays but we're trying to save code space. void LCDdelay() { __delay_cycles(150); } void resetLCD() { shiftOut(0x20, 1); // Not in 4 bit mode yet so we have to do it by shifting LCDdelay(); shiftOut(0x20 + LCDE, 1); LCDdelay(); shiftOut(0x20, 1); LCDdelay(); LCDOut(0x28, 0); // 4 bit, 2 lines, 5x7 font LCDOut(0x08, 0); // Enable display, cursor off LCDOut(0x01, 0); // Clear display, move cursor home LCDOut(0x06, 0); // Move cursor LCDOut(0x0c, 0); // Display on } void LCDOut(char b, char d) { char btosend = 0; btosend = (b & 0xf0); // High nibble if (d) { btosend += LCDRS; } shiftOut(btosend, 1); LCDdelay(); shiftOut(btosend + LCDE, 1); LCDdelay(); shiftOut(btosend, 1); LCDdelay(); btosend = (b & 0x0f); // Low nibble btosend <<= 4; // Move it to the high nibble if (d) { btosend += LCDRS; } shiftOut(btosend, 1); LCDdelay(); shiftOut(btosend + LCDE, 1); LCDdelay(); shiftOut(btosend, 1); LCDdelay(); } // Takes whatever is in buf[] and sends it to the LCD void LCDString() { int dp = 0; while (buf[dp] != 0) { LCDOut(buf[dp], 1); dp++; } } void itoa(unsigned int val, char *str) { int temploc = 0; int digit = 0; int strloc = 0; char tempstr[5]; do { digit = val % 10; tempstr[temploc++] = digit + '0'; val /= 10; } while (val > 0); // The digits are now reversed. Let's fix that. while(temploc>0) str[strloc++] = tempstr[--temploc]; str[strloc]=0; } void showMenu() { LCDOut(0x02, 0); if (menu == 0) { strcpy(buf, "Shoot to\0"); LCDString(); LCDOut(0xc0, 0); // Line 2 strcpy(buf, "start. \0"); LCDString(); } if (menu == 1) { strcpy(buf, "Interval\0"); LCDString(); LCDOut(0xc0, 0); // Line 2 itoa(shutterInterval, buf); LCDString(); strcpy(buf, " \n"); LCDString(); } if (menu == 2) { strcpy(buf, "Period \0"); LCDString(); LCDOut(0xc0, 0); // Line 2 if (period == 's') { strcpy(buf, "Seconds \0"); } else { strcpy(buf, "Minutes \0"); } LCDString(); } } #pragma vector=PORT1_VECTOR __interrupt void Port_1(void) { // REALLY cheap debounce. // We can get away with this because we're edge triggering but reading the level. __delay_cycles(10000); if ((~P1IN & SHUTTERBUTTON) == SHUTTERBUTTON) { if (menu == 0) { SHUTTER; intervalCounter = shutterInterval; delayAcc = 0; minAcc = 0; } if (menu == 1) { shutterInterval++; if (shutterInterval == 100) { shutterInterval = 99; } } if (menu == 2) { if (period == 's') { period = 'm'; } else { period = 's'; } } } if ((~P1IN & MENUBUTTON) == MENUBUTTON) { clockRunning = 0; shutterCount = 0; menu++; if (menu == 3) { menu = 0; } } if ((~P1IN & DOWNBUTTON) == DOWNBUTTON) { if (menu == 1) { if (shutterInterval != 1) { shutterInterval--; } } if (menu == 2) { if (period == 's') { period = 'm'; } else { period = 's'; } } } if (!clockRunning) { showMenu(); } P1IFG &= ~(SHUTTERBUTTON + MENUBUTTON + DOWNBUTTON); } #pragma vector=TIMERA0_VECTOR __interrupt void Timer_A (void) { if (clockRunning) { delayAcc++; if (delayAcc == 836) { // Near as damn it 1Hz P1OUT ^= STATUS; LCDOut(0x02, 0); itoa(intervalCounter, buf); LCDString(); LCDOut(0x2f, 1); itoa(shutterInterval, buf); LCDString(); LCDOut(period, 1); strcpy(buf, " \0"); LCDString(); if (period == 's') { intervalCounter--; } else { // Not seconds so must be minutes minAcc++; if (minAcc == 60) { intervalCounter--; minAcc = 0; } } if (intervalCounter == 0) { intervalCounter = shutterInterval; SHUTTER; } itoa(shutterCount, buf); LCDOut(0xc0, 0); // Line 2 LCDString(); strcpy(buf, " \0"); LCDString(); delayAcc = 0; } } }
     
    Board layout

    If there's enough interest, I'll be getting several boards made up for sale. Possibly even a kit of parts.
     
    Archive of all files
    Gerber are included in this file.
    LCDIntervalometer.zip
  17. Like
    xmob got a reaction from RobG in Camera Intervalometer With LCD   
    This is the updated version of my camera intervalometer posted here.
     
    The first version was a proof of concept. This is a more feature rich version.
     

     
    What is a camera intervalometer? It's a device that makes your camera take a photo at set intervals. These photos can then be stitched together into a movie. This is often referred to as time lapse photography. A sample video created using this intervalometer is this:


     
    The hardware is what I would consider feature complete for this revision. The software is paused for now. It is functional, but there are several features/improvements that I will be adding in the future.
     
    Compatible cameras
    Currently, the intervalomter works with any Nikon camera that uses the Nikon ML-L3 remote. Support for Canon, Sony and Pentax cameras should be possible. However, I cannot test these so have not spent time on them. Shout up if you have one of the other makes of camera and can test for me!
     
    Operation
    The intervalometer has three buttons: "Menu", "Down" and "Up/Shoot"
     
    On start up the intervalometer is ready to shart shooting at a default interval of 60s. Pressing the "Up/Shoot" button will trigger the camera shutter then start the countdown to the next frame.
     
    At any time the "Menu" button can be pressed to cycle through the various menu options. There are only 2 at the moment; Interval count and interval period.
     
    Interval count is the number of seconds or minutes between frames. Use the "Down" and "Up/Shoot" button to adjust. The interval count can be set to any integer value between 1 and 99. The interval period can be configured to be either seconds or minutes.
     
    Entering the menu will cause any shooting to pause and will wait for you to restart it.
     
    During operation the LCD will display the current interval setting along with how long to go before the next shutter release. It also displays the total number of frames shot during the current session. An on board LED will flash every second to indicate that the intervalometer is currently operating.
     
    Hardware
    The hardware is built around the MSG430G2231. This particular device was chosen simply because it is supplied with the launchpad. Later versions may use a different device with more code space. The 2231 is configured to use the internal DCO clocked in such a way that we are able to get SMCLK to ~38KHz. This frequency is required for the carrier of the IR. SMCLK is connected to P1.4, which drives the IR LED via a 2N2222 transistor.
     
    To save on IO space, the LCD is driven in 4-bit mode via a 74HC595 shift register. This uses only 3 IO instead of 7 (or even 11!).
     
    The input buttons are configured to interrupt on edge changes. A really crafty debounce is possible by adding a delay after entering the interrupt then reading the input level. It's dirty, but it works in this application.
     
    To do
    The hardware supports external triggering (motion sensor, lightning sensor etc) but the software doesn't.
    The hardware supports controlling the LCD backlight but the software doesn't.
    Other camera support.
     
    Resources
    I'll add all the resources here as I've prepared them.
     
    Schematic

     
    Code

    #include "msp430g2231.h" #include #define SHUTTERBUTTON BIT5 #define DOWNBUTTON BIT3 #define MENUBUTTON BIT0 #define IROUT BIT4 #define STATUS BIT6 #define SCLOCK BIT6 #define SDATA BIT7 #define SLATCH BIT7 #define LCDRS 0x02 #define LCDRW 0x04 #define LCDE 0x08 #define SHUTTER nikonShutter() //#define SHUTTER canonShutter() //#define SHUTTER pentaxShutter() //#define SHUTTER sonyShutter() volatile int i; volatile unsigned int shutterInterval = 60; volatile unsigned int intervalCounter; volatile unsigned int delayAcc = 0; volatile unsigned int minAcc = 0; volatile char clockRunning = 0; volatile char period = 's'; volatile unsigned int shutterCount = 0; volatile unsigned int menu = 0; char buf[9] = {0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; void nikonShutter(); void shiftOut(char w, char latch); void LCDdelay(); void resetLCD(); void LCDOut (char b, char d); void LCDString (); void itoa (unsigned int val, char *str); void showMenu(); void main(void) { intervalCounter = shutterInterval; WDTCTL = WDTPW + WDTHOLD; // stop WDT DCOCTL |= DCO0 + DCO1; // DCO = ~300kHz BCSCTL1 |= RSEL0 + RSEL1; BCSCTL1 &= ~(RSEL2 + RSEL3); BCSCTL2 |= DIVS0 + DIVS1; // SMCLK / 8 is ~38kHz P1DIR |= IROUT; // Connect 1.4 to SMCLK which give us our carrier P1OUT &= ~IROUT; P1SEL &= ~IROUT; P1DIR |= STATUS; P1OUT &= ~STATUS; P1REN |= SHUTTERBUTTON + MENUBUTTON + DOWNBUTTON; // pull-up P1OUT |= SHUTTERBUTTON + MENUBUTTON + DOWNBUTTON; // pull-up P1IE |= SHUTTERBUTTON + MENUBUTTON + DOWNBUTTON; // interrupt enabled P1IES |= SHUTTERBUTTON + MENUBUTTON + DOWNBUTTON; // hi/lo edge P1IFG &= ~SHUTTERBUTTON + MENUBUTTON + DOWNBUTTON; P2SEL = 0x00; // User XIN/XOUT as output (P2.6, P2.7) P2DIR |= SCLOCK + SDATA; P1DIR |= SLATCH; // Define timer interrupt TACCR0 = 46; TACTL = TASSEL_2 + MC_1 + ID_0; TACCTL0 = CCIE; __delay_cycles(5000); resetLCD(); showMenu(); _EINT(); } void nikonShutter() { // 628, 8798, 119, 496, 125, 1128, 122, 19973 for (i = 0; i < 2; i++) { P1SEL |= IROUT; __delay_cycles(628); P1SEL &= ~IROUT; __delay_cycles(8798); P1SEL |= IROUT; __delay_cycles(119); P1SEL &= ~IROUT; __delay_cycles(496); P1SEL |= IROUT; __delay_cycles(125); P1SEL &= ~IROUT; __delay_cycles(1128); P1SEL |= IROUT; __delay_cycles(122); P1SEL &= ~IROUT; __delay_cycles(19973); } shutterCount++; clockRunning = 1; } void shiftOut(char w, char latch) { P1OUT &= ~SLATCH; for (i = 0; i < 8; i++) { if ((w & 0x80) == 0x80) { P2OUT |= SDATA; } else { P2OUT &= ~SDATA; } P2OUT |= SCLOCK; w <<= 1; P2OUT &= ~SCLOCK; } if (latch) { P1OUT |= SLATCH; P1OUT &= ~SLATCH; } } // This should be enough for most LCDs. Technically, there should be // at least three different delays but we're trying to save code space. void LCDdelay() { __delay_cycles(150); } void resetLCD() { shiftOut(0x20, 1); // Not in 4 bit mode yet so we have to do it by shifting LCDdelay(); shiftOut(0x20 + LCDE, 1); LCDdelay(); shiftOut(0x20, 1); LCDdelay(); LCDOut(0x28, 0); // 4 bit, 2 lines, 5x7 font LCDOut(0x08, 0); // Enable display, cursor off LCDOut(0x01, 0); // Clear display, move cursor home LCDOut(0x06, 0); // Move cursor LCDOut(0x0c, 0); // Display on } void LCDOut(char b, char d) { char btosend = 0; btosend = (b & 0xf0); // High nibble if (d) { btosend += LCDRS; } shiftOut(btosend, 1); LCDdelay(); shiftOut(btosend + LCDE, 1); LCDdelay(); shiftOut(btosend, 1); LCDdelay(); btosend = (b & 0x0f); // Low nibble btosend <<= 4; // Move it to the high nibble if (d) { btosend += LCDRS; } shiftOut(btosend, 1); LCDdelay(); shiftOut(btosend + LCDE, 1); LCDdelay(); shiftOut(btosend, 1); LCDdelay(); } // Takes whatever is in buf[] and sends it to the LCD void LCDString() { int dp = 0; while (buf[dp] != 0) { LCDOut(buf[dp], 1); dp++; } } void itoa(unsigned int val, char *str) { int temploc = 0; int digit = 0; int strloc = 0; char tempstr[5]; do { digit = val % 10; tempstr[temploc++] = digit + '0'; val /= 10; } while (val > 0); // The digits are now reversed. Let's fix that. while(temploc>0) str[strloc++] = tempstr[--temploc]; str[strloc]=0; } void showMenu() { LCDOut(0x02, 0); if (menu == 0) { strcpy(buf, "Shoot to\0"); LCDString(); LCDOut(0xc0, 0); // Line 2 strcpy(buf, "start. \0"); LCDString(); } if (menu == 1) { strcpy(buf, "Interval\0"); LCDString(); LCDOut(0xc0, 0); // Line 2 itoa(shutterInterval, buf); LCDString(); strcpy(buf, " \n"); LCDString(); } if (menu == 2) { strcpy(buf, "Period \0"); LCDString(); LCDOut(0xc0, 0); // Line 2 if (period == 's') { strcpy(buf, "Seconds \0"); } else { strcpy(buf, "Minutes \0"); } LCDString(); } } #pragma vector=PORT1_VECTOR __interrupt void Port_1(void) { // REALLY cheap debounce. // We can get away with this because we're edge triggering but reading the level. __delay_cycles(10000); if ((~P1IN & SHUTTERBUTTON) == SHUTTERBUTTON) { if (menu == 0) { SHUTTER; intervalCounter = shutterInterval; delayAcc = 0; minAcc = 0; } if (menu == 1) { shutterInterval++; if (shutterInterval == 100) { shutterInterval = 99; } } if (menu == 2) { if (period == 's') { period = 'm'; } else { period = 's'; } } } if ((~P1IN & MENUBUTTON) == MENUBUTTON) { clockRunning = 0; shutterCount = 0; menu++; if (menu == 3) { menu = 0; } } if ((~P1IN & DOWNBUTTON) == DOWNBUTTON) { if (menu == 1) { if (shutterInterval != 1) { shutterInterval--; } } if (menu == 2) { if (period == 's') { period = 'm'; } else { period = 's'; } } } if (!clockRunning) { showMenu(); } P1IFG &= ~(SHUTTERBUTTON + MENUBUTTON + DOWNBUTTON); } #pragma vector=TIMERA0_VECTOR __interrupt void Timer_A (void) { if (clockRunning) { delayAcc++; if (delayAcc == 836) { // Near as damn it 1Hz P1OUT ^= STATUS; LCDOut(0x02, 0); itoa(intervalCounter, buf); LCDString(); LCDOut(0x2f, 1); itoa(shutterInterval, buf); LCDString(); LCDOut(period, 1); strcpy(buf, " \0"); LCDString(); if (period == 's') { intervalCounter--; } else { // Not seconds so must be minutes minAcc++; if (minAcc == 60) { intervalCounter--; minAcc = 0; } } if (intervalCounter == 0) { intervalCounter = shutterInterval; SHUTTER; } itoa(shutterCount, buf); LCDOut(0xc0, 0); // Line 2 LCDString(); strcpy(buf, " \0"); LCDString(); delayAcc = 0; } } }
     
    Board layout

    If there's enough interest, I'll be getting several boards made up for sale. Possibly even a kit of parts.
     
    Archive of all files
    Gerber are included in this file.
    LCDIntervalometer.zip
  18. Like
    xmob got a reaction from bluehash in Camera Intervalometer   
    I've got my version 2 intervalometer up and running and gave it a quick test this evening. It seems to work okay.
     
    I've just got to finish the PCB layout and I'll create a new project thread for the version 2. For now, here's my first ever time lapse video:
     


  19. Like
    xmob got a reaction from zeke in Camera Intervalometer   
    I've got my version 2 intervalometer up and running and gave it a quick test this evening. It seems to work okay.
     
    I've just got to finish the PCB layout and I'll create a new project thread for the version 2. For now, here's my first ever time lapse video:
     


  20. Like
    xmob got a reaction from MarkoeZ in Shopping for a Bluetooth Module. Any experiences?   
    Well, I've ordered a couple now. I don't need any. And I already have others. :oops:
     
    Here's the datasheet.
     
    And the AT command set.
  21. Like
    xmob got a reaction from xpg in Shopping for a Bluetooth Module. Any experiences?   
    Have you seen these?
     
    They are very similar to the RN41. I have used one of these on a previous project and it is relatively easy to solder header cables to the module.
  22. Like
    xmob got a reaction from DanAndDusty in Shopping for a Bluetooth Module. Any experiences?   
    Have you seen these?
     
    They are very similar to the RN41. I have used one of these on a previous project and it is relatively easy to solder header cables to the module.
  23. Like
    xmob got a reaction from MarkoeZ in Shopping for a Bluetooth Module. Any experiences?   
    Have you seen these?
     
    They are very similar to the RN41. I have used one of these on a previous project and it is relatively easy to solder header cables to the module.
  24. Like
    xmob reacted to RobG in TI MSP430 Family Eagle library   
    I was little annoyed by the existing MSP430 libraries so I decided to modify them and create my own.
    Here's my first version of it. Let me know what you think.

    Updated May 7 2012
    Added 2210 & 2230

    Updated May 16 2012
    Added FR5739 (TSSOP 38)

    Updated June 20 2012
    Fixed two 14 pin devices. Pin 8 was assigned to pin 10 and vice versa.
    Affected devices: MSP430G2X31-14PW14 and MSP430G2X21-14PW14
     
    Updated March 26 2013
    Added:
    G2x55 TSSOP 38 DA
    G2x44 TSSOP 38 DA
    G2x44 PDIP 40 N

     
     
    ti_msp430.lbr
  25. Like
    xmob got a reaction from bluehash in Camera Intervalometer   
    Code, as promised.
     
    Again, the code is defunct and will be changing soon. It's also very heavily commented. :oops:
     

    #include "msp430g2231.h" #define SHUTTERIND BIT0 #define SHUTTERBUTTON BIT3 #define IROUT BIT4 #define STATUS BIT6 #define SCLOCK BIT6 #define SDATA BIT7 #define SLATCH BIT7 #define SHUTTER nikonShutter() //#define SHUTTER canonShutter() //#define SHUTTER pentaxShutter() //#define SHUTTER sonyShutter() volatile int i; volatile int shutterInterval = 30; volatile int intervalCounter; volatile int delayAcc = 0; unsigned char digits[10] = {0x3f, 0x06, 0x5b, 0x4f, 0x66, 0x6d, 0x7d, 0x07, 0x7f, 0x6f}; void nikonShutter(); void shiftOut(char w, char latch); void main(void) { intervalCounter = shutterInterval; WDTCTL = WDTPW + WDTHOLD; // stop WDT DCOCTL |= DCO0 + DCO1; BCSCTL1 |= RSEL0 + RSEL1; BCSCTL1 &= ~(RSEL2 + RSEL3); BCSCTL2 |= DIVS0 + DIVS1; P1DIR |= IROUT; P1OUT &= ~IROUT; P1SEL &= ~IROUT; P1DIR |= SHUTTERIND; P1OUT &= ~SHUTTERIND; P1DIR |= STATUS; P1OUT &= ~STATUS; P1REN |= SHUTTERBUTTON; P1OUT |= SHUTTERBUTTON; P1IE |= SHUTTERBUTTON; P1IES |= SHUTTERBUTTON; P1IFG &= ~SHUTTERBUTTON; P2SEL = 0x00; // User XIN/XOUT as output (P2.6, P2.7) P2DIR |= SCLOCK + SDATA; P1DIR |= SLATCH; TACCR0 = 46; TACTL = TASSEL_2 + MC_1 + ID_0; TACCTL0 = CCIE; _EINT(); } void nikonShutter() { P1OUT |= SHUTTERIND; // 628, 8798, 119, 496, 125, 1128, 122, 19973 for (i = 0; i < 2; i++) { P1SEL |= IROUT; __delay_cycles(628); P1SEL &= ~IROUT; __delay_cycles(8798); P1SEL |= IROUT; __delay_cycles(119); P1SEL &= ~IROUT; __delay_cycles(496); P1SEL |= IROUT; __delay_cycles(125); P1SEL &= ~IROUT; __delay_cycles(1128); P1SEL |= IROUT; __delay_cycles(122); P1SEL &= ~IROUT; __delay_cycles(19973); } P1OUT &= ~SHUTTERIND; } void shiftOut(char w, char latch) { P1OUT &= ~SLATCH; for (i = 0; i < 8; i++) { if ((w & 0x80) == 0x80) { P2OUT |= SDATA; } else { P2OUT &= ~SDATA; } P2OUT |= SCLOCK; w <<= 1; P2OUT &= ~SCLOCK; } if (latch) { P1OUT |= SLATCH; P1OUT &= ~SLATCH; } } #pragma vector=PORT1_VECTOR __interrupt void Port_1(void) { SHUTTER; P1IFG &= ~SHUTTERBUTTON; P2SEL = 0x00; } #pragma vector=TIMERA0_VECTOR __interrupt void Timer_A (void) { delayAcc++; if (delayAcc == 836) { // Near as damn it 1Hz P1OUT ^= STATUS; shiftOut(digits[intervalCounter % 10], 0); // 1's shiftOut(digits[intervalCounter / 10], 1); // 10's intervalCounter--; if (intervalCounter == -1) { intervalCounter = shutterInterval; SHUTTER; } delayAcc = 0; } }
×
×
  • Create New...