This is an old revision of the document!


Table of Contents

Preface

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

Microcontrollers and robotics

Microcontrollers

The first microcontroller in the world: Intel 8048

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:

  • All functions are placed on a single chip on a smaller and more compact scale.
  • It is programmed to perform certain task, in order to change its functionality new software must be installed.
  • It consumes less power, because all physical characteristics are smaller and less energy demanding than in PC, laptop or server. Usually the developers of microcontrollers are concentrating on low energy demand, so that mobile applications which use batteries can work longer.
  • Single purpose inputs and outputs. Microcontrollers have so called peripherals, which establish connection between microcontroller and other microcontrollers or computers (e.g. USB, CAN, UART), helping to understand the processes in the real physical world (e.g. switching actions, temperature measuring, etc. ) and helping to control surroundings (e.g. control motor, trigger alert etc.)

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

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.

Microcontrollers in robotics

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:

  • Atmel AVR microcontrollers (ATmega, ATtiny, etc.)
  • Microchip Technology PIC microcontrollers (PIC16, PIC24, etc.)
  • Microcontrollers based on ARM technology.

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:

  • processor operating frequency - determines chip operating speed
  • program memory capacity – determines the size of the program that can be installed on the chip
  • data memory capacity – how much data can be processed in the program
  • number of input/output pins and their function – different pins have different possibilities
  • number of timers – important for pursue time criteria
  • energy consumption – important for mobile applications

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.

AVR microcontroller

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.

Introduction

ATmega128 in SMT package (TQFP64, to be precise)

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.

Physical appearance

ATmega32 in 40-pin DIP casing

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.

ATmega128

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.

ATmega128 pinout

Registers

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.

Essence

Tape player's buttons

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:

  • Record
  • Rewind
  • Play
  • Fast forward
  • Stop
  • Pause

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.

Register's “buttons” and bit values

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.

Usage

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.

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.

Negation, logical multiplication, logical addition and exclusive disjunction

 

  • Negation / Inversion
    Negation changes the bit's value to its opposite, a 0 becomes a 1 and vice versa. In C, negation is marked with “~”.
  • Logical multiplication / Conjunction
    When multiplying two bits, the answer is 1 if both bits are 1 and in any other case 0. In C, logical multiplication is marked with “&”.
  • Logical addition / Disjunction
    When adding two bits, the answer is 1 if at least one of the bits is 1 and 0 if both bits are 0. In C, logical addition is marked with “|”.
  • Exclusive disjunction / Exclusive OR / XOR
    Exclusive OR operation will return 1 if the two bits differ from each other (one is 1 and the other 0), otherwise the answer is 0. In C, exclusive disjunction is marked with “^”.

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.

Setting a single bit high
Setting a single bit high

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
Setting a single bit low
Setting a single bit low

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

 

Inverting a single bit
Inverting a single bit

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
Inverting the whole register
Inverting all bits

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
Reading the value of a single bit
Reading the value of a bit

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

 

Shifting a bit

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.

Shift left

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

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.

AVR registers

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.

One of AVRs registers from its datasheet

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

Architecture

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.

Block diagram of ATmega128

Instruction pipeline

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

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;

Instruction set

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

Program stack

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.

Clock

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

Using an 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

Using a 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

Using an 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

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();

Digital inputs/outputs

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:

  • PORT - for setting the output value of the bus.
  • PIN - for reading the input on the bus.
  • DDR - for setting the direction of the bus.

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 interrupts

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:

  • Logical zero (voltage of 0V)
  • Change in the logical value
  • Descending front - logical change from one to zero.
  • Rising front - logical change from zero to one.

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

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/Timers

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.

Counter's default mode

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.

External clock counter

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.

Timing events

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.

Signal generating

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

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

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;
}

Electronics

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

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:

Resistance of the conductor, applied voltage and current through the resistance

I = U / R

where:

  • I is current
  • U is voltage
  • R is resistance

Several other equations are derived from Ohm's law which are used most often in electronics.

LED resistor calculation

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:

LED with series resistor

R = (Uin - Uf) / If
Ur = Uin - Uf
Pr = Ur ⋅ If

where:

  • R is resistor.
  • Uin is supply voltage.
  • Uf is LED voltage.
  • If is LED current.
  • Ur is voltage drop on the resistor.
  • Pr is power of the resistor that is transformed to the heat.

The current limiting resistor of a LED has to be not smaller than R and at least with the power of Pr.

Voltage divider

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:

Electrical schematics of the voltage divider

U2 = U1 ⋅ (R2 / (R1 + R2))

where:

  • U1 is input voltage
  • U2 is output voltage
  • R1 and R2 are resistors

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.

Robotic & Mechatronic HomeLab

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

Integrated Concept of Robotic Studies

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:

  • Conventional textbook, e.g. “Integrated Systems & Design” ISBN:978-9985-59-850-4.
  • Practical Hands-On Exercise book.
  • Robotic HomeLab.
  • DistanceLab.
  • The Network of Excellence of Robotic and Mechatronic community http://www.roboticlab.eu.

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.

Teaching the robotics

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 …..

  1. Team size

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.

  1. Practical work and reporting

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:

  • Lab introduction
    • The teacher explains the new lab, going through the all necessary theoretical material and the example program is performed together.
    • The lab is divided into small stages so that every stage consists of theoretical part and source code development. It means that the source code is developed together with theoretical explanations.
  • Individual work. This is performed according to the lab working guide where the aim, work process, requirements and reporting are defined. Learners are working individually (personally or as a small team) and can get the support from the teacher if needed. The aim of the individual work is that the learners will find the solution by themselves. If possible it is recommended that learners can take the HomeLab kit home to perform the individual work. In that case they are not fixed with the lab time frame and place.
  • Reporting. Learners have to compile a report where they can describe the work according to reporting requirements and as well as answer to team-specific questions assigned by the teacher. Report together with a working solution (HEX file) have to be sent to the teacher by e-mail or uploaded to e-learning system.
  • Inspection
    • The teacher checks the report and the working file sent by the team. The result evaluation is made according to the quality of the report and working solution.
    • The teacher can check whether the solution works according to the lab requirements and can also ask to comment the program orally where each team member has to explain one part of the report.
  1. Group management

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.

  1. Evaluation

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.

  1. Debugging

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.

  1. Competition

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

  1. Perform the example exercise with the potentiometer.
    http://home.roboticlab.eu/en/examples/sensor/potentiometer
  2. Perform the common “Warm-up” exercise.
    http://home.roboticlab.eu/en/exercises/sensor
  3. Perform the team specific exercise. (the exercise will be assigned by the supervisor right before the work)
  4. answer the questions (team specific questions are assigned by the supervisor right before the work)

Report

The electronic report has to be submitted after the individual work and needs to consist the following structure.

  • Report of the work
    • The aim
    • Short description of performed work
    • The algorithm and commented source code out print for steps 2 and 3.
      NB! source code has to be commented and color coded (“Programmer's Notepad” may be used to get the color code to MS Word or OpenOffice writer).
    • Answer to the question (step 4)
    • Results and Comments
  • Working solutions (HEX files) of steps 2 and 3

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

  1. HomeLab support center: http://home.roboticlab.eu
  2. ATmega128 datasheet
  3. Sharp infrared sensor datasheet

HomeLab hardware

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.

HomeLab Basic Kit

HomeLab Basic Kit
  • AVR ATmega128 development board
  • Digital i/o board (buttons, LEDs, 2 x LCD output, 7-segment indicator)
  • 2 x 16 alphanumeric LCD display with back-light
  • JTAG programmer and debugger (USB) + cable
  • Example exercises with C-source code (examples)
  • Power supply
  • Multimeter
  • Software for programming in Assembler and C language
  • Portable case

Sensor and Motor Add-On Kit

Motor module
Sensor and Motor Add-On Kit
  • DC motor (w/ gear)
  • RC servo motor
  • Stepper motor (bipolar or unipolar stepper)
  • motor driver board
  • Supply divider
  • Ribbon cable
Sensor module
  • analogous sensor and low-pass filter combined board with on-board sensors (temperature sensor, light intensity sensor, potentiometer)
  • Ultrasonic distance sensor SRF05 with cable
  • Infrared distance sensor with cable
  • Ribbon cable

HomeLab Advanced Kit

HomeLab Advanced Kit

Includes all modules and components from Basic Kit and Motor and Sensor Add-On Kit. In addition, following modules are included:

RFID module
  • Reader
  • Tag (2)
  • Cable
Communication module
  • Bluetooth
  • Ethernet
  • SPI
Machine vision module
  • Intelligent camera module (CMUcam3)

Controller module

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:

  • Atmega128-16AU microcontroller in the TQFP64 package (ROHS)
    • 8-channel 10-bit A/D converter
    • 128kB Flash memory (program memory)
    • 4kB EEPROM memory (data memory)
    • 6 channel programmable PWM
  • Standard 6-pin In system programming interface (ISP) connector
  • 14,7456 MHz clock (can be changed very easily, crystal is mounted on small 2-pin socket)
  • Real Time clock (RTC)
  • Reset protection circuitry for the ATmega128
  • Status LED and Power LED
  • Standard DB-9 serial port connector with RS232 transmitter / receiver
  • All Atmega signals available on three connectors (1. D, B, E ports, 2. G, C, A ports, 3. F port with ADC I/O lines)
  • Reset button
  • JTAG 10-pin Interface connector
  • 2-pin on/of connector – can be used for connecting on/off button
  • DC 2.1mm power connector
  • Built-in voltage stabilizer
Controller module

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.

Connector pins and functions

NrPinAlternative function / Description
1PD7T2Timer/Counter2 Clock Input
2PD6T1Timer/Counter1 Clock Input
3PD5XCK1USART1 External Clock Input/Output
4PD4IC1Timer/Counter1 Input Capture Trigger
5PD3INT3/TXD1External Interrupt3 Input or UART1 Transmit Pin
6PD2INT2/RXD1External Interrupt2 Input or UART1 Receive Pin
7PD1INT1/SDAExternal Interrupt1 Input or TWI Serial Data
8PD0INT0/SCLExternal Interrupt0 Input or TWI Serial Clock
9VCC-+5V
10GND-GND
11PB7OC2/OC1C Output Compare and PWM Output for Timer/Counter2 or Output Compare and PWM Output C for Timer/Counter1
12PB6OC1BOutput Compare and PWM Output B for Timer/Counter1
13PB5OC1AOutput Compare and PWM Output A for Timer/Counter1
14PB4OC0Output Compare and PWM Output for Timer/Counter0
15PB3MISOSPI Bus Master Input/Slave Output
16PB2MOSISPI Bus Master Output/Slave Input
17PB1SCKSPI Bus Serial Clock
18PB0SSSPI Slave Select Input
19PE7INT7/IC3External Interrupt 7 Input or Timer/Counter3 Input Capture Trigger
20PE6INT6/ T3External Interrupt 6 Input or Timer/Counter3 Clock Input
21PE5INT5/OC3C External Interrupt 5 Input or Output Compare and PWM Output C for Timer/Counter3
22PE4INT4/OC3B External Interrupt4 Input or Output Compare and PWM Output B for Timer/Counter3
23PE3AIN1/OC3A Analog Comparator Negative Input or Output Compare and PWM Output A for Timer/Counter3
24PE2AIN0/XCK0 Analog Comparator Positive Input or USART0 external clock input/output
25PE1PDO/TXD0Programming Data Output or UART0 Transmit Pin
26PE0PDI/RXD0Programming Data Input or UART0 Receive Pin
NrPinAlternative function / Description
1GND-GND
2VCC-+5V
3PA0AD0External memory interface address and data bit 0
4PA1AD1External memory interface address and data bit 1
5PA2AD2External memory interface address and data bit 2
6PA3AD3External memory interface address and data bit 3
7PA4AD4External memory interface address and data bit 4
8PA5AD5External memory interface address and data bit 5
9PA6AD6External memory interface address and data bit 6
10PA7AD7External memory interface address and data bit 7
11-NCNot connected
12-NCNot connected
13PG2ALEAddress Latch Enable to external memory
14-NCNot connected
15PC6A14External memory interface address and data bit 14
16PC7A15External memory interface address and data bit 15
17PC4A12External memory interface address and data bit 12
18PC5A13External memory interface address and data bit 13
19PC2A10External memory interface address and data bit 10
20PC3A11External memory interface address and data bit 11
21PC0A8External memory interface address and data bit 8
22PC1A9External memory interface address and data bit 9
23PG0WRWrite strobe to external memory
24PG1RDRead strobe to external memory
25-NCNot connected
26-NCNot connected
NrPinAlternative function / Description
1VCC-+5V
2GND-GND
3REFAREFAnalog reference voltage for ADC
4GND-GND
5PF0ADC0ADC input channel 0
6GND-GND
7PF1ADC1External memory interface address and data bit 4
8GND-GND
9PF2ADC2External memory interface address and data bit 6
10GND-GND
11PF3ADC3Not connected
12GND-GND
13PF4ADC4/TCKADC input channel 4 or JTAG Test ClocK
14GND-GND
15PF5ADC5/TMSADC input channel 5 or JTAG Test Mode Select
16GND-GND
17PF6ADC6/TDOADC input channel 6 or JTAG Test Data Output
18GND-GND
19PF7ADC7/TDIADC input channel 7 or JTAG Test Data Input
20GND-GND

Connecting module

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 input-output module

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:

  • Green, yellow and red LED;
  • Three push-button;
  • 7-segment indicator;
  • Alphanumeric LCD connector;
  • Graphical LCD connector;
  • Ultrasonic distance sensor connector.

electrical connections

Controller module and Digital i/o module connection

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).

Schematics of buttons and LEDs

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.

Schematics of 7-segment indicator

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.

Schematics of LCD

Connecting the module

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.

  1. Connect the Digital i/o module with Controller module by using the ribbon cable.
  2. If needed connect the JTAG programmer with Controller module.
  3. Connect the power supply with Controller module.
Connecting the Digital i/o module

LCD module

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.

Connecting the LCD module

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.

  1. Connect the Digital i/o module with Controller module by using the ribbon cable.
  2. Connect LCD module with Digital i/o module (only one at a time)
  3. If needed connect the JTAG programmer with Controller module.
  4. Connect the power supply with Controller module.
Connecting the LCDs

Sensors module

Sensors module includes the combined board of sensors, low-pass filter and separate distance sensors.

Sensors module features

  • Ultrasonic distance sensor Devantech SRF05 with cable (measuring distance 1-400 cm) datasheet
  • Infrared distance sensor Sharp GP2Y0A21YK0F (measuring distance 10-80 cm) datasheet
  • Light intensity sensor (photoresistor) VT935G (resistance 18,5 kΩ on 10 lux) datasheet
  • temperature sensor (thermistor NTC) (resistance 10 kΩ on 25 °C) datasheet
  • Potentiometer 5 kΩ

Sensors board

Electrical connections

Controller module and Sensors module connection
Sensors

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.

Schematics of sensors connections
Low-pass filter

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.

Schematics of low-pass filter

Connecting the module

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.

  1. Connect the Sensors module with Controller module by using the ribbon cable. NB! Check that the ribbon-cable is connected to the CON1/PF connector - not CON2
  2. If needed connect the infrared distance sensor (black wire to GND)
  3. If needed connect the Digital i/o module with LCD.
  4. If needed connect the ultrasonic distance sensor with Digital i/o module (black wire to GND)
  5. If needed connect the JTAG programmer with Controller module.
  6. Connect the power supply with Controller module.
Connecting the Sensors module

Motors module

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:

  • 4 x DC motors or 3 x DC motors and 2 x encoder.
  • 2 x unipolar stepper motors.
  • 1 x bipolar stepper motor.
  • 2 x RC servo motor.
  • UART connector.
  • Driver supply selection: jumper JP1 (from controller board or external).
  • Supply indicator LED.

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.

Motors

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

 

Electrical connections

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.

Signal connector of Motors module , powers supply of motors and UART connector
DC motors

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.)

Schematics of DC motor connection
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
Stepper motors

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
Unipolar (left) and bipolar (right) stepper motor windings
Schematics of stepper motor connections
Servo motor

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

Connectors of the motors

Connectors of the motors

Connecting the module

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.

  1. Connect the Motors module with Controller module by using the ribbon cable.
  2. Connect the motors which are needed.
  3. Connect the power distributor of the motors supply.
  4. If needed connect the JTAG programmer with Controller module.
  5. Connect the power supply with Controller module.
Connecting the Motors module

HomeLab library

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.

HomeLab library source code files

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

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.

Funktsioonid

  • bit_mask(bit)

Bit index to bit mask converting. Parameters:

  • bit - Bit index.
  • Returns bit mask.
  • bit_set(value, bit)

Sets a specified bit in the variable. Parameters:

  • value - Variable.
  • bit - Bit index.
  • bit_clear(value, bit)

Clears a specified bit in the variable. Parameters:

  • value - Variable.
  • bit - Bit index.
  • bit_set_to(value, bit, state)

Set a specified bit in the variable to desired state. Parameters:

  • value - Variable.
  • bit - Bit index.
  • state - State (true or false).
  • bit_invert(value, bit)

Inverts a specified bit in the variable. Parameters:

  • value - Variable.
  • bit - Bit index.
  • bit_is_set(value, bit)

Checks whether a specified bit in the variable is set or not. Parameters:

  • value - Variable.
  • bit - Bit index.
  • Returns boolean value true when bit is set and false when bit is cleared.
  • bit_is_clear(value, bit)

Checks whether a specified bit in the variable is cleared or not. Parameters:

  • value - Variable.
  • bit - Bit index.
  • Returns boolean value true when bit is cleared and false when bit is set.

Example

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);
}

Source

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

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.

Data types

  • pin
    Data type to hold pin registers addresses and bit mask. To get the most efficent program, pin typed variables should be constant and they should be initialized at the beginning of the program code. Intializing can be done with macro function PIN, which first parameter is port letter (capital A, B, C, etc) and the other one is pin index (0 to 7). Only existing ports and pins can be used.

Functions

  • void pin_setup_output(pin pin)

Configures pin as an output. Parameters:

  • pin - Pin variable.
  • void pin_setup_input(pin pin)

Configures pin as an input without pull-up resistor. Parameters:

  • pin - Pin variable.
  • void pin_setup_input_with_pullup(pin pin)

Configures pin as an input with pull-up resistor. Parameters:

  • pin - Pin variable.
  • void pin_set(pin pin)

Sets output pin high. Parameters:

  • pin - Pin variable.
  • void pin_clear(pin pin)

Sets output pin low. Parameters:

  • pin - Pin variable.
  • void pin_toggle(pin pin)

Inverts output pin state. Parameters:

  • pin - Pin variable.
  • void pin_set_to(pin pin, bool value)

Sets output pin to desired state. Parameters:

  • pin - Pin variable.
  • value - Desired state boolean value.
  • bool pin_get_value(pin pin)

Gets pin value. Parameters:

  • pin - Pin variable.
  • Return boolean true when pin is high and false when pin is low.
  • bool pin_get_debounced_value(pin pin)

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:

  • pin - Pin variable.
  • Return pin boolean value - true when pin is high and false when pin is low or undetermined.

Example

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);
	}
}

Analog to digital converter

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.

Data types

  • adc_reference
    ADC reference voltage enumeration data type. Options:
    • ADC_REF_AREF - Reference voltage from the AREF pin.
    • ADC_REF_AVCC - Reference voltage from the AVCC pin.
    • ADC_REF_2V56 - Internal 2,56 V reference voltage.
  • adc_prescale
    ADC clock prescaler enumration data type. It determines the division factor of system clock. Options:
    • ADC_PRESCALE_2 - Division factor 2.
    • ADC_PRESCALE_4 - Division factor 4.
    • ADC_PRESCALE_8 - Division factor 8.
    • ADC_PRESCALE_16 - Division factor 16.
    • ADC_PRESCALE_32 - Division factor 32.
    • ADC_PRESCALE_64 - Division factor 64.
    • ADC_PRESCALE_128 - Division factor 128.

Functions

  • void adc_init(adc_reference reference, adc_prescale prescale)

Initializes ADC. Parameters:

  • reference - Reference voltage selection.
  • prescale - Clock prescaler selection.
  • unsigned short adc_get_value(unsigned char channel)

Converts specified ADC channel analog value to digital. Function is blocking. Parameter:

  • channel - ADC channel number (0 to 7).
  • Return 10-bit digital value.
  • unsigned short adc_get_average_value(unsigned char channel, unsigned char num_samples)

Converts specified ADC channel analog value to digital desired number of times and calculates the average. Function is blockin. Parameters:

  • channel - ADC channel number (0 to 7).
  • num_samples - Number of samples for calculation (1 to 64).
  • Return 10-bit digital value.

Example

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);
}

Serial interface

This library provides AVR asynchronous serial interface usage functions.

Data types

  • usart
    Data type to hold USART inteface control, status and data register addresses. usart typed variables should be initialized at the beginning of the program code. For initializations, there is a macro-functions USART which only parameter is the index of interface (0 or 1).
  • adc_usart_databits
    Data bits count enumeration data type. Options:
    • USART_DATABITS_5 - 5 data bits.
    • USART_DATABITS_6 - 6 data bits.
    • USART_DATABITS_7 - 7 data bits.
    • USART_DATABITS_8 - 8 data bits.
    • USART_DATABITS_9 - 9 data bits.
  • usart_stopbits
    Stop bits count enumeration data type. Options:
    • USART_STOPBITS_ONE - One stop bit.
    • USART_STOPBITS_TWO - Two stop bits.
  • usart_parity
    Parity mode enumeration data type. Options:
    • USART_PARITY_NONE - Disabled.
    • USART_PARITY_EVEN - Even parity.
    • USART_PARITY_ODD - Odd parity.

Functions

  • USART_BAUDRATE_ASYNC(baud)

Macro function to calculate USART baud rate register value in asynchronous mode. Parameters:

  • baud - Desired baud rate.
  • Returns baud rate register value.
  • void usart_init_async(usart port, usart_databits data_bits, usart_stopbits stop_bits, usart_parity parity, usart_baudrate baudrate)

Initializes asynchronous USART. Parameters:

  • port - USART interface variable.
  • data_bits - Data bits.
  • stop_bits - Stop bits.
  • parity - Parity mode.
  • baudrate - Baud rate register value (can be calculated with USART_BAUDRATE_ASYNC macro function).
  • void usart_send_char(usart port, char symbol)

Blocking character transmission. Functions wait until transmit buffer empties before writing a character to the buffer. Parameters:

  • port - USART interface variable.
  • symbol - Character to be sent.
  • void usart_send_string(usart port, char *text)

Blocking string transmission. Parameters:

  • port - USART interface variable.
  • text - Pointer to strin (char array). String has to end with null character.
  • bool usart_has_data(usart port)

Checks for data in receive buffer. Parameters:

  • port - USART interface variable.
  • Returns true when there is a character in receive buffer, and false when not.
  • char usart_read_char(usart port)

Reads a character from receive buffer. Before reading user must check if there actually is a received character. Parameters:

  • port - USART interface variable.
  • Returns character.
  • bool usart_try_read_char(usart port, char *symbol)

Reads a character from receive buffer if there is any. Parameters:

  • port - USART interface variable.
  • symbol - Pointer to character variable. If there is a character in receive buffer, it is written to the pointed variable.
  • Returns true when there was a character in receive buffer, and false when not.

Example

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);
}

Timers

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.

Data types

  • timer0_prescale
    Timer 0 prescaler enumeration data type. Options:
    • TIMER0_NO_PRESCALE - No prescaler (no division).
    • TIMER0_PRESCALE_8 - Clk / 8.
    • TIMER0_PRESCALE_32 - Clk / 32.
    • TIMER0_PRESCALE_64 - Clk / 64.
    • TIMER0_PRESCALE_128 - Clk / 128.
    • TIMER0_PRESCALE_256 - Clk / 256.
    • TIMER0_PRESCALE_1024 - Clk / 1024.
  • timer2_prescale
    Timer 2 prescaler enumeration data type. Options:
    • TIMER2_NO_PRESCALE - No prescaler (no division).
    • TIMER2_PRESCALE_8 - Clk / 8.
    • TIMER2_PRESCALE_64 - Clk / 64.
    • TIMER2_PRESCALE_256 - Clk / 256.
    • TIMER2_PRESCALE_1024 - Clk / 1024.
    • TIMER2_PRESCALE_T2_FALLING - Clock on pin T2 falling edge.
    • TIMER2_PRESCALE_T2_RISING - Clock on pin T2 rising edge.
  • timer1_prescale
  • timer3_prescale
    Timer 1/3 prescaler enumeration data type. Options (“n” means 1 or 3):
    • TIMERn_NO_PRESCALE - No prescaler (no division).
    • TIMERn_PRESCALE_8 - Clk / 8.
    • TIMERn_PRESCALE_64 - Clk / 64.
    • TIMERn_PRESCALE_256 - Clk / 256.
    • TIMERn_PRESCALE_1024 - Clk / 1024.
    • TIMERn_PRESCALE_Tn_FALLING - Clock on pin Tn falling edge.
    • TIMERn_PRESCALE_Tn_RISING - Clock on pin Tn rising edge.
  • timer1_ctc_top
  • timer3_ctc_top
    Timer 1/3 CTC mode top value enumeration data type. Options (“n” means 1 or 3):
    • TIMERn_CTC_TOP_OCRA - Top value from timer 1/3 output compare register A.
    • TIMERn_CTC_TOP_ICR - Top value from timer 1/3 input capture register.
  • timer1_fast_pwm_top
  • timer3_fast_pwm_top
    Timer 1/3 fast PWM mode top value enumeration data type. Options (“n” means 1 or 3):
    • TIMERn_FAST_PWM_TOP_256 - Top value 255.
    • TIMERn_FAST_PWM_TOP_512 - Top value 511.
    • TIMERn_FAST_PWM_TOP_1024 - Top value 1023.
    • TIMERn_FAST_PWM_TOP_ICR - Top value from timer 1/3 input capture register.
    • TIMERn_PAST_PWM_TOP_OCRA - Top value from timer 1/3 output compare register A.
  • timer1_fast_pwm_output_mode
  • timer1_fast_pwm_output_mode
    Timer 1/3 fast PWM mode outputs configuration enumeration data type. Options (“n” means 1 or 3):
    • TIMERn_FAST_PWM_OUTPUT_DISABLE - No output.
    • TIMERn_FAST_PWM_OUTPUT_TOGGLE_ON_MATCH - Output toggles on compare match.
    • TIMERn_FAST_PWM_OUTPUT_CLEAR_ON_MATCH - Output clears on compare match.
    • TIMERn_FAST_PWM_OUTPUT_SET_ON_MATCH - Output sets on compare match.

Functions

  • void timer0_init_normal(timer0_prescale prescale)

Initializes timer 0 in normal mode. In this mode timer counts from 0 to 255 (including). Overflow interrupt can be used. Parameters:

  • prescale - Prescaler.
  • void timer2_init_normal(timer2_prescale prescale)

Initializes timer 2 in normal mode. In this mode timer counts from 0 to 255 (including). Overflow interrupt can be used. Parameters:

  • prescale - Prescaler.
  • void timer0_stop()
  • void timer2_stop()

Stops timer 0/2.

  • unsigned char timer0_get_value(void)
  • unsigned char timer2_get_value(void)

Returns timer 0/2 current value. Parameters:

  • Return 8-bit timer value.
  • void timer0_set_value(unsigned char value)
  • void timer2_set_value(unsigned char value)

Sets timer 0/2 value. Parameters:

  • value - New 8-bit timer value.
  • void timer0_overflow_interrupt_enable(bool enable)
  • void timer2_overflow_interrupt_enable(bool enable)

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:

  • enable - true to enable interrupt, false to disable.
  • bool timer0_overflow_flag_is_set(void)
  • bool timer2_overflow_flag_is_set(void)

Checks timer 0/2 overflow flag. Parameters:

  • Returns true when overflow has happened, false when not.
  • void timer0_overflow_flag_clear(void)
  • void timer2_overflow_flag_clear(void)

Resets timer 0/2 overflow flag.

  • void timer1_init_normal(timer1_prescale prescale)
  • void timer3_init_normal(timer3_prescale prescale)

Initializes timer 1/3 in normal mode. In this mode timer counts from 0 to 65535 (including). Overflow interrupt can be used. Parameters:

  • prescale - Prescaler.
  • void timer1_init_ctc(timer1_prescale prescale, timer1_ctc_top top)
  • void timer3_init_ctc(timer3_prescale prescale, timer3_ctc_top top)

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:

  • prescale - Prescaler.
  • top - Timer top value selection. Actual value must be specified with register selected as a top value holder.
  • void timer1_init_fast_pwm(timer1_prescale prescale, timer1_fast_pwm_top top, timer1_fast_pwm_output_mode output_a, timer1_fast_pwm_output_mode output_b, timer1_fast_pwm_output_mode output_c)
  • void timer3_init_fast_pwm(timer3_prescale prescale, timer3_fast_pwm_top top, timer3_fast_pwm_output_mode output_a, timer3_fast_pwm_output_mode output_b, timer3_fast_pwm_output_mode output_c)

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:

  • prescale - Prescaler.
  • top - Timer top value selection. Actual value must be specified with register selected as a top value holder.
  • output_a - Output compare unit A pin configuration.
  • output_b - Output compare unit B pin configuration.
  • output_c - Output compare unit C pin configuration.
  • void timer1_stop()
  • void timer3_stop()

Stops timer 1/3.

  • unsigned char timer1_get_value(void)
  • unsigned char timer3_get_value(void)

Returns timer 1/3 current value. Parameters:

  • Returns 16-bit timer value.
  • void timer1_set_value(unsigned char value)
  • void timer3_set_value(unsigned char value)

Sets timer 0/2 value. Parameters:

  • value - New 16-bit timer value.
  • unsigned short timer1_get_compare_match_unitA_value(void)
  • unsigned short timer1_get_compare_match_unitB_value(void)
  • unsigned short timer1_get_compare_match_unitC_value(void)
  • unsigned short timer3_get_compare_match_unitA_value(void)
  • unsigned short timer3_get_compare_match_unitB_value(void)
  • unsigned short timer3_get_compare_match_unitC_value(void)

Returns timer 1/3 output compare unit A/B/C compare match register value. Parameters:

  • Returns 16-bit compare match register value.
  • void timer1_set_compare_match_unitA_value(unsigned short value)
  • void timer1_set_compare_match_unitB_value(unsigned short value)
  • void timer1_set_compare_match_unitC_value(unsigned short value)
  • void timer3_set_compare_match_unitA_value(unsigned short value)
  • void timer3_set_compare_match_unitB_value(unsigned short value)
  • void timer3_set_compare_match_unitC_value(unsigned short value)

Sets timer 1/3 output compare unit A/B/C compare match register value. Parameters:

  • value - New 16-bit compare match register value.
  • unsigned short timer1_get_input_capture_value(void)
  • unsigned short timer3_get_input_capture_value(void)

Returns timer 1/3 input capture register value. Parameters:

  • Returns 16-bit input capture register value.
  • void timer1_set_input_capture_value(unsigned short value)
  • void timer3_set_input_capture_value(unsigned short value)

Sets timer 1/3 input capture register value. Parameters:

  • value - New 16-bit input capture register value.
  • void timer1_overflow_interrupt_enable(bool enable)
  • void timer3_overflow_interrupt_enable(bool enable)

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:

  • enable - true to enable interrupt, false to disable.
  • void timer1_compare_match_unitA_interrupt_enable(bool enable)
  • void timer1_compare_match_unitB_interrupt_enable(bool enable)
  • void timer1_compare_match_unitC_interrupt_enable(bool enable)
  • void timer3_compare_match_unitA_interrupt_enable(bool enable)
  • void timer3_compare_match_unitB_interrupt_enable(bool enable)
  • void timer3_compare_match_unitC_interrupt_enable(bool enable)

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:

  • enable - true to enable interrupt, false to disable.
  • void timer1_input_capture_interrupt_enable(bool enable)
  • void timer3_input_capture_interrupt_enable(bool enable)

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:

  • enable - true to enable interrupt, false to disable.
  • bool timer1_overflow_flag_is_set(void)
  • bool timer3_overflow_flag_is_set(void)

Checks timer 1/3 overflow flag. Parameters:

  • Returns true when overflow has happened, false when not.
  • bool timer1_input_capture_flag_is_set(void)
  • bool timer3_input_capture_flag_is_set(void)

Checks timer 1/3 input capture flag. Parameters:

  • Returns true when input capture has done, false when not.
  • void timer1_overflow_flag_clear(void)
  • void timer3_overflow_flag_clear(void)

Resets timer 1/3 overflow flag.

  • void timer1_input_capture_flag_clear(void)
  • void timer3_input_capture_flag_clear(void)

Resets timer 1/3 input capture flag.

Example

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();
}

Delay

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.

Functions

  • void sw_delay_ms(unsigned short count)

Software delay in milliseconds. Function expects the usage of some compiler optimization mode. Parameters:

  • count - Delay time in milliseconds. 0 to 65535 ms.
  • void sw_delay_us(unsigned short count)

Software delay in microseconds. Function expects the usage of some compiler optimization mode. Parameters:

  • count - Delay time in microseconds. 0 to 65535 μs.
  • void hw_delay_ms(unsigned short count) \\'

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:

  • count - Delay time in milliseconds. 0 to 65535 ms.

Examples

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);
}

7-segment LED display

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.

Functions

  • void segment_display_init(void)

Configures display driver control pins.

  • void segment_display_write(unsigned char digit)

Displayes a digit on display. Parameters:

  • digit - Number value from 0 to 9. Any other case E like “error” is displayed.

Example

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);
}

Alphanumeric LCD

Related to: [HW] lcd

This library contains functions to use HomeLab alphanumeric LCD.

Data types

  • lcd_alpha_mode
    LCD configuration enumeration data type. Options:
    • LCD_ALPHA_DISP_OFF - Display off.
    • LCD_ALPHA_DISP_ON - Display on with invisible cursor.
    • LCD_ALPHA_DISP_ON_CURSOR - Display on with cursor.
    • LCD_ALPHA_DISP_ON_CURSOR_BLINK - Display on with blinking cursor.

Functions

  • void lcd_alpha_init(lcd_alpha_mode disp_attr)

Initializes LCD. Parameters:

  • disp_attr - Display configuration.
  • void lcd_alpha_clear(void)

Clears the whole display. Cursor is moved to the beginning of the first line.

  • void lcd_alpha_clear_line(unsigned char line)

Clears a line on display. Cursor is moved to the beginning of cleared line. Parameters:

  • line - Line number. 0 or 1.
  • void lcd_alpha_home(void)

Moves cursor to the beginning of the first line.

  • void lcd_alpha_goto_xy(unsigned char x, unsigned char y)

Moves cursor to the desired position. Parameters.

  • x - X coordinate (column number). 0 to 15.
  • y - Y coordinate (line number). 0 to 1.
  • void lcd_alpha_write_char(char c)

Writes a character to the cursor position. Parameters:

  • c - ASCII character.
  • void lcd_alpha_write_string(const char *s)

Writes a string to the display starting from the position of the cursor. Parameters:

  • s - Pointer to string (char array).
  • void lcd_alpha_write_string_p(const char *progmem_s)

Writes a string from program memory to the display starting from the position of the cursor. Parameters

  • progmem_s - Pointer to string in program memory.

Example

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");
}

Graphic LCD

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.

Functions

  • void lcd_gfx_init(void)

Intializes LCD.

  • void lcd_gfx_backlight(bool set)

Switch LCD backlight on or off. Parameters:

  • set - true when backlight on, false when off.
  • void lcd_gfx_clear(void)

Clears a whole display.

  • void lcd_gfx_clear_line(unsigned char line)

Clears a single text line. Parameters:

  • line - Line number. 0 to 5.
  • void lcd_gfx_goto_char_xy(unsigned char x, unsigned char y)

Selects a position to write text to. Parameters:

  • x - X coordinate. 0 to 13.
  • y - Y coordinate. 0 to 5.
  • void lcd_gfx_write_char(char c)

Writes a character to the pre-defined position. Parameters:

  • c - ASCII character.
  • void lcd_gfx_write_string(char *s)

Writes a string to the display starting from the pre-defined position. Parameters:

  • s - Pointer to string (char array).

Example

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");
}

Motors

Related to: [HW] Motor Module

This library contains functions to control different HomeLab motors. There are functions for DC, stepper and servo motors.

Functions

  • void dcmotor_init(unsigned char index)

Initializes one of the DC motor controllers. Parameters:

  • index - Index of motor controller. 0 to 3.
  • void dcmotor_drive(unsigned char index, signed char direction)

Drives one of the DC motor controllers. Parameters:

  • index - Index of motor controller. 0 to 3.
  • direction - Motor polarity. -1, 0 or +1. In case of 0 motor is stopped, otherwise it turns in given direction.
  • void unipolar_init(unsigned char index)

Initializes one of the unipolar stepper motor controllers. Parameters:

  • index - Index of motor controller. 0 or 1.
  • void unipolar_halfstep(unsigned char index, signed char direction, unsigned short num_steps, unsigned char speed)

Unipolar stepper motor half-stepping command. Functions is blocking as it is fulfilled as long as steps are done. Parameters:

  • index - Index of motor controller. 0 or 1.
  • direction - Directions of rotation. -1 or +1.
  • num_steps - Count of half-steps.
  • speed - Time of a single step in milliseconds.
  • void bipolar_init(void)

Initializes bipolar stepper motor controller.

  • void bipolar_halfstep(signed char direction, unsigned short num_steps, unsigned char speed)

Bipolar stepper motor half-stepping command. Functions is blocking as it is fulfilled as long as steps are done. Parameters:

  • direction - Directions of rotation. -1 or +1.
  • num_steps - Count of half-steps.
  • speed - Time of a single step in milliseconds.
  • void servomotor_init(unsigned char index)

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:

  • index - Index of servo motor. 0 or 1.
  • void servomotor_position(unsigned char index, signed short position)

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:

  • index - Index of servo motor. 0 or 1.
  • position - Position or rotation speed. -100 to +100. 0 corresponds to stop.

Example

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);
}

Sensors

Related to: [HW] Sensors Module

This library contains functions to use different sensors in HomeLab kit.

Data types

  • ir_distance_sensor

Infrared distance sensor distance calculation parameters structure. Formula for distance calculation is a / (ADC + b) - k. Structure members:

  • a - Dividend.
  • b - Non-linear constant.
  • k - Linearizing constant.

Constants

  • ir_distance_sensor GP2Y0A21YK

Sharp GP2Y0A21YK distance calculation formula parameters.

Functions

  • signed short thermistor_calculate_celsius(unsigned short adc_value)

Calculates thermistor temperature in Celsius degrees from ADC conversion result. Functions use pre-calculated conversion table. Parameters:

  • adc_value - ADC conversion result (10-bit with +5 V reference voltage)..
  • Returns temperature in Celsius degrees in the -20 to 100 degrees limit.
  • signed short ir_distance_calculate_cm(ir_distance_sensor sensor, unsigned short adc_value)

Calculates distance from ADC result received from IR distance sensor voltage. Parameters:

  • sensor - Distance sensor calculation parameters.
  • adc_value - ADC conversion result (10-bit with +5 V reference voltage).
  • Returns distance in centimeters or -1 if it cannot be calculated.
  • unsigned short ultrasonic_measure(pin trigger, pin echo)

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:

  • trigger - Trigger pin variable.
  • echo - Echo pin variable.
  • Returns distance in centimeters or 0 when measuring failed.

Example

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

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:

  • [HW] Physical module used in th example.
  • [LIB] Part of the software library used in the example.
  • [ELC] Reference to the chapter Basics of electronics.
  • [AVR] Reference to the chapter AVR microcontroller's module.
  • [PRT] Reference to other exercises.

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:

  • Program, all its functions and variables are in English and in small letters, the words are separated with an under-strike.
  • Functions are in following form: object_action_subject.
  • more important places in the program are commented.
  • Every block in C-language (marked with { and } )starts and begins in separate row.
  • Blocks are tabulated with tabulator keys. Tabulator is not used inside rows.

Beginning

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

Installation instructions for Windows

this section contains the information necessary to install AVR development software on Windows operating system.

Installation

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.

Notice of finding the new device

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 .

Creating new project

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.

Setting's test

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);
	}
}
Compilation and programmer buttons

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!

Debugger

The list of registers in the debugger of the ATmega128.

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.

The usage of floating-point variables

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 software environment for Linux

The following guide describes the installation and use of the AVR toolkit 9:10 Ubuntu operating system.

Software Installation

Install the following software:

1. Linux software packages

  • gcc-avr – GNU C-language compiler for AVR
  • avrdude – Program for uploading hex-files to microcontroller
  • avr-libc – C language libraries to write code for AVR platform

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

Connecting the programmer

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.

Creating a new project

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.

Writing and testing a 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);
	}
}
Toolbar

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.

Using floating-point numbers

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.

Digital input/output pins

Digital input-output

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.

Light-emitting diode

Necessary knowledge: [HW] Controller module, [HW] User Interface Module, [ELC] LED Resistor Calculation, [AVR] Registers, [AVR] Digital Inputs/Outputs, [LIB] Pins

Theory

5 mm legged LED

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 “-“.

Schematic symbol of LED and it's polarity

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.

Polarity of legged and SMD LED's

HomeLab practice 1

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);
}

HomeLab practice 2

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~~

Switch

Neccesary knowledge: [HW] User Interface Module, [AVR] Registers, [AVR] Digital Inputs/Outputs, [LIB] Pins, [PRT] Light-emitting Diode

Theory

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:

Push button switch Toggle switch Rocker switch Micro switch DIL switch

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.

Switch connection scheme with pull-up resistor

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.

Practice

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.

Filtration of switch bounce

Vajalikud teadmised: [HW] User Interface Module, [AVR] Digital Inputs/Outputs, [LIB] Pins, [LIB] Delay, [PRT] Switch

Theory

Bouncing of contacts of a 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.

RC-filter of a switch

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.

Practice

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.

Digital i/o exercises

The goal is to write a program which is able to perform tasks described below.

Warm up exercise

  • By pressing S1 one LED is lit, pressing S2 two LEDs and S3 three LEDs lit.

For beginners

  1. This simulates manually operated traffic light at pedestrian crossing. Until no button is pressed a green LED is lit for the cars. After pressing a random button the green LED will start to blink for 3 seconds, this is followed by lighting the yellow LED for 3 seconds and red for 10 seconds, after this sequence a green LED is lit again.
  2. This counts how many times the button is pressed. Only the full release of the button will complete the pressing. The result is displayed in binary code on LEDs. Maximum result for three LEDs is 7(23-1). Green marks 1. bit, yellow 2. bit and red 3. bit.
  3. By pressing switch S1, LED1 and LED3 are lit; pressing S2 yellow LED is lit, pressing S3 all LEDs are switched off. This operation must be done by directly changing the values of corresponding registers (without using the library of the HomeLab).
  4. This counts how many times the button is pressed. The result is displayed by blinking of the LEDs. After each pressing the number of blinking is increased by one. A random button can be selected. In order for the LED to blink a sub function must be used, with parameters set on the number of blinks.
  5. When pushing button S1, red LED will blink “SOS” in Morse. By pressing S2, yellow LED blinks “CQD” and by pressing S3, green will blink “OK”.

For advanced

  1. For each of the three buttons corresponds one LED which lights up when the button is pressed. For turning on or off, registers must be used and only one assigning operation (hint: use bit shifts).
  2. For each of the three buttons corresponds one LED which lights up when the button is pressed. For turning on or off, registers must be used and only one assigning operation (hint: use bit shifts).
  3. There is one button for each LED. The controller blinks LEDs randomly and the user has to repeat the same sequence. The sequence of blinks gets longer – one random LED is added with a new round. The sequence is controlled after each user. The time gap between each entry is two seconds. In case of an incorrect entry all LEDs blink three times. (The number of correct entries by the user may be displayed on a LCD screen).
  4. The program measures time for reaction. A random LED is lit and the user has to press the corresponding button as fast as possible. The time when the LED is lit is random, but not less than 100 ms. the result is displayed on a LCD screen in milliseconds. The value of the buttons cannot be read with filtering function for the flickering, since this will cause an extra delay.

Questions

  1. What is the difference between operations”=” and”=” ? Give two examples to verify the statement.
  2. What is the difference between operations “|” and “||”? Give two examples to verify the statement.
  3. Write an expression using the equation “x = x + 1” twelve times.
  4. How an endless loop is written in C-programming language? Provide two different examples.
  5. What type of a variable do you choose in C–language to present positive values between 7 and 154?
  6. Witch register determines the direction of a port? Provide an example of configuring settings of ports’ input and output.
  7. What are the methods for eliminating the bouncing of the contacts and the false connections caused by flickering? Provide examples of solutions using both hardware and software.
  8. What is bit shift? Give a practical example with an explanation.
  9. Why are pull-up resistors used in switches? How is their resistance determined?
  10. Calculate the resistance for regulating LED current, the voltage is 5 V, forward voltage of the LED is 2,7 V and the current is 30 mA.

Timers and delays

Timers

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.

Software delay

Necessary knowledge: [HW] Controller module, [AVR] Architecture, [LIB] Pins, [LIB] Delay

Theory

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.

Practice

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.

Practice

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

Hardware delay

Necessary knowledge: [HW] Controller module, [AVR] Counters/Timers, [LIB] Pins, [LIB] Delay, [LIB] Timers, [PRT] Software delay

Theory

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.

The events which come with the changes of AVR 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.

Practice

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);
	}
}

Periodic interrupt

Necessary knowledge: [HW] Controller module, [HW] User Interface Module, [AVR] Interrupts, [AVR] Counters/Timers, [LIB] Pins, [LIB] Delay, [LIB] Timers, [PRT] Software delay

Theory

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.

Practice

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.

Exercises

The goal is to write a program performing tasks described below.

Warm up exercise

  • Make the red LED blink. The period is 10 seconds (half period is 5 seconds). Create function of software delay, with amount of seconds as a parameter.

For beginners

  1. Display on LCD screen, with the accuracy of 100 ms, time between pressing of any random two buttons. Execution of the time measurement is your own choice
  2. Pressing button S1, the blinking speed of all three LEDs is slowed down two times. Pressing button S3, the blinking gets 2 times faster and pressing button S2 the frequency of the blinking will be set to 1 Hz. Use delay functions or interruptions (the interruptions are more difficult but when delay functions are used there will be an extra delay due to the filtering function of the buttons).
  3. Display the frequency of pressing down the button S1 on the 7 segment LED indicator in Hz-s. The display of the frequency must be limited with 0 at the bottom and with 9 at the top.
  4. If button S1 is pressed, the program counts down the seconds from 60 to 0 and then lights the red LED. If button S2 is pressed the time is 30 seconds, followed by lighting of the yellow LED. For S3 the time is 10 seconds and the LED is green. All the processes have to take place simultaneously. The LEDs switch off when corresponding buttons are pressed.
  5. Display the Time on the LCD as follows: hh:mm:ss. Use hardware timer 1 with interruptions and the Time must be configurable with the buttons. It is advised to use three buttons, S1 for increasing rotationally the hours, S2 for minutes and S3 for seconds.

For advanced

  1. Design a stopwatch, which displays hours, minutes, seconds and milliseconds on LCD screen. Button S1 starts the time, S2 stops it and S3 zeros it. Interruption of the timer must be used.
  2. In 2 second intervals red, yellow and green LED are smoothly lit and switched off after each other. The smooth lighting is achieved by modulating the LEDs with hundredths of hertz (by lighting and switching it off very fast) and by changing the proportions between lighted/switched off time, causing to appear for the eye, that the brightness of the LEDs is changing (this is Pulse With Modulation).
  3. Make a part of program in C-language which produces a delay of 10 μs ± 10 % at frequency of 14,7456 MHz. Verify the functioning of the delay theoretically - compiled program. By commenting the instructions in assembler language in the .lss file of compiled program.

Questions

  1. What are the methods for crating delay?
  2. How is software delay created? On which parameters depends the duration of the software delay?
  3. What makes us use hardware delay/timer with interruptions?
  4. Calculate the overflow interruption period for 8-bit timer, if the clock rate is 16 MHz and frequency divider's factor is 1024.
  5. What is RTC hidden in the computers?
  6. What happens on the 19.01.2038 in the world of computers?
  7. What can be done with the AVR timers besides counting time?
  8. What indexes can be used to set up the ATmega128 timer 0? What can be adjusted with these registers?
  9. Which is the longest duration of the interruptions in milliseconds which can be achieved with ATmega128 micro-controller that work at the clock frequency of 14,7456 MHz? Show the calculation formula.
  10. In case the processor is heavily loaded with an execution of a program (for example, it controls several motors and the values of several sensors at once), does this have an effect on the accuracy of the timer? Explain your answer.

Indicators and displays

Displays

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.

7-segment LED display

Necessary knowledge: [HW] User Interface Module, [LIB] Delay, [LIB] 7-segment LED Display, [PRT] Light-emitting Diode

Theory

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.

Positioning of the LED indicator's segments and electrical scheme

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.

Practice

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 build-up of the LED driver's shift-index with corresponding segments of the indicator.
  • Latch-signal (latch) - PG2
  • Clock-signal (clock) - PC7
  • Data-signal (data) - PC6

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);
	}
}

Alphanumeric LCD

Necessary knowledge: [HW] lcd, [LIB] Alphanumeric LCD, [LIB] Delay, [PRT] Periodic interrupt

Theory

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.

The text formed of alphanumerical LCD pixels' matrixes.

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.

Practice

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);
	}
}

Graphic LCD

Necessary knowledge: [HW] lcd, [LIB] Graphic LCD, [LIB] Delay, [PRT] Alphanumeric LCD

Theory

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.

The text formed of pixels of a graphic LCD.

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.

Practice

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);
	}
}

Exercises

The goal is to write a program which is able to perform tasks described below.

Warm up exercise

  • It counts numbers 1…9 and then back 9…1 on the 7-segment indicator. The period of the counting is 1 second.

For beginner

  1. Present numbers in hexadecimal system randomly on the 7-segment display. The frequency is 1 Hz.
  2. Light in circular sequence 6 outside segments on the 7-segment indicator with the period of 500 ms.
  3. Write the following text on the alphanumerical LCD: “õun öö äpu ülo”
  4. Show special symbols on the alphanumerical LCD. Display the following text in two rows: “My program ©”, “HomeLab™”
  5. Display 10 rows of text on the graphic display which can be scrolled up and down using the buttons S1 and S2.
  6. Make user interface using three buttons for inserting text. For example, one button chooses the sign, second verifies the sign and finally the third verifies the text. The maximum length of the text is 10 signs and the text must be turned backwards and displayed on the second row. You may use just Latin alphabet. LCD by own choice.

For advanced

  1. Display Greek letters on the graphic LCD. Display the following rows: “Resistance Ω”, “∑R=∑πR²”, “π=3.141592”. Use source code of the HomeLab's library (from the website).
  2. Write a converter for converting decimal numbers to binary code. Use buttons S3-S1 for inserting decimal numbers (S3- hundredths, S2 – tenths, S1 – ones) in 10 seconds. For example, pressing button S3 4 times signifies number 400. Present the sign for starting the insertion and the result in binary code on a random LCD.
  3. Write a function which displays a rectangle on the graphic LCD when the width, length and the coordinates of the top left corner are given. The width of the line is 1 pixel. Check if the rectangle fits inside the screen. It is advised to study the library of the HomeLab.
  4. Write a simple worm game on the graphic LCD. The width of the worm is 1 pixel and the length is 5 pixels. The worm can be made to turn either left or right using buttons. The worm must be able to avoid collision with the border of the screen (turning before). Bonus points are for ability of picking up the eggs and making the worm to grow. The simplest solution is to make the worm out of the letter “O”.
  5. 5. Write a program for displaying different weather conditions on the graphic LCD. Sunny weather - icon of the sun, rainy weather – a cloud with rain, cloudy weather – just a cloud, snow – snowflake. The size of the icons may vary, main point is that the icons are clearly distinguishable. A possibility to change the icons by pressing a button must be present.

Questions

  1. How many pins uses the 7-segment number-indicator (with point segment), if it is connected directly to the controller? How many pins would be needed if if were controlled through the driver (driver - A6275)?
  2. What determines the brightness of the 7-segment number-indicator? How can it be adjusted if the number-indicator is controlled a)directly b)through the driver (driver - A6275)?
  3. If the 7-segment number indicator is connected directly to the port A of the controller, so that the segment A is PA0, B is PA1 … and DP is PA7, then which are the values of the PORTA register with numbers 0…9?
  4. What is the difference between 4-bit and 8-bit alphabetic LCD controllers?
  5. Through which pins and how is the background light of the alphanumerical LCD regulated?
  6. Which I/O protocol uses graphic LCD? Explain the meaning of the I/O pins?
  7. How can the numbers in decimal system be converted to binary system (to text) and vice versa?
  8. Draw the consisting layers of LCD using twisted nematic technology.
  9. How are the letters formed on a graphical LCD?
  10. How is monochrome (black and white)LCD different from color LCD?

Sensors

Sensors

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.

Potentsiometer

Theory

Electric symbol of potentiometer

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.

Single-turn potentiometer

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.

Practice

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);
	}
}

Thermistor

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

Theory

NTC thermistor

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:

The relation between temperature and resistance of a NTC thermistor.

where:

  • T0 - nominal temperature, usually 25 °C.
  • R0 - resistance at nominal temperature.
  • B - parameter B.

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.

Practice

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);
	}
}

Extra

Photoresistor

Theory

A photoresistor
Electrical symbol for a photoresistor

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

Practice

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.

Relationship between resistance (R) of VT935G and intensity of light (E)

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);
	}
}

Infrared distance sensor

Necessary knowledge: [HW] Sensors Module, [HW] lcd, [AVR] Analog-to-digital Converter, [LIB] Analog to Digital Converter, [LIB] Alphanumeric LCD, [LIB] Sensors

Theory

Sharp GP2Y0A21YK

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 route of the light beam from an IR distance sensor
Diagram of voltage-distance of IR distance sensor

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.

Practice

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.

The graph of linearizing ADC value and distance

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

  • d - distance in centimeters.
  • k - corrective constant (fund using tial-and-error method)
  • ADC - digitalized value of voltage.
  • a - linear member (value is determined by the trend line equation)
  • b - free member(value is determined by the trend line equation)

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);
	}
}

Extra materials

Ultrasonic distance sensor

Necessary knowledge: [HW] Controller module, [HW] lcd, [AVR] Counters/Timers, [LIB] Timers, [LIB] Alphanumeric LCD, [LIB] Sensors

Theory

Ultrasonic distance sensor SRF04

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.

Working principle of the ultrasonic distance sensor.

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.

Practice

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:

The signals of the SRF04

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);
	}
}

Exercises

The goal is to write a program which executes following task:

Warm-up exercise

  • The value of the resistance of the potentiometer is displayed on the LCD in ohms. The nominal value of the resistance is 5 kΩ.

=== For beginners =

  1. The value of the resistance of the potentiometer in the range of (0 Ω…999 Ω) is displayed in ohms and in the range of (1000 Ω…5000 Ω) in kilo ohms. The nominal value of the resistance of the potentiometer is 5kΩ. The result is displayed in correct units and correct symbols.
  2. Measuring the distance to an object. The distance to an object is measured with an IR sensor by pressing the button S1. During the measuring yellow LED is blinked. If the object is beyond 50 cm a green LED is lit and when the object is closer than 50 cm a red LED is lit.
  3. The distance to an object is measured using an IR sensor. The result is displayed in binary system using three LEDs (LED1, LED2, and LED3). If the distance is growing, the displayed value must grow respectively. The scale should be approximately 1 bit = 1 dm.
  4. The value of the NTC temperature sensor is displayed on the LCD in degrees. By pressing the S2 button the units may be selected: Kelvin (K), Fahrenheit (F) and Celsius (C). The temperature is displayed in correct units and symbols.
  5. By using a light intensity sensor, rapid changes of light intensity are registered (switching lamps on-off). If the change is rapid, red LED is blinked for 5 seconds. If the light changes smoothly the direction of the change is shown. Green LED means that the light intensity grows and yellow shows that it diminishes.

For advanced

  1. Data recorder. The values of all analogue sensors are measured constantly and minimum and maximum values are recorded. By pressing button S1 the user may change the information displayed on the LCD. Displayed must be: name of the sensor (shortly) and present minimum and maximum values. The sequence of changing the sensors: IR distance sensor → Photoresistor → Thermistor → Potentiometer.
  2. Distance sensor. If the button number S2 is pressed, 10 sequential measurements are taken place in one second. After measuring the average distance to the object in decimetres it is displayed on 7-segment indicator. By pressing button S1, minimum measured value is displayed and by pressing button S3 maximum value is displayed.
  3. Velocity. According to the changing rate of the distance to the object, it is shown with LEDs as follows: slow change – green LED, moderate change – yellow LED and rapid change red LED. The speed may be displayed on the LCD.
  4. Automatic measuring range. According to the current measuring results minimum and maximum temperatures measured are found and correspondingly the range in which the values fit is scaled from 0 to 9. Value is displayed on 7-segment indicator.
  5. By combining IR and ultrasonic distance sensors the distance to the object, velocity of the object and direction of the object are determined. Results are displayed on LCD.

 

Questions

  1. How accurate is the digital-analogue converter of ATmega128 microcontroller? How small is the minimal change of input voltage which can be measured?
  2. How long takes one ADC process? How is the operating frequency changed?
  3. Which is the the range of input voltage of the ADC? Is it possible to be changed? How?
  4. What is the difference between positive temperature coefficient (PTC) thermistor and negative temperature coefficient(NTC)? What are the advantages of each?
  5. What is the purpose of a voltage divider in a measuring circuit?
  6. Combine a voltage divider, which allows to use an analogue sensor with ATmega128 microcontroller. The maximum output voltage of the sensor is 10 V. In addition determine the contents of the ADMUX register.
  7. Extra resistors were added to the potentiometer pins and voltage of 5 V were applied on them. How big must be the resistances of extra resistors and the potentiometer so the regulated voltage on the slider of the potentiometer can be regulated between 1 V and 2 V (from one end to the other end)? The current should not exceed 10 mA.
  8. Which parameters of the surroundings have an effect on the functioning of ultrasonic distance sensor and why?
  9. Which light sensitive sensors could be used in a robotics project. Count at least three principal components and explain their differences.
  10. Besides trigonometrical method, how is distance determined by using light? Name at least 3 methods.

Motors

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.

DC motor

Necessary knowledge: [HW] Motor Module, [AVR] Digital Inputs/Outputs, [LIB] Motors, [LIB] Delay

Theory

DC motor

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).

The perfect graph of relationship between speed (V), current (I), power (P), efficiency (η)and torque (T)of a DC motor.

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.

The working principle of H-bridge used on switches.

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.

Practice

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;
	}
}

Servomotor

Necessary knowledge: [HW] Motor Module, [HW] Sensors Module, [AVR] Digital Inputs/Outputs, [LIB] Motors, [LIB] Analog to Digital Converter

Theory

RC servo motor
The relationship between width of the signal and position of Servo PWM.

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.

Practice

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);
	}
}

Stepper motor

Necessary knowledge: [HW] Motor Module, [AVR] Digital Inputs/Outputs, [LIB] Motors

Theory

Stepper-motor

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

The windings of an 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

The Windings of a 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 + - - +

Practice

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);
	}
}

Exercises

The goal is to write a program which is able to perform tasks described below.

Warm up exercise

  • Controlling the DC motor using a digital board. By pressing S1 button, LED1 is lit and the motor turns clockwise. By pressing S3 button, LED3 is lit and the motor revolves anti clockwise. By pressing S2, LED2 is lit and the motor is stopped.

For beginners

  1. Robot, by using two DC motors and touch sensors movement of a robot is simulated. Touch sensors are the buttons of the digital board (S1…S3). The motor is controlled by pressing the buttons. S1 stops the left motor for 2 seconds and then starts both motors at full speed. S2 stops the right motor for two second and then starts both motors at full speed. If both buttons are pressed, the motors are rotating in reverse direction until the buttons are released.
  2. Servo motor, the servo motor is controlled via the buttons of the digital board. By pressing down S1 the servomotor moves one step to the right. By pressing down S3, the servo motor moves one step to the left and S2 makes the servo motor to move to the initial (middle) position. The position of the servo motor is displayed live on the 7 segment display (each number corresponds to 10 degrees of the turn: middle position equals 5).
  3. Radar, functioning of radar is simulated. In order to identify objects closer than 0,5 meters, IR distance sensor is installed to the lever of the servo motor. The lever of servo motor is moving constantly form one extreme position to the other and it carries this sensor all the time with itself. If there happens to be an object in closer range than 0,5 meters of the sensor, then the servo motor is stopped for 5 seconds and by signaling a LED (PB7) on the controller board detection of the object is announced.
  4. Stepper motor, after each pressing on the buttons S1 and S3 it rotates 10 steps, accordingly clock wise and anti clock wise. The rotation is stopped immediately by pressing on the button S2.
  5. All three different types of the motors are connected. By pressing a button it starts and stops a certain motor. S1 controls the DC motor. S2 controls the servo-motor and S3 controls the stepper-motor.

For advanced

  1. DC motor accelerates when S1 is pressed down and holds achieved speed when the button is released. By holding S2 pressed down, the motor decelerates smoothly. By pressing button S3, the motor stops instantly (simulating emergency stop).
  2. Tracking an object. By using ultrasonic distance sensor, which is installed on the lever of the servo-motor, the servomotor has to track a bypassing object. The motor turns according to the movement of the object so that the object is all the time in the middle of the tracking sector of the sensor.
  3. Stepper motor keeps the last position of the motor after change of each sequence. When activating new sequence, use variable, so the movement continues exactly from the last position of the motor.
  4. Acceleration, the program allows changing the acceleration/deceleration of the stepper motor. Use linear speed slopes which can be easily identified at visual inspection. Longer movements have to follow the following scheme: acceleration –> steady speed –> deceleration.
  5. Design a PID regulator for a DC motor. NB! This exercise demands a motor with feedback. This exercise may be solved also theoretically.

Questions

  1. For what is the H-bridge used? On what principle is it working?
  2. How is the position of the shaft of RC servo motor determined?
  3. What is the main difference between unipolar- and bipolar-stepper motors?
  4. How can half step and micro step modes of stepper-motor be applied? Give an example.
  5. How is the rotation speed of a DC motor controlled? Give an example.
  6. Which PWM working cycle is needed to achieve DC motor's shaft rotation speed 70% of nominal?
  7. How is the direction of motor's rotation determined when encoder is used?
  8. How can a DC motor be electrically slowed?
  9. What happens if the scheme of commutation of a stepper-motor is changing too fast?
  10. Is it possible and if yes, how is it possible to use dynamical braking?

Data interfaces

Data interfaces

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.

RS-232

Necessary knowledge: [HW] Controller module, [AVR] USART, [LIB] Serial Interface, [LIB] Alphanumeric LCD

Theory

Cable RS-232. Left plug is „male” and the right one is „female” plug.”

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.

the frame of UART, where S is start-bit , 0-7 are data-bits, P is parity-bit (if existing) and T is stop-bit (or 2).

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.

Practice

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);
			}
		}
	}
}
The window of the HyperTerminal

With Windows XP OS comes a program called HyperTerminal. It is opened from the Start menu by selecting Accessories CommunicationsHyperTerminal. 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.

Exercises

The goal is to write a program which is able to perform tasks described below.

Warm up exercise

  • To the computer is sent a number with grows once in a second. The number must be in a form of text and end with a row changing sign (\n).

For beginner

  1. For the commands (letter signs) which are coming through RS-232 interface from a computer are being waited. Command “R” lights green LED, “K” lights yellow LED and “P” red LED. “R” is switching all LEDs off.
  2. If a button is pressed, the name of the corresponding button (S1, S2, S3) is received in the computer through the RS-232 interface.

For advanced

  1. The communication between two RS-232 controllers. If a button is pressed, the controller transmits to the controller the number of the button. When the number is received the status of a corresponding LED is changed. '1' → green, '2' → yellow, '3' → red. Requires two sets of controllers, but the software is identical for both controllers.
  2. Make a “teletype” – device, which allows changing text messages between two controllers through RS-232 interface. Use LCD for displaying the messages. On the first row must be seen inserted message and on the second the last received message. For inserting the message a potentiometer and/or buttons may be used.

Questions

  1. Describe the UART package.
  2. What is the baud rate?
  3. What is the difference between full- and half duplexes.
  4. Find at least 5 different sensors which are using serial interface.
  5. What is the difference between UART and USART interfaces? Which is faster?
  6. How is the SPI interface working?
  7. Name interfaces, which enable connecting at least 100 devices to a single bus.
  8. Name different topologies and explain their differences.
  9. On what voltage level are RS-232 and UART connections happening?
  10. How long time takes to transmit 1 MiB at the baud rate of 9600 bps, when there is 8 data-bits, 1 stop-bit and not parity control?

Example projects

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.

Report

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:

  • Titlepage
  • Summary
  • Summary (in foreign language)
  • Table of contents
    1. Initial task
    2. System requirements and limitations
    3. The overall system model
      Structure and functionality of a system as block diagrams. In addition, instructions for use case diagram, interface diagram etc. can be composed.
    4. Design solutions
      At least three different conceptual solutions of how to solve the task. Suitable are manually sketched diagrams, sketches etc. Documentation with comments.
    5. Mechanics
      Preferably a 3D model with drawings of essential nodes and engineering instructions. If a node requires special instructions, then assembly drawings and instructions as well. If possible, add an animation on functioning of the system.
    6. Electronics
      Give the overall block diagram, where all the used modules are showed (controller, motor actuator, motor, encoder etc. and their connections). At opportunity provide a standard electronic unit and PCB assembly layout.
    7. Control system
      Control algorithm and source code. If self made functions are used, then provide a table of functions and parameters, table of interruption priorities etc.
    8. Ready-to-use solution
      Description and pictures
    9. Economic calculation
      The list of components with cost, time estimates etc.
    10. Project management
      The project schedule, resource allocation, division of labor, the crew's contribution, the minutes of meetings etc.
    11. Summary and conclusions
      What was difficult, what would you do differently, what you gained from the project etc. In the summary a short description of the work done, problems encountered and a description of the outcome should be outlined. It is also good to add your opinion on the necessity of the project, what it offers to the members of the project and what should be done differently in the future, what was learned and what would be suggested to the supervisors and organizers of the project.
  • References and materials used
  • Annexes

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 platform

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.

Initial tasks

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:

  • Manipulator
  • Radar
  • Camera

Robot must be able to move on a flat surface in indoors.

Requirements
  • Maximum dimensions: 20 cm x 20 cm x 20 cm
  • Maximum weight 2 kg
  • Speed max. 0,2 m/s
  • Full autonomy
Restrictions
  • Must be constructed mainly from HomeLab components
  • Cannot exceed the cost limit 10 000 EEK

 

The overall model of the system

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.

Model of system structure

Design solutions

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.

Design solutions

 

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) 1927 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

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.

Primary 3D model of the robot and location of its components.

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.

Bumper plate drawing

Electronics

The electronics of the system is described as a principle scheme and electronic scheme with PCB assembly scheme.

Block diagram of electronic components

 

As an example, Line following sensors electric scheme and respective PCB assembly scheme of the robot's bumper is shown.

Electric scheme of the bumper sensors
Assembly scheme of the bumper

 

Control system

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

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:

SymbolMeaning01-1
M1left motorstoprotates clockwiserotates counter-clockwise
M2right motorstoprotates clockwiserotates counter-clockwise
Ffirst middle touch sensorno signalsignal
FRfirst right touch sensorno signalsignal
FLfirst left touch sensorno signalsignal
dreference
Algorithm state diagram
Source code

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);
		}
	}
}

 

Ready-to-use solution

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.

Robot with infrared radar
Robot with intelligent camera module (CMUcam3)

 

Economic calculation

Economic calculation includes the cost of components and robot production costs

Tabel of components cost

ComponentMarkQuantityPriceCost
MotorM LE149.6.432500.-1000.-
MicrocontrolleruC ATmega1281900.-900.-
Motors actuator boardActuator Board v1.21700.-700.-
Power plateTP1500.-500.-
Line following sensorsLFS QRD1114830.-240.-
Touch sensorsTS Microswitch825.-200.-
Hull plateABS 450.-200.-
PCB blank 250.-100.-
Motor mountings profileAl-L 210.-20.-
Wheel60/10 mm 230.-60.-
BatteryNI-MH 9,6 V1350.-350.-
Different cables 1020.-200.-
Nuts-bolts 150.-50.-
Other acsessories 1100.-100.-
Total 4620.-

Estimated labor and production cost for a single copy.

WorkTime (h)PriceCost
Milling construction details1300.-300.-
Milling PCBs (bumpers)0,5500.-250.-
Construction of the robot0,5250.-125.-
Building bumpers (soldering components)1300.-300.-
Programming5300.-1500.-
Compiling documentation3250.-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.

Project management

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.

Action diagram of a project

Summary and conclusions

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.

References and materials used

  1. HomeLab generic manual http://home.roboticlab.eu
  2. ATmega128 datasheet
  3. Dudziak, R., Köhn, C., Sell, R., Integrated Systems & Design, TUT Press, 2008
  4. Friendenthal, S., Moore, A., Steiner, A., A Practical Guide to SysML, Elsevier, 2008
  5. Perens, A. Project Management, Külim, 1999
  6. Bräunl, T. Embedded Robotics, Springer-Verlag, 2003

What's next?

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

de/book.1311234890.txt.gz · Last modified: 2020/07/20 09:00 (external edit)
CC Attribution-Share Alike 4.0 International
www.chimeric.de Valid CSS Driven by DokuWiki do yourself a favour and use a real browser - get firefox!! Recent changes RSS feed Valid XHTML 1.0