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.

Advertisements

17 thoughts on “How to create a driver for NuttX?

  1. Hi Alan, I have followed your tutorial but unfortunately I was not able to have success on it.
    I think the final result would be to have a /dev/leds entry after nuttx bootup and to be able to send commands from nsh command line. After bootup I have the same devices that I have without the addition of the led driver . The only warning that I had during the make process, was related to the “printf” statement on the ssize_t leds_write function: it warned about implicit definition of printf.
    Is there any other code snippet or configuration that I need to have , in order to function ?
    Regards ,
    Jeronimo

  2. Hi Jeronimo,
    I suspect that register_driver is failing.
    Please change your code to print the return of register_driver function:

            int ret;
    
            ret = register_driver("/dev/leds", &leds_ops, 0444, NULL);
            if (ret < 0)
            {
                    lldbg("register_driver failed: %d\n", ret);
                    return;
            }
            lldbg("LED Driver initialized successfully!\n");
    

    Also remember to enable CONFIG_DEBUG to get lldbg() working.

    BR,

    Alan

  3. ABCDF are the stages when NuttX is booting, similar to ancient LILO linux bootloader where each character means something. If nothing is printed in your driver it means that your driver was not called.

  4. Alan Thanks a lot for the help,

    I identified the problem: the up_nsh.c module needs to be added at the beginning ot the Makefile :
    AOBJS = $(ASRCS:.S=$(OBJEXT))

    CSRCS = kl_boardinitialize.c
    CSRCS += up_nsh.c

    I had at first added it at the same place of the led_drivers and it had not functioned:

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

    After adding to the beginning , I was able to see the messages at the console:
    nsh> ABCDF
    up_leds: LED Driver initialized successfully!

    NuttShell (NSH)
    nsh> ls /dev
    /dev:
    console
    leds
    null
    ttyS0
    nsh>

    If my conclusion is right , I suggest you to put a remark at your excellent post about this situation. I am using the 7.5 version of nuttx.

    1. Hi Jeronimo,
      yes, this is the right fix. Thank you for finding this issue, I just sent a patch to Greg fix it in the git.

      I hope now your driver is working, right?

      BR,

      Alan

  5. hello,

    i m a beginner in linux,makefiles and shells.
    As there are different makefiles in every folder of nuttx, i m confused which top make file Jeronimo is specifying.

    i added the patch in src folder makefile, up_nsc.c is compiled. but no reference spotted in shell /dev folder.

  6. hi DJ Ganvir,
    it was the Makefile inside nuttx/configs/freedom-kl25z/src/

    Please note that currently there are other better options: i.e. put the initialization code inside kl_boardinitialize()!

    I’m assuming you are using Freedom board KL25Z128

  7. Also now when the debug is on
    and when i reset the circuit
    it shows

    A{FF}CDF

    NuttShell (NSH)
    nsh> [K

    does it means that the boot process ‘b’ hadn’t executed well ??
    Also [K denotes something ??

  8. What serial console program are you using? This “[K” is a VT102 terminal command. I suggest you to use minicom/picocom on Linux or TeraTerm on Windows

  9. thanks for the reference acassis

    i will surely try to have a better understanding of that code as well, since i m pretty less familiar to linux device drivers and there implementations

    Will let u know for any further updates

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s