-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathlessons.html
606 lines (504 loc) · 29.6 KB
/
lessons.html
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
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
<!DOCTYPE html>
<!--
/*
* Copyright (c) 2008-2010 Chris Kuethe <[email protected]>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
-->
<html lang="en">
<head>
<title>A Brief Tutorial on Programming the ATMega (Arduino) Without
Arduino Software</title>
<meta name="keywords" content="Arduino, arduino, Cduino, cduino, AVR, avr,
ATMega, atmega" />
<meta http-equiv="content-type" content="text/html; charset=utf-8">
<script type="text/javascript">
var _gaq = _gaq || [];
_gaq.push(['_setAccount', 'UA-23228808-1']);
_gaq.push(['_trackPageview']);
(function() {
var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true;
ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js';
var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s);
})();
</script>
</head>
<body>
<table width="720"><tbody><tr><td>
<h2>A Brief Tutorial on Programming the ATMega (Arduino) without Arduino
Software</h2>
This series of tutorials cover programming of many features of the ATMega chip
using the C programming language. They are available as modules in the <a
href="home_page.html">cduino</a> system, so building and
uploading them should be pretty easy.
<p />
<ol>
<li><a href="#blinky1">A Blinking LED - Busy Waits and IO ports</a></li>
<li><a href="#blinky2">Two Blinking LEDs - Addressing Pins</a></li>
<li><a href="#digital-in">A Switched LED - Digital Input and output</a></li>
<li><a href="#usart-out">Serial Output</a></li>
<li><a href="#usart-in">Serial Input</a></li>
<li><a href="#lesson06">Putting it Together: Printing Button Presses and Controlling LEDs</a></li>
<li><a href="#pwm-out">Pulse Width Modulated (PWM) Output</a></li>
<li><a href="#avr-libc-goodies">avr-libc Goodies</a></li>
<li><a href="#adc-in">Analog Input</a></li>
<li><a href="#blinky3">Interrupts and Timers (Blink a LED Efficiently)</a></li>
<li><a href="#eeprom">Persistent Storage Using EEPROM</a></li>
<li><a href="#watchdog">Watchdog Timer Resets</a></li>
<!-- These modules aren't ported and verified in cduino yet
<li><a href="#input-capture">Input Capture</a></li>
<li><a href="#watchdog">Watchdog</a></li>
<li><a href="#i2c-spi">I2C/SPI peripherals</a></li>
-->
</ol>
<ul>
<li><a href="#atmega644p-only">ATmega644P-specific</a></li>
<li><a href="#attiny85-only">ATtiny85-specific</a></li>
<li><a href="#at90usb162-only">AT90USB162-specific</a></li>
</ul>
<hr>
<h3><a name="blinky1">Lesson 1: A blinking LED - Busy Waits and IO ports</a></h3>
<p align="justify">This section is an introduction to microcontrollers. Out of
the box, your microcontroller won't do anything - you need to load a program
before it's useful. Here I present the the hardware equivalent of "Hello
World" - a blinking light.</p>
<p align="justify">To begin, let's look at the <a href="pin_map.html">pin
map</a>. We see that Arduino pin 13 is PB5 on the ATmega168 - part of port B.
To use this pin, port B must first be set to be an output pin. There are
various ways to do this[2] - writing to Port B Data Direction Register[3] at
address 0x24 or the lazy/better way, using the DDRB macro. Let's keep things
simple and set the whole port to output with <tt>DDRB
= 0xff;</tt>. Finally, we can start writing to PORTB (address 0x25).</p>
<p align="justify">A simple approach would would be to do something like
this:</p><pre>while(1){
PORTB = 0xff;
_delay_ms(500);
PORTB = 0x00;
_delay_ms(500);
}</pre>
which makes use of the delay routines defined in <tt><util/delay.h></tt>.
If you try run this, you'll find your LED blinking very rapidly. Far more
rapidly than you'd like.</p>
<p align="justify">A brief inspection of delay.h is instructive: with respect
to _delay_ms, "The maximal possible delay is 262.14 ms / F_CPU in MHz." This
can easily be addressed by computing the maximum time _delay_ms will sleep,
and given that, the number of times to call _delay_ms to achieve the desired
delay interval. You may wish to put this computation into a separate function
which you can call whenever you need to delay.</p>
<p align="justify"> WARNING: this technique doesn't translate to all the other
IO pins on a typical arduino, because the arduino bootloader uses some of them
for its own purposes (e.g. PD0 is set up as the RX pin for serial
communication, which precludes its use as an output). The unconnected IO pins
are presumably ok to use, or you can just nuke the bootloader with an
AVRISPmkII or similar device. </p>
<p align="justify">
<code><a href="xlinked_source_html/lesson1.c.html">lesson1.c</a></code> is one
possible solution.</p>
<hr>
<h3><a name="blinky2">Lesson 2: Two blinking LEDs - Addressing
Pins</a></h3> <p align="justify">This section builds on the previous
section. By now you're probably bored of looking at the onboard LED blinking.
That's good. Let's try hooking up some other LEDS. If you try this, you'll find
they'll all blink together. Why?</p>
<p align="justify">Because you asked for it with <tt>PORTB = 0xff;</tt>. More
astute readers may have figured out that writing all 1's to the port would
set all the pins to 1. If you're new to this... well, I just said it. By the
same token, writing 1 to only a single bit in the port's register will only
turn that pin on.</p>
<p align="justify">Recall that the Arduino pin map said that pin 13 - the LED -
is PB5. In other words, bit 5 of port B, or 0x20. Take a copy of your solution
from lesson 1, and change it to write 0x20 to PORTB, rather than 0xff. After
doing this, your program should blink just the onboard LED.</p>
<p align="justify">Now that you have control over a given port, extend your
program to alternate or cycle the LEDs.
<code><a href="xlinked_source_html/lesson2.c.html">lesson2.c</a></code> is one
possible solution.</p>
<hr>
<h3><a name="digital-in">Lesson 3: A switched LED - Digital Input and
output</a></h3> <p align="justify">We've now become comfortable with basic
digital output, let's work on getting signals in.</p>
<p align="justify">Bear with me a moment while we look at some background on
inputs. We like to think of processors as purely digital entities - ones and
zeros, highs and lows. Alas, there are plenty of analog signals inside, and
depending on your processor (like AVRs and PICs) you may even have specific
analog inputs. A pin connected to ground is logic 0, and a pin connected to
power is logic 1, but what value does an unconnected pin have? Indeterminate.
The pin is said to be floating.</p>
<p align="justify">Typically, pins are connected to high or low through a
relatively high-value resistor, called a pull-up or pull-down. This offers a
weak signal to that pin, putting it into a known state. This known state is
then selectively overriden by shorting the pin to low or high. Think of it as
someone yelling "zero", and someone whispering "one". The very loud zero will
overpower the quiet one, but in the absence of other input, you'll hear the
one. As to what value of resistor to use, I've heard "it depends", "low enough
to get the signal, high enough to not waste power", and "47K". My point in
mentioning this is to let you know that something will need to be done to the
input signal so the AVR knows it's there.</p>
<p align="justify">If you're lazy (or wise), you can tell the AVR to use
internal pull-ups rather than having to mess with them on your own. Using Port
D as an example, let's set up Digital Pin 2 (PD2) as an input with internal
pull-up:</p>
<blockquote>
<code>
<pre>
DDRD &= 0xFB; /* leave all the other bits alone, just zero bit 2 */
PORTD |= 0x04 /* leave all the other bits alone, just set bit 2 */
value = PIND & 0x04; /* and that's what's on the pin... */
</pre>
</code>
</blockquote>
</p>
<p align="justify">Try a program to read a switch connected between digital pin
2 and ground, and use this to control an LED. One possible solution is
<code><a href="xlinked_source_html/lesson3.c.html">lesson3.c</a></code></p>
<hr>
<h3><a name="usart-out">Lesson 4: Serial Output</a></h3> <p
align="justify">Serial is icky. It took several passes through chapter 19 of
the ATmega168 datasheet before all the pieces started to come together. I'd
advise against copying the sample code from the datasheet, at least until you
understand where the magic bits come from. I'd also recommend against making
the init function too configurable; just set up the com port once and be done
with it. Flexible configuration makes your program significantly larger.</p>
<p align="justify">The first set of magic bits (Baud Rate Register) is found in
table 19-1. This is very machine dependent; you must recompile if you change
your device's clock rate. The datasheet also includes tables listing the magic
values. The BRR value is written into a clock generator which clocks the UART.
Since the baud rate register is 12 bits wide and the AVR is an 8-bit core, we
need to split the write into two halves. According to the datasheet the clock
generator is updated when the low byte is written. Thus the baud rate register
should be written to high-byte first.</p>
<p align="justify">Next is framing, most things these days default to "8N1",
and that's what we will do here. Section 19.10.4 (UCSR0C) describes the framing
controls. To use asynchronous mode, set the top 2 bits to 00. To inhibit parity
generation and checking, set the next 2 bits to 00. To use 1 stop bit, set the
next bit to 0. To use 8 data bits, set the next 2 bits to 11 (decimal 3). In
async mode, the bottom bit should be 0. Altogether, <tt>UCSR0C = 0x06; </tt> or
<tt>UCSR0c = (3 << UCSZ00);</tt> will program the register correctly.</p>
<p align="justify">Once the serial port is initialized, you can write to it by
writing to <tt>UDR0</tt>. You should check that the port is actually ready to
transmit; the usual way to do this seems to be a busy wait that tests if the
USART Data Register is Empty - <tt>(UCSR0A & (1 << UDRE0))</tt>.</p>
<p align="justify">Try write a program that prints a fixed string to the serial
port. One way to do this is
<code><a href="xlinked_source_html/lesson4.c.html">lesson4.c</a></code></p>
<hr>
<h3><a name="usart-in">Lesson 5: Serial Input</a></h3>
<p align="justify">Building on lesson 4, we can now handle serial input. The
companion function - receive - busy waits on USART Receive Complete, and then
returns the Data Register <tt>(UCSR0A & (1 << RXC0))</tt>. The busy
wait is not good for applications that shouldn't block on serial IO, but for
now it's good enough.</p>
<p align="justify">Reusing your code from lesson 4, write a
program to read a character from the serial port and echo the next one back.
If you type 'A' it should echo 'B'. One way to do this is
<code><a href="xlinked_source_html/lesson5.c.html">lesson5.c</a></code>.</p>
<hr>
<h3><a name="lesson06">Lesson 6: Putting it
together: printing button presses and controlling LEDs</a></h3> <p
align="justify">I haven't written a sample solution to this yet, but at this
point you should know everything you need to read a switch or the serial port
and use this to control an LED (and print its status to the serial port). When
I do finish this example, you can see
<code><a href="xlinked_source_html/lesson6.c.html">lesson6.c</a></code></p>
<hr>
<h3><a name="pwm-out">Lesson 7: Pulse Width
Modulated (PWM) Output</a></h3> <p align="justify"> Digital outputs have a
hard time generating outputs other than logic low and high - probably +0 and
+Vcc in Arduino applications. It's easy enough to set an LED on a digital pin
to 0% or 100% brightness, and we know it's possible to make LEDs glow at less
than full brightness, but how? The answer is PWM - Pulse Width Modulation.</p>
<p align="justify">PWM works by exploiting the fact that we're using rapidly
changing signals to manipulate physical objects that can't react as quickly.
By sending a series of fast, short pulses we can simulate their average effect.
The math is simple: 100ms@100%+900ms@0% has the same average power over 1
second as 1000ms@10%. In other words, a low-pass filter. With that in mind,
let's set up the the AVR to do PWM output.</p>
<p align="justify">Try writing a program to slowly ramp the brightness of the
LED. Or maybe have it fade in and out.
<code><a href="xlinked_source_html/lesson7a.c.html">lesson7a.c</a></code> is
one way to do this. Alas, this solution sucks - it's basically a big nasty
busy wait that prevents you from getting any real work done while generating
the PWM waveform.</p>
<p align="justify">As we learn about hardware PWM, application Note 130 will
be your friend. Though it's written for the AT90S8535 and doesn't use gcc,
the general principles are still useful. First off, you need a timer to
generate pulses. This is created from a counter, a periodic increment and a
compare register.</p>
<p align="justify">For this example, TCNT0 is the counter, the system clock is
the periodic increment (CS = 1), and OCR0A as the compare register. Section
13.3.3 of the ATmega168 datasheet says that PORTD.6 is the output pin for OC0A.
First, the control register TCCR0A should be set for Fast PWM mode. Then the
compare mode (inverting/non-inverting) is also set in the control register. At
this point, it would be good to initialize the timer and comparator values
because the timer isn't ticking until the clock source is set (control register
TCCR0B). It is now safe to set the output bits on the output port. One way
to do hardware PWM is <code><a href="xlinked_source_html/lesson7b.c.html">
lesson7b.c</a></code>. </p>
<hr>
<h3><a name="avr-libc-goodies">Lesson 8: avr-libc goodies</a></h3>
<p align="justify">Now that you've written a few programs, I imagine you're
getting tired of typing in certain constructs (and your eyeballs are probably
bleeding from the aggregated ugliness). It's time to introduce a few macros
to make things simpler:</a></p>
<table border="1">
<tbody><tr><td><b>By hand</b></td><td><b>Macro</b></td></tr>
<tr><td><code>while ( (UCSR0A & (1 << UDRE0)) ){ ; }</code></td>
<td><code>loop_until_bit_is_clear(UCSR0A, UDRE0);</code></td></tr>
<tr><td><code>while (!(PORTD & 0x4) ){ ; }</code></td>
<td><code>loop_until_bit_is_set(PORTD, _BV(PD2));</code></td></tr>
<tr><td><code>PORTD = 0x4;</code></td>
<td><code>PORTD = _BV(PD2);</code></td></tr>
<tr><td><code>
#define BAUD 9600<br>
UBRR0H = (((F_CPU/BAUD/16)-1) >> 8);<br>
UBRR0L = ((F_CPU/BAUD/16)-1);<br>
UCSR0C = (3 << UCSZ00);<br>
UCSR0B = (1 << RXEN0) | (1 << TXEN0);<br>
</code></td>
<td><code>
#define BAUD 9600<br>
#include <util/setbaud.h><br>
UBRR0H = UBRRH_VALUE;<br>
UBRR0L = UBRRL_VALUE;<br>
UCSR0C = _BV(UCSZ01) | _BV(UCSZ00);<br>
UCSR0B = _BV(RXEN0) | _BV(TXEN0);<br> </code></td></tr>
</tbody></table>
<p align="justify">I'm rather fond of stdio and formatted IO. I've been
spoiled by big machines with multiple multi-GHz cpus, a few GB of memory and a
few TB of storage. Programming an embedded device makes me realize how
wonderful <tt>printf("sensor %d value %f\n", n, values[n])</tt> is. Avr-libc
has some glue to simulate stdio. Here's an example of how to use this:
<code><a href="xlinked_source_html/lesson8.c.html">lesson8.c</a></code>. The
manual warns that printf and scanf are quite resource-intensive. This is not
even a little bit of an exaggeration. Have a look at the annotated assembly
files and be afraid. Be very afraid. In my example implementation, I also use
the program space macros to avoid keeping constant strings in RAM when they
could simply be used straight from flash. </p>
<hr>
<h3><a name="adc-in">Lesson 9: Analog Input</a></h3>
<p align="justify">In this example, we'll use a 10 kohm potentiometer to
provide an analog voltage to sample. </p>
<p align="justify">There are a few little things to do for a simple, polled
analog sample. Ensure that the pin you're reading has pull-ups disabled, since
these interfere with the sampling. Writing 0 to the pin register (PORTC = 0) or
setting it to output mode (DDRC = 0) Set the ADC clock correctly - it needs to
be clocked at 50 kHz - 200 kHz. The Arduino's 16MHz clock divided by 128 is a
very comfortable 125 kHz. Set the reference voltage correctly. This determines
the upper limit for measurement; using Vcc is probably the best bet. USB power
will probably be very stable. Finally, set the ADC enable bit and force one
conversion - the first conversion takes extra time due to some internal setup
that is required. Here is
<code><a href="xlinked_source_html/lesson9.c.html">lesson9.c</a></code>.</p>
<hr>
<h3><a name="blinky3">Interrupts and Timers (Blink a LED Efficiently)</a></h3>
<p align="justify">Like the serial ports, it took a bit of studying of the
datasheets to understand how to make timers work. A large part of this was due
to one incorrectly set bit - it compiled and didn't do what I thought it should
do. As it turns out, a simple blinky light was easier to figure out than
PWM.</p>
<p align="justify">In this example, I'm using the TIMER0_OVERFLOW interrupt to
decide what to do when the timer overflows - whether to toggle the LED or not.
Interrupt handlers must be written carefully if they are run often since they
can cut into more useful processing time. Setting up an interrupt handler has
some noticeable overhead - 14 instructions for the prologue and epilogue -
before the main body of the handler is executed. Furthermore, a since line of C
might be translated into 10 instruction it is very easy to write an interrupt
handler that takes a long time to run. Depending on the length of the handler
and the frequency of interrupts, you may run out of stack space and crash. The
example compiler invokation below includes flags to emit
source-annotated-assembly language.</p>
<p align="justify">Using the 8-bit timer0 is fairly straightforward. There are
four things to be done:</p>
<ol>
<li>select the timer's clock source</li>
<li>allow the timer to generate interrupts</li>
<li>reset the timer</li>
<li>enable interrupt processing</li>
</ol>
<p align="justify">Each of these items takes a just single line of code. First,
the clock source must be configured. The timer system can accept external
inputs or use the system clock - optionally divided down to a slower rate. The
Clock Select (CS00-CS02) bits in the timer/counter control register B (TCCR0B)
are used. Assuming a 16MHz clock, a prescale of 1024 will cause 15625
increment operations per second. Since the counter can hold 256 unique values,
it will overflow at 61Hz. If a higher interrupt rate is needed, the prescaler
can be set lower, or a smaller Output Compare (OCR0A) can be used. The
timer/counter interrupt mask register defaults to 0 - no interrupts will be
generated by the timer/counter system. To enable timer overflow interrupts, set
the TOIE0 bit of TIMSK0. Timers can be set and reset by storing into them, in
this case <tt>TCNT0 = 0</tt>. Finally, the <tt>sei()</tt> function enables
interrupt processing.</p>
<p align="justify">One way to do this is
<code><a href="xlinked_source_html/lesson10.c.html">lesson10.c</a></code>. My
version can blink either the onboard LED on PORTB.5 or a set of LEDs on PORTB.1
- PORTB.3 (with a change to the <code>#if</code> statement in the code).</p>
<h3><a name="eeprom">Lesson 11: Persistent Storage Using EEPROM</a></h3> <p
align="justify">EEPROM provides developers with the ability to persistently
store data on chip, inside the running program. It requires no peripherals, and
doesn't place the program code at risk like writing the flash. Of course,
EEPROM is much slower than RAM access. You should be prepared to deal with
power failures while writing to EEPROM.</p>
<p align="justify">EEPROM use is easy. Because EEPROM is slower than memory,
you first need to check that the EEPROM is ready to accept operations. There is
a function - <code>eeprom_is_ready()</code> - to do this, and this is the core
of the <code>eeprom_busy_wait()</code> function. Once the EEPROM is ready, it
can be written with <code>eeprom_write_{byte,word,dword,block}</code> and read
back with <code>eeprom_read_{byte,word,dword,block}</code>. See
<code><a href="xlinked_source_html/lesson11.c.html">lesson11.c</a></code> for
an example.</p>
<p align="justify">To safely use EEPROM storage in applications where power
loss is a significant risk, consider storing a version counter and a validity
flag in the structure. The structure should be marked invalid before any
changes are made, and should only be marked valid once all other changes are
made. Keep two (or more) copies of the structure and rotate through them;
The version counter can be used to determine which is the most current copy.
In the event power is lost during a write, older versions remain intact. You
may want to have a look at "AVR101: High Endurance EEPROM Storage". </p>
<h3><a name="watchdog">Lesson 12: Watchdog Timer Resets</a></h3>
<p align="justify"> WARNING WARNING WARNING: you really shouldn't be depending
on the watchdog timer for anything without a really careful reading of Atmel
document AVR132 "Using the Enhanced Watchdog Timer". Obviously if your
application needs this functionality for anything important, you have some
careful thinking to do to make sure the watchdog timer really offers the
robustness you're looking for. </p>
<p align="justify">Because some arduino bootloaders do not clear the watchdog
timer reset flag early in the boot sequence (potentially resulting in infinite
reset loops), the AVRISPmkII programming method (and a connected AVRISPmkII
programmer) must be used for this lesson. </p>
<p align="justify"> The onboard watchdog timer can be used to reset the AVR if
the program unexpectedly hangs. The timer is first enabled, and after being
enabled must be reset more frequently than the interrupt interval to avoid
triggering a reset. If your code hangs somewhere, the timer reset won't
happen, and so the whole processor will be reset. The program in
<code><a href="xlinked_source_html/lesson12.c.html">lesson12.c</a></code> shows
how this works. </p>
<p align="justify">The newer AVR chips (including the ones on the arduino) also
allow an interrupt to be generated instead of or in addition to (before) the
reset. This usage is not covered here. </p>
<p align="justify"> Finally, because the newer AVRs use a reset flag that must
be cleared early in the AVR boot process to avoid re-resetting after a watchdog
reset, careful programs will want to clear this flag early in the boot process.
Even if your code never explicitly enables the watchdog timer at all, it is
possible that it could get turned on by erroneous code or electrical noise.
The <code>fetch_and_clear_mcusr()</code> declaration in
<code><a href="xlinked_source_html/lesson12.c.html">lesson12.c</a></code> shows
how to ensure that the watchdog timer bit is always cleared early in the boot
process. </p>
<!-- This is the old description of the interrupt-generation WDT usage, whic
we don't do yet. FIXME: probably should just remove this
From
time to time, it may be useful to have an interrupt or reset delivered
independently of the main program code. This may be to wake the AVR from sleep
mode or to reset when the program gets stuck in a loop; that's where the
watchdog timer comes in. An onboard 128kHz oscillator is used to drive the
watchdog with intervals from 16ms to 8s. If the watchdog expires without being
touched, the system can catch an interrupt, be reset or both. If you "kick the
dog" before the time expires, no action is taken.</a></p>
<p align="justify">While it can be slightly complicated to set up the watchdog
by hand, avr-libc has some convenience functions. To enable the watchdog, call
<tt>watchdog_enable()</tt> with the desired duration. Refreshing the watchdog
is as simple as a call to <tt>watchdog_reset()</tt>. Note that if the
<tt>WDTON</tt> fuse is set the watchdog activates automatically, rather than
requiring explicit activation.</p>
<p align="justify">A short example of using the watchdog timer to wake from a
deep sleep mode is found in </a> <a href="lessons/lesson13.c.html">lesson13.c</p>
<a name="i2c-spi"></a><h3><a name="i2c-spi">Lesson 14: I2C/SPI peripherals</a></h3>
-->
<!-- FIXME: these modules haven't been added to cduino yet
<a name="input-capture"></a><h3><a name="input-capture">Lesson 13: Input Capture</a></h3>
<p align="justify"><a name="input-capture">One of the useful features of the AVR is the input capture
system. An application requiring low latency timestamping (a stopwatch or a
pwm detector) could benefit from this. Input capture works by atomically
copying the current value of the Timer/Counter Register into the Input Capture
Register. The timer continues running and can optionally be reset to zero.
Inside the input capture interrupt handler the user code can run without
having to take heroic measures to minimize time spent reading the timer or
resetting it - this is all done in hardware.</a></p>
-->
<h3> <a name="atmega644p-only">ATmega644P-specific</a></h3> <p
align="justify">Most of this document should be applicable to any AVR board. I
wrote it using a Freeduino SB which has an ATmega168. I'm also running a
Sanguino which is based on the ATmega644P. This section covers some of the
differences when running on this hardware.</p>
<p align="justify">I recently ported an application I built on the '168 to the
'644P. The only changes I needed to make were to the pins with the LEDs and the
input capture pin. Most Arduino compatible boards seem to use PORTD.5 for the
on-board <i>blinkenlicht</i> - the Sanguino I'm working with uses PORTB.0.
Input capture moves from PORTB.0 to PORTD.6. After some thought I changed all
of the program's references to specific ports and pins to preprocessor macros
allowing me to use a different set of pins depending on the board I'm running.
</p>
<h3><a name="attiny85-only">ATtiny85-specific</a></h3> <p align="justify">Most
of this document should be applicable to any AVR board. I wrote it using a
Freeduino SB which has an ATmega168. I'm also running some ATtiny85 parts. This
section covers some of the differences when running on this hardware.</p>
<p align="justify">While porting my firefly simulator to the tiny85, I found
the smaller peripheral mix got to be very annoying. In particular, TIMER2 which
can wake the AVR from sleep does not exist, nor does the SLEEP_MODE_PWR_SAVE.
Power down mode does exist, but it may disable too many other parts of the
processor. In this case, I was able to use the watchdog timer to wake from
SLEEP_MODE_PWR_DOWN, but the big lesson was to carefully read the datasheets
before committing to a particular part.</p>
<h3><a name="at90usb162-only">AT90USB162-specific</a></h3>
<p align="justify">Most of this document should be applicable to any AVR board.
I wrote it using a Freeduino SB which has an ATmega168. I'm also running a
Teensy which is based on the AT90USB162. This section covers some of the
differences when running on this hardware.</p>
<h3>References</h3>
<li>[1] <a href="pin_map.html">pin_map.html</a></li>
<li>[2] http://www.atmel.com/dyn/resources/prod_documents/avr_3_04.pdf
</li>
<li>[3] http://www.atmel.com/dyn/resources/prod_documents/doc2545.pdf
</li>
<li>http://avrbasiccode.wikispaces.com/Atmega168</li>
<li>http://javiervalcarce.es/wiki/Program_Arduino_with_AVR-GCC</li>
<li>http://www.ladyada.net/learn/Arduino/</li>
<li>http://ccrma.stanford.edu/courses/250a/docs/avrlib/html/</li>
<li>http://www.nongnu.org/avr-libc/user-manual/</li>
<li>http://www.engbedded.com/cgi-bin/fc.cgi</li>
<li>http://piconomic.berlios.de/</li>
<li>http://www.sanguino.cc/</li>
<li>http://www.pjrc.com/teensy/</li>
<li>... and the rest of atmel's ginormous tech library</li>
<h3>Appendix A: compiling and loading your code</h3>
The simple way is to use the build framework in <a
href="home_page.html">cduino</a>.
<p />
It's also possible to run the build commands yourself. It should look
something like the example below. Note that you'll probably have to change
some file and processor names to make this work:
<p />
<pre>avr-gcc -g -mmcu=atmega168 -c example.c -Wa,-alh,-L -o example.o > example.asm
avr-gcc -g -mmcu=atmega168 -Wl,-Map,example.map -o example.elf example.o
avr-objdump -h -S example.elf > example.lst
avr-objcopy -j .text -j .data -O ihex example.elf example.hex
avr-size example.elf
avrdude -b19200 -P /dev/cuaU0 -c avrisp -p m168 -U flash:w:example.hex
</pre>
<pre>avr-gcc -g -mmcu=atmega644p -c blinkprint.c -Wa,-alh,-L -o blinkprint.o > blinkprint.asm
avr-gcc -g -mmcu=atmega644p -Wl,-Map,blinkprint.map -o blinkprint.elf blinkprint.o
avr-objdump -h -S blinkprint.elf > blinkprint.lst
avr-objcopy -j .text -j .data -O ihex blinkprint.elf blinkprint.hex
avr-size blinkprint.elf
avrdude -c usbtiny -p atmega644p -U flash:w:blinkprint.hex
</pre>
<p align="justify">NB: older versions of GCC may not support newer mcu types
like <tt>-mmcu=atmega168</tt> or <tt>-mmcu=atmega644p</tt>. For some of
these examples you may be able to use <tt>-mmcu=avr5</tt>, but things like
the serial port won't work correctly or at all without the correct mcu flag.
Arduino-0011 uses GCC 4.0.4, that should be considered the minimum required
version.</p>
<p>Copyright 2008,2009 Chris Kuethe <[email protected]><br>
</p></td></tr></tbody></table>
</body></html>