layout | title | license |
---|---|---|
default |
Output device driver - LED |
This lab is concerned with device driver development for the LPC4088QSB development board.
Developing a device driver can be a complicated business. Even for a device as simple as a LED. This lab will require you to look at industry-standard documentation such as user manuals and schematics. This can be intimidating. But don't worry. You are not expected to understand everything straightaway. The point is to develop an appreciation of the complexity of the process of device driver development and sufficient understanding to make progress with the development of drivers of your own for very simple devices. The lab will also help to reinforce aspects of C programming that are essential for understanding OS implementation.
We'll begin by considering the steps required to develop a driver for LED1 on the LPC4088QSB board.
-
Identify the hardware features of the microcontroller that are used to control the device
A LED is an output device. It is a simple device that is either on or off. We can guess that it is associated with one of the GPIO (General Purpose I/O) pins of theLPC4088QSB
. But which one? We could read the schematic. Find theLED1
pin on the schematic of the microcontroller. Notice that it is associated with P1.18. That means pin 18 in GPIO port 1. Actually, the pin is associated with several functions. We'll need to make sure that the GPIO function is selected for this pin. -
Understand how to control the relevant hardware features from software
So now we need to know how to program the GPIO pins. We need to read the user manual for theLPC4088
. Notice by looking at page 16 that theLPC4088
communicates with its peripheral devices (I/O devices) by mapping them into its own memory space. The devices are said to be memory-mapped. This is convenient for the programmer, who can control devices simply by assigning values to the device addresses as though they were ordinary variables. Note that some other processors, e.g. the Intel range, require the programmer to control I/O devices by working with a dedicated set of I/O ports using special read and write instructions. These devices are said to be I/O mapped (or port-mapped). We won't consider this approach in this module. There's more relevant information about programming the GPIO ports in Chapter 8 of the LPC4088 user manual, see p.146 in particular. -
The user manual tells us that the direction register for fast GPIO port 1 (
DIR1
) is at memory address0x20098000 + 0x020
. This register is used to determine, for each pin, whether it's an input pin (value 0) or an output pin (value 1). To control an LED we need an output pin. The manual also mentions a register (PIN1) that controls the value of the pins. Its address is0x20098000 + 0x034
. -
So, in order to configure pin 18 as an output pin, we need to set bit 18 of the direction register to the value 1. Once the pin is configured as an output pin, we can control the state of the pin by setting (assigning the value 1 to) or clearing (assigning the value 0 to) bit 18 in the
PIN1
register. We should modify both of these registers without disturbing the value of the other bits in them. We need to understand how to manipulate individual bits in order to do this. Read the section on 'Common Bit Manipulation Techniques' in this Wikipedia article for more information. Once, we've gathered enough information from the documentation, we might try the following program to control the LED:#include <stdint.h> #include <stdbool.h> #define IOCON_P1_18 (*(volatile uint32_t *)(0x4002C0C8UL)) #define GPIOBASE (0x20098000UL) #define GPIO1DIR (*(volatile uint32_t *)(GPIOBASE + 0x020)) #define GPIO1PIN (*(volatile uint32_t *)(GPIOBASE + 0x034)) #define LED1PIN (1UL << 18) void delay(uint32_t ms); int main() { IOCON_P1_18 = 0UL; // configure as GPIO pin GPIO1DIR |= LED1PIN; // set P1_18 as output pin while (true) { GPIO1PIN |= LED1PIN; // set pin HIGH delay(1000); GPIO1PIN &= ~LED1PIN; // set pin LOW delay(1000); } }
Ask your tutor to explain this program carefully to you. You need to become familiar with all of its details.
-
Use the Start menu to run the git bash shell (you can just search for
git bash
and then left-click on the result). Clone thegit
repository for this lab by executing the following command in the git bash shell:$ git clone https://github.com/DavidKendall/blinky
We'll be using git
throughout this module to manage our source code. It is
easy to learn to use git
for the simple source code management tasks that
we need to perform. There is very good official documentation for git and also a useful guide to learning resources for git.
- Build and run the program by following these steps:
- Start the Keil uVision5 IDE, select
Project -> Open Project
and browse to theblinky
directory. - Open the file
blinky
. - Build the program by choosing the
Build
option from the toolbar (or by pressing function key<F7>
). - Load the code to the target by choosing the
Download
option from the toolbar (or by pressing function key<F8>
). - Choose the option
Debug -> Start/Stop Debug Session
. Wait patiently for the session to start. - Run the program by choosing the
Run
option from the toolbar. - Observe
LED1
flashing approximately once per second.
-
Now you're going to modify the program so that it also flashes
LED3
.-
Choose the
Debug -> Start/Stop Debug Session
option to stop the debug session and return to the editor. -
Refer to the schematic and find out which pin controls
LED3
. -
Create a new branch in your repository to work on your modification, e.g.:
$ cd blinky $ git checkout -b X03
-
Add the code required to flash
LED3
at the same time asLED1
. This should take exactly six lines of code. One line for each of the following steps:- Define the address of the pin connect register (IOCON_...) for the pin.
- Define a mask for the LED3 pin.
- Configure the pin for LED3 as a GPIO pin.
- Configure the direction register for the pin so that it is an output pin.
- Add a line in the loop to make the pin go HIGH.
- Add a line in the loop to make the pin go LOW.
-
Build, run and observe your program as before.
-
-
When you're satisfied with your solution to exercise 3, commit your changes to your repository and compare your solution with the one that we propose. Use the commands below in the git bash shell to do this, then reload the files into Keil uVision5.
$ git add . # add changes to the staging area $ git commit # commit changes - you'll be prompted to enter a commit message $ git checkout P03 # checkout our solution to exercise 3
-
Practice your understanding of the logical operators by modifying the solution to problem 3 so that you only use a single assignment statement for each of the following: setting the DIR register so that P1.13 and P1.18 are both outputs; setting both P1.13 and P1.18 to HIGH; and setting both P.13 and P.18 to LOW. The program should behave as before.
-
Now simplify the body of the loop so that you toggle the state of both LEDs using a single compound assignment statement involving the XOR operator (
^
). The program should behave as before. -
Add the code that is needed to flash LEDs 2 and 4, so that your program now flashes all LEDs - you'll need to refer to the schematic to find the right pins. Make sure that you follow the example of the solution to exercise 6 for the selection of the GPIO function via the IOCON registers - you'll run into difficulties if you just assign 0 to IOCON_...
-
Occasionally, the device hardware provides features intended to make life easier for the programmer. The GPIO devices on the LPC4088 fall into this category. In this case the feature that is provided is a pair of registers that allow the programmer to manipulate the value of individual bits in the PIN register without having to worry about disturbing the value of other bits. These registers are known as the SET and CLR registers. Writing a 1 into any bit position in the SET register causes the corresponding bit in the PIN register to go HIGH but (and this is the key point) writing a 0 into any bit position in the SET register has no effect. So, for example, we can set bit 13 in the PIN register with a simple assignment statement
GPIO0SET = (1UL << 13);
without being concerned that we'll affect the value of any other bits in the PIN register. Similarly, we can cause a bit in the PIN register to go LOW by writing a 1 to the corresponding bit in the CLR register, e.g.GPIO0CLR = (1UL << 13);
causes bit 13 in the PIN register to go LOW while leaving the other bits unchanged (contrast this with the alternative approachGPIO0PIN &= ~(1UL << 13);
). Modify your solution to exercise 7 so that the program has the same behaviour but uses the SET and CLR registers rather than using the XOR (^
) operator on the PIN register. -
The electrical configuration of the board is such that for LEDs 1 and 2, the LED is turned ON when the corresponding GPIO pin is LOW (0), but for LEDs 3 and 4 the LED is turned ON when the corresponding GPIO pin is HIGH (1). This is an important lesson. Devices don't always behave in the way that you expect! Modify your program for exercise 8, so that all LEDs are turned ON simultaneously.
-
Now add more code to your previous program to control the RGB-LED. Your program should behave as before except that, in addition, it should cycle through the colours RED, GREEN and BLUE on the RGB-LED, changing state every time LED1 .. LED4 are turned off. The RGB-LED is on the experiment base board. You'll need to refer to the schematic for this board in order to find out which GPIO pins to use to control it.
-
Modify the
delay()
function by removing the wordvolatile
from the declarations ofi
andj
. Rebuild and run the program and observe how the behaviour of the program changes. Can you explain what's going on? After you've thought about this, read Use volatile judiciously to help your understanding. Why is this approach to implementing a delay function so poor?
If you want to find out a bit more about GPIO configuration, you should have a look at Understanding Microcontroller Pin Input/Output Modes. Remember devices don't come any simpler than this! Be grateful for your operating system.