Global Sources
EE Times-India
Stay in touch with EE Times India
EE Times-India > Embedded

Using device registers in C

Posted: 14 Aug 2014     Print Version  Bookmark and Share

Keywords:C language  embedded  programming language  UART  register 

One of the key benefits of the C language, which is the reason it is so popular for embedded applications, is that it is a high-level, structured programming language, but has low-level capabilities. The ability to write code that gets close to the hardware is essential and C provides this facility. This article looks at how C may be used to access registers in peripheral devices.

Device registers
The broad issue is quite straightforward. A peripheral device is likely to have a number of internal registers, which may be read from or written to by software. These normally appear just like memory locations and can, for the most part, be treated in the same way. Typically a device register will have bit fields – groups of bits that contain or receive specific information. Such fields may be single bits, groups of bits, or a whole word. There may also be bits that are unused—reading from them or writing to them normally has no effect.

For example, a serial interface (UART) might have an 8bit register, with the bits used like this:

 • Bits 0-2: baud rate, where the values [0-7] have specific significance.
 • Bits 3-4: parity, where 0=none, 1=even, 2=odd and 3 is an illegal setting.
 • Bits 5-6: unused.
 • Bit 7: interrupt enable.

Memory addressing
There are a few important matters to get right when accessing device registers from C. The first is data type. It is almost 100% certain that a register will be unsigned and its size (width in bits) must be accommodated. Using traditional C data types, our example register would be declared unsigned char. In newer versions of the language, there are ways to explicitly specify the bit width; in this case uint8_t would do the job.

If the devices registers are larger than 8 bits – perhaps 16 bits or 32 bits – endianity may be an issue; different CPUs locate most and least significant bytes differently. Any attempt to break down such registers into 8bit units is likely to go awry, for example.

A really important issue to address is the suppression of unnecessary compiler optimisations.

Normally, a compiler will detect multiple accesses to a variable and make the code more efficient by accessing the memory location once, working on the data in a CPU register, and then writing it back to memory later. With a device register, it is essential that the actual device is updated exactly when the code when requires it to be. Similarly, repeated read accesses must be honoured. For example, there may be some code that looks like this:

unsigned char dev_reg;

while ((dev_reg & 1) == 0)

The idea here is to continuously poll the variable dev_reg, which is actually a device register, waiting for the least significant bit to be set. Unless dev_reg is declared volatile, most compilers would optimise the code to a single read access and the loop would never end.

Using pointers
A common question from developers, working this close to the hardware for the first time, goes something like this: "I have a variable of the right type for my device register. How do I arrange for it to be mapped to the correct address?" This is a simple question, but the answer is less simple.

Some embedded software development toolkits do make it fairly simple by providing a facility whereby a variable (or a number of variables – a program section) can be precisely located using the linker. Although this is very neat and tidy, it renders the code somewhat toolchain-dependent, which is generally unwise. There is no standard way to achieve this result in the C language.

The usual solution is to think in terms of pointers, which are effectively addresses. So, instead of creating a variable of the appropriate type, you need a pointer to that type. Then, accessing the device register is simply a matter of dereferencing the pointer. So the above example may be re-written.

unsigned *char dev_reg;

while ((*dev_reg & 1) == 0)

There is just the question of making sure that the pointer does point to the device register. So, for example, maybe the device register in the example is located at 0x8000000. The declaration of the pointer could include an initialisation, thus:

unsigned *char dev_reg = (unsigned char *)0x80000000;

Notice that the address is expressed as an integer, which is type cast to be an appropriate pointer type.

1 • 2 Next Page Last Page

Comment on "Using device registers in C"
*  You can enter [0] more charecters.
*Verify code:


Visit Asia Webinars to learn about the latest in technology and get practical design tips.


Go to top             Connect on Facebook      Follow us on Twitter      Follow us on Orkut

Back to Top