Learning to program on the 3pi robot

I’m the proud owner of a new 3pi robot, and i love it. My problem is, I need to learn to program in c without the use of the 3pi libraries. I have an STK-500 programmer, and will be using avr studio to compile and burn. I’d like to start with a simple program that makes the robot move a couple feet forward, and a couple feet back and stop. I’ll build on this starter program to do much more than that as i learn more. My question is: Does anyone know where i can get help with my simple starter program? thank you

Hello.

Are you trying to learn C programming as you learn to program the 3pi, or are you already familiar with C? Is there some reason why you don’t want to use the Pololu AVR libraries to start out with? I think it’s going to be much harder than you think to do everything from scratch since interfacing with the on-board hardware is not trivial. For example, with the Pololu AVR library, your simple starter program might look like:

#include <pololu/3pi.h>
void main()
{
  set_motors(100, 100);
  delay_ms(1000);
  set_motors(0, 0);
  while (1);  // loop forever
}

Without the library, you will need to learn how to configure the AVR’s registers to generate the proper signals on the lines that control the motor drivers. We make the source code for the library available, so you can see for yourself what’s going on behind the scenes when you call set_motors(). Is that the kind of thing you’d feel comfortable writing for your first simple starter program?

- Ben

first of all, thanks for the response. I know, and appreciate how the pololu libraries simplify programming. I’m going to my local university to learn about c programming, and I have to only use the standard AVR libraries there. After I’m proficient in programming with the standard libraries, I’ll have the luxury of using the Pololu libraries to have fun with. I realize a program like that can get pretty complicated, but the reason i posted is because I’m sure there are other students that might have had to overcome a similar problem.
I actually don’t have to make it complicated at all, I only have to prove that I can successfully program the MCU through the ISP port in their computer lab. I’m pretty sure I can accomplish this by toggling a PORT pin that’s connected to an LED. but at this moment flashing LED’s are the extent of my programming abilities. I just wanted to do something a little more interesting for my connectivity test.

Using the motor drivers well pretty much immediately gets complicated because they use hardware PWMs to achieve variable speeds. You can use the motor drivers in a simpler way (one that only requires toggling of I/O lines) if you are willing to give up intermediate speeds and just use full forward, off, or full reverse, but this will produce rather jerky motion (and could cause your 3pi to drive away, off a table, or into a wall if you’re not careful).

Here is a list of a few simple, digital I/O-based things you can do:

  1. Toggle the LEDs
  2. Play notes with the buzzer by toggling the buzzer pin at desired frequencies
  3. Respond to user pushbutton presses by flashing LEDs in a different pattern or playing different notes with the buzzer
  4. Drive the motors without PWMs

Anything more than this will require you to consult the ATmega328 datasheet and the 3pi schematic. If you need help getting your program to work, feel free to post it here and we can take a look at it.

- Ben

I would really appreciate your help. Here is what I came up with:

/* Simple connectivity program to demonstrate isp communication with the  robot */

/* not sure if I need to define crystal speed. #define F_CPU 20000000UL */

#include <avr/io.h> // avr header file for IO ports

int main(void){

unsigned char i; // temporary variable

DDRb = 0x05; // set PORTB for input switches
DDRd = 0x08; // set PORTD for output leds

while(1){
// Read input from PORTB
// We will use the two switches on PB1 and PB4
i = PINB;
// Send output to PORTD.
// We will use the 2 LEDS on PD1 and PD7
PORTD = i;
}
return 1;
}

I’m not entirely sure, but I think this will let me turn off and on 2 LEDS when you press the first 2 user pushbuttons. Do you agree so far?

Your program will not work. Where did you come up with the values for the DDRB and DDRD registers? Do you understand how the the PORT, PIN, and DDR register correspond to the I/O pins and what they do?

- Ben

I’m not sure about the DDRB and DDRD registers. I think they are for initializing ports. I do know what ports and pins are. My programming knowledge is very sketchy, that’s why I’m signed up for the programming class. I am interested in learning to program, and excited about the idea of making the robot do something besides running the demo programs. Is there a beginner online tutorial you would recommend?

I don’t really know of any good beginner AVR programming tutorials, but there are plenty of C tutorials out there. The problem with learning to program C on an AVR is that you need to learn more than just C: you need to also learn about the AVR architecture and how to use its registers. The registers are bytes (eight-bit values), and many of them require targeted bit manipulation to achieve desired results. Are you comfortable with the notion of bits and bytes? Do you have any experience with bitwise operations? Raw register manipulation on the AVR requires a lot of this, and it can be somewhat difficult for a beginner to pick up.

I can give you a very brief overview of digital I/O on the AVR, but you might also want to look at the ATmega328 datasheet.

I/O lines on the AVR are broken up in to ports, and each port has up to eight pins. On the ATmega328, there are three ports: B, C, and D.

Each port is controlled by three registers–DDRx, PORTx, and PINx–and the bits of these registers correspond to the pins of the port. For example, bit 5 of DDRB corresponds to pin PD5.

DDRx is the data direction register. It determines which port pins are outputs and which are inputs. A bit value of 1 makes a pin an output; a bit value of 0 makes the pin an input.

PORTx is determines the output state of port pins. If a pin is an output (i.e. if its corresponding DDRx bit is 1), the pin drives low when the corresponding PORTx bit is 0 and high when the PORTx bit is 1. If the pin is an input (i.e. if its corresponding DDRx bit is 0), the pin is high-impedance (or “floating”) when the corresponding PORTx bit is 0 and weakly pulled high through an internal pull-up resistor when the PORTx bit is 1.

PINx is used to read the voltage on port pins. If a PINx bit is 1, the voltage on the corresponding pin is high, else it is low.

To use these register effectively, you need to be able to set and clear register the right register bits, which in turn requires you to be comfortable with bitwise operations. If you’re already comfortable with this, please feel free to skip it, but your sample program makes me think you might not be so familiar with bits and bytes. Operators you’ll frequently use are:

<< bitshift left (the same as multiplying by 2)
>> bitshift right (the same as dividing by 2 and throwing away the remainder)
& bitwise AND
| bitwise OR
^ bitwise exclusive OR (XOR)
~ bitwise NOT

Examples:

01010001 << 2 = 01000100
01010001 >> 2 = 00010100
11000100 | 01001000 = 11001100 (since 1 | x = 1, 0 | x = x)
11000100 & 01001000 = 01000000 (since 1 & x = x, 0 & x = 0)
11000100 ^ 01001000 = 10001100 (since 0 ^ x = x, 1 ^ x = !x)
~00010000 = 11101111 (~1 = 0, ~0 = 1)

Putting it together:

Let’s say you want to set pin PD4 as an input with its pull-up enabled and pin PB2 as an output driven low. You could accomplish this with the following lines of code:

DDRD &= ~(1 << 4); // clear bit 4 of register DDRD, making PD4 an input
PORTD |= 1 << 4; // set bit 4 of register PORTD, enabling PD4 internal pull-up

DDRB |= 1 << 2; // set bit 2 of register DDRB, making PB2 an output
PORTD &= ~(1 << 2); // clear bit 2 of register PORTB, driving PB2 low

If you then want to wait until a button press drives our input PD4 low, you could do the following:

while ( (PIND & (1 << 4)) != 0)
;

Does this make sense? If not, please feel free to ask for clarification.

- Ben

Thanks for the insight. I don’t have a problem with ANDing and ORing two numbers together in your examples, but I get confused when there is an AND or an OR symbol next to an equal sign.

The line

DDRB |= 1 << 4;

is the same as writing

DDRB = DDRB | (1 << 4);

Doing this only modifies bit 4 of DDRB and leaves all other bits unchanged. The alternative of:

DDRB = 1 << 4;

sets bit 4 and clears all other bit.

One thing that made me worry about your comfort level with these sort of operations was the line you used in your proposed sample program:

DDRd = 0x08; // set PORTD for output leds

It looks like you decided that since bits 7 and 1 of DDRD control the two LED pins, you can make them both outputs by adding 7 and 1 to get 8. Rather, you must note that bit 7 has a decimal value of 128 and bit 1 has a decimal value of 1, so adding the two gives you 129, not 8. The correct way to set the two LED pins to inputs would be one of the following lines:

DDRD = 129; // LED pins = outputs, all other port D pins are inputs
DDRD = 0x81; // LED pins = outputs, all other port D pins are inputs
DDRD = (1 << 7) | (1 << 1); // LED pins = outputs, all other port D pins are inputs

or

DDRD |= 129; // LED pins = outputs, all other port D pins are unchanged
DDRD |= 0x81; // LED pins = outputs, all other port D pins are unchanged
DDRD |= (1 << 7) | (1 << 1); // LED pins = outputs, all other port D pins are unchanged

Your absolutely correct, I had a great misunderstanding in this area, and you’ve given me alot to think about and experiment with bit operations. I have a couple ATMEGA8515’s I can plug into my stk500 and do a little experimenting with bit operations before I try anything with my 3pi. You’re great Ben, Thanks a bunch for the great tips.

If you have any further questions, please ask, and if you want to run another version of your sample program by me, I can give you feedback on it. I haven’t posted a fully working version because it seems like it would be better for you to understand how to do it yourself, but if you think you’d learn more just by seeing a working example, let me know.

- Ben