672Views29Replies

### Author Options:

Here's my setup, I'm building an electronic car that needs to go a predetermined distance for a contest. We get the distance right before the challenge, and then my team is going to program it into the avr. We get distance feedback via an encoder wheel and a phototransistor with IR LED. The phototransistor pulls the pin (PB0) up. Do I need a pulldown resistor? The code powers a motor through a transistor via PB4. There is a calabrate function so that we can enter the distance on top, the calabrate function will turn it into a useful number, and then the code will run. The motor runs until the calabrated number is reached. Unfortuanteley the code doesn't work. Any help, please? I'm probably not specific enough in this little paragraph, so please leave a comment if this doesn't make any sense.
The target uController is the attiny13/attiny13a

code (this is one of the first AVR codes i've made, with the help of other team members):

#include <stdio.h>
#include <avr/io.h>
#include <avr/delay.h>

#define mmDistance 5 // the required distance in millimeters

// For the aTTiny13a
/*
pinout:
1) PB5, RESET
2) PB3, CLK
3) PB4
4) GND
5) PB0 MOSI
6) PB1 MISO
7) PB2 SCK
8) VCC
*/

// Input from encoder wheel is PB0 (physical pin 5)
// Output to motor is PB4 (physical pin 3)

int Calibrate(int x) // Calibrate converts distance to set
{
int y;
y = x+3;
return y;
}

int main(void)
{
int clickDistance, setDistance, on = 0;

clickDistance = 0; // clickDistance is the distance travelled so far

DDRB = 0b00001000; // setting outputs and inputs 00001000

setDistance = Calibrate(mmDistance); // setDistance is set distance in clicks

PORTB = 0b00001000; // turn on motor

on = 1; //on tells if previous sense had button on or off

while(clickDistance < setDistance)

{ // while loop
if(PINB == 0b00000000 && on == 1) // button off, previous true
{
clickDistance = clickDistance+1;
// increments clickDistance by 1
on = 0;
}

if(PINB == 0b00001000)
{
on = 1;
}

}

PORTB = 0b00000000; // turn off motor
_delay_ms(5000);
return (0);
}

Tags:

## Discussions

Does the program compile correctly? I'm not familiar with any "bit notation" for C (0b00000000). You'd be better off using the avrgcc bit macros and bit definitions.

Bit value 00001000 is PB3, not PB4 ( 00010000, count from zero.)

The _BV() macro is just a shift operation. _BV(4) is (1<<4), i.e., shift a "1" left, four times. 00000001 becomes 00010000.

To set the PB4 bit:
DDRB = _BV(4); // binary 00010000

Even better, use the predefined DDR bit values in the macro:
DDRB = _BV(DDB4); // Data Direction (port)B #4

To set multiple bits, OR them:
DDRB = _BV(DDB3) | _BV(DDB4);

As constants, AVRGCC will optimize all the shifts away, and just compile the # itself.

Setting all the bits of PORTB (instead of setting individual bits):

PORTB = 0b00001000; // turn on motor

PORTB |= _BV(PB4);

Can potentially mess with the state of the input pin. Both DDRB and PORTB together are used to set the configuration of inputs (tri-state or internal pullup.)

Haven't really looked at the logic itself...

How exactly are the photo transistor and motor wired?

okay, I kinda get what your saying, but I don't know everything to change in the code. This is how it's wired up: You have an axle with wheels on it that a motor is driving. Attached to the axle is an encoder wheel (a wheel with a bunch of wholes along the rim). On one side is an IR LED that's always on. On the other side is the phototransistor that is matched to the LED. for example, if there's 360 slits in the wheel, the phototransistor will turn on and off 360 times (using this data the uController knows how far the car has traveled). Once the number of on/off happen (which our group calls clicks) the uController turns off the motor at the calibrated distance so that the car coasts to a stop at a predetermined distance (called at the define at the beginning of the code). Things about the code: the delay at the end was just for experimental purposes, trying to get the code to work. The calibrate and the mmDistance are just random numbers to see if the code worked. I don't know much about reading inputs and such, I just tried to throw stuff together from various websites. Basically the code is a counter, once the counter reaches a number a pin (that was normally high) goes low. Very simple, yet our group can't figure it out (we all know limited C, very limited, and I'm the only person that knows anythinga bout uControllers, so we are in deep doody, lol)

haha, any place where I should start I don't get all of these reading pins and how to turn them on, am I close? haha

I'll get ya started, just with the ports. See if this helps...

Setting PORTB for output on PB4:

DDRB |= _BV(DDB4);

Setting PB4:

PORTB |= _BV(PB4); // HI
PORTB &= ~_BV(PB4); // LO

Setup PB0 for input:

DDRB &= ~_BV(DDB0); // clear bit 0

Set the input config with PORTB. Yeah, that's confusing. The same register does different things, depending on the data direction...

If you want the internal pull resistor:
PORTB |= _BV(PB0); // set bit 0, enable pull-up resistor
If you want the input to float:
PORTB &= ~_BV(PB0); // clear bit 0, HI-Z

( tri-state logic, important stuff once you start connecting active devices together...)

Read the input of PB0 (adjust the if statement for 0 or 1, depends on the pin setting, and your circuit ):

if(PINB & _BV(PB0))
{ Do something; }

Gonna crash now, feeling like I'm catching a cold...

My bad-- it's obvious what PORTB is... But not DDRB; that's the Data Direction Register for PORTB. It's primary function is setting PORTB pins to either input or output.

don't catch a cold! Thatnks, that's a lot of good info can you explain the input a bit more? Thanks

Yeah, I'm a little under the weather. And I just lost my answer, and had to retype it.... #\$@##

Nice bitwise operator tutoral, AVR oriented.

Initialization (only once in main())

Clearing the bits in DDRB sets pins for input. For PB0:

DDRB &= ~_BV(DDB0);

Configuring the inputs
It's weird, but the PORTB output register is used to configure the inputs.

If you have a simple push button input, internal pullup mode works fine. It pulls the pin HI by default, and the pin is "activated" by grounding it through the switch (pulled LO.) Set PORTB bit (PB0) for pullup:
PORTB |= _BV(PB0);

You might want a floating hi-impedance input instead for a photo transistor. Clear PORTB bit (PB0) for HI-Z:

PORTB &= ~_BV(PB0);

"B" pins are read with PINB registers. Bitwise AND (&) PINB and the desired pin (PB0).

read input status of PB0 = PINB & _BV(PB0)

In an if statement: (test for set bit)
`if(PINB & _BV(PB0)){clickDistance = clickDistance+1;on = 0;} `

Use NOT (!), together with AND, to test for a cleared bit:
`if( !(PINB & _BV(PB0)) ){clickDistance = clickDistance+1;on = 0;} `

this is very good info, but it is very complicated.
i wrote this code (and replaced it with the old statements. would they work?

DDRB.3 = 0xFF; // setting pin 3 to output
DDRB.0 = 0x00; // setting pin 0 to input
PORTB.3 = 0xFF
if(PINB.0 == 0x00 && on == 1) // button off, previous true
PORTB.3 = 0x00 // turn off motor

Yes, it's a bit complicated, and C isn't very friendly to start with...

The new statements look like references to elements in a structure or a class. It's actually much simpler than that-- you were closer before.

None of these registers (PORTB, PINB, DDRB) are structures. They are simply 8-bit (one byte) hardware registers. Notice how they all end in "B"--the ATtiny13 has only 8 pins. For larger AVRs with more pins, you'll find PORTA, PORTC, PORTD, PINA, PINC, etc., etc. And each of those registers on the other chips is also 8 bits (note--not all the registers in an AVR are 8 bit, some are larger.)

Think of each register as a byte-size variable. To set the register, just feed it a number (0-255.)

But the registers are bit-fields -- each bit represents a different pin (I'm guessing you already knew that, from your original code) or a different function.

The pins are numbered from zero: PB0 to PB7 (of course, there's no PB7 or PB6 on the '13-- with VCC and GND pins, only 6 pins are left for I/O.) Bit-fields are always numbered right-to-left (the first, or zero bit is the farthest right.)

PB0 is 00000001
PB1 is 00000010
. . .
PB6 is 00100000

So all the following statements are equivalent ways of representing PB0:

00000001(as a byte, but you can't use byte notation in C)
1(as decimal)
0x01(as hex)
_BV(PB0)(using the AVR macros)

So writing:
PORTB = 1;

is exactly the same as
PORTB = _BV(PB0);

So why use the complicated bit-wise operators? Because with them, you can address (set, unset) individual bits without affecting the other bits.

Here are more equivalent statements, which set the first bit of PORTB:
PORTB |= 1;
PORTB |= _BV(PB0);
PORTB = PORTB | _BV(PB0);

This is often necessary with bit-field registers. Setting or unsetting a single bit without changing the rest can be vital.

Note that PORTB |= _BV(PB0); is a compound assignment, and is a shorthand way of saying: PORTB = PORTB | _BV(PB0); And that is the C way of saying PORTB = PORTB OR 1.

And why the heck use the AVR macros instead of simple numbers?

-- It's a consistent way of doing it.

-- Some registers have multiple uses, and a bit's function can be tied to a name, not a bit. For instance, INT0 the External Interrupt Request 0 Enable bit is the 6th bit of the GIMSK register. But it's WAY easier to remember INT0, than to go to the datasheed an lookup the bit #.

-- It's much easier to move the code to a different AVR, since the same register bit location might be in a different place in the other AVR, but have the same name and function.

-- Atmel can make changes (announced or unannounced) to the chips, and your code would still work with just a re-compile.

The statement
if(PINB.0 == 0x00 && on == 1) // button off, previous true

uses a logical AND (&&), but you need a bit-wise AND (&) instead... the logical AND will return only true or false, but the bitwise AND will work on an individual bit-by-bit fashion.

Another error in my post above:

PB0 is 00000001
PB1 is 00000010
. . .
PB6 is 00100000

Should be

PB0 is 00000001
PB1 is 00000010
. . .
PB5 is 00100000

PB5 being (of course) the sixth bit...

What's up with the PINB.0? Is this some sort of macro?

Sorry, I didn't correct that
if(PINB == 0x00 && on == 1)

And honestly, I'm wrong about the logical AND here. I just didn't read the statement carefully, and thought he followed mine (which tests a single input bit):

if(PINB & _BV(PB0))

But if(PINB == 0x00 && on == 1) test if PINB == clear, and also if on == 1. I.E., both of those tests are true/false only. A logical AND works fine here.

wow...thank you so much. it makes tons of sense now.

Thanks for that _BV macro info. I haven't used it in a while and I wasn't entirely certain about the exact way it worked. (awesome post)

Thanks-- I'm working from memory here (my own ;-) The DDRx, PORTx and PINx interactions get a little hairy...

. Do you have a debugger/monitor? If so, single-step through the program and watch your variables and I/O.

Hello-- I am in Brian's group. We are using avrstudio to compile the hex file, and avr dude to flash it. What would you suggest for a debugger/monitor, because it sounds like a great idea? Thanks

AVRstudio has a built-in debugger / simulator...

really, i couldn't figure that out... can you explain?

Compile the code :-) The "I/O View" window should be on the right side of AVRStudio. If not, open it. It displays the status of the I/O registers during debugging. Go to the "Debug" menu, and choose "Start Debugging". The "Processor" window should open on the left. That displays internal registers, status flags, etc. Now press F11 to step through your program. An arrow will move through the source code, marking exactly where execution is currently taking place. Watch how the I/O and internal registers change with each clock cycle. Sure, you can't simulate the photo transistor, but you can manually change the values of registers like PORTB with a mouse click (while the sim runs.)

Can you put the code in triple curly brackets? ({{{...}}})

btw, dwj300 is in my group for this project, so treat him nice ;)

Its just for this site. It makes monospaced font and preserves formatting. Otherwise some things might get messed up (although I think you're actually fine. Square brackets and apostrophes generally cause the most problems)

i've just had a quick read through the code, this may be wrong, but i can't see in your if statements in the while loop where the state of the photo transistor is read (it looks like your trying to check the 3rd bit as opposed to the 0 bit where you say at the start the photo transistor is connected). Also i'm not sure if it's the same with avr's as with pics but if your reading PINB it will never equal 0b00000000 because PORTB has already been set to 0b00001000 to turn on the motor, and when PINB is read it will read in the value set as the output of PORTB, but i could be wrong. I hope that makes some sense.

Yeah. Those if statements are a little wierd. I don't know if PINB inherits from PORTB, but comparing the value to a specific value is generally a bad idea.

`use:if(PINB & 0b00001000)...which is really just shortened form of:if(PINB & 0b00001000 == 0b00001000)since it could only be 0b00000000 or 0b00001000; only the latter is truthyA nicer way of writing this is:if(PINB & (1 << 3))or better yet, using the _BV macro`