Thursday, June 27, 2013

Part 2: Wrapping Up the Cable Release Project


PREV: PART 1
READ FROM BEGINNING: PART 0 

P A R T 2,   W R A P P I N G   U P

It's time to wrap up this project. Another plus from using the ?auxilary flash?  case to house my project is that it has it's own battery compartment. It takes 4 AA batteries to make 6 volts. I've been thinking about this for a while, and the servo runs just fine off 6 - 4 volts. However, my precious processor doesn't (2.7 - 5.5).  

Can't use a 7805 voltage regulator as it needs at least 7v to output 5v. 
Too lazy to buy a low drop out regulator.

I've decided that I want to use a Diode to drop the tiny's supply voltage.

For those that don't know what a diode is, it's a component that only lets current flow in one direction. And in addition to only letting current flow in one direction, there must be a constant voltage drop across it. (Something that has to do with electrons jumping the electron band gap for ya'll with a chem 101 background). Another interesting fact is that with a diode, voltage and current are independent to each other.. they do not follow ohms law. So by putting a diode in front of my processor, I drop the voltage by about 0.7v (almost all diodes have this voltage drop). What's going to keep me up at night is the inefficiency of this, because the diode consumes power by V * I = watts consumed.

P I C T U R E S

Lets face it, people wanna see the good stuff first

































Whew! You made it to part 2. You deserve my source code: 
Git Hub

Some things to mention about my code:
  • There's an example of how to get servo control running on a 4313 chip at 8MHz 
    • The servo code is messy, I do not use Timer1 to its full potential, you can potentially have two servos running at once, I didn't write that code, but it should be easy.
    • I tried to "inherit" the Timer1 fast PWM code that I wrote since the servo uses fast PWM, so you'll see that I over write register that I had just written to in another file.
  • I don't think the delay functions work properly, you are better off using the <util/delay.h> ones. 


Wednesday, June 26, 2013

Part 1 Software: Automatic Camera Cable Release with an ATTINY 4313


Prev: Part 0
Next: Part 2
P A R T 1,   S O F T W A R E   D E T A I L S

I know reading can be boring, so here's the end result of the user interface I was trying to achieve, read more about how the menu user interface works below. 

video

The necessary logic components this project requires are:
  • Timer tick for the countdown - this will be interrupt driven in the background by the 8 bit timer on the 4313. 
  • Servo control - will be driven by fast pwm on the 16 bit timer for extra precision
  • User interface
    • Buttons UP, DOWN, SELECT all of which will use interrupts
    • Menu logic
I wanted the menu to be as extendible as possible, the code should be slick and work in blocks. I'd like to approach this problem like I would in Java but in C. 

I settled on having a main menu:

typedef struct {
   uint8_t len;
   MenuItem menuItems[];
}Menu;

A Menu has items in it, that makes sense, and it has a length, which is necessary of iterating through each of the individual menuItems..
Then you've got a Menu Item:

typedef struct {
   char name[17];
   void (*action)(void);
}MenuItem;

The key to this I thought was to only allow menuItems to be flexible in the struct. And make each MenuItem a strict static size. 

Digression about structs:

The interesting thing about a struct that has an array in it with an unknown size like:
typedef struct {
   uint8_t len;
   MenuItem menuItems[];
}Menu;
Is that the compiler will actually leave this as a variable size.. That is until you instantiate it, then it has to be a fixed array. 

An also interesting aspect about the structs is that:
typedef struct {
   uint8_t len;
   MenuItem * menuItems;
}Menu;
Isn't the same thing as above. The compiler will actually (it seemed) only reserve space in the struct for the size of a pointer, not continuous block of menuItems. You'll just have to find out for yourself when you do it!

end Digression

So, we've got this flexible Menu with a bunch of menu items. 
I thought it was a cool idea to say that each MenuItem has a name, 17 chars long (16 character lcd + '\0') and that each has an action method

So in my main loop, all the code does is iterrate through a the list of menuItems. Each menuItem has a name so the code will print that to LCD. And when the user hits select, the code calls the 

menuItem[cursor]->action();  

R E A L I S T I C   S U D O   C O D E

void setTimer() { stuff }
void setHoldtime(){ stuff }
void start(){ stuff }

//Instantiation 
Menu menu = 
{5, 
   { 
   {">SET TIMER      ", &setTimer},
   {">SET HOLD TIME  ", &setHoldTime},
   {">START          ", &start},
   {">TEST           ", &test},
   {">RESET          ", &reset}
   }
};

You just call the action like this:

   (*menu.menuItems[cursor].action)();

cursor is being adjusted in the background by the user and appropriate logic to prevent cursor from going out of bounds because of menu.len 

When it comes to displaying things not selected by the cursor, I just advance the pointer of the menuItem name string by 1, this way only the index of cursor will skip over the '>'

//puts takes a char *
puts( menu.menuItems[anything but cursor index].name + 1 );

D E M O

video

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:
image

The left most part screws into a camera:
image

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;
//low
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.  


S E R V O

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