Just like Hello World program for any programmer, blinking a LED should be a first program for any embedded engineer. In this chapter, we will blink one of the user LEDs on the dragonboard.

The dragonboard has 4 user defined LEDs. LED1 is used for heartbeat and we will cover it later. For now, we will skip that. We will make use of LED2 in our program.

Method 1: Memory mapping

Note: This is not the recommended way, but this will give an opportunity to look into GPIO registers.

Before we get into the program, lets look at the registers that control the LED. It is connected to APQ GPIO 120.

From DragonBoard_HardwareManual_v5:

The four user LEDs are surface mount Green in 0603 size located next to the two USB type A connector and labeled ‘USER LEDS 4 3 2 1’. The 410c board drives two LEDs from the SoC GPIO, APQ GPIO_21 and APQ GPIO_120. The other two User LEDs are driven by the PMIC via PM GPIO_1 and PM GPIO_2.

From chapter 7 of lm80-p0436-5_e_peripherals_programming_guide:

Each MSM/MDM/APQ chipset has a dedicated number of GPIOs that can be configured for multiple functions. For example, if you check the GPIO mapping for MSM8916 GPIO 0, you will see that the GPIO can be configured as one of the following functions at any time:

  • Function 0 – GPIO

  • Function 1 – BLSP1 SPI MOSI

  • Function 2 – BLSP1 UART TX

  • Function 3 – BLSP1 User Identity Module (UIM) data

  • Function 4 – HDMI_RCV_DET

GPIO_CFGn controls the GPIO properties, such as Output Enable, Drive Strength, Pull, and GPIO Function Select.

Physical Address: 0x01000000  + (0x1000 * n) = GPIO_CFGn

 

Bit

Definition

Notes

31 : 11    

Reserved

10

GPIO_HIHYS_EN

Control the hihys_EN for GPIO

9

GPIO_OE

Controls the Output Enable for GPIO
when in GPIO mode.

8 : 6

DRV_STRENGTH

Control Drive Strength
000:2mA  001:4mA  010:6mA  011:8mA
100:10mA 101:12mA 110:14mA 111:16mA

5 : 2

FUNC_SEL

Make sure Function is GSBI.
Check Device Pinout for Correct Function

1 : 0

GPIO_PULL

Internal Pull Configuration
00:No Pull   01: Pull Down

10:Keeper    11: Pull Up

      
*The configuration register is already programmed to the correct settings

GPIO_IN_OUTn controls the output value or reads the current GPIO value.

Physical Address: 0x01000004 + (0x1000 * n) = GPIO_IN_OUTn n = GPIO #n

Bit

Definition

Notes

31 : 2      

Reserved

1

GPIO_OUT      

Control value of the GPIO Output

0

GPIO_IN

Allows you to read the Input value of the GPIO

The code is pretty straight forward:

  • Open a memory device

  • Memory map the above register address using mmap

    • Note from man page: offset must be a multiple of the page size as
             returned by sysconf(_SC_PAGE_SIZE).  

  • Enter an infinite loop (until aborted) which toggles the LED

    • Note that we are not programming the configuration register as it is already programmed.

    • Recommend printing the configuration value and mapping it to the above table.

  • When abort is called, we clean up and exit.

Source code:

/*
 * led.c
 *
 *  Created on: Jun 10, 2016
 *      Author: basu
 */


#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <fcntl.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/mman.h>

#define ERR_FATAL do { fprintf(stderr, "Error: file %s, line %d, error (%d) [%s]\n", \
		__FILE__, __LINE__, errno, strerror(errno)); exit(1); } while(0)

typedef struct
{
	unsigned int cfg;            /*place holder for configuration register*/
	unsigned int in_out;				 /*place holder for IO register*/
}db_gpio_type_s;

int abort_program = 0;

/*GPIOn physical address = 0x01000004 + (0x1000 * n); ref: e peripherals programming guide*/
unsigned int GPIO_120_CFG_ADDR = 0x01000000 + (0x1000 * 120);
unsigned int GPIO_120_IO_ADDR = 0x01000004 + (0x1000 * 120);

void stop_program(int sig)
{
	abort_program = 1;
}

int main(int argc, char *argv[])
{

	db_gpio_type_s *gpio_120;
	int fd_mem;

	/*initialize signal to abort the program*/
	signal(SIGINT, stop_program);

	fd_mem = open("/dev/mem", O_RDWR | O_SYNC);

	if (fd_mem < 0)
	{
		printf("Failed to open /dev/mem. Aborting\n");
		ERR_FATAL;
	}

	gpio_120 = (db_gpio_type_s *)(mmap(0, sizeof(db_gpio_type_s), PROT_READ | PROT_WRITE, MAP_SHARED, fd_mem, GPIO_120_CFG_ADDR));

	if (gpio_120 == (void *)-1)
	{
		printf("Failed to map GPIO address. Aborting\n");
		close(fd_mem);
		ERR_FATAL;
	}

	while (!abort_program)
	{
		gpio_120->in_out = gpio_120->in_out | 0x02;
		printf("Contents of physical address 0x%X is 0x%X\n", GPIO_120_IO_ADDR, gpio_120->in_out);
		sleep(1);
		gpio_120->in_out = gpio_120->in_out &; ~0x02;
		printf("Contents of physical address 0x%X is 0x%X\n", GPIO_120_IO_ADDR, gpio_120->in_out);
		sleep(1);
	}
	/*clean up and exit*/
	gpio_120->in_out = gpio_120->in_out & ~0x02;

	if (munmap(gpio_120, sizeof(int)) == -1)
	{
		printf("Failed to unmap GPIO address. Aborting\n");
		close(fd_mem);
		ERR_FATAL;
	}

	printf("Contents of physical address 0x%X is 0x%X\n", GPIO_120_IO_ADDR, gpio_120->in_out);

	close (fd_mem);
	printf("\nProgram exiting\n");
	return 0;
}

If all goes well, execute the program and you should see the LED2 blinking. You may have to run as sudo to get permission to open a device.

Method 2: Using /sys files

Using /sys files to control LEDs is much easier. For explanation on using sys files, please refer to kernel documentation for leds.

Under /sys/class/leds, try the following (this is on the target board)

    ls -lF

This will list all the LEDs on the dragonboard as shown in the screenshot below

LED files in /sys

The code does the following:

  • Open the brightness file

  • Write "1" to turn LED ON

  • Write "0" to turn LED OFF

  • Close the file and exit

Source code:

/*
 * led_sys.c
 *
 *  Created on: Jun 13, 2016
 *      Author: basu
 */


#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <signal.h>
#include <fcntl.h>
#include <unistd.h>


#define ERR_FATAL do { fprintf(stderr, "Error: file %s, line %d, error (%d) [%s]\n", \
		__FILE__, __LINE__, errno, strerror(errno)); exit(1); } while(0)

int abort_program = 0;

/*file system for LED 2*/
#define LED2 "/sys/class/leds/apq8016-sbc\:green\:user2/brightness"

void stop_program(int sig)
{
	abort_program = 1;
}


int main(int argc, char *argv[])
{
	int fd_led2;
	/*open the file*/

	/*initialize signal to abort the program*/
	signal(SIGINT, stop_program);

	printf("%s\n", LED2);

	fd_led2 = open(LED2, O_WRONLY);

	if (fd_led2 < 0)
	{
		ERR_FATAL;
		return 1;
	}

	while (!abort_program)
	{
		write(fd_led2, "1", 2);
		sleep(1);
		write(fd_led2, "0", 2);
		sleep(1);
	}

	/*clean up and exit*/
	write(fd_led2, "0", 2);

	close (fd_led2);
	printf("\nProgram exiting\n");
	return 0;

}

 

Makefile: 

#
#  make all - build dragonboard_led
#  make dragonboard_led - build dragonboard_led
#  make clean - clean

CC = aarch64-linux-gnu-gcc
CFLAGS = -Wall -g 

led.o:
	${CC} -c $(CFLAGS) led.c
	
led_sys.o:
	${CC} -c $(CFLAGS) led_sys.c 

dragonboard_led: led.o
	$(CC) -o $@ $^

dragonboard_led_sys: led_sys.o
	$(CC) -o $@ $^ 


clean:
	rm -f *.o
	rm -f dragonboard_led dragonboard_led_sys

Add comment


Security code
Refresh