原创 Alternative models for memory-mapped devices (Part 2)

2011-3-22 16:56 2031 12 12 分类: 消费电子

[Continued from Alternative models for memory-mapped devices (Part 1)]


Using structures

Structures provide a better way to model memory-mapped devices. You can use a structure to represent each collection of device registers as a distinct type. For example:

 

typedef uint32_t volatile device_register;

typedef struct timer_registers timer_registers;

struct timer_registers

{

device_register TMOD;

device_register TDATA;

device_register TCNT;

};

 

The typedef before the struct definition elevates the tag name timer_registers from a mere tag to a full-fledged type name.9 It lets you refer to the struct type as just timer_registers rather than as struct timer_ registers.

 

You can provide corresponding structures for each device type:

 

typedef struct UART_registers UART_registers;

struct UART_registers

{

device_register ULCON;

device_register UCON;

device_register USTAT;

device_register UTXBUF;

~~~

};

 

Using these structures, you can define pointers that let you access device registers. You can define the pointers as macros:

 

#define the_timer ((timer_registers *)0xFFFF6000)

#define UART0 ((UART_registers *)0xFFFFD000)

 

or as constant pointers:

 

timer_registers *const the_timer = (timer_registers *)0xFFFF6000;

UART_registers *const UART0


= (UART_registers *)0xFFFFD000;

 

In C++, using a reinterpret_cast is even better:

 

timer_registers *const the_timer

 

= reinterpret_cast (0xFFFF6000);

UART_registers *const UART0


= reinterpret_cast (0xFFFFD000);

 

Whichever way you define the pointers, you can use them to access the actual device registers.

 

For example, you can disable the timer using the expression:

 

the_timer->TMOD &= ~TE;

 

Even better, you can wrap it in an inline function:

 

inline

void timer_disable(timer_registers *t)

{

t->TMOD &= ~TE;

}

 

or a function-like macro:

 

#define timer_disable(t) ((t)->TMOD &= ~TE)

 

Whether you use an inline function or a macro, you can simply call:

 

timer_disable(the_timer);

 

For device operations that use more than one register, you can pass just the address of the entire register collection rather than individual registers. Again, sending data to a UART uses both the UART status register and the transmit buffer register. You can declare the UART_put function as:

 

void UART_put(UART_registers *u, int c);

 

and write it so that it picks out the specific registers that it needs. A call to the function looks like:

 

UART_put(UART0, c);

 

which is just a tad simpler than it was before. Plus, you can't accidentally mix registers from two UARTs at once.

 

Using structures avoid other mistakes as well. Each struct is a truly distinct type. You can't accidentally convert a "pointer to timer_registers" into a "pointer to UART_registers." You can only do it intentionally using a cast. Thus, compilers can easily catch accidents such as:

 

timer_disable(UART0); // compile error

UART_put(the_timer, c); // compile error

 

One of the problems with using structures to model collections of memory-mapped registers is that compilers have some freedom to insert unused bytes, called padding, after structure members.10 You may have to use compile switches or pragma directives to get your structures just so.4 You can also use compile-time assertions to verify that the structure members are laid as they should be.11

 

Classes are even better

In C++, using a class to model hardware registers is even better than using a struct. I'll show you why in my upcoming articles..

 

Endnotes:

1. Saks, Dan. "Mapping Memory," Embedded Systems Programming, September 2004, p. 49.

2. Saks, Dan. "Mapping Memory Efficiently," Embedded Systems Programming, November 2004, p. 47.

3. Saks, Dan. "More ways to map memory," Embedded Systems Programming, January 2005, p. 7.

4. Saks, Dan. "Sizing and Aligning Device Registers," Embedded Systems Programming, May 2005, p. 9.

5. Barr, Michael. "Introduction to fixed-width integers," Embedded.com, January 2004.

6. Saks, Dan."Use Volatile Judiciously," Embedded Systems Programming, September 2005, p. 8.

7. Saks, Dan."Place Volatile Accurately," Embedded Systems Programming, November 2005, p. 11.

8. Meyers, Scott. "The Most Important Design Guideline?" IEEE Software, July/August 2004, p.14.

9. Saks, Dan. "Tag Names vs. Type Names," Embedded Systems Programming, September 2002, p. 7.

10. Saks, Dan. "Padding and rearranging structure members," Embedded Systems Design, May 2009, p. 11.

11. Saks, Dan, "Catching Errors Early with Compile-Time Assertions," Embedded Systems Programming, July 2005, p. 7.

文章评论0条评论)

登录后参与讨论
我要评论
0
12
关闭 站长推荐上一条 /2 下一条