Very Simple LED Program



In this example, we'll write a simple C program for the Renesas YRDKRX63 development board. The real goal here is to write a program from "first principles". In other words, with no provided software development kit (SDK), no example programs, none of it! This is just pure embedded software engineering. You're given a schematic and a data sheet, and you make it work.

As usual with any embedded system, the first program we'll write is a simple LED program. Also as usual with any embedded system, you'll still need to spend a lot of time looking at data sheets and schematics. The Hardware User's Manual which defines all the registers, etc. can be found at the Renesas site here. Similarly, the schematic can be found here.

Reading the Schematic
In this example, we'll try to blink LED5 on the YRDKRX63N development board. If you study the schematic, you'll see that the cathode of what's labelled LED5 on the board is actually tied via resistor R100 to a wire named "LED12" which is connected to the processor on pin 75, otherwise known as

"PE3/D11[A11/D11]/MTIOC4B/TIOCB9/PO26/POE8#/ET_ERXD3/CTS12#/RTS12#/SS12#/MISOB/AN1"

The "PE3" (as well as any of the other aliases) is something you can search for in the Hardware User's Manual.

Also notice the configuration of the LED circuit. The anode is connected to 3.3V, and and the cathode is connected via R100 to the processor. In this configuration, the processor pin is "sinking" current, and the LED will be turned on when the pin is pulled low (to ground). When the pin is high (3.3V), no current flows. Therefore, when we want to turn the LED on, we'll use a logical 0, and when we want to turn the LED off, we'll use a logical 1.

Deciphering the Hardware User's Manual
It turns out that PE3 refers to a specific I/O port (see Table 1.10 of the manual). It takes some digging, but you'll find (see Table 21.1) that it is in fact a specific pin (3) of an I/O port named "PORTE". A "port" in a microcontroller is generally just a collection of pins that can be controlled simultaneously. Figure 21.4 of the User's Manual graphically explains the I/O port configuration.

I/O Port Configuration
Studying Section 21 of the User's Manual, you'll learn that each port controls 8 I/O pins on the microcontroller package, named Px0-Px7. In our case, of course, we're controlling PE3, or the I/O pin 3 of PORTE. Furthermore, each port consists of a number of hardware registers used for configuration and I/O. The layman's version of each register is outlined below: Note that some ports don't implement all of the above registers. Moreover, other smaller packages of the RX63N incorporate a couple of additional registers to allow you to keep the same functionality with fewer physical pins.
 * PDR (Port Direction Register, 8-bits, R/W): This register sets whether a given I/O pin is used as an input (corresponding bit set to 0) or an output (corresponding bit set to 1).  In our case, we'll set bit 3 of this register to 1, since the LED is an output.
 * PODR (Port Output Data Register, 8-bits, R/W): This register will set the output of the I/O pin to low (corresponding bit set to 0) or high (corresponding bit set to 1).  In our case, we'll set bit 3 of this register to 0 when we want to turn the LED on, or we'll set it to 1 when we want to turn the LED off.
 * PIDR (Port Input Data Register, 8-bits, R): This register will store the logic level for an I/O pin that is configured as an input (using the PDR register).   In fact, the User's Manual explains that the pin level can be read regardless of how the PDR and PMR (see next bullet) registers are set.  In our case, we could care less about this register because our LED is an output.
 * PMR (Port Mode Register, 8-bits, R/W): The register sets whether a given I/O pin is used as a general I/O pin (corresponding bit set to 0) or one of many peripheral functions that the pin can have (corresponding bit set to 1).   In our case, we'll set bit 3 of this register to 0, since we'll use the LED as general I/O .  Just to understand better what peripheral functions PE3 (our LED pin) could have, below is the list that I've taken from Table 22.1 of the User's Manual.  In parentheses, you'll see the pin functions, which are the other "aliases" shown on the schematic for pin 75:
 * Multi-function timer (MTIOC4B)
 * Port output enable (POE8)
 * 16-bit timer pulse (TIOCB9) (Note, Table 22.1 states that this function is not available on the 100-pin package, so I'm not sure if it's an option)
 * Programmable pulse generator (PO26)
 * Ethernet controller (ET_ERXD3)
 * Serial communications interface (CTS12, RTS12, or SS12)
 * Serial peripheral interface (MISOB)
 * 10-bit A/D converter (AN1)
 * ODR0/ODR1 (Open Drain Control Register 0/1, 8 bits in each register, every other bit reserved, R/W): These registers set whether the corresponding output bit is configured as a CMOS (push-pull) or NMOS (open-drain) output.  I found a decent explanation of push-pull/open-drain here.  The important thing to know is that push-pull pin will directly drive current to the device .  Therefore, if the device draws more current than the pin can source, you'll probably fry your microcontroller.  On the contrary, in open-drain configurations, an external voltage source provides the current, and the controller pin just acts to switch the external circuit on or off; these are generally used in high-current applications (think motors).  You're advised to study the datasheet to determine the maximum current an I/O pin can supply before choosing a push-pull configuration.   Unfortunately, I can't decipher the datasheet for the RX63N controller (found here) to determine the maximum per-pin supply current. (If anyone reading can provide the answer, please comment with how you found it.)  However, I do know that it's a very tiny LED that's not too bright, so it should be safe enough to make it a push-pull pin.  Therefore, in our case, we'll set bit 6 of ODR0 (remember, every other bit is reserved) to 0, since we'll directly drive the LED with a push-pull configuration.
 * PCR (Pull-Up Resistor Control Register, 8 bits, R/W): This register is used to set whether or not a pull-up resistor is connected to the corresponding I/O pin.  Pull-up registers are used to mitigate "floating" inputs, which can occur if nothing is actually connected to the pin (such as when a switch connected to the input is open).  In our case, since the pin is an output, could care less about this register.  In fact, the User Manual explains that when the pin is configured as an output, the pull-up resistor will be disabled regardless of how the PCR register is set.
 * DSCR (Drive capacity control register): This register sets the output capacity of the of the given pin to either normal (corresponding bit set to 0) or high (corresponding bit set to 1).  The User Manual is not very clear what exactly "normal" and "high" drive capacity is, in our case, we'll set bit 3 of this register to 0 and use "normal" drive capacity.

In summary, by reading the schematic and the User Manual, we know that we want the following:
 * 1) PORTE, PDR register (0x0008C00E), bit 3 set to 1 (set LED5 as output)
 * 2) PORTE, PODR register (0x0008C02E), bit 3 set to 0/1 for turning LED5 on/off
 * 3) PORTE, PMR register (0x0008C06E), bit 3 set to 0 (use pin as general I/O, not a peripheral function)
 * 4) PORTE, ODR0 register (0x0008C09C), bit 6 set to 0 (use push-pull configuration instead of open-drain)
 * 5) PORTE, DSCR register (0x0008C0EE), bit 3 set to 0 (use "normal" drive capacity for pin, whatever that means)

Writing the Code
Now we know which registers need to be set to what, but how do we actually set them? It's quite straight forward if you're familiar with C pointers, hexidecimal notation, and a few boolean operators.

Let's start with number 1 in the list above:  PORTE, PDR register (0x0008C00E), bit 3 set to 1 (set LED5 as output) First of all, we need to create a pointer to the register itself. The pointer must point to address 0x0008C00E in the microcontroller, and it must be 8 bits wide. Furthermore, since this register will always be the "PDR" register, we'll tag it with "const" to tell the compiler that we'll never change this address. In the C language, the preceding description corresponds to the following line of code:

Here we've created a constant pointer to a piece of memory the same size as an unsigned char, which is 8 bits wide, and we said the block we're pointing to lives at address 0x0008C00E..

Now we need to set bit 3 of his register to be a 1. We can accomplish this with the following block of code:

In this line, we've assigned an 8-bit-wide value to the address where PDR (a pointer) is pointing). This is called "dereferencing the pointer".  In fact, we've simply modified the value that was previously at the address by using the "or" boolean operation.  By "or-ing" the value with the hex value 0x08 (binary value 0b00001000), we've ensured that the 3rd bit of the register will be set to 1, while all of the other bits were left at whatever value they previously held.  In this method, the hex value 0x08 is called a "bit mask" because it masks only the bits that you want to change.

So what about, say, number 3 or our previous to-do list, in which we need to clear a bit (set it to 0)?

Here we've created another pointer pointing to the appropriate address of the PORTE PMR register, and we've cleared the 3rd bit by "and-ing" the existing register value with the complement of 0x08. This is equivalent to "or-ing" it with 0xF7 (binary value 0b11110111). In this way, we're clearing the 3rd bit in the register without changing the other bits.

The other key to the blinking LED code is the toggling operation. In C code, this can be accomplished by using the exclusive or operator (^).

Anytime the line above is used, we will toggle the 3rd bit of the register. If it was a 1, the exclusive or with 1 from the bit mask (thinking in binary) will yield a 0; if it was a 0, the exclusive or with 1 in the bit mask will yield a 1.

Putting it all together, we can write the following very simple program to blink LED5:

In the above example, we use a finite for-loop as a wait function, which simply counts down from 10,000 to chew up time. The number 10,000 was determined empirically so that the LED blinks a bit faster than once per second. In order to have more precise control over the blink rate, we'll need to add a bit more code to configure the timing of the microcontroller.

Making Life Easier (Using The Register Definition File)
The previous section was a really good introduction to embedded software and using a few critical documents (the schematic and the User's Manual) to create some working code. Now we'll incorporate the register definitions that Renesas has already provided in the RX toolchain of IAR Embedded Workshop. First you'll need to include it in your program:

If you examine this file, you'll find that all of the pointer declarations we did in the previous section were already done for us, and not just for PORTE, but for ALL of the RX63N registers! The've organized each port into a structure, and the bits of each register are declared using a bit field notation, for PORTE, we have

While the code above only creates the structure of the port, the following line actually creates the pointer to that structure in the appropriate memory location:

While the code above looks rather involved, the great part is, you don't have to write it! It was already done for you. Now if you want to say,

 PORTE, PDR register (0x0008C00E), bit 3 set to 1 (set LED5 as output)

You simply write:

We can now rewrite main, taking advantage of the register descriptions that Renesas has provided:

The source code and working IAR Embedded Workbench projects (SimpleBlinkingLED and SimplerBlinkingLED) can be found at GitHub:

https://github.com/mattmunee/RenesasYRDKRX63N



[[Category:C]]