Robot Programming in "C": Tak Auyeung, Ph.D. February 15, 2006
Robot Programming in "C": Tak Auyeung, Ph.D. February 15, 2006
Robot Programming in "C": Tak Auyeung, Ph.D. February 15, 2006
2
20040201 2242 TA: Add a little more to the debouncing code, mostly just explanations of how it works. 20040125 1636 TA: I am giving up on installing AVR Studio 4.08 at the lab. Hacking an .MSI le turned out to be more trouble than I anticipated. The notes now include instructions to use the debugger/simulator chain included with WinAVR. 20040118 2333 TA: add design log section 20031103 2107 TA: creation, set outline according to curriculum
Contents
0.1 Copyright Notice . . . . . . . . . . . . . . . . . . . . . . . . . . . 10
Background
11
1 Programming Robots 13 1.1 Robots . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13 1.2 Robot Behavior . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13 1.3 Our Scope . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13 2 Tools for the Class 2.1 Free Software . . . . . . . . . 2.2 Windows Platforms . . . . . . 2.2.1 Getting the Software . 2.2.2 Setting up and Testing 15 15 16 16 16
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
II
19
21 21 21 22 22 22 23 23 24 25 27
3 Binary Input/Output 3.1 Hardware Aspects . . . . . . . 3.2 Conguration . . . . . . . . . . 3.2.1 Including header les . . 3.2.2 Naming convention . . . 3.2.3 Registers for the ports 3.2.4 What is pull up? . . . 3.2.5 Conguration code . . . 3.3 Common Input Techniques . . 3.3.1 Edge Sensing . . . . . . 3.3.2 Debouncing . . . . . . .
4 The Timer Device 31 4.1 Functions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 31 4.1.1 As a Stopwatch . . . . . . . . . . . . . . . . . . . . . . . . 31 4.1.2 As a Source of Periodic Tick . . . . . . . . . . . . . . . . 32 3
4 4.1.3 Pulse Width Modulation (PWM) ATMega128 Timer Conguration . . . . 4.2.1 Timer Conguration . . . . . . . 4.2.2 PWM Conguration . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
CONTENTS . . . . . . . . . . . . . . . . . . . . 32 33 33 36 37 37 37 38 38 39 39 40
4.2
5 Interrupts and Interrupt Service Routines 5.1 Concept . . . . . . . . . . . . . . . . . . . . 5.1.1 The global interrupt enable ag . . . 5.1.2 The interrupt vector . . . . . . . . . 5.1.3 Interrupt service routines . . . . . . 5.1.4 Writing an ISR . . . . . . . . . . . . 5.1.5 But what about INTERRUPT? . . . . 5.2 Timer ISR . . . . . . . . . . . . . . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
III
43
45 45 45 46 46 47 49 50 50 50 50 51 51 52 53 55 55 55 57 57 58 59 59 59 59 60 60 60
6 Stepper Motors 6.1 Concepts . . . . . . . . . . . . . . . . . 6.1.1 Full Stepping . . . . . . . . . . . 6.1.2 Half Stepping . . . . . . . . . . . 6.1.3 Software Control . . . . . . . . . 6.2 Frequency Division . . . . . . . . . . . . 6.3 Displacement, Velocity and Acceleration 6.3.1 Displacement . . . . . . . . . . . 6.3.2 Velocity . . . . . . . . . . . . . . 6.3.3 Acceleration . . . . . . . . . . . . 6.3.4 Getting back to Displacement . . 6.3.5 Physical Parameters . . . . . . . 6.3.6 Velocity Prole . . . . . . . . . . 6.3.7 C code framework . . . . . . . . 6.3.8 What about dierential steering? 6.3.9 Implementational Alternatives . 6.4 Coding Tips . . . . . . . . . . . . . . . . 6.4.1 Eciency issues . . . . . . . . .
7 Project 1 7.1 Final Objectives . . . . . . . . . . . . . . 7.2 Step by Step . . . . . . . . . . . . . . . . 7.2.1 Set up the Tools . . . . . . . . . . 7.2.2 Input/output . . . . . . . . . . . . 7.2.3 Frequency Division and Velocity . 7.2.4 Acceleration and Deceleration . . . 7.2.5 Displacement Control . . . . . . . 7.2.6 Handle Negative Displacement and 7.2.7 Add Left and Right Motors . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Velocity . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
CONTENTS 7.2.8 Add Angular Control . . . . . . . . . . . . . . . . . . . . Due Date and Grading . . . . . . . . . . . . . . . . . . . . . . . . How to Turn it In? . . . . . . . . . . . . . . . . . . . . . . . . . .
5 60 60 61 63 63 63 63 64 64 65 66 66 66 66 66
7.3 7.4
8 R/C Servo Motors 8.1 Concepts and Rationale . . . . . . . . 8.1.1 Components of an R/C Servo . 8.1.2 Control Mechanism . . . . . . . 8.1.3 Why use R/C servos? . . . . . 8.2 Modication for Full Rotation . . . . . 8.3 PWM Control . . . . . . . . . . . . . . 8.4 Issues and Tips . . . . . . . . . . . . . 8.4.1 Poor Speed Control . . . . . . 8.4.2 Uneven Full Rotation . . . . . 8.4.3 Fragile Plastic Gears . . . . . . 8.4.4 Not Meant for Continuous Use
. . . . . . . . . . .
. . . . . . . . . . .
. . . . . . . . . . .
. . . . . . . . . . .
. . . . . . . . . . .
. . . . . . . . . . .
. . . . . . . . . . .
. . . . . . . . . . .
. . . . . . . . . . .
. . . . . . . . . . .
. . . . . . . . . . .
. . . . . . . . . . .
. . . . . . . . . . .
. . . . . . . . . . .
. . . . . . . . . . .
IV
69
71 71 71 71 72 72 75 75 76 76 77 77 77 77 79 79 80 81 83 83 83 84 84 84
9 DC Motor Control 9.1 DC Motor Direction Control . . . . . . . 9.2 DC Motor Strength Control . . . . . . . 9.2.1 Analog Electrical Potential Control 9.2.2 PWM . . . . . . . . . . . . . . . . 9.3 Are we done? . . . . . . . . . . . . . . . .
10 Motion Encoding 10.1 Optical Encoding . . . . . . . . . . . . . . . . 10.2 Magnetic Encoding . . . . . . . . . . . . . . . 10.3 Quadrature Encoding . . . . . . . . . . . . . 10.4 Implementation . . . . . . . . . . . . . . . . . 10.4.1 General Approach . . . . . . . . . . . 10.4.2 External Interrupts . . . . . . . . . . . 10.4.3 Algorithm for Quadrature Encoding . 10.5 Electronic Interface . . . . . . . . . . . . . . . 10.5.1 Problems with a plain phototransistor 10.5.2 Solution . . . . . . . . . . . . . . . . . 10.5.3 Example . . . . . . . . . . . . . . . . . 11 PID Loop 11.1 The Theory . . . . . . . . . . . 11.1.1 The Proportional Term 11.1.2 The Integration Term . 11.1.3 The Derivative Term . . 11.2 From Continuous to Discrete .
. . . . . . . . . . .
. . . . . . . . . . .
. . . . . . . . . . .
. . . . . . . . . . .
. . . . . . . . . . .
. . . . . . . . . . .
. . . . . . . . . . .
. . . . . . . . . . .
. . . . . . . . . . .
. . . . . . . . . . .
. . . . . . . . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
6 11.3 Practical Concerns . . . . . . . . . . . . . . . . 11.3.1 Resolution of Output . . . . . . . . . . . 11.3.2 The Resolution of e(t) and Kp . . . . . 11.3.3 The Resolution of the Integration Term 11.3.4 The Resolution of the Dierential Term 11.4 Improving Feedback Resolution . . . . . . . . . 11.4.1 The Problem . . . . . . . . . . . . . . . 11.4.2 A Solution . . . . . . . . . . . . . . . . 11.4.3 Practical Considerations . . . . . . . . . 12 Motor Driver Circuit Design 12.1 TPIC0108 Interfacing . . . . . . . . . . . . 12.1.1 TPIC0108 Logic for DC Motors . . . 12.1.2 TPIC0108 Logic for Stepper Motors 12.1.3 TPIC0108 Chopper Drive Logic . . . 12.2 Layout Considerations . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
CONTENTS . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 85 85 85 86 86 87 87 87 87 89 89 89 90 91 91
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
Reading Sensors
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
93
95 95 95 95 96 96 96 96 97 97 97 97 97 98 101 101 101 102 102 102 103 103 104
13 Analog to Digital Conversion 13.1 Conguration . . . . . . . . . . . . . . . 13.2 The ADC clock . . . . . . . . . . . . . . 13.2.1 Voltage Reference . . . . . . . . 13.2.2 Multiplexer Selection . . . . . . . 13.2.3 Free Running versus Manual . . 13.2.4 Interrupts . . . . . . . . . . . . . 13.2.5 Enabling and Starting . . . . . . 13.2.6 Bit Positions . . . . . . . . . . . 13.2.7 Reading Results . . . . . . . . . 13.3 Common Techniques . . . . . . . . . . . 13.3.1 One-shot ADC Read . . . . . . . 13.3.2 Free-running Background Update 13.3.3 Server-client Approach . . . . . . 14 IR Ranging Sensor 14.1 General Information . . . . . . . . . 14.2 Interface . . . . . . . . . . . . . . . . 14.3 Important Notes . . . . . . . . . . . 14.3.1 Stablizing the Supply Voltage 14.3.2 Debouncing . . . . . . . . . . 14.3.3 Sampling Frequency . . . . . 14.3.4 Inter-measurement Filtering . 14.3.5 Distance Noise Prediction . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
CONTENTS
VI
Communication
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
105
107 107 107 107 107 108 108 109 109 109 110 110 111 113 113 113 114 114 114 114 114 115 115 116 116 117 117 117 120 120 121 123 123 123 124
15 General Communication 15.1 Reasons . . . . . . . . . . . . . . 15.1.1 Program update . . . . . 15.1.2 Remote control . . . . . . 15.1.3 Debugging and Logging . 15.1.4 Environmental Interaction 15.1.5 Inter-robot Interaction . . 15.2 Devices . . . . . . . . . . . . . . 15.2.1 RS-232, asynchronous . . 15.2.2 RS-485 . . . . . . . . . . 15.2.3 Ethernet . . . . . . . . . . 15.2.4 Wi . . . . . . . . . . . . 15.2.5 Bluetooth . . . . . . . . .
16 Low-Level Asynchronous Communication 16.1 Timing Details . . . . . . . . . . . . . . . 16.1.1 Start Bit(s) . . . . . . . . . . . . . 16.1.2 Data Bits . . . . . . . . . . . . . . 16.1.3 Parity Bit . . . . . . . . . . . . . . 16.1.4 Stop Bit . . . . . . . . . . . . . . . 16.2 The USART . . . . . . . . . . . . . . . . . 16.2.1 Clock and Transmission Speed . . 16.2.2 Frame Format . . . . . . . . . . . 16.2.3 The Ninth Bit and Networking . . 16.2.4 Errors . . . . . . . . . . . . . . . . 16.2.5 Interrupts . . . . . . . . . . . . . . 16.2.6 Enabling and Disabling . . . . . . 16.3 Circular Queues as Software Buers . . . 16.3.1 An Example: Circular Queue . . . 16.3.2 Where is the Array? . . . . . . . . 16.3.3 The Record Structure . . . . . . . 16.3.4 Logic for a Circular Queue . . . . 16.4 Circular Queue Implementation . . . . . . 16.4.1 cq.h . . . . . . . . . . . . . . . . . 16.4.2 UART ISR Logic . . . . . . . . . . 16.4.3 UART API . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . .
VII
Robot Behavior
125
CONTENTS
VIII
Project
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
129
131 131 131 132 133 133 133 133 133 133
18 Project 18.1 Robot Objectives . . . . . . . . . . . . 18.2 Robot Specications . . . . . . . . . . 18.3 Design . . . . . . . . . . . . . . . . . . 18.3.1 Drive Platform . . . . . . . . . 18.3.2 Fire Extinguisher Subsystem . 18.3.3 Sensors . . . . . . . . . . . . . 18.3.4 Communication Subsystem . . 18.3.5 Battery and Power Subsystem 18.3.6 Control Units . . . . . . . . . .
IX
135
137 137 137 137 138 138 138 139 140 140 140 140 141 141 141 143 143 144 144 145 147 147 147 147 147 149
19 Robot Design Log 19.1 Design Requirements . . . . . . . . . 19.1.1 Physical . . . . . . . . . . . . 19.1.2 I/O . . . . . . . . . . . . . . 19.1.3 Software (added 20040307) . 19.2 Components . . . . . . . . . . . . . . 19.2.1 Pin count . . . . . . . . . . . 19.2.2 Component Selection . . . . . 19.2.3 Mini-Sumo Chassis Design . . 19.3 Design (added 20040307) . . . . . . 19.3.1 Bootloader (added 20040307) 19.3.2 H-Bridges . . . . . . . . . . . 19.3.3 (20040322) RS232 . . . . . . 19.4 PCB Design Log . . . . . . . . . . . 19.4.1 20040410 . . . . . . . . . . . 19.4.2 20040406 . . . . . . . . . . . 19.4.3 20040404 . . . . . . . . . . . 19.4.4 20040331 . . . . . . . . . . . 19.4.5 20040322 . . . . . . . . . . . 19.5 Drive System . . . . . . . . . . . . . 20 A.N.T. 20.1 GPS . . . . . . . . 20.1.1 Interface . . 20.2 Stereo Vision . . . 20.2.1 Theory . . 20.2.2 Equipment
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
CONTENTS 21 Project Log 21.1 20050129 21.2 20040326 21.3 20040319 21.4 20040318
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
Work In Progress
Motor Encoding The EMF . . . . . . . . . The Problem . . . . . . . The Solution . . . . . . . Detecting Circuit Open . Detecting Circuit Closure . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
155
157 157 157 158 158 158 158
10
CONTENTS
Copyright Notice All materials in this document are copyrighted. The author reserves all rights. Infringements will be prosecuted at the maximum extent allowed by law. You are permitted to do the following: 1. add a link to the source of this document at www.drtak.org 2. view the materials online at www.drtak.org 3. make copies (electronic or paper) for personal use only, given that: (a) copies are not distributed by any means, you can always refer someone else to the source (b) copyright notice and author information be preserved, you cannot cut and paste portions of this document without also copying the copyright notice
Part I
Background
11
Chapter 1
Programming Robots
1.1 Robots
There are many dierent types of robots. For example, there are remotely controled robots in Battlebots, mostly autonomous robots for Mars exploration, autonomous robots for Micromouse competitions and etc. There are also vehicles that are autonomous, hence sometimes considered special robots. For example, a cruise missile can be considered a robot.
1.2
Robot Behavior
Except in remotely controled robots, the behavior of a robot is usually governed by one or more programs, running on one or more computers on board the robot. Robot behavior programming is often considered an area in articial intelligence, and it is a form of high level programming. Programs that determine the behavior of a robot are often not written in conventional languages (such as C, Basic, Pascal and etc.).
1.3
Our Scope
This class approaches robot programming from the lowest level. In other words, we begin with very basic control of individual components on a robot, then gradually introduce concepts that combine components to more meaningful robot functions.
13
14
Chapter 2
This class utilizes mostly, if not all, free software. There is a wealth of high quality free software tools out there, but most people do not know such tools because there is little advertisement about such tools. The primary compiler we will use is gcc (GNU C Compiler). gcc is a mature C compiler that is multihomed and multitargetted. This means the compiler itself can run on many platforms (such as Linux and Windows). At the same time, the code compiled by gcc can also run on many platforms. In fact, the platform on which the compiler itself runs does not need to be the same platform that the compiled code is targetted for! A compiler that has a target platform dierent from its home platform is called a cross compiler. Besides the compiler, there are other necessary tools we need to get:
binutils: this is a collection of programs that supports a compiler. Such programs include a linker and an assembler. libc: the job of a compiler is to translate. This means a programmer still needs to specify how to do everything. libc is a library of useful subroutines that are commonly used in programs. In other words, libc is a collection of subroutines that most people nd useful in programming. libc also include the necessary header les to use such pre-dened subroutines. gdb: this is the debugger. It is not too helpful unless we use ICE (in-circuit emulator) devices for debugging. uisp: this is a device programmer software tool that allows a PC write a program to a target computer.
15
16
2.2
Windows Platforms
The tool chain is *nix-oriented, but it is ported to the Windows platform. Ideally, the tools should be used on a Linux-based platform, but the Windows platform is sucient for all practical purposes.
2.2.1
The main website to get the Windows port of the tool chain is http://www.avrfreaks.net. Click on the AVR GCC tab, then click on Download it HERE after the content list. This brings you to another site. Scroll down until you nd the Latest File Releases, then click on Download on the row of WinAVR. This brings to another page. Scroll down, and download the le ending with bin-install.exe. You may want to save the executable in case you need to reinstall WinAVR. Then, you can select a host that is close to you and click on the icon with lots of 0s and 1s. Now, click Back on the browser and go all the way back to the AVR GCC page at www.avrfreaks.net. Be sure to download and save the document written by Colin OFlynn about installing and conguring WinAVR. Go to http://www.avrfreaks.net/Tools/showtools.php?ToolID=258 and download AVR Studio 4.08. This version works well with the WinAVR tool set.
2.2.2
First, install the simulator. This should be a fairly painless process (unless you dont have admin. access rights). The simulator is not required, but it is convenient. Then, install WinAVR, which should also be painless. Now a little bit of elbow grease. Start a command line interface (otherwise known as a DOS prompt) rst. Go to the directory where WinAVR was installed. Lets say you keep the defaults and install WinAVR at c:\winavr, then issue the following commands: c: cd \winavr\examples cd demo copy \winavr\sample\makefile . edit makefile In the editor, look for the line that reads MCU = atmega128 Change atmega128 to at90s2313. Then, look for the line that reads TARGET = main
17
Change main to demo. Save the le and exit the editor. Generally, you only need the following command to make all the targets: make all AVR Studio 4.08 Back in the command line, issue the following command: make extcoff No error should occur, and a le name demo.cof should be created (along with many other les). Now, start up AVR Studio 4.08 (in Atmel AVR Tools from the Start menu). Click Open from the opening screen, then look for the le named demo cof.aps (the extension .aps) may not show. You can now run the program step-by-step using the similator. Simulavr + Insight Unfortunately, AVR Studio 4.08 is not installed at the lab. The older version of AVR Studio has a bug that makes it incompatible with the WinAVR tool set. This is not a mistake on WinAVRs part (or any of its contributors), but on Atmels part. WinAVR has the necessary tools to create les conforming to Atmels description of the COFF format, but AVR Studio 3.5x itself does not conform to Atmels own standard. Another reason why software source should be open. Anyway, we can still debug programs without Atmels simulator. A GPLd simulator, called simulavr, has been written to provide the backend to simulate the execution of AVR code on practically any platform. This simulator runs with gdb (GNU Debugger) to provide debugging ability. Lets get familiarized with the necessary components to get this to work:
WinAVR: this is the inclusive package that you should install on a PC running Windows. If you use Linux, you need to download various packages. avr-gcc: this is the GNU C Compiler cross targetted to the AVR. avr-libc: this is a collection of libraries for use with avr-gcc. avr-gdb: this is a debugger cross targetted to the AVR. avr-binutils: this is a collection of useful utility programs to create executables. simulavr: this is the simulator. It can run as a server so that a debugger connects to it as it is a remote target machine. The connection, however, is via a socket rather than a serial port.
18
Now, lets go through the step-by-step procedures to debug a program using this tool chain: 1. Make sure the program is compiled and linked, see subsection 2.2.2. 2. Click the Start button, then run..., then type in the following: simulavr --gdbserver --device at90s2313 Substitute at90s2313 with whatever target MCU your program is targetted. Selecting the correct target is very important. Note that once simulavr runs, it does not exit. It gives you a message like Waiting on port 1212 for gdb client to connect... if everything is okay. Leave the command line interface alone (minimize it if you dont want to clutter up your screen). 3. If you install WinAVR, you should see an icon titled AVR Insight (WinAVR) on the desktop. Run the program. If you do not see the desktop icon, go to the folder containing the executable and launch it manually. The full path is usually c:\WinAVR\bin\avr-insight.exe. 4. In Insight, use File Target Settings, and select GDBServer/TCP as the Target, the Host should be localhost, and the Port should be set to 1212. For convenience, make sure Set breakpoint at main is checked. Click on More Options, and make sure both Attach to Target and Download Program are both checked. You only need to do this once. Insight saves the options autonatically. 5. In Insight, use File Open and locate demo.elf. Click Open. 6. In Insight, use Run Download to upload the executable. This step should not have been necessary, but it is! 7. Click the run icon (a running person), the program should start, and the debugger stops on the rst statement. 8. If you see a line highlighted in green, you have successfully launched the debugger!
Part II
19
Chapter 3
Binary Input/Output
This chapter deals with the most basic I/O ability of any controller board intended for embedded system control. Robots are essentially very specialized machines with embedded control systems. As a result, almost every robot controller has these binary I/O ports.
3.1
Hardware Aspects
On a physical controller, many pins (or electrical contacts) can be congured to serve as a binary input or a binary output. Even a US$12 microcontroller IC (Atmels ATMega128) has more than 40 pins of binary I/O. A binary input pin is a pin where the electronics can sample the voltage. A sampled voltage of high (5V or 3.3V) is registered as a 1 in software, whereas a sampled voltage of low (0V) is registered as a 0 in software. A binary output pin is a pin where the electronics can drive the voltage. When software writes a 1 to a binary output, most controllers will attempt to drive the output to a high voltage (5V or 3.3V). When software writes a 0 to a binary output, most controllers will attempt to drive the output to a low voltage (0V). Note that the binary I/O of a controller is, more often than not, unsuitable for sensing common voltages or controling anything directly. Interface circuits are often needed to protect the binary I/O pins on controllers. Since this class is not an electronic class, such interface circuits are not discussed.
3.2
Conguration
Most MCUs allow software congure whether a physical pin be used as input or output. On the ATMega128 (and other members of the AVR family), each binary I/O pin has two bits for conguration:
direction: whether the pin is input or output
21
22
In addition, there is also a sense or input bit for each binary I/O pin that reports either a 1 (for a high voltage) or a 0 (for a low voltage) at the pin.
3.2.1
When a C program includes the proper header, and the Makefile is properly set up, a C program can use handy symbolic name to control the I/O features of an MCU. Generally speaking, you want to place the following lines at the beginning of a C program: #include <avr/io.h> #include <avr/ina90.h> Also, specify the MCU in a Makele: MCU = atmega128 This combination allows gcc automatically include the correct header le for the chosen MCU (so that you dont have to make your program specify to a particular MCU).
3.2.2
Naming convention
Although software can congure each individual pin, the AVR MCUs provides interfaces to congure 8 pins at the same time. A port is a group of 8 pins, and software can congure all 8 pins in a port in one operation. It is conventional with AVR MCUs to refer to the ports as port A, port B and etc. The ATMega128 has port A to port G available, while some of the smaller MCUs only have one or two ports.
3.2.3
For port A, there are three variables dened. Bit n of each of these variables corresponds to a pin n of port A.
DDRA (Data Direction Register A): this register (not to be confused with registers in a processor) controls the directions of the 8 pins of port A. A bit value of 0 means the corresponding pin is congured as input, whereas a bit value of 1 means the corresponding pin is congured as output.
3.2. CONFIGURATION
23
PORTA (data register of PORT A): this register is a read-write register, and its bits has dierent meanings depending on whether the corresponding pin is congured for input or output.
For an output pin, a bit value of 0 means the pin drives low (tries to lower the voltage), whereas a bit value of 1 means the pin drives high (tries to raise the voltage). For an input pin, a bit value of 0 means the pin has no pull-up. A bit value of 1 means the pin is pulled up by a large resistor. Well discuss the concept of pull up later.
3.2.4
Consider pull up as a weak tendency to default to a high voltage for an input pin. This means that if a pin, congured as input and with pull up, is not connected to anything, it senses a high voltage. The weak tendency is important, because we want the sensed voltage of this input pin change as soon as some component (such as a sensor) drives the voltage low. Most sensors that are transistor based or labeled open collector only have the ability to drive low but not drive high, which means it is important for the input pin to have the ability to default to a high voltage. To make this more clear, there are only two situations with an input pin that is congured to pull up:
The external component is not driving low. The input pin defaults to a high voltage and reports a 1. The external component drives low, overrides the default voltage, the input pin senses a low voltage and reports 0.
If there is a pull up conguration, is there a pull down conguration? The answer is yes. However, pull down is less commonly used, so MCUs typically do not have built-in abilities to congure for pull down. One can still congure pull down with circuits external to the MCU.
3.2.5
Conguration code
The easiest way to congure pins of a port is simply to use assignment statements. For example, the following assignment statement congures pins 0, 2, 3 and 7 for input, and all other pins (1, 4, 5 and 6) for output: DDRA = 0x72; The value 0x72 is the value 21 + 24 + 25 + 26 , represented by a hexadecial number. While simple assignment statements are useful for the initialization of port pins, they are not suitable when only one bit needs to be changed. For example, if we only need to change the state of pin 4 of port A so it drives high (assuming it is already congured for output), we should not use the following statement:
24 PORTA = 0x10;
This is because this statement also aects pins 0, 1, 2, 3, 5, 6 and 7 of port A! Fortunately, PORTA is a read/write variable. We can read it back rst, then use bitwise operators to change only the bits that we want to change. In this example, we should use the following statement to make pin 4 drive high: PORTA = PORTA | 0x10; This works because when a bit is ored with 0, the original value is preserved. To change pin 2 to drive low (assuming it is already congured for output), we can use the following code: PORTA = PORTA & ~0x04; The ~ operator means bitwise not. It is also called ones complement. Essentially, ~0x04 is the same as 0xfb, but the former is easier to read because it is more clear which bit is a 1. Note that any bit anded with 1 prserves its original value, and thats why this operation does not the conguration of pins other than pin 2. For the C savvy programmers, you can also replace the previous simple assignment operators with the operator-assignment operators: PORTA |= 0x10; PORTA &= ~0x04; If youd rather let the compiler gure out the bit pattern (from the pin number), you can use the left-shift operator << as follows: PORTA |= 1 << 4; PORTA &= ~(1 << 2);
3.3
This section discusses some common techniques to lter raw input from the pins of an MCU. Both techniques can use push buttons as examples. In this context, we assume a push button is normally open. This means when the push button is not being pushed, the two electrical contacts are not connected. When the push button is pushed, the two electrical contacts are connected. A typical use of such NO (normally-open) push buttons is to connect one end to ground, and another end to a binary input pin that has a pull-up. This way, there are two states:
When the button is released, there is no connectivity to ground. As a result, the binary input pin goes to its default state. Due to the pull-up conguration, the default state is high. In software, this pin reads 1.
25
When the button is pushed, it connects the binary input pin to ground (0V). This connection results in a very strong force to change the voltage at the binary input pin to 0V. As a result, the default pull-up is overcome, and the binary input pin reads 0.
3.3.1
Edge Sensing
Push buttons are often used as crude user interface for embedded systems. Most push buttons are fairly inexpensive, small, and easy to solder onto a PCB (printed circuit board). As a result, it is a common task that an embedded program needs to interpret buttons. Let us assume pin 5 of port A is congured as an input pin with internal pull up. You should already know by now that the conguration code for this pin should be as follows (assuming all other pins are already congured): DDRA &= ~(1 << 5); PORTA |= 1 << 5; To call button_down when the button is pressed, and call button_up when the button is released, we can use the following code: if (PINA & (1 << 5)) button_up(); else button_down(); This works if you only want to know the current state of a button. However, often we need to know a change of state, rather than the current state. In other words, a program may not be interested at all in whether a button is pressed or released, but when the button is being pushed or released. There are two techniques to detect the edge of a binary signal change. The rst method is called busy polling, whereas the second does not have an actual technical name. Busy Polling This technique keeps monitoring the state of a button, and a change of state causes the code exit a loop. For example, the following code waits for a button to be pressed, and then released, before it calls button_event: while (PINA & (1 << 5)); // button is now pressed while (!(PINA & (1 << 5))); // button is now released button_event();
26
The two loops are busy loops that do not do anything inside. The rst loop exits when the button is pressed, whereas the second loop exits when the button is released. You can insert another function call between the loops if you need to respond to the button is being pushed event. Busy polling is relatively easy to code, but it suers from one major problem: it is wasteful of processing resources. In addition, it is dicult to extend the code, due to its structure, to detect and respond to edge events of multiple buttons. Stateful Event Detection Okay, I am inventing this name here. In this second approach, we write our code in a much more modular fashion. For each binary input, we write a function similar to the following: void event_a5(unsigned char oldstate, unsigned char newstate) { if ((oldstate ^ newstate) & (1 << 5)) { if (oldstate & (1 << 5)) { // respond to falling edge } else { // respond to rising edge } } } This function, by itself, does not even read any inputs! So, we need some extra code to wrap around it: newstate = PINA; event_a5(oldstate, newstate); oldstate = newstate; The code shown above updates the variables newstate and oldstate. newstate can be an auto (stack allocated) local variable in a function, while oldstate should be either a global variable, or a static local variable. This is because we can potentially invoke the function containing this code repeatedly by yet another caller. But wait a minute here, what have we gained with this complicated scheme? The main advantage of this approach, compared to the previous polling techinque, is that this code is easily extended. If you want to handle events for another button connected to pin 6 of port A, write a subroutine event_a6, and insert a call so you have
3.3. COMMON INPUT TECHNIQUES newstate = PINA; event_a5(oldstate, newstate); event_a6(oldstate, newstate); oldstate = newstate;
27
The kind of control structure allows the handling of events for each pin to be separated from each other, resulting in cleaner code that is easier to maintain. To be complete, the entire subroutine (to call event_a5) should look like the following: void one_tick(void) { static unsigned char oldstate = 0xff; unsigned char newstate; // ... whatever code you want to put here newstate = PINA; event_a5(oldstate, newstate); event_a6(oldstate, newstate); oldstate = newstate; // whatever code you want to put here } Shouldnt this function one_tick be called repeatedly? Yes, it must be called periodically for the logic to work. You can write the following code: while (1) { one_click(); } Besides the fact that you can detect and handle events for multiple input pins, this approach may not seem to be that dierent from busy polling. However, once we discuss timers and timer interrupts, you will appreciate the strengths of this approach.
3.3.2
Debouncing
In the perfect world, when a switch changes state, it is a single event. Unfortunately, switches, such as push button switches, are mechanical devices. The contact bounces a few times before it settles. In other words, a program, polling at a relatively high frequency, sees a whole bunch of transitions for a single button push. The same is true when a button is released. This is not good. When a button is pushed once, a program may register it as several push-and-release events. If you rely on counting events to change settings, you will have a hard time getting to the correct settings!
28
Instead of xing the problem in the switches, the problem can be solved in software. Debouncing is a software technique used to lter out bounces to yield clean state change events. The Concept The concept of debouncing is quite simple. Unless we can observe the same state n times, we keep the previous state. That is, we need n consistent reads to conrm the state. The Implementation The implementation of debouncing varies from very clumsy hard coding, to exible schemes that is easy to extend. At the core of a exible scheme, we need to maintain a circular queue to track the previous n readings. Let us assume the const int db_n represents this n. We can write a quick-and-dirty subroutine to maintain the circular queue: void db_tick(void) { const int db_n = 5; // or whatever static unsigned char db_buffer[db_n] = { 0xff,0xff,0xff,0xff,0xff }; static int db_cursor = 0; db_buf[db_cursor] = PINA; if (++db_cursor >= db_n) db_cursor -= db_n; } I called this quick-and-dirty because a circular queue should have been implemented as a struct with associated functions to initialize and maintain its state. Every time we call db_tick, it reads from the input pins, and update the circular buer. However, it does not track the debounced states of each pin. We can add to the function to do this: void db_tick(void) { const int db_n = 5; // or whatever static unsigned char db_buffer[db_n] = { 0xff,0xff,0xff,0xff,0xff }; static int db_cursor = 0; static unsigned char db_state = 0xff; // default state unsigned char db_and, db_or; int i; db_buf[db_cursor] = PINA;
3.3. COMMON INPUT TECHNIQUES if (++db_cursor >= db_n) db_cursor -= db_n; db_or = 0x00; db_and = 0xff; for (i = 0; i < db_n; ++i) { db_or |= db_buffer[i]; db_and &= db_buffer[i]; } db_state |= (db_and); db_state &= (db_or); } Explanations
29
The key to the previous code is the loop. Let take a look at the initialization, the loop itself, and its results. db_or = 0x00; db_and = 0xff; This initializes the two variables we track in the loop. db_or is a cumulative bitwise-or sum (disjunction is somewhat analogous to addition in boolean algebra). The reason why each bit is initialized to 0 is simple: if any bit is initialized to 1, it will always be a 1, making the loop useless! Likewise, db_and is a cumulative bitwise-and product. All bits in this variable are initialized to 1 because if any were initialized to 0, it will always remain 0. for (i = 0; i < db_n; ++i) { db_or |= db_buffer[i]; db_and &= db_buffer[i]; } In the loop, both db_or and db_and are getting updated by the elements of db_buffer. Recall that db_buffer is a history of previous states of a port. Let us think for a second here, not that I think you are starting to zone out. After the loop exits, how do we interpret each of the following?
a bit value of 0 in db_or a bit value of 1 in db_or a bit value of 0 in db_and a bit value of 1 in db_and
a bit value of 0 in db_or: the corresponding pin of the port has a complete history of 0. a bit value of 1 in db_or: the corresponding pin of the port has at least one sample of 1 in the history. a bit value of 0 in db_and: the corresponding pin of the port has at least one sample of 0 in the history. a bit value of 1 in db_and: the corresponding pin of the port has a complete history of 1.
Isnt this fantastic? Afterall, we were looking for consistent samples for the entire history buer! db_state |= (db_and); db_state &= (db_or); Marks the end of this logic. db_state is the debounced state. Each bit in this variable represents a ltered state of the corresponding pin of the port. If a pin has consistently read 1 for the whole history, it is safe to update db_state and make the corresponding bit a 1. This is done by a bitwise-or with db_and. Similarly, if a pin has consistently read 0 for the whole history, it is safe to update db_state and make the corresponding bit a 0. This is done by a bitwise-and with db_or. Q.E.D., quod erat demonstrandum (which was to be demonstrated, or in plain English, end of proof).
Chapter 4
4.1
Functions
At the core of a timer is a counter. Depending on the design, this counter can either count up or down. The frequency of counting (increment or decrement) is often a factor of the main processor frequency. However, a timer often has a alternate operation mode as a counter, in which an external clock is used to trigger the increment or decrement.
4.1.1
As a Stopwatch
The counter in a timer is software readable. In other words, a program can check on the counter from time to time. As a result, a timer can be used as a stop watch by software:
processor resets the counter of a timer processor wait for signal to time signal received, start the mentioned timer to count (using internal clock)
31
32
In this case, the counter value is a measurement of the duration between the start signal and the stop signal.
4.1.2
In an application that needs to keep track of time, or has operations to perform periodically, a timer can be used to provide periodic ticks. In this mode, a timer is set up to count continuously. The counting frequency is often a factor of the main clock frequency. For count up timers, software usually sets up an overow value. For count down timers, software can usually set up a restart value. Sometimes, the overow or restart value cannot be specied in software, and hardcoded to some powers of 2. For a count up timer, when the counter value becomes greater than or equal to the overow value, the timer resets the counter to zero and causes a hardware interrupt. Well talk about interrupts later. For now, lets just say that the timer calls a particular subroutine in software. For a count down timer, when the counter value gets to zero, the timer reloads the counter with the reload value, and it causes a hardware interrupt. As you can see, whether a timer counts up or down is not important. The important point is that it calls a particular subroutine in software periodically. Well discuss interrupt service routines (ISRs) later.
4.1.3
On some AVR MCUs, timers can also be used to generate PWM signals. A PWM signal has a xed period. However, the on-duration (pulse width) of one period can range from 0 to the entire period. PWM is a very useful concept because it can be applied to normally on/o switches to achieve analog or graduated results. In the context of robot programming, PWM is often used for the following purposes:
directly control the duty cycle of DC motors generate the control signal to control RC servos implement software-based chopper drive for stepper motors
In order for a timer to generate PWM, there is one additional value called output compare. This value is less than or equal to the overow value. When the counter of a timer is less than this output compare value, an output pin drives high, when the counter of a timer is greater than or equal to this output compare value, the same output pin drives low. As a result, the duty cycle is
33
directly proportional to the ratio of the output compare value and the overow value. Note that the overow value controls the period of a PWM signal. The PWM feature of a timer is very useful. It is possible to generate PWM in software. However, software generated PWM is processor power intensive, and it does not have the same precision of timer generated PWM signals.
4.2
This section is specic to timers in the ATMega128. However, many of the concepts are also applicable to other members in the AVR family. Of all the timers in the ATMega128, this section concentrates on timer1, although timer3 is almost identical in terms of features of conguration. Because timer1 is such a exible device, it is impossible to enumerate all possible congurations. This section focuses on setting up timer1 so that it does the following:
overows every 20ms outputs PWM signals interrupts whenever it overows
4.2.1
Timer Conguration
Given a clock frequency of 16MHz and a desired period of 20ms, we can compute that we want the timer to overow every 16000000Hz 20ms = 320000 main clock cycles. Since the overow value is a 16-bit number, we know that we need a divider to reduce the counting frequency. 320000 65536 is more than 4 but less than 5. As a result, we know a prescalar of more than 4 will work. According to page 134 of the ATMega128 data sheet, the closest prescaler is a divide-by-8 prescaler. This means, from the table, that the clock select bit pattherns to be 0102 . There three bits are bit 2 to bit 0 in Timer/Counter1 Control Register B (TCCR1B). We have so far determined that TCCR1B has a bit pattern of ?????0102 . Because we are not using the device as a counter, bits related to input capture are useless, so we can further determine that the bit pattern is 00???0102 . Bit 5 is reserved and must be written as a 0, so our pattern is now 000??0102 . Based on the divide-by-8 prescaler, the counter counts at a frequency of 2MHz. At this frequency, we need an overow value of 2000000 20ms = 40000. We can use the single slope fast PWM mode because we do not change the period. We can choose to use either OCR1A (Output Compare Register 1 for Channel A) or ICR1 (Input Capture Register 1) for this overow value. Since we want to to reserve as many PWM output channels as possible, we use ICR1. Each I/O location is only 8-bit, but ICR1 is a 16-bit quantity. As a result, ICR1 is split into two 8-bit I/O locations: ICR1L and ICR1H. AVRs use an
34
internal buer to synchronize the update of a 16-bit number. It requires that the high byte be written rst, then the write of the low byte updates all 16 bits at once. The following code does just this: ICR1H = 40000U >> 8; ICR1L = 40000U & 0xff; As per table 61 on page 132 of the data sheet, to use ICR1 as the overow value and use fast PWM, we need to use mode 14. The WGM (Wave Generation Mode) bit pattern should be 11102 . According to page 129, bit 1 and bit 0 of WGM are bits 1 and 0 of Timer/counter1 Control Register A (TCCR1A). We know that TCCR1A should have a bit pattern of ??????10. Bit 3 and bit 2 of WGM are bit 4 and bit 3 of TCCR1B. Since we already know all the other bits of TCCR1B, we now know the following: TCCR1B = 0x1a; // 00011010 Because hardware PWM is such a useful feature, we want to use all channel A, B and C for that purpose. Each channel has a 2 bit COM pattern to congure its characteristics. According to table 59 on page 130 of the datasheet, bit 1 and bit 0 of COM has the following meanings when the timer is set up for fast PWM:
00: normal port operation, OC1A/B/C disconnected. This means we are not using the PWM feature for this channel. You can enable and disable the channels independently. 01: same as 00 because we choose WGM = 14 (binary 11102 ) 10: clear on compare match, set when the timer overows 11: set on compare match, clear when the timer overows
It is clear that we want to use either 10 or 11. Mode 10 is more intuitive because a larger OCR (Output Compare Register) value means more time without the corresponding pin at a high voltage. However, some electronic devices are active-low, which means the device is on when the control signal is low, and o when the control signal is high. For these active-low devices, mode 11 makes more sense. Assuming we want mode 10 for all three channels, we can now determine the value of TCCR1A (page 129): TCCR1A = 0xfe; // 11 11 11 10 We are almost done. We still need to set up the timer to interrupt when the counter overows. This is done by setting bits in TIMSK (Timer/counter Interrupt Mask Register). According to page 137, we want bit 2 set. Because TIMSK is also used to control timer0, we want to make sure bits unrelated to timer1 remains unchanged. The following code can be used:
35
It is also a good idea to reset the counter TCNT1. Again, the counter is a 16-bit number, which means we need to write to the high byte rst, then the low byte: TCNT1H = 0; TCNT1L = 0; Thats it! We are done with the initialization of timer1. Lets look at the code so far: TCCR1B = 0x1a; ICR1H = 40000U ICR1L = 40000U TCCR1A = 0xfe; TIMSK = (TIMSK TCNT1H = 0; TCNT1L = 0; // 00011010 >> 8; & 0xff; // 11 11 11 10 & ~(0x3c)) | 0x04;
Although this code makes sense from the perspective of working out the values, it is not the best sequence. With this sequence, the timer is ticking after the rst statement. The following code is safer, because
it rst disables global interrupt (well talk about this later) it disables the ticking initializes everything, start ticking again, reenable global interrupt
_CLI(); // disable all interrupts TCCR1B = 0; // disable ticking TIMSK = (TIMSK & ~(0x3c)) | 0x04; // enable overflow interrupt TCNT1H = 0; // reset counter TCNT1L = 0; ICR1H = 40000U >> 8; // set overflow value ICR1L = 40000U & 0xff; TCCR1A = 0xfe; // 11 11 11 10, set channel config TCCR1B = 0x1a; // 00011010 start ticking _SEI(); // reenable interrupts
36
4.2.2
PWM Conguration
The previous section initializes the timer so that channels A, B and C are set up for PWM. However, as the program runs, it can change the duty cycle of the PWM channels. This is done by setting OCR1A, OCR1B and OCR1C. Each of these OCR (Output Compare Register) is a 16-bit number. So to initialize them, we need to write to the high byte rst, then the low byte. It is best to write a small subroutine to initialize these OCRs. You can write one for each channel, I am using channel A as an example: void setOCR1A(unsigned value) { OCR1AH = value >> 8; OCR1AL = value & 0xff; } Note that this code is not interrupt safe. All 16-bit timer registers use the same internal 8-bit buer to store the high byte value. If an interrupt occurs between the two statements, and use the 8-bit buer in the ISR, then OCR1A will be corrupted because when the low byte is written, the buered high byte is already corrupted by the ISR.
Chapter 5
5.1
Concept
Interrupts are essential concepts in moderm computers (and controllers) because it relieves a processor from the responsibility to poll and check which hardware device requires attention. When a hardware device (such as a timer) requires attention (when the counter overows), it requests attention from the processor. This request is an interrupt. The processor then responses to the request and perform whatever operation is necessary.
5.1.1
It is sometimes important to disable interrupt. Well explain this later. In order not to listen for any request from any device, most processors have a ag that enables interrupt for all devices. On the ATMega128 (as well as all AVR variants), this is a boolean ag called the I ag. It is a bit in the status register (SREG). To clear the I ag, you can use the macro CLI(). This disables all interrupts. To set the I ag (to enable global interrupt), you use the macro SEI(). These two macros typically translate to assembly code. It is not always the case that you want to disable or enable interrupts. For example, sometimes you need to disable interrupts, then restore the system to what it used to be. In other words, if interrupt was disabled originally, you dont want to enable it. 37
38 CHAPTER 5. INTERRUPTS AND INTERRUPT SERVICE ROUTINES To accomplish this, you can use a local variable to remember whether interrupt was enabled to begin with. This is done by the following code template: { unsigned char oldSREG = SREG; _CLI(); // disable interrupt ... // do whatever if (oldSREG & (1 << SREG_I)) _SEI(); }
5.1.2
Although it is possible to have one single source interrupt, and use software to nd out which hardware device requires attention, it is much better to allocate a vector for each device. This means that the processor automatically knows which code to execute, depending on which device requests an interrupt. Most processors, including the AVRs, use a vector table. A vector table, in C terms, is an array of pointers to functions. In plain English, a vector table is an ordered list of addresses of subroutines. Each hardware device that can interrupt has an unique entry in this table. When a device requires attention and that the processor is willing to respond, the processor automatically index to the correct entry in the vector table, and starts to execute whatever code is at the address. Some procesors can relocate this vector table, most low-end AVRs must have this table located from location zero. In C programming, you dont have to deal with this vector table directly. The SIGNAL macros take care of the low level details.
5.1.3
When the processor jumps to execute code to respond to an interrupt, it actually makes a call. In other words, the processor rst saves the address of the next instruction onto the stack, then it jumps to the code to respond to an interrupt. This is necessary because after the processor responds to an interrupt, it needs to know how to resume execution of whatever it was doing when it got interrupted. Right before the processor saves the address of the instruction to resume to, it also disables interrupt. This is done automatically. This practice is common to most processors and MCUs because it is tricky to handle another interrupt when a processor is in the process of responding to the rst one. An ISR is typically short and does not take much time to complete. At the completion of an ISR, a special return instruction is used. This instruction returns to the saved address on the stack. This allows the processor resume execution of code that was interrupted. However, this special instruction also reenables interrupt after the address to resume to is retrieved.
5.1. CONCEPT
39
This way, even if there are multiple devices request interrupts, we only need enough space to save one single instruction address.
5.1.4
Writing an ISR
It is relatively easy to write an ISR in C using avr-gcc and the libc package. If you plan to write your own ISR, be sure to include the following lines at the top of your program les: #include <avr/io.h> #include <avr/signal.h> The rst line allows the compiler and header les automatically determine which MCU is being used, then automatically include the proper macro denitions. For a list of applicable interrupts for a particular MCU, do the following:
locate where header les are placed. This is usually in include/avr from the main folder. For linux installations, the main folder is often /usr/avr, for WinAVR installations, the main folder is often C:\WinAVR. nd the header le corresponding to the MCU. For example, the header le for the ATMega128 is iom128.h. use an editor or text viewer to inspect the le. Look for the pattern SIG. you should see a table of denitions of SIG_. Note that entry 0 is missing because that is the main reset vector.
Once you have found the name of the interrupt you want to handle, dene a shell ISR as follows: SIGNAL(SIG_OVERFLOW1) { } This example is a shell to handle overow interrupts from timer 1. Note that the macro SIGNAL automatically species the necessary compiler options so that the ISR uses the special return instruction at the end.
5.1.5
If you read avr/signal.h, you will notice that there is another macro called INTERRUPT. Indeed, you can dene ISRs using INTERRUPT instead of SIGNAL. However, ISRs dened with INTERRUPT have the global interrupt reenabled in the body of the subroutine. This is potentially very risky and it lead to bugs that are very dicult to reproduce. I recommend using SIGNAL instead of INTERRUPT unless there is a strong reason.
5.2
Timer ISR
As mentioned in a previous section, you can dene a shell ISR for a timer as follows: SIGNAL(SIG_OVERFLOW1) { } A very common thing to do is to keep track of a tick counter. This way, the rest of the program can get a sense of time. The rst cut is simply to use a global variable as follows: unsigned long tick_counter = 0; SIGNAL(SIG_OVERFLOW1) { ++tick_counter; } Then the rest of the program can track time as follows: { unsigned long first_tick; while (PINA & (1 << PB1)); // wait for PB1 be pressed first_tick = tick_counter; // mark the time while (!(PINA & (1 << PB1)); // wait for PB1 be released duration = tick_counter - first_tick; } This code attempts to time how long a push button has been kept pushed. Even though tick_counter can overow, the unsigned subtraction actually is insensitive to overows. As long as the duration to measure does not exceed 232 1, duration still reects the number of ticks that has passed. This code does have a serious problem. As we read tick_counter (which is a 32-bit integer), the timer interrupt can occur. This is very bad. We can be halfway loading the 32-bit integer into registers when this happens. The ISR will complete without any problems and update tick_counter. However, as it returns, we resume to load the other part of the now-updated tick_counter into registers. This means half of the bits of the registers comes from the original tick_counter, while the other portion comes from the updated tick_counter. What if tick_counter wraps around to 0 in the ISR? Besides, gcc is a smart compiler. It is likely to optimize the program and just assume that nothing is going to happen to tick_counter, so it may just decide that duration always get 0. In other words, by default, gcc assumes a global variable is not modied by ISRs. To x this code, we need to use the following denition for tick_counter:
41
The reserved word volatile tells the compiler that anything can happen to this variable at any time. However, we still have not xed the rst problem (getting interrupted when a multi-byte variable is getting loaded or stored). The solution to the rst problem is a little longer. Well dene a subroutine rst: volatile unsigned long get_tick_counter(void) { unsigned char oldSREG = SREG; unsigned long tmp; _CLI(); // disable interrupt tmp = tick_counter; if (SREG & (1 << SREG_I)) _SEI(); return tmp; } Again, volatile tells the compiler that this subroutine has the potential to return something dierent everytime it is called. This subroutine has the wrapper to disable interrupt rst, then restore the interrupt ag at the end. The key of this subroutine is that we capture the tick_counter value after interrupt is disabled. This guarantees the operation is atomic. The captured value in local variable tmp is then returned at the end. Having dened get_tick_counter, we just need to change all references to tick_counter to get_tick_counter() in the program.
Part III
43
Chapter 6
Stepper Motors
6.1
6.1.1
Concepts
Full Stepping
Since this is not a hardware class, well only use bipolar stepper motors in our discussion. There are more unipolar stepper motors, but bipolar stepper motors are more ecient. As a result, most energy-conserving robots use bipolar stepper motors. Figure 6.1 shows a bipolar stepper motor in its schematic form. In this diagram, there are two phases. Each phase is, essentially, a set of coils. The coils with terminals A and C represent one phase, while the coils with terminals B and D represent another phase. The arrow in the diagram represents the rotor, which is a freely rotating permanent magnet. In a real stepper motor, the permanent magnet is connected to the drive axle of a stepper motor. As current passes through coils, a magnetic eld develops. This magnetic eld, in return, tries to align the permanent magnet (rotor) in a certain direction. Lets assume that when current passes from A to C, the magnet arrow points to the north. As we turn o coil A-C and turn on coil B-D (with current owing from B to D), the magnet rotates clockwise to point to the east. Here comes the fun part. If we turn o coil B-D, then turn on coil A-C in reverse (current owing from C to A), the magnet rotates clockwise and points south. This is why it is called a bipolar stepper motor: each terminal can assume two polarities. Not surprisingly, we can also turn o coil A-C, then make current ow from D to B so that the magnet rotates and points west. This completes one cycle. With a two-phase bipolar stepper motor, there are these four steps: A-C, B-D, C-A and D-B. 45
46
N D B
C
Figure 6.1: Schematics of a bipolar stepper motor.
6.1.2
Half Stepping
What happens when we enable both coil sets A-C and B-D? The magnet points north-east. This is called a half step because it is half way between the full steps A-C and B-D. You can also insert half steps between all the other full steps. Half stepping is a useful concept because it eectively doubles the resolution of a stepper motor with no mechanical change. It also improves the torque of a stepper motor at the expense of some more current consumption.
6.1.3
Software Control
Since this is not an electronics class, I am going to skip the discussion of Hbridges. From the software perspective, there are only two bits to control per phase. One bit indicates whether a phase is active or not, while the other bit controls the polarity of current. As a result, we can write the following table for a full cycle, using half-stepping:
47
Phase 1 Enable Phase 1 Polarity Phase 2 Enable Phase 2 Polarity 1 1 0 0 1 1 1 1 0 0 1 1 1 0 1 1 1 0 0 0 1 0 1 0 0 0 1 0 1 1 1 0 In this table, a value of 1 for Enable means the phase is switched on. A value of 1 for Polarity means one way, a value of 0 means the other way. As a program generates these steps patterns in this sequence, the motor rotates in one direction. To rotate in the opposite direction, one only needs to reverse the order of these steps. To make the motor spin faster, generate these steps more frequently, to make the motor spin slower, generate these steps less frequently. At rst glance, a stepper motor is dicult to control. However, due to the precise stepping nature, stepper motors are actually the easiest to control for open-loop control. Open-loop means there is no feedback utilized in the control code.
6.2
Frequency Division
Because the rotational speed of a stepper motor is proportional to the frequency of cycling through the stepping bit patterns, it becomes important to control this stepping frequency precisely. The most intuitive method is simply to change the overow value of a timer. This approach works to an extent, but it is not well suited for precise frequency 1 control. The problem is because f = p , in which f is frequency, and p is period. In our case, p is always quantized by the divider of the timer clock. Most stepper motors can step at 500Hz. This translates to a period of exactly 2ms. But how about 499Hz? It translates to a period of about 2.004ms. It may seem to make sense to set up the timer so we can control its period at 0.004ms intervals. But this is not true. At 333Hz, the period is 3.333ms, which is not a multiple of 0.004ms! Using the period of a timer to directly control stepper also has other disadvantages. For example, a dierentially driven robot will need two timers to control its two motors. As we will discuss later, acceleration control also needs its own frequency control. Does this mean that we need to use two more timers to control acceleration and deceleration? Soon, we are left with no timer to provide the basic monotonic tick for time keeping and other periodic logic. This is why we need a dierent scheme for generating a variety of frequencies. We will use one single timer to do this. The basic idea is that we begin with a timer overow period that is much shorter than the shortest stepping period. In our example, lets assume that a
48
stepper steps at a maximum speed of 500 steps per second. In this case, we can assume a 5kHz timer interrupt frequency for relatively smooth stepping. But 5kHz is not divisible by 499Hz, is it? You are correct! With a 5kHz base frequency, the 499Hz stepping will be slightly uneven. This is what happens in one second:
49 steps spaced out by exactly 2ms 1 step with 2.2ms from the previous one 49 steps spaced out by exactly 2ms 1 step with 2.2ms from the previous one 49 steps spaced out by exactly 2ms 1 step with 2.2ms from the previous one 49 steps spaced out by exactly 2ms 1 step with 2.2ms from the previous one 49 steps spaced out by exactly 2ms 1 step with 2.2ms from the previous one 49 steps spaced out by exactly 2ms 1 step with 2.2ms from the previous one 49 steps spaced out by exactly 2ms 1 step with 2.2ms from the previous one 49 steps spaced out by exactly 2ms 1 step with 2.2ms from the previous one 49 steps spaced out by exactly 2ms 1 step with 2.2ms from the previous one 49 steps spaced out by exactly 2ms 1 step with 2.2ms from the previous one 48 steps spaced out by exactly 2ms 1 step with 2.2ms from the previous one
The period is (49 9 + 48) 2 + 10 2.2 = 1000 in milliseconds. Is this terrible? Not really. Since the variance of the long versus short steps is only o by 10%, most stepper motors will work ne. This pattern is generated using the following code:
6.3. DISPLACEMENT, VELOCITY AND ACCELERATION sum += s t e p f r e q ; i f ( sum > b a s e f r e q } { sum = b a s e f r e q ; perform step ( ) ; }
49
In this code, step_freq is the stepping frequency (499Hz), whereas base_freq is the base frequency (5000Hz). Intuitively, this code should work. As step_freq increases, it takes fewer iterations to overow sum. To generate a 499Hz frequency (for stepping), this code should be executed at 5kHz (5000 times per second). To make this logic cleaner, one can always make use of structures. The following code makes this frequency division logic exible: struct FreqDiv { unsigned int f r e q ; unsigned int sum ; void ( * c b f u n c ) ( void * ) ; void * cb param ; }; void F r e q D i v t i c k ( struct FreqDiv * pfd ) { pfd >sum += pfd > f r e q ; i f ( pfd >sum >= b a s e f r e q ) { pfd >sum = b a s e f r e q ; i f ( pfd > c b f u n c ) pfd > c b f u n c ( pfd >cb param ) ; } } The only tricky part in this code are the use of the cb_func and cb_param. cb_func is a call back function whenever there is an overow. cb_param is just a pointer to something that is passed to the callback function. This way, the call back function can have its own very complex structure for storing parameters and non-volatile state information. Also, note that base_freq is not specic to the structure. This is because typically, all mechanisms that rely on frequency division can rely on the same timer, hence having the same base frequency.
6.3
When you control the motion of a robot, there are three main parameters. Displacement is the amount of distance traveled, velocity is the speed, and acceleration is the rate of change of speed.
50
This section explains some of the physics background of these terms, and relate the physics concepts to software coding.
6.3.1
Displacement
Displacement is the amount of distance traveled. In stepper motor control, this linear distance is translated into number of steps. The translation is linear, which means D S , in which D is the linear displacement (in millimeters, inches or other length units), and S is the number of steps. In stepper motor control, it is helpful to keep track of the number of steps yet to perform.
6.3.2
Velocity
D Velocity is the rate of change of displacement. In mathematical terms, v = d dt . If we know that a robot is running at a constant velocity over a displacement D, then v = D t , in which t is the amount of time to cover the displacement D (at constant velocity). In stepper motor control, velocity is proportional to the frequency of stepping. In other words, v fstep .
6.3.3
Acceleration
v Acceleration is the rate of change of velocity. In mathematical terms, a = d dt . If we know that a robot changes its velocity at a constant acceleration, then a= v t , in which v is the velocity after accelerating for t from stationary. In stepper motor control, acceleration is proportional to the frequency of changing (increment or decrement) fstep . We call this ffstep 1 . You need one object of struct FreqDiv just to control acceleration. The call-back function cb_func should be responsible to increment or decrement the freq property of the struct FreqDiv for velocity control. You may also want to maintain a parameter structure (and initialize property cb_param) for acceleration control (to at least indicate whether it is acceleration or deceleration).
6.3.4
Mathematically, we can derive displacement as D = v (t)dt, in which v (t) is the velocity at time t. If the velocity does not change (constant velocity), then D = vt (6.1)
in which v is the constant velocity, and t is the amount of time that the robot travels at v . This equation is useful when the robot is traveling at a constant velocity.
51
Another way to write velocity is v = a(t)dt, in which a(t) is the acceleration at time t. If we assume accleration is constant, then v (t) = at, in which a is the constant acceleration, and t is the amount time for acceleration. Substituting v (t) = at into D = v (t)dt, we can say that
t
D(t)
=
0 t
v (x)dx axdx
0 t
= = a = =
xdx
0 2
at 2 v (t)2 2a
6.3.5
Physical Parameters
What is a good top velocity? What is a good acceleration? It all depends on the physical characteristics of a robot. Without going into too much physics, we can always experiment and nd out the maximum reliable velocity and acceleration/deceleration constants. Let us use vmax to represent the maximum velocity, and amax to represent the maximum acceleration. For simplicity, let us use amax as the maximum deceleration.
6.3.6
Velocity Prole
Given a distance D to travel, a robot needs to plan for acceleration, constant velocity travel, and deceleration. Given what we know from 6.6 that the distance for acceleration is Daccel = vmax 2 2amax . It takes the same distance to stop. In other words, the total distance to
max accelerate and decelerate is Dramp = v amax . What is D < Dramp ? If this is the case, it means that we need to stop accelerating and immediately begin deceleration before we reach vmax . Instead of predetermining the speed prole, we can do the following (in pseudocode). Assume v is the current velocity. 2
D then start deceleration at amax else if v < vmax then start acceleration at amax if
v2 2amax
52
else stop acceleration end if end if Sometimes it is necessary to change the target velocity. As a result, we usually want to use another term, vset , to represent the desired velocity. But this means that the current velocity can exceed vset . Our logic needs to be modied as illustrated in algorithm 1. Note that in this algorithm, D, vset can both be changed dynamically. Algorithm 1 Code to control velocity and acceleration based on remaining displacement if D = 0 then clear acceleration/deceleration set v to 0 optionally notify completion of motion 2 else if (D > 0) ( 2av D) then max set vset 0 start deceleration at amax 2 else if (D < 0) ( 2av D) then max set vset 0 start acceleration at amax else if v < vset then start acceleration at amax else if v > vset then start deceleration at amax else switch to constant velocity end if end if Note that this code also handles positive and negative D and vset . In a program, v should be replaced by fstep , and vset should be replaced by an appropriate frequency term. Furthermore, the acceleration term should be replaced by ffstep 1 , and amax replaced by an appropriate frequency term determined by experiment. Note that algorithm 1 should be executed independent of the stepping logic, but within the same tick. This logic should also be invoked whenever D or vset is changed.
6.3.7
C code framework
You can code the velocity prole logic anyway you want. However, from the organization point of view, I suggest you use a structure to include all the
6.3. DISPLACEMENT, VELOCITY AND ACCELERATION properties: struct M o t i o n P r o f i l e { struct FreqDiv v e l ; // v e l o c i t y c o n t r o l struct FreqDiv a c c ; // a c c e l e r a t i o n c o n t r o l long D; // d i s p l a c e m e n t int v s e t ; // d e s i r e d v e l o c i t y int amax ; // maximum a c c e l e r a t i o n int v s i g n ; // s i g n o f v e l o c i t y int a s i g n ; // s i g n o f a c c e l e r a t i o n };
53
Given that pmp is of type struct MotionProfile *, you should initialize it as follows: pmp> v e l . f r e q = 0 ; // i n i t i a l l y s t a t i o n a r y pmp> v e l . sum = 0 ; // r e s e t t o z e r o pmp> v e l . c b f u n c = s t e p f u n c ; // s t e p f u n c p e r f o r m s a s t e p pmp> v e l . cb param = pmp ; // i t i s g i v e n t h e e n t i r e s t r u c t pmp>a c c . f r e q = 0 ; // i n i t i a l l y no a c c e l e r a t i o n pmp>a c c . sum = 0 ; // r e s e t t o z e r o pmp>a c c . c b f u n c = a c c e l f u n c ; // a c c e l f u n c i n c r e m e n t s / d e c r e m e n t s f s t e p pmp>a c c . cb param = pmp ; // i t i s a l s o g i v e n t h e e n t i r e s t r u c t pmp>amax = . . . ; // w h a t e v e r c o n s t a n t works pmp>D = 0 ; // i n i t i a l l y nowhere t o go pmp> v s e t = 0 ; // i n i t i a l l y s t a t i o n a r y In this example, the function step_func must handle positive and negative velocities. While pmp->vel.freq represents the magnitude of velocity, pmp->v_sign represents the direction. Similarly, the function accel_func must take into account the sign of acceleration, stored in pmp->a_sign. step_func has to handle the sign crossover of pmp->D. This is easy because pmp->D is a signed number. accel_func has to handle the sign crossover of velocity magnitude pmp->vel.freq and velocity direction pmp->v_sign.
6.3.8
You can treat each motor independently in a dierentially steered, and you can use one struct MotionProfile for each motor. This also means you probably need dierent versions step_func and accel_func, one for each motor. This approach works ne when the two motors are symmetric and have exactly the same prole. However, for error correction and turning, it is often necessary to speed up one and slow down the other one. As a result, treating two motors independently is often a bad idea, leading to many problems down the line. From the top level, it is best to look at motion from two physical perspectives. First, there is linear motion. Second, there is angular motion.
54
Consider the following scenario. Your robot is heading north (zero degree). It makes a 90-degree turn clockwise using dierential steering at an average velocity of 300mm/s. Also, let us assume the turning radius is 600mm, and each wheel is 100mm from the center of the robot. This means the left wheel needs to move at 300mm/s 600+100 = 350mm/s. 600 100 The right wheel, on the other hand, needs to move at 300mm/s 600 = 600 250mm/s. At the same time, you can also express this motion as a linear speed of 300mm/s, with an angular speed of 100mm/s. The linear speed is easy to interpret. However, the angular speed needs some explanation. The angular speed is the dierence of speed between the two wheels. Once you separate into these two proles (linear and angular), each prole has its own parameters. As a result, each prole also ends up with its own displacement, target velocity and velocity. It is also important to know that the limits (vmax and amax ) for these two proles will be dierent. In our example, we keep the target velocity for the linear prole as 300mm/s, and its displacement is kept the same. However, for the angular prole, the displace was originally zero. It is then set to 200mm 2, which is approximately 314mm. The target velocity for the angular prole is set to whatever maximum that the robot is capable of. The logic of 1 automatically takes care of the angular aspects of turning. Here comes our next question. So far, we dont have anything that is useful for controling the motors. The linear and angular motion proles need to combined so we can control the stepper motors. This can be done easily. Afterall, the angular prov le describes how the two motors dier. As a result, vL = vlin + ang 2 , and vang vR = vlin 2 . What does this mean for our C code? As it turns out, we dont need to track the displacement or acceleration for the individual motors anymore. Instead, we only know need the velocity (step frequency) of each motor. We can now create our macro structure for a dierentially steered robot as follows: struct D i f f S t e e r { struct M o t i o n P r o f i l e l i n e a r ; struct M o t i o n P r o f i l e a n g u l a r ; struct FreqDiv l e f t ; struct FreqDiv r i g h t ; }; Assuming ds is a struct DiffSteer, we need to update ds.left.freq and ds.right.freq whenever ds.linear.vel.freq or ds.angular.vel.freq updates. This is easy, since we can put this logic in the call back functions ds.linear.acc.cb_func and ds.angular.acc.cb_func. However, you need to track the sign/direction of each motor separately. It is not necessary to track this as a separate property. You can use ds.linear.vel.freq,
55
6.3.9
Implementational Alternatives
Instead of using unsigned types for sum and freq, you can consider using signed types. If you do this, you dont need to track the signs of acceleration and velocity separately, and it simplies comparisons. However, this also means that you need to change the way you compare sum with base_freq, as well as how you reset sum once it exceeds the limits.
6.4
6.4.1
Coding Tips
Eciency issues
Dont worry about eciency until you have the algorithm working. Once you have the algorithm working, you can start to consider these issues. On a powerful processor, you may need to worry much about code eciency. However, with an 8-bit processor that has no division instructions, some hassle in optimization can mean dramatic improvement of performance. 2 D is a fairly expensive operation. First, Let us review algorithm 1. 2av max 2 v involves multiplication. The killer part is division by 2amax . Division is a bit more expensive than multiplication. Lets deal with one problem at a time. To avoid division, we can change the comparison as follows: v2 D 2amax v 2 2Damax
(6.7) (6.8)
This is true because amax is a positive quantity. But, doesnt this still involves two multiplications in 2Damax ? In general, the answer is yes. If amax is a constant, then we can roll 2amax into a single constant to get rid of one multiplication. D is a variable, so we cannot roll this into a constant. However, we can still do some tricks to avoid having to multiply D to 2amax every time. Remember what D represents: it is the number of steps to perform. This means most of the time, D is incremented or decremented by 1. This is useful, because now we can use a variable for a scaled D. Whenever we decrement 1 from D, we subtract 2amax from the scaled version. Note that we still need to multiply when we reset D to an arbitrary number. How about the computation of v 2 ? It certainly appear that we cannot easily get rid of the multiplication here. Once again, think of what v represents. This is the frequency of stepping fstep . Most of the time, if not all, fstep is only incremented or decremented
56
by 1 (in the acceleration logic). This is a very useful fact because we can now eliminate multiplication. If we know (v 1)2 , we can compute v 2 as follows: (v 1)2 v
2
= v 2 2v + 1 = = (v 1) + 2v 1 (v 1) + v + v 1
2 2
See, no multiplications! Using a similar technique, you can also derive (v 1)2 using no multiplication from v 2 . Although this optimization can save you a lot of processing time, you do need to track quite a few more properties in the structures. As a result, I recommend the use of abstract data types so that you use a single function to increment/decrement v . This same function is also responsible to track a separate variable for v 2 . The same applies to D and its scaled version.
Chapter 7
Project 1
In this project, you need to write a program that controls a dierentially steered robot using two stepper motors. I realize that we do not have any actual robots. On the other hand, it is probably easier that way because on-controller debugging is dicult for mobile robots.
7.1
Final Objectives
One of the nal objectives of this project is a piece of code that works for practically all dierentially steered robots using stepper motors. To make your program easier to debug without robots, it must product suitable output to standard output (stdout) so that you can use another program to analyze the output. The following describes the output of your program:
Any text on a line after a pound (#) symbol is ignored, use this for commenting (and debugging). An event is recorded on one line. A motor performing a step is an event. Two events at the same exact time takes two lines. Time is measured in ticks. Use an unsigned long integer (32-bit) to count ticks. Each line begins with a tick count, expressed as a decimal unsigned number. Use a space character to separate elds. Use the letter L (uppercase) to indicate a step of the left motor. Use the letter R (uppercase) to indicate a step of the right motor. Although your program doesnt need to know the physical characteristics of the robot, it may be helpful to know this if you want to visualize the path of the robot. Assume the following physical characteristics:
57
58
CHAPTER 7. PROJECT 1 the base frequency is 5kHz (5000 times per second) one stepper motor step moves 1mm linearly for the attached wheel each wheel is 20mm from the center of the robot
For example,
14334 L means that the left motor performs a step in time slice 14334. The following describes the input of your program:
Each line of input is one setting, there can be multiple settings per time slice. Everything after the pound symbol (#) on a line should be ignored. Fields are separated by spaces. The following names are used for specifying parameters:
lin is a prex for linear parameters ang is a prex for angular parameters vel sets the desired top velocity (in number of steps per second), this is a magnitude, so it should be an unsigned integer. acc sets the maximum acceleration (in number os steps per second squared), this is a magnitude, so it should be an unsigned integer. D sets the remaining number of steps. This can be positive or negative.
A parameter is set by the parameter name, followed immediately by a possibly signed decimal integer for the value For example,
5621 lin.D 234 means that at time slice 5621, reset linear displacement to 234.
7.2
Step by Step
Write your program step by step. Test and debug as necessary in each step before moving one. Also, copy and paste code from my notes as you see t. Copy and paste from sources other than my notes is not permitted.
59
7.2.1
My recommendation is use gcc. This makes sure your code is relatively portable to avr-gcc when you are all done. You can use gcc in Linux, FreeBSD or as a component of Cygwin. If you dont want to use gcc, Borland C (installed at the lab) probably works okay. However, you need to make sure your program compiles in gcc when you turn it in. I will not port programs to gcc, but I can help you diagnose problems at the lab. If you use Windows and dont want to install a new OS just for this class, you have two main alternatives. First, you can use the Knoppix CD (or some other live Linux/FreeBSD live CD) so you can run Linux/FreeBSD without installing it. Second, you can install Cygwin from http://www.cygwin.com. Cygwin is a collection of les that emulates Linux on a Windows platform. In other case, I can burn CDs for Knoppix or Cygwin. For a project this big, it may be worthwhile to look into version control tools. RCS (revision control system) is available with Linux, FreeBSD, Cygwin and also native Win32. It is relatively easy to set up and use. This is not required, and it does incur a little bit of overhead initially. However, I think it will save you time in the end, and its a good skill set to have for the rest of your programming career. If there is enough interest, Ill write a small section about RCS and include it in this book.
7.2.2
Input/output
Write simple subroutines to read from a sample input le and write to a sample output le. The input le is probably more dicult to deal with because it requires some parsing (minimal). The output is easy, it should take you about 3 minutes at the most.
7.2.3
Implement and test your frequency division code rst. If this part isnt working, dont bother with the rest. After you get frequency division to work, implement velocity control. Dont worry much about displacement at this point. Set the initial displacement (remaining number of steps) to a huge number. You probably also want to limit the number of time ticks in the simulation. Focus on just the linear parameters at rst.
7.2.4
Once you get velocity to work, implement acceleration and deceleration. This requires the ability to parse velocity change input lines (and also the initial acceleration setting). Again, dont worry about displacement by setting it to a huge number.
60
CHAPTER 7. PROJECT 1
Be sure to test your logic by setting the target velocity high and low. Your program should respond by increasing and decreasing the velocity to the target velocities using the specied acceleration.
7.2.5
Displacement Control
After you have acceleration logic implemented, you can incorporate displacement control. Now, your program needs to know when to decelerate as the remaining number of steps becomes less than the distance required for deceleration. Furthermore, if the displacement is changed (increased) again, make sure your program responses by speeding up to the target velocity. At this point, assume displacement is always positive and get your program to work rst.
7.2.6
After your program works perfectly for linear control for positive displacements, extend it so it can also handle negative displacement.
7.2.7
At this point, your program only understands linear parameters. Add the concepts of left and right motors so that you can command the motors to spin independently. Because there is no angular parameters, your motors should be fully synchronized.
7.2.8
Angular motion prole control is identical to linear motion prole control, as far as logic is concerned. The settings is probably dierent. Since the settings (maximum velocity and acceleration) are read from standard input anyway, this should not be much of an issue. When you incorporate angular control, the left and right motors should start to spin dierently when there is angular motion involved.
7.3
This project is due three weeks from the assignment date, and it is worth 300 points (probably about a quarter of all the project points). Your program is graded by running feeding test input les and comparing output les with the proper answer. I will make some sample test cases available as soon as possible. The actual test cases I use for grading will not be disclosed. Ill probably also write a simple program that plots the course graphically from the output of your program so you can visualize the path. The score of your project is based on the proportion of test cases that work. This means a program that works for all test cases get 100% of 300 points (plus
61
or minus early or late adjustments). A program that works for half of the test cases get 50% of 300 points, and a program that does not work for any test cases get 0% of 300 points. Note that a program that does not compile automatically gets 0%.
7.4
Turn in this program as an attachment by email. If you use multiple les (highly recommended to keep yourself sane!), use either the ZIP format or TAR format (with or without GZIP or BZIP2 compression) to combine les into one archive. Send your email with the following subject line (wrong subject line gets 40 points deducted): CISP299 Project 1 by your name Send your email to [email protected].
62
CHAPTER 7. PROJECT 1
Chapter 8
R/C servo motors are self contained units used mostly for remote control vehicles (airplanes, boats and cars). An R/C servo motor has a very simple physical interface consisting of three wires. Two wires supply ground and power, while the third one carries the control signal.
8.1.1
Interface: three wires. As mentioned, one for power, one for ground and one for control. Motor: R/C servos use tiny DC motors internally to convert electrical energy to motion. There is a wide variety of these DC motors used in R/C servos. The inexpensive ones are quite crude, while expensive ones use coreless high eciency DC motors. Control circuit: a small circuit board holds all the electronic components necessary to control the DC motor of an R/C servo. Gears: R/C servos have very ne and precise gearing. Inexpensive ones have plastic gears, while expensive ones have metallic ones. Potentiometer: a potentiometer is used to encode the angular position of the output shaft of an R/C servo.
8.1.2
Control Mechanism
The control signal idles at 0V. A square pulse peaking at 5V with a width of 1ms to 2ms (for most R/C servos) species the desired angle. 1ms species one extreme, while 2ms species the other extreme. The angular dierence corresponding to the 1ms is about 180 degrees. In other words, if we consider the angle at 1.5ms is 0, 1ms species an angle of -90 degrees, while 2ms species an angle of +90 degrees. 63
64
Upon reception of the falling edge of this 1ms to 2ms pulse, the R/C servo control circuit compares the potentiometer output with the command. If there is any dierence, the control circuit turns on the DC motor to correct the error. The correction lasts about 20ms, then the R/C servo control mechanism idles again. Note that the correction may be strong or weak, depending on the dierence between the control signal and the potentiometer output. If an R/C servo is under sucient load that the angular position changes without active correction, the pulses should be spaced no more than 20ms apart. If the control pulses are too far apart, it is possible for the R/C servo to creep a little bit before correction is made by the next control pulse.
8.1.3
For any robot that needs limited angular control, R/C servos are good choices. This is because R/C servos can be inexpensive (less than $10 each, as of 2004), and they come in all sizes and capabilities. It is relatively easy to mount a GP2D12 sensor (or any other ranging sensor) on an R/C servo for scanning purposes. R/C servos are also commonly used in smaller legged robots for actuating the legs and other limbs. Interestingly, R/C servos are also used in some small wheeled robots. Most R/C servos can be modied for full rotation (instead of -90 degree to +90 degrees). The main advantage of robots utilizing modied R/C servos for full rotation is packaging and size. The standard size R/C servos pack lots of torque in a small package. Furthermore, despite the high gear ratio, most R/C servos have little play. The low voltage requirement of R/C servos is also an attractive features.
8.2
As mentioned, R/C servos are designed to rotate from -90 degrees to +90 degrees, and the control signal is meant to control the angle of the output shaft. This means that R/C servos are not useful for wheeled robots that require gear motors that rotate 360 degrees. As it turns out, most R/C servos can be easily modied for full rotation. Regardless of the brand and basic design, heres the general method:
open the case, disassemble the gears on the output (nal) gear, remove the tab that stops it from free rotation gain access to the printed circuit board desolder the potentiometer, you have two options to go from here:
65
replace the potentiometer with a series of two 5k Ohm resistors. The mid point connects to the pad/through hole of the center tap of the removed potentiometer, while the two ends connect to the pad/through hole of the two poles of the removed potentiometer. use wires to connect the pad/through hole to the removed potentiometer so that the potentiometer is external to the R/C servo for tweaking.
reassemble
After you perform this modication, send 1.5ms pulses to the control signal. If you bring the removed potentiometer out with wires, you can tweak the center position until the motor stops spinning. With the modication, the R/C servo is tricked to think it is physically at the center position (0 degree). When you send pulses of 1.75ms, for example, the R/C servo control circuit tries to correct the error by turning on the DC motor to spin one way. When you send control pulses of 1.25ms, on the other hand, the R/C servo control circuit rotate the DC motor the other way to try to correct the error.
8.3
PWM Control
Although one can use software to produce the pulse signal required by R/C servos, it is much better to use dedicated hardware to do so. For example, Parallax BASIC Stamps use the software approach. While it is easy to specify the pulses, it is dicult to send the pulses and perform other tasks at the same time. You can control multiple R/C servos at the same time with a BASIC Stamp because the pulses for each R/C servo need to be spaced out by 20ms. In the ATMega128 (and some other AVR variants), the built-in PWM component of the timers can be used to generate the necessary pulses for R/C servo control. Timer 1 and Timer 3 each has 3 channels of PWM, providing a total of 6 channels of PWM waveforms. To utilize a timer and its associated PWM ability to control R/C servos, the timer must be congured as follows:
period set to about 20ms enough resolution for pulse widths from 1ms to 2ms
This means we want to have as many counts as possible in 20ms. Because timer1 and timer3 both have 16-bit timer counters, the largest count value is 20ms 65536. 65536 is approximately 0.3s. Assuming the master clock is 16MHz, this means we need a prescale constant of 0.3s 16000000, which is approximately 4.88. The closest prescale constant is 8. Each counter increment then takes 1ms 8 16000000Hz , which is 0.5s. The resolution of the pulse is then 0.5s , which
66
is 2000 steps over 1ms. This provides sucient precision over 180 degrees of rotation. 20ms The period of the pulses should be 20ms. The count value for 20ms is 0 .5s , which is 40,000. Register ICR can be initialized to 40,000 to control the period of the pulses. The timer should also specify fast PWM operation for each output compare (OC) output pin.
8.4
Although R/C servos (especially ones modied for full rotation) have many advantages, they do have problems.
8.4.1
Most R/C servos modied for full rotation are used as on/o devices. In other words, a controller either request full speed forward, stop, or full speed backward. While some R/C servos exhibit somewhat proportional responses when the control pulse is close to 1.5ms, the response is entirely dependent on manufacturer and model. In other words, there is no standard like 1.55ms means 50% of full speed. A correct implementation of R/C servo control should not even have a predictable proportional response based on pulse width. As a result, proportional response can only be expected from lower end R/C servos.
8.4.2
Some R/C servos do not rotate evenly after modied to rotate 360 degrees. This is hardly surprising because an R/C servo is not designed for smooth full circle rotation! In fact, some miniature R/C servos only has one half of a full gear at the drive axle.
8.4.3
At least in inexpensive R/C servos, the plastic gears are fairly fragile. A competition robot can easily break these gears. Although replacement gears are available, it is still a hassle to have to repair a servo. This is especially the case when the mechanism is dicult to access. More expensive R/C servos use metallic gear and ball bearing for the main shaft, which signicantly improves the longevity and reliability of the device.
8.4.4
Most R/C servos are designed for steering, apper control and other intermittent tasks. Although an R/C servo needs to exert a large amount of torque to actuate
67
something, it needs relatively little torque to hold position due to friction and the high gear ratio. When R/C servos are modied for full rotation, they often have to operate continuously. This is not a problem when the continuous motion does not require much torque. For example, desktop robots or robots designed to run on smooth and level surfaces require little torque to maintain a xed speed. However, for robots that run on rough surfaces or push a high load (such as sumo-type competition robots), R/C servos may overheat and burn o winding insulation. This causes shorts in the motor, which can potentially blow components on the control circuit board.
68
Part IV
69
Chapter 9
DC Motor Control
This is a relatively short chapter on DC motor control. It is short because there is not much to discuss! Throughout this discussion, we assume a DC motor is just the usual brushed kind. Brushless motors require a bit more complicated control.
9.1
To control the direction of rotation of a DC motor, a controller only needs to control the polarity of the electrical potential applied to the terminals of a DC motor. This is typically done by a device called an H-bridge. The construction of an H-bridge is out of the scope of this class. From the software perspective, we use one bit to control the direction of torque of a DC motor, thats it!
9.2
I put quotes around strength because the torque of a DC motor is controled by the current passing through the device. This is, generally speaking, dicult to achieve. As a result, we only control the average voltage applied across the terminals of a DC motor.
9.2.1
You can change the continuous voltage across a DC motor by a transistor. Without going into details, this can be done with a N-channel MOSFET with an opamp, or a bipolar junction transistor (BJT) with a variable bias current. In the past, it is dicult to adjust the continuous voltage from a controller without a digital-to-analog converter (DAC). This is much easier these days because of digitally controled potentiometers. In other words, it is possible to control a DC motor by controling the continuous voltage. 71
72
However, this approach has a problem. Power dissipation is P = V I . In other words, the power that needs to be dissipated as heat is proportional to the product of the voltage drop across a device and the current passing through the device. Let us consider a modest DC motor that draws up to 4A at 12V. In order to produce 6V across the motor, the other 6V must drop across the transistor (N-MOSFET or NPN BJT). The current consumption at 6V is probably about 2A. This means the power dissipated by the transistor is 12W. 12W does not seem to be much. However, if this much power needs to be disspiated by a small device like a transistor, it is a lot! If you try to control the continuous voltage across a DC motor, youll need lots of heatsink area and even forced air cooling (use a fan to blow on the heatsink).
9.2.2
PWM
Pulse width modulation means a device is either fully on (100%) or completely o (0%). The period of PWM is the length of a cycle, and this is xed at some relatively small amount of time. For DC motor control, a period of 5ms is suitable for smaller motors, while bigger ones can have periods up to 20ms. Within a cycle, a controller controls the proportion of time in which the device is on. In other words, for a motor not to do anything, the duty cycle is 0%. To utilize all the torque available, the duty cycle should be 100%. A controller can also adjust the duty cycle to any point between 0% and 100% to change the average voltage applied to the motor. How is this better? Lets try to produce an average of 6V across a motor, just like in the previous subsection. The available voltage is 12V, this means we want to have a 50% duty cycle. In other words, half of the time, the transistor is fully on. When a transistor, be it N-MOSFET or NPN BJT, is fully on, the power dissipation on the device is at a minimum. A modest N-MOSFET has a resistance of about 0.1. This means the power dissipated at the transistor is P = I 2 R = 16 0.1 = 1.6W when the transistor is turned on at 100% duty cycle. At 50% duty cycle, the power dissipation is exactly one half at 0.8W. 0.8W can be dissipated by a TO220 package without external heatsink at room temperature. We just reduced our power dissipation from 12W to 0.8W!
9.3
Are we done?
Since most controllers can general PWM waveforms, shouldnt we be done? Unfortunately, DC motors require feedback for precise control. What we have discussed up to this point is merely throttling. You cannot drive a car blind-folded with just a gas pedal and a brake pedal! Similarly, just being able to PWM a DC motor does not mean we can control the rotational speed. The actual rotational speed of a motor depends on many factors, including internal friction (it changes over time), terrain, battery output voltage (also changes over time), switch eciency and many other factors.
9.3. ARE WE DONE? In short, we have little idea of how fast a motor is actually rotating!
73
74
Chapter 10
Motion Encoding
The previous chapter introduces PWM control for DC motors. We also realized that we cannot deduce the rotational speed of a DC motor just using the PWM duty cycle. In fact, there is no way to theoretically compute the rotational speed of a DC motor precisely! We need to rely on feedback to let a controller know exactly how fast a DC motor is rotating. More precisely, we need some feedback to know how fast a robot is moving.
10.1
Optical Encoding
Optical encoding relies on an encoding disc and an encoding sensor. The encoding disc is usually an opaque disc with slots that let light pass though. An encoding sensor has two sides. One side has an Infrared Emitting Diode (IrED), and the other side has a phototransistor. The encoding disc is placed between the IrED and the phototransistor. This way, as the disc rotates, the phototransistor sees transitions of light and darkness because the slots of the disc has motion relative to the sensor. Optical encoders are commonly found in devices that operate in clean environments. Some printers use DC motors with optical encoding instead of stepper motors because it is less expensive. Small robots that operate in controled (indoor) environments also use optical encoders. However, optical encoding cannot be used in dirty environments. Dust, sand, oil droplets, gunk can all easily render optical encoders useless. Most larger robots that operate in outdoor environments cannot use optical encoding unless the encoder is concealed (air-tight). This is still possible because motion can be clutched by magnets so that there is no mechanical linkage from the axle to be encoded to the encoder axle. 75
76
10.2
Magnetic Encoding
Recent semiconduction development has made Hall-eect sensors very cost effective. There are several kinds of hall-eect sensors. The most common type is eectively a semiconductor reed-relay. It is a transistor that is turned on if and only if the magnetic eld perpendicular to it exceeds a threshold. With hall-eect sensors, we can encode motion by placing small magnets on a wheel or a gear. The encoding resolution is somewhat limited with this approach because magnets are fairly big (compared to a slot on an optical encoder disk). Furthermore, because the magnet is on the rotating part, it can fall o when the rotational speed is high. An alternative is to place a bias magnet near the sensor, then use a metalic (with iron component) gear or slotted disc for encoding. The teeth of the gear serves as magnetic eld concentrators. As the gear turns, the magnetic eld experienced by the sensor changes, and these transitions indicate motion. See http://rocky.digikey.com/WebLib/Melexis Web Data/MLX90217.pdf for more information. Magnetic encoding is not aected by most dirt, dust and oil. It is suitable for robots that need to operate in dirty or outdoor environments.
10.3
Quadrature Encoding
Regardless of the sensor type, if it outputs a 50% duty cycle waveform, quadrature encoding can be used. In quadrature encoding, two sensors of identical properties are used. In other words, both sensors must have the same 50% duty cycle output waveform. The two sensors are spaced out by a quarter period. In other words, if it takes x rotation for one period (one half on, one half o), the two sensors are spaced by (n + 0.25)x rotation. n can be any integer, as multiples of periods does not change the shape of the output. Let us call one sensor A and the other one B. Once A and B are spaced out by a quarter period, we get four transitions instead of two per period. Furthermore, we can also tell the direction of rotation. If waveform A leads waveform B, it indicates rotation in one direction. If waveform B leads waveform A, it indicates rotation in the other direction. This means that with two sensors, we can keep the same encoder disc (optical or magnetic), but we end up with:
twice the resolution the ability to tell direction of rotation
10.4. IMPLEMENTATION
77
10.4
10.4.1
Implementation
General Approach
Regardless of the encoding technology (optical, magnetic or even contact based), a program needs to know when a transition occurs. Without any hardware peripherals to assist, a program can still rely on a timer ISR to sample an encoder input line. The logic to do this was already discussed when we discussed general purpose input. Debouncing is usually not needed nor desired. Detecting edges (transitions) this way is general enough that itll work on most platforms. However, it is also limited in terms of performance. An interrupt frequency of 5kHz is considered fairly high. Even at this frequency, a program can only detect 2500 transitions per second. The frequency is divided by 2 because it takes two samples to detect one transition. With a ne optical encoder disk with 500 slots, one revolution yields up to 2000 transitions because there are 4 transitions per period with quadrature encoding. As a result, we can only correctly interpret transitions up to a speed of 1.25r/s, which isnt exactly fast.
10.4.2
External Interrupts
The ATMega128 has many external interrupt lines. An external interrupt pin is a general purpose I/O pin as far as conguration is concerned. However, an external interrupt pin also has the ability to cause an interrupt when it senses a transition. This is great for encoding purposes, because we only need to pay attention when there is a transition. Dierent members of the AVR have dierent external interrupt designs. On an ATMega128, an external interrupt can be set up to sense either a rising edge or a falling edge. Although this appears to be a problem, it is not. Except for the rst transition, all other transitions (rising or falling) can be captured correctly. This is because the ISR can ip the edge sensing polarity! In other words, after sensing a falling edge, the ISR can ip the polarity to sense a rising edge. This makes sense because a falling edge must be followed by a rising edge before another falling edge occurs. Although external interrupts only occur when there is a transition, there is still a maximum number of transitions that the processor can handle. For very high speed or high resolution encoding, ISRs should be written in assembly language to minimize the overhead (mostly for saving and restoring registers).
10.4.3
The general logic for interpreting a quadrature encoder input pin is as follows (assume we are reading channel A). Also assume that this code is only invoked when we know there is a transition at channel A. if A has falling edge then
78
if B is high then A leads B, increment motion tick counter else A follows B, decrement motion tick counter end if else if B is high then A follows B, decrement motion tick counter else A leads B, increment motion tick counter end if end if While this code may seem dicult to implement, the AVR has some instructions that can read a pin and branch in two clocks. For those who are interested in assembly programming, these instructions are sbic and sbis, for skip if bit of I/O is cleared and skip if bit of I/O is set, respectively. With these instructions, assuming channel A is pin X of port Z, and channel B is pin Y of port Z, the corresponding assembly code is as follows: sbis Z,X rjmp falling_edge ; my state is high, rising edge sbis Z,Y ; leading rjmp increment rjmp decrement falling_edge: sbis Z,Y ; following rjmp decrement rjmp increment increment: ; code to increment counter ret decrement: ; code to decrement counter ret This code only takes about 8 clocks to decide whether it needs to increment or decrement. This translates to 0.5s. The code to increment or decrement a counter takes about 10 clocks. As a result, the entire operation should be done within 2s given a master clock of 16MHz. I knew assembly programming is good for something.
79
10.5
Electronic Interface
Some optical encoders have built-in conditioning circuits so that the output is TTL/CMOS compatible. In other words, the output is a signal of high and low voltages with clean and vertical transitions. Other optical sensors do not include such conditioning circuits. This means that the output of these sensors can be a little dicult to be interpreted by a TTL/CMOS input. There are several reasons for the diculty.
10.5.1
What this section describe are problems that is common to all phototransistors. Transition edge The output of a phototransistor (in a tranmissive sensor) does not have sharp vertical edges when the sensor transitions (from light to dark or vice versa). This is because of physical properties of a transmissive sensor. First, the IrED (infrared emitting diode) is not a point source of infrared. Instead, from the perspective of the phototransistor, it looks more like a disc of infrared. This means that during the transition from opague to clear (from the encoder disc), a phototransistor experiences a continuous change from full IrED disc to no IrED disc. This continuous change of IrED disc exposure translates to a continuous change of the output voltage. For a normal CMOS/TTL input, this is a problem. This is a problem because the amount of time to transition from full exposure to the IrED disc to no exposure is longer than the response time of a CMOS/TTL gate. Most CMOS/TTL input have specications similar to the following:
High-level minimum input voltage: VIH = 3.15V Low-level maximum input voltage: VIL = 1.35V
This means that the device guarantees to register a one when the input voltage is 3.15V or more, and guarantees to register a zero when the input voltage is 1.35V or less. But what if the input voltage is, say, 2.5V? Is that a one, or is that a zero? Many CMOS devices bounce the interpretation between 0 and 1 when the input voltage is between the high-level minimum and the low-level maximum. This leads to false transition readings. Rail to rail range As described in the previous section, a TTL/CMOS device only guarantees to register a zero when the input voltage is below about 1.35V, and only guarantees to register a one when the input voltage is above 3.15V. Unfortunately, some transmissive sensors cannot produce an output voltage range that exceeds this range of 1.35V and 3.15V.
80
The problem is usually due to two physical properties of an optical encoder. First, some optical encoders have weak phototransistors. This means the phototransistors cannot let much current through the device, even when it is 100% on. As a result, it is often necessary to use a large value pull-up resistor. This, in return, change the response time of the phototransistor. As a compromise, sometimes it is necessary to use a less-than-optimal resistor value, making it impossible for a phototransistor to yield a low voltage when it is fully turned on. The other limiting factor, surprisingly, is the encoder disc. Industrial encoder discs are machined from thin aluminmum sheets. A thin foil of aluminum is 100% opague to infrared. However, it is far more cost eective to create encoder discs from transparency sheets (for projector use). A stripe pattern can be easily generated by various programs, and the pattern can be printed to a transparency sheet using a laser printer. Although the color black from a laser printer is dark enough for the human eye (in the visible spectrum), it is not guaranteed to be opague enough in the infrared spectrum. This is because infrared has a longer wavelength than visible light. As a result, a toner density that is sucient to block the transmission of visible light is not necessarily sucient to block the transmission of infrared. As a result, a cheap homemade encoder disc may be somewhat transmissive even at the opague (black) area. This infrared leak through means that the phototransistor is always on a little bit, and it is never completely o. The corresponding output of the phototransistor never reaches the high end of the rail.
10.5.2
Solution
Comparators may be used, combined with a variable voltage divider bridge, to generate the correct output. The LMV339 (a low voltage variant of the LM339) is one option. It is inexpensive at less than $1 each (at single quantity). The datasheet from National Semi detailed how to add hysteresis. A single chip is enough for two quadrature encoders, although we will need a number of resistors (3 per comparator, yielding a total of 12 for the whole set up). The basic circuit is illustrated as follows:
81
R2 R5
VCC
R1
3 12
IC1A
2
IN
4
R3
LM339D
R4
R3 R3 +
1 1/R2+1/R5
GND
Heres how it works. When the output of the comparator is high, the voltage at the non-inverting terminal is as follows: Vh = VCC (10.1)
When the output of the comparator is low, the voltage at the non-inverting terminal is as follows: Vl =
1 1/R3+1/R5 VCC 1 R2 + 1/R3+1 /R5
(10.2)
This means the input voltage must drop below Vl for the output to switch to high, and the input voltage must rise above Vh for the output to switch to low. This logic appears to be inverted, hence the name inverted. Note that the pull-up resistor, R1, is necessary because the comparator has an open-collector type output. Without a pull-up resistor, the device cannot drive the output high (pin 2 of IC1A). R4, the load resistor, is not actually there. It is merely a symbol that renotes the impedence of the load (in our case, it is in the range of mega Ohms).
10.5.3
Example
Lets say the voltage swing (of the phototransistor output) is between 1.8V and 4V. We want to make the switch-over point centered at 2.9V with 0.5V of hysteresis. This means that we want the comparator to output high when the voltage drops below 2.4V, and output low when the voltage exceeds 3.4V. There should be no change when the voltage is between 2.4V and 3.4V.
82
This can be achieved by 130k for R2, 200k for R3 and 330k for R5. Once one of the three resistor values is xed (R5, for example), the other two can be solved in closed form. It is the ratio among the resistor values that is important.
Chapter 11
PID Loop
11.1 The Theory
The drive system of a robot is control system. In order for this system to behave predictably, it should be a closed-loop control system. This means some feedback should be used to adjust the output to the motors. This section discusses the theory of a well-known method of closed loop control: PID (proportional integral and dierential). For any control, there is a set point. The set point is what is the feedback should read. Let us use s(t) to represent the set point (reference) at time t. The actual feedback, f (t), however, if very unlikely to be at s(t). As a result, there is an error term, which is simply the dierence of the two, e(t) = s(t) f (t). Based on the error term, a control system adjusts the output to the system. In continuous terms, the output of a control system, k (t) is expressed as follows:
t
k (t) = Kp e(t) + Ki
0
e(x)dx + Kd
de(t) dt
(11.1)
11.1.1
The proportional term is merely a scaled error e(t). This term makes perfect sense. When e(t) is large, it means our set point is far away from the feedback. As a result, the output needs to be very high in order to bring the feedback closer to the set point. As our feedback gets closer, e(t) becomes smaller, and it makes sense to reduce the output so we dont overshoot. In a lossless system, just relying on Kp e(t) should get us to the set point, eventually. However, because all practical systems are not lossless, Kp e(t) never gets us to the set point. This is because at some point, the term Kp e(t) is merely enough to counter the loss, and the system enters a steady state in which the input (from the heater or motor) cancels the output (heat loss or friction). 83
84
Despite the fact that Kp e(t) is not sucient by itself, it is the main component during the rst phase of approaching the set point. Physically, Kp e(t) is the bulk of the response to changes.
11.1.2
The integration term, Ki 0 e(x)dx is the area between the set point and the feedback in a plot against time. As the proportional term Kp e(t) runs out of t gas as the feedback gets closer to the set point, the integration term Ki 0 e(x)dx t continues to increase. As a result, the summation of Kp e(t)+Ki 0 e(x)dx should eventually become large enough so that any loss of the system is balanced by t the contribution of Ki 0 e(x)dx. Note that that the integration term is not entirely independent from the proportional term. As the integration term helps to bring feedback closer to the set point, the proportional term gets even smaller. Eventually, in a tuned system, as the feedback becomes the same as the set point, the entire output comes from the integration term. t Physically, Ki 0 e(x)dx is the amount of output necessary to balance loss of the system. This term is very dependent on how the other two terms are set up, although it will, eventually, get the feedback to match the set point.
11.1.3
(t) The derivative term, Kd de dt only contributes when there is an abrupt change of e(t). This change can be due to a change of set point, or it can be due to a change of feedback. A sudden change of feedback can be due to external factors such as a bump on the road. This term is also called the dampening term because it dampens the eect (t) of the other two terms. As e(t) gets smaller, Kd de dt is negative. In fact, for de(t) motor control, Kd dt is very negative to begin with because the response of a motor is stronger at lower speeds. So why do we need the derivative term? One use of the derivative term is so that we can crank up Kp . With a dampening term, we can adjust the other two terms so that the output does not zzle out as the feedback gets closer to the set point. Note that Kp e(t) t zzles out by itself anyway, but Ki 0 e(x)dx contributes more in time. Also, the response of a motor decreases as speed increases.
11.2
In a discrete system, we can transform the previous equation to a discrete form (t is a natural number): k (t) = Kp e(t) + Kd (e(t) e(t n)) + Ki e(t) (11.2)
85
Note that the division of n for the dierential term is absorbed by Kd . Why choose n instead of 1? To get a dierential, we just need to compare the error of time t to the error at time t 1. This issue will be addressed later. It should be noted that the sum term should be reset every time the reference is changed. This is because the integration of errors should be particular to a particular reference.
11.3
Practical Concerns
11.3.1
Resolution of Output
First, there is the resolution of the output. Since most robots are controlled by pulse-width-modulation (PWM), the output is an adjustment of the duty cycle of the PWM signal. The real question is, then, the resolution of PWM. Obviously, a resolution of 2 (1 bit) is not sucient. That would be the same as on-o control. On the other hand, a resolution of 1024 (10 bits) is too much because most mechanical systems have errors that exceed 0.1%. For a mechanical system that has an inherent error of 1%, 128 divisions for the PWM is enough. We will use rk to represent the number of bits to represent the output.
11.3.2
The proportional term is the main component of the output of a PID loop. As a result, Kp is an important coecient, as is the error term itself. We need to determine the resolution of the error term, e(t). Let us consider a system where the speed can be up to xticks/s. This means the error term can be up to 2x because the motor can be spinning full speed backward and ask to spin full speed forward. The next factor is how often to run the PID loop logic. The frequency of the loop f helps to determine the resolution of the error term. Although the actual error can be up to 2xticks/s, between each PID invocation, the dierence only x be up to 2 f . For example, if the top speed of a reversible system is 1200 ticks/s, and the PID loop is invoked at 50Hz, e(t) can only range from -48 to 48, which can be represented by a 7-bit signed integer. The number of bit required for e(t) is, then, re = log
4x f
log 2
(11.3)
When re is close to rk , the magnitude of Kp is small. This means you need to rely on fractions in Kp to ne tune the terms. Instead of using oating point numbers, many small MCUs benet from the use of more ecient integer/xed number computations.
86
For example, even if we know that Kp does not exceed 4, we can use 8 bits to represent it. The least signicant 6 bits become fractional terms. Given an 2 integral bit pattern of 101101012 , the actual represented value is 10110101 . The 26 6 division by 2 is inexpensively performed by right shift operations.
11.3.3
The integration term is important because it allows a system to reach the reference. Without the integration term, a PID loop can never achieve the reference signal. The integral term relies on the summation of errors. This sum can become much larger than the error term itself. It is advisable to put limits on this term (caps). The range of the integral term should be about 20 times that of the range of the error term. Due to the magnitude of the error term, there is no problem with resolution in the integration term. Because the integration is a large number to start with, the coecient Ki must be a small fraction. This is easily done by assuming the binary point to the to the left of the most signicant bit of the binary integer representing Ki . The trouble, however, is that we now have a large number multiplied by another large number. This can take a bit of time for an 8-bit system. As a result, some control systems choose to ignore some of the less signicant digits to speed up computation.
11.3.4
The dierential term is the change of error. While this term can have the same magnitude as the error term itself, most of the time it is very small (one tenth the magnitude of the error term itself or smaller). In a continuous system, this is not a problem because there is innite precision. However, in a digital system, this poses the problem of digitizing the contribution from the dierential term. Instead of just relying on the number of ticks between PID invocations, we can also rely in the period between ticks. Although period is proportional to the inverse of speed, it is still more accurante compared to the number of ticks at slow speeds. Consider a realistic case where a system expects about 1000 ticks per second. This translated to one tick of millisecond. However, as the system slows down to rely on friction to come to a complete stop, the speed can be down to a small number (such as one) of ticks per second. It becomes impossible to know the actual speed. Using a timer, however, we can track the period between ticks and use the period to deduce the actual speed. If we consider the worst case of a 16 count timer for a 50Hz PID invocation frequency, the lowest frequency is 50Hz, while the next lowest frequency (due to digitization) is 15/161 20ms , which translates to 53.3Hz. This resolution is better than 7%, even when the tick count per time method fails. While 7% may not sound like a lot, it makes a lot of dierence when there is nothing lese to rely on.
87
11.4
11.4.1
The feedback and set point are both in units of ticks per PID period up to this point. This is okay for most cases, assuming the encoding has sucient resolution. However, for robots with low resolution encoders, and also for lowspeed situations, just counting ticks between PID invocations produces coarse and quantized results. Let us assume the PID control loop is invoked at 20Hz (20 times per second). Let us also assume the encoder disk has 1000 transitions per revolution. At a very low speed, such as 0.1 revolutions per second, we only get 100 ticks per second. This is divided by 20 because the sampling frequency is 20Hz. As a result, we only have, on the average, 5 ticks per period. The error term is going to be even more quantized because it is the dierence between the set point and the feedback. As a result, the control signal has very poor resolution because it depends on the error term.
11.4.2
A Solution
A solution is to compute the period between ticks, and use its reciprocal as the speed. This method is not suitable when there is sucient encoding resolution because reciprocal is dicult to compute (compared to counting ticks), and the resolution of the period-oriented approach drops as speed increases. However, at lower speeds, this period-oriented method yields much better resolution than tick counting. In our previous example, using the tick count method, 0.1r/s cannot be distinguished from 0.09r/s because both yields about 5 ticks per PID invocation. However, using the period measurement, 0.1r/s results in a period of 10ms, whereas 0.09r/s results in a period of 11.1ms. Even with a timing resolution of 1ms, we can still distinguish 0.09r/s from 0.1r/s. Note that timing resolution can be improved by hardware peripherals. The ATMega128, for example, has an input capture device associated with timer 1. It can record the duration of a pulse at resolutions down to single clock periods.
11.4.3
Practical Considerations
Although we can improve the resolution of low speed encoding, the benet may not be useful because of other problems. For example, due to the nature of DC motors, they are not smooth at lower speeds. As a result, it may not be practically useful to use period measurement to improve the encoding resolution at lower speeds.
88
Chapter 12
The TPIC0108 is the perfect chip to use because it supports all four modes. However, it does not have an enable input. This can be provided using a PAL/CPLD. The ATF750C-15SC (SO24 package) is a good option because it has enough pins, and cheap enough at $2.14. The Needhams EMP-20 programmer can program this chip, provided that I get an SO-DIP convertor rst (SO330-28p). Atmel provides WinCUPL that is free to create les for these devices.
12.1.1
The logic to control the two lines of the TPIC0108 uses the following truth table: PWM DIR BRK IN1 IN2 comments 1 ? ? 0 0 quiescent hi-Z (o) 0 ? 0 1 1 brake 0 1 1 0 1 one way 0 0 1 1 0 the other way The equation for IN1 is as follows: IN1 = PWM BRK + PWM DIR Whereas the equation for IN2 is as follows: (12.1)
(12.2)
This allows a PWM line be used for each channel. Note that the PWM 89
90
signal and the BRK signal are both active-low so that the default state can be pulled-up when the controller is in reset state. This plan uses 3 input channels per H-bridge for maximum exibility. Since this is often only useful for DC motors, we only need to use 6 lines from the MCU to control two DC motors.
12.1.2
For stepper motors, we can use only two lines per motor for step and direction. This requires that the logic chip maintain its current state, and also to have an initial reset state. The truth table of the stepper half-stepping state machine is as follows (coils A and B, each has its own IN1 and IN2 signals): A.IN1 A.IN2 B.IN1 B.IN2 comment 0 1 1 1 A forward, B o 0 1 0 1 A forward, B forward 1 1 0 1 A o, B forward 1 0 0 1 A backward, B forward 1 0 1 1 A backward, B o 0 1 0 A backward, B backward 1 1 1 1 0 A o, B backward 0 1 1 0 A forward, B backward Because we need to use a previous state to determine the next state, the programmable logic needs to be registered. Fortunately, the ATF750C device has a product clock for each macro cell. This means we can dedicate a clock (STEP) for each stepper motor. Next, we need to express the next state as an equation of the current terms, as well as the clock and direction. ar is async. reset state, ck is the clock product term, oe is output enable and d is the D-latch input term. The following is the equation for coil A IN1.
A.IN1.oe A.IN1.ar
= 0
A.IN1.ck = STEP A.IN1.d = DIR A.IN1 A.IN2 B.IN1 B.IN2 + DIR A.IN1 A.IN2 B.IN1 B.IN2 + A.IN1 A.IN2 + DIR A.IN1 A.IN2 B.IN1 B.IN2 + DIR A.IN1 A.IN2 B.IN1 B.IN2
The other output pins can be determined similarly. Using this technique, we only need two lines to control two stepper motors.
91
12.1.3
To perform software controled chopper drive, it is necessary to use a PWM signal to enable and disable the device. Each coil requires its own PWM signal. We can build on top of the equations in the previous section. The trick is to utilize the output enable line and a pull-down resistor for each output. This way, we can connect the PWM (output compare) signal from the MCU directly to the output enable signal. When a pin is not driving, the signal defaults to 0, and the corresponding H-bridge goes to Hi-Z state. The modication is as follows:
A.IN1.oe A.IN2.oe
= =
A.P W M A.P W M
(12.11) (12.12)
This approach requires the use of four output compare channels. In addition, the duty cycle needs to be controled dynamically (the overow interrupt needs to change the duty cycle).
12.2
Layout Considerations
The TPIC0108 is a thermal-enhanced SO20 IC. Although it has the same footprint as other SO20 (small-outline 20 pin) ICs, it has an extraheatsink area that is under the IC. This pad allows the chip to be soldered on a large area of pad on a PCB, and provide heatsinking capacity so the chip can be fully utilized. With a reow oven, this does not pose any problem because the solder paste placed under the chip will ow as the temperature increases.However, for hand soldering, it is much more dicult to get solder under a chip to ow. This is why I included four large vias under the chip. A nd soldering tip should t in such a via, and so we can solder the chip at least to the vias. This design also allows me to put a large pad on the top side for additional heatsinking. As far as the traces are concerned, the current (20040322) layout has two main trunks for ground and supply. The design is also chainable, making it easy to add three more H-bridges without adding to ground and power routing problems. The relatively large ground and supply traces also provide additional heatsinking resources. Because of the use of a PAL/CPLD, routing control signals is easier (than without a PAL/CPLD). As long as the PAL/CPLD is placed between the MCU and the H-bridges, I can use APL/CPLD equations as routing resources.
92
Part V
Reading Sensors
93
Chapter 13
13.1 13.2
The successive approximation nature of the ADC (used in the ATMega128) requires its own clock. Instead of using another oscilator that is asynchronous to the system clock, the ATMega128 allows software to congure the ADC clock based on the main clock. This is documented on page 232 of the ATMega128 datasheet. The prescaler is controled by ADPS0-ADPS2, bits 0 to 2 of I/O location ADCSRA. The exact division factor is shown on page 244 of the datasheet. Note that a high speed ADC clock cannot fully utilize the 10-bit resolution capable of the device.
13.2.1
Voltage Reference
An ADC only computes the ratio of some input voltage with respect to a supplied voltage. The ATMega128 ADC has a programmable voltage reference selection. Bits REFS0 and REFS1 (bit 0 and 1 of ADMUX) selects the source of voltage reference. Refer to page 242 of the datasheet. 95
96
For most practical use, one can either use AVCC (analog supply voltage) or the internal 2.56V reference (modes 1 and 3, respectively). Note that both modes require that an external capacitor be connected between the pin AREF and ground.
13.2.2
Multiplexer Selection
Although there is really only one ADC in the MCU, and only 8 external pins for analog input purposes, the ADC peripheral has 32 possible channels to select from. This is because the ATMega128 can operate in dierential mode and have internal ampliers to amplify the voltage. The channel is selected by MUX0 to MUX4 (bits 0 to 4 of ADMUX). Refer to table 98 on page 242 for a full list of the channels.
13.2.3
The ADC can be set to either free running mode or manual mode. In free running mode, the ADC samples and updates automatically and continuously. You can get the highest sampling frequency using this mode. Note that the ADC must be xed to a channel in free running mode. In other words, you cannot set up the ADC to scan channels in the free runnung mode. In manual mode, the ADC only samples and updates after it is started manually in software. After one conversion, the devices stops. This mode is useful if you need the best resolution (you need to put the rest of the MCU to sleep during conversion), or if you need to reduce power consumption. Free Running mode is selected when ADFR is set to 1. ADFR is bit 5 of ADCSRA.
13.2.4
Interrupts
Because it takes a signicant amount of time for a conversion to complete, most programs do not poll for completion. Instead, most programs enable ADC interrupt so that the corresponding ISR is called only after a conversion is completed. Bit ADIE (bit 3 of ADCSRA) controls if interrupts should occur after each conversion. ADIF (bit 4 of ADCSRA) is set when an ADC conversion completes. It is cleared either by executing the ISR of ADC completion, or when a 1 is written to this bit. This ag is useful when AD conversion is handled by polling.
13.2.5
ADEN (bit 7 of ADCSRA) controls whether the ADC is enabled or not. A one in this bit means the device is enabled. ADSC (bit 6 of ADCSRA), on the other hand, starts an AD conversion. A one should be written to this bit to start the next conversion.
97
13.2.6
Bit Positions
The ADC also allows you control where the signicant digits are located. Bit ADLAR (bit 5 of ADMUX) selects whether zeros are inserted to the most signicant bits, or the least signicant bits. If ADLAR is 0, then the most signicant bits are zeros, whereas bit 0 to bit 9 are the results. If ADLAR is 1, then the least signicant bits are zeros, whereas bit 6 to bit 15 are the signicant bits.
13.2.7
Reading Results
After each conversion, the result is stored in ADCL and ADCH. ADCL is the least signicant byte, whereas ADCH is the most signicant byte. In order to keep both bytes synchronized, ADCL should be read rst, then ADCH. This is because reading ADCL latches the value of ADCH into an 8-bit buer, then reading ADCH afterwards ensures both bytes belong to the same sample.
13.3
Common Techniques
This section discusses common techniques that can be used with ADCs. Most of these techniques are not specic to the AVR MCUs.
13.3.1
Some embedded applications do not need to read the ADC continuously. For example, in a room temperature control application, you only need to read temperature every 30 seconds or so. In these applications, you can use the following approach: set up ADC start ADC while ADC is not done do nothing to do here end while This approach is only suitable if an application has no time critical operations when it is waiting for the ADC to nish. The advantage of this approach is that it is easy and universal. On the other hand, this approach makes the program wait for ADC completion.
13.3.2
For ADCs that has a free-running mode and interrupt capability, you can set up the ADC to free-running and interrupt when a conversion completes. In the ISR, do the following: copy from ADC value I/O location to global variable
98
Then, you write a function to read the global variable. Youll need a function to do this because interrupt should be disabled when you take a snapshot of the global variable: unsigned ADC getsnapshot ( void ) { unsigned r e s u l t ; char s = SREG; CLI ( ) ; r e s u l t = ADC value ; SREG = s ; return r e s u l t } Then, whenever you need to read the current ADC value, you just need to call this function: i f ( ADC getsnapshot ( ) < 5 1 2 ) // t u r n on t h e h e a t e r ... This method is better than the previous one because there is very little overhead to read the current ADC value. A program does not need to stop and wait for a conversion. However, one drawback of this approach is that the value can be stale. In other words, the snapshot can be a value that is t old, whereas t is the period of free-running conversion. With the AVR, you dont even need to set up an interrupt because the value register of the ADC is already buered. In other words, the function to return the current ADC value can be as simple as follows: unsigned ADC value ( void ) { unsigned r e s u l t ; r e s u l t = ADCL; r e s u l t | = (ADCH << 8 ) ; return r e s u l t } What if we need to read values of dierent ADC channels?
13.3.3
Server-client Approach
To get the latest value of dierent ADC channels without busy waiting, we need to set up a server-client arhitecture. Although this may sound dicult, it is not. Let us assume that we are using a multithreading kernel. When a thread needs to read the value of a channel, it calls a function that has the following logic: queue the request and current thread
99
if ADC is inactive then activate (set up the ADC) else nothing to do end if block the current thread The ADC ISR has the following logic: locate the lled request (head of queue) copy ADC value to a return value for the request remove the request from the queue if there is more in the queue then set up the ADC for the requested channel end if This approach is only useful for multithreading programs because when one thread is waiting for an ADC conversion, other threads can continue. The actual code to do this is a little messy because a multithreading kernel must be involved. Instead of using a dedicated queue for ADC requests, it is also possible to use a semaphore for queuing purposes. When a thread needs to start an ADC conversion, it can call a function that does the following: P(adc semaphore) set up ADC P(adc completion) copy the ADC value to return value V(adc semaphore) The ISR is then modied to do the following: V(adc completion) My real-time kernel supports both multithreading and semaphores, so it can easily implement this approach.
100
Chapter 14
IR Ranging Sensor
This chapter discuss the Sharp GP2D12 sensor.
14.1
General Information
The Sharp GP2D12 sensor (and its siblings) are triangulation-based sensors. In other words, a beam of infrared is emitted, and the reection is sensed by a linear CCD (charged-couple device). Objects at dierent distances project reections at dierent positions of the linear sensor. This technique is similar to the focusing mechanism of point-and-shoot cameras and older range-nder cameras. While this technique has excellent near-range distance resolution, the performance (accuracy) degrades rapidly as the distance increases. This is because distant objects yield very small reection angular dierences, making them more dicult to distinguish using a digitized triangulation device. You can purchase the GP2D12 sensor from many distributors, http://www.digikey.com is one of them. Note that you need to order a relatively large number of units from these distributors to get a good price. At 25 quantities, Digikey wants about $8.50 for each sensor. This is a reasonable price. For more information about the sensor, refer to http://rocky.digikey.com/WebLib/Sharp/Web%20Data/gp2d12.pd
14.2
Interface
The GP2D12 sensor outputs an analog signal that maps to the location of the CCD device detecting the IR reection. This means that, in theory, this means that the voltage you measure is not the distance. You can get the distance using an equation. Refer to http://www.acroname.com/robotics/info/articles/irlinear/irli for a discussion of how to calibrate and compute the distance sensed by the sensor. To use an GP2D12 sensor, a microcontroller should have an analog-to-digital converter. We discussed this topic in the previous chapter. The specication of 101
102
GP2D12 also indicates that the voltage range of the output ranges from 0V to about 2.5V. This is perfect, because we can set up the ATMega128 to use its internal refernce voltage of 2.56V.
14.3
Important Notes
The GP2D12 sensor is easy to work with. It only requires 5V, ground, and outputs an analog voltage that can be converted to distance. However, you must understand some of the weaknesses of this sensor to utilize it optimally. Otherwise, you will be disappointed because it is not likely to live up to your expectations.
14.3.1
The sensor draws relatively big amount of current in pulses when it operates. The average is only about 33mA to 50mA (from the specications). This is not much. However, the instantaneous current consumption can reach 200mA (according to some unocal experiments) or even more. This instantaneous current will upset most voltage regulators if a design does not have provisions for it. Some controller will even reset because this much current will make the output of the regulator drop below its threshold to reset. Other controllers will have a ripple superimposed on VCC, which can then aect the eective resolution and accuracy of the ADC. To minimize the eect of this large current, the easiest solution is to put a large capacitor across the VCC and GND pins of the sensor. You need to put the capacitor as close to the sensor as possible to reduce the side eects. The capacitor value can range from 20F to 200F. It is also recommended that you use low-ESR (low equivalent series resistance) capacitors that are capable of large ripple current.
14.3.2
Debouncing
Due to an internal design aw, the GP2D12 sensor can, occasionally, give you spurious values even when the distance to object remains the same. This problem can compound on the current consumption problem. However, even with the power consumption problem xed (using capacitors), the internal design aw still make the sensor output spurious readings that are incorrect. You can x this problem by software debouncing. First, you need to experiment can nd out the range of these spurious errors. The magnitude of this error is the same across the entire range. Once you nd out, in terms of counts of the ADC value, then you can keep a circular queue of values, and throw away ones that are at least this much from its neighboring values. In other words, you can use the following algorithm to lter the buer (you need to wrap around correctly using the modulus operator, not shown in the following code):
14.3. IMPORTANT NOTES t0 while t < n do if (|vt1 vt | ethres ) (|vt+1 vt | vt ethres ) then vt1 vt vt+1 + 2 end if tt+1 end while
103
14.3.3
Sampling Frequency
The GP2D12 is not a continuous analog sensor. Instead, it measures every 38ms or so, but maintains the output of the previous measurement continuously. This means that if you measure more frequently than 38ms, every two values will be identical. Does this mean that you should not sample more frequently than every 38ms? To facilitate debouncing, you should read at least twice for each measurement. That means you probably want a sampling frequency of 80Hz or so. Although the average sampling period is 38ms, it can be as short as 28ms. Given that you want to read at least twice for each reading, you need to read every 14ms. A sampling frequency of 80Hz translates to 12.5ms between samples. Does it help to read at a higher frequency, like 200Hz? In terms of processor overhead, reading at 200Hz is still very easy. At a higher sampling rate, you can average out and get a more accurate result from within the same measurement. This is because the output of the device can uctuate even for the same measurement. However, there is also a noise between measurements. To remove the intermeasurement noise, increasing the sampling frequency will not help.
14.3.4
Inter-measurement Filtering
In order to get a higher resolution, especially for distant objects, you need to do inter-measurement averaging. This means you have to average the values measured over some multiples of 38ms. In theory, assuming inter-measurement noise is uniformly distributed, if you average over about 80ms, the resolution is improved by one bit. If you average over about 160ms, the resolution is improved by two bits. To determine the number of bits you need to resuce by averaging, you have to x the distance of an object, then see how the ADC values uctuate. Because the distance is xed, you should be getting the same values all the time. However, if you observe a uctuation of up to 8 counts even after debouncing, then you have an inter-measurement noise of 3 bits. To get you the best resolution, you need to average samples collected over 320ms (about 8 times 38ms). In the previous example, averaging over 320ms will not help because you are already bound by the limitations of the ADC. To get more information out of the sensor, youll need to use the dierential mode of the ADC, and provide
104
a programmable precise voltage reference source. In general, it is not worth doing. Note that resolution improvement by averaging only works when the distance to object does not change. If you are tracking a moving object (which is often the case for mobile robots), you cannot aord to average over 320ms. What you can do is to maintain a circular queue that is large enough for 30 to 40 samples. Depending on the distribution of the samples, you can determine what time frame to use for averaging. If all the samples are within 8 counts from each other, average over the entire circular queue. If the most recent 16 samples are very dierent from the rest, only average over the most recent 16 samples. In other words, from the most recent sample, work your way back in time until either you are the entire circular queue, or up to a point where the dierence is more than x counts from the most recent sample. This automatically trades responsiveness for accuracy.
14.3.5
All robots should know how much it can count on the sensors. In other words, if you already know that the noise level is up to 2 counts, you need to translate that to distance noise so that the robot knows that the actual distance (to an object) is between x and y , although the sensor indicates v . Because the noise level is linear to the voltage (ADC value), you need to go through some computations to gure out distance noise. You can use the same equations for computing distance from voltage.
Part VI
Communication
105
Chapter 15
General Communication
While the virtue of a robot is autonomy, it is often necessary for a robot to communicate with another computer, robot or human operator. This chapter discusses the various reasons for communication, as well as common devices to do so.
15.1
15.1.1
Reasons
Program update
Most robots are now controled by reprogrammable controllers. This means, in theory, the program running in a robot can be changed. This feature is very handy because it allows a development team rene the software of a robot without having to physically disassemble (most of) a robot.
15.1.2
Remote control
In the initial phase of developing a robot, the development team needs to debug low-level component, including power electronics and mechanical components. While a robot can be equipped with traditional remote control circuits and receivers, it can just as easily use more computer-oriented means of communication for remote control.
15.1.3
Debugging a robotic application is not as easy as debugging a desktop application program. The main reason is that events are asynchronous to the execution of the program. The other reason is that we cannot mount a monitor or keyboard to the embedded controller running on a robot. Even if this is possible (some robots use laptop computers as their controllers), it is dicult to read or type when a robot is in motion. 107
108
As a result, developers rely on remote debugging techniques. This means a debugger runs on a host machine, which is most likely a notebook computer (for portability) or a desktop in some cases. The debugger then relies on some means to communicate with a bare-bone monitor program on the robot controller. This way, a programmer can debug a program that is actually running on a robot using a computer that may not be physically connected to the robot. Although software-based debuggers are helpful, they are often not 100%. The reason is that when a program is being debugged using single stepping or stopped, the dynamics of the program changes. As a result, some bugs that are present when a program executes at full speed disappear when the program is being debugged. To facilitate software bugs that are sensitive to real-time, sometimes developers rely on logging. Since logging events tend to have very little impact on the overall execution speed of a program, most real-time sensitive bugs remain undisturbed by logging. The information contained in a log entry is exible, and is entirely up to the programmer to decide what needs to logged. It is possible for some controllers to keep the log on themselves until such time that the programmer wants to download it. However, on thin controllers, there may not be sucient storage to log events. As a result, the log is transmitted, at real-time, to another computer that receives the information and write everything to a le. This le can be viewed at real-time, or it can be examined manually or with the help of lter programs after the fact.
15.1.4
Environmental Interaction
In some environments, a robot may need to communicate with other devices. For example, a vacuum robot may want to communicate with occupancy sensors already installed in a house so it does not enter rooms that are currently in use. In other environments, beacons can be set up at known locations. When a robot gets close enough to a beacon, the robot can communicate with the beacons to get the ID of the beacon, along with the current time (according to the beacon) and other data. At the same time, the robot can also upload information to the beacon. Such information may include states of the robot, the ID of the robot, images it has captured and etc. The beacon approach is particularly useful in indoor environments where an existing wired network is already established. Not only does it help a robot conrm its location, it also helps to eliminate the need for a robot to have full time connectivity to the network.
15.1.5
Inter-robot Interaction
Robots can also communicate with each other. This is particularly important when robots need to coordinate with each other to accomplish a common goal. Robot soccer is a robot sport that requires the coordination of several robots in order to score.
15.2. DEVICES
109
In emergency handling, rescue robots also need to coordinate with each other to handle a crisis. Otherwise, a robot may block the exit of another robot, or robots may even collide by themselves.
15.2
15.2.1
Devices
RS-232, asynchronous
The EIA-232 standard species voltage levels for 1 and 0. It is the asynchronous protocol that species timing of the 1s and 0s for the transmission and reception of data. However, most people associate RS-232 with the asynchronous protocol. In this section, we will use this more popular denition of RS-232 communication. The nice part of RS-232 is that it is widely available on notebook computers and desktop computers. Many controllers also have the hardware to handle RS-232 communication. Even with modern sub-notebook computers without any built-in RS-232 ports, they can be added inexpensively using USB-serial converters. The maximum speed (bandwidth) of RS-232 is 115200bps, although some devices are capable of up to 250000bps. By todays standard, this bandwidth is not impressive. 250kbps translates to about 30kB/s because one byte takes 10 bits to transmit (including the start and top bits). A major drawback of RS-232 is that it relies on the absolute voltage of signals. This can be easily disturbed because of a long cable, or interference from electro-magnetic devices (such as motors). As a result, RS-232 usually only has a recommended range of 12 feet or so. RS-232 is also an end-to-end protocol. This means an RS-232 port on a notebook computer can only talk to one controller. While this may not appear to be a signicant limitation for usual applications, it is somewhat limiting for robotics. It is not uncommon that a relatively complex robot have several controllers.
15.2.2
RS-485
The EIA-485 protocol species a dierential signal instead of an absolute (RS232). This dierence makes it possible for RS-485 to have a much greater range (up to 1 mile) because it is not senstive to ground dierences. Using twisted wires, RS-485 is also insensitive to usual magnetic interference. RS-485 species a half-duplex device, whereas RS-422 species two RS-485 channels for a full-duplex device. In reality, RS-485 is often sucient because many communication protocols are half-duplex, hence not requiring the fullduplex capability of RS-422. The bandwidth of RS-485 depends on many environmental factors. The length of the cable, for example, has a large impact on the available bandwidth.
110
For short distance (12 feet or so), RS-485 has the same bandwidth as RS-232. For up to a mile, RS-485 drops down to 2400bps to 4800bps. One major drawback of RS-485 is that it is seldom available on computers. Some companies make expansion cards or converters, but they are usually quite expensive. RS-485 is suitable for lower-bandwidth communication within a large robot.
15.2.3
Ethernet
More and more often, embedded controllers are now equipped with ethernet connectors and drivers. As we all know, ethernet has a minimum bandwidth of 10Mbps. Ethernet is extremely suitable for dry-dock development work due to the high speed. In fact, for embedded controllers that has operating systems, a developer can even telnet into the controller and debug programs on the controller itself. Because of the higher bandwidth, a developer can also quickly update the OS on an embedded controller over ethernet. Unlike RS-485, which only requires two twisted wires, ethernet requires a thicker cable. This means that ethernet is not suitable when a robot is in motion.
15.2.4
Wi
For embedded controllers with a PCMCIA, Compact Flash or PCI slot, we can use Wi (wireless delity) for wireless communication. Even the older standard, IEEE 802.11b, has a maximum bandwidth of 11Mbps. Of course, to utilize a Wi expansion card, we also need to run a fairly moderm operating system with proper drivers. Wi oers many advantages over the previous means. For example, it is wireless. This means there is no tangling of wires even when the robot is in motion. Wi is also omnidirectional. There is no need to point a sensor/transmitter to the robot. To extend the range of wi, however, it is possible to design antennae that are highly directional to get a better range. But then it is necessary to point the antennae to the direction of the robot. Wi also has enough bandwidth for live on-target debugging and even development. This simplies the set up of the tool chain. Most operating systems that can handle wi also have full TCP/IP stacks, which means the robot can be its own web-server, database server and etc. The main disadvantage of Wi is that it requires a bit of resources. Typically, this is only useful if a controller is based on the mini-ITX, PC104 or other embedded PC form factor. Furthermore, it also requires a fairly modern operating system (such as Linux, WinCE and etc.) to support the device.
15.2. DEVICES
111
15.2.5
Bluetooth
Lately, there is a wireless solution for thin controllers that cannot utilize Wi. Bluetooth is a relatively low-end wireless standard for appliance devices such as cell phones. There are converters that converts RS-232 to/from bluetooth. These devices are not inexpensive, but they are easy to use. Bluetooth also consumes less power compared to Wi. Besides the lower bandwidth (57600bps) of bluetooth, it is also less secure than Wi because there is no protocol level encryption. It is, typically, not a major problem for development work.
112
Chapter 16
The adjective asynchronous means that the transmitter and receiver are not synchronized by a common clock signal. Instead, the transmitter and receiver agrees on a common bit rate for information transmission. Common bit rates are 1200bps, 2400bps, 4800bps, 9600bps, 14400bps, 19200bps, 28800bps, 38400bps, 57600bps and 115200bps. While nothing is being transmitted, an asynchronous communication signal idles at a logical-high state. Logical high state means that from the CMOS/TTL point of view, the voltage is high (typically 3.3V or 5V). When a byte is to be transmitted, the following happens:
1+ start bit up to 8 data bits an optional parity bit 1+ stop bit
16.1.1
Start Bit(s)
Each word begins with at least one start bit. A start bit is a logical low signal for the duration of one bit. In other words, for 19200bps, the signal is low for 52s. The main purpose of a start bit is to indicate the beginning of a byte. Without a start bit (that has the opposite polarity of the idle state), it is impossible to indicate when the transmission of a byte begins. 113
114
16.1.2
Data Bits
Data bits are bits to be received. A bit value of 1 is transmitted as logical high, while a bit value of 0 is transmitted as logical low. Most devices handle up to 8 data bits. Within a byte, data bits are transmitted LSb (least signicant bit) rst.
16.1.3
Parity Bit
Older devices may require the use of parity bits. There are two options. An even parity bit is 0 if and only if there is an even number of 1s in the data bits, where as an odd parity bit is 0 if and only if there is an odd number of 1s in the data bits. For example, the 8-bit value 0x56 has an even parity bit of 0 and an odd parity bit of 1. Note that a parity bit is optional.
16.1.4
Stop Bit
A stop bit is driving the signal logical high for the duration of one bit. It marks the end of a byte (or smaller packet). The minimum number of stop bit(s) can vary from 1 to 2.
16.2
The USART
On the ATMega128, there are two USARTs. USART stands for Universal Synchronous/Asynchronous Receiver Transmitter. It is a hardware device on the MCU silicon die that can both transmit and receive information. For now, well focus on the asynchronous part of this device. Note that the ATMega128 is a fairly modern and complicated MCU. As such, the USART device is highly congurable, which also leads to the complexity of getting the correct bit values right. There are two identical USARTs on the ATMega128. As a result, all I/O locations must be postxed by a 0 or 1 to indicate which USART. In our notation, n indicates where either 0 or 1 should be specied.
16.2.1
UMSEL in UCSRnC should be 0 to select asynchronous operation. U2X (bit 1) of UCSRnA selects between normal (0) or double speed (1) asynchronous operations. Double speed is not free, as it sacrices some of the USARTs ability to resynchronize transmissions that are not accurate in receive mode, and it can also lead to outgoing (transmitter) error for recipients that are not sampling quickly enough.
115
UBRR (UBRRnL and UBRRnH) needs to be initialized to the correct period (of a bit), refer to page 172 of the datasheet for an equation to compute the value of UBRR from the desired bit rate
16.2.2
Frame Format
The rst 8 bits are specied by UDRn both for reception and transmission. For transmission, bit 8 is specied by TXB8 (bit 0 of UCSRnB . For reception, bit 8 is stored in RXB8 (bit 1 of UCSRB).
parity bits: this can be none (no bit used), even or odd It is controlled by UPM (bits 0 to 1) of UCSRnB.
Table 78 on page 190 of the data sheet explains the possible modes.
stop bit(s): this can range from 1 to 2. It is controlled by bit USBS in UCSRnC.
16.2.3
On page 185 of the datasheet, Atmel explains the multiprocessor communication mode. Here is a simplied version. The MPCM (multi-processor commmunication mode) bit of UCSRna is used to enable (i set to 1) this mode of operation. The MPCM is a useful feature in a multidrop network using the asynchronous protocol. Multidrop in this context means multiple network nodes all listen to the same physical signal. In such a network, it is common that a networking protocol (above physical level, in the link level) uses a byte to identify the destination (recipient) address. This byte is normally followed by the payload of a message. Without MPCM, a node must receive and interpret every byte transmitted on the network, even though a message may not be directed to this node. This is because if a node does not interpret the entire message, it does not know when the message ends, and as a result it does not know when the next message (which may be directed to this node) begins. MPCM utilizes a ninth bit to aleviate the average processing load of nodes on a multidrop network. If and only if the ninth bit is a one, the transmitted byte (the rst eight bits) is an address. As a result, if the ninth bit is a zero, the payload is some data byte of a message.
116
This scheme helps to reduce the average processing requirements. Each receiving node only needs to listen for bytes with a one for the ninth bit. If the address does not match the nodes own address, then the following data bytes can be ignored. Otherwise, a node needs to switch mode and receive/interpret all following data bytes.
16.2.4
Errors
Page 181 describes the possible error ags set by the USART.
FE (frame error): essentially, when an expected stop bit is a zero instead of a one, this bit is set. Note that it does not catch all possible cases. DOR (data overrun): this happens when the MCU cannot remove data from UDR fast enough. UDR can buer one byte when the following is still begin received. However, when the following one is received, it overwrites UDR so the previous byte is lost if UDR is not read. UPE (parity error): when the parity bit of a byte does not match the payload, this error ag is set.
16.2.5
Interrupts
The USART is designed so that the processor only needs to pay attention when something happens. The sources of interrupts are as follows:
Reception complete: this is enabled by RXCIE (bit 7) of UCSRnB. An interrupt is generated when RXCIE and RXC (bit 7 of UCSRnA) are both set. This is cleared by reading UDR. Transmission complete: this is enabled by TXCIE (bit 6) of UCSRnB. An interrupt is generated when TXCIE and TXC (bit 6 of UCSRnA) are both set. The interrupt request is cleared when the corresponding ISR executes. When TXC is set, it means the USART is done with transmitting everything asked of it (and that the UDR for the transmitter is empty). This interrupt is generally useful to indicate that a packet is fully transmitted. The corresponding ISR typically needs to turn around the bus, or at least turn o the tranmitter hardware in a multidrop network. UDR empty: this is enabled by UDRIE (bit 5) of UCSRnB. And interrupt is generated when UDRIE and UCRE (bit 5 of UCSRnA) are both set. When UCRE is set, it only means that the transmitter is ready to accept another byte in UDR. It is possible that the previous byte is still being transmitted out of the USART. This interrupt is generally useful to keep lling the UDR so that the transmission of data is continuous and uninterrupted. The corresponding ISR typically gets the next byte of the current message to transmit. When there is nothing else to transmit. UDRIE should be cleared because there is no way to clear UDRE.
117
16.2.6
RXEN (bit 4) and TXEN (bit 3) of UCSRnB should be set if and only if the receiver and transmitter needs to be enabled. When the receiver or transmitter are disabled, the corresponding pins return to act like general purpose I/O pins. Note that the receiver and transmitter can be enabled independently from each other. It should be noted that when the transmitter is disabled, it still clocks out any remaining bits not yet transmitted. This means it is safe to disable the transmitter as soon as the last byte of a message is written to UDR. In contrast, disabling the receiver stops reception instantaneously. The value of UDR (for reading) as well as the error ags become invalid as soon as a receiver is disabled.
16.3
Most UARTs or USARTs on inexpensive MCUs only has a one byte buer. This means that it can only store on byte while another byte is arriving. As soon as the other (new) byte is received, the buered byte is overwritten. For output purposes, an inexpensive UART has a buer to hold a byte, while the shift register shifts out another byte. Without any additional buering, this means that an application must be able to read and process a received byte within the transmission time of a byte. This may sound ver reasonable. Afterall, even at 57600bps, the actual transmission rate is about 5kB/s, or 200s between each byte. In reality, there may be important matters for the processor to attend to, rather than interpreting a received byte. For example, in a robot, it is more important to avoid collision with an object than to reply to a query from an end user. As a result, it is common to separate the read operation from the process/interpret operation. We want to read a byte from the UART before it is overwritten by the following byte, but we may want to delay the processing of this byte until we are cleared from a collision threat. In order to do this, we need to implement a buer in software to store the received-but-not-processed bytes. This section describes the concept of a circular queue (in pseudocode).
16.3.1
A queue is a British term for a line of persons waiting for something. It is also the ocial term for any data structure that has the rst-in-rst-out characteristics. Let us consider the example of a carousel in-slot that is responsible for taking all incoming work to be done at a workers desk. For our discussion, let us assume the carousel has 8 slots to handle up to 16 buered incoming work requests.
118
The worker uses a post-it to indicate the next to process. As the worker removes an incoming request from a slot, the post-it is moved clockwise by one slot. There is also a post-it to indicate next request. Anyone who wishes to place a work order must put the work request at the next request post-it, then move this post-it clockwise by one slot. Figure 16.1 illustrates an initial conguration where there are two buered requests (to be processed). Note how the next to process post-it is tagging the rst lled (gray) slot clockwise, and how the next request post-it is tagging the rst unlled (white) slot clockwise.
next request
next to process
Figure 16.1: Initial conguration with two requests. After we process a request, we move the post-it to the next request (clockwise). Figure 16.2 illustrates the carousel after one request is processed.
next request
next to process
Figure 16.2: One request is removed and processed.
119
If the worker process the only remaining request before any new requests come in, it becomes the one illustrated in gure 16.3. Note that in this conguration, both the next to process and next request post-its are on the same slot. This is how the worker knows there is no request to be processed at this point.
Figure 16.3: No request is remaining. Lets say lunch hour is over, and suddenly many requests come in at about the same time. More specically, let us assume eight requests are received at about the same time before the worker can remove any one for processing. Our carousel is now lled. The next request is moved clockwise eight times. Figure 16.4 illustrates our 100% lled carousel.
Figure 16.4: Eight requests are received. Figure 16.4 also demonstrates how we know there is no more room for one more request. When the next request post-it is at the same slot as the next to process post-it, our carousel is lled up.
120
This also means it is not sucient to just keep two post-its. We need to actually count the number of lled slots.
16.3.2
The carousel is our array! Imagine that we assign indices to each slot in the carousel. Figure 16.5 illustrates the numbering of the slots.
7 6 next request
0 1
5 4 3 next to process
Figure 16.5: Carousel as an array, numbered slots. This means instead of using a post-it to tag a slot, we can use two indices (integers) to indicate which slot is to be processed, and which slot should receive the next request. That is, next to process and next request become two variables of integer type.
16.3.3
In order to represent a carousel, we can use the following record representation, after we rename carousel to its formal name, circular queue, next request to tail and use count to count the number of lled slots: record circular queue a : array of 8 slot head : integer count : integer end record Note that we did not say specically what a slot is. In other words, slot can be a simple type like integer, or it can a complex record or array all by itself. As far as the logic of a circular queue is concerned, the type of a slot is not important.
121
16.3.4
You can perform several operations for a circular queue. For example, we can initialize it, check if it is empty, if it is full, to remove the item at the head and to insert an item at the tail. We will write some subroutines to handle each operation. Initialize an Empty Circular Queue The following subroutine initializes a circular queue c. dene subroutine initialize circular queue given and provide c : circular queue c.head 0 c.count 0 Check for Empty As discussed earlier, checking for an empty circular queue only needs to check if the head and tail are the same. The following is the subroutine to do so: dene subroutine is circular queue empty given c : circular queue provide isempty : boolean isempty (c.count = 0) This subroutine is rather simple. To invoke this subroutine to check if a particular circular queue x is empty, we use the following pseudocode: invoke is circular queue empty x c, result isempty if result then print the circular queue is empty else print the circular queue is non-empty end if Check for Full The logic to check for a full circular queue is similar to the one to check for empty. We only need to compare the count eld to 8 instead of 0. Add to Tail The algorithm to add to the tail slot is as follows: dene subroutine add to circular queue tail given and provide c : circular queue given s : slot provide result : boolean invoke is circular queue full c c, result result result result
122
if result then c.a(c.head+c.count) mod 8 s c.count = c.count + 1 end if The invoke statement may look a little confusing. This is because the subroutine being called, is circular queue full, also has formal parameters c and result. The negation of result is necessary because add to circular queue tail provides a result of true if and only if it successfully add to the tail of a queue. If result is true from the invoke statement, it means the queue is already full and therefore the add operation fails. After the negation of result, result is true if and only if there is room to add to the circular queue. The statement c.a(c.head+c.count) mod 8 s also deserves a little explanation. c.head is the index of the head of the queue. c.head + c.count is the index of the tail (next available slot). However, this sum can exceed 8 (e.g., when c.head = 7 and c.count = 3). (c.head + c.count) mod 8 ensures the number is between 0 and 7. This number is then used as the index for the array a (as a eld) of a circular queue record c. The indexed element of this array is nally used as the destination so we can copy s (the intended contents of a slot) to a slot in the circular queue.
Remove from Head The algorithm to remove contents from the head of a circular queue is as follows: dene subroutine remove from circular queue head given and provide c : circular queue provide s : slot provide result : boolean invoke is circular queue empty c c, result isempty result result if result then s c.ac.head c.head (c.head + 1) mod 8 c.count c.count 1 end if This subroutine is similar to add to circular queue tail. result is a formal parameter to indicate whether a slot was successfully removed from the head of the provided circular queue. s is a formal parameter to contain the contents of the removed slot. The only part that requires explanation is c.head (c.head + 1) mod 8. After we remove one slot from the head, the head must advance to the next slot in the carousel. However, we may be advancing from index 7 to index 0. This is why we need to use the modulus operator to ensure the resulting number (the next head index) is still between 0 and 7 inclusively.
123
16.4
Circular queues are very common data structures, there is no need to reinvent the wheel. This section describes what I have already implemented.
16.4.1
cq.h
cq.h is the header le of circular queues. It describes everything that an application programmer needs to know about a circular queue. \ i n p u t { . . / s a m p le s / cq . h } Let us examine the three functions that you need to access a circular queue. void cqInitialize(struct Cq *p, void *, unsigned size); cqInitialize is used to initialize a circular queue. The rst parameter p is a pointer to a struct Cq. The second parameter (no name in the prototype) is a pointer to a chunk of memory for actually storing bytes. This is usually a piece of memory that is statically allocated. The third parameter, size, is an unsigned integer that indicates the number of bytes available in the second parameter. This subroutine does not return any value. int cqPutByte(struct Cq *p, char byte); cqPutByte is used to add/insert a byte into an initialized circular queue. The rst parameter, p, is a pointer to the circular queue (to which a byte will be added). The second parameter, byte, is the byte value to put into the circular queue. This subroutine returns 0 if and only if there is no room left in the circular queue to store the byte. int cqGetByte(struct Cq *p); cqGetByte is used to remove/retrieve a byte from a circular queue. The only parameter, p, is a pointer to the circular queue from which a byte will be removed. The subroutine returns a value from 0 to 255 if the retrieval is successful. It returns a negative value if and only if there is nothing in the circular queue to remove.
16.4.2
Given that we have a circular queue structure, we can now dene the logic to handle various interrupts generated by the UART. When the receive buer has a received byte, the following should be done: b U DR if cqPutByte(uart rcq, b) == 1 then were done! else
124
ag software buer overrun error end if When the transmit buer UDR is empty, the following should be done: i = cqGetByte(uart_tcq) if i < 0 then turn o the UART transmitter else UDR i end if When transmission is completed, the following should be done: disable transmitter driver
16.4.3
UART API
We now dene how the rest of the system sees the UART. First, we need to dene a subroutine to add a byte to the transmitter circular queue: save global interrupt enable disable global interrupt given b to add to the queue if uartt cq has space then call cqPutByte(uart_tcq,b) enable transmitter driver enable UART transmitter enable UART transmitter interrupts restore global interrupt enable else restore global insterrupt enable, return error, there is no space left end if The subroutine to receive a byte is simpler: save global interrupt enable disble global interrupt i = cqGetByte(uart_rcq) restore global interrupt enable, return i Note that this is a very simple API to use an UART. It lacks certain nice features. For example, if the transmit circular queue is full, the requesting code must try to transmit later. Likewise, if the receive circular queue is empty, the reading logic has to attempt to read later. This easily leads to polling code, which is inecient and makes program logic more dicult to understand.
Part VII
Robot Behavior
125
Chapter 17
Threading
This chapter discusses multithreading, a concept more commonly found in larger operating systems.
17.1
To understand why multithreading is helpful, we rst have to understand what it was like without. Most robots require parallel algorithmic logic of some kind. For example, a rescue robot needs to handle the following tasks in parallel:
Maintain communication with the rescue command. Monitor its own vital status (temperature, battery voltage, etc.). Process information from sensors. Control motion to get to a certain destination. Plan the most eective course to handle the current crisis.
Without multithreading, we need to implement each parallel task as a state machine. A state machine is often coded as follows: struct SomeSM { unsigned s t a t e ; // s t a t e machine p r i v a t e p r o p e r t i e s }; void SomeSMTick ( struct SomeSM * p ) { switch ( p> s t a t e ) { 127
128
CHAPTER 17. THREADING case STATE1 : // . . . p> s t a t e = STATE2 ; // change s t a t e // . . . break ; case STATE2 : // . . . break ; // more s t a t e s
} }
Part VIII
Project
129
Chapter 18
Project
This is your nal project.
18.1
Robot Objectives
The rst section of your project states the objectives of your robot. Use examples to illustrate what your robot is supposed to do. For example, the following describes what a re ghting robot does:
patrol the interior of buildings identify potential res notify the re department put out the re with internal extinquishers
18.2
Robot Specications
In this section, esh out more details, in technical terms. In our example, the specications may look like the following:
to patrol:
optimal speed to be about the same as fast pace walking speed, just 4 miles/hour. obstacle avoidance to avoid injuring people or causing property damage. robot base to have about the same width as a person, about 24 inches.
to identify res:
on-robot siren and ashing lights radio transmission via wireless/cellular technology to 911
to extinguish re:
elevating extinguisher head with tilt and swivel control. sensors on the extinguisher head for aiming, logging and remote control purposes. equipped with usual re extinguisher canisters.
self maintenance:
low battery detection. automatic recharge mechanisms. dual battery for hot swapping. self diagnosing on critical components
user friendliness:
LCD screen and keypad for on-robot conguration. wireless communication for on-the-run conguration, logging or debugging. total mass not to exceed 40 pounds.
safety:
optional remote kill switch optional remote control unit automatic shutdown if it fails self diagnostics. audio/visual alarm if it fails self diagnostics. radio/audio/visual beacon.
18.3
Design
In this section, describe how the robot is designed. This can get very long, tedious. I am looking for analyses and explanations. Why do you choose a particular design/component? How does the chosen one compare to alternatives? What are you selection criteria? In our example, the design section may look like the following:
18.3. DESIGN
133
18.3.1
Drive Platform
choose to use auto windshield wiper motors only requires 12V, commonly available from car batteries quiet and plenty of torque not very ecient, but since we can recharge, it is not a major issue inexpensive and reliable, built for continuous operation easy to mount wheels
motors:
wheels:
small 8 to 10 wheels designed for hand trucks or go-karts pneumatic tires for traction and bump absorbing. commonly available
Fire Extinguisher Subsystem Sensors Communication Subsystem Battery and Power Subsystem Control Units
This section is particular important for our class, since software runs on the control units.
main controller
responsible for coordination, communication choose Mini-ITX based controllers include cooling (thermo-electric combined with fans) less expensive than PC104s powerful enough to run Linux use compact ash as solid state le system use USB-blue tooth for wireless communication use USB-cell phone for communication with re department use USB-serial to communication with other controllers
motion controller
134
CHAPTER 18. PROJECT communicates using serial protocol, RS-485 accepts high level commands monitors motor temperature and current consumption monitors output voltage of batteries
sensor controller
Part IX
135
Chapter 19
Design Requirements
Physical
up to 10cm by 10cm (for mini-sumo rules) up to 1.1 pounds, including all components
19.1.2
I/O
able to drive 2 bi-polar driven stepper motors able to drive 2 DC motors able to control 2 R/C servos 2 quadrature encoding input 2 to 3 ADC input for GP2D12 sensors 2 to 3 phototransistor sensor ports programming port LEDs DIP switch battery connector on/o switch push-buttons RS-232 interface
137
138
19.1.3
gdb remote serial protocol bootloader supports gdb remote serial protocol bootloader to support the following modes:
run: always run application program do not enter program mode program: always enter program mode, do not enter run mode auto: after resets, listen for RSP trac for 10 seconds (make this programmable?). If there is no RSP trac in 10 seconds, enter run mode. Otherwise, enter program mode.
19.2
19.2.1
Components
Pin count
2 L293Ds, one with PWM, the other one without, 10 pins (alternative to above, added 20040307) 4 mc33186 (alternative to above, added 20040307) 4 tpic0108 3 ADC channels, 3 pins 2 quadrature inputs, 4 pins RS-232, Rx/Tx, 2 pins RS-485, Rx/Tx and enable, 3 pins (added 20040307) run/program mode, two pins (added 20040307) programming port, clk, in, out, 3 pins 3 phototransistor ports, 3 pins 2 LEDs, 2 pins 4 position DIP switch, 4 pins 2 push buttons, 2 pins
A total of 33 I/O pins are needed. This rules out the DIP packages. For exibility, might as well use the ATMega128.
19.2. COMPONENTS
139
19.2.2
Component Selection
1x ATMega128, TQFP version 2x 0.1uF decouping cap for ATMega128 1x 10uH inductor for ATMega128 1x 16MHz ceramic oscilator 2x Ti 754410 (equiv. to L293D) 1x 5V LDO regulator 1x jumper to select whether to use regulator reset or not 1x push button to reset board manually 2x software readable push buttons 1x RS-232 switched cap level convertor 4x 1uF cap for RS-232 level convertor 1x RS-485 driver chip misc. resistors for RS-485 termination 1x DE9 connector for RS-232 1x resistor pack for IrED current limit 1x resistor pack for phototansistor pull-up 1x resistor pack for quadrature encoder IrED current limit 2x LED (dierent colors) 1x LED power indicator 1x 6-pin programming header 2x 3-pin servo connectors 2x 2-pin DC motor connectors 2x 4-pin bipolar stepper motor connectors 2x 6-pin quadrature encoder connectors 3x 3-pin GP2D12 connectors 3x 20uF cap for GP2D12 connectors 3x 1uF cap for GP2D12 connectors
140
19.2.3
19.3
19.3.1
The bootloader is to be loaded with a monitor capable of handling the gdb remote serial protocol (rsp). RSP should listen to an UART connected to RS485 for long-range noise-tolerant communication. Due to the small page size of the ATMega128, we should be able to debug a program via this interface. A special hardware circuit is used to detect the break signal. A break signal longer than a threshold is used to reset the board. This feature is jumper selected so that we can disable this resetting for an application. This special hardware can be done with a small signal transistor, an RC circuit and a comparator (or schmidt trigger) and a diode (so that reset is always sunk and never driven). The use of a bootloader also means that the vector table is relocated. Special linker scripts must be used instead of the default avr-gcc and avr-binutils ones.
19.3.2
H-Bridges
The new surface mount H-bridges are ecient, but they require bottom soldering to pads with vias for optimal heat dissipation. This poses a problem with hand soldering because it is generally speaking dicult to do this without specialized tools. The motorola TSOP package is the best option because at least there is some exposed area from the bottom heat sink plate for external hand soldering. The package is also relatively small (15mm by 16mm). This part is capable of handling 5A continuous! Due to the high current ability and requirement of a large heat sink area, it may not be the best option to put these H-bridges on the main board. A seperate board with separate connectors for power and control signals makes a design more versatile. An alternative is to use discrete transistors (high eciency N-MOSFET) and a switch cap voltage doubler. This approach makes the design more exible in terms of component selection, but it is somewhat expensive in terms of soldering. In order to limit the gate voltage, it may be necessary to use a zener diode that connects from gate to ground. A pull-up resistor than connects from gate to the doubled voltage.
141
Update 20040319: found a new way to use the TPIC0108. use a large via on the underside, and solder the heatsink to the via from the underside. Update 20040324: assigned pins from PAL to the H-bridges. The assignment is mostly based on routing needs.
19.3.3
(20040322) RS232
A usual MAX3221 chip will be used for RS-232 PC Interface. It requires four 0.1uF caps for the voltage doublers, but otherwise a easy chip to use. Will use a SSOP RS-232 transceiver chip that only needs four 0.1uF caps. This chip is tiny, doesnt take up too much board space. The land pattern is entered in EAGLE. Update (20040324): Decide to use TXD0/RXD0 for the RS-232 port. The rationale is that the TXD1/RXD1 are external interrupt capable. To resolve the conict between the RS-232 driver and the in-system programming port, we can use the negation of the reset line to disable the RS-232 chip. We can also use the PAL to select one of the two sources to drive the pin: ISP or RS-232 ROUT. Havent decided yet. Another reason to preserve TX1/RX1 is that other external interrupt lines dual-purpose with output compare. For maximum exibility, we need four output compare (for software chopper drive). As a result, I want to use TX1/RX1 for external interrupt purposes.
19.4
19.4.1
Yes, the whole break is over, and I am not done with the design yet (sigh!). On the other hand, its fortunate that I am not done with it yet. Read on. The major hang up was the routing of the pulse IrED circuit. It is still not completely done, but the remaining portions should be relatively easy. Still need to add the AD5245 digital potentiometer to the library. I also found out yesterday that Ill need to include a comparator on the PCB. Most quadrature encoders output real output from the phototransistors. This means well see a smooth and continuous voltage swing that is not friendly to CMOS inputs. If the extremes of the swing are close enough to 5V and 0V, we can easily use a schmidt trigger so the signal doesnt bounce. However, this is not always possible. The reason is that black is not necessarily black enough. The density of toner from a normal laser printer is not dense enough in the IR frequency. As a result, enough IR leaks through the opague portions, making the so-called o current not really close to no current. As a result, comparators must be used, combined with a variable voltage divider bridge, to generate the correct output. The LMV339 (a low voltage variant of the LM339) is one option. It is inexpensive at less than $1 each
142
(at single quantity). The datasheet from National Semi detailed how to add hysteresis. A single chip is enough for two quadrature encoders, although we will need a number of resistors (3 per comparator, yielding a total of 12 for the whole set up). The basic circuit is illustrated as follows:
R2 R5
VCC
R1
3 12
IC1A
2
IN
4
R3
LM339D
R4
R3 R3 +
1 1/R2+1/R5
GND
Heres how it works. When the output of the comparator is high, the voltage at the non-inverting terminal is as follows:
Vh = VCC
(19.1)
When the output of the comparator is low, the voltage at the non-inverting terminal is as follows:
1 1/R3+1/R5 1 R2 + 1/R3+1 /R5
Vl = VCC
(19.2)
This means the input voltage must drop below Vl for the output to switch to high, and the input voltage must rise above Vh for the output to switch to low. This logic appears to be inverted, hence the name inverted. Note that the pull-up resistor, R1, is necessary because the comparator has an open-collector type output. Without a pull-up resistor, the device cannot drive the output high (pin 2 of IC1A). R4, the load resistor, is not actually there. It is merely a symbol that renotes the impedence of the load (in our case, it is in the range of mega Ohms).
143
19.4.2
20040406
Just realize that we dont need a small signal N-MOSFET. The 4051 tri-states the outputs when enable is negated. This means that a simple pull-down resistor (10kOhm or so) on each power MOSFET should disable the output! Because the opamp has an output capacity that matches the input of the mux (about 20mA at 5V), we can use a relatively small current limiting resistor (220Ohm or so). For cost and soldering considerations, a Darlington array (such as the ULQ2003 or ULN2801) may be used. The main tradeo is eciency, as a Darlington device has close to 2V of drop at 500mA. On the other hand, a single SO18 package is much smaller than four SO8 packages. A tiny transistor is NDS331N (or NDS351AN). It is a SOT-23 3 pin package that is acapable of up to 10A pulsed (at a very low duty cycle). This part is also very cheap. Compared to the SOT-6 package, it is easier to solder because of more space between the pins. The NDS355AN has the same package, but it has a specication of 1.7A continuous and a lower on resistance. The price is $0.48 each, and $0.27 at 100 quantities. This is probably the best part to use for driving the IrEDs.
19.4.3
20040404
Yet another revision. Instead of using a negative supply, I am switching back to use a single rail opamp for current control. This is because I already have a way to shut down the output completely. The Burr-Brown OPA340 or Microchip MCP601T opamp is tiny (SOT23), and it is rail-to-rail. Combined with the IR7301 (SO8 for two N-MOSFETs), the IrED pulsing circuit has the following components:
an AD5245 for current selection an OPA320 for current feedback and gate control a 2.2kOhm resistor from the output of the opamp to limit control current an 2N7002 sot-23 N-MOSFET to disable the drive voltage a 4051 to mux the gate control voltage a 10kOhm resistor network to pull power FET gates to ground a common 0.5Ohm or 1Ohm current sense resistor four IR7301 power FETs
144
19.4.4
20040331
Need to consider adding a negative supply to the board. This is necessary for the op-amp for current control. BB1 had a similar circuit based on a single rail op-amp, but the result is somewhat mixed due to the near-rail (0V) nature. A negative supply removes the requirement of the op-amp to operate near rail, hence making it possible to use less expensive non R2R op-amps such as LM324s. The Maxim1044 SO8 (also known as ICL7660) chip seems to be a good choice. Although not as cheap as some other switched capacitor charge pumps, this one has a very high eciency. Combined with a digital pot, the board can adjust the reference voltage applied to the non-negated input of the opamp to set the current. To turn o the device, we can simply ground the non-negated input using one I/O pin from the MCU. We can also ground the output of the opamp (via a resistor of about 5kOhm) to ground so that the MOSFET is disabled. In order to make sure that the MOSFET has a safe reset state, however, it is better to drive the input to the MOSFET through another small signal MOSFET. The small signal MOSFET is enabled by an output of the MCU that has an external pullup resistor. This way, the small signal MOSFET has a reset state of on, which turns o the MOSFET to be controled by the circuit. A good digital pot to use is the AD5160 from Analog Devices. This one has the A-B terminals not tied to power and ground. Unfortunately, it uses an SPI interface (instead of a multidrop I2C bus). The AD5245 is is similar device using I2C, which makes it more exible. I am inclining to use the AD5245 instead of the AD5160. (20040402) bad news. The AD5245 cannot handle negative voltage relative to ground, we need to switch to a DS1666. This part has a -ve bias so the low terminal can go below 0V. In order to have good control over the reference voltage, We should use the series of 20k-5k(pot)-24k from postive supply to negative supply. This ensures that we have most of the 256 divisions of the digital pot working over the useful range of reference voltage. A 24kOhm 0805 resistor with 0.5% precision is about 15 cents each (RR12P24.0KDCT from Digikey). Once the pulse current control circuit is set up, we need work on a multiplexer that directs the pulse current to dierent series of IrEDs. To make things easier, a device like SN74CBT3251 (1-of-8 demux FET) can be used. However, this type of device is relatively inecient. The on-resistance is in the range of 20 Ohms or so. The CD4051 can also be used to mux the drive signal (after the current limiting resistor from the op-amp) to 8 individual MOSFETs.
19.4.5
20040322
bb2-20040322.pdf illustrates the current board layout. The H-bridge layout is almost done, except that I have not put in the top ground plate for heatsinking purposes. This layout is chainable so that I can add additional H-bridges next to
145
the current one easily, while preserving excellent ground and Vdd connectivity.
19.5
Drive System
C&H Sales has a $60 motor geared down with encoder. Stock# DCGME2201. Output shaft is 12mm by 7/8 inch, 3mm cross drilled hole. Encoder has 500 steps per revolution. C&H Sales #DCGM2103PR L/R pair of wheelchair motors, 17mm diameter shaft tapped for 8mm by 1.25mm screw, 6mm wide key slot #DCGM2003 12VDC, shaft is 3/4 inch diagmeter, standard 3/16 inch straight keyway. $140 each. Tank tread set at robotmarketplace.com, $180/set. Drive sprocket has a 3/4 inch inner diameter. NPC-PH804 from robotmarketplace.com is a hub to t 3/4 inch bore with 3/16 inch keyway. 3/16 inch key stock is also available from robotmarketplace.com. Hub with 3/4 inch inner diameter and 2.75 inches bolt diameter is available at robotmarketplace.com (NPC-PH804).
146
Chapter 20
A.N.T.
ANT (autonomously navigated transportation) is an experimental/vapor project that is a side eect of this course. You are not required to read this chapter. You may nd some of the topics interesting, but I cannot guarantee that anything is accurate or correct in this chapter. In other words, read at your own risk.
20.1
20.1.1
GPS
Interface
A serial port should be ne to interface with most GPS units. We can get handheld GPS units that come with computer ports, or get GPS units that are intended to interface with computers. It is not worth it to get GPS modules that require housing because they are usually more expensive than GPS units that have the circuit board, antenna and housing all integrated.
20.2
20.2.1
Stereo Vision
Theory
With two or more high resolution optical imagers, it is possible to use triangulation to provide stereo views of the road and terrain. It is important to emphasize high resolution because low resolution images do not contain enough information to determine the size and distance of distant objects. It is critical that a robot be able to see and determine the terrain for as far as possible. To perform stereo vision interpretation, two images are taken at about the same time. The only critical part for terrain mapping is that the two images be taken from dierent points, and the robot does not move between the time of the two pictures taken. This means that one camera can be sucient, as long as 147
148
the robot does not move and an articulated arm is used to change the camera position and view. Once two images are acquired (from dierent points and views), software can start to identify common objects. Because of the change of view and angle, objects will not be identical in the two images. As a result, identifying the same object in the images requires some processing. Once objects are identied, triangulation can be used to determine the distance to the objects. Note that many hints can be used. For example, images taken earlier when the robot is at a dierent location can be used, combined with the encoded distance traveled. This can help to conrm the location of known objects. Once the distance to known or tracked objects is conrmed, the two frames can be corelated and distances to new objects can be deduced. This method is rather useful because it does not rely on extreme accuracy of placing and aiming the cameras. Instead, it uses clues already embedded into the pictures to help resolve distances. In order to help calibration, laser beams can be used to help identify objects with little contrast. Object Id The key to interpret stereo-vision is to id objects. This is easy for people because our eyes and brains have massively parallel processing to recognize objects. For a computer, however, this is not an easy or natural task. The general question is: given this region of pixels in this image, where is this region in the other image? We are looking for a spatial displacement of a region of pixels. Although this may seem like a very dicult problem, we do have some clues that can be helpful.
it cannot be too far away because there is only so much the cameras can be spaced apart, and only a particular range of distance to objects for vertically displaced images, the two regions must align vertically, for horizontally displaced images, the two regions must be aligned horizontally. for a tracked object, we already know approximately where a region of it should be located objects that are at a far distance should not be displaced at all (use them as anchor points)
To do this, both images should be pre-processed so that instead of looking at pixels, we can focus on larger macro constructs. Basic straight lines, curves, shapes and etc. can be recognized with parameters (angle, length, orientation, curvature and etc.). This facilitates the comparison of macro constructs between two images. With high resolution scans, there can be a lot of details in an image. It is unnecessary to identify each piece of small rock. As a result, the analysis
149
algorithm should use a divide and conquer approach to rst identify larger macro constructs, then break the larger constructs into smaller ones as necessary.
20.2.2
Equipment
Although one can spend much money, there are existing o-the-shelf solutions. Most digital cameras can be operated remotely via an USB connection. A 4 megapixel digital camera has sucient resolution for stereo vision. In Linux, a program like gphoto2 can be used to acquire images from digital cameras remotely. It may be necessary to protect consumer-grade digital camera from harsh environment. A dust-free sealed box with temperature control may be sucient for some applications.
150
Chapter 21
Project Log
21.1 20050525
For larger robots, we can use suspension equipped bicycle seatposts. We can also consider using elastomer rubber bung. An interesting option is elastomer pellets. At eBay, US$20 gets four pounds. It needs to be placed in some sliding cylinder/piston structure. I have no idea how much of this stu a robot will need as a shock absorber. Minerelastomer.com seems to have some useful bumpers.
21.2
20050525
At vetcosurplus.com, they have sealed aluminum boxes. These are perfect as an enclosure for oil-lled electronics for better cooling. Have nally decided to use pre-built DC-to-DC converters. This saves space and the trouble of debugging switching regulators. Digikey has some. This clears the path to use the HIP4082 FET driver chip to drive N-MOSFETs. The whole circuit board is going to be submerged in oil inside an aluminum sealed case. This way, there is no need to mount individual air heatsinks to the chips. The only remaining problem is to get the cables through. We can always drill a hole, get the cable in, then use sealant to plug the hole. However, oil can leak through capillary eect. A solution is to use a block to separate inside wiring from outside wiring. One option is to use a Molex connector pair. The crimp pins can be coated with epoxy to stop oil leaks. We can also consider soldering the crimp connectors, then coat just the part between the conductor and the insulator with epoxy. The molex connector housing is then tted in a opening cut just for it. The connector needs to be completely sealed with sealant, and xed to the opening. JB Weld may be used. For the main regulated power supply, a power supply designed for car may be used. The only concern is whether such supplies can deal with the voltage 151
152
dip when the motors draw current. There is one that has a specication of 10.8V to 20V (from powerstream.com). It has quite a bit of power for 5V and 12V applications.
21.2.1
The oil submerged box only needs to deal with high power devices that generate heat. Itll need the following interfaces:
main power from battery and ground conditioned (after TVS protection) power to other components + and - terminals to motor 1 (2) + and - terminals to motor 2 (2) control signals to each N-FET (8 total) thermister terminals (2), may be combined to the same header as the control signal regulated 5V and paired grounds (5 pairs)
21.2.2
Components
8 high current N-FETs 1 high current 14V bi-directional TVS 1 5V switching regulator in DIP format 1 12V switching regulator in DIP format (maybe)
21.3
20050129
Happy new year (after 9 months of no activities!). Got a consulting application that requires the use of windshield wiper motors. So I am now looking into big-and-hefty electronics. After a little bit of research, I have decided to use N-channel MOSFETs for the switches, and use some high-side FET drivers to handle the high-side switches. The windshield wiper motors are modied to work at 24V (instead of 12V). This improves torque and speed at the same time. Furthermore, from some review on the web, this modication also makes the forward and backward speeds more comparable. As far as parts are concerned, I am inclining to use the National Semiconductor LM9061M as the high-side FET driver. Compared to the Linear Technology, Maxim and International Rectier alternatives, the LM9061M is inexpensive, and it will get the job done. It does require at 7V for the supply
21.3. 20050129
153
(bootscrap) voltage. Ideally speaking the Intersil isl6801 is even better because it only requires 5V as a bootstrap The full-bridge driver Intersil HIP4082 is even better because it can interface with four N-FETs directly. The HIP4082 does require a 12V supply, which should not be a problem. The same applies to the HIP4080 and HIP4081, which are more expensive due to their internal charge pump. Note that the HIP408x family oers shoot-through protection and programmable dead time, which are extremely important safety features that should not be implemented in software. The HIP4081 has a better interface for software control. For the actual switch, any N channel FETs that handles up to 12V at the gate can be used. For example, the International Rectier IRFZ48V can handle VGS up to 20V, and the FET can switch up to 72A (continuous) with a breakdown voltage of 60V. This part is also quite inexpensive at US$0.84 each at quantity 10. In order to support passive braking, we need a hefty diode that can be used to loop back current. Ideally speaking, we want all of the energy be dissipated by the motor itself. However, this is not possible due to the forward voltage drop of a diode. Diodes that are rated at 40A or more are quite expensive and big. The only exception is the On Semiconductor MBR6045WT ($3.29 at quantity 10). Note that the loopback diode needs to handle the maximum motor current continuously because the energy to dissipate is not due to the collapsing magnetic eld of the coil winding, but rather the mechanical momentum of the robot itself. We know that this current cannot exceed the stall current of the motor because of coil resistance. The loopback diode may not be needed because the N-FET has a specied continuous source current of 72A (max.). According to the diode forward voltage curve, the forward voltage is about 1V at 72A. This means the package has to dissipate 72W. With proper heat sinking, the package has a thermal resistance of about 1.5C/W. This means that at 72W, the temperature is just about at the maximum (150W). If we dont have to put in external loopback diodes, the set up can be much cheaper (because the diodes are 3 times the price of FETs!). Without loopback/yback diodes, we still need a TVSes to help protect the switches against inductive electrical potential. This is a lot cheaper, though. For example, the Diodes Corp. 1.5KE series are typically less than $1 per unit (for bidirectional, C sux). For a 24V system, the 27V or 30V parts are appropriate (for a little margin). The exact part numbers are 1.5KE27CA and 1.5KE30CA. With the TO220 package, there are a few heat sinking options. Although a wiper motor only has a current draw of about 4A to 5A continuously, it is best to design the board to accommodate for more current capability. The relatively cost eective Aavid 529902b02500 has a rated 4.5C/W thermoresistance. With a total 6C/W, we can still easily switch 20A with the afore mentioned inexpensive MOSFET (with no airow). The only drawback is that the size of the heatsink is a bit big, requiring a foot print of 42mm by 25mm. A dierent technique is to mount the TO-220 transistors on the case using
154
thermal adhesive. This gives us more freedom as far as heat sinking is concerned. However, because the large heat sinking pad is tied to drain on the MOSFET, we cannot use a single heat sink.
21.4
20040326
Finally got batteries and got them charged. The wheelchair still wont move. When it is turned on, the control box beeps. The voltage is good, though. Check all the connections and they are all good. Dont know why the control box beeps. The steering mechanism does work. The drive wheel turns left and right. However, forward and backward does not work at all. I am guessing that either the control box is shot, or the motor itself is shot. I am going to ohm out the motor terminals later (somehow) and see if them are shorted. Oh, when the control box is not connected to the motor, the beeping stopped. Battery voltage is veried to be good (all the way to the green side). I hope that the problem is only due to the power component in the control box is shot. I can probably either x the circuit or design a replacement circuit. However, if the problem is with the motor, then Ill have to replace that. Ooops, it was my own operator error. After reading the manual of the MPV4 (which, by the way, is quite a bit more modern than the original MPV), I realized that I had to switch from brake back to run to make it go. Duh! The wheelchair now operates just ne. It propelled me without any problem. The next challenge is to decode the signals coming out from the hand control unit and replicate that using a controller. It seems easiest to decode from the user interface box after I open it up.
21.5
20040319
The charger that came with the battery doesnt seem to charge the battery. I think the connector is wrong. Anyway, I am now using my automobile battery charger, set to 2A charge rate (instead of 12A). Each 12V battery that came with the wheelchair is rated at 33AH, itll take a little while to fully charge both. I think I have already determined to use a mini-ITX computer as the main controller. Itll be running Linux with most peripherals connected by USB or serial (via USB-serial convertor). The one from http://www.jkmicro.com seems to be a good choice, with a switching power supply and two included CF slots, it has all the components that I need. Well, almost. I may want to add my 802.11b WiFi card to the PCI bus, or a CF version so I can program it remotely. The wheels on the wheelchair are not encoded. Because all three wheels are mounted fairly close to some chassis component, I dont think itll be too dicult to mount some hall eect sensors and magnets (on the wheels). For
21.6. 20040318
155
outdoor use, magnetic encoding is the only choice because optical encoders are too sensitive to dirt. The back steering mechanism also needs to be encoded. This is a little more tricky because I need to know the exact angle. I can put one extra magnet so that it can indicate the home position, then I can track the current angle using the other sensors. This also means that when the bot resets, it needs to rst control the steering mechanism to nd the home position. My rst task, after the batteries are charged, is to test the wheelchair using the user controls. If all works out, then I can start to reverse engineer the control mechanism and see how I can interface that with a microcontroller. I am suspecting the control mechanism (joystick) is really just two potentiometers, and I replace them with two digital pots easily. Just tested the batteries. One doesnt charge, but the other seems okay. Now I need to go nd a replacement so I can power up the vehicle for the rst time. eBay has U1 batteries sold for 55 dollars for two, so thats the way itll go.
21.6
20040318
Wheelchair nally arrives! The condition is as expected, but I have no way to test it yet. Need to get two 1U 12V batteries rst. Cant spend any more money on it now, must wait... Looked up USB GPS for Linux. Only the ones with Prolic PL2303 should be used. UC-232 and IO-Data USB to serial adaptors may also work. To be safe, may need to get a serial GPS, then get an USB to serial converter so I dont use up one serial port. GPS 18 LVC from GPSCity seems to be a good choice. It has barewire output/input for maximum exibility.
156
Part X
Work In Progress
157
Chapter 22
DC Motor Encoding
Since we can use optical encoding, this topic may appear to be quite dead. However, optical encoding has many disadvantages. First, it is somewhat dicult from the mechanical perspective. Second, it has fairly limited resolution unless the encoding is performed near the motor axle. This chapter discusses a method that can potentially perform encoding at axle resolution.
22.1
The EMF
Whenever a current circuit is broken, the collapsing magnetic eld produces enough voltage to push current through initially, then the current falls o according to the LR forula. Most of the time, this EMF (electro-magnetic feedback) is undesirable because it can potentially break semiconductor devices. On the other hand, this EMF can also be used for encoding purposes. A DC motor has an armature that has several contacts that touch the brushes on the stator. As the contact opens, the EMF generated can potentially be detected by a circuit. As a result, we can encode motor rotation based on EMF detection.
22.2
The Problem
The main problem of this approach is noise. Although in theory the contact remains closed until it moves out of the reach of the brush, it is noisy in reality. In other words, the brushes bounce as it makes contact with the contact on the rotor. This generates a lot of EMF noise spikes that interfere with motion encoding. 159
160
22.3
The Solution
If we know the bouncing characteristics, we can potentially use a software approach to lter out the bounces. We do need to assume that the time between bounces is signicantly shorter than the delay due to contact rotation.
22.4
22.5
This is harder than the previous case. The only detectable clue is the ramping up of current due to the inductance of the rotor coil. We can potentially use a current sensing circuit to detect this transition.
22.6