summaryrefslogtreecommitdiff
path: root/digital/avr/doc/training/training.txt
blob: 318e0b88777380afa8946cb374123daa5a1ba40a (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
==============
AVR Training
==============
:Author: djerem

.. contents:: Table of Contents


Introduction
============

AVR chips are quite like PC but smaller. They have many advantages:

  simple
    you just need to wire them and they work out of the box; no need to add
    more components. You just need to program them to control their
    input-output;

  open
    they are compatible with open-source softwares;

  interface
    we can made them work with tons of components.

.. figure:: avr_architecture.png
   :align: center
   :alt: AVR architecture diagram

   Structural AVR diagram.

AVR are powered with 5V.

Two input pins of the AVR can be used to make it work with an external clock,
generally generated by a quartz. This way, you can increase both its speed and
accuracy.

The communication between the AVR and the PC can be done with the serial port.

**Be careful**, you need to convert the -12/+12V of the PC to the 0/5V of the AVR
(you can use a MAX232 component to achieve this).


Input and output
================

Input and output are regrouped by port, each one composed of 8 pins. Ports
management are thus done by 8 bits words.

Each port disposes of 3 registers: each register have 8 bits and each bits
are mapped to a pin of the port.

Here is a description of the 3 registers of each port:

  ``PIN``
    it is a *read* register, reflecting the current state of the port. It is
    thus used to read the input and output state of the port. Of course, you
    can only read it and never write to it;

  ``DDR``
    it used to to select the port *direction*:

      * input mode: it is set to ``0``. This is the default case;

      * output mode: it is set to ``1``.

  ``PORT``
    it has got two functionalities:

      * if we are in input mode, it can be used to enable a *pull-up*
        mechanism;

      * if we are in output mode, it can be used to define the *state* of the
        pin (higher or lower state).

Here is a summary of the use of the ``PORT`` register:

+---------+----------+------------------+
| ``DDR`` | ``PORT`` | Result           |
+=========+==========+==================+
|    0    |     0    | No pull-up,      |
|         |          | High impedance   |
+---------+----------+------------------+
|    0    |     1    | pull-up in use   |
+---------+----------+------------------+
|    1    |     0    | output state = 0 |
+---------+----------+------------------+
|    1    |     1    | output state = 1 |
+---------+----------+------------------+


Example of port A use
*********************

With one led
~~~~~~~~~~~~
A led is connected to the first pin of the A register (we called this pin
*A0*). We would like to make the led blinked.

Here is the algorithm you should used::

  // enable output mode for the pin A0
  DDRA = 1

  // infinite loop
  while (1)
  {
     
     // Switch on the led by putting the A0 pin in higher state
     PORTA = 1

     // Wait a few moment (to be sure we have seen the led on)
     Delay (tps)  // The delay function already exist on the APBteam
                  // repository

     // Swith off the led by puttin the A0 pin in lower state
     PORTA = 0

     // Wait again
     Delay (tps)
  }

Remember to protect your led with a resistor.


With two led
~~~~~~~~~~~~

Now, we would like to add a second led, connected to the pin *A3* and that
will light on when the first one is switched off and vice-versa.

By using our previous algorithm, we just need to change the following code::

  // We would like to put a 1 (high state) on the third bit
  // So 2³ = 8
  DDRA  = 1  ->  DDRA  = 8
  PORTA = 1  ->  PORTA = 0
  PORTA = 0  ->  PORTA = 8

The problem with this algorithm is that we will also change the other bits of
the ``PORTA``. For example, setting ``PORTA`` to 8 (00001000 in binary) will
make us loose the first bit we have previously set to 1. Consequence, we will
never switch on the first led (connected to the *A0* pin). Not really
useful...

To prevent the change of the bits of the other pins of the port, we need to
use some logical function:

  to put a bit to 1
    you need to use a logical *or*. Here is a sample algorithm to change the
    change from 0 to 1 the third bit of the ``PORTA`` register::

      // 8 correspond, in binary, to 00001000.
      PORTA |= 8

  to put a bit to 0
    you need to use a logical *and* between the ``PIN`` register and an 8 bits
    word with all bits set to 1 expect the bit you want to change.  Here is a
    sample algorithm to change from 1 to 0 the third bit of the ``PORTA``
    register::

      // ~8 correspond to the opposite of 8. In binary, it means the opposite
      // of 00001000, that is to say 11110111. Only the third bit is set to 0
      // because it is the one we would like to change.
      PORTA &= ~8

In fact, there is a macro ``BV_`` (defined in ``common/io.h``) which lets you
directly work with the desired bit of a byte. If we use this macro, the
previous code will become::

  // 1 << 3 = 8
  PORTA &= ~_BV(3)
  PORTA |= _BV(3)

You can also work with multiple bits at the same time. For example, if you
want to change both second and third bit of the ``PORTA`` register to 1, you
can do the following::

  // 2³ + 2² = 12
  PORTA |= 12
  // You can also use the BV_ macro:
  PORTA |= _BV(2) | _BV(3)

In fact, you should try to use the ``BV_`` macro as much as possible: it
usually make the code easier to read and understand.


Many devices ready to use in the AVR
====================================

AVR chip contains some ready to use devices. For example:

  * an analog/digital converter: it uses a specific register;

  * a serial port: it uses configuration, reception and emission registers;

  * an I2C/TWI port;

  * a watchdog timer to make the AVR restart when it crashes;

  * PWM;

  * ...


Polling mode versus interrupts
=================================

Polling mode
************

The polling mode corresponds to the action of regularly check something to see
if there is change and take actions according to. For example, with the serial
port, you need to do something like::

  If there is a byte arrived on the serial port
    If no:
       Go on with the main program;
    If yes:
       I manage it now;

We need to do this algorithm between each program instruction to be able to
manage data as they arrive. Expect the complexity of doing this every time, it
decrease the performance by losing time for the verification of data on the
serial port.

To solve this issue, we can use interrupts.


Interruptions
*************

Some pins of the AVR on specific ports can trigger some interrupts. For
example, when there is a logical state change on a pin, an interrupt is
triggered. It can be handle or not, depending on its level.

Let's take our previous example with the serial port in polling mode and adapt
it for interrupts::

  I am currently executing the instruction of my program, normally;
  Coming from nowhere without a warning, an interrupt is triggered because a
   has arrived on the serial port;
  My program is stopped and sent to the handler of this interrupt:
    I get the data received on the serial port and manage it;
  When my interrupt handler is finished, I am going back to the place where I
   have been stopped in my program;

Interrupts are managed in a linear way: when two interrupts are triggered at
the same time or within a little time, there is no way to predict which one
has come first.

Things to keep in mind for interrupts:

  volatile variables
    they force the compiler to never optimize their access. This way, the AVR
    need to get the value where it is stored and never caches it.
    You need to use them every time you have a variable that it is used in the
    interrupt handlers and in other functions of the program. It ensures you
    the value you are working is the actual one, anywhere in your program;

  enable interrupts
    in order to enable interrupts, you must use the function ``sei ()`` in
    your code;

  disable interrupts
    in order to disable interrupts, you must use the function ``cli ()`` in
    your code;


.. I do not understand this part:
   * Interrupteur en entrée
   Il est possible de mettre un interrupteur en entrée sur les AVR. Mais lorsque
   l'interrupteur est ouvert, il faut que la patte soit branchée. Elle ne peut
   rester dans le vide. Pour cela, il suffit d'activer le mode ''pull-up'' de la
   patte correspondante. Attention au rebond !


AVR at APBTeam
==============

AVR used
********

We use two kind of AVR, from Atmel: ATmega64 and ATmega128. The difference
between the tow is, expect the price, the size of the memory flash to store
the program (64k and 128k).


AVR code
********

I can only advise you to have a look at the code in the SVN repository, in the
directory `trunk/digital/avr
<https://apbteam.org/browser/trunk/digital/avr>`_. You can find here some
documentations (this one for example), the code of the program of the
different card (IO, Asserv), a simulator to make the code work on PC and even
some examples.

Repository organisation
~~~~~~~~~~~~~~~~~~~~~~~
The directory `trunk/digital/avr
<https://apbteam.org/browser/trunk/digital/avr>`_ of the repository is organised in the
following way::

	|-- common
	|   >>> Some general defines to include in programs
	|-- doc
	|   >>> Some documentations
	|-- make
	|   >>> Makefile used for the build
	`-- modules
	    |   >>> Many modules ready to use
	    |-- adc
	    |   >>> Analogic to digital conversion
	    |-- host
	    |   >>> Build AVR program for PC architecture
	    |-- math
	    |   >>> floating point computation, random number generation
	    |-- proto
	    |   >>> Serial protocol management
	    |-- twi
	    |   >>> I2C bus management
	    |-- uart
	    |   >>> Serial bus management
	    `-- utils
	        >>> Some useful functions, like reset, delay, ...

To create a project, have a look at the `build.txt
<https://apbteam.org/browser/trunk/digital/avr/doc/build.txt>`_ document.


Resources
=========

 * Datasheets of the AVR component (available on the Atmel website);
 * Documentation on the AVR libc:
   http://www.nongnu.org/avr-libc/user-manual/index.html;
 * `AVRFreaks <http://www.avrfreaks.net/>`_ with many things on everything;


Acknowledgment
==============

Thanks to Ni for taking the time to give us this training while he was ill!