Wednesday, June 26, 2013

Part 0: Automatic Camera Cable Release with an ATTINY 4313

Prev: Part 2
Next: Part 1

P A R T   0,   G E T T I N G   T H E   H A R D W A R E   U P   

A N D   R U N N I N G

My buddy wanted me to build a timer controlled cable release cable for his old school camera. 
Here's a cable release:

The left most part screws into a camera:

and the operator presses down on the trigger to take a picture. 
Anyways, he wanted a timer-based-device that uses a cable release to take a picture. The user should be able to set a count down time for when the cable release triggers. 

I N G R E D I E N T S 
  • Using an Atmel Tiny 4313 as the controller. I have a pretty good code library that I've built up, I'd rather not waste my time reading another datasheet. 
  • A 2 X 16 LCD junk character display. The good thing about these are that they all have a common interface. I actually didn't write the code to control it, I used a generic library: Generic 2x16 LCD Driver 4-bit mode
  • Parallax Servo from Radio Shack.
CREDIT: I got a couple of ideas from this hackaday post: Handya

One of the main ideas I got from Handya's post was using a hot shoe (something that mounts on top of the camera to hold my device. I thought about it, chewed on the idea and settled on: 

An ?auxiliary flasher? device from my parent's camera cabinet. They won't be missing this baby. I figured I'd be giving it a new life. 

C O N S T R U C T I O N 

The first step I took was getting my 2x16 LCD code working. Again, I got the code from here: Generic 2x16 LCD Driver 4-bit mode

C O N F I G U R I N G   T H E    L C D   C O D E

It was written to be generic. In 4-bit mode, you need 4 data lines, a register select RS line, and a LCD enable E line. Write on the LCD should always be pulled low.

So you ultimately have six GPIO pins, the original library used

#define LCD_RS    PORTD.2    // Register select
#define LCD_EN    PORTD.3    // Enable
#define LCD_D4    PORTD.4    // Data bits
#define LCD_D5    PORTD.5    // Data bits
#define LCD_D6    PORTD.6    // Data bits
#define LCD_D7    PORTD.7    // Data bits

#define    LCD_STROBE    ((LCD_EN = 1),(LCD_EN=0))

To drive each individual pin, the code does something like this:
//drive LCD_RS high
LCD_RS = 1;
LCD_RS = 0;

It's kind of like a PIC. But I'm using the avr-gcc library, which doesn't have fancy structs. So to implement this interface I had to go:

typedef struct {
   uint8_t bit0 : 1;
   uint8_t bit1 : 1;
   uint8_t bit2 : 1;
   uint8_t bit3 : 1;
   uint8_t bit4 : 1;
   uint8_t bit5 : 1;
   uint8_t bit6 : 1;
   uint8_t bit7 : 1;
} PortBits;

volatile PortBits * PORTBbits = (PortBits*) &PORTB;
volatile PortBits * PORTDbits = (PortBits*) &PORTD;

#define LCD_RS PORTDbits->bit6    // Register select
#define LCD_EN PORTBbits->bit0    // Enable
#define LCD_D4 PORTBbits->bit1    // Data bits
#define LCD_D5 PORTBbits->bit2    // Data bits
#define LCD_D6 PORTBbits->bit5    // Data bits

#define LCD_D7 PORTBbits->bit6    // Data bits

#define    LCD_STROBE    LCD_EN = 1; _delay_ms(2); LCD_EN=0

TRANSLATION: I had to make a new type, that used bit fields to separate each of the 8 bits. This allows me to set each individual bit simply by going:

PortBits port;
port.bit0 = 1;
port.bit1 = 1;

But this code above won't actually write to PORTA, or B or C. 
Since every single pointer is simply an address to a location in RAM, I simply made a pointer of the type PortBits to the address of PORTB: 

Newbies {

   PortBits * port = &PORTB;
   unsigned char * port2 = &PORTB;

   //but since the compiler will complain, I have to override it by casting:
   PortBits * port = (PortBits*) &PORTB;

port == port2, pointers simply point to addresses in RAM, all pointers are also the same size (I actually don't confidently know... it must depend on the max address of the memory?). The difference between port and port2 is how the compiler will ALLOW you to handle the pointer once it is dereferenced. Therefore, making a pointer of the type PortBits will allow me to handle the byte at location &PORTB with bit fields 

   PortBits * PORTBbits = (PortBits*) &PORTB;

  //will set PB0 high, get it?
   PORTBbits->bit0 = 1;

CODE ERRATA: Initially my display wasn't working. I basically lucked out guessing and said: 

#define    LCD_STROBE    LCD_EN = 1; _delay_ms(2);  LCD_EN=0

I would recommend playing around with the delay value, maybe even taking it out if your display isn't working. 
4313 running at 8MHz 

Conclusion: I bread boarded my LCD module and got a simple hello world message running.  


Now for the tricky part. I mean, I'm an EE/software guy, generally mechanical engineering is a tougher game to play. But this is the BOMB solution I came up with it's fricken rock solid and I didn't use any epoxy. 

I used brass hinges to mount the servo. I basically walked into a hardware store and felt my way around. I let the parts speak to me! 

And for this next part of actually mounting the cable release I have to thank Handya's post that gave me the idea of bending the shutter release trigger head like so:

Do you see it coming together??

I'd like to thank my console modding days at benheck forums for giving me the skillz to drill holes larger than the biggest drill bit I have!

H A R D W A R E   P R E - D E V E L O P M E N T

Note the tactile button on bottom left (part of user interface)

Coming up next, coding the user interface!
Part 1

No comments:

Post a Comment