Day: June 11, 2013

How to create a driver for NuttX?

If you want to create a simple driver for NuttX this tutorial is just for you.

I created a small board powered by kinetis KL25 and put NuttX to run on it, but this board has 4 LEDs for user interface, then I created a simple driver just to let an application to interact with them directly.

Here is my LED driver source code:

#include <nuttx/config.h>
#include <nuttx/arch.h>

#include <stdint.h>
#include <unistd.h>
#include <errno.h>
#include <sched.h>

#include "kl_gpio.h"

/****************************************************************************
 * HW access
 ****************************************************************************/

#define GPIO_LED_1 (GPIO_OUTPUT | GPIO_OUTPUT_ONE | PIN_PORTE | PIN2)
#define GPIO_LED_2 (GPIO_OUTPUT | GPIO_OUTPUT_ONE | PIN_PORTE | PIN3)
#define GPIO_LED_3 (GPIO_OUTPUT | GPIO_OUTPUT_ONE | PIN_PORTE | PIN4)
#define GPIO_LED_4 (GPIO_OUTPUT | GPIO_OUTPUT_ONE | PIN_PORTE | PIN5)


/****************************************************************************
 * LEDs: Fileops Prototypes and Structures
 ****************************************************************************/

typedef FAR struct file		file_t;

static int     leds_open(file_t *filep);
static int     leds_close(file_t *filep);
static ssize_t leds_read(file_t *filep, FAR char *buffer, size_t buflen);
static ssize_t leds_write(file_t *filep, FAR const char *buf, size_t buflen);

static const struct file_operations leds_ops = {
	leds_open,		/* open */
	leds_close,		/* close */
	leds_read,		/* read */
	leds_write,		/* write */
	0,			/* seek */
	0,			/* ioctl */
};

/****************************************************************************
 * LEDs: Fileops
 ****************************************************************************/

static int leds_open(file_t *filep)
{
	/* Nothing to do here, maybe I should increase a counter like for Linux driver? */

	return OK;
}

static int leds_close(file_t *filep)
{
	/* Nothing to do here, maybe I should decrease a counter like for Linux driver?*/

	return OK;
}

static ssize_t leds_read(file_t *filep, FAR char *buf, size_t buflen)
{
	register uint8_t reg;

	if(buf == NULL || buflen < 1)
		/* Well... nothing to do */
		return -EINVAL;

	/* These LEDs are actived by low signal (common anode), then invert signal we read*/
	reg = ~(kl_gpioread(GPIO_LED_4));
	reg = (reg << 1) | ~(kl_gpioread(GPIO_LED_3));
	reg = (reg << 1) | ~(kl_gpioread(GPIO_LED_2));
	reg = (reg << 1) | ~(kl_gpioread(GPIO_LED_1));
	reg = reg & 0x0F;

	*buf = (char) reg;

	return 1;
}

static ssize_t leds_write(file_t *filep, FAR const char *buf, size_t buflen)
{
	register uint8_t reg;

	if(buf == NULL || buflen < 1)
		/* Well... nothing to do */
		return -EINVAL;

	reg = (uint8_t) *buf;

	printf("Trying to write %d\n", reg);

	/* These LEDs are actived by low signal (common anode), invert the boolean value */
	kl_gpiowrite(GPIO_LED_1, !(reg & 0x01));
	kl_gpiowrite(GPIO_LED_2, !(reg & 0x02));
	kl_gpiowrite(GPIO_LED_3, !(reg & 0x04));
	kl_gpiowrite(GPIO_LED_4, !(reg & 0x08));

	return 1;
}


/****************************************************************************
 * Initialize device, add /dev/... nodes
 ****************************************************************************/

void up_leds(void)
{
	kl_configgpio(GPIO_LED_1);
	kl_configgpio(GPIO_LED_2);
	kl_configgpio(GPIO_LED_3);
	kl_configgpio(GPIO_LED_4);

	(void)register_driver("/dev/leds", &leds_ops, 0444, NULL);
}

I saved this driver file as configs/freedom-kl25z/src/leds_driver.c and added it to Makefile:

ifeq ($(CONFIG_ARCH_LEDS),y)
CSRCS += kl_led.c
CSRCS += leds_driver.c
endif

I’m reusing the same ARCH_LEDS config but you can create a specific config for your driver.

Unfortunately you cannot call up_leds() initialization function from kl_boardinitialize.c because this is called before the RTOS finished initialization. More information click here: http://comments.gmane.org/gmane.comp.embedded.nuttx/3158

I decided to use NSH_ARCHINIT to let nsh initialize my driver. First of all add this define to your config file:

CONFIG_NSH_ARCHINIT=y

Now create the configs/freedom-kl25z/src/up_nsh.c file and add it:

/****************************************************************************
* Name: nsh_archinitialize
*
* Description:
* Perform architecture specific initialization
*
****************************************************************************/

int nsh_archinitialize(void)
{
#ifdef CONFIG_ARCH_LEDS
up_leds();
#endif

return OK;
}

Just compile and flash the firmware inside your board.

Now my application can control the user LEDs.