Laboratory Exercise 5 - Peripherals, GPIO
Laboratory Exercise 5 - Peripherals, GPIO
Peripheral input and output devices, which are set (configured) via Special Function Registers
(SFR), serve for this purpose.
Therefore, a large part of the documentation in the data sheets serves to describe these peripherals
and their use.
In this and the following exercises, we will take a closer look at the peripherals units built into our
microcontroller.
We will start with the so-called general purpose registers (GPIO), which can be used to directly
control LEDs or read the states of buttons.
2. Task
Task 1: LED test pattern
Unlike other microcontrollers, ARM controllers use so-called memory mapped peripherals, i.e. the
SFR are addressed like main memory; they are mapped to defined memory addresses and can thus
be read and written to like internal RAM memory.
The SFR are all located in the upper address space (0xE0000000 - 0xFFFFFF).
When using the periphery as GPIO pins, the data direction (input or output) is defined.
Additional other SFRs define the operating mode of the input and output pins.
With a limited number of pins, these can take on various tasks, e.g. serial interfaces (COM port,
CAN, SPI, I2C) or inputs of A/D converters.
The SFR PINSEL0 and PINSEL1 and PINSEL2 (Pin function Select) determine the use of ports
0 and 1.
Although 32-bit SFRs are available for each port, port 0 has only 30 pins (P0.0 - P0.25 and P0.27 -
P0.30), port 1 even has only 21 pins (P1.16 - P1.36).
Since up to four different functions can be assigned to the individual pins, the three 32-bit pin select
registers are used to configure the two 32-bit ports.
Since the ports are "by default" configured as GPIOs, we do not need to configure the PINSEL
registers for this exercise.
Four registers are used to configure the GPIO ports, each separately for port 0 and port 1:
1.) GPIO port Pin value register IOPIN: Contains the value that is present at a port bit.
2.) GPIO port Direction register IODIR: Direction of the port pin, 0: input or 1: output
3.) GPIO port Output Set register IOSET: A '1' sets the port pin to 'HIGH'.
4.) GPIO port Output Clear register IOCLR: A '1' sets the port pin to 'LOW'.
Note: Setting the value zero (0) in one of the bits of the last two registers has no effect, the value
that has been set previously is retained.
This peculiarity is common with ARM processors and initially takes some getting used to.
The buttons on our experimental hardware are connected to port 0 and the LEDs to port 1.
After reset, the pins are set to 'GPIO' and 'input' by default.
With this knowledge, we can now set the registers and their associated addresses.
At the beginning of the program, we set the base address of the stack to 0x40001000 as usual.
We use pins P1.16 to P1.23 as outputs to which eight LEDs are connected:
Firstly, we set only the least significant "LED bit" (bit 16) to high (= 1):
Via pointer r2, the GPIO register bits are set in a loop through register IOSET1, where we
increment the "value" of the LED bits in a loop.
Since the bits are not cleared within the loop (writing the value 0 to the IOSET1 register has no
effect), the LEDs - once activated - are no longer switched off.
ldrr2, = IOSET1
strr0 , [r2]
The use of a "delay" allows the program to be executed in real-time (simply run and count up).
Firstly test the function without delay in single steps and execute it with delay in real time if
the function is correct.
3.) Task 2: Test pattern for controlling individual I/O (input/output) bits
In the next step, we want to switch the LEDs on and off individually.
For this purpose we have to use the IOSET1 and IOCLR1 registers.
After initializing the stack pointer and the I/O registers as well as the data direction just as described
above, we use the registers r2 and r3 to access the two peripheral registers IOSET1 and IOCLR1.
We store the start value for the LED pattern, which is again 0x10000, in register r0.
Via pointer r2, we switch on again only the leading bit of the counter value in a loop by
immediately switching off the bit just set via pointer r3, which points to IOCLR1, and double the
counter value by one bit by shifting it to the left:
To start over after setting the last LED, we test the value of r4 (the code below looks a little bit
esoteric – wait a minute and think about what is happening here, the value #0x01 is rotated right
by 8 bit):
If it has not been reached the eventual value yet, we increase it, otherwise we start the whole
process from the beginning by setting the start value 0x10000 for the LED pattern in register r0.
Task:
Implement the LED test pattern for controlling individual I/O bits. Firstly test the function
without delay in single steps and execute it with delay in real time if the function is correct.
4.) Task 3:
Input and output via buttons and LEDs
To simplify matters, we define bit masks for the GPIO bits corresponding to the push buttons:
Again we use r2 and r3 as pointers to the IOSET1 and IOCLR1 registers, and we use register r9 to
hold the values to be switched on.
Please note that pressing a push button switches the input pin to ground (active low)!
The following program excerpt switches the first two LEDs by pressing button 1.
Firstly we switch on the first LED again, this time via register r9:
b led1_done
led1_done:
Tasks:
1. Firstly implement the program for switching between LED0 and LED1 using button 1.
2. If this part of the program works, implement the switching of the other LED pairs
through buttons 2 to four.
3. Make sure that you use the correct bit masks for the LEDs and increment the output
value with the appropriate "bit shift".
4. In the last step, you outsource the query of the individual buttons and the control of
the LEDs to individual subroutines, which you call up in the main routine with (bl =
Branch and Link):
loop:
bl read_button_1
bl read_button_2
bl read_button_3
bl read_button_4
b loop
Note: Do not forget to use the stack within each of the subroutines to store the link register
(lr), the value of which must be copied back into the program counter (pc) after the
subroutine has ended.
Congratulations, you have successfully completed exercise 5. You can now configure our
microcontroller for simple input and output operations.