Writing NuttX Applications
NuttX provides a POSIX like API for application development. In this article we will see, how to develop simple NuttX applications. This is the third article in the NuttX series, for the previous articles please see:
Hello World
NuttX provides a Hello World example examples/hello
, in the apps
folder. The application prints the message "Hello World" on the serial
console. The contents of hello_main.c
is listed below.
#include <nuttx/config.h> /* */
#include <stdio.h>
int hello_main(int argc, char *argv[]) /* */
{
printf("Hello, World!!\n"); /* */
return 0;
}
Pulls in the configuration macros. Required in all NuttX applications. | |
The main() function, is give a custom name, because multiple
NuttX applications can co-exist in a single NuttX image.
| |
The stdout is mapped to the serial console, by default.
|
We will get to build this first, and then we will modify it, to write
more complex applications. Configure Nuttx to build for NX,
first. Note that the commands, assume you are in nuttx-dev
.
$ pushd nuttx/tools
$ ./configure.sh lm3s6965-ek/nx
$ popd
Edit apps/.config
, and modify CONFIGURED_APPS
line to add the
examples/hello
application. This specifies which applications are to
be built.
CONFIGURED_APPS += examples/hello
Edit nuttx/.config
, and modify CONFIG_USER_ENTRYPOINT
line, to
specify the entry point as hello_main
. This specifies the first
application function to be invoked by NuttX.
CONFIG_USER_ENTRYPOINT="hello_main"
Now build NuttX and the application, and execute it in Qemu. You
should be able to see the message Hello World
on the serial console.
$ pushd nuttx
$ make CONFIG_LM3S_CODESOURCERYL=y CONFIG_LM3S_BUILDROOT=n
$ popd
$ qemu-system-arm -M lm3s6965evb -kernel nuttx/nuttx
Adding an Application
Before we start modifying our Hello World program, we will create a
separate copy of the application. This way we will learn how to add
new application, to the existing application framework. First copy the
hello
folder to myhello
.
$ cp -a apps/examples/hello apps/examples/myhello
Edit myhello.c
and set the main function name to myhello_main
. And
to ensure, that its our application, that is getting executed, modify
the message, for example to Hello NuttX!
.
#include <nuttx/config.h>
#include <stdio.h>
int myhello_main(int argc, char *argv[])
{
printf("Hello NuttX!\n");
return 0;
}
Edit myhello/Makefile
, set APPNAME
to myhello
. APPNAME
should
be same as the prefix of the main application function. A
configuration macro CONFIG_EXAMPLES_HELLO_BUILTIN
is used to specify
if, an application should be created as builtin NuttShell
application. Change the reference to the macro to
CONFIG_EXAMPLES_MYHELLO_BUILTIN
.
Edit apps/.config
, and change CONFIGURED_APPS
to reflect the new
application folder.
CONFIGURED_APPS += examples/myhello
Edit nuttx/.config
, and modify CONFIG_USER_ENTRYPOINT
line, to
specify the entry point as myhello_main
. Rebuild, and execute in
Qemu, you should be able to see the message Hello NuttX!
, on the
serial console.
$ pushd nuttx
$ make clean
$ make CONFIG_LM3S_CODESOURCERYL=y CONFIG_LM3S_BUILDROOT=n
$ popd
Accessing SD Card
Let’s modify our program to read a file from the SD Card. The complete program is listed below.
#include <nuttx/config.h>
#include <nuttx/spi.h>
#include <nuttx/mmcsd.h>
#include <sys/mount.h>
#include <stdio.h>
#include <errno.h>
int mmcsd_init()
{
struct spi_dev_s *spi;
int ret;
spi = up_spiinitialize(0);
if (!spi) {
printf("failed to initialize SPI port 0\n");
return 1;
}
ret = mmcsd_spislotinitialize(0, 0, spi);
if (ret < 0) {
printf("failed to bind SPI port 0 to MMC/SD slot 0: %d\n", ret);
return 1;
}
}
int myhello_main(int argc, char *argv[])
{
int ret;
char buf[80];
char *retp;
FILE *fp;
ret = mmcsd_init();
if (ret == -1)
return 1
ret = mount("/dev/mmcsd0", "/mnt/sd", "vfat", 0, NULL);
if (ret == -1) {
perror("error mounting sd card");
return 1;
}
fp = fopen("/mnt/sd/TEST.TXT", "r");
if (fp == NULL) {
perror("error opening TEST.TXT");
return 1;
}
while (1) {
retp = fgets(buf, sizeof(buf), fp);
if (retp == NULL) {
if (ferror(fp))
perror("error reading file");
break;
}
puts(buf);
}
fclose(fp);
return 0;
}
Except for the mmcsd_init()
function, the rest of the program should
be familiar to a seasoned Unix developer. mmcsd_init()
invokes
up_spiinitialize()
, to initialize the SPI controller driver. The SD
card, is connected to the SPI controller, so we initialize the SPI
controller driver first. up_spiinitialize()
accepts the index of SPI
controller as argument, and returns an instance of struct spi_dev_s
,
which represents an instance of the SPI controller driver.
mmcsd_init()
then invokes mmcsd_spislotinitialize()
, to initialize
the MMC/SD driver. The function accepts 3 arguments. The first
argument is the device no., the device no. specifies the number that
will appear in the device file name, such as /dev/mmcsd0
. The second
argument specifies the index of the SD card slot. A single MMC/SD
driver can handle more than one MMC/SD slot. The third argument
specifies the SPI controller driver, used to access the SD card slot.
Edit nuttx/.config
and set CONFIG_FS_FAT
to y
. We need FAT
filesystem to access the SD card.
Rebuild NuttX and the application, and execute in Qemu. The contents
of TEST.TXT
should be printed on the console (that happens to be
the text Hello World
).
$ qemu-system-arm -M lm3s6965evb \
-kernel nuttx-nsh.elf \
-sd sd.img
Concluding Notes
Hope this helps you to get started with application development on NuttX. We will see how to access other interfaces, in up coming articles. Happy Hacking!