我们接着上一节的内容来学习驱动程序的开发,本节要学习的是用应级驱动的开发:
与系统级驱动不同的是,应用级驱动只需要对已存在的设备节点进行操作,并与上层应用程序做交互即可。也就是说,应用级驱动的作用是将已存在的设备与上层应用建立起一个链接。通常可以使用任何方式的数据通讯,但在PX4里采用的方式是uORB机制。例如:我们在/dev下有一个串口设备结节/dev/ttyS2,我们将一个GPS设备接入到这个串口节点上,然后就可以通过标准的驱动程序调用(open、close、read、write、seek、ioctl等)来对这个设备节点/dev/ttyS2来操作了。下面我们来通过一个简单的例子来实现这样的一个应用级驱动程序。
1.配置编译选项:在cmake/configs/nuttx_px4fmu-v3_default.cmake中config_module_list列表中加入gps驱动的编译选项:
2.创建驱动程序:在src/drivers/目录下创建一个叫gps的文件夹src/drivers/gps,并在其中创建两个文件CMakeLists.txt和gps.c(这里作者是采用C语言来编写的驱动程序,如果使用C++的话需要创建gps.cpp不再赘述):
px4_add_module(
MODULE drivers__gps #模块名称
MAIN gps #入口函数 int gps_main(int argc, char *argv[])
STACK_MAIN 2000 #栈大小
SRCS #源代码文件
gps.c
DEPENDS #依赖模块
platforms__common
)
3.编写入口函数:在gps.c中声明并实现一个叫gps_main()的主函数,在这里我们通过对argv[1]参数来判断用户的两个操作:start和stop,即为启用/停用gps驱动程序,在启用时调用start()函数,在停用时调用stop()函数:
int gps_main(int argc, char *argv[]);
int gps_main(int argc, char *argv[])
{
if (strcmp(argv[1], "start") == 0)
{
start();
return 0;
}
if (strcmp(argv[1], "stop") == 0)
{
stop();
return 0;
}
info();
return 0;
}
4.创建后台运行进程:关于创建进程和线程或是工作队列的相关知识,我们在后续章节中来学习,这里读者只需要知道我们是通过px4_task_spawn_cmd()函数创建了一个进程即可:
static int _running = 0;
void start(void)
{
_running = 1;
int taskid = px4_task_spawn_cmd("gps",
SCHED_DEFAULT,
SCHED_PRIORITY_SLOW_DRIVER,
CONFIG_PTHREAD_STACK_DEFAULT,
&gps_core, NULL);
if (taskid < 0)
{
printf("gps start err.\n");
}
}
void stop(void)
{
_running = 0;
}
程序中定义了一个静态变量static int _running = 0;在start()函数中将其赋值为1,表示运行;在stop()函数中将其赋值为0,表示停止,这个_running在下面的核心函数中的while(_running)作为循环的条件。
5.驱动核心函数:在这里我们将核心函数命名为gps_core(),在它内部通过一个while(_running),来循环读取/dev/ttyS2中的数据,并显示出来:
int gps_core(int argc, char *argv[])
{
char buff[200];
int fd = open("/dev/ttyS2", O_RDONLY | O_NONBLOCK);
if (fd < 0)
{
printf("open dev err.\n");
return -1;
}
while (_running)
{
int len = read(fd, buff, 200);
if (len > 0)
{
for (int i = 0; i < len; i++)
{
putchar(buff[i]);
}
}
usleep(10 * 1000);
}
return 0;
}
这样我们就通过对/dev/ttyS2的读取操作,将gps设备中的数据显示出来,结果如下:
$GPGGA,,,,,,0,,,,,,,,*66
#BESTVELA,COM1,0,69.0,UNKNOWN,0,88.400,004c0000,10a2,14392;
INSUFFICIENT_OBS,NONE,0.000,0.000,0.0000,0.000000,0.0000,0*663f14ae
$GPGGA,,,,,,0,,,,,,,,*66
#BESTVELA,COM1,0,73.5,UNKNOWN,0,88.600,004c0000,10a2,14392;
INSUFFICIENT_OBS,NONE,0.000,0.000,0.0000,0.000000,0.0000,0*d557333d
$GPGGA,,,,,,0,,,,,,,,*66
#BESTVELA,COM1,0,73.5,UNKNOWN,0,88.800,004c0000,10a2,14392;
INSUFFICIENT_OBS,NONE,0.000,0.000,0.0000,0.000000,0.0000,0*f23a6b0d
此测试结果不是在户外运行的结果,所以没有经度纬度和速度内容,但基本数据的格式已经显示出来,说明我们的驱动程序已经可以正常工作了。
6.发布uORB:通过上面的程序可以将gps的数据显示出来,但我们的目的是通过uORB机制将数据提供给上层应用使用,所以还需要将数据组织成uORB的msg格式,并用“公告”、“发布”的方式向上层应用发布uORB:
struct gps_s _orb_gps = {0};
_orb_gps_topic = orb_advertise_multi(ORB_ID(gps),
&_orb_gps,
&_orb_gps_instance,
ORB_PRIO_DEFAULT);
while (_running)
{
int len = read(fd, buff, BUFF_SIZE);
//组织uORB的msg数据到_orb_gps结构体对象中
...
orb_publish(ORB_ID(gps), _orb_gps_topic, &_orb_gps);
usleep(MS_SLEEP * 1000);
}
关于uORB的原理与使用我们已经在前面的章节中学习过了,这里不再赘述。
Copyright © 2015-2023 问渠网 辽ICP备15013245号