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