I found the following #define statement confusing.
#define LPC_GPIO0 ((LPC_GPIO_TypeDef *) LPC_GPIO0_BASE )
I think I don't understand how the pointer thing works. So I googled an article to read.
.END
Placing C variables at specific addresses to access memory-mapped peripherals
http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.faqs/ka3750.html
In most ARM embedded systems, peripherals are located at specific addresses in memory. It is often convenient to map a C variable onto each register of a memory-mapped peripheral, and then read/write the register via a pointer. In your code, you will need to consider not only the size and address of the register, but also its alignment in memory.
Basic Concepts
The simplest way to implement memory-mapped variables is to use pointers to fixed addresses. If the memory is changeable by 'external factors' (for example, by some hardware), it must be labelled as volatile.
Consider a simple example:
#define PORTBASE 0x40000000
unsigned int volatile * const port = (unsigned int *) PORTBASE;
The variable port is a constant pointer to a volatile unsigned integer, so we can access the memory-mapped register using:
*port = value; /* write to port */
value = *port; /* read from port */
The use of volatile ensures that the compiler always carries out the memory accesses, rather than optimizing them out (for example, if the access is in a loop).
This approach can be used to access 8, 16 or 32 bit registers, but be sure to declare the variable with the appropriate type for its size, i.e., unsigned int for 32-bit registers, unsigned short for 16-bit, and unsigned char for 8-bit. The compiler will then generate the correct single load/store instructions, i.e., LDR/STR, LDRH/STRH, LDB/STRB.
You should also ensure that the memory-mapped registers lie on appropriate address boundaries, e.g. either all word-aligned, or aligned on their natural size boundaries, i.e., 16-bit registers must be aligned on half-word addresses (but note that ARM recommends that all registers, whatever their size, be aligned on word boundaries - see later).
You can also use #define to simplify your code, e.g.:
#define PORTBASE 0x40000000 /* Counter/Timer Base */
#define PortLoad ((volatile unsigned int *) PORTBASE) /* 32 bits */
#define PortValue ((volatile unsigned short *)(PORTBASE + 0x04)) /* 16 bits */
#define PortClear ((volatile unsigned char *)(PORTBASE + 0x08)) /* 8 bits */ void
init_regs(void)
{
unsigned int int_val;
unsigned short short_val;
unsigned char char_val; *PortLoad = (unsigned int) 0xF00FF00F;
int_val = *PortLoad;
*PortValue = (unsigned short) 0x0000;
short_val = *PortValue;
*PortClear = (unsigned char) 0x1F;
char_val = *PortClear;
}
...
.END
No comments:
Post a Comment