- •Chapter 1: Introduction
- •Goals
- •Chapter 2: Quick Start Guide
- •Software
- •WinAVR – Oh, Whenever…
- •Programmers Notepad
- •AVRStudio – FREE and darn well worth it.
- •Br@y++ Terminal:
- •Hardware
- •Constructing Your Development Platform
- •Blinking LEDs – Your First C Program
- •Write it in Programmers Notepad
- •Download to the Butterfly with AVRStudio
- •Blinky Goes Live
- •Simulation with AVRStudio
- •GOOD GRIEF!
- •Comments
- •Include Files
- •Expressions, Statements, and Blocks
- •Operators
- •Flow Control
- •Functions
- •The Main() Thing
- •Chapter 4: C Types, Operators, and Expressions
- •Data Types and Sizes
- •Seen on a shirt at a Robothon event:
- •Bits
- •Bytes
- •The long and short of it
- •Variable Names
- •Constants
- •Declarations
- •Arithmetic Operators
- •Relational and Logical Operators
- •Bitwise Operators
- •Testing Bits
- •Assignment Operators and Expressions
- •Conditional Expressions
- •Precedence and Order of Evaluation
- •Projects
- •Port Input and Output
- •Cylon Eye Speed and Polarity Control
- •Chapter 5: C Control Flow
- •Statements and Blocks
- •If-Else and Else-If
- •Switch
- •Loops – While, For and Do-while
- •Break and Continue
- •Goto and Labels
- •A few practical examples: strlen, atoi, itoa, reverse
- •Chapter 6: C Functions and Program Structures
- •Function Basics
- •Returns
- •Variables External, Static, and Register
- •Scope
- •Headers
- •Blocks
- •Initialization
- •Recursion
- •Preprocessor
- •Macro Substitution
- •Conditional Inclusion
- •Projects
- •Is anybody out there? Communicating with a PC
- •Demonstrator
- •PC_Comm
- •Using CommDemo:
- •Chapter 7: Microcontroller Interrupts and Timers
- •Interrupts
- •Projects
- •Grab your joystick – and test your interrupts
- •Using joystick
- •Timers/Counters
- •Calibrating the Butterfly oscillator:
- •OSCCAL_calibration() function – detailed explanation
- •ALL THIS AND WE HAVEN’T EVEN STARTED CALIBRATING YET!
- •Projects
- •Precision Blinking
- •Using Precision Blinking:
- •Pulse Width Modulation – LED Brightness Control
- •Pulse Width Modulation - Motor Speed Control
- •Speedometer
- •Chapter 8: C Pointers and Arrays
- •Addresses of variables
- •Function Arguments
- •Arrays
- •FIFOs and LIFOs: Stacks and Queues (Circular Buffers)
- •Stacks
- •Queues (Circular Buffers)
- •Function Pointers
- •Complex Pointer and Array Algorithms
- •Projects
- •Messenger
- •Arrays in RAM and ROM
- •Does anybody know what time it is? A Real Time Clock.
- •A one second interrupt
- •Converting Computer Time to Human Readable Time
- •The Real Timer Clock Software
- •Music to my ears. “Play it again Sam.”
- •More on pointers to arrays
- •Setting the frequency
- •Setting the duration
- •An example song array – Fur Elise
- •Using the Piezo-element to make sound
- •Initializing the Timer1 for PWM to the piezo-element.
- •Generating the tone using PWM from Timer1
- •Using the Timer0 interrupt to play a tune
- •Chapter 9 – Digital Meets Analog – ADC and DAC
- •But First - A Debugging Tale
- •Analog to Digital Conversion
- •What is Analog to Digital Conversion?
- •Analog to Digital Conversion by Successive Approximation
- •Analog to Digital Conversion with the ATMEGA169
- •Starting a Conversion
- •Conversion Timing
- •Changing Channels
- •Digital Noise Reduction
- •Conditioning the Analog Input Signal
- •Accuracy
- •Projects
- •Initializing the ADC
- •Reading the ADC
- •Light Meter
- •Temperature Meter
- •The @#%#&*#!!!! Volt Meter
- •Using ADC
- •DAC and ADC - Function Generator / Digital Oscilloscope
- •Chapter 10: C Structures
- •Structure Basics
- •Structures and Functions
- •Structure Arrays
- •Typedef
- •Unions
- •Bit-fields
- •Bit-Fields the C-way
- •Bit-fields the masking-way
- •Projects
- •Finite State Machine
- •Chapter 11 The Butterfly LCD
- •PC to LCD test program
- •Conclusion
- •Appendix 1: Project Kits
- •Data I/O
- •PWM Motor Control
- •Appendix 2: Soldering Tutorial
- •Appendix 3: Debugging Tale
- •Appendix 4: ASCII Table
- •Appendix 5: Decimal, Hexadecimal, and Binary
- •Appendix 6: Motor Speed Control Wheel
- •Appendix 7: HyperTerminal
- •Index
Appendix 3: Debugging Tale
Appendix 3: Debugging Tale
Sometimes you have to search high and low to find out what a something really means. For instance, we often see the sbi() function, as in Butterfly main.c:
sbi(DDRB, 5); |
// set OC1A as output |
sbi(PORTB, 5); |
// set OC1A high |
We search around a while and eventually in sfr_def.h we find:
/** \def sbi \ingroup avr_sfr \deprecated
\code #include <avr/io.h>\endcode
For backwards compatibility only. This macro will eventually be removed.
Set bit \c bit in IO register \c sfr. */
#define sbi(sfr, bit) (_SFR_BYTE(sfr) |= _BV(bit))
This means that sbi() is not a function, it’s a macro, and a deprecated one at that. By deprecation, they mean that we shouldn’t use in and eventually it may go away. To understand what it does though, we need to find the definition of SFR_BYTE(sfr) and _BV(bit) and we can now guess these aren’t functions, but macros. More searching and in the same header we find:
#define _SFR_BYTE(sfr) _MMIO_BYTE(_SFR_ADDR(sfr))
Hmmm… that’s not a lot of help so more searching to find out what _MMIO_BYTE and _SFR_ADDR mean. In the same header we find:
#define _MMIO_BYTE(mem_addr) (*(volatile uint8_t *)(mem_addr))
Okay, we still don’t know. So we look for uint8_t, which is tucked away in the bf_gcc directory readme.txt:
- changed some char to uint8_t to avoid compiler warnings.
279
Appendix 3: Debugging Tale
Now we can speculate that uint8_t is a char. I say speculate because going further would require poking around in the gcc compiler stuff, and I’d rather live with my guess than suffer that. Anyway, I’ve already lost track of what I was trying to figure out in the first place. All theses layers of deception can be quite dense. Let’s state what we found.
_MMIO_BYTE is a macro that declares mem_addr to be a pointer to a volatile char pointer. I’m getting scared, how about you?
Backing up a bit we look at the _SFR_BYTE macro and see that it provides _MMIO_BYTE with a mem_addr of the type _SFR_ADDR. Oh, bother. What is a _SFR_ADDR? Well, in sfr_defs.h we find:
#define _SFR_ADDR(sfr) _SFR_MEM_ADDR(sfr)
Which doesn’t help so we look for _SFR_MEM_ADDR and find:
#define _SFR_MEM_ADDR(sfr) ((uint16_t) &(sfr))
We now know that _SFR_ADDR is an alias for _SFR_MEM_ADDR which is macro to declare sfr as a uint16_t, and we’ll guess that’s a 16 bit integer. What the heck is the & for? Let’s do some substitutions. If you remember we were trying to understand the meaning of the sbi macro and it had _SFR_BYTE(sfr) in it so:
We had: |
_SFR_BYTE(sfr) |
which is and alias for: |
_MMIO_BYTE(_SFR_ADDR(sfr)) |
the _ SFR_ADDR aliased |
_MMIO_BYTE(_SFR_MEM_ADDR(sfr)) |
that aliased |
_MMIO_BYTE( ((uint16_t) &(sfr)) ) |
and _MMIO_BYTE aliased |
(*(volatile uint8_t *)( ((uint16_t) &(sfr)) )) |
which we can substitute for the _SFR_BYTE(sfr) in the sbi macro
#define sbi(sfr, bit) (_SFR_BYTE(sfr) |= _BV(bit))
280
Appendix 3: Debugging Tale
Substitution yields:
#define sbi(sfr, bit) ( (*(volatile uint8_t *)( ((uint16_t) &(sfr)) )) |= _BV(bit))
And _BV is? In pgmspace.h it is:
#define _BV(bit) (1 << (bit))
More substitiuton yields:
#define sbi(sfr, bit) ((*(volatile uint8_t *)(((uint16_t) &(sfr)) )) |= (1 << (bit)))
By the by, what’s a volatile? It is an implementation specific type qualifier that tells a complier to suppress optimization that might, in our case, screw things up if we are using pointers to memory mapped registers. We don’t want the compiler to help us by using some other memory address since a register is hardwired into the machine and though addressed like memory, isn’t ordinary memory. Volatile also tells the compiler that that the so modified variable can change unexpectedly (like by an interrupt) so it needs to be checked each time it is used and not just stored somewhere like on the stack.
More substitution for an actual use of sbi:
sbi(PORTB, 5); |
// set OC1A high |
yields:
((*(volatile uint8_t *)(((uint16_t) &(PORTB)) )) |= (1 << (5)))
But the complier doesn’t know what PORTB is. What is it?
From io169.h
#define PORTB _SFR_IO8(0x05)/* PORTB */
and _SFR_IO8 id defined in sfrdefs.h:
281
Appendix 3: Debugging Tale
#define _SFR_IO8(io_addr) _MMIO_BYTE((io_addr) + 0x20)
Goody, we already know what _MMIO_BYTE is so we can do substitutions:
PORTB is _SFR_IO8(0x05)
_SFR_IO8(0x05) is _MMIO_BYTE((0x05) + 0x20) _MMIO_BYTE((0x05) + 0x20) is (*(volatile uint8_t *)((0x05) + 0x20)) yields:
((*(volatile uint8_t *)(((uint16_t) &((*(volatile uint8_t *)((0x05) + 0x20)))) ))
|= (1 << (5))) |
|
What we wrote was: |
|
sbi(PORTB, 5); |
// set OC1A high |
What the compiler sees is:
((*(volatile uint8_t *)(((uint16_t) &((*(volatile uint8_t *)((0x05) + 0x20)))) )) |= (1 << (5)))
Aren’t you glad you aren’t a compiler?
282