Wednesday, February 20, 2008

Variable Speed Motor Control

Pulse Width Modulation (PWM) is a way to control the speed of a motor.  The following example program, pwm.c, demonstrates PWM with the Atmega32.  A motor is run in the forward direction from slow-->fast-->slow, and then in the reverse direction from slow-->fast-->slow. 

In order for the program to work, the microprocessor needs to be connected to some circuitry to drive the motor, probably an H-Bridge, which allows bi-directional control of a motor (e.g. you can get it to spin in either direction, by way of sending current forward or backwards through the circuit).

Here are some kinds of motor-driving H-Bridges:


general reference on H-Bridges: http://www.discovercircuits.com/H/hbridge.htm

---------------------

//

// pwm.c

//

// a simple program to control the speed of a DC motor in the forward and reverse directions, using Pulse Width Modulation (PWM).

// This program requires an hbridge motor driver (such as L6203 or LMD18200), a 12-24V DC motor, and a sufficient power supply (12-24V).

//

// rtwomey@ucsd.edu

//



// includes

#include <avr/io.h> // include I/O definitions (port names, pin names, etc)

#include <avr/interrupt.h> // include interrupt support


#include "global.h" // include our global settings

#include "uart.h" // include uart function library

#include "rprintf.h" // include printf function library

#include "timer.h" // include timer function library (timing, PWM, etc)


// function prototypes

int pwmDemo(void);


// program

int main(void)

{

// initialize the UART (serial port)

uartInit();

// set the baud rate of the UART for our debug/reporting output

uartSetBaudRate(57600);

// initialize rprintf system

rprintfInit(uartSendByte);

// initialize the timer system

timerInit();

// initialize pins for PWM

// set OC1B (PD4) to output for PWM

// set OC1A (PD5) to output for PWM

// set PD6 to output for DIR

// set PD7 to output for BRAKE

sbi(DDRD, PD4);

sbi(DDRD, PD5);

sbi(DDRD, PD6);

sbi(DDRD, PD7);


// turn off pull-up resistor on those pins

cbi(PORTD, PD4);

cbi(PORTD, PD5);

cbi(PORTD, PD6);

cbi(PORTD, PD7);

// print a welcome message so we know things are working

rprintf("rnnnWelcome to the PWM demonstration!rn");


// run the demo

while(1) {

pwmDemo();

};

return 0;

}


int pwmDemo(void)

{

// PWM demo cycle

int i=0;

// initialize timer1 for PWM output, 8 bit resolution.

rprintf("Initializing timer1 for PWMrn");

timer1PWMInit(8);


// turn on the channel A PWM output of timer1

// - this signal will come out on the OC1A I/O pin (PD5)

rprintf("Turning on timer1 channel A PWM outputrn");

timer1PWMAOn();


// turn on the channel B PWM output of timer1

// - this signal will come out on the OC1B I/O pin (PD4)

rprintf("Turning on timer1 channel B PWM outputrn");

timer1PWMBOn();


// set direction to 1 (forward);

//rprintf("Setting direction pin (PD6) to forward (1)rn");

//sbi(PORTD, PD6);


// set brake to 0 (no braking)

//rprintf("Setting brake pin (PD7) to no braking (0)rn");

//cbi(PORTD, PD7);


// sweep through range of pwm duty cycles in forward direction. 

for(i=0; i<=255; i++)

{

// set duty cycle;

timer1PWMASet(i);

rprintf("Forward = %drn", i);

    timerPause(10);

}


// wait

rprintf("Pause for 0.5 second...rn");

timerPause(500);


for(i=255; i>=0; i--)

{

// set duty cycle;

timer1PWMASet(i);

rprintf("Forward = %drn", i);

    timerPause(10);

}

// wait

rprintf("Pause for 0.5 second...rn");

timerPause(500);


rprintf("Turning off PWM in forward dir (PWMA)rn");

timer1PWMAOff();


// wait

rprintf("Pause for 0.5 second...rn");

timerPause(500);


// set direction to 0 (reverse);

//rprintf("Setting direction pin (PD6) to reverse (0)rn");

//cbi(PORTD, PD6);


// sweep through range of pwm duty cycles in forward direction. 

for(i=0; i<=255; i++)

{

// set duty cycle;

timer1PWMBSet(i);

rprintf("Reverse = %drn", i);

    timerPause(10);

}

// wait

rprintf("Pause for 0.5 second...rn");

timerPause(500);


for(i=255; i>=0; i--)

{

// set duty cycle;

timer1PWMBSet(i);

rprintf("Reverse = %drn", i);

    timerPause(10);

}

rprintf("Turning off PWM in reverse dir (PWMB)rn");

timer1PWMBOff();


// wait

rprintf("Pause for 0.5 second...rn");

timerPause(500);


// now turn off all PWM on timer1

rprintf("Turning of all PWMrn");

timer1PWMOff();


// wait

rprintf("Pause for 0.5 second...rn");

timerPause(500);


return(0);

}


Code for Reading From a Range Sensor


Here is code (a program called irsensor.c) to read data from an IR range sensor. (Sharp GP20YA02
The sensor is hooked up to pin #40 on the microprocessor.
 
-----------------------

//

// irsensor.c

//

// Simple program to read data from a Sharp 2YOA02 IR distance sensor and send it to the computer.

// Adapted from a2dtest.c from Pascal Stang.

//

// rtwomey@ucsd.edu

//

 

// includes

#include <avr/io.h> // include I/O definitions (port names, pin names, etc)

#include <avr/interrupt.h> // include interrupt support


#include "global.h" // include our global settings

#include "uart.h" // include uart function library

#include "rprintf.h" // include printf function library

#include "timer.h" // include timer function library (timing, PWM, etc)

#include "a2d.h" // include A/D converter function library

#include "vt100.h" // include VT100 terminal support


// constants


// function declarations


// program

int main(void)

{

int readVal=0;


// initialize the UART (serial port)

uartInit();

uartSetBaudRate(57600);

// make all rprintf statements use uart for output

rprintfInit(uartSendByte);

// initialize the timer system

timerInit();

// turn on and initialize A/D converter

a2dInit();


// configure a2d port (PORTA) as input

// so we can receive analog signals

DDRA = 0x00;

// make sure pull-up resistors are turned off

PORTA = 0x00;


// set the a2d prescaler (clock division ratio)

// - a lower prescale setting will make the a2d converter go faster

// - a higher setting will make it go slower but the measurements

//   will be more accurate

// - other allowed prescale values can be found in a2d.h

a2dSetPrescaler(ADC_PRESCALE_DIV32);


// set the a2d reference

// - the reference is the voltage against which a2d measurements are made

// - other allowed reference values can be found in a2d.h

a2dSetReference(ADC_REFERENCE_AVCC);


// use a2dConvert8bit(channel#) to get an 8bit a2d reading

// use a2dConvert10bit(channel#) to get a 10bit a2d reading


// loop, and transmit the A/D value every 10 msecs.

while(1)

{

                readVal=a2dConvert8bit(0);

rprintf("%dn", readVal);

//uartSendByte(readVal);

// wait a 0.1s before reading next value

timerPause(100);

}


return 0;

}



-----------------------

Here is a program for taking the readings from the processor, and rendering them as color on the screen. The program is written in processing, which you can download here.

To get this to program to work, you first need to change the irsensor.c program to switch the program from sending data as text to the terminal window, and instead to send it as data to be read by processing.  Change it as follows:

  1. uncomment uartSendByte(readVal) above (remove the "//" at the beginning of that line)
  2. comment out rprintf("%dn", readVal); (add a "//" at the beginning of that line)
  3. recompile the program.  (make, from the terminal, or [AvrLib] Build All in Programmer's Notepad)

And then run this program in processing, AtoD:

/*

Simple program to demonstrate a2d functionality and IR rangefinding.
A Sharp 2YOA02 distance sensor is connected to an ADC pin on the microprocessor,
and distance data is fed through the serial port to this program. 

*/

import processing.serial.*;

Serial port;  // Create object from Serial class
int val;      // Data received from the serial port

float MULTIPLIER = 1.6;  // Scales readings to full color range.

void setup() 
{
  size(600, 600);

  // Open the port that the board is connected to and use the same speed (57600 bps)
  port = new Serial(this, Serial.list()[0],57600);
  
}

void draw()
{

  if (0 < port.available()) {  // If data is available,
    val = port.read();         // read it and store it in val
  }
 
  background(val*MULTIPLIER, 0, 0);  // color the window proportional to distance reading.
  
}

Tuesday, February 19, 2008

Distance Sensor

Good info on IR distance sensors:
http://www.acroname.com/robotics/info/articles/sharp/sharp.html


The one I am going to show in class tomorrow is this:
http://www.acroname.com/robotics/parts/gp2y0a02_e.pdf

Thursday, January 31, 2008

Communicating with your chip

Communicating with your chip

OS X:

1. Install the driver for the usb adapter: http://www.ftdichip.com/Drivers/VCP.htm

2. Connect the USB adapter to your chip and the computer. 

3. Install zterm http://homepage.mac.com/dalverson/zterm/
Simply double-click the install icon and follow the on screen instructions. 

4.  Start up Z-term, and select the serial port that corresponds to the USB adapter you just installed.  Configure the connection with the following parameters.

In connection settings:
Turn off flow control
Set Data Rate to 57600
Parity: None

In Text Pacing:
0 delay between characters
1/60th seconds delay between lines

You will want to save these settings, otherwise it is tedious to enter them every time you launch Z-Term. Upon quitting, it will ask you if you want to save, click yes.

5. Open Zterm, and power on the chip while holding the reset button.  You should see the cockroach logo appear in the terminal screen.

6. To program your chip, select File—>Send Text.  Under enable, select All Documents.  browse for the .hex file of the program you compiled, click OK.  
The transfer should start automatically.  When the transfer has completed successfully, you can restart the chalkroach and test that it works.


Windows:

1. Install the driver for the usb adapter: http://www.ftdichip.com/Drivers/VCP.htm

2. Connect the USB adapter to your chip and the computer. 

2. Open hyperterminal, make a new connection.  Go to File/Properties/Connect Using --> select the COM port chalkroach is connected to.  This is the COM port assigned to your USB device, after installing the usb driver.

3. Configure the connection as follows.  Click Configure and select:

Bits per second --> 57600
Data bits --> 8
Parity --> None
Stop bits --> 1
Flow control --> None
Click OK.

On to the Settings tab, click on ASCII Setup.

Line delay --> 20 ms,
character delay --> 0 ms
Click OK.

4. Power on the chip while holding the reset button.  You should see the cockroach logo appear in the terminal screen.  

5. From the HyperTerminal Transfer menu, click Send Text File. The Send Text File dialog box appears. Change the file type to All Files, browse for the .hex file of the program you compiled, click OK. The transfer should start automatically. When the transfer has completed, it should notify you of its success, and you can restart the chalkroach and see that it works.

Wednesday, January 30, 2008

First Programming/Breadboarding Assignment


Week 4 — First Programming/Breadboarding Assignment:
Simple control of an LED, with a program!

Hardware Materials:
LED.
220 Ohm resistor.
Wire.

Software Materials:
cmdlinetest.c from sixcode/examples


You will connect an LED from Port C Pin 0 (PC0 in the schematic below) to +5V (VCC), with a 220 ohm resistor to limit current flow.  
You will then write a program which tells pin PC0 to turn on or off, in some pattern that pleases you.


This is a schematic for the circuit:

  


According to the schematic from the Atmega32 datasheet, PC0 is pin #22 on the chip (see below).

Pin #22 on our chip corresponds to pin #22 on the breadboard adapter—so we will connect the resistor and then the LED (in series) to that pin on our breadboard, and then the other end of the LED to positive voltage.  Make sure that you orient the LED the proper way, it is polarized!  So the positive side of the LED (rounded) is the one that connects to positive voltage, and the negative side (flat) is the part that connects to the resistor.  

Properly connected, your board should look something like this:
 


This concludes the hardware setup, and we are now ready for the programming part of this project. 

We are going to modify a pre-existing program, cmdlinetest, to allow us to turn the led on with a new command, "flash".

1.  Make a directory for software you will write inside of sixcode.  From your home directory, type: 

cd /sixcode
mkdir twomey

This is the folder that will hold any programs that you write.


2. Copy the folder cmdline from sixcode/examples to your folder.  This will copy the code files (.c) , header files (.h), the make file (makefile), and anything else you need to compile this program.
From a command prompt in the directory ~/sixcode, type:
 
cp -r examples/cmdline twomey/led

And verify that the files were copied:

ls twomey/led

will show

cmdlineconf.h cmdlinetest.eep cmdlinetest.lst global.h
cmdlinetest.bin cmdlinetest.elf cmdlinetest.map makefile
cmdlinetest.c cmdlinetest.hex cmdlinetest.o

or 

cmdlineconf.h cmdlinetest.c global.h makefile

depending on whether or not you compiled the program previously.

3.  Now we are going to modify the program that exists.  In your copy of cmdlinetest.c:

under:  

// functions

add:

void flashLED(void);


in:  

int main(void)

add:


// configure port C for led output

outb(DDRC, 0xFF);

// all LEDs on

outb(PORTC, 0x00);

// wait for hardware to power up

timerPause(100);

// all LEDs off

outb(PORTC, 0xFF);


in: 

void goCmdline(void)

after:

// add commands to the command database

cmdlineAddCommand("exit", exitFunction);

cmdlineAddCommand("help", helpFunction);

cmdlineAddCommand("dumpargs1", dumpArgsStr);

cmdlineAddCommand("dumpargs2", dumpArgsInt);

cmdlineAddCommand("dumpargs3", dumpArgsHex);

add:

cmdlineAddCommand("flash", flashLED);



in:

void helpFunction(void)

after:

rprintf("dumpargs1 - dumps command arguments as stringsrn");

rprintf("dumpargs2 - dumps command arguments as decimal integersrn");

rprintf("dumpargs3 - dumps command arguments as hex integersrn");

add:

rprintf("flash     - flash LEDs attach to PORT Crn");


at bottom of program

add:

void flashLED(void)

{

// all LEDs on

outb(PORTC, 0x00);

// time to keep light on

timerPause(100);

// all LEDs off

outb(PORTC, 0xFF);

}



4.  Save your changes and compile the program.  In Programmers Notepad in Windows, select

Tools --> [WinAVR] Make All

or in OS X

cd ~/sixcode/twomey/led
make

Your computer should display messages about its progress, and should successfully compile, producing a hex file (cmdlinetest.hex) to be sent to the processor.


5. Open the terminal program, turn on your processor in bootloader mode, and send it the text file cmdlinetest.hex.  The program should finish with


XXXX bytes written to FLASH with 00 errors.


And you know the program was uploaded successfully.


6. Restart your processor and see if your program controls the light. 



7. Modify this program for some more interesting behavior.  For instance, you could separate lightOn and lightOff to be two separate commands.  Or you could control more than one light with the processor.  I have seven-segment displays in the lab, if you want to try writing alphanumeric data to a display.  You could also incorporate basic input (with a switch) to trigger behavior.  Look at sixcode/examples/basic_io for a dscription of input and output settings for the microcontroller.



7b.  Modify the makefile (and rename you directory, your header files, and your source code) so that the program compiles under a different name.

You don't really want to call it cmdlinetest, do you?

Breadboard Adapter

Breadboard Adapter


Materials: PCB, 1 dual-row socket, two 80 pin 0.1" headers.
DISCLAIMERS: 
OUR BREADBOARD ADAPTERS WERE PRINTED TOO WIDE (so we have to trim them by hand)
THE BOTTOM SIDE OF THE BOARD WAS PRINTED BACKWARDS (so the large VCC and GND connections at top are not going to be any use to us)

1.  Cut the pieces to the right sizes, as shown in photo on the right.  Cut 0.1" headers to be 23 pins long, and cut the double socket to be 20 pins long.  Please be very careful cutting these pieces—you can use your wire clippers, but squeeze very slowly and gently, so that you do not get a bad/ugly break on the socket.  Since the socket is extra long, you could try a few practice cuts before doing the real one.  
Cut the breadboard as well (even though my pictures don't show this) this will save you trouble later!
2. Position the socket on the top side of the board (the side with text), and solder it on the bottom.  Solder the corners first, to secure the socket flush with the board, and then fill in the rows of connections.


3. Place the two single row headers on the board, inserting them from the bottom (first picture), and soldering them on the top side (second picture).


Your breadboard adapter is now ready to be used.

4. Stick the adapter in the breadboard so it spans the central gap in the board.  One row of pins should go into the left half of the board (columns A-E below), and the other row of pins should go into the other half (F-J).
4b. Wire up the power as well, with a red wire from VCC (in the middle of the board) to +, and a black wire from GND (in the middle of the board) to -.  If our boards weren't misprinted, we would connect VCC and GND to one of the large terminals at the top of the adapter.





5.  Attach your microprocessor to the adapter. Make sure that Pin 1 (labelled on the bottom side of your microprocessor) connects to Pin 1 (labelled on the top of the breadboard adapter).  Notice the power connections (red and black) in the first image.




6. Connect your power and your serial adapter, start the terminal program, turn on your chip, and you are ready to go!




Sunday, January 20, 2008

Build the USB-TTL Adapter

Build the USB-TTL adapter:

We need to connect our microprocessor to our computer for programming and any other computer—processor communication. The processor is designed for serial communication through its Tx and Rx pins (#14 and #15 in the diagram), and the USB port on your computer is a serial port of sorts (a Universal Serial Bus), but we cannot connect them directly together. The USB signals are different from the data signals our processor expects, plus the USB port on your computer has certain protocol by which it expects a device to identify itself and behave. There are many ways to make communication possible, but we will use a USB-TTL converter manufactured by FDTI because of its simplicity as a solution.

The USB-TTL adapter plugs into the usb port on your computer, and has a plug at the other end, terminating in 6 pins.

pin#
color
value
connects to
1
black
GND
"G"
2
brown
CTS#

3
red
VCC

4
orange
TxD
"R"
5
yellow
RxD
"T"
6
green
RTS#


We need to connect those pins—GND, TxD and RxD (#1, 4, 5)—to the appropriate pins ("G", "R" and "T") on the programming socket of our board. There are various approaches.
We will make a wire with a socket at either end—one connects to the USB-TTL cable, and the other attaches to the programming socket of our board. Here's how we do it:

Materials: thin gauge wire, three crimping socket terminals, one 4-position socket receptacle, one 6-position 0.1" spaced header, and small heat shrink tubing.


  1. Cut three thin gauge wires, approximately 4 inches long. (Preferrably black, orange and yellow to match those colors on our USB-TTL adapter) Strip the last 1 cm of the wires to insert it into the wire terminal.
  2. Crimp the two crimping tabs of the terminal (the left-most in the photo below) to the end of the wire, and shape the connection with a pair of needle-nosed pliers to assure that the socket is firmly joined to the wire. Repeat for all three wires.
  3. With your soldering iron, very delicately heat the joint you have made, and apply a tiny bit of solder to secure the socket to the end of the wire. Trim the excess wire which sticks beyond the end of the terminal with your clippers. Repeat for all three terminals.
  4. Stick the socket terminals into the appropriate positions on the socket receptacle. The yellow wire in the first position, the orange in the second, and black in the third. This corresponds to the labels "T", "R", and "G" on the bottom of your number 6 board. Note the orientation of the terminals and the housing in the photo below—so that the little metal tab on the back side of the terminal is caught by the free black plastic tab on the receptacle.
  5. Strip the other end of each of these wires to about 1 cm. This is the end that will solder onto the 6-position header that connects to the USB-TTL plug.
  6. Heat the end of each of these wires with the soldering iron, and try to get a little solder on the bare wire, to prepare to join it with the header.

  7. Heat each of the appropriate pins (#1, 4, and 5—black, orange, and yellow) on the header as well, and prep with a bit of solder.
  8. Cut three short pieces of heat shrink for each wire, and slip one onto each wire. These will insulate and strengthen the joints to the 0.1in header.

  9. Align the wires with their respective pins on the header, and solder to join them.
  10. Slip the small piece of heat shrink over the joints, and use a lighter or other heat source to shrink the tube.

  11. You are ready to go!


Be sure that you have installed the Virtual Com Port drivers for the USB-TTL device from FTDI. http://www.ftdichip.com/Drivers/VCP.htm
Otherwise your system will not recognize the adapter.