Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
C Programming for microcontrollers (Joe Pardue, 2005).pdf
Скачиваний:
260
Добавлен:
12.08.2013
Размер:
4.55 Mб
Скачать

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