This is an old revision of the document!
The book you are about to read is a microcontroller and practical robotics handbook which according to the authors hope will help to build intelligent microcontroller based solution and popularize the engineering profession amongst the young people. The book is oriented to the schools and universities, but can also be used for hobbyist and companies who are planning to use AVR microcontrollers in their projects. The target group is as well teachers as learners by helping to get fast results for both of them: for learner to get new knowledge and for teachers to make their life easier when trying to bring new subject for the learners :)
Platform
The selected base platform for the practical examples is AVR ATmega128 microcontroller based Robotic HomeLab Kit. AVR is one of the most widely used microcontroller besides Microchip PIC amongst the hobby robotic community and is very good for educational purpose.
The book is logically divided into five sections:
1. section is giving a fast introduction to the most simple electronic fundamentals and calculations. These formulas are very useful for several practical exercises. Also the C-language is briefly covered and some simple examples are given.
2. section gives an overview of 8-bit AVR microcontroller by using the ATmega128 as an example. The section relays basically on the ATmega datasheet but simplifies and presents the information in more friendly way. This is a general microcontroller basic function description.
3. section introduces the hardware and software platform which is developed for the educational use and is a base platform of the next chapter examples. Special software library is developed for most common functions. The user can concentrate on the program logic instead of working with the registers in deep. Both Windows and Linux operation systems are supported.
4. section consists of practical examples together with exercises. Examples are divided into labs by covering the most common electromechanical devices and functions. Every chapter has short theory background description and practical example which enables to run the function immediately by coping the program into the controller. Every exercise group (lab) has exercises in the end of sub-chapter together with questions. Exercises are divided for beginners and for advanced users, in a way giving the selection according to the learners skills. For better understanding the chapters are provided by the references to the theoretical section.
5. section is giving tips for doing the teamwork. The section illustrates how to present the solution developed as a teamwork and how to write the report. The topics are highlighted and commented which should be included into the team report. The example project is also introduced to give a pattern for compiling the concise report.
We hope that the book is a assistant for young and senior microcontroller and robotic enthusiast and will bring more people closer to the technology.
Snowy and cold Tallinn, January 2010
Raivo Sell
Microcontroller is basically a computer which is placed on a single integrated circuit chip. It consists of memory, processor, as well as input-output interfaces. Microcontroller is programmed to run a certain task, which means, if there is a need to change or enhance its functionality, one must install a new program on the chip. Aspects which differentiate microcontrollers from other computers (PC, laptop, server, etc.) are:
Microcontrollers can be found in a variety of everyday items: household appliances (e.g. microwave oven, TV-set), toys (Lego NXT, talking dolls), vehicles (cars, hoists), etc. Microcontrollers’ wide usage has been possible because they are easy to program and have wide range of functionality, hence is very easy to add new features and upgrade the level of intelligence of the appliance they are in.
Robotics is science which combines technology and knowledge necessary to build robots. Due to fast development of technology the term robot, as an automatic machine that replaces human, is not so clear anymore. Robot is not anymore just humanoid robot, robotic hand at automotive industry’s assembly line, autopilot in aircraft, artificial intelligence built of living neurons or simple cleaning robot, it is also computer software witch completes tasks meant for humans(for example complies reports). It is known that robots are built to replace humans at certain tasks. There are many reasons for this: dangerous working conditions, cheaper production, monotonous work may cause humans to err, new systems are so complex and time critical, that automatic system makes better decisions than human.
Because of the wideness of the robotics, we determinate it as hobby robotics, the systems are not too complex and it is possible to build them alone. Common microcontrollers in hobby robotics are:
Quite often third parties have created development boards and environments based on microcontrollers mentioned herein before. For example: Arduino (AVR), BASIC Stamp (PIC) and Lego NXT (ARM). Necessaries for developing HomeLab witch are described in this book hereinafter are based on AVR ATmega128 microcontroller. Question arises from the large amount of microcontrollers and development boards available: how to find the most appropriate? Generally we can classify following four properties: - price, physical characteristics, development environment and customer support. Notable physical characteristics are:
Here development environment is PC software, witch allows creating and compiling programs, uploading programs to the microcontrollers and bridging in the programs during work in order to detect possible faults. How easy and comfortable it is to do all that becomes decisive, because during development period of the program it will be the primary working area. All this leads to the fourth characteristic, witch is customer support. It is important that receiving help and support for solving possible issues is made as easy as possible. By considering all four mentioned properties, it should be possible to find the development board needed.
The following chapters introduce the AVR microcontroller, which this book is based on. In spite of being so small, a microcontroller is full of features that have been documented by Atmel in a manual almost 400 pages long. Besides that, there are a lot of additional specific documents. All that information has been compressed to a fast and simple overview of the subject, which helps a beginner to understand better the AVR and learn to read its datasheet.
AVR is a series of 8-bit RISC microcontrollers produced by Atmel. AVR follows Harcard architecture and therefore has separate program and data memory. For the program it has an internally overwriteable flash memory, for data there are static (SRAM) and EEPROM memory. Controller's frequency is usually up to 16 MHz and performance is almost 1 MIPS per a 1-megahertz cycle.
The production of AVR microcontrollers began in 1997 and by now AVR is one of the most popular controller with freelance electronic engineers. Thanks to cheap developing tools, the diversity of peripherals in a single package and low power consumption came the initial success. Today, there is another reason for choosing AVR: the massive amount of information and tutorials built up over the years. The AVR technology is inevitably aging, but to stay in the competition Atmel is also making new AVR microcontrollers with more up-to-date peripherals and 16- and 32-bit buses, first of which are from the 8-bit compatible XMega series and the latter from the brand new AVR32 series.
Based on the type of the application, there are several types of AVR microcontrollers, each with a different configuration. Most of the AVRs belong to the megaAVR series, which have a large program memory. To balance the megaAVR series, there is also the tinyAVR series, which have smaller packages and less features. In addition to those, there are also different series of microcontrollers designed specifically for controlling USB, CAN, LCD, ZigBee, automatics, lighting and battery-powered devices.
The following text describes the main features of megaAVR series microcontrollers, using one of the most popular controllers in this series, ATmega128 as an example. This controller is also in the HomeLab kit. Generally, all the AVR series microcontrollers' register names, meanings and usage is reglemented in a way which able the examples also to be used with other controllers by making only slight changes. The main differences are in the peripherals. The code samples of this introduction are written in assembler and C, using AVR LibC.
Like all other controllers, the AVR is also packaged in some standard shell. The traditional casing is DIP (also called DIL). DIP is a so-called casing on legs - all the pins extrude as legs, about 5 mm in length, from the black plastic casing. DIP casing is a sensible choice for hobby applications and prototypes, because there are cheap sockets available for it, so the microcontroller can easily be replaced, should it happen to malfunction or die. The legs are also the down-side of the DIP casing, because it requires holes to be drilled in the circuit board.
The surface mount casings (SMT, also called SMD) are much more compact, because their pins are designed to be soldered straight to the board without the need to penetrate it. SMT microchips are in thin, coin-sized rectangular casings with pins about 1 mm long. A more precise hand and better tools are required for soldering SMT chips.
AVRs are available in both DIP and SMT casings. The layout of the pins is designed as logical and electrically even as possible. For example, on larger chips, the ground and supply pins are located on several sides of the microcontroller, the pins for an external oscillator are near the ground pin, the bus pins are in numerical order, the communication pins are next to each other etc. AVRs digital pins are compatible with TTL/CMOS standard voltage levels. At 5 V supply voltage, 0 to 1 V means logical zero, which is also called zero, null, 0, low, ground, or GND. At the same supply voltage, 3 to 5.5 V means logical one, also called one, 1, high. This type of wide voltage ranges only apply to the inputs - the output voltage on a pin with no load is still 0 V or near supply voltage, depending on the state of the pin. The allowed analog voltage level on the ADC channels is 0 to 5.5 V.
To better understand the following examples on ATmega128, there is a pinout schematic of ATmega128 (SMT package) below. Next to each pin, is a text with its number, primary function and secondary (alternate) function in brackets. Supply pins are GND and VCC. AVCC and AREF are analog-to-digital converter's supply and reference voltage pins. XTAL1 and XTAL2 are for connecting an external crystal oscillator, resonator or clock generator. Pins PB0 to PG4 mark the bits of input-output buses. The secondary functions of pins are discussed in the corresponding chapters.
One of the toughest things for beginners to understand in a microcontroller is typically a register. When dealing with microcontrollers, it is impossible to get by without knowing what it is. This book is in no way different, as the reader is also expected to familiarize him/herself with the concept of a register and therefore the following text will try to explain it in simple enough terms, so that even a beginner can grasp the idea of a register.
A register is like a panel of buttons on a home appliance. It has switches, which can be turned on or off. One of the best examples is a simple tape player. For those, who don't remember, the tape player has (had) 6 buttons, left to right:
Each button does something, but only when it is used correctly. For example, the stop button does nothing unless a tape is playing - only then will it do something apparent and stop the playback. Forward and rewind buttons, on the other hand, can be pressed at any time, because the tape can be wound in both directions, no matter if it is playing or stopped. The recording begins only when the play and record buttons are pressed down simultaneously. Some may have tried to depress several or all buttons at once - in this case, the tape player might have done something unexpected or break altogether.
Microcontroller registers behave like buttons on a tape player - each button does something, when used correctly. By pressing the wrong buttons, a microcontroller usually won't break, but it won't work either. In reality, there are no buttons in the registers, instead there are a whole lot of transistors, which turn the electricity on and off. Simpler microcontrollers have 8 transistor-based switches in a single register. A register can be thought of as an 8-bit number, where every bit is marked by the state of one of those switches. For example, a bit value of 1 can mean that the switch is on and 0 that the switch is off.
Since the state of a register's switches can easily be displayed as a number and vice versa, a register can be compared to a memory, which can hold data in the size of one number. By this comparison, we see that registers actually are memory slots. The difference between a register and a memory slot is that a memory slot only stores the information, but in a register this information actually controls something. For example, if a binary value of 01100001 is written to a register, then three imaginary buttons are pressed down and something happens.
On a tape player it is possible to press each button separately, but in a register it is more difficult to change the value of one “switch” or bit. Typically it is necessary to change the entire content of the register. Before moving on to bit changing, one should know that there are a lot of registers in a microcontroller. Some parts of the microcontroller use tens of registers to control them. The variety of registers means that there has to be a way to distinguish between different registers and that is done by naming the registers. One register, for example, is called PORTB. Actually, these names are just to make things easier for the developer and each name corresponds to a numeric address.
To write to a register or read a value from it, it has to be addressed as a variable in C. The following example demonstrates writing a binary value to an imaginary register REG and then reading it to variable reg. Binary values are distinguished by 0b (leading zero), so that the compiler understands the numeric system.
REG = 0b01100001; unsigned char reg = REG;
There is nothing difficult in writing and reading register values, but it gets a little tricky if only a single bit needs to be changed. To change bits, one needs to know how to do binary math and use different numeric systems. It isn't forbidden to deal only with binary numbers, but they can be a bit troublesome, because binary numbers are quite long, and this is why most people use shorter hexadecimal numbers.
In hexadecimal, the numbers are not only 0 and 1 as in binary or 0 to 9 as in decimal, but instead 0 to F. A hexadecimal number consists of four bits. The table on the right shows the binary numbers and their hexadecimal counterparts. Binary numbers are converted to hexadecimal by reading bits four at a time, starting from the lowest rank. Ranks are read from right to left and their numbers start from 0. For example, the lowest ranked (rank 0) bit is 0 and the highest (rank 3) is 1. In the previous example, the register's binary value is 01100001, which is 61 in hexadecimal and is written as 0x61 (leading zero) in C.
To change single bits in a number (register, variable or anywhere else for that matter) it is necessary to use binary operations. Binary operation is an operation between two binary numbers, where each bit of the numbers is subject to its own logical operation. Typically a microcontroller supports four binary operations, each having several names. The following section describes the logical operation behind each of these four binary operations with a single bit or multpile bits.
This is all one needs to know to change single bits. The theory alone is probably not enough, though, and that is why there are some typical examples with registers in the next few paragraphs.
To set one or more bits in a register high (1) a logical addition operation is needed. One of the operands of the operation must be the register and the other a binary number, where the only high bit is the one that needs to be set high in the register. This binary number is called a bitmask. Below is the C code for the operation shown on the right:
// Let's suppose REG = 0x0F REG = REG | 0x11; // First method REG |= 0x11; // Second method // Now REG = 0x1F
To set one or more bits in a register low (0) a logical multiplication operation is needed. One operand of the operation must be the register and the other a bitmask, in which the only low bit is the one that needs to be set low in the register. Below is the C code for the operation shown on the right:
// Let's suppose REG = 0x0F REG = REG & 0xFE; // First method REG &= 0xFE; // Second method // Now REG = 0x0E
To invert one or more bits in a register an exclusive disjunction operation is required. One of the operands of the operation must be the register and the other a bitmask, where the only high bit is the one that needs to be inverted in the register. Below is the C code for the operation shown on the right:
// Let's suppose REG = 0x0F REG = REG ^ 0x11; // First method REG ^= 0x11; // Second method (use only one per inversion) // Now REG = 0x1E
To invert all bits in a register a negation operation is used. This operation is unary, which means it has only one operand. Below is the C code for the operation shown on the right:
// Let's suppose REG = 0x0F REG = ~REG; // Now REG = 0xF0
To read one or more bits from a register the same operation is required as was used for setting a bit low - logical multiplication. One of the operands of the operation must be the register and the other a bitmask, where the only high bit is the one that needs to be read from the register. Below is the C code for the operation shown on the right:
// Let's suppose REG = 0x0F unsigned char x = REG & 0x01; // Now x = 0x01
Many programming languages actually have a few additional bitwise operations, which make it easier for the programmers. These are bit shifting operations that shift bits left or right in a binary number. The main value of shift operations in dealing with registers is their ability to convert bit ranks to bitmasks and vice versa.
The image on the right shows a shift left operation. Although bit shifting is not a logical operation and has no corresponding symbol, in C it is marked as “«”. Shift left is used to transform a bit rank to a bitmask. For example, to get the mask for the 6th bit (NB! rank 5), number 1 has to be shifted left 5 times. The example operation looks like this in C:
REG = 0x01 << 5; // Now REG = 0x20
Shift right operation works similarly to shift left operation. It is marked as “»” in C. Right shift is used to get the logical value of a bit from a bitmask. A leading example showed how to read the value of a single bit. Let's suppose the bit to read is not of the lowest rank, but for example of rank 5. In this case, the result would be either 0x20 or 0x00, but sometimes a result of 1 or 0 is needed and that is when the right shift comes to the rescue. The example operation on the right looks like this in C:
// Let's suppose REG = 0x20 unsigned char x = REG >> 5; // Now x = 0x01 (or simply 1)
If a bit is shifted right from the lowest rank or left from the highest rank by the bit shifting operation, it disappears. Some programming languages also have rotating bit shift operations, where the bit doesn't disappear, but moves from the lowest rank to the highest or vice versa. C doesn't have that kind of bit shift operations, but they can be written by the programmer if needed.
All bit operations work with not only registers, but with variables and constants as well. The latter can of course only be used as operands and not the result.
To do anything actual with the microcontroller's registers, one needs to know how to use that particular microcontroller. Each microcontroller comes with one or several datasheets, which describe the whole structure and functionality or the microcontroller. The datasheet also describes the registers. The following will help understand the register descriptions in AVR datasheets.
The image shows ATmega128 microcontroller's UCSRnA register, which stands for “USART Control and Status Register A”. This register is used to configure AVR's USART module and read its states. All AVR register names are written in capital letters, but as the reader might notice, the register name contains also a lower case n. A lower n is used to mark some module's index. Since ATmega128 has 2 almost identical USART modules, they are not described twice, but only once and the n must be read as 0 or 1 by the user. Therefore ATmega128 has registers UCSR0A and UCSR1A.
The content of the register is marked by an 8-slot box with a bold line around it. Each slot marks one bit. Bit ranks are marked above the box - increasing from right to left. Since AVR is an 8-bit microcontroller, most of the registers are 8-bit as well. There are some exceptions, a few registers are 16-bit, but they actually consist of two 8-bit registers. Like each register has a name, each bit in the register has also a name - just like the buttons on a tape player. Each bit is described in the datasheet. Bit names are abbreviations as well and the lower n must be substituted with the module's index, just like with register names. Some registers don't use all 8 bits, in this case the bit's slot is marked with a hyphen.
Below the register's bits are two lines, which state whether the bit is readable (R), writable (W) or both (R/W). For example, the status bits can't be overwritten and even if it's attempted in the program, the bit will remain unchanged. If the bit is marked as writable, reading it will always result in one specific value stated in the datasheet. The second line specifies the default value of the bit, which it has after the reset of the microcontroller.
While AVR register names point to an actual memory slot address, the bit names hold the rank number of the corresponding bit. Therefore it is necessary to transform the names to bitmasks using a shift operation, in order to manipulate with bits in a register. The following code contains a few example lines for using the USART 0 module's register.
// Set TXC0 bit high UCSR0A |= (1 << TXC0); // Set U2X0 bit low UCSR0A &= ~(1 << U2X0); // Read the value of UDRE0 bit(mask) unsigned char u = (UCSR0A & (1 << UDRE0)); // At this point u value is either 0 or 32, // which enables using it in a logical operation if (u) { // Invert MPCM0 bit UCSR0A ^= (1 << MPCM0); } // Sometimes it is necessary to acquire a specific 0 or 1 value, // so the read bit needs to be shifted right u >>= UDRE0; // Now the value of u is either 0 or 1
AVR has an internal 8-bit data bus, through which is moved the data between arithmetic logic unit (ALU), status register (SREG), program counter (PC), random access memory (SRAM) and peripherals. The program, an array of commands, that get executed in the ALU comes from an address in the flash memory, specified by the program counter. The ALU has 32 8-bit general purpose registers, which are used as operands when carrying out instructions.
AVR has a two-stage instruction pipeline. While one instruction is executed, the next is fetched from the program memory. This is why carrying out jump instructions takes 2 cycles on fulfillment of the jump condition. Since the new instruction is always fetched from the next memory address, it is necessary to discard the previously fetched instruction and fetch a new one when jumping to another address, because it was fetched from an old, wrong location.
General purpose registers R0-R31 are like buffers for storing and operating with memory and peripheral data. They simplify the architecture of the processor, because they are quickly accessible by the ALU and the use of the data bus to read operands from the memory is not necessary for every operation. General purpose registers are used for performing all data-related arithmetical and logical operations.
While programming in assembler, it is possible to store the urgent data in general purpose registers. While programming in C and a need to store a variable in a general purpose register arises, the variable is additionally defined as “register”. For example:
register char x;
The instruction set of most AVRs consists of 90-133 different instructions. ATmega128 has 133 instructions. Instructions have either one, two or no operands. Most instructions take only one cycle to complete, but the more complex ones can use up to 5 cycles. For XMega, the successor of AVR, several instructions have been modified to use less cycles. Most instructions in AVR are used for jumps, moving and comparing data and executing arithmetic calculations. A status register is used for performing calculations and comparisons. It stores the output status of the ALU - whether the result was negative, positive, zero, exceeded the maximum allowed value (8 bits), needed to transfer a bit to the next operation etc (there are a few more complex cases).
Example
This is a piece of code written in Assembler and consists of pure instructions, which adds 5 to the byte at random access memory address $100 (decimal 256). These instructions exist in all AVRs.
ldi r1, 5 ; Load the constant 5 to general purpose register r1 lds r2, $100 ; Load the byte from the memory to register r2 add r2, r1 ; Add the value of r1 to r2 sts $100, r2 ; Write the value of r2 back to the memory
Stack is a data structure, where the last data written to the memory is read out first. AVR's stack can be used while operating with subroutines, interrupts and temporary data. Before executing a subroutine or interrupt, the address in the program counter where the program was interrupted is stored in the stack. When the subroutine or interrupt has finished its execution, this address is read back from the stack and the program continues from the address it left off before. Storing temporary data in the stack is usually used when dealing with shorter chunks of code, which do not require reserved memory throughout the execution of the program. Simpler assembler programs are usually written so that it is not necessary to use the stack, but if the program contains a lot of variables and functions, the compilers automatically make use of it.
The stack of MegaAVR series microcontrollers is physically located in the random access memory. Some tinyAVR series devices do not have a random access memory at all and the stack is realized as a separate, quite limited memory unit. Typically there are no compilers for devices with no random access memory.
To program in a high level language (Pascal, C, C++), it is not necessary to be familiar with the inner workings of the microcontroller, because the compiler is capable of selecting general purpose registers and instructions by itself, but knowing what goes on in the controller is certainly beneficial. It is necessary to know the instructions of the microcontroller when developing time-critical applications, where the operations have to be completed in a limited amount of cycles.
As it is with most digital electronics, an AVR also works at a constant frequency. A constant frequency assures the reliability of data exchange throughout the device. There are several methods for generating a clock signal for an AVR.
Internal RC oscillator
This is a internal clock generator, which does not need any external components. Its main cons are low frequency and inaccuracy.
External RC oscillator
Works on the same principal as an internal RC oscillator and has no significant advantage over the internal one.
Crystal oscillator
Crystal oscillators use a crystal (usually quartz) that vibrates at its resonant frequency in the electric field and has a piezoelectic quality to produce an electric field on mechanical deformation (vibration). Crystal oscillators enable a precision of nearly 0.001%, which does not depend on the temperature.
Ceramic resonator
Ceramic resonators are similar to crystal oscillators, but are made from cheaper piezoelectric materials. Ceramic resonators are typically smaller than crystal oscillators, but are also less precise (~0.5%) and more sensitive to temperature changes.
External clock signal
External clock signal can be generated with any device, provided that the frequency and amplitude (voltage) are in the correct range. For example, an external clock signal generator can be used to provide a clock signal to several microcontrollers at once.
Interrupts in AVR can be caused by counters, communication interfaces, analog-to-digital converter, comparator, special input-output pins and several other functions, depending on the controller. Any interrupt can be allowed or disallowed from the unit that generates it. Regardless of the interrupt being allowed or disallowed, there is a 1-bit field (interrupt flag) in the corresponding unit of the controller, which is marked as true when the interrupt condition is fulfilled. If the interrupt flag is changed to true and the interrupt is allowed, the controller starts to execute the code specified for this interrupt.
Every interrupt in the AVR microcontroller is tied to a specific event. Each event has a flag bit in the status register, which marks the occurring of the event. Events are also tied to interrupt mask registers and the corresponding bits. If the event's interrupt bit is left unmasked and an event occurs, the processor halts executing the current program for a few duty-cycles and begins executing the interrupt program. After the interrupt program has been executed, the processor resumes executing the paused main program.
Example
To use interrupts with the AVR LibC library, it is necessary to include interrupt.h. The code that gets executed upon interrupt is written after the “ISR” keyword. The text between the brackets after “ISR” is the name of the interrupt. C language example:
#include <avr/interrupt.h> ISR(XXX_vect) { // Do something }
Global allowing of all interrupts is configured from the control and status register SREG. The option to allow or disallow all interrupts at once is there to help protect data. Since interrupts disrupt the execution of the main program, some data used by the main program may be disturbed or corrupted in the process. Situations like this can be avoided by simply disallowing all interrupts before dealing with such delicate data. Disallowing interrupts globally is easy, if it can be done by changing only one register (SREG). After the critical part of the program has been executed, the interrupts can easily be allowed again and all the interrupts that would have fired in the meanwhile will get executed.
Example
Let's suppose there is a 16-bit variable in use in the program, which is changed by both the main program and the interrupt program and the value of this variable is later given to another variable:
#include <avr/interrupt.h> // Global 16-bit variables x and y unsigned short x, y; // A random interrupt that changes the value of x ISR(XXX_vect) { x = 0x3333; } int main() { // Give a value to x x = 0x1111; // Allow interrupts globally sei(); // Give the value of x to y y = x; }
The program itself is very simple - first, variable x is given a value of 0x1111 and later, its value is given to variable y. If an interrupt fires between those two operations, x gets a value of 0x3333. By logic, variable y can have two possible values by the end of the program, but on an 8-bit AVR there is also a third option. This is because 8-bit architecture needs 2 cycles to move 16-bit data and therefore a badly-timed interrupt can corrupt the integrity of the data. Consequently, y can have a value of 0x1111 or 0x3333, but it can also have a value of 0x3311 at the end of the program. To avoid getting the third, unwanted value, all interrupts should be temporarily disallowed before performing operations that take more than 1 cycle.
In the following example, the value of x is given to y using a safe method:
// Disallow interrupts globally cli(); // Give the value of x to y y = x; // Re-allow all interrupts again sei();
All the buses on AVR are both readable and writable, if they are used in the default logical input/output (I/O) mode. AVR buses are named as letters from the beginning of the Latin alphabet: A, B, C etc. Some AVRs might not have bus A, though, if bus B exists. Each bus is 8-bit and each bit usually has its own pin on the controller. Pins are counted with numbers starting from zero. For both directions on the bus, there are two separate registers. In addition, there is a register to decide the real direction of the bus, where a bit value of 1 marks the bus as output and 0 as input. Altogether, each bus has three registers:
Example
Task: make pins 0-3 on bus B inputs, pins 4-7 outputs, set pin 5 high and read the values of pins 0-3 to a variable. The C code for it looks like this:
#include <avr/io.h> int main() { unsigned char x; // Pins 0-3 as inputs, 4-7 as outputs DDRB = 0xF0; // Set pin five as high PORTB |= (1 << PIN5); // Read the values from inputs 0-3 x = PINB & 0x0F; }
In this example, the inputs are used in Hi-Z (high impedance) mode. In essence, the input does not put virtually any load on the source of the signal. This mode might be necessary, if the pin is used as a data bus. It is wise to use a pull-up resistor on the input, if the pin is used for a button, a switch or any other solution, where the input is connected to ground. For that, the output bit of the corresponding pin must be set high in the input mode - as a result, a resistor is placed between the supply voltage and the input, which keeps the input voltage high unless it is being pulled down by something. The goal of a pull-up resistor is to prevent floating of the input due to statical electricity and other interferences. After booting the controller, all IO buses are in the high impedance input mode by default.
Usually, the pins on the IO bus are also used for other peripherals, aside from the logical connections. If there is a need to use the alternate function of the pin, the appropriate IO pin mode can be found in the AVR's datasheet. For example to use an ADC channel as an input, the pin should be in input mode and to generate PWM signal, it should be in output mode. On the other hand, some peripheral modules select the IO pin mode by themselves.
External interrups are one of the most simple peripheral functions. Typically AVRs have 1 to 8 special pins, which are used to cause interrupts in the program when their logical value changes or they are at a certain state. Since this function is usually used to monitor external logical signals, these pins are called external interrupt pins.
To use an external interrupt, the pin has to be configured as standard IO input (it can also be used as an output, but in this case the interrupt can only be created by the controller itself). It is necessary to allow receiving interrupts and specify the condition that causes the interrupt to fire in the external interrupt configuration register. There are four possible conditions:
When the mode is set to logical zero, the interrupt will fire continuously as long as the pin has a value of zero. During this period the execution of the main program is stopped.
Grouped by principal, there are two types of interrupts: synchronized to the controller's clock and asynchronous. Synchronized interrupts work by remembering the values of the inputs, which means that the changes in logical values are found by comparing values read during two different clock cycles. If the logical changes in the signal happen faster than the controller's duty-cycle, the interrupts either fire incorrectly or are skipped altogether. Asynchronous interrupts do not depend on the controller's clock and enable detecting faster changes in the external signal as well - the logical level must still be constant for at least 50 ns. ATmega128 has 4 synchronized and 4 asynchronous external interrupts.
Example
Task: Make ATmega128 pin number 9 (pin 7 on bus E) fire an interrupt if its value is changed. This pin corresponds to the INT7 external interrupt, which is synchronous.
#include <avr/interrupt.h> // The code of the external interrupt ISR(INT7_vect) { // Do something } int main() { // Change pin 7 on bus E to an input by changing bit 7 to zero DDRE &= ~(1 << PIN7); // Defining a pull-up resistor to to pin 7 on bus E // to prevent input floating PORTE |= (1 << PIN7); // Set the interrupt mode to logical change for interrupt 7 // in the external interrupt configuration register EICRB = (1 << ISC70); // Allow external interrupt 7 EIMSK |= (1 << INT7); // Allow global interrupts sei(); // Endless loop while (1) continue; }
In addition to interrupts fired by single pins, if the AVR has enough pins it is possible to use entire groups of pins to fire logical value change interrupts. These interrupts are simply called pin change interrupts. They fire when the value of at least one pin in the group is changed.
Analog-to-digital converter (ADC) transforms an analog voltage value to a digital value. The allowed voltage range on an ADC input of an AVR microcontroller is 0 to 5.5 V. The size of the digital value is 10 bits, but its precision is ±2 units. The error may be even larger, if the microcontroller's supply voltage is not protected from interference. AVR has a separate voltage supply and comparison voltage pin for ADC. The separate supply voltage helps to cut down the interference and it may not differ more than 0.3 V from the main supply voltage. Comparison voltage defines the maximum digital value. For example, if the comparison voltage is 3 V then an input with the same voltage will read as 210 - 1 (1023).
AVR ADC works on the principal of successive approximation. In short, the measured voltage is compared to specific voltage levels and the results are reported as a bit array. This method is relatively slow, as each bit in the final result is calculated separately. AVR spends 13 clock cycles for each measuring, except the first (on start-up), which takes 25 cycles. These cycles are not the controller's duty cycles though, but instead special cycles allocated to the ADC unit by the frequency divider. The ADC frequency should be 50-200 kHz to achieve maximum precision, on higher frequencies the precision fades. In some cases it is more important to get a large number of readings instead of maximum precision, in which case using a larger frequency is totally justified. According to AVR documentation, one measuring takes 13-260 µs.
The measured value can be read as an 8- or 10-bit value. Since AVR itself is an 8-bit device, it has two 8-bit registers for storing the ADC values. It is possible to specify in the settings whether the first two or the last two bits go to a separate register. If the two younger bits, which characterize the result less, are separated, the result can be read as an 8-bit value - a combination like that is called a left aligned result. The other combination, where both registers are read and the value is in 10-bit format, is called a right aligned result.
A typical AVR has 8 analog voltage input channels, ATtiny series have only a few, some ATmega devices have 16, but there is always only one converter. To make it possible to use different inputs, the device has a built-in multiplexer. The input of the multiplexer is definable using a special register. The ADC unit has a few more properties: using the processor's sleep mode for converting to reduce the interference and the option to use an internal fixed comparison voltage (usually 2.65 V, in some models 1 V).
Example
Task: measure the voltage in ADC channel 3 of an ATmega128. The voltage is in range of 0-5 V and the result should be at 8-bit precision.
#include <avr/io.h> int main() { unsigned char result; // Choose AREF pin for the comparison voltage // (it is assumed AREF is connected to the +5V supply) // Choose channel 3 in the multiplexer // Left align the result ADMUX = (1 << REFS0) | (1 << ADLAR) | (3); // Start the ADC unit, // set the conversion cycle 16 times slower than the duty cycle ADCSRA = (1 << ADEN) | (1 << ADPS2) | (1 << ADSC); // Wait for the measuring process to finish while (ADCSRA & (1 << ADSC)) continue; // Read the 8-bit value result = ADCH; }
Counters, which in some sense can be called timers, are one of the most important sub-functions of a microcontroller. These enable to precisely time processes, generate signals and count events. A counter converts the number of input cycles to a binary value using an array of triggers. The maximum number of counted cycles depends on the length of this array and this is marked by the length of the binary code. AVR has 8- and 16-bit counters. If a timer has reached its maximum value (255 in 8-bit and 65535 in 16-bit counters), the next cycle will generate an overflow and the counter resets back to 0. A counter's clock signal can come from the clock signal of the microcontroller and in this case it is possible to decrease its value using a prescaler. Some AVRs have an internal independent clock signal generator, which can be modified to run faster using a frequency multiplier. Counters also differ in application cases and work modes.
In the default mode, a counter does nothing more than count sequential numbers all the time. Its value can, of course, be read and changed from the program at any time. The only additional function in the default mode is to cause an interrupt on counter overflow. The default mode is typically used to execute a section of the program at certain intervals.
Example
Task: Make an 8 MHz ATmega128 fire an interrupt every 10 ms (frequency 100 Hz). For this task, the 8-bit counter 0 is suitable.
#include <avr/interrupt.h> ISR(TIMER0_OVF_vect) { // Give the counter such a value // that the next overflow occurs in 10 ms. // Formula: 256 - 8 MHz / 1024 / 100 Hz = 177,785 = ~178 TCNT0 = 178; } int main() { // To make the first overflow interrupt fire in 10 ms as well, // the counter needs to be initialized here. TCNT0 = 178; // Prescaler value 1024 TCCR0 = 0x07; // Allow overflow interrupts TIMSK |= (1 << TOIE0); // Allow interrupts globally sei(); // Endless loop while (1) continue; }
The counter in this example will not generate the interrupt in exactly 10 ms though, in order to do that it would require giving the counter a decimal value and this is not possible. To achieve a precise interval between the interrupts, both the prescaler value and the initial value of the counter have to be chosen so that their division results in an exact number. This is not always possible, especially with 8-bit counters as their value range is quite small. To achieve a more precise or larger interval, a 16-bit counter can be used.
It is also possible to use an external clock source as a counter's clock signal. AVR has a pin called Tn for this purpose, n marking the number of the counter. External clock signal and the polarity can be selected using the prescaler register.
Since the counters allow timing operations, more complex AVR microcontrollers have an option to time specific events on a hardware level. This part of the counter is called an input capture unit. There is a choice between two events: the logical change in the value of a special input pin or in the value of the analog comparator result. If the selected event occurs, the counter's value is written to a special register, from where it can be read at any time. If the event is longer than the overflow time of the counter, the program has to count the overflows as well and take them into account when calculating the final result.
Example
Task: Measure the frequency of an external 122 Hz - 100 kHz logical square signal using an 8 MHz ATmega128. The measurement has to be at 1 Hz precision. The program uses a 16-bit counter with 1 input capture unit.
#include <avr/interrupt.h> unsigned long frequency; // Interrupt for the event ISR(TIMER1_CAPT_vect) { // Counter to 0 TCNT1 = 0; // The result is valid only if the counter // has not overflown yet if (!(TIFR & (1 << TOV1))) { // Calculating the frequency from the period frequency = (unsigned long)8000000 / (unsigned long)ICR1; } else { // Frequency is less than 122 Hz frequency = 0; // Set the counter's overflow flag to 0 TIFR &= ~(1 << TOV1); } } int main() { // Register a rising front, prescaler value 1 TCCR1B = (1 << ICES1) | (1 << CS10); // Allow event interrupts TIMSK = (1 << TICIE1); // Allow interrupts globally sei(); // Endless loop while (1) continue; }
The program fires an interrupt each time a rising front occurs in the external signal. During the interrupt, the counter is checked for overflows - this can happen if the frequency of the signal is below 122 Hz (8 MHz / 216) and in this case the value of the counter doesn't reflect a real period any more. The frequency is calculated using 32-bit numbers to get the inverse of the period. First thing is to set the counter to 0, because the timer works on the same clock signal as the processor and each instruction execution occurring after the external event shortens the measured period corrupting the result. The maximum measured frequency is limited by the time spent in the interrupt program.
Catching events and registering the time it took for them to occur can also be resolved at the software level. It is possible to use external or other interrupts and read the value of the counter during these events. The hardware-level event catching is meant to run independently form the main program and time relatively short (or frequent) events.
More complex counters can generate a signal, in addition to timing the length of one. For this purpose the counter has an output compare unit and a compare match output unit. The output compare unit has registers with the same bit-width as the counter and the values of these registers are compared to the value of the counter while it is running. An interrupt can be generated and special pins' values can be changed each time the counter's value is equal to the value in the compare unit register. At this moment a pin can either be set high or low or inversed. The signal is generated by changes in the value of the output pin.
In some signal generating modes, the counter's maximum value can be altered. The counter's physical size will remain the same, but a comparison register is used to reset the counter at a specific count. The previous examples could also be solved by using this method, but the function is rather for changing the period of the signal. In addition to that, a counter can be configured to a mode where it works with both incrementing and decrementing.
The counters and the signal generating modes using them are one of the most complex peripheral modules in an AVR. Writing about all of them here would be a huge waste of time and typically there is no need to know everything in order to use them. The following describes one of the most common PWM signals in robotics. The rest can be read from the AVR documentation.
Pulse width modulation (PWM) is a type of signal, where the frequency and period (typically) are both constant, but the length of the half-periods changes. PWM signals are used for controlling electromechanical, optical and other devices. For example, the servo motors known from modeling use a PWM signal of 50 Hz and have a high half-period of 1 to 2 ms.
Example
Task: using an 8MHz ATmega128, generate two speed regulating servo motor signals. Use pin PB5 (OC1A) to generate a pulse width of 1 ms and pin PB6 (OC1B) to generate pulse width of 2 ms.
#include <avr/io.h> int main() { // Set pins as outputs DDRB |= (1 << PIN5) | (1 << PIN6); // Set outputs A and B low for comparison, // "Fast PWM" mode, prescaler value 8 TCCR1A = (1 << COM1A1) | (1 << COM1B1) | (1 << WGM11); TCCR1B = (1 << WGM13) | (1 << WGM12) | (1 << CS11); // Maximum value of the counter. Formula: // TOP = 8 MHz / 8 / 50 Hz ICR1 = 20000; // Half-period of the first motor is 1 ms, and second 2 ms OCR1A = 1000; OCR1B = 2000; // Endless loop while (1) continue; }
USART is a universal synchronous serial interface, UART its simplified version - universal asynchronous serial interface. The difference is, USART uses also a clock signal line to synchronize data, but UART only uses data lines. AVR's USART allows the use of full duplex communication, 5- to 9-bit data words (8-bit word = byte), 1 or 2 stop bits, three parity modes and a wide variety of baud rates. AVR microcontrollers typically have up to 2 USART interfaces, although some may have no USART. Data transmission is performed one word at a time - AVR converts the word it got from the user to bits on the hardware level and transmits it independently and vice versa. The user controls USART by reading and writing configuration, status and data registers.
Every configuration option has corresponding registers, which are quite easy to configure using the datasheet. The baud rate, though, is a bit more difficult to set. Clock signal for data transmission is generated from the controller's own clock and the user can specify a number from 1 to 4096, by which the controller's clock cycle is divided. The result is additionally divided by 2, 8 or 16, depending on the mode. The problem is, not all clock frequencies can be divided so that the result is a standard baud rate. With some frequencies, the baud rate can differ from the standard by up to 10%. AVR datasheets contain tables with typical clock frequencies, baud rates, the needed multiplier to reach that baud rate and the possible calculation error.
Since data transmission takes place independently of the processor and much slower, it is necessary to confirm that the interface is ready for the next word before transmitting. This can be done by keeping an eye on the transmit buffer's ready bit, which signifies if the buffer is ready to accept a new word or not. The controller starts with the ready bit enabled. As soon as a word is transmitted and the buffer is empty, the ready bit is brought high.
The arrival of a word is signified also by a special status bit. In addition to that, there are status bits to signify framing errors, parity errors and receive buffer overflows. Buffer overflow can occur, when the last word is yet to be read from the buffer while a new one arrives - this is why it is always important to read the incoming words to the program as soon as possible, for example by using an interrupt for that. There are three possible interrupt reasons: transmit buffer ready, transmit successful and receive successful.
The transmit and receive buffers are physically separate registers, but share the same memory address and name. When writing to the shared register, the data is stored in the transmit buffer and when reading from it, the data is read from the receive buffer. When using 9-bit words, the 9th bit is transmitted and read using one of the configuration registers.
Example
Task: Configure an 8 MHz ATmega128's USART0 interface to transmit 8-bit words asynchronously using 9600 bps baud rate, 1 stop bit and no parity bits. Send the symbol “X”.
#include <avr/io.h> int main() { // Set baud rate to 9600 bps. Formula: // multiplier = clock frequency / 16 / baud rate - 1 // UBRR = 8000000 / 16 / 9600 - 1 = ~51 UBRR0H = 0; UBRR0L = 51; // Allow transmitting UCSR0B = (1 << TXEN0); // Configure asynchronous mode, set the word size to 8 bits // 1 stop bit, no parity bits. UCSR0C = (1 << UCSZ01) | (1 << UCSZ00); // Wait for the data buffer to empty (previous word to transmit) // In this example there is no need to wait, as the first symbol is yet // to be sent, but it should be done when transmitting more symbols. while (!(UCSR0A & (1 << UDRE))) continue; // Write the symbol to the buffer for transmitting UDR0 = 'X'; // Endless loop while (1) continue; }
Some electronic circuits in practical examples are so common, that they are separately described in the following chapters. It's important to learn them as they ease understanding the examples. They will most probably help building your own circuits too.
Ohm's law states that the current through a conductor between two points is directly proportional to the potential difference or voltage across the two points, and inversely proportional to the resistance between them, provided that the temperature remains constant.
The equation is as follows:
I = U / R
where:
Several other equations are derived from Ohm's law which are used most often in electronics.
If the supply voltage is higher than LEDs voltage the resistor have to be included into the circuit in series. The resistor limits the current and produces the required voltage drop. To find the correct value for the resistor the following equation is derived from Ohm's law:
R = (Uin - Uf) / If
Ur = Uin - Uf
Pr = Ur ⋅ If
where:
The current limiting resistor of a LED has to be not smaller than R and at least with the power of Pr.
Voltage divider is an electrical circuit which divides the input voltage. The output voltage is a portion of an input voltage and the value of the output is dependent of the resistors ratio in the circuit. The schematics on the right includes two resistor.
The output of the circuit is calculated by the following equation:
U2 = U1 ⋅ (R2 / (R1 + R2))
where:
The equation is derived from Ohm's law
where:
I = U1 / (R1 + R2)
and:
U2 = I ⋅ R2
Voltage divider is very often used together with resistive sensors (photo resistor, thermistor etc.) where one of the resistors is replaced by the resistive sensor. In this way the change of the resistance of the sensor is easily transformed to the voltage change which is compatible with microcontrollers analogous input.
Mechatronics and Robotics HomeLab is ATmega128 microcontroller based inter-related set of modules that are completed to a portable case. Various mechatronic and robotic experiments and exercises can be carried out with HomeLab kit, ranging from a simple flashing light to a complex device construction. HomeLab is primarily intended for schools, as it includes methodological materials and exercises with solutions in addition to hardware. HomeLab has been integrated into web environment, which is aimed for students and teachers to boost interaction between each other. In short, the HomeLab kit is a mobile tools suite with the purpose of learning and practicing at home, school or workplace.
HomeLab kit has been developed by Tallinn University of Technology and the Estonian company's cooperation with the European partner universities and Leonardo da Vinci program support. HomeLab modules are bundled into different kits. The basic suite, which teaches simple digital input and output operations, is called the HomeLab Basic kit. Sensors and Actuators Add-on kit contains set of sensors and motors and its drivers. Advanced kit includes both HomeLab Basic kit, Sensors and Actuators Add-on kit, as well as additional modules.
HomeLab Advanced and Basic kit can be successfully used in experiments, as robot base platform and other mechatronics control systems. HomeLab versions are constantly evolving and,therefore every now and then please check whether new exercises have been published or module updates have been released. It is also worthwhile to check the version numbers of modules, some of the exercises and examples can be drawn up in the newer versions. Significant value to HomeLab is added by DistanceLab. DistanceLab allows HomeLab hardware platform to be used through the web.
HomeLab webpage
http://home.roboticlab.eu
DistanceLab webpage
http://distance.roboticlab.eu
Today's engineering studies cannot rely only on one specific teaching method any more - such as classical university lecture-practicum. Today's learner is not satisfay with fixed time lectures and labs. Most young people (and also grown-ups) are living solid information society era life, by spending much of the time on Internet. This means that classical study methods do not confirm the learners way of studing any more. Based on that, the engineering studies needs to be brought into Internet environment and made more attractive, but without losing the quality and practical hands-on experience.
The integrated concept of robotic and mechatronic study incorporates the standard teaching and studying aids and novel approaches, which are integrated into one methodical unit.
The following example can help schools to initiate the robotic studies or give an idea how to apply different studying concepts into practical learning in the technology field.
The concept includes the following teaching and studying aids:
Theoretical study aids are conventional textbooks and exercise books which can be accessed in addition to paper version also electronically.
Practical part consists of Robotic HomeLab and DistanceLab - over the Internet robot programming environment. HomeLab, combined with DistanceLab forms a solid solution, meaning that the learner can practice the single functions of robotics (i.e. sensor readings, motor control etc.) from home and at the same time apply his/her acquired knowledge on the robotic system. The robotic system can be accessed over the DistanceLab solution which consists of real equipment. The system is built up from the same components included in the HomeLab mobile case. This way the user can test each single learned skill immediately on the bigger system. The feedback of the robotic system is provided over the video stream.
The sequence of one study process/labwork is described on the next picture.
The topic begins with introduction which can be a classical lecture, distance online lecture or video lecture. The recorded video lecture is important also on the conventional lecture case as this enables to re-watch the lecture in case of something left unclear or in case the student wasn't able to participate in the lecture. The introduction lecture gives an overview of the problem and along with the teacher the practical example can be gone through supported by teachers comments. The theoretical part is supported by the textbook and hand-on exercise book.
After the introduction lecture the individual practical work is carried out. Practical work is about running the example code solution in the beginning and later modifying the example case according to the first “Warm-up” exercise. This is followed by the more complex exercise which is assigned by the teacher for every person or team individually from “Exercise” pages of each topic. The practical work is carried out with the HomeLab or other robotic kit which is supported by the exercises and DistanceLab. An important role is played here by the Network of Excellence environment which supports individual studies providing the communication environment and help for the learners and teachers.
The results of practical work are concluded in the report and this is sent to the supervisor. If the e-environment supports automatic report uploading, it can be used instead of sending e-mail. In addition to text report the working machine code HEX file is also included. The report includes typically the description of the work and full, clearly commented source code of the solution.
It is well-known that ICT has entered into everyday life for many people and changed the way of studying and communication channels. For many young people it is very likely to get most of the information about the course material from different communication channels on the internet. Before trying to solve a problem, firstly a quick search is done to find possible solution, examples or background information. As the everyday communication is partly moved into several social networks on the Internet it is quite natural that the methodology performed by the schools have to follow the same trend and find the way to reach the students in their everyday environment. Of course it is not the total replacement for the traditional teaching methods, but relying only on those is not enough any more.
Robotics and Mechatronics are fields of the future, where following the newest trends and technologies are especially important. At the same time the fields are very practical and lots of hands-on experience is needed to acquire the skills and knowledge. Even several virtual simulations can be performed for studying the system behavior. However the virtual simulations do not replace the hands-on experience of using the real hardware. The following is a practical guide how to implement a robotic course where different studying approached are applied. The methodology expect that the teacher can use practical HomeLab kit.
Issues to conciser, when starting a new course …..
Optimal number of team members is largely set by limits of using a computer workplace. This means that usually no more than 3 persons fits to work together with one computer workplace. If more persons have to work with the same place they might not be able to participate actively and will lose the focus on the work.
Practical work is divided into labs where every lab has similar structure but different skills are trained. Every lab ends with a report. It is also possible to divide the work within the group, e.g one is responsible for creating the source code another is responsible for writing the report or connecting the hardware. In addition to commented source code the report should also include the algorithm, description of the work, answers to the questions, hardware connection schema, pros and cons of the lab. The last one is a good feedback for the teacher to investigate the level of difficulty of the lab, how interesting the lab was, what was learned etc.
It is important to start the example coding together with a teacher who can comment the background of the functions and solve the example on the screen. Whole process should be written down step-by-step to ensure that all learners are able to follow the process even if they proceed at different speed. One possible lab process structure is described as follows, but can vary according to the specific situation and needs:
While doing lab work in a classroom it often happens that one team is performing slower from the others and thus taking up most of the teachers efforts in follow-up the lesson. Similar case is when one team is getting things done faster from others and gets bored while waiting for a new exercise. Both problems can be minimized if the working process is well defined and the lab guide is provided to the team members. This way the team can perform the work without waiting for the next step from teacher but can simply follow the guide. In addition different levels of exercises are provided in the end of every lab and the teacher can assign the exercises according to the team skills and progress.
If the the ultimate target is to ensure that all team members have acquired the programming skills, the teacher can arrange the team work in a way that all members can perform the programming work and the explanation. Another possibility is for the teacher to pick randomly the member who has to comment the code, even if it is not directly written by this particular member. The final result is assigned for the whole team according to the result of the weakest member. If this kind of evaluation seems unfair an individual reporting inside the team can also be done.
Quite often similar mistakes are done by the learners when handling the hardware or doing the programming work. It is very reasonable to compose the so called frequently asked question FAQ section into the webpage to prevent waisting the learners time in solving some common mistakes and problems. The FAQ along with solutions should be maintained by the HomeLab support website.
One common way to rise the motivation in a robotics studies is to connect the practical work with some sort of competition, where teams have to develop a solution to a given problem which in the end is evaluated together. It is important to publish the measurable criteria (time, speed, distance etc.) to evaluate the final solution. Also secondary criteria can be proposed to evaluate the outlook, inventiveness, cost efficiency etc. of the solution. the task can be also connected to some real-life problem e.g. battery sorter project, security system. Most often the hobby robotic tasks such as mobile robotic for following the line, sumo robots, ball games etc. are taken.
The following example is a typical lab guide which can be used for practical labs.
Lab Nr 3. Sensors and analog-digital converter
Supervisor: Raivo Sell
The Aim
To get familiar with analog-digital converter basics and converting the analog signals by using the AVR ATmega128 8-bit microcontroller. To study different sensors with analog output. To perform a simple analog sensor lab.
Needed equipment
HomeLab Basic kit, Sensor and Motor Add-On kit, AVR programming software.
Working guide
Report
The electronic report has to be submitted after the individual work and needs to consist the following structure.
The report has to contain the name, lab number, date and team members (if it is teamwork). The report has to be short but concise. The quality is evaluated, not the quantity! Be ready to demonstrate the solution or comment the source code. The report has to be uploaded into e-learning systems assignment section. The deadline of the report is one week after starting the lab.
Readings
The modules of Robotic HomeLab are divided into different kits, allowing to select the best option for the needs of a student or a school. The simplest set is HomeLab Basic Kit including the base components to carry out the basic microcontroller experiment. In addition to microcontroller the kit consists of Digital i/o module with LCD display. Together with Basic Kit the Motor and Sensor Add-on Kit can be used, which includes different types of motors and sensors together with accessories. Sensor and motor Add-On Kit cannot be used separately from Basic Kit as microcontroller is not included in the Add-On kit. HomeLab Advanced Kit includes all modules and components from Basic Kit and Add-On Kit and in addition the RFID, Communication and Machine Vision modules. HomeLab Advanced Kit is more targeted to teachers. For the students Basic Kit together with Add-On Kit is much more convenient to use and carry on to home or working place.
Includes all modules and components from Basic Kit and Motor and Sensor Add-On Kit. In addition, following modules are included:
Main module of a HomeLab is a controller development board (controller board) equipped with AVR ATmega128 micrcontroller. In addition to microcontroller, the board consists of several peripherals, voltage stabilizer, connectors etc. The controller board has the following features:
Module is equipped with AC/DC rectifier circuit and LDO voltage stabilizer (with low dropout) - external feeder with voltage stabilization is not needed.
Module can be powered just from step down transformer with output voltage greater than 6 Volts and lower than 15 Volts. Module has wrong-polarization protection circuit (Greatz bridge). POWER LED is signalizing connected feed (“POWER” description on the board).
Circuit can be equipped with external power switch by connecting it to S2 2-pin on/off connector. If external power switch is not used, S2 2-pin on/off connector must be shorted.
All ATmega128 signals are available on three connectors on the edge of the board. Connectors pin assignment is described in the next part of this instruction. It includes full descriptions of ATmega128 pins and their alternative functions. The module is equipped with microprocessor reset circuit (when power on) and reset button for microprocessor restart. Microprocessor can be programmed with ISP or JTAG programmer.
To the seventh pin of port B (named as PB7) is connected the status LED (described as PB7 on the board). This LED can be used as a status indicator of application software. Low state on PB7 pin causes the status LED to be lit.
Module provides two serial ports described on the board as UART1 and UART2. The first one is 9-pin female connector (DB-9 PC serial port compatible) and the second is 3-pin connector. MAX232 circuit provides signal conversion to serial standard.
Multiplexer 74HC4053 provides full use of ports, including those used for microprocessor programming, because in reset state (when programmed) the processor multiplexer switches PE0, PE1 and PB1 signals to ISP and JTAG connector. When processor is in run state these signals are switched to connectors on the edge of the board.
Module is equipped with external memory (Atmel AT45DB041B). It is 4Mbit serial data flash memory connected to Master/Slave SPI serial interface of the microprocessor. Internal Real Time Clock is connected to external 32.768 kHz crystal resonator named as X2 on the board.
Nr | Pin | Alternative function / Description | |
---|---|---|---|
1 | PD7 | T2 | Timer/Counter2 Clock Input |
2 | PD6 | T1 | Timer/Counter1 Clock Input |
3 | PD5 | XCK1 | USART1 External Clock Input/Output |
4 | PD4 | IC1 | Timer/Counter1 Input Capture Trigger |
5 | PD3 | INT3/TXD1 | External Interrupt3 Input or UART1 Transmit Pin |
6 | PD2 | INT2/RXD1 | External Interrupt2 Input or UART1 Receive Pin |
7 | PD1 | INT1/SDA | External Interrupt1 Input or TWI Serial Data |
8 | PD0 | INT0/SCL | External Interrupt0 Input or TWI Serial Clock |
9 | VCC | - | +5V |
10 | GND | - | GND |
11 | PB7 | OC2/OC1C | Output Compare and PWM Output for Timer/Counter2 or Output Compare and PWM Output C for Timer/Counter1 |
12 | PB6 | OC1B | Output Compare and PWM Output B for Timer/Counter1 |
13 | PB5 | OC1A | Output Compare and PWM Output A for Timer/Counter1 |
14 | PB4 | OC0 | Output Compare and PWM Output for Timer/Counter0 |
15 | PB3 | MISO | SPI Bus Master Input/Slave Output |
16 | PB2 | MOSI | SPI Bus Master Output/Slave Input |
17 | PB1 | SCK | SPI Bus Serial Clock |
18 | PB0 | SS | SPI Slave Select Input |
19 | PE7 | INT7/IC3 | External Interrupt 7 Input or Timer/Counter3 Input Capture Trigger |
20 | PE6 | INT6/ T3 | External Interrupt 6 Input or Timer/Counter3 Clock Input |
21 | PE5 | INT5/OC3C | External Interrupt 5 Input or Output Compare and PWM Output C for Timer/Counter3 |
22 | PE4 | INT4/OC3B | External Interrupt4 Input or Output Compare and PWM Output B for Timer/Counter3 |
23 | PE3 | AIN1/OC3A | Analog Comparator Negative Input or Output Compare and PWM Output A for Timer/Counter3 |
24 | PE2 | AIN0/XCK0 | Analog Comparator Positive Input or USART0 external clock input/output |
25 | PE1 | PDO/TXD0 | Programming Data Output or UART0 Transmit Pin |
26 | PE0 | PDI/RXD0 | Programming Data Input or UART0 Receive Pin |
Nr | Pin | Alternative function / Description | |
---|---|---|---|
1 | GND | - | GND |
2 | VCC | - | +5V |
3 | PA0 | AD0 | External memory interface address and data bit 0 |
4 | PA1 | AD1 | External memory interface address and data bit 1 |
5 | PA2 | AD2 | External memory interface address and data bit 2 |
6 | PA3 | AD3 | External memory interface address and data bit 3 |
7 | PA4 | AD4 | External memory interface address and data bit 4 |
8 | PA5 | AD5 | External memory interface address and data bit 5 |
9 | PA6 | AD6 | External memory interface address and data bit 6 |
10 | PA7 | AD7 | External memory interface address and data bit 7 |
11 | - | NC | Not connected |
12 | - | NC | Not connected |
13 | PG2 | ALE | Address Latch Enable to external memory |
14 | - | NC | Not connected |
15 | PC6 | A14 | External memory interface address and data bit 14 |
16 | PC7 | A15 | External memory interface address and data bit 15 |
17 | PC4 | A12 | External memory interface address and data bit 12 |
18 | PC5 | A13 | External memory interface address and data bit 13 |
19 | PC2 | A10 | External memory interface address and data bit 10 |
20 | PC3 | A11 | External memory interface address and data bit 11 |
21 | PC0 | A8 | External memory interface address and data bit 8 |
22 | PC1 | A9 | External memory interface address and data bit 9 |
23 | PG0 | WR | Write strobe to external memory |
24 | PG1 | RD | Read strobe to external memory |
25 | - | NC | Not connected |
26 | - | NC | Not connected |
Nr | Pin | Alternative function / Description | |
---|---|---|---|
1 | VCC | - | +5V |
2 | GND | - | GND |
3 | REF | AREF | Analog reference voltage for ADC |
4 | GND | - | GND |
5 | PF0 | ADC0 | ADC input channel 0 |
6 | GND | - | GND |
7 | PF1 | ADC1 | External memory interface address and data bit 4 |
8 | GND | - | GND |
9 | PF2 | ADC2 | External memory interface address and data bit 6 |
10 | GND | - | GND |
11 | PF3 | ADC3 | Not connected |
12 | GND | - | GND |
13 | PF4 | ADC4/TCK | ADC input channel 4 or JTAG Test ClocK |
14 | GND | - | GND |
15 | PF5 | ADC5/TMS | ADC input channel 5 or JTAG Test Mode Select |
16 | GND | - | GND |
17 | PF6 | ADC6/TDO | ADC input channel 6 or JTAG Test Data Output |
18 | GND | - | GND |
19 | PF7 | ADC7/TDI | ADC input channel 7 or JTAG Test Data Input |
20 | GND | - | GND |
When connecting the Controller module with other peripheral modules and devices, connecting the power supply should be done last. If power is on, it is dangerous to connect and disconnect external devices. Modules have to be connected with each other with great care without applying strong force as this can bend connectors. JTAG-ICE programmer have to be connected to the correct port and the ribbon cable directs out from board (red stripe is by the power supply connector side).
digital i/o module is designed for simple tasks and basic process control. Module has three push-buttons and three LEDs, which can be used as digital inputs and outputs of microcontroller. Additionally to simple LEDs the module is equipped with 7-segment indicator and LCD display outputs. Digital i/o module is handy to use along with other modules enabling to control the output device behavior, like motors and display the sensor readings.
Module features:
Digital i/o module is connected with Controller module by one ribbon cable to port PA/PC/PG, which includes the 8-pin ports PA and PC and 3-pin port PG. The module is also powered by the same cable. If the module is connected correctly the on-board SMD LED +5V_OK has to light up. In other case the cable is probably wrongly connected.
Digital i/o module is equipped with three buttons S1, S2, S3 which are connected to ports PC0, PC1, PC2 accordingly. The other end of buttons are connected through the resistors to ground (logical 0). LED1, LED2 and LED3 on the module are connected to the ports PC3, PC4, PC5 accordingly. The anodes of LEDs are connected to the supply (logical 1).
Digital i/o board is equipped with 7-segment indicator, which is connected with microcontroller ports through the driver A6275. Driver data bus (S-IN) is connected to pin PC6, clock signal (CLK) to the pin PC7 and latch signal (LATCH) to pin PG2.
The module has two different connectors for port PA. The port is connected concurrently to separate pinout groups where one of them is connected through the voltage converter enabling to use 3.3 V devices (like graphical LCD). another port PA pinout is connected directly to microcontroller port (5 V level) and is aligned according to standard 2 x 16 alphanumeric LCD. Two unused ports - PG0 and PG1 are grouped on the board together with ground and supply (starting from module version 3.3). Different devices can be connected to this connector, for example ultrasonic distance sensor. However while connecting the ultrasonic sensor great care has to be paid in connecting power supply plug correctly. In case of mixing up the ground and supply the sensor will be permanently damaged. For ultrasonic sensor, connect the black wire with ground (marked as GND on the board) and red wire with supply. Signal wires have to be connected with PG0 and PG1 ports.
While connecting the module it is suggested to follow the order as described below. Before connecting any cable make sure that the power is removed from Controller board.
LCD module is a combination of connectors of Digital i/o module and two different LCD displays. Digital i/o module can provide two voltage level connections for the LCDs. Alphanumeric LCD which is working on the 5 V level and graphical LCD which is working on the 3.3 V level. The back-light intensity of alphanumeric LCD is regulated with small potentiometer (LCD_BG) on the digital i/o board and graphical LCD back-light can be controlled by the software. Both LCD displays are connected to the port PA but only one at a time.
It is suggested to follow the order below, for connecting the module. Before connecting any cable make sure that the power is removed from Controller board.
Sensors module includes the combined board of sensors, low-pass filter and separate distance sensors.
Sensors module features
Every sensor on the board is connected through the jumper, enabling to select between on-board sensor or external sensor which can be connected to the same port on the CON2 connector. By default all on-board sensors are connected to the analogous inputs of the microcontroller (ADC0-ADC3). In addition to on-board sensors, several analogous external sensors can be connected with sensor board through the connection CON2 to ports ADC4-ADC7. Attention have to be paid to the fact that the ATmega128 is using same ports (ADC4-ADC7) for JTAG programming. Both functions cannot be used simultaneously, the microcontroller can be configured to use just one. Both functions cannot be used simultaneously; the microcontroller can be configured to use just one. However if there is a need to use e the external sensors together with JTAG program, they can be connected to ports ADC0-ADC3, but appropriate jumpers have to be shifted.
Low-pass filter can be composed between analogous inputs 0-3 (PF0-PF3). Resistor and capacitor will be connected to the socket for building up the low-pass filter. By default a low-pass filter is composed for the channel 0 (PF0). Resistor value is 10 kΩ and capacitor value is 100 nF.
While connecting the module it is suggested to follow the order as described below. Before connecting any cable make sure that the power is removed from Controller board.
Motors module incorporates the motors driver board and different electrical motors. Motors board is designed to be compatible with the Controller module but can also successfully be used for other controller boards as it uses standard motor drivers.
Motor power supplies are separated from the control signals on the Motors board and enable to connect all control signals with one ribbon cable
It is possible to drive different types of motors with the module and all these can have separate voltage supply.
Following motors can be connected with Motors board:
motors modules supply connector (PWR):
Pin | Supply connection | Voltage | Current |
---|---|---|---|
1 | DC motors | up to 36 V | up to 600 mA |
2 | Bipolar stepper motor | up to 36 V | up to 600 mA |
3 | Servo motors | 4,8 - 6 V | up to 1 A |
4 | Unipolar stepper motors | up to 50 V | up to 500 mA |
5 | Drivers logic supply (selectable with JP1) | 5 V | |
6 | Ground(GND) |
NB! Applied voltage and current are primary dependent of motors which are used and cannot exceed the limits of specific motor. These limits should be checked from motor datasheet. Motors provided with the kit are most commonly supplied with the voltage 5-6 V and therefore the power distribution cable is using voltage regulators, limiting the output to 5 or 6 V.
Specific model or mark of the motor in a Add-On Kit can vary, but every kit has at least one DC motor, 1 RC servo motor and 1 bipolar or unipolar stepper motor.
Following specific models can be found in the kit:
Motors module board is connected with Controller module with one ribbon cable to the port PE-PB-PD. The power supplies of the motors are connected with separate PWR connector, where every type of motor can have different voltage. Drivers on the board can be supplied externally or directly from Controller module. This is determined by the jumper JP1. If the jumper connects pin 1 and 2 the logic supply is taken from the Controller module. If logic supply is correctly provided the on-board LED +5V have to light up. The Motors board is equipped with UART connector which enables to connect external UART compatible devices directly to board.
DC motors are connected to the connector group DC1. Every pair can handle one motor - all together 4 DC motors. In parallel of connector 3 two encoders ENC1 and ENC2 are connected. In case of using the encoders the DC motor connector 3 cannot be connected with DC motor at the same time (these ports have to be configured as inputs by the software). Motors are controlled with widely used H-bridges, L293D dual H-bridge driver is used for the DC motors. Motors can also be replaced by some other actuator which can be controlled digitally and if the current do not exceed 500 mA (piezo generator, relay etc.)
AVR pin | Control signal | AVR pin | Control signal |
---|---|---|---|
PB4 | Motor 1 1A | PD6 | Motor 3 1A |
PB7 | Motor 1 2A | PD7 | Motor 3 2A |
PD0 | Motor 2 1A | PD4 | Motor 4 1A |
PD1 | Motor 2 2A | PD5 | Motor 4 2A |
Motors board supports two different types of stepper motors. Two unipolar and one bipolar motor can be connected with the board. Bipolar stepper is controlled with dual H-bridge L293D and unipolar stepper is driven by transistor array ULN2803. The patterns of the stepper signals are generated by the software. It is important to check the sequence of the stepper motor windings. The supply wires of unipolar stepper motor are connected to pins 1 and 2. If unipolar stepper has only 5 wires, the pin 1 is left unconnected.
Stepper motor connections:
Pin | Winding wire | Unipolar 1 | Unipolar 2 | Bipolar |
---|---|---|---|---|
1 | 1 | + supply | + supply | |
2 | 2 | + supply | + supply | |
3 | 1a | PE0 | PE4 | PB0 |
4 | 2a | PE1 | PE5 | PB1 |
5 | 1b | PE2 | PE6 | PB2 |
6 | 2b | PE3 | PE7 | PB3 |
RC servo motors are connected to the connector PWM1 and PWM2 on the Motors board. Motors are connected so that the signal wire (usually yellow or white) is on the pin 1 (close to the edge of the board). Two servo motors can be used at the same time. The control signals are connected directly with microcontroller timer outputs.
AVR pin | Control signal |
---|---|
PB5(OC1A) | PWM1 |
PB6(OC1B) | PWM2 |
While connecting the module it is advised to follow the order as described below. Before connecting any cable make sure that the power is removed from Controller board.
HomeLab library is composed of several C language header files (with “.h” extension) and one static library file (with “.a” extension“). On library installation all these files are copied to the AVR-GCC subdirectories where compiler finds them automatically. User does not have to copy these files to his or her program folder.
Step-by-step AVR development software installation instructions for Windows and Linux are written in the first chapter of practical examples. Although different practical examples use different parts of the library, all of them must include static library (”.a“ file) in the project. Only header files can be included selectively. Header files which are directly related to the AVR microcontroller are in the “homelab” folder, HomeLab module specific files are in the “homelab/module” folder. Both these folders are in the compiler's root folder and to include the files from them, less-than and greater-than signs are needed to specify the path. An example of how to include AVR pins and HomeLab motors library header files:
#include <homelab/pin.h> #include <homelab/module/motors.h>
If HomeLab library is not used, then the following avrlibc header file needs to be included into the project:
#include <avr/io.h>
In the HomeLab library, this file is already included in the pin.h file.
HomeLab library installer is freely available on the HomeLab web-page. Users who are interested in customizing the library can also download the source code for it. The following chapters describe the functionality of the library.
Bitwise operations library contains a set of macro functions to do common bit manipulation operations. They are used by the rest of the library and can be used everythere else. As the macro functions have no type, they are compatible with any data type.
Bit index is used to specify the bit in the binary number. Indexes are counted from zero, where zero represents the least significant bit (LSB). For example, 8-bit number have 8 bits with indexes from 0 to 7 and 16-bit number have 16 bits with indexes from 0 to 15.
Bit index to bit mask converting. Parameters:
Sets a specified bit in the variable. Parameters:
Clears a specified bit in the variable. Parameters:
Set a specified bit in the variable to desired state. Parameters:
Inverts a specified bit in the variable. Parameters:
Checks whether a specified bit in the variable is set or not. Parameters:
Checks whether a specified bit in the variable is cleared or not. Parameters:
Setting up a third bit in the 8-bit variable b and inverting of the last one.
#include <homelab/bit.h> int main(void) { unsigned char b = 0x00; bit_set(b, 2); bit_invert(b, 7); }
The following is a shortened version of the bitwise operations library source code.
// // Functions for handling bits. // #define bit_mask(bit) (1 << (bit)) #define bit_set(value, bit) value |= bit_mask(bit) #define bit_clear(value, bit) value &= ~bit_mask(bit) #define bit_invert(value, bit) value ^= bit_mask(bit) #define bit_is_set(value, bit) ((value) & (bit_mask(bit))) #define bit_is_clear(value, bit) (!((value) & (bit_mask(bit)))) #define bit_set_to(v, b, x) v = ((x) ? (v | bit_mask(b)) : (v & ~bit_mask(b))) // // Functions for handling bit masks. // #define bitmask_set(value, bitMask) value |= (bitMask) #define bitmask_clear(value, bitMask) value &= ~(bitMask) #define bitmask_invert(value, bitMask) value ^= (bitMask) #define bitmask_set_to(v, m, x) v = ((x) ? (v | (m)) : (v & ~(m))) #define bitmask_is_set(value, bitMask) ((value) & (bitMask))
Pins library provides an easy way for operating with AVR digital input-output pins. User can create a pin related variable and do all the pin operations with that variable. This way there's no need to deal with the register names and bit indexes like it is done while programming in direct register access method. Pin's port and index must be specified only once, so the changes are easy to implement.
Configures pin as an output. Parameters:
Configures pin as an input without pull-up resistor. Parameters:
Configures pin as an input with pull-up resistor. Parameters:
Sets output pin high. Parameters:
Sets output pin low. Parameters:
Inverts output pin state. Parameters:
Sets output pin to desired state. Parameters:
Gets pin value. Parameters:
Reads pin value through the switch debounce filter. Filtering takes at least 8 ms and may last up to 100 ms, depending of when the bouncing ends. If the bouncing does not end, false is returned. Function use software delay. Parameters:
Example of getting and setting a pin's value. Pin PC0 value is inverted and attached to pin PC3.
#include <homelab/pin.h> pin output_pin = PIN(C, 3); pin input_pin = PIN(C, 0); int main(void) { bool value; // Configuring pin as an output pin pin_setup_output(output_pin); // Configuring pin as an input pin with pull-up pin_setup_input_with_pullup(input_pin); // Endless loop while (true) { // Getting an input pin value value = pin_get_value(input_pin); // Setting an output pin value pin_set_to(output_pin, !value); } }
This library provides functions to use AVR analog to digital converter. All the conversion functions in library are blocking, which means the processor waits as long as it takes to get the results. The conversion time depends on the ADC clock.
Initializes ADC. Parameters:
Converts specified ADC channel analog value to digital. Function is blocking. Parameter:
Converts specified ADC channel analog value to digital desired number of times and calculates the average. Function is blockin. Parameters:
For example ADC is initialized and two analog channel values are converted to digital. Value of channel 0 is assigned to variable x and averaged value of channel 1 to variable y.
#include <homelab/adc.h> int main(void) { unsigned short x, y; // Initializing ADC. Reference voltage from AVCC. // Clock is 8 times slower than system clock. adc_init(ADC_REF_AVCC, ADC_PRESCALE_8); // Converting channel 0 value. x = adc_get_value(0); // Converting and averaging channel 1 value. y = adc_get_average_value(1, 10); }
This library provides AVR asynchronous serial interface usage functions.
Macro function to calculate USART baud rate register value in asynchronous mode. Parameters:
Initializes asynchronous USART. Parameters:
Blocking character transmission. Functions wait until transmit buffer empties before writing a character to the buffer. Parameters:
Blocking string transmission. Parameters:
Checks for data in receive buffer. Parameters:
Reads a character from receive buffer. Before reading user must check if there actually is a received character. Parameters:
Reads a character from receive buffer if there is any. Parameters:
USART interface is configured to use 8 data bits, one stop bit, 9600 bps baud rate and no parity mode. Program sends a string, waits until some character is received and then reads it out.
#include <homelab/usart.h> // Use USART interface 0. usart port = USART(0); int main(void) { char c; // Interface initialization. usart_init_async(port, USART_DATABITS_8, USART_STOPBITS_ONE, USART_PARITY_NONE, USART_BAUDRATE_ASYNC(9600)); // String sending. usart_send_string(port, "Hello\n"); // Waiting for incoming data. while (!usart_has_data(port)) {} // Read out the received character. c = usart_read_char(port); }
This library covers large part of the functionality of ATmega128 timers. There are data types and functions which make usage of timers easier. Unfortunately, because of the complexity of AVR timers, there are no common functions to use different timers. Each of the timer has functions which starts with the prefix of “timer” and its index.
Initializes timer 0 in normal mode. In this mode timer counts from 0 to 255 (including). Overflow interrupt can be used. Parameters:
Initializes timer 2 in normal mode. In this mode timer counts from 0 to 255 (including). Overflow interrupt can be used. Parameters:
Stops timer 0/2.
Returns timer 0/2 current value. Parameters:
Sets timer 0/2 value. Parameters:
Enables or disables timer 0/2 overflow interrupt. The name of the interrupt vector is “TIMERn_OVF_vect” where “n” represents 0 or 2. Parameters:
Checks timer 0/2 overflow flag. Parameters:
Resets timer 0/2 overflow flag.
Initializes timer 1/3 in normal mode. In this mode timer counts from 0 to 65535 (including). Overflow interrupt can be used. Parameters:
Initializes timer 1/3 in CTC (Clear Timer on Compare Match) mode. In this mode timer counts to specified top value. Overflow interrupt can be used. Parameters:
Initializises timer 1/3 in fast PWM mode. In this mode timer counts to a specified value, which also determines the period of the PWM signal. Timer 1/3 has three 3 output compare units (A, B and C) to generate PWM signals. Overflow and compare match interrupts can be used. Parameters:
Stops timer 1/3.
Returns timer 1/3 current value. Parameters:
Sets timer 0/2 value. Parameters:
Returns timer 1/3 output compare unit A/B/C compare match register value. Parameters:
Sets timer 1/3 output compare unit A/B/C compare match register value. Parameters:
Returns timer 1/3 input capture register value. Parameters:
Sets timer 1/3 input capture register value. Parameters:
Enables or disables timer 1/3 overflow interrupt. The name of the interrupt vector is “TIMERn_OVF_vect” where “n” represents 1 või 3. Parameters:
Enables or disables timer 1/3 output compare unit A/B/C compare match interrupt. The name of the interrupt vector is “TIMERn_COMPx_vect” where “n” represents 1 or 3 and “x” represents A, B or C. Parameters:
Enables or disables timer 1/3 input capture interrupt. The name of the interrupt vector is “TIMERn_CAPT_vect”, where “n” represents 1 or 3. Parameters:
Checks timer 1/3 overflow flag. Parameters:
Checks timer 1/3 input capture flag. Parameters:
Resets timer 1/3 overflow flag.
Resets timer 1/3 input capture flag.
In the following program timer 0 is started in normal mode with overflow interrupt.
#include <homelab/timer.h> #include <avr/interrupt.h> // Overflow interrupt program. ISR(TIMER0_OVF_vect) { } int main(void) { // Initializing of timer 0 in normal mode. timer0_init_normal(TIMER0_PRESCALE_32); // Enabling timer 0 overflow interrupt. timer0_overflow_interrupt_enable(true); // Global interrupts enabling. sei(); }
This part of library contains functions to generate delays in program with software algorithms or with hardware timers. Delays do not block interrupts therefore software delays are interfered by them. Delay functions are not pre-compiled to be compatible with different clock frequencies.
Software delay in milliseconds. Function expects the usage of some compiler optimization mode. Parameters:
Software delay in microseconds. Function expects the usage of some compiler optimization mode. Parameters:
Hardware timer based delay in milliseconds. Functions use ATmega128 8-bit timer 0. Depending of the clock frequency, up to several milliseconds delay error may occur. Parameters:
Demonstration of both types of delays.
#include <homelab/delay.h> int main(void) { // Software base delay of 100 ms. sw_delay_ms(100); // Hardware timer based delay of 100 ms. hw_delay_ms(100); }
Related to: [HW] User Interface Module
This library is intended to use 7-segment LED display on HomeLab Digital i/o module board. Numbers from 0 to 9 can be displayed with library.
Configures display driver control pins.
Displayes a digit on display. Parameters:
Number 5 is showed on LED display.
#include <homelab/module/segment_display.h> int main(void) { // Display initialization. segment_display_init(); // Digit displaying. segment_display_write(5); }
Related to: [HW] lcd
This library contains functions to use HomeLab alphanumeric LCD.
Initializes LCD. Parameters:
Clears the whole display. Cursor is moved to the beginning of the first line.
Clears a line on display. Cursor is moved to the beginning of cleared line. Parameters:
Moves cursor to the beginning of the first line.
Moves cursor to the desired position. Parameters.
Writes a character to the cursor position. Parameters:
Writes a string to the display starting from the position of the cursor. Parameters:
Writes a string from program memory to the display starting from the position of the cursor. Parameters
Demonstration of how to use alphanumeric LCD to display text.
#include <homelab/module/lcd_alpha.h> int main(void) { // LCD initialization. lcd_alpha_init(LCD_ALPHA_DISP_ON); // Display clearing. lcd_alpha_clear(); // Cursor to the beginning of second line. lcd_alpha_goto_xy(0, 1); // Text displaying. lcd_alpha_write_string("Hello"); }
Related to: [HW] lcd
This library contains functions to use HomeLab graphical LCD. Although LCD is capable of displaying graphical images no such functions are implemented in the library. LCD is used as a 14 x 6 character alphanumeric display.
Intializes LCD.
Switch LCD backlight on or off. Parameters:
Clears a whole display.
Clears a single text line. Parameters:
Selects a position to write text to. Parameters:
Writes a character to the pre-defined position. Parameters:
Writes a string to the display starting from the pre-defined position. Parameters:
Demonstration of using a graphical LCD.
#include <homelab/module/lcd_gfx.h> int main(void) { // LCD initialization. lcd_gfx_init(); // Display clearing. lcd_gfx_clear(); // Cursor to the middle of the screen. lcd_gfx_goto_char_xy(5, 2); // Displaying a text. lcd_gfx_write_string("Hello"); }
Related to: [HW] Motor Module
This library contains functions to control different HomeLab motors. There are functions for DC, stepper and servo motors.
Initializes one of the DC motor controllers. Parameters:
Drives one of the DC motor controllers. Parameters:
Initializes one of the unipolar stepper motor controllers. Parameters:
Unipolar stepper motor half-stepping command. Functions is blocking as it is fulfilled as long as steps are done. Parameters:
Initializes bipolar stepper motor controller.
Bipolar stepper motor half-stepping command. Functions is blocking as it is fulfilled as long as steps are done. Parameters:
Initializes one of a servo motor PWM signal generations units in ATmega128 timer 1. PWM signal is 50 hz with high period of 1.5 ms ± 0.5 ms. Parameters:
Servo motor pulse width control command. If positioning servo motor is driven, its position is altered, if rotating one, its rotation speed is altered. Parameters:
The following program demonstrates the usage of DC, stepper and servo motors.
#include <homelab/module/motors.h> int main(void) { // DC motors initialization. dcmotor_init(0); dcmotor_init(1); // Bipolar stepper initialization. bipolar_init(); // Servo motors initialization. servomotor_init(0); servomotor_init(1); // One DC motors drives forward, another in backward direction. dcmotor_drive(0, -1); dcmotor_drive(1, +1); // Rotating the stepper motor 100 steps in one direction // and then back with twice the speed. bipolar_halfstep(1, 100, 50); bipolar_halfstep(-1, 100, 25); // Rotating servo motors in opposite directions. servomotor_position(0, -100); servomotor_position(1, +100); }
Related to: [HW] Sensors Module
This library contains functions to use different sensors in HomeLab kit.
Infrared distance sensor distance calculation parameters structure. Formula for distance calculation is a / (ADC + b) - k. Structure members:
Sharp GP2Y0A21YK distance calculation formula parameters.
Calculates thermistor temperature in Celsius degrees from ADC conversion result. Functions use pre-calculated conversion table. Parameters:
Calculates distance from ADC result received from IR distance sensor voltage. Parameters:
Measures distance with ultrasonic distance sensor. Functions generate a trigger pulse on one pin and measures the time of echo pulse on another pin. Distance is calculated from the time. Function expects a 14.7456 MHz clock frequency. Measuring may take up to 36 ms. Parameters:
The following program demonstrates usage of IR and ultrasonic distance sensors.
#include <homelab/module/sensors.h> // Ultrasonic distance sensor control pins. pin pin_trigger = PIN(G, 1); pin pin_echo = PIN(G, 0); int main(void) { unsigned short adc_value = 400; // random ADC result. signed short distance; // Distance calculation from IR distance sensor ADC result. distance = ir_distance_calculate_cm(GP2Y0A21YK, adc_value); // Measuring with ultrasonic distance sensor. distance = ultrasonic_measure(pin_trigger, pin_echo); }
Practical examples are built up in uniform style and as concrete as possible. Each example begins with a short introduction of the theory and gives necessary knowledges for completing the following practical part. Practical part contains commented example of the source code of the software. Generally the library of the HomeLab is used in the example of the code, but in some cases the registers are also configured. The first chapter is a little bit different with a bit different goal. It describes installation and set-up of necessary software. The examples are about Windows and Linux operation systems. Chapters and practical examples after the first chapter are suitable for both OS and development software.
Buildup
There is a list of necessary knowledges to complete the example at each chapter of practical examples. In this list are references to other chapters of the book, which are marked with icons as follows:
Basic software of the examples
As mentioned before, practical code samples are composed on the basis of the library of the HomeLab. There are most used specific AVR operations and procedures connected with the HomeLab’s modules written in the library. The usage of the library means that in order to start the modules the user does not have to write a hardware-like, program code which uses registers, although this opportunity always exists. Separating the software which communicates with hardware from the examples and exercises, allows the user to focus on creating his/hers own algorithm instead of peculiarities not connected to the hardware.
Code style of the examples
The example programs are written in one style, to achieve more synoptic program code. Uniform style makes the program more readable and prevents making simple syntax mistakes. It is advised to follow a certain style when making exercises. The main characteristics of the style:
Environment for writing the code, compiler for corresponding language and software for uploading written program to the controller are needed in order to program AVR controller. The most comfortable method would be using special integrated developing environment (IDE). The AVR microcontroller can be programmed in many different programming languages: assembler, C, C+, Pascal, Basic, etc. This book is focusing on using the C-language when programming the microcontroller. There is free software for programming AVR microcontroller for both the Windows OS and the Linux OS. Next chapters are introducing the usage of both.
All software for passing the exercises can be found from the homepage of the HomeLab
this section contains the information necessary to install AVR development software on Windows operating system.
You need the following software which can be downloaded from manufacturers homepage or acquired from the CD supplied with the HomeLab kit.
1. AVR Studio
AVR Studio 4 IDE (Integrated Development Environment) is a base software for writing the source code, compiling it and uploading the program to the controller. AVR Studio can be downloaded from Atmel homepage.
2. WinAVR
WinAVR is a GNU-GCC compiler for AVR microcontrollers. This is a freeware which can be downloaded from Sourceforge webpage. During the installation process WinAVR offers a folder name containing long version number. We suggest to cut the version number from folder name and install it just like shown below:
C:\WinAVR
3. HomeLab library
HomeLab library is a set of functions designed for HomeLab kit and AVR controllers. By using the library the programming is much easier and effective. Latest version of the library can be downloaded from the HomeLab webpage. This library should be installed in the same folder where WinAVR was installed.
4.. Virtual COM port driver
Driver enables to connect USB JTAG-ICE programmer with your PC. Driver have to be installed before connecting the programmer. Driver file name is “CDM x.xx.xx.exe”, where “x” denotes the version number. After installing the driver you can connect the JTAG-ICE programmer through the USB port and let Windows to detect the device automatically. You should see the notice on the windows task bar.
According to number of virtual ports previously defined on the particular computer the windows assigns next port number for the new device. New COM port name (range number) is generated if the device is plugged in to the same computer using another USB port. Some AVR Studio versions can not use the programmer if the COM port number exceeds number 10 and number 4 in case of using it as debugger. To prevent this situation you can modify the assigned port number in Device manager and assign it to the range COM0…COM4. See the complete procedure here .
In order to write a program for the controller you need to create the project space. The project includes typically different files like source codes, header files, compiled program files, etc. It is strongly advised to create new folder for every project (which is offered also by the New Project Wizard).
Following steps have to be completed when creating new project with the help of wizard.
1. Open AVR Studio and press New Project. If the dialog box is not opened automatically select Project - New project from the menu bar. Press Next.
2. Next dialog box is about compiler and initial settings. Select AVR GCC compiler. On the left, insert the name of the project and main source file name. The source file name should have extension ”.c“. Two check boxes should be also checked, which will create the new folder and initial file. You should also show the folder where project files will be created. After proper selections press Next.
NB! If AVR GCC is missing on the compiler list, it is not properly installed. In that case the WinAVR software have to be installed before starting to write C source code.
3. On the next dialog box you have to select the debugger platform and microcontroller. HomeLab kit is using JTAG ICE as debugger platform and ATmega128 as microcontroller. After selections press Finish.
4. Now the project space is created and new window will open where you can start to write the program source code.
5. You need to set some project parameters before the first compilation. The important parameters are controller frequency and optimization method. HomeLab controller has frequency 14,7456 MHz (14745600 Hz). The frequency can be set in the project properties with Hz (not MHz): Project → Configuration Options → General. Optimization method should left -Os, if there is no need for other methods.
6. For using HomeLab library functions the software have to be properly installed. Every new project requires adding library to list of linked objects. To do that go to Project → Configuration Options → Libraries and add object “libhomelab.a”.
If object libhomelab.a is missing from the left list the library is not properly installed to the system and it should be reinstalled.
After set up of the development environment it is wise to test it, for ensuring its correctness. Simplest way is to write a short program, compile it and upload to controller.
1. Connect the programmer with ATmega128 board. Be sure that the programmer is correctly connected to JTAG connector (cable is directed away from controller board- see next picture). Connect the controller board supply (small green LED should light up if correct power supply is connected).
Insert simple C source code:
#include <avr/io.h> #include <homelab/delay.h> int main(void) { // Pin PB7 to output DDRB = 0x80; // Endless cycle while (true) { // Pin PB7 invertion PORTB ^= 0x80; hw_delay_ms(500); } }
Compile the project with Build command (keyboard F7). Make sure that the compilation succeeded. For this you should see the following message on the message window.
Build succeeded with 0 Warnings...
2. Open the controller window Tools → Program AVR → Auto Connect. Be sure that the tab Program is open.
If the described window does not open and Connection Failed window is opened instead you do not have proper connection with the board or programmer. First check that micrcontroller is correctly powered (green LED is on) and the programmer is properly connected to JTAG connector. If this is OK check the COM port number which is assigned by the Windows. If this is greater than 9, the AVR Studio can not recognize the programmer. Follow the instructions given in the beginning of the chapter and assign the port number between 0 and 4.
3. On the programmer window insert into Flash-section textbox Input HEX File the location of the compiled program by pressing the ”…“ button. Compiled program is usually located in the project folders sub folder default and has same name as the project but with the extension ”.hex“, for example “labor1.hex”. After selecting correct file press button Program which uploads the program to the controller. If all went well you should see the following message on the end of the window:
OK Reading FLASH input file.. OK Setting device parameters for jtag programming ..OK Entering programming mode.. OK Erasing device.. OK Programming FLASH .. OK Reading FLASH .. OK FLASH contents is equal to file.. OK Leaving programming mode.. OK
According to the program the on-board LED (PB7) should start flashing. If the program works you have successfully set up your programming environment and completed your first program. Congratulations!
Debugging a program means searching errors from the program. For that programs called debuggers are created, they allow to execute the program step by step and stopping it where it is needed. Such implementation of the program allows checking the values of the variables at any phase of the program, contents of the registers and the sequence of executing the program. Debugging is especially important while dealing with more complex programs where it is often difficult to find errors. With microcontrollers, it is important that step-by-step implementation of program is done in the controller, which allows seeing change of real outputs in addition to the values of the registers. Two conditions must be met for using a debugger: microcontroller must support debugging and you must have necessary hardware – JTAG programmer which allows debugging. Cheaper programmers using ISP programming interface may upload compiled program into the controller but not necessarily allow debugging.
To start the program in debugging mode with the AVR Studio, firstly it should be compiled by pressing button build (F7) and the compiled program started with the command Run (F5). Before that break points (F9) can be added to selected palces in the source code. When implementation of the program reaches the break point, the program is stopped for determining the state of the microcontroller in that point. Implementation of the program may be continued with command Run again or use Step Into (F11) for implementing the program one command at the time.
Some times in AVR program it is necessary to use floating-point variables. For calculating with them and presenting with printf-type functions, the following set-up changes must be done to the configuration of the project:
1. Open the set-up of the project from the menu Project → Configuration Options. Add libprintf_flt.a and libm.a after libhomelab.a of the library of HomeLab in the configuration tab Libraries.
2. Next, open tab Custom Options and chose [All files]. Next add lines with ”-lprintf_flt“ ja ”-lm“ to the box on the right and line with ”-uvfprintf“ to the [Linker Options] section.
3. Press OK and close configuration window.
The following guide describes the installation and use of the AVR toolkit 9:10 Ubuntu operating system.
Install the following software:
1. Linux software packages
To install the packages, use the terminal command:
sudo apt-get install gcc-avr avrdude avr-libc
or graphical installation software (such as Ubuntu software center or Synaptic packet manager).
2. HomeLab library
The library simplifies the writing of program code, since lower-level functions are ready-made. Visit the HomeLab website and download the file named Homelab library vX.X.run, where XX represents the version number. Run the following command to install the library:
sudo sh homelab_library_vX.X.run
Make sure that your library installation is successful.
3. KontrollerLab
KontrollerLab is an IDE (integrated development environment) for writing, compiling, loading and debugging AVR software. Download the KontrollerLab software and save it to some folder (for example, ~/Documents/AVR/KontrollerLab). Run the following command in this folder:
sudo dpkg -i kontrollerlab*.deb
If you have problems with packet dependencies, run the command which installs the missing packages:
apt-get install –f
Connect to programmer with computer and see if the computer recognizes it. Write the command “lsusb” in the terminal window which should show the list of connected USB devices. Programmers name is “Future Technology Devices International, Ltd FT 232 USB-Serial (UART) IC”.
To get the port to which the programmer is connected to, check the /dev folder using the commands cd /dev (sets /dev as a current folder) and dir (displays the content of the folder). As the programmer is USB-serial interface, it is named as a “ttyUSBx” where “x” marks the index of the inteface. If there are no other USB-serial devices this number is zero.
To write an AVR program, a project must be created which contains all the necessary files: source codes, header files, compiled program, etc. To clearly seperate projects, a folder for each project should be made (this option is also given by the project wizard).
To create a project follow the steps:
1. Open KontrollerLab (Applications → Programming → KontrollerLab) and start a new project from the File → New → New project menu . A window opens where the project location must be specified. In this example the locations is ../Homelab/blinkingLED/.
2. The project must have at least one C file where to write the program code. Files can be added from the File → New → New menu. In the opening window, select C source as a file type and specify the name of the file.
3. Next, the project must be configured for HomeLab kit. From the Project → Configure Project menu open the configurations window and select Common tab. CPU type should be ATmega128 and the clock should be 14745600,0 Hz. Also the names of the HEX and MAP files should be specified. Pressing a Set as default button makes these configurations as a default for new projects. This is reasonable when working only with HomeLab AVR microcontroller. As the HEX and MAP files are also stored as a default it is recommended to use some general name for them, for example “out.hex”.
NB! As the HomeLab library can not be added on the Linker configuration tab, the command ”-lhomelab“ must be added behind the name of the MAP file.
Apply settings on the Compiler tab as shown on the following screenshot. Before pressing OK set compiler configurations also as a default.
4. Configure the programmer by opening the configurations window from the Project → Configure Programmer menu. Choose “AVRDUDE” as a programmer. On the “AVRDUDE” tab set the programmer type to jtagmkI and connection port to /dev/ttyUSBx (where “x” is the port index). Set these configuration settings also as a default.
5. At last, place the windows in the KontrollerLab as it is more convenient and get ready to write your first program.
After the configurations steps are done, it is time to programm.
1. Connect the programmer with Controller module. Double-check if the programmer is correctly connected with a JTAG socket. The cable must head away from the board (look at the picture). Now connect the power supply and check if the green LED lights on the Controller board.
2. Write the following program in the Kontrollerlab file editor window and compile it:
#include <avr/io.h> #include <homelab/delay.h> int main(void) { // Set pin PB7 as an output DDRB = 0x80; // Lõputu tsükkel while (true) { // Inverting pin PB7 PORTB ^= 0x80; hw_delay_ms(500); } }
Make sure that the output window shows “File compiled successfully” message. If the “Error(s) occurred: ” message is shown, check the program code for mistakes.
3. To download the program into the Controller press the Ignite button. After the succeful download, the “Project built and uploaded successfully” message should be shown.
If everything was done correctly the red LED on the Controller board should blink periodically, with 1 second interval.
Sometimes it is neccessary to use floating-point numbers in program. To calculate with them and use them with printf-typed functions, some modifications in project configuration must be done:
1. Open the Project Configurations window and the Linker tab. Check the first line in the Linker flags list (look at the picture).
2. Press OK and close the window.
Following chapters are introducing digital input/output which are basic functions of microcontroller. Input/output pins are microcontroller contacts. They are so called “legs”. Digital signal is received and transmitted through them. If pin is configured as input, the status of switches or simple sensors connected to pin can be received. When pin is configured as output, it can be used to light LEDs or control electrical equipment.
Almost all typical microcontroller pins allow executing easier input-output functions, although often these pins have alternative functions. Before addressing the alternative functions, witch are covered in separate chapters, it would be useful to get to know the pins by studying their basic functions.
Necessary knowledge: [HW] Controller module, [HW] User Interface Module, [ELC] LED Resistor Calculation, [AVR] Registers, [AVR] Digital Inputs/Outputs, [LIB] Pins
Light-emitting diode is a semiconductor which emits light when forward voltage is applied. The acronym for light-emitting diode is LED. There are different color combination of diodes and the diodes, which can also emit white light. Like a normal diode, the LED has two contacts – anode and cathode. On drawings anode is marked as “+” and cathode as “-“.
When forward voltage is applied, LED’s anode is connected to the positive voltage and cathode to the negative voltage. The voltage of LED depends on LED’s color: longer wavelength (red) ~2 V, shorter wavelength (blue) ~3 V. Usually the power of LED is no more than couple of dozens milliwatts, which means electrical current must be in the same range. When applying greater voltage or current LED may burn out.
If the LEDs are used specially to illuminate, it is wise to use special electronic circuits witch would regulate current and voltage suited for LEDs. However LEDs are quite often used as indicators and they are supplied directly from microcontroller’s pins. Since the supply voltage for microcontrollers is usually higher than the voltage for LEDs, there must be a resistor connected into series with the LED, which limits current and creates the necessary voltage drop. Instructions to calculate proper resistor can be found from electronics chapter.
LEDs are produced by a variety of casings. Most common LEDs with feet have 3 mm or 5 mm diameter round shell and two long metal connector pins. Longer pin stands for anode, shorter is cathode. Surface mounted casing LEDs (SMD – Surface Mounted Device) have a T shaped symbol on the bottom to indicate the polarity, where the roof of T stands for the location of anode and pole marks cathode.
HomeLab Controller control module has one single red LED, witch anode is connected through resistor to +5 V power supply and cathode is connected to ATmega128 pin number PB7. In order to switch on and off this LED, PB7 should be defined as output pin and set low or high accordingly. Which means if pin is set high, LED is turned off and if pin is set low LED is turned on. Basically it would be possible to connect LED also so that anode is connected to the pin of microcontroller and cathode is connected to the earth (somewhere has to be a resistor too) – in that case when the pin is set as high, LED is shining and when the pin is set as low LED is switched off.
All practical examples for the HomeLab kit, LED switching included, are using HomeLab’s pin’s library. Pins’ library includes data type pin, witch contains addresses of pin related registers and pin’s bitmask. If to create a pin type variable in the program and then initialize it by using macro function PIN, the pin can be used freely with this variable (pin) through whole program without being able to use registers. Here are 2 example programs, which are doing exactly the same thing, but one is created on the basis of HomeLab’s library, the other is not.
// // HomeLab Controller module LED test program. // The code is base on HomeLab library. // #include <homelab/pin.h> // // LED pin configuration. // pin debug_led = PIN(B, 7); // // Main program // int main(void) { // Configuring LED pin as an output pin_setup_output(debug_led); // Lighting up LED pin_clear(debug_led); }
// // HomeLab Controller module LED test program. // The code directly accesses registers. // #include <avr/io.h> // // Main program // int main(void) { // Configuring LED pin as an output DDRB |= (1 << 7); // Lighting up LED PORTB &= ~(1 << 7); }
First example uses pins’ library (pin.h file). First a pin-type variable named debug led is created in the program, which holds information about LED pin. In the main program this pin will be set as output by using pin_setup_output function. After that the pin is set as low by function pin_clear. As the result LED will glow. In the second example variables are not used, setting LED output and lighting it will be done by changing port B data direction and output registers values. The reader who knows more about AVR notices, that in both examples there is no need to give command to light LED, because the default output value of the AVR is 0 anyway, but here it is done by the means of correctness.
What is the difference between the use of the library and the registers? The difference is in the comfort – library is easier, because you do not need to know the registers’ names and their effects. Most important benefit of library is adaptability. Using registers, you must change registers’ names and bitmasks through entire program in order to change pin. When using library, it must be done only in the beginning of the program where pin variable is initialized. Using registers has one deceptive advantage – usage of pin is direct and it is not done through program memory and time consuming functions. However, newer AVR-GCC compiler versions are so smart that they transform library’s functions to exactly same direct commands for manipulating registers like it would have been done directly in program. Must be said that compilers can optimize the code only when it deals with constant single variables not with volatile variables that are changing during work and with arrays.
The next program code is partial pin operations library. Its purpose is to explain the procedures with pin variables. It might not be understandable for the beginners as it uses C language pointers which are not covered in this book, but a lot of materials about pointers can be found from books and internet.
// Macro constant for defining register pointer #define _REG_PTR_ volatile uint8_t * // // Pin data type // typedef struct pin { _REG_PTR_ ddr; _REG_PTR_ port; _REG_PTR_ pin; uint8_t mask; } pin; // // Macro function for pin variable initializing // #define PIN(port_char, bit_index) \ { \ (_REG_PTR_)&DDR ## port_char, \ (_REG_PTR_)&PORT ## port_char, \ (_REG_PTR_)&PIN ## port_char, \ bit_mask(bit_index) \ } // // Configuring pin as output // inline void pin_setup_output(pin pin) { bitmask_set(*pin.ddr, pin.mask); } // // Setting pin high // inline void pin_set(pin pin) { bitmask_set(*pin.port, pin.mask); } // // Setting pin low // inline void pin_clear(pin pin) { bitmask_clear(*pin.port, pin.mask); }
In addition to the Controller module, LEDs are also located on the Digital i/o module board. They are connected electrically in the same way as Controller module’s LED, which means cathode is connected to the AVR pin. Red LED is connected to the pin PC5, yellow to PC4 and green to PC3. Their HomeLab library based example program looks as follows:
// // HomeLab Digital i/o module LED test program. // #include <homelab/pin.h> // // LED's pin configuration. // pin led_red = PIN(C, 5); pin led_yellow = PIN(C, 4); pin led_green = PIN(C, 3); // // Main program // int main(void) { // Configuring LED pins as an output pin_setup_output(led_red); pin_setup_output(led_yellow); pin_setup_output(led_green); // Lighting up green LED pin_clear(led_green); // Turn off red and yellow LED pin_set(led_red); pin_set(led_yellow); }
~~DISCUSSION~~
Neccesary knowledge: [HW] User Interface Module, [AVR] Registers, [AVR] Digital Inputs/Outputs, [LIB] Pins, [PRT] Light-emitting Diode
A switch is an electromagnetic device which can connect or break an electrical circuit. There are many different types of switches; the most common mechanical switch is mechanically connectible electrical connectors. If connectors are connected, electrical circuit is closed and electricity can flow through the switch. If connectors are disconnected, electricity cannot flow through the switch.
Switches are commonly used to electrify electrical circuits, but can also be used as sensors. Switch as a sensor is also the subject of this exercise and therefore examining specific high-voltage and high-amperage switches has been excluded. Switches can be differentiated by the number of contacts and by their connection method. Different types include: two contact switches and double switches, where contact pairs are connected, but also push button, sliding, rocker and toggle switches as well as switches which disconnect instead of connect electrical circuit.
Different schematic symbols are used for identifying different types of switches. Below are examples of typical electrical switches used in electrical drawings with their symbols:
In order to use a switch as a sensor connected to a microcontroller, one contact of the switch is connected to the microcontrollers pin and is set as input in the program. If contact is connected to the ground or input potential, the bus’s bit rate of microcontroller’s pin is changed. It would sound logical to use toggle switch, which allows connecting one contact with desired contact (in this case ground or input). For our purpose it is not as simple, since on the moment of shifting of the connections the contacts are not connected. The moment itself is very short (milliseconds), but during this moment microcontrollers input pin is connected to nowhere and therefore has indefinite value. Due to electromagnetic interference (which exists everywhere) input pin that is connected to nowhere can have a value of 0 or 1 at random moments in time.
The interferences complicate the usage of switches. Most common method for avoiding the undetermined results is to connect microcontroller’s input to the ground or input potential through a resistor. A resistor for this function is called pull-down or pull-up resistor. Usually the resistance of pull-down or pull-up resistor varies from 1 kΩ to 1 MΩ. With open switch, the input is charged with voltage of the resistor, by closing the switch, the input receives the voltage of the switch since the resistivity of the switch is a lot smaller (close to zero) compared to the one of the resistor. Basically it can be referred to as a volt-box.
A simple two contact switch can be used as a sensor with pull-up or pull-down resistor, switch connects input with one and resistor to the other potential. Usually microcontrollers have built-in pull-up or pull-down resistor option; therefore, there is no need to add a resistor to the circuit. For example, AVR microcontrollers have 20 kΩ – 50 kΩ pull-up resistors on their IO pins. It must be mentioned that, mechanical switches have one more problem – switch bounce. This causes several very short missconnections at the moment of connection. It must be emphasized, that the examples used in this chapter are not affected by the switch bounce and the issue will be covered in more detail in the next chapter.
There are three push button-switches on the Digital i/o module. Those switches connect the pins of microcontroller to the earth, however not directly through a resistor, this is to avoid short circuiting when pressing the button while setting the pins as output. The switches have also pull-up resistors, but those have much greater resistance than protective resistors, therefore while pressing the button, there will still be approximately 0 V voltages on the corresponding pin.
The switches are on PC0, PC1 and PC2 pins. In order to read the status of the switches, the corresponding pins of the microcontroller must be set as inputs. There is no need to put AVR internal pull-up resistors into operation, because the pins already have external resistors. When the button is pressed down, the corresponding bus of the pin has value 0, when the button is released, the value is 1. LED indicators can be used to see if the microcontroller understood the buttons action.
Sample code for using buttons is based on the HomeLab pins library, which was introduced in the example of LED.
// // Program for testing the buttons of the Digital i/o module // #include <homelab/pin.h> // // Determining the pins of LED-d and buttons. // pin leds[3] = { PIN(C, 5), PIN(C, 4), PIN(C, 3) }; pin buttons[3] = { PIN(C, 2), PIN(C, 1), PIN(C, 0) }; // // Main program // int main(void) { unsigned char i; // Setting the LED pins as outputs and buttons pins as inputs. for (i = 0; i < 3; i++) { pin_setup_output(leds[i]); pin_setup_input(buttons[i]); } // Endless cycle while (true) { // Each button has a corresponding LED, // which lights when button is pressed. for (i = 0; i < 3; i++) { pin_set_to(leds[i], pin_get_value(buttons[i])); } } }
In the given example LEDs and buttons are defined as array – this allows them to be use in for loop. When starting the program, LED pins are set as output and buttons as input. In the inter programs endless cycle a constant acquiring of the state of buttons is performed which in turn determines the state of corresponding LEDs. First button is lighting green LED, second yellow and third red.
Vajalikud teadmised: [HW] User Interface Module, [AVR] Digital Inputs/Outputs, [LIB] Pins, [LIB] Delay, [PRT] Switch
As mentioned in the introductory chapter of switches, there exists an issue of bouncing or flickering when dealing with mechanical switches. Problem arises since contacts are made of metal which has some elasticity, at the moment of connecting or disconnecting the contacts bounce and this results in number of false switching’s. The number and duration of the switching's depends on the switch, but usually fits between just few milliseconds. In case a switch is used to start an electrical device it is not considered as a big issue but if the switch is used for controlling a device, multiple switching’s may turn out to be harmful for the device.
Main method used for avoiding flickering caused by the bouncing of contacts is filtering. Filtering can be done electrically or by software. To filter electrically the switch must be connected through a low-pass filter – for example a RC filter – witch smoothed changes of voltage and therefore the pin of microcontroller does not have transient values. RC filter is shown on the drawing. Filtering by software is done by assessing the value of the pin where the switch is connected; if the value remains the same at number of times in pre set time limit, it is considered to have a steady position and hence has no flickering problem. However, with each type of filtering a delay factor in defining the status must be taken under consideration.
Electrical filtering is not used on HomeLab switches, since it would not allow practicing the elimination of miss switching’s with software. The exercise is in two parts. The goal of the first part is to demonstrate the bouncing of Digital i/o module switches. The following program is used for this; each pressing on the button will light the next LED in line. Wrongly pressed button will causes LEDs to light several times and it appears as the LEDs light randomly.
// // The Program for demonstrating switch bounce on Digital i/o module. // #include <homelab/pin.h> // // Determining pins of LEDs and buttons. // pin leds[3] = { PIN(C, 5), PIN(C, 4), PIN(C, 3) }; pin button = PIN(C, 0); // // Main program // int main(void) { unsigned char new_value, old_value = 0; unsigned char index, counter = 0; // Setting LED pins as output. for (index = 0; index < 3; index++) { pin_setup_output(leds[index]); } // Setting button'spins as input. pin_setup_input(button); // Endless loop while (true) { // Reading the state of the button. new_value = pin_get_value(button); // Control, wether the button is pushed down, // that means is the new state 1 and old 0. if ((new_value) && (!old_value)) { // Enlarging the reader and taking module 3 counter = (counter + 1) % 3; // Lighting LED witch corresponds to the value of the reader. for (index = 0; index < 3; index++) { pin_set_to(leds[index], index != counter); } } // Remember the old state. old_value = new_value; } }
Several software solutions are used for filtering, this can be performed either easy or complex way both with its advantages and disadvantages. If the program is set to have only few infrequent pressings of the button, a long pause can be set to follow the pressing, this will rule out reaction to the switching caused by bounce. However, while using this solution must be considered – in case the user holds the button down for a long period of time, the program reacts also on the miss witching caused by the release of the button. A program which controls the state of the switch several times in fixed period of time is more dependable (the longer is the time and the higher is the number of controls performed, the better is the result). Below is a function for reading filtered values of a button for Digital i/o module:
// // Function for reading filtered values of a IO extension module. // unsigned char pin_get_debounced_value(pin button) { unsigned char buffer = 0xAA; unsigned char timeout = 100; // We wait until the status of the button is celar or clearing the state expires while (timeout-- > 0) { // Having 8 place (bit) bufffer of state. // All previous states (bits) are shifted to left // and a new state(bit) is added to the right. buffer <<= 1; buffer |= (pin_get_value(button) ? 0x01 : 0x00); // If all 8 bits are high, then the button is definitely pressed down if (buffer = 0xFF) { return 1; } // If all 8 bits are low, then the button is definitely up. if (buffer = 0x00) { return 0; } // 1 ms break. // This function can be found from the library of the HomeLab. sw_delay_ms(1); } // If we can not examine the state, then we assume that the button was not pressed. return 0; }
This function generates a delay using a function which is explained in corresponding exercise. At this moment all we need to know about the delay function is that it generates a 1 ms delay at the end of each cycle for reading the state of the button. If the button is in the same position during 8 readings, it returns to the counted position. In case the button is unstable the entire procedure may take up to 100 ms. This function is included in the library of pins, hence there is no need to add it to the program for passing the example. In order to try this first part of the exercise, it has to be altered a little - include library of generating the delay in the program and at the point where the value of the button was read, directly apply the function with filter. The result is as follows:
// // The program for filtering the debounce of buttons of Digital i/o module. // #include <homelab/delay.h> #include <homelab/pin.h> // // Determining pins of LEDs and buttons. // pin leds[3] = { PIN(C, 5), PIN(C, 4), PIN(C, 3) }; pin button = PIN(C, 0); // // Main program // int main(void) { unsigned char new_value, old_value = 0; unsigned char index, counter = 0; // Setting the pins of LEDs as outputs. for (index = 0; index < 3; index++) { pin_setup_output(leds[index]); } // Setting the pins of button as input. pin_setup_input(button); // Endless loop. while (true) { // Reading the state of the button. new_value = pin_get_debounced_value(button); // Control whether the button was pressed down, that means, // is the new state 1 and the old state 0. if ((!new_value) && (old_value)) { // Widening the counter and taking module number 3. counter = (counter + 1) % 3; // Lighting the LED witch corresponds to the value of the counter. for (index = 0; index < 3; index++) { pin_set_to(leds[index], index != counter); } } // Remember the old state. old_value = new_value; } }
If we test this program now, the LEDs are lighting exactly in this sequence as the user is pressing the switch.
The goal is to write a program which is able to perform tasks described below.
This chapter introduces the possibility of co-ordinate time of the microcontrollers - timers and delays. These are helpful in applications which require time measuring, make breaks or accurately steer processes. All timers and delays are similar by their nature yet are clearly distinguishable at the same time. The timers are physical modules of microcontroller which are functioning independently from the processor of the microcontroller, delays are paragraphs of software program which are specifically designed to use working time of a processor. From the aspect of similarity both work by counting the firing strokes of the microcontroller.
Both methods have their pros and cons; which are covered in more detail in corresponding exercises. Out of correctness should be said, in the beginning the timers were separate integrated circuits and not as a part of a microcontroller. Separate timer chips exist also today, because in some applications the microcontrollers are not needed and some timers are more efficient than those integrated into the microcontroller.
Timers have evolved from being simple time counting devices to complex systems for receiving and generating signals. They modulate and demodulate signals and they are capable of multiplying and dividing (taktsignaal?). There are also special time-to-digital converters (TDC), which can register time in picoseconds. At this moment we are concentrated on more simple timers.
Necessary knowledge: [HW] Controller module, [AVR] Architecture, [LIB] Pins, [LIB] Delay
There is often a need to create delays in programs of microcontrollers, it is necessary to time the actions or wait them to end. By its concept one of the easiest methods for creating a break in the work of a microcontroller is overloading its processor with some alternative action – for example order it to count big numbers. From the stroke frequency of the processor can be calculated to which number it should count in order to get a certain time delay. Counting a number from zero up to the value of the stroke frequency of the processor in hertz-s, should theoretically create a delay for one second. For number of reasons this is not as simple in practice.
If the processor of the microcontroller calculates using numbers which’s binary form is as wide as its inner bus (AVR has 8 bits), then it takes one firing stroke of the processor to perform one arithmetical operation for example adding 1 to any value. In order to operate with thousands or millions the number has to be 16 or 32 bit and for an 8 bit processor it takes more than one firing stroke to calculate them. Hence when dealing with large numbers, one has to be familiar with the inside of the processor – exactly its commands.
When programming in advanced languages (C-language for example), the programs are not written directly on the basis of the command, in order to create a software delay, one needs to know also its compiler which converts the program to the machine code. From this depends how many instructions (and phases therefore) it takes to perform one arithmetical operation. Complexity is added by compilers ability to convert the program into the machine code in several ways – for example, by making the machine code as memory saving as possible or easily executable. Such compiler’s actions are called optimization. With different modes of optimization the software delay’s machine codes and their duration come out different.
The following is an example of generating software delay with AVR microcontroller. A part of a program in C-language is written, which counts the variable x of for-cycle from 0 to 100. Inside each cycle a no-action empty instruction is completed. It is needed, because if the content of the cycle is empty, the compiler optimizes it out of the program as it considers this to be useless.
unsigned char x; // Cycle until x is 100 for (x = 0; x < 100; x++) { // With empty instruction nop asm volatile ("nop"); }
This is the same part of a program after compiling. 2 hexadecimal numbers on the left is machine code and on the right is the command with operand(s) in assembler language. The machine code and the assembler language are conformal; the assembler is just for presenting the machine code for humans in a readable form. In compiling, the optimization of the length of the program is used (compiler’s parameter –Os).
80 e0 ldi r24, 0x00 ; r24 loading number 0 to the index 00 00 nop ; Empty operation 8f 5f subi r24, 0xFF ; subtracting 255 form the r24 index, that means adding +1 84 36 cpi r24, 0x64 ; comparing r24 index with number 100 e1 f7 brne .-8 ; If the comparison was wrong, then transfer 8-baits back
In the compiled form can be seen, what is actually happening with the cycle of the C-language and it can be used to calculate how many clock sycles is needed to complete a cycle of one period. The information about the effect of the instructions and operating time can be found from AVR’s instructions datasheet. In the given example, it takes 4 clock cycles to complete 4 instructions in one cycle period, because all instructions demand one clock rate. In addition, one clock rate is used before the cycle for loading instruction and afterwards one extra clock rate for exiting the cycle. By assuming, that the working clock rate of the controller is 14,7456 MHz, the whole delay produced by the program can be calculated.
(1 + 100 ⋅ 4 + 1) / 14745600 = 27,26 μs
The delay produced in the example, is in microseconds and the variable used is 8-bit so the machine code is fairly simple. In order to produce a break in millisecond, we need to count much larger numbers and thus the machine code gets longer. Cycles working inside each other may also be used, but with this method the delay is not in linear relation with the number of the cycles because with each level of cycle a small extra delay occurs.
The goal of this exercise is not creating precise software delay on the level of machine code, because it is quite an accurate work and we already have the functions necessary to produce delays in the avr-libc and in the library of the HomeLab. Those are used also in the following examples.
When dealing with the software delay it is important to know, that regardless its basic simplicity; it is extremely inefficient method from the power consumption point of view. During all of these clock rates when the microcontroller is counting the useless, energy is consumed. So if using applications operating on batteries, it is not wise to write long software delays. Wiser is to use hardware timers, which are working independently and wake the processor from hibernating when it is time to continue the work.
The following code of a program is about software delay function sw_delay_ms , which makes a given delay in milliseconds using the parameter count. The function uses avr-libc library’s function _delay_ms which is partly written in assembler language. The reason why the _delay_ms is not used in this exercise immediately is that when using the _delay_ms problems may occur when the delays are long. The sw_delay_ms enables creating 65535 ms long delays without any complications.
// // Software delay in milliseconds. // void sw_delay_ms(unsigned short count) { // Counting the variable of the delay to 0 while (count-- > 0) { // 1ms delay with a special function. _delay_ms(1); } }
The following program is for using the given function, this creates two delays in the endless loop: 100 ms and 900 ms. During the shorter delay LED is lit and during the longer it is switched off, the result – the LED is blinking periodically
// // The demonstration program of the software delay of the HomeLab. // The program is blinking a LED for a moment after ~1 second. // #include <homelab/pin.h> #include <homelab/delay.h> // // Determining the pin of the test LED // pin debug_led = PIN(B, 7); // // Main program // int main(void) { // Setting the pin of the LED as output. pin_setup_output(debug_led); // Endless loop while (true) { // Lighting the LED pin_clear(debug_led); // Software delay for 100 ms sw_delay_ms(100); // Switching off the LED pin_set(debug_led); // Software delay for 900 milliseconds. sw_delay_ms(900); } }
Although it seems that the LED blinks in every 1 second, the time is actually a little bit longer, because the callouts of LED’s and delay functions are taking a couple of clock rates of the microcontroller
Necessary knowledge: [HW] Controller module, [AVR] Counters/Timers, [LIB] Pins, [LIB] Delay, [LIB] Timers, [PRT] Software delay
The software delay is not the only method for creating breaks. The same can be done using timer. Timer is a hardware which counts up or down with a certain frequency. The clock frequency of the timer can be generated from microcontroller’s frequency or from some kind of other outside pace. In general the clock frequency can be divided with a multiplier to reach a smaller frequency - this is done with prescaler. Important fact is that the fixed clock frequency timer’s value is linearly related to the time. The time can be calculated by multiplying the period of the clock frequency of the timer with the value of the timer.
AVR counters can be made to inform about overflow of the counter or achieving compare mach. Overflow happens when the counter has the maximal possible value and the cycle starts all over again form 0. With reaching a pre set value during the moment of growth of the counter’s value it is compared to the value given by the user. On the occurrence of the event, the bits in the status indexes of the AVR are automatically set as high.
For generating a delay using a timer, it is only necessary to set the timer and waiting for the status bit to go high. Different from the software delay, the work of the timer is not depending on the compiler, which makes them more reliable. At the same time the diversity (or complexity) of the set-up of the AVR counter can be considered fairly troublesome. Depending on the microcontroller’s timing signal, may happen that it will not divide exactly with the desired delay period and the delay will not be accurate.
The program code below is a delay function based on a timer, which is simplified a little bit. The principle of counting is the same as it is at software delay function – a desired amount of 1 ms long delays are produced. The delay is produced with an 8-bit ATmega 128 counter 0. It is calculated previously that at clock frequency 14,7456 Mhz the timing signal has to be divided at least 64 times, so that the counter would not reach to overflow in 1 ms. The value which the counter must have so that the overflow occurs after 1 ms is presented in the form of an expression and the variable is timer_start. F_CPU which is a constant in macro-language, that shows clock frequency in Hz. The clock frequency should be 25,6 at the moment but since fractions can not be used, the initial value will be set 26. Unfortunately here arises a mistake in delay time, however it is fairly small (-1,7 μs).
In the cycle takes place initialing of the counter and zeroing the flag of the overflow (by writing 1 into that). Then is waited until the counter counts to 256 from the initial value, i.e. to the overflow. At the moment of the overflow the flag goes high and the delay of 1 ms has taken place. In the end of the function the timer is stopped.
// // Hardware delay in milliseconds. // void hw_delay_ms(unsigned short count) { // Calculating the initial value of the timer. register unsigned char timer_start = 256 - F_CPU / 1000 / 64; // Starting the timer. timer0_init_normal(TIMER0_PRESCALE_64); // Counting the variable of the delay to the 0. while (count-- > 0) { // Initializing the timer. timer0_set_value(timer_start); // Zeroing the overflow flag. timer0_overflow_flag_clear(); // Waiting for overflow. while (!timer0_overflow_flag_is_set()) { asm volatile ("nop"); } } // Zeroing the overflow flag. timer0_overflow_flag_clear(); // Stoping the timer. timer0_stop(); }
The following is a similar program to the example of the software delay. In the shorter 100 ms half-period the LED is lit and on the longer 900 ms half-period it is switched off. As the result the LED is blinking after every second. Unfortunately, in this example the period isn't precisely 1 second either, because executing other functions of the program takes also time. For exact timing a 16-bit timer with interruptions must be used.
// // Demonstration program of hardware delay of the HomeLab. // The Program blinks LED for a moment after every ~1 second. // #include <homelab/pin.h> #include <homelab/delay.h> // // Determining the pin of the Test LED. // pin debug_led = PIN(B, 7); // // Main program. // int main(void) { // Setting the pin of the LED as output. pin_setup_output(debug_led); // Endless loop. while (true) { // Lighting the LED. pin_clear(debug_led); // Hardware delay for 100 milliseconds. hw_delay_ms(100); // Switch off of the LED. pin_set(debug_led); // Hardware delay for 900 milliseconds. hw_delay_ms(900); } }
Necessary knowledge: [HW] Controller module, [HW] User Interface Module, [AVR] Interrupts, [AVR] Counters/Timers, [LIB] Pins, [LIB] Delay, [LIB] Timers, [PRT] Software delay
The goal of these practical exercises is to demonstrate the usage of the interrupts on the example of the counters. The interrupts are program parts which are reacting to the events taking place in the microcontrollers. They are usually used for quick response to an event, but they can also be used for completing several parallel processes, precisely timed action and saving power. For example, it is possible to make a LED blinking using interruptions, so that blinking frequency does not depend on what is happening in the program at the moment.
The following program shows how the counter is set up to make an interrupt. There are 2 LEDs of the Digital i/o module in the program, the state of the red LED is changed periodically with software delay, the state of the green LED is changed when interrupts occur. There is a separate exercise for blinking LED with software delay and it is not explained here. The main goal is to explain the usage of the library of the counters and interrupts.
In the beginning of the program, the 16-bit counter/timer 1 has been set up with the function timer1_init_ctc. With this function the counter CTC clear timer on compare match has been set to the mode where the maximum value of the timer is not 216 – 1 but can be selected. In this case the maximum value is set to equal the value of the ICR1 index. The divider of the counter is 1024 and the value of ICR1 is 14400, so when the clock frequency is 14,7456 MHz, the period will be exactly one second. It is easy to calculate with following formula:
f = 14745600 Hz / 1024 / 14400 = 1
After allowing the interrupt to achieve the maximum value of the counter 1, an interrupt must be allowed also at the global level, that means in the microcontroller. For allowing global interrupts, there is a function sei and for forbidding cli. A header file avr/interrupt.h must be included for defining the program part of these functions and interrupts. The program part of the interrupt is defined with macro function ISR, which parameter is the name of the interrupt vector. In this set-up the vector of counter 1’s maximum value achievement interrupt is TIMER1_CAPT_vect.
// // The HomeLab's example of blinking LED which blinks due to counter interruptings. // For comparison to the LED blinking due to interrupts, // there is a software delay blinking LED working parallel. // #include <homelab/pin.h> #include <homelab/delay.h> #include <homelab/timer.h> #include <avr/interrupt.h> // // Determining the pins of the LEDs. // pin led_red = PIN(C, 5); pin led_green = PIN(C, 3); // // Interruption // ISR(TIMER1_CAPT_vect) { // Changing the state of the green LED. pin_toggle(led_green); } // // Main program. // int main(void) { // Setting the pins of the LEDs as outputs. pin_setup_output(led_red); pin_setup_output(led_green); // Seting the timer up in the CTC mode. timer1_init_ctc( TIMER1_PRESCALE_1024, TIMER1_CTC_TOP_ICR); // The maximal value of the timer is 14400, which // makes the length of the period 1 s. // Formula: 14,7456Mhz / 1024 = 14400 timer1_set_input_capture_value(14400); // Allowing interruption of achieving the value. timer1_input_capture_interrupt_enable(true); // Allowing global interruption. sei(); // Endless loop. while (true) { // Software delay 1000 ms. sw_delay_ms(1000); // Change of the state of the red LED. pin_toggle(led_red); } }
At the start of the program it is seen that regardless of what the microcontroller is doing in the main program, the interrupts are taking place and the green LED is blinking.
If we let the program to work for a couple of minutes, an important aspect occurs, that was not so easily noticeable during software delay exercise. Although the delay in red LED blinking is 1000 ms, the actual time for completing the full cycle is a little bit longer. This is because, the change of the LED’s state, callout of the function of the delay and completion of the cycle are demanding some clock cycles of time for the processor. As a result the blinking of the red LED is always behind from the blinking of the green LED. This is why it is not advised to design clocks and other precise actions with delay but with interruptions of the counter.
The goal is to write a program performing tasks described below.
One of the easiest methods for being convinced that the microcontroller is functioning is adding a code to the program which blinks a LED or LEDs during certain activities. Quite often it is not enough, because it is necessary to forward more information than it is possible by using LED and some applications require user interface where text or numbers are displayed. Here become handy all sorts of indicators and displays, which allow to display numbers, texts or even pictures. One of the most known information displaying devices is computer monitor and typical programmer does not see anything special about using it, but it takes a great deal of effort using microcontrollers to bring every single pixel on the display.
The following chapters are introducing simpler types of displays and screens – indicator with LED segments and two types of monochromatic LCD. In addition to those also LED matrixes and (quite common today) organic color LED (OLED) screens are being used with microcontrollers quite actively. Once filament numerical indicators were used, which had separate glowing filament for each number. There are also electromechanical screens, where different colored facet segments are turned physically. Lately, electronic paper has become popular,which can imitate printings already. First e-papers (electronic paper) were consisting of balls with different colored sides which were turned in electric field. Newer e-papers contain small capsules, where two colors of charged particles are influenced by electric field whether to sing down or stay on the surface. Last two examples are made to visualize different possibilities, but these are not viewed more closely in HomeLab concept.
Necessary knowledge: [HW] User Interface Module, [LIB] Delay, [LIB] 7-segment LED Display, [PRT] Light-emitting Diode
7-segmented LED number-indicator is a display which consists of 7 LEDs positioned in the shape of number 8. By lighting or switching off the corresponding LEDs (segments), it is possible to display numbers from 0 to nine as well as some letters.
Electrically all anodes of the LEDs are connected to one anode pin ca. LEDs are lit by switching their cathodes (a, b, c…). Exists also reversed connections, where the indicators have a common cathode cc. Generally several number-indicators are used for displaying multi digit numbers - for this purpose the indicators are equipped with coma (point) segment dp. All in all one indicator has 8 segments, but they are still called 7-segmented according to the number of number-segments.
LED number-indicators are easy to use, they can be controlled directly from the pins of the microcontroller, but there are also special drivers, which able to control number-indicators using fewer pins of the microcontroller. There are different colors of LED number indicators, which can be very bright and very large. For displaying the entire Latin alphabet exist indicators with extra segments.
There is one 7-segment LED number-indicator on the Digital i/o module. It is controlled through a driver with serial interface A6275. The serial interface of the driver is similar to SPI, where both clock signal and data signal are used. Different from SPI the chip-select is not used there, and is replaced with latch function. The above mentioned three lines are connected to the ATmega128 as follows:
The data is delivered by bits through the data pin. Every time the clock signal goes high, the contents of the shift-index is shifted to the right and the bit from the data-pin is read to the left most cell. By this 8-bits are loaded to the shift-index. If the latch signal is set high, the value of the shift-index is loaded to the latch-index, where it remains until new loading. Each bit of the latch-index is connected through the current switching to one number-indicator’s segment. The segment is lit during the high bit-rate.
For displaying the numbers on the HomeLabs Digital i/o module indicator, the following functionality is written to the library of the HomeLab.
// // Set-up of the pins // static pin segment_display_latch = PIN(G, 2); static pin segment_display_data_out = PIN(C, 6); static pin segment_display_clock = PIN(C, 7); // // Marking card. // The bits are marking the segments. Lower ranking is A, higher ranking is DP. // static const unsigned char segment_char_map[11] = { 0b00111111, 0b00000110, 0b01011011, 0b01001111, 0b01100110, 0b01101101, 0b01111100, 0b00000111, 0b01111111, 0b01100111, 0b01111001 // E like Error }; // // Start-up of the 7-segment indicator. // void segment_display_init(void) { // Set latch, data out and clock pins as output pin_setup_output(segment_display_latch); pin_setup_output(segment_display_data_out); pin_setup_output(segment_display_clock); } // // Displaying number on the 7-segment indicator. // void segment_display_write(unsigned char digit) { unsigned char map; signed char i; // Check-up of the number if (digit > 9) { digit = 10; } // Number as the card of the segments. map = segment_char_map[digit]; // Latch-signal off pin_clear(segment_display_latch); // Sending he bits. Higher ranking goes first. for (i = 7; i >= 0; i--) { // Setting the pin according to the value of the bit of the card. pin_set_to(segment_display_data_out, bit_is_set(map, i)); // The clock-signal as high for a moment. pin_set(segment_display_clock); _delay_us(1); // The clock-signal as low. pin_clear(segment_display_clock); _delay_us(1); } // Latch-signal on. pin_set(segment_display_latch); }
For displaying numbers and the letter “E”, is created a constant array segment_char_map, where lighting of all 8 segments is marked with bit 1 and switch off is market with bit 0. The bits form higher to lower (from left to right in binary form) are marking segments DP, G, F, E, D, C, B, A. The control interface of the driver is realized through software SPI, i.e. by using a software for controlling the data communication pins in the program. All three pins are set as output with segment_display_init function. segment_display_write is for displaying the function, which finds the segment-card of the mark from the array and transmits bit by bit all values of the segments to the driver. The frequency of the clock signal with the software delays is now approximately 500 kHz.
The following is a more concrete example of a program for using the number-indicator. Previously described function of the library is described in the program. The program counts numbers from 0 to 9 with approximate interval of 1 second. Such counting is achieved by taking a module from a much larger number.
// // The example program of the 7-segment LED indicator of the HomeLab's // input-output module. // #include <homelab/module/segment_display.h> #include <homelab/delay.h> // // Main program. // int main(void) { int counter = 0; // Set-up of the 7-segment indicator. segment_display_init(); // Endless loop. while (true) { // Displaying the ones values of the counter. segment_display_write(counter % 10); // Counting very long. counter++; // Delay for 1 second. sw_delay_ms(1000); } }
Necessary knowledge: [HW] lcd, [LIB] Alphanumeric LCD, [LIB] Delay, [PRT] Periodic interrupt
Alphanumeric LCD is liquid crystal display, with the purpose of displaying letters and numbers. In basic LCD is used liquid crystal which is placed between transparent electrodes, and which changes the polarization of the passing light in electrical field. The electrodes are covered by polarization filters, which assure that only one way polarized light can pass the screen. If the liquid crystal changes its polarity due to an electrical field, the light can not pass the screen or part (segment) of it and it looks dark.
Main characteristic of alphanumerical LCD is the placing of its segments. The screen is divided into many indicators. Each indicator has either enough segments for displaying letters and numbers or it is formed from matrix of little square segments (pixels). For example, a matrix of 5×7 pixels is enough to display all numbers, and letters of Latin alphabet. There are usually 1 – 4 rows of indicators and 8 – 32 columns. Each indicator has a small difference similar to the differences of the letters in text.
Besides the screen Alphanumerical LCD has also controller which controls the segments of the screen according to the commands from the communication interface. A controller has a preprogrammed card of letters, where each letter, number or symbol has its own index. Displaying the text on the screen is basically done by sending the indexes to the controller. In reality there must be more control orders sent to the controller before anything can be displayed. It is important to get familiarize with each LCD data-sheet, because there are many different types of LCDs and they all are controlled differently.
Alphanumerical LCDs are usually with passive matrix, where renewal of the electrical field of the segments is tone in turns. That is why the screens with passive matrix are slower and have less contrast compared with the active matrix screens where the charge of each segment is controlled by separate transistor. Some LCDs are with reflective back and others with backlight sometimes even with several different backlights. But segment colour for alphanumerical LCDs is usually still one – which is black, however there are also screens with white and colorful writings.
HomeLabs Digital i/o module connects a 2×16 symbol alphanumerical LCD WC 1602A. For controlling the screen, there is a 4-bit data-bus and 3 control pins, but its communication protocol is too capacious, to be explained here. For simplicity, the library of the HomeLab has corresponding functions for using the display.
Before using the display it is vital to adjust its settings. For this purpose is the lcd_alpha_init function, which sets a blinking cursor on the screen. On screen is always only one active position for the cursor where next letter is entered, regardless whether it can be seen or not. So before entering the text, the cursor must be taken to the desired place. For changing the position of the cursor is a function lcd_alpha_goto_xy and for displaying it lcd_alpha_write_string. All the functions of the alphanumerical LCD are explained in its library.
The following program code demonstrates the usage of alphanumerical LCD as a clock. The Time begins at 00:00:00 and grows approximately in every second. Since the counting of the time is done with the delay function, it is not very precise. The inaccuracy is explained in the exercise of the periodic interruption. The program counts the seconds and converts them into minutes and seconds. For using clock time is a standard function in the C-language: sprintf.
// // The example of using the alphanumerical LCD of the HomeLab. // The clock time starting at the beginning of the program is displayed on the LCD. // #include <stdio.h> #include <homelab/module/lcd_alpha.h> #include <homelab/delay.h> // // Main program. // int main(void) { int seconds = 0; char text[16]; // Set-up of the LCD. lcd_alpha_init(LCD_ALPHA_DISP_ON); // Cleaning of the LCD. lcd_alpha_clear(); // Name of the program. lcd_alpha_write_string("The Time Counter"); // Endless loop while (true) { // Converting the seconds to the clock form: // hh:mm:ss sprintf(text, "%02d:%02d:%02d", (seconds / 3600) % 24, (seconds / 60) % 60, seconds % 60); // Displaying the clock text in the beginning of the second row of the LCD. lcd_alpha_goto_xy(0, 1); lcd_alpha_write_string(text); // Growing seconds by 1. seconds++; // Hardware delay 1000 ms. hw_delay_ms(1000); } }
Necessary knowledge: [HW] lcd, [LIB] Graphic LCD, [LIB] Delay, [PRT] Alphanumeric LCD
Graphical LCD liquid crystal display is a display which allows displaying pictures and text. Its construction is similar to the alphanumerical LCD, with a difference that on the graphic display all pixels are divided as one large matrix. If we are dealing with a monochrome LCD, then a pixel is one square segment. Color displays’ one pixel is formed of three subpixels. Each of the three subpixels lets only one colored light pass (red, green or blue). Since the subpixels are positioned very close to each other, they seem like one pixel.
Monochrome graphic displays have usually passive matrix, large color displays including computer screens have active matrix. All information concerning the color of the background and the pixels of the graphic LCDs are similar to alphanumerical LCDs. Similar to the alphanumerical displays, graphic displays are also equipped with separate controller, which takes care of receiving information through the communication interface and generates the electrical field for the segments. If for alphanumerical LCD is enough to send indexes of the signs in order to display text, then graphic displays are not capable of generating letters by themselves – all the pictures and text needs to be generated pixel by pixel by the user.
In the Home-Lab set is a 84×48 pixels monochrome graphic LCD. It is the same display as used in Nokia 3310 mobile phones. Philips PCD8544 controller is attached to the display which can be communicated through SPI-like serial interface. The background lighting of the display module is separately controlled. Communicating with the display is not very difficult, but due to the large amount of the functions it is not explained here. Home-Labs library has functions for using it.
The functions of the graphic LCD are similar to the alphanumeric LCD functions. First, the screen must be started with lcd_gfx_init function. After start-up it is advised to clean the screen, more precisely controllers memory with the lcd_gfx_clear function. There is a letter map in side of the library with full Latin alphabet, numbers and with most common signs written. The height of the letter is 7 and the width of the letter is 5 pixels. The gap between each letter is horizontally 6 and vertically 8 pixels, i.e. in total it fits 6 rows and 14 columns of letters. To display a letter or text, first its position must be determined by using function lcd_gfx_goto_char_xy . For displaying s letter is lcd_gfx_write_char function and for displaying text lcd_gfx_write_string function.
The following is an example of time counter. The program counts seconds (approximately), minutes and hours. For converting time to text sprintf function is used.
// // Example of using the graphic LCD of the HomeLab. // Time of day is displayed on LCD since the beginning of the program. // #include <stdio.h> #include <homelab/module/lcd_gfx.h> #include <homelab/delay.h> // // Main program. // int main(void) { int seconds = 0; char text[16]; // Set-up of the LCD. lcd_gfx_init(); // Cleaning the screen. lcd_gfx_clear(); // Switching on the background light. lcd_gfx_backlight(true); // Displaying the name of the program. lcd_gfx_goto_char_xy(1, 1); lcd_gfx_write_string("Aja loendur"); // Endless loop. while (true) { // Converting the seconds to the form of clock. // hh:mm:ss sprintf(text, "%02d:%02d:%02d", (seconds / 3600) % 24, (seconds / 60) % 60, seconds % 60); // Displaying the clock text. lcd_gfx_goto_char_xy(3, 3); lcd_gfx_write_string(text); // Adding one second. seconds++; // Hardware delay for 1000 ms. hw_delay_ms(1000); } }
The goal is to write a program which is able to perform tasks described below.
Sensors are devices converting any kind of physical attributes (temperature, luminance, force, acceleration etc.) to a understandable form for humans or machines. With the help from sensors the microcontroller receives information from the surrounding environment and makes decisions based on it. There are many types of sensors, approximately 195 different types are listed in Wikipedia. Only sensors with electrical output signal can be connected to microcontrollers. Based on electrical output signal, is possible to segment sensors as digital and analogue sensors.
In analogue sensor any change in physical attributes changes one of its electrical values, usually voltage, current or resistance. Since microcontrollers are digital devices, the signal have to be converted from analogue to digital before delivering it to controller. For this purpose analogue-digital converters are used witch usually are built-in to the microcontroller.
Analogue sensor which already includes digitizer of information, it is called digital sensor. Digital sensors can also standardize information, calibrate sensors and perform a great deal of other functions. There are many ways for transmitting info from digital sensor to microcontroller: the easiest is with logical signals, more complex way – through some data link interface. The following exercises though, are introducing simpler sensors and sensors known in robotics.
Necessary knowledge : [HW] Sensors Module, [HW] User Interface Module, [ELC] Voltage Divider, [AVR] Analog-to-digital Converter, [LIB] Analog to Digital Converter, [LIB] 7-segment LED Display
Potentiometer is a three terminal resistor, witch has fixed resistance between its two side contacts and variable resistance between side and middle contact. In principle a potentiometer is a attenuator (voltage divider), where resistance is formed between side contacts and middle contact.
A typical potentiometer consists of a resistor with conducting surfaces and of a sliding contact called slider. The closer the slider is to the edge of the resistor, the smaller is the resistance between the slider and the edge and vice versa. A material with high resistivity or coil made of resistance wire can act as a resistor. Some potentiometers have linear or logarithmic relations between the resistance and the slider position. Potentiometers are mainly single turn-potentiometers (example on the picture), but exist also slider potentiometers. A special type of potentiometers are digital potentiometers, where the regulation of the resistance is done inside the micro scheme according to the signals.
On the module of the HomeLab is a 4.7 kΩ turn potentiometer. This potentiometer is connected between ground and +5 V potentials and the slider is connected to the channel 3 of the analogue-digital converter. With this connection, the output voltage of the potentiometer can be regulated between 0 V and 5 V. The digital value of the potentiometer output voltage on its entire adjusting range can be measured if the comparison voltage from AVR digital-analogue converter is taken from AVCC pin. The following function for AVR ADC are in the library of the HomeLab:
// // Data types for adjustment // typedef enum { ADC_REF_AREF = 0x00, ADC_REF_AVCC = 0x01, ADC_REF_2V56 = 0x03 } adc_reference; typedef enum { ADC_PRESCALE_2 = 0x01, ADC_PRESCALE_4 = 0x02, ADC_PRESCALE_8 = 0x03, ADC_PRESCALE_16 = 0x04, ADC_PRESCALE_32 = 0x05, ADC_PRESCALE_64 = 0x06, ADC_PRESCALE_128 = 0x07 } adc_prescale; // // Starting the ADC // void adc_init(adc_reference reference, adc_prescale prescale) { // Allowing ADC to operate, selecting the frequency divider ADCSRA = bit_mask(ADEN) | (prescale & 0x07); // Selecting comparison voltage ADMUX = (reference & 0x03) << REFS0; } // // Converting the values of selected channel // unsigned short adc_get_value(unsigned char channel) { // Setting the channel ADMUX = (ADMUX & 0xF0) | (channel & 0x0F); // Starting the conversion bit_set(ADCSRA, ADSC); // Waiting the end of the conversion while (bit_is_set(ADCSRA, ADSC)) { asm volatile ("nop"); } // Returning the results return ADCW; }
The function adc_init must be called out in the beginning of the program, this is used for making the ADC to work. The reference voltage must be chosen from either AREF pin or AVCC pin, or fixed inner voltage of 2.56 V must be selected. In addition the clock cycle of the converter must be set by the prescaler (factor of frequency divider), which will be used to divide the controller clock cycle. The conversion is quicker when using higher clock cycle but with this the accuracy may suffer. Function adc_get_value is for measuring, this able to select the channel and it returns 10 bit results as 16 bit integer. The function for measuring is inter locking, hence it waits for the end of conversion and returns the results only after all measuring is done.
In previously explained example program analogue-digital converter and 7 segment number indicator library are used. The 10 bit value of analogue-digital converter is multiplied by 10 and divided by 1024 to get the value between 0 and 9. The value 10 is impossible to reach because while dividing in C-language only integer value is calculated and not rounded result. Function of averaging the result of converter is used to get more accurate result. Derived from this the operating program shows the numbers 0 to 9, which correspond to the position of the potentiometer on the indicator.
// // Example program of potentiometer on the Sensor module // The position of the potentiometer is displayed on the 7-segment indicator // #include <homelab/adc.h> #include <homelab/module/segment_display.h> // // Selecting the channel // // 1 = photoresistor // 2 = thermistor // 3 = potentiometer // #define ADC_CHANNEL 3 // // Main program // int main(void) { int value; // Adjusting 7-segment indicator segment_display_init(); // Adjusting ADC adc_init(ADC_REF_AVCC, ADC_PRESCALE_8); // Endless loop while (true) { // Reading 4 times rounded values of the channel value = adc_get_average_value(ADC_CHANNEL, 4); // Displaying the hundreds of the indicated value segment_display_write(value * 10 / 1024); } }
Necessary knowledge: [HW] Sensors Module, [HW] lcd, [ELC] Voltage Divider, [AVR] Analog-to-digital Converter, [LIB] Analog to Digital Converter, [LIB] Alphanumeric LCD, [LIB] Sensors
A thermistor is a type of resistor which resistance varies with temperature. There are two types of thermistors: positive temperature coefficient of resistance and negative temperature coefficient of resistance. The resistance of thermistors with positive temperature coefficient of resistance is increasing when the temperature grows and with negative the resistance decreases. The respective abbreviations are PTC (positive temperature coefficient) and NTC (negative temperature coefficient).
The thermistors resistances' dependence of the temperature is not linear and this complicates the usage of it. For accurate temperature measurements in wider temperature flotation the Steinhart-Hart third-order exponential equation is used as the thermistors resistance is linear only in small temperature range. The following simplified Steinhart-Hart equation with B-parameter exists for NTC thermistors:
where:
Parameter B is a coefficient, which is usually given in the datasheet of the thermistor. But it is stable enough constant only in a certain ranges of temperature, for example at ranges 25–50 °C or 25–85 °C. If the temperature range measured is wider , the data sheet should be used for retrieving the equation.
Usually a voltage-divider is used for measuring the resistance of a thermistor, where one resistor is replaced with a thermistor and the input voltage is constant. The output voltage of the voltage-divider is measured, which changes according to the change of the resistance of the thermistor. If the voltage is applied, current goes through the thermistor which heats up the thermistor due to thermistors resistance and therefore alters again the resistance. The fault caused by heating up of the thermistor can be compensated with calculations, but it is easier to use a thermistor that has higher resistance and therefore heats up less.
With restricted resources and with less demands on accuracy, previously calculated charts and tables for temperatures are used. Generally the tables have ranges of temperatures and respective values of resistance, voltage or analogue-digital converters. All exponential calculations are already done and the user needs to only find the correct row and read the temperature given.
The Sensor module of the HomeLab is equipped with a NTC type thermistor which has 10 kΩ nominal resistance. At temperatures 25-50 °C the parameter B of the thermistor is 3900. One pin of the thermistor is connected to +5 V supply and the other one is connected to the channel 2 (pin number PF2) of the analogue-digital converter. A typical 10 kΩ resistor is also connected with the same pin of the microcontroller and earth and together with the thermistor forms a voltage divider. Since we are dealing with a NTC thermistor, which resistance decreases as the temperature grows; the output voltage of the voltage divider is increasing repectively with growing temperature.
While using the AVR it is practical to use a conversion table of values of temperature and analogue-digital converter to find the correct temperature. It is wise to find corresponding value of analogue-digital converter for each temperature degree of desired range of temperature because reverse table will be too large due to the amount of 10 bit ADC values. It is recommended to use any kind of spreadsheet program (MS Excel, Openoffice Calc, etc.) to make the table. Steinhart-Hart formula which is customized for the mentioned NTC thermistors able's to find the resistance of the thermistor which corresponds to the temperature. Derived from the resistance, is possible to calculate the output voltage of the voltage divider and using this output voltage to calculate the value of the ADC. Calculated values can be inserted to the program as follows:
// // Table for converting temperature values to ADC values. // Every element of the array marks one Celsius degree. // Elements begin from -20 degree and end at 100 degree. // There are 121 elements in the array. // const signed short min_temp = -20; const signed short max_temp = 100; const unsigned short conversion_table[] = { 91,96,102,107,113,119,125,132,139,146,153, 160,168,176,184,192,201,210,219,228,238,247, 257,267,277,288,298,309,319,330,341,352,364, 375,386,398,409,421,432,444,455,467,478,489, 501,512,523,534,545,556,567,578,588,599,609, 619,629,639,649,658,667,677,685,694,703,711, 720,728,736,743,751,758,766,773,780,786,793, 799,805,811,817,823,829,834,839,844,849,854, 859,863,868,872,876,880,884,888,892,896,899, 903,906,909,912,915,918,921,924,927,929,932, 934,937,939,941,943,945,947,949,951,953,955 };
Following algorithm may be used to find the temperature which corresponds to the parameters of the ADC:
// // Converting the ADC values to Celsius degrees: // signed short thermistor_calculate_celsius(unsigned short adc_value) { signed short celsius; // Covering the table backwards: for (celsius = max_temp - min_temp; celsius >= 0; celsius--) { // If the value in the table is the same or higher than measured // value, then the temperature is at least as high as the temperature // corresponding to the element. if (adc_value >= conversion_table[celsius])) { // Since the table begins with 0 but values of the elements from -20, // the value must be shifted. return celsius + min_temp; } } // If the value was not found the minimal temperature is returned. return min_temp; }
The algorithm searches range from the table where the ADC value is and acquires the lower ranking number of this range. The ranking number marks degrees, adding the primary temperature to this a temperature with accuracy of 1 degree is reached.
This conversion table and function are already in the library of the HomeLab, therefore there is no need to write them for this exercise. In the library the conversion function is named thermistor_calculate_celsius. Must be considered, that the conversion is valid only when used on the thermistor on the Sensors module of the HomeLab. For using other thermistors, a conversion table needs to be created and more complex function described in the manual of the library must be used. Example program of this exercise is a thermometer, which measures temperature in Celsius scale and displays it on an alphabetical LCD.
// // Example program of the thermistor of Sensors module. // The temperature is displayed on the LCD. // #include <stdio.h> #include <homelab/adc.h> #include <homelab/module/sensors.h> #include <homelab/module/lcd_alpha.h> // // Main program // int main(void) { unsigned short value; signed short temperature; char text[16]; // Setting the LCD lcd_alpha_init(LCD_ALPHA_DISP_ON); // Cleaning the LCD lcd_alpha_clear(); // Name of the program lcd_alpha_write_string("Termomeeter"); // Setting the ADC adc_init(ADC_REF_AVCC, ADC_PRESCALE_8); // Endless loop while (true) { // Reading the 4 times rounded values of the voltage of the thermistor value = adc_get_average_value(2, 4); // Converting the values of ADC into celsius scale temperature = thermistor_calculate_celsius(value); // Converting the temperature in to text. // To display the degree sign, the octal variable is 337. sprintf(text, "%d\337C ", temperature); // Displaying the text in the beginning of the second row of the LCD. lcd_alpha_goto_xy(0, 1); lcd_alpha_write_string(text); } }
Necessary knowledge: [HW] Sensors Module, [HW] lcd, [ELC] Voltage Divider, [AVR] Analog-to-digital Converter, [LIB] Analog to Digital Converter, [LIB] Alphanumeric LCD, [PRT] Installation instructions for Windows
A photoresistor is a sensor which electrical resistance is altered depending on the light intensity falling on it. The more intense is the light the more free carriers are formed and therefore the lower gets the resistance of the element. Two exterior metal contacts of the photoresistor are reaching through the ceramic base material to the light sensitive membrane, which determines the electrical resistance properties with its geometry and material properties. Since photo sensitive material itself has high resistance, with narrow, curvy track between the electrodes, low total resistance at average light intensity is gained. Similarly to the human eye, the photoresistor is sensitive at certain range of wavelengths and needs to be considered when selecting a photo element, otherwise it may not react to the light source used in the application. Following is simplified list of wavelengths of visible light segmented by colours:
Colour | Range of wavelength (nm) |
---|---|
Purple | 400 – 450 |
Blue | 450 – 500 |
Green | 500 – 570 |
Yellow | 570 – 590 |
Orange | 590 – 610 |
Red | 610 – 700 |
A range of working temperature is set for photoresistor. Wishing the sensor to work at different temperatures, precising conversions must be executed, because the resisting properties of the sensors are depending on the temperature of the ambient.
For characterizing light intensiveness physical concept called light intensity (E) is used, this shows the quantity of light reaching any given surface. Measuring unit is lux (lx), where 1 lux represents, the even flow of light 1 lumen, falling on a surface of 1m2. Hardly ever in reality falls light (living area) on a surface evenly and therefore light intensity is reached generally as a average number. Below are few examples of light intensity:
Values of light intensity for comparison:
Environment | Intensity of light (lx) |
---|---|
Full moon | 0,1 |
Dusk | 1 |
Auditorium | 10 |
Class room | 30 |
Sunset or sunrise | 400 |
Operating room (hospital) | 500 - 1000 |
Direct sun light | 10000 |
The Sensor module in the HomeLab is equipped with VT935G photoresistor. One pin of the photoresistor is connected to +5 V power supply and second pin to the channel 1 (pin PF1) of the analogue-digital converter. Between this pin and the ground 10 kΩ resistor is also connected, which forms a voltage divider with the photoresistor. Since the electrical resistance of the photoresistor is decreasing as the light intensity falling on it grows, the measured voltage on the pin of the microcontroller grows as light intensity grows. It is worth to take into account that the photoresistor used in the HomeLab reacts most on orange and yellow light.
The sensor VT935G is not meant to be a specific measuring device. It is meant to be more a device to specify overall lighting conditions – is there a lighted lamp in the room or not. In this case one has to just measure the resistance of the sensor in the half dark room, note it in the program and compare measured values – is it lighter or darker.
The exercise here is a little bit more complex as the light intensity is measured also in lux. For doing this, exists an approximate formula and floating-point variables. In the C-language are floating-point variables float- and double-type variables, which can be used to present fractions. Their flaw is high demand of resources. Computers have special hardware to calculate floating-point variables, in the 8-bit AVR microcontroller calculations are executed in software which demands a lot of memory and time. If the flaws are not critical, the floating-point variables are worth using.
There is an approximate formula showing the relationship between the intensity of light and electrical resistance in the sensor datasheet. As seen on the graph (on the right), with using logarithm scale, the resistance and intensity of light are almost in linear relationship and form a in-line formula, because following conversion applies:
log(a/b) = log(a) - log(b)
The relation is characterised by the ascent of the factor γ (ascend of the line), which is 0,9 on VT935G sensor. We have also data on one of the points on that line: resistance 18.5 kΩ (RA) at 10 lx intensity of light (EA). Hence we have the coordinates of one point as well as the ascent of the line and for calculating any other point, we only need one coordinate. Meaning, if sensors' resistance (RB) is measured, it is possible to calculate from the equation of line, the intensity of light EB) that falls on the sensor. Finding EB from the equation of line:
log(EB) = log(RA/RB) / γ + log(EA)
EB = 10log(RA/RB) / γ + log(EA)
This gives the formula for calculating the intensity of light when the resistance is known. The resistance can not be measured directly with microcontroller. For this the photoresistor is in the voltage divider. The output voltage of this voltage divider is converted to a specific variable by the analogue-digital converter (ADC). To find the resistance, the output voltage (U2) of the voltage divider must be calculated first, using the ADC value, also comparison voltage (Uref) of the converter must be taken into account: The formula is following:
U2 = Uref * (ADC / 1024)
From the formula for voltage divider(check the chapter on voltage divider) the resistance of the upper photoresistor (R1) can be found:
R1 = (R2 * U1) / U2 - R2
In the following calculation of voltage and resistance, the known factors are replaced with numbers and indexes have been removed:
U = 5 * (ADC / 1024)
R = (10 * 5) / U - 10
For finding the intensity of light, simplifying conversions can be done:
E = 10log(18.5/R) / 0.9 + 1 = 10log(18.5/R) * 10/9 * 101 =
= 10log18.5*10/9 - logR*10/9 * 10 = (10log18.5*10/9 / 10logR*10/9) * 10 =
= (18.510/9 / R10/9) * 10 = 18.510/9 * 10 * R-10/9
By calculating the constant in front of the variable of the field R, the expression remains follows:
E = 255,84 * R-10/9
These formulas help only if the photoresistor on the module of sensors of the HomeLab is used. If circuit is used equipped with different components, respective variables need to be changed. Next, source code of the example program is presented, which measures and calculates using ADC and displays the intensity of light on the LCD. But before compiling the program, the settings for using floating-point variables must be written in the project. How to do that is explained in the chapter of installing the software.
In the example program variables of voltage, resistance and intensity are defined using type double of floating-point variables. The variables which should be used as floating-point variables must always contain a decimal point (it can be also just 0, because then the compiler understands it correctly). When using sprintf for converting floating-point variable to text, “%f” format must be used, this may be enhanced using integers and decimal places. For example: “%3.2”, which displays always 3 integers and 2 decimal places.
// // The example program of the photoresistor of the module of sensors of the HomeLab // Approximate value of the intensity of light is displayed on the LCD. // #include <stdio.h> #include <math.h> #include <homelab/module/lcd_alpha.h> #include <homelab/adc.h> #include <homelab/delay.h> // // Main program. // int main(void) { char text[16]; unsigned short adc_value; double voltage, resistance, illuminance; // Initializing the LCD lcd_alpha_init(LCD_ALPHA_DISP_ON); // Clearing the LCD. lcd_alpha_clear(); // Name of the program lcd_alpha_write_string("Luxmeter"); // Setting the ADC adc_init(ADC_REF_AVCC, ADC_PRESCALE_8); // Endless loop. while (true) { // Reading the average value of the photoresistor adc_value = adc_get_average_value(1, 10); // Calculating the voltage in the input of the ADC voltage = 5.0 * ((double)adc_value / 1024.0); // Calculating the resistance of the photoresistor in the voltage divider resistance = (10.0 * 5.0) / voltage - 10.0; // Calculating the intensity of light in lux illuminance = 255.84 * pow(resistance, -10/9); // Converting the intensity of light to text sprintf(text, "%0.1f lux ", illuminance); // Displaying it on the LCD lcd_alpha_goto_xy(0, 1); lcd_alpha_write_string(text); // Delay 500 ms sw_delay_ms(500); } }
Necessary knowledge: [HW] Sensors Module, [HW] lcd, [AVR] Analog-to-digital Converter, [LIB] Analog to Digital Converter, [LIB] Alphanumeric LCD, [LIB] Sensors
For measuring the distance to an object there are optical sensors using triangulation measuring method. Company “Sharp” produces most common infra-red (IR) wavelength using distance sensors which have analogue voltage output. The sensors made by “Sharp” have IR LED equipped with lens, which emits narrow light beam. After reflecting from the object, the beam will be directed through the second lens on a position-sensible photo detector (PSD). The conductivity of this PSD depends on the position where the beam falls. The conductivity is converted to voltage and if the voltage is digitalized by using analogue-digital converter, the distance can be calculated. The route of beams reflecting from different distance is presented on the drawing next to the text
The output of distance sensors by “Sharp” is inversely proportional, this means that when the distance is growing the output is decreasing (decreasing is gradually slowing). Exact graph of the relation between distance and output is usually on the data-sheet of the sensor. All sensors have their specific measuring range where the measured results are creditable and this range depends on the type of the sensor. Maximum distance measured is restricted by two aspects: the amount of reflected light is decreasing and inability of the PSD registering the small changes of the location of the reflected ray. When measuring objects which are too far, the output remains approximately the same as it is when measuring the objects at the maximum distance. Minimum distance is restricted due to peculiarity of Sharp sensors, meaning the output starts to decrease (again) sharply as the distance is at certain point (depending on the model 4-20 cm). This means that to one value of the output corresponds two values of distance. This problem can be avoided by noticing that the object is not too close to the sensor.
The HomeLab set of sensors includes IR distance sensor SHARP GP2Y0A21YK. Measuring range of the sensor is 10 cm – 80 cm. The output voltage of this sensor is, depending on the distance measured, up to 3 V. The distance sensor is connected to the Sensor module. Its output voltage is sent to the channel 0 of the analogue-digital converter of the AVR. On the basis of previous exercises of sensors, it is easy to write a program which measures the output voltage of the distance sensors, but in addition, this exercise includes converting this output voltage to distance.
On the datasheet of the GP2Y0A21YK is graph of relation between its output voltage and measured distance. This graph is not a linear one, however the graph of inverse values of output voltage and distance almost is, and from that is quite easy to find the formula for converting voltage to distance. To find the formula, the points of the same graph must be inserted to any kind of spreadsheet application and then generate a new graph. In spreadsheet programs is possible to calculate automatically the trend-line. Next, the graph of GP2Y0A21YK corrected output voltage inverse value’s relation to the corrected inverse value of measured distance with linear trend-line is presented. To simplify, the output voltage is already converted to 10 bit +5 V values of analogue-digital converter with comparison voltage.
As seen on the graph, the trend-line (blue) overlaps quite precisely with the points of the graph. Such overlapping is achieved by using the help of the corrective constant. This corrective constant is discovered by using the trial-and-error method – many variables were tested until such was found which made the graph overlap the trend-line the most. This corrective constant of present graph is +2; this means that to all real distances +2 is added. This way the graph is very similar to the linear trend line and a generalization can be made and say that the relation between the distance and the voltage is following:
1 / (d + k) = a * ADC + b
where
Distance d can be expressed from the formula:
d = (1 / (a * ADC + B)) - k
Now it is basically possible to calculate the distance by using this formula, but this requires floating-point calculations, since while dividing fractions will occur. Because the microcontroller operates using integers, the formula must be simplified and converted to larger ratios. Then when dividing the quotient with a linear-member it will look as follows:
d = (1 / a) / (ADC + B / a) - k
When introducing the corrective constant to the formula and also the linear-member and the free-member from the trend-line equation, the formula for calculating the distance will be:
d = 5461 / (ADC - 17) - 2
This formula is computable with 16-bit integers and completely suitable to AVR. Before calculating, must be ensured that the value of the ADC is over 17, otherwise dividing with 0 or negative distance may occur.
Following is the function for converting the values of ADC to centimeters, it is written in the library of the HomeLab. Linear- and free-member and corrective constant are not stiffly written into the function, they are fed with the structure object parameters of the IR distance sensor. By holding the parameters in separate constant, it is easy to add new IR distance sensors to the program.
// // The structure of the parameters of the IR distance sensors // typedef const struct { const signed short a; const signed short b; const signed short k; } ir_distance_sensor; // // The object of the parameters of GP2Y0A21YK sensor // const ir_distance_sensor GP2Y0A21YK = { 5461, -17, 2 }; // // Converting the values of the IR distance sensor to centimeters // Returns -1, if the conversion did not succeed // signed short ir_distance_calculate_cm(ir_distance_sensor sensor, unsigned short adc_value) { if (adc_value + sensor.b <= 0) { return -1; } return sensor.a / (adc_value + sensor.b) - sensor.k; }
To make the conversion the function ir_distance_calculate_cm must be engaged. The first parameter of this function is the object of the parameters of the IR distance sensor, second is the value of the ADC. The function returns the calculated distance in centimeters. If the operation is wrong (unnatural value of the ADC) the returned value is -1. Following program demonstrates the use of IR distance sensor and conversion function. Alphanumeric LCD is used, where measured results are displayed. If the distance is unnatural “?” is displayed.
// // The example program of the IR distance sensor of the HomeLab // Measured results in centimeters is displayed on the LCD // #include <stdio.h> #include <homelab/adc.h> #include <homelab/delay.h> #include <homelab/module/sensors.h> #include <homelab/module/lcd_alpha.h> // // Main program // int main(void) { unsigned short value; signed short distance; char text[16]; // Initialization of LCD lcd_alpha_init(LCD_ALPHA_DISP_ON); // Clearing the LCD lcd_alpha_clear(); // Name of the program lcd_alpha_write_string("Distance sensor"); // Setup of the ADC adc_init(ADC_REF_AVCC, ADC_PRESCALE_8); // Endless loop while (true) { // Reading the 4 times rounded value of the output voltage of the sensor value = adc_get_average_value(0, 4); // Conversing ADC value to distance distance = ir_distance_calculate_cm(GP2Y0A21YK, value); // Was the calculation successful? if (distance >= 0) { // Conversing distance to text sprintf(text, "%d cm ", distance); } else { // Creating the text for unknown distance sprintf(text, "? cm "); } // Displaying the text in the beginning of the second row on the LCD lcd_alpha_goto_xy(0, 1); lcd_alpha_write_string(text); // Break sw_delay_ms(500); } }
Necessary knowledge: [HW] Controller module, [HW] lcd, [AVR] Counters/Timers, [LIB] Timers, [LIB] Alphanumeric LCD, [LIB] Sensors
Ultrasonic distance sensor determines the distance to an object by measuring the time taken by the sound to reflect back from that object. The frequency of the sound is somewhere in the range of ultrasound, this ensures more concentrated direction of the sound wave because sound at higher frequency dissipates less in the environment. A typical ultrasonic distance sensor consists of two membranes. One membrane produces sound, another catches reflected echo. Basically they are speaker and microphone. The sound generator generates short (the length is a couple of periods) ultrasonic impulses and triggers the timer. Second membrane registers the arrival of the sound impulse and stops the timer.From the timers time it is possible to to calculate the distance traveled by the sound. The distance to the object is half of the distance traveled by the sound wave.
The ultrasonic sensors have quite a lot of use in everyday life. They are used to replace measuring tapes in measuring devices at construction sites. Cars are equipped with ultrasonic parking sensors. Besides measuring distances, they can just register the presence of the object in the measuring range, for example in danger zones of working machines. If ultrasound transmitter and receiver are separated, the flowing speed of the substance between them can be measured, because the sound wave travels slower upstream and vice versa.
HomeLab is equipped with Devantech SRF04/SRF05 ultrasonic distance sensor. SRF04/SRF05 is just a sensor and it does not give direct information about the distance. Besides the power supply pins the sensor has also a triggering pin and a echo pin. When the triggering pin is set high the sensor generates 40 kHz ultrasonic wave which is 8 periods long. At that moment the echo pin becomes high and remains high until the reflected sound is reached back to the sensor. So the echo signal reflects basically the time during which the sound reaches to the object and comes back from the object. By measuring that time and multiplying it by the speed of sound and then divide it by two, is calculated the distance to the object. The following graph represents the relationship of time and the signals of sound transmitter, trigger and echo:
To use the SRF04/SRF05 with the AVR, its trigger pin and echo pin should be connected to some of the AVR pins. For measuring time, it is suitable to use a 16-bit timer, for example timer3. Following is a function which executes all measuring procedures – it generates the signal of the trigger, starts the timer, measures the length of the echo signal and converts it to the distance in centimeters. The function is blocking, meaning the processor is occupied by it until the result of measuring is received or the measuring takes too long time. The faster the echo arrives the faster the result is returned. If the echo does not arrive, the function waits for it for 36 ms and returns to 0. It is important to leave approximately 20 ms break between each measuring, to able the sound generated during the previous measuring to die out and the new measuring will not be affected. It is important to notice that the sound waves don’t disturb each other, when several ultrasonic sensors are used simultaneously
#define ULTRASONIC_SPEED_OF_SOUND 33000 // cm/s // // Instant ultrasonic distance measuring // unsigned short ultrasonic_instant_measure(pin trigger, pin echo) { // Pin setup pin_setup_output(trigger); pin_setup_input_with_pullup(echo); // Set timer 3 to normal mode // with period of F_CPU / 8 timer3_init_normal(TIMER3_PRESCALE_8); // Create trigger pulse pin_set(trigger); // Reset timer timer3_overflow_flag_clear(); timer3_set_value(0); // Wait ~10 us while (timer3_get_value() < 18) {} // End trigger pulse pin_clear(trigger); // Wait for echo start while (!pin_get_value(echo)) { // Timeout ? if (timer3_overflow_flag_is_set()) { return 0; } } // Reset timer again timer3_set_value(0); // Wait for echo end while (pin_get_value(echo)) { // Timeout ? if (timer3_overflow_flag_is_set()) { return 0; } } // Convert time to distance: // distance = timer * (1 / (F_CPU / 8)) * speed / 2 return (unsigned long)timer3_get_value() * ULTRASONIC_SPEED_OF_SOUND / (F_CPU / 4); }
Presented function allows the user to choose the echo pin and the trigger pin, so the sensor can be connected where it is more suitable or where is more space. In addition, the freedom of choosing the pins allows to use the function also elsewhere than only in the HomeLab. Presented function belongs already to the library of the HomeLab so it is not necessary to write it. One thing must be remembered: the function in the library of the HomeLab is stiffly connected to the clock rate of the Controller module of the HomeLab. The clock rate is 14,7456 MHz and while using the function with other clock-rates, it would give incorrect result. To use the function with other clock-rates, it should be written in the program manually. Following code of program demonstrates the use of SRF04/SRF05 ultrasonic sensor with the library of the HomeLab.
// // The example program of the ultrasonic distance sensor of the HomeLab // Measuring the distance is blocking. // #include <stdio.h> #include <homelab/pin.h> #include <homelab/delay.h> #include <homelab/module/sensors.h> #include <homelab/module/lcd_alpha.h> // // Pins of ultrasonic sensor // pin pin_trigger = PIN(G, 1); pin pin_echo = PIN(G, 0); // // Main program // int main(void) { unsigned short distance; char text[16]; // Initialization of the LCD lcd_alpha_init(LCD_ALPHA_DISP_ON); // Clearing of the LCD lcd_alpha_clear(); // Name of the program lcd_alpha_write_string("Ultra sound"); // Little break sw_delay_ms(100); // Endless loop. while (true) { // Measuring distance = ultrasonic_measure(pin_trigger, pin_echo); // Was the measuring successful ? if (distance > 0) { // converting the distance to text. sprintf(text, "%d cm ", distance); } // Were there errors during the measuring ? else { // Text of the errer. sprintf(text, "Error "); } // Displaying the text in the beginning of the second row of the LCD lcd_alpha_goto_xy(0, 1); lcd_alpha_write_string(text); // Little break. sw_delay_ms(500); } }
The goal is to write a program which executes following task:
=== For beginners =
Motors are actuator devices, actually some of them are and those can also be very different, beginning with operating principles and ending with power and size. In robotics mainly electric motors are used. Electrical motor is a device which converts electrical energy to mechanical energy (work). It works on principles of electromagnetism.
There are several ways to classify electrical motors. Most important is to divide them as alternate current (AC) and direct current (DC) motors. In addition, there are electrical motors with brushes and brush-less motors, linear motors and rotary motors, nano-motors and large motors and so on. On the other hand, some of the segmentations are provisional. For example, linear motion is achieved usually using rotary electrical motor, which is integrated into unitary body with screw mechanism and treated so as linear actuator. In this chapter are covered three most common types of electrical motors in robotics: DC motor with permanent magnets, RC servos and stepper-motor.
Necessary knowledge: [HW] Motor Module, [AVR] Digital Inputs/Outputs, [LIB] Motors, [LIB] Delay
Permanent magnet DC motors are very common in different applications, where small dimensions, high power and low price are essential. Due to their fairly high speed, they are used together with transmission (to output lower speed and higher torque).
Permanent magnet DC motors have quite simple construction and their controlling is quite elementary. Although controlling is easy, their speed is not precisely determined by the control signal because it depends on several factors, primarily of the torque applied on the shaft and feeding current. The relationship between torque and speed of a ideal DC motor is linear, which means: the higher is the load on the shaft the lower is the speed of the shaft and the higher is the current through the coil.
Brushed DC motors are using DC voltage and basically do not need special control electronics because all necessary communication is done inside the motor. When the motor is operating, two static brushes are sliding on the revolving commutator and holding the voltage on the coils. The direction of revolving of the motor is determined by the polarity of the current. If the motor must revolve in only one direction, then the current may come through relay or some other simple connection. If the motor has to revolve in both directions, then an electronic circuit called H-bridge is used.
In the H-bridge are four transistors (or four groups) directing the current for driving the motor. The electrical scheme of the H-bridge is similar to the letter H and that is where it gets its name. The peculiarity of the H-bridge is the possibility to apply both directional polarities to the motor. Picture on the side shows the principal scheme of the H-bridge based on the example of the switches. If two diagonal switches are closed, the engine starts operating. The direction of the revolving of the motor depends on in which diagonal the switches are closed. In the real H-bridge the switches are replaced with transistors which are selected according to the current of the motor and voltage.
In addition to changing direction of revolving the H-bridge allows to change the speed of revolving. To do that, the transistors need to be closed and opened constantly using pulse width modulation (PWM), so the total energy supplied to the motor is somewhere between motor switched off and motor running on full power. Opened time in the whole PWM period is called duty cycle, which is marked as percents. 0% means the transistor is closed constantly – it is not conducting current. 100% means the transistor is constantly open and is conducting current all the time. The frequency of the PWM has to be high enough to avoid vibrations in the shaft of the motor. At low frequencies the motor produces noise and due to that, modulation frequencies over 20 kHz are used quite often. On the other hand the efficiency of the H-bridge is not so good at higher frequencies. Vibrating of the shaft of the motor is reduced by inertia of the rotor and the inductivity of the coils.
There exist also integrated H-bridges, for conducting smaller currents. For higher currents special power MOSFET-s are used. The H-bridge with other electronics is called motor controller or driver. The driver of DC motor in the HomeLab L293D includes 2 integrated H-bridges and circuit breaking diodes. The motor is controlled with three digital signals,one of them is operation enabling signal enable and the other two are determining the state of the transistors in the H-bridge. Never can occur that two vertical transistors are opened, because this would short-circuit the power source. This means that the driver is designed as foolproof and only option that can be chosen is which transistor (upper or bottom) of one side of the H-bridge (of “semi-bridge”) is opened. In other words the polarity is selected using two driving signals which is applied to the two ends of the coil of the motor.
Note: do not mix up RC PWM signal with ordinary PWM signals.
The board of the motors of the HomeLab allows connecting up to four DC motors. The schemes and instructions for connection are found in the chapter “Motors module”. Basically, for every motor there is a H-bridge which is controlled with two digital output pins of the microcontroller, because the enable pin is constantly high. If both controlling pins have same value, then the motor is stopped if different then it revolves in the corresponding direction. The state of the H-bridge is described in the following table:
Input A | Input B | Output A | Output B | Result |
---|---|---|---|---|
0 | 0 | - | - | The motor is stopped |
1 | 1 | + | + | The motor is stopped |
1 | 0 | + | - | The motor revolves in direction 1 |
0 | 1 | - | + | The motor revolves in direction 2 |
DC motors can be controlled by manipulating directly corresponding driver pins with microcontroller. Though, special functions for driving the motor are in the library of the HomeLab.
// // The setup of the pins driving pins. // static pin dcmotor_pins[4][2] = { { PIN(B, 7), PIN(B, 4) }, { PIN(D, 1), PIN(D, 0) }, { PIN(D, 7), PIN(D, 6) }, { PIN(D, 5), PIN(D, 4) } }; // // Allowing the control of the chosen DC motor. // void dcmotor_init(unsigned char index) { pin_setup_output(dcmotor_pins[index][0]); pin_setup_output(dcmotor_pins[index][1]); } // // Determining the operation and the direction of the chosen DC motor. // void dcmotor_drive(unsigned char index, signed char direction) { pin_set_to(dcmotor_pins[index][0], direction < 0); pin_set_to(dcmotor_pins[index][1], direction > 0); }
With the array dcmotor_pins in the library, the controlling pins of four motor-controllers are determined. Before controlling the motors, function dcmotor_init with the number of the motor-controller (0 – 3) must be called out. It sets the pins as output. For controlling is the function dcmotor_drive, with it the negative direction parameter is used to give to the motor one revolving direction and other direction with positive parameter, and 0 if the motor is stopped.
The following is an example program which controls first and second DC motor so that they alter their revolving direction after every second. The speed could be controlled if one controlling pin were modulated with PWM signal
// // Testing program of the DC motor of the motor's module of the home-lab. // #include <homelab/module/motors.h> #include <homelab/delay.h> // // Main program // int main(void) { // Variable of direction signed char direction = 1; // Setup of motors no 1 and 2. dcmotor_init(0); dcmotor_init(1); // Endless loop while (true) { // One motor revolves in one direction and the other one to other direction. dcmotor_drive(0, -direction); dcmotor_drive(1, +direction); // Break for 1 second. sw_delay_ms(1000); // Reversing the direction. direction = -direction; } }
Necessary knowledge: [HW] Motor Module, [HW] Sensors Module, [AVR] Digital Inputs/Outputs, [LIB] Motors, [LIB] Analog to Digital Converter
Servo motors are often used in radio-controlled (RC) models, and are very useful in different kinds of small robotics applications because they are compact and inexpensive. An RC servo motor includes a built-in DC motor, gearbox, position feedback sensor (usually potentiometer), and drive electronics. RC servo motors can be controlled by an external pulse-width modulation (PWM) signal. If a signal meets RC servo timing requirements, it usually “describes” a (target) position input for the servo drive electronics. Servo motor electronics compare the shaft position with the inputted position, trying to find a shaft position where they match. The position control signal is a continuous square-wave signal, as depicted in figure.
RC (radio-controlled) servo-motors are very common actuator devices in robotics and model building. RC servo motors are consisting of small DC motor, reduction gear and control logic device. Usually the rotor of the servo motor moves to a certain position and tries to maintain that position. The position of the rotor depends of the control signal received by the servo motor. Depending on the type of the motor, the maximum revolving angle of the motor may differ. Servo motors that revolve constantly are rare. In this case, the control signal determines not the revolving angle but the speed of revolving. Servo motor “hack” is also quite common. This makes the position determining servo motor to constantly revolving servo. In this case the feedback potentiometer is replaced by two fixed resistors and the mechanical resistor that prevents full rotations is removed from the gear. A very important characteristic of servo motors is its power-weight ratio.
The controlling signal of servo motor is specific pulse with modulated signal (PWM), where width of the pulse determines the position of the rotor. The period of the signal is 20 ms (50 Hz) and the width of the high period is 1 ms – 2 ms. 1 ms marks one extreme position and 2 ms marks the second one. 1,5 ms marks the middle position of the servo motor’s rotor.
Traditional RC servo motor is also known as analogue-servo motor. It is because in the last decade so called digital servo motors were becoming common. The difference between those two is that in analogue servo motor the motor is controlled by the same 50 Hz PWM input signal. In digital servo motor the motor is controlled by a microcontroller with much higher frequency signal. The input signal is the same in the digital servo motor but higher modulation frequency of the motor enables much more precise and faster position determining.
On the board of module of motors of the HomeLab are two plugs for connecting RC servo motors. The PWM ends of the plugs are connected to the PB5 and PB6 pins of the microcontroller, which alternative functions are outputs of comparing units A and B of the timer 1. Timer 1 is capable of producing PWM signal and due to that the control of motors is very simple in the program. Only difficulty is set-up of the timer.
The timer 1 must be set up in PWM production mode, where the maximum value of the timer is determined with ICR register. With the maximum value changed in the program and in the pace divider of the timer, the precise PWM frequency for controlling the servo motor can be determined. With the comparison register of the timer, lengths of both high semi periods of PWM signal can be determined. The timers have special comparing units which are monitoring the value of the counter and in case it remains equal with the value of the comparison register they change the output value of comparing units. The following is the program code of the servo motor control library of the HomeLab. For the purpose of functionality, it uses parameters for timers which are determined with macro functions. For example, the period is found using F_CPU constant, which marks the clock rate of the microcontroller. When using macros, there is no need to calculate the parameters of timer for different clock rates and the compiler converts the operations with macros to constants anyway, so the program memory is not growing and does not demand more time.
// // The value of the timer (20 ms)for achieving the full period of PWM. // F_CPU is the clock rate of the microcontroller which is divided with 50 Hz and 8. // // #define PWM_PERIOD (F_CPU / 8 / 50) // // Middle position of PWM servo (5 ms / 20 ms) // Middle position is 15/200 of full period. // #define PWM_MIDDLE_POS (PWM_PERIOD * 15 / 200) // // Factor for converting the percents (-100% to 100%)to periods. // +1 is added to ensure that semi periods would reach to the boundaries of 1 ms and 2 ms or // a little over. // #define PWM_RATIO (PWM_PERIOD / 20 / 2 / 100 + 1) // // Set-up of the pins. // static pin servo_pins[2] = { PIN(B, 5), PIN(B, 6) }; // // Preparing the servo motor for working. // void servomotor_init(unsigned char index) { // The pin of PWM signal for output. pin_setup_output(servo_pins[index]); // Setup of timer 1. // Prescaler = 8 // Fast PWM mode, where TOP = ICR // OUTA and OUTB to low in comparisson. timer1_init_fast_pwm( TIMER1_PRESCALE_8, TIMER1_FAST_PWM_TOP_ICR, TIMER1_FAST_PWM_OUTPUT_CLEAR_ON_MATCH, TIMER1_FAST_PWM_OUTPUT_CLEAR_ON_MATCH, TIMER1_FAST_PWM_OUTPUT_DISABLE); // Determining the period by maximum value. timer1_set_input_capture_value(PWM_PERIOD); } // // Determining the position of the servo motor. // The parameter of the position is from -100% to +100%. // void servomotor_position(unsigned char index, signed short position) { switch (index) { case 0: timer1_set_compare_match_unitA_value( PWM_MIDDLE_POS + position * PWM_RATIO); break; case 1: timer1_set_compare_match_unitB_value( PWM_MIDDLE_POS + position * PWM_RATIO); break; } }
The example program uses described functions of the library of the HomeLab. In the beginning of the program the first servo motor’s PWM signal generator is started with the servomotor_init function. The value of the position of the servo motor is obtained from the channel number 3 of the analogue-digital converter, where a potentiometer on the board of sensors is connected. To get the range -100 % - +100 % necessary for controlling the servo motor, half of the maximum (512) is subtracted of the ADC value and the result is divided with 5. The result is +/- 102, but small inaccuracy does not count because servo motors also differ by the relation of the PWM signal and revolving angle. Final PWM‘s semi period’s width in applications has to be determined using test-and-error method. Also the remote controls of RC models have corresponding opportunities for precise setup. When the program is started the rotors position of the servomotor is changed according to the position of the potentiometer.
// // Testing program of the motors module of the HomeLab kit. // #include <homelab/adc.h> #include <homelab/module/motors.h> // // Main program. // int main(void) { short position; // Set-up of the ADC. adc_init(ADC_REF_AVCC, ADC_PRESCALE_8); // Set-up of the motor. servomotor_init(0); // Endless loop. while (true) { // Reading the position of the potentiometer and converting the range of // the servo motor. position = ((short)adc_get_value(3) - (short)512) / (short)5; // Determining the position of the servo motor. servomotor_position(0, position); } }
Necessary knowledge: [HW] Motor Module, [AVR] Digital Inputs/Outputs, [LIB] Motors
Stepper motors can generally be divided into unipolar and bipolar steppers. Unipolar stepper motors are characterized by their centre-tapped windings, which divide two coils into four. Stepper motors have neither built-in brushes nor internal electronics, meaning all commutation must be performed externally. The most common commutation type is the open-loop mode: the motor driver energizes the coils following a certain pattern, but uses no feedback. Steps can be missed in case of motor shaft torque overload. Missed steps cause inaccurate positioning. Bipolar stepper motors usually have four wires and two separate coils inside; they have many features similar to those of unipolar steppers. Unipolar stepper motors can be run as bipolar stepper motors, but not vice versa.
Stepper-motors are widely used in applications which demand accuracy. Unlike DC motors, stepper motors do not have brushes nor commutator – they have several independent coils, which are commutated with exterior electronics (drivers). Rotating the rotor is done by commutating coils step by step, without feedback. This is one of the faults in stepper motors – in case of mechanical overloading, when the rotor is not rotating, the steps will be mixed up and movement becomes inaccurate. Two types of stepper motors are distinguished by coils : unipolar and bipolar stepper motors. By construction three additional segments are considered:
* Variable Reluctance Stepper (high accuracy, low torque, low price) * Permanent Magnet Stepper (low accuracy, high torque, low price) * Hybrid Synchronous Stepper (high accuracy, high torque, high price)
Variable reluctance stepper motors have toothed windings and toothed iron rotor. The largest pulling force is when the teeth of both sides are covering each other. In Permanent magnet stepper motor,just like the name hints, are permanent magnets which orientate according to the polarity of the windings. In hybrid synchronous steppers both technologies are used.
Depending on the model of stepper motor, performing one full rotation (360 degrees) of the rotor, demands hundredths of steps of commutations. For stable and smooth movement, appropriate control electronics are used which control the motor according to its parameters (inertia of the rotor, torque, resonance etc.). In addition to control electronics different commutating methods may be applied. Commutating one winding in a row is called Full Step Drive and if the drive is alternated between one and two windings it is called Half Stepping. Cosine micro stepping is also used, allowing specially accurate and smooth controlling.
Unipolar stepper-motor
Unipolar-stepper motor has 5 or 6 leads. According to the scheme of the motor only ¼ of the windings is activated. Vcc lines are usually connected to the positive power supply. During commutation the ends of windings 1a, 1b, 2a and 2b are connected through transistors (transistor array of the motor board ULN2803) only to the ground and that makes their control electronics fairly simple.
Bipolar stepper-motor
Bipolar stepper motor differs from unipolar stepper motor by having the polarity of the windings altered during the commutation. Half of the windings are activated together, this allows to gain higher efficiency than unipolar stepper motors. Bipolar stepper motors have four leads, each connected to a different half-bridge (driver L293 on the board of motors). During commutation half-bridges are applying either positive or negative voltage to the ends of the windings. Unipolar motors can be started using bipolar driver: just connect lines 1a, 1b, 2a and 2b of the windings (Vcc will be not connected).
The commutation necessary for controlling stepper-motors with windings at full step mode and half step mode is displayed in the table below. Since in drivers for uni-polar stepper motors only opening of the transistors takes place, the steps are marked by 0 and 1. Controlling of bipolar stepper motors may need more signals and therefore the steps are marked using the polarity of the driver outputs:
Unipolar | Bipolar | |||||||
---|---|---|---|---|---|---|---|---|
Step | 1A | 2A | 1B | 2B | 1A | 2A | 1B | 2B |
Full step | ||||||||
1 | 1 | 0 | 0 | 0 | + | - | - | - |
2 | 0 | 1 | 0 | 0 | - | + | - | - |
3 | 0 | 0 | 1 | 0 | - | - | + | - |
4 | 0 | 0 | 0 | 1 | - | - | - | + |
Half step | ||||||||
1 | 1 | 0 | 0 | 0 | + | - | - | - |
2 | 1 | 1 | 0 | 0 | + | + | - | - |
3 | 0 | 1 | 0 | 0 | - | + | - | - |
4 | 0 | 1 | 1 | 0 | - | + | + | - |
5 | 0 | 0 | 1 | 0 | - | - | + | - |
6 | 0 | 0 | 1 | 1 | - | - | + | + |
7 | 0 | 0 | 0 | 1 | - | - | - | + |
8 | 1 | 0 | 0 | 1 | + | - | - | + |
The goal of this exercise is to start a bipolar stepper motor, which can be replaced by unipolar stepper motor using the above described method. There are drivers, on the board of motors, which must be controlled via four input pins by the microcontroller. Each pin represents the polarity of one end of a winding. The voltage of the end of the winding is positive if the pin is high and negative if the pin is low. To the ends 1A, 1B, 2A and 2B correspond the pins of the microcontroller PB0, PB1, PB2 and PB3.
There is function bipolar_init in the library of the HomeLab for controlling the bipolar stepper motors setting the pins as output and function bipolar_halfstep executes revolving by determined half steps. The commutation is done by the table of half steps, but more complex bit operations are used.
// // Preparing for controlling the bipolar stepper motor. // void bipolar_init(void) { DDRB |= 0x0F; PORTB &= 0xF0; } // // Moving the bipolar stepper motor by half steps. // void bipolar_halfstep(signed char dir, unsigned short num_steps, unsigned char speed) { unsigned short i; unsigned char pattern, state1 = 0, state2 = 1; // Insuring the direction +- 1 dir = ((dir < 0) ? -1 : +1); // Execution of half-steps. for (i = 0; i < num_steps; i++) { state1 += dir; state2 += dir; // Creating the pattern. pattern = (1 << ((state1 % 8) >> 1)) | (1 << ((state2 % 8) >> 1)); // Setting the output. PORTB = (PORTB & 0xF0) | (pattern & 0x0F); // Taking a break to wait for executing the step. sw_delay_ms(speed); } // Stopping the motor. PORTB &= 0xF0; }
Usage of the functions is demonstrated by the example program which rotates the motor alternately to one direction and then to the other direction 200 half steps. The speed of rotating the motor is determined by the length of the brakes made between the steps. If the break is set to be too short, the motor can not accomplish the turn due to the inertia of the rotor and the shaft does not move.
// // The test program for the bipolar stepper motor of the motor's //module of the HomeLab. // #include <homelab/module/motors.h> // // Main program. // int main(void) { // Set up of the motor. bipolar_init(); // Endless loop. while (true) { // Turning the rotor 200 half steps to one direction at speed of 30 ms/step. bipolar_halfstep(+1, 200, 30); // Turning 200 half steps to the other direction at speed 30 ms/step. bipolar_halfstep(-1, 200, 30); } }
The goal is to write a program which is able to perform tasks described below.
With microcontrollers it is possible to control actuators, read the values of sensors and many other stuff, but always stays the need for connecting all kind of devices, which does not allow to communicate by sending simple digital signals. The reason may be: there are too many control signals needed to control the device or there is too much data to be sent. That is why there are many data interface standards developed for microcontrollers or for every kind of electronics. The standards are determining the electrical parameters of the signals and the rules of transmission of the signals (the protocol).
One simple example of a protocol is the Morse code, where the information is transmitted using peeps and pauses and varying their lengths. Digital data transmitting protocols are functioning similarly; in there the info is transmitted as bit values and depending on the interface also as modulated form. Different data transmitting interfaces with their protocols have been created according to the need but the data quantities have always grown and new methods have been constantly added. The situation in the data transmission between the electronics components is calmer. There are I²C, SPI and UART interfaces used already for a long time. More traditional intersystem transmission interfaces are RS-232, Rs-485, LIN and CAN, but many microcontrollers are already produced with USB, Ethernet and wireless ZigBee interfaces. This chapter is focusing on the data transmitting by using RS-232 interface.
Necessary knowledge: [HW] Controller module, [AVR] USART, [LIB] Serial Interface, [LIB] Alphanumeric LCD
RS-232 is a standard of physical data interface, which is used for delivering binary data. The standard is used mainly in serial ports of computers, which are also called “COM” ports in everyday language. Nowadays is the RS-232 largely replaced by USB interface, but due to its simplicity it is still used very successfully in hobby applications, especially if there are USB – RS-232 converters. The RS-232 standard determines the plugs, electrical parameters and meanings of the signals but not the protocol.
The RS-232 interface is used mainly with UART hardware data transmission module which protocol is standardized, but it does not determine the plugs or other things. So the RS-232 enhances the UART. Since the UART is usually one module of periphery of the microcontroller, which digital input-output does not correspond to the electrical parameters of the RS-232, they are connected together with a special leveling-converter. One best known leveling-converters between RS-232 and TLL/CMOS is MAX232.
UART means universal asynchronous receiver/transmitter. USART is almost the same, with the difference that the data is sent with clock signal. The UART may be called also a serial interface. The serial interface is a data transferring mechanism, where each bit is transmitted one by one. For example, to transmit 1 bait, 8 bits are transmitted with certain interval. This means that on the serial interface line, which is one pin of the microcontroller, the value of voltage is changed after certain time, once low and then high. Usually there are two devices connected to the serial interface. One is transmitting the information (by changing the value of the pin) and the other is receiving it (by registering the value of the pin). Transmitting pin is TX, and receiving pin is RX. The info is moving always to one direction on one line. For sending data to the other direction an other line is used. If data is moved on two lines at the same time, it is called full duplex bus.
Transmitting data is done by frames of the UART interface, in which is 5-9 data bits (depending on the configuration). Most common is 8 bits (1 bait). In addition to the data bits also extra bits are transmitted with the frame, which are used to recognize the moments of arrival and ending of the data on the receiver’s side. The first is called start-bit and it is always 0. The second is called stop-bit (or bits), which is always 1. Before the stop-bit also parity bit may come. It is use to control regularity. The parity-bit shows whether in the amount of the data-bits is odd or even number of ones. Which reading it has depends on the configuration of the UART interface. The parity-bit is usually not used anymore and it can be banned in configuration. Like the parity-bit can be configured, also can the amount of data-bits and stop-bits.
In addition to the frame structure, there is one more important parameter – it is baud rate, with which the number of transmitted symbols in one second is determined. Baud shows the number of symbols. When we are dealing with UART then 1 baud is 1 bit and that is why we talked about bits when we were talking about frame. Basically it does not matter which baud rate is used for data transmitting, but there is a certain amount of commonly used baud rates, which should be used. For example: 9600 bps, 19200bps, 38400 bps, 57600 bps, 115200 bps;
Furthermore, it is worth to know that the RS-232 standard includes in addition to the data-signals (RX, TX) also data flow control pins DTR, DCD, DSR, RI, RTS and CTS, which are used for controlling the communication between the devices. For example they can be used to notify whether it is ready to receive data or not. Since the RS-232 interface’s original goal is to connect the computers to a modem, some signals are (were) useful rather for showing the state of the telephone lines.
The Controller module board is equipped with one RS-232 type male plug. Through that can controller be connected to computer or to an other controller. For connecting to a computer a usual not inverted cable must be used, which one end is male and other one is female. For connection to an other controller a cable must be used where RX and TX and current control signals are perpendicularly inverted and both plugs are female. The inverted cable is also called zero modem cable. The following is an example program of using UART serial interface. When the program is started, it transmits a welcome through a RS-232 interface and displays messages, which are received. LCD and USART libraries are used.
// // Connecting the Controller module of the HomeLab to a computer through RS-232. // The example is using digital input-output module with LCD. // The text inserted in the terminal of the computer is displayed on the LCD. // #include <homelab/usart.h> #include <homelab/module/lcd_alpha.h> // // Determining USART interface. // usart port = USART(0); // // Main program // int main(void) { char c; unsigned char row = 1; // The set-up of the USART interface. usart_init_async(port, USART_DATABITS_8, USART_STOPBITS_ONE, USART_PARITY_NONE, USART_BAUDRATE_ASYNC(9600)); // The set-up of the LCD. lcd_alpha_init(LCD_ALPHA_DISP_ON_BLINK); // Displaying welcome message on the screen. lcd_alpha_write_string("Waiting for the message"); // Putting the cursor in the beginning of the second row. lcd_alpha_goto_xy(0, row); // Saying hello to the computer. usart_send_string(port, "Hello, write something!\r\n"); // Endless loop while (true) { // Reading the sign from the serial interface. if (usart_try_read_char(port, &c)) { // Are we dealing with the sign of changing the row? if (c = '\r') { // Changing the row. row = 1 - row; // Emptying the row from the previous message. lcd_alpha_clear_line(row); } else { // Issuing the sign directly to the screen. lcd_alpha_write_char(c); } } } }
With Windows XP OS comes a program called HyperTerminal. It is opened from the Start menu by selecting Accessories → Communications → HyperTerminal. Select 9600 bps, 1 start-bit and 1 stop-bit without parity- and stream-control for configuration. When the HyperTerminal is opened during the time when the microcontroller is starting, there will be a welcoming message on the display. The letters inserted through the window are displayed in the alphanumerical LCD. By pressing Enter button the row is changed on the LCD.
The goal is to write a program which is able to perform tasks described below.
Some instructions on how to prepare a project documentation, what themes should be dealt with and what should be written on every theme are brought out in this example project. It is only practical to use a documentation of a typical project as an example. Unfortunately, the documentation of the project is too voluminous due to the graphic material and would not have fit here anyway. Therefore, all the main points are reflected in this example project, but not in its entirety. For example, only one detail is showed from drawings and only one PCB from electronic schemes. Nevertheless, in real documentation all created details should have their drawings and schemes of PCB. Hopefully this example project is helpful for teachers as well, as it is easier for them to explain what they expect from students at the end of the project and how it should be presented. However, few points can be left out from the documentation depending on the level of the student and the actual situation.
In order to engineer a simple mechatronic device it is needed to project the construction and mechanics, electronics and sensors, control system and software for this device. At the end of the project a report should be compiled. It is a documentation which should at least consist following content points:
The following example project is a sample of how to compile a documentation and a report on the project. Unfortunately, the report is in an abbreviated form due to the limited volume of this book, but it is aimed to show the different aspects of documentation.
Mobile robot is one of the most popular robot for construction. Very common are sumo robots, sports robots (football, volleyball etc.), robots simulating rescue operations (firefighting, person or object finding etc.) and many other. For these kinds of robots there are many different competitions in the world and in Estonia, even standard classes have been developed (eg sumo robots). The common feature for these types of robots is mobile platform, which may have different construction and capabilities, but its main functionality remains the same. It is controlling the motors and basic navigation, which includes avoiding objects and travelling to the desired destination. Usually, a specific functionality is added to the main functionality, which is planned according to the requirements and opportunities set for the project.
Here we look at the documentation of a typical mobile robot platform project and its different phases.
Plan and construct a multifunctional mobile robot platform with basic navigation functionality using HomeLab components. Robot platform must have an easy-to-change operational functionality, when equipped with different gadgets:
Robot must be able to move on a flat surface in indoors.
The overall model of the system is presented as a block diagram. It describes the structure, behaviour and other important aspects of the system. As an example, an hierarchical model of the overall system is depicted below.
For this task the team used a brainstorming method and generated 3 conceptually different solutions. Evaluation matrix was compiled and the most optimum construction was found. The main differences of the solutions lay in the movement schemes.
Simplified evaluation matrix was as following:
Function/Solution | I | II | III | Weight factor |
---|---|---|---|---|
Cost | 3 | 4 | 6 | 0,8 |
Complexity of building | 2 | 4 | 7 | 0,7 |
Maneuverability | 4 | 8 | 8 | 0,5 |
Permeability/läbitavus? | 5 | 8 | 2 | 0,3 |
Applicability of HomeLab | 5 | 4 | 5 | 0,9 |
Weight | 5 | 6 | 7 | 0,8 |
Total (with weight factor) | 19 | 27 | 28 |
Evaluation scale was 1-10 points and weight factor 0-1. Weight factors were chosen according to the requirements and restrictions set for this system. Eg, although solution 2 was significantly more capable moving on rough ground, it wasn't required in the preliminary task and therefore the weight factor was low.
Based on the assessment the optimum solution for given task was proved to be a platform moving on two wheels with two separate motors. Further work continued developing the chosen solution into a real system.
Mechanics was tried to make as simple as possible, while following the principle of modularity. The front and the rear bumper are identical modules. Electronics have three modules, which are placed on top of each other, allowing simple ribakaabelühendusi, while ensuring relatively simple changeability of modules. Motors have been selected from HomeLab kit: motors with integrated reducer and coder, which are connected directly to the actuator of the motors. Model aircraft wheels have been used, because they are very light and strong enough for the robot. To simplify the construction the bottom and the top plate are identical. Plates are equipped with holes, allowing different devices to be attached on the top plate. Besides the electronic modules a battery fits between the plates as well.
The bumper of the robot is projected separately and it is integrated with touch sensors and line following sensors. The bumper is made from PCBs and therefore has electricity in addition to construction. Line following sensors are soldered directly to the bumper of the bottom plate. Touch sensors (micro switches) are placed between the bumper plates and are covered by a single rubber piece at front. The rubber piece absorbs the hit and at the same time enables to identify where the hit came from.
The electronics of the system is described as a principle scheme and electronic scheme with PCB assembly scheme.
As an example, Line following sensors electric scheme and respective PCB assembly scheme of the robot's bumper is shown.
The control system of the robot derives from behavioral model and is set by the functionality, requirements and restrictions of the initial task. From the behavioral model of the system a specified control program is created, which in turn is the basis for software program code. All three levels (behavioral model-algorithm-source code) must be consistent with each other.
Algorithm describes the control logic of the system and is depicted as a block diagram. A few elements and description of their relations is enough to create a simple algorithm. If the algorithm of the robot is composed correctly, then it is relatively easy to compose a control program for this robot. Mainly two different objects are used in the algorithm: a rectangle with rounded corners, which marks an activity and a small diamond for controlling a condition, followed by a startup of further activities in accordance with the results of the inspection.
Meanings of the symbols used in the algorithm:
Symbol | Meaning | 0 | 1 | -1 |
---|---|---|---|---|
M1 | left motor | stop | rotates clockwise | rotates counter-clockwise |
M2 | right motor | stop | rotates clockwise | rotates counter-clockwise |
F | first middle touch sensor | no signal | signal | |
FR | first right touch sensor | no signal | signal | |
FL | first left touch sensor | no signal | signal | |
d | reference |
Simple navigation
#include <homelab/module/motors.h> #include <homelab/pin.h> #include <homelab/delay.h> // Defining bumper pins pin front = PIN(C, 0); pin frontleft = PIN(C, 1); pin frontright = PIN(C, 2); // // Mainprogram // int main(void) { // Initiating motors 0 and 1 dcmotor_init(0); dcmotor_init(1); // Sensor pins as inputs pin_setup_input_with_pullup(front); pin_setup_input_with_pullup(frontleft); pin_setup_input_with_pullup(frontright); // Endless cycle while (true) { // Clockwise motor startup dcmotor_drive(0, 1); dcmotor_drive(1, 1); // Controlling the middle sensor signal if (pin_get_value(front)) { // Reversal of the motors dcmotor_drive(0, -1); dcmotor_drive(1, -1); // Paus 1 second sw_delay_ms(1000); // Left motor clockwise startup dcmotor_drive(0, 1); // Paus 2 seconds sw_delay_ms(2000); } // Controlling the left sensor signal else if (pin_get_value(frontleft)) { // Reversal of right motor dcmotor_drive(1, -1); // Paus 2 seconds sw_delay_ms(2000); } // Controlling the right sensor signal else if (pin_get_value(frontright)) { // Reversal of left motor dcmotor_drive(0, -1); // Paus 2 seconds sw_delay_ms(2000); } } }
Robot platform completed under this project is largely made from plastic, except from motor mountings, which are made from aluminum. Electronic modules are placed on top of each other and the battery is loose between the plates. Bumpers are made from PCB and painted black. The top plate of the robot is completely flat, allowing to attach different desired devices. A simple radar was installed on the robot, which consisted of a small RC servo motor and an infra red sensor. As a second solution, intelligent camera module was installed on the platform for solving machine vision problems. Both solutions are brought out on the following pictures. Standard manipulator was tested as a third device, which components are controlled with standard servo motors as well, using serial interface for controlling their actuator.
Economic calculation includes the cost of components and robot production costs
Tabel of components cost
Component | Mark | Quantity | Price | Cost |
---|---|---|---|---|
Motor | M LE149.6.43 | 2 | 500.- | 1000.- |
Microcontroller | uC ATmega128 | 1 | 900.- | 900.- |
Motors actuator board | Actuator Board v1.2 | 1 | 700.- | 700.- |
Power plate | TP | 1 | 500.- | 500.- |
Line following sensors | LFS QRD1114 | 8 | 30.- | 240.- |
Touch sensors | TS Microswitch | 8 | 25.- | 200.- |
Hull plate | ABS | 4 | 50.- | 200.- |
PCB blank | 2 | 50.- | 100.- | |
Motor mountings profile | Al-L | 2 | 10.- | 20.- |
Wheel | 60/10 mm | 2 | 30.- | 60.- |
Battery | NI-MH 9,6 V | 1 | 350.- | 350.- |
Different cables | 10 | 20.- | 200.- | |
Nuts-bolts | 1 | 50.- | 50.- | |
Other acsessories | 1 | 100.- | 100.- | |
Total | 4620.- |
Estimated labor and production cost for a single copy.
Work | Time (h) | Price | Cost |
---|---|---|---|
Milling construction details | 1 | 300.- | 300.- |
Milling PCBs (bumpers) | 0,5 | 500.- | 250.- |
Construction of the robot | 0,5 | 250.- | 125.- |
Building bumpers (soldering components) | 1 | 300.- | 300.- |
Programming | 5 | 300.- | 1500.- |
Compiling documentation | 3 | 250.- | 750.- |
Total | 11 | 3225.- |
Estimated cost of the robot 7845.-
The cost calculation of the robot is estimated, since it is an educational project, where most of the work and construction is done in significantly larger volumes, but without direct charge. Therefore, the work and the approximate time spent does not reflect the real situation.
Mechatronic system (Robot) is created as a team work with a firm timetable and budget, thus having most of the significant features of a project. The key activities of the project management were: time planning, team work planning and management, budget monitoring and obtaining supplies, current reporting to the supervisor, presentation and documentation of the outcome. The project report includes working groups minutes of meetings, project plan (preferably in a Gantt diagram), resource allocation (including human resources), planned and actual budget. For example a simple action plan is given as a Gantt diagram.
Economic calculation showed that the production cost of the robot is quite high, especially when dealing with a single original, but remains within a predetermined initial task. Production costs could certainly be substantially reduced through the optimization of materials and components, and producing a larger quantity of robots at the same time. During this project we learned how to project a mechatronic system, how to construct and test it, which gave us the first time experience of this kind.
At the end of the work a fact revealed: inorder for the robot to function properly significantly more time should be planned for testing, especially for the software testing. Different modules may not always work properly together, although as a separate experiment it works. This shows that the integration of the modules of the system is a serious challenge, and therefore more time and resources should be planned for this.
In conclusion, we believe that the project was very interesting and instructive, it gave an indication how to design and construct integrated systems.
If you have reached the end of this book you have already quite a lot of knowledge and practical skills of programming the microcontroller and using different devices. If you have used examples selectively is also OK. Hopefully you got help for solving the problems on the time and you got also some theoretical knowledge about the problem. Next logical step would be to start building custom intelligent devices according to your interest or needs. Students should look around and participate in some robotic contest. In any case you can apply the acquired knowledge to make your everyday life easier and more fun. You can build a simple security system for your summer house or garage, which uses different sensors to detect the intruders and alarm the owner.
If you feel troubled, ask for help. This book is not finished and will keep on developing further in his electronic form. The book is a part of robotic study concept which includes the support website. In addition to the material the web includes a user community forum where you can find answers for your problems. The electronic version has also additional chapters like Bluetooth, Ethernet, RFID, machine vision for the advanced users etc..
For teachers: Register yourself in our website and send a note that you are a teacher, you will get the access to the exercise solutions and answers of the questions in the end of every lab ;)
Web environment:
http://www.roboticlab.eu