上一篇我们讲了PX4的SPI/IIC设备驱动,我们现在讲一下PX4的串口设备。
PX4的串口设备驱动框架比SPI/IIC设备简单不少,使用了两种底层实现方式:
一种是系统自带的标准字符设备接口,一种是直接使用mcu的底层资源通过自行配置中断和DMA的方式实现的串口收发。
使用系统接口
Nuttx的串口使用的是标准的字符设备接口,通过标准接口函数open、close、read、write、ioctl等函数操作。
熟悉linux的同学对这个应该不陌生,通过看linux的驱动编程书籍对Nuttx的串口编程也同样有参考意义。
大多数搞飞控的都没有系统性的学习过系统编程,这里分享一个linux的设备驱动编程参考书,里面的知识在Nuttx中也非常适用,可以参考学习一下,对于了解Nuttx非常有参考意义。可以在公众号中回复 驱动编程 获取。
PX4的大多数串口使用的是系统接口,如GPS、数传、以及所有的串口外设等等。
PX4的串口驱动在任务调度上分两种情况,一种是使用task、一种是使用工作队列,那么都在什么情况下使用呢。
我们回顾前面分享的 PX4-4-任务调度 里面讲到的task与工作队列的区别,具体到串口驱动上。
- 使用task
使用task的典型应用是gps驱动,gps驱动独立开启了一个线程,串口采用poll阻塞方式读取。 以下是gps读取的关键代码:
int GPS::pollOrRead(uint8_t *buf, size_t buf_length, int timeout)
{
pollfd fds[1];
fds[0].fd = _serial_fd;
fds[0].events = POLLIN;
int ret = poll(fds, sizeof(fds) / sizeof(fds[0]), math::min(max_timeout, timeout));
if (ret > 0) {
/* if we have new data from GPS, go handle it */
if (fds[0].revents & POLLIN) {
...
}
}
}
这样的读取的方式的优点是在串口没有数据时任务可以自动挂起,在收到数据时有系统唤醒直接读取数据,这样的方式数据延时可以做到很小。
- 使用工作队列
大多数的串口任务会使用工作队列的方式,比如距离传感器distance_sensor下的各种串口传感器,这些驱动的串口都采用轮训的方式读取,即非阻塞的定时获取。
它的串口读取的关键代码为:
int
XXX::collect()
{
// Check the number of bytes available in the buffer
int bytes_available = 0;
::ioctl(_fd, FIONREAD, (unsigned long)&bytes_available);
do {
// read from the sensor (uart buffer)
ret = ::read(_fd, &readbuf[0], readlen);
// bytes left to parse
bytes_available -= ret;
} while (bytes_available > 0);
}
可以发现串口只有在周期调度函数collect执行到时尝试读取一次,没有获取到数据则直接返回,这样做的读取延时最大为一个调度周期,如果以100hz调度的话即为10ms。
为什么采用这种方式呢?PX4同时支持非常多的串口设备,而大多数的串口设备的实时性要求不高,采用工作队列非阻塞的方式,可以降低系统的内存、堆栈、调度的消耗,同时也能满足应用的需要。
值得一提的时使用工作队列的方式不能使用阻塞方式的串口读取,否则会影响队列中的其它任务的运行导致严重的故障!
自定义串口驱动
只有一种情况PX4采用的自定义的串口驱动,即板载fmu芯片与io芯片间的串口,这个串口用于两个单片机间的高速数据通信,波特率达到921600,用于传输输出控制信号,遥控输入信号。
这个串口的要求是高带宽、低延时,因此PX4选择自己实现这个串口驱动。
我们以pixhawk为例,它的串口驱动底层代码在 platforms/nuttx/src/px4/stm/stm32f4/px4io_serial/px4io_serial.cpp 和 src/drivers/px4io/px4io_serial.cpp 中
这个驱动主要实现了:
-
使用Nuttx的底层接口(irq_attach()),配置串口中断为自定义的串口中断函数 ArchPX4IOSerial::_interrupt(int irq, void *context, void *arg)
-
配置了发送和接收数据的dma stm32_dmasetup()
-
大多操作直接对寄存器操作,效率非常高
这个驱动这里不做详细的分析,感兴趣的同学可以深入的去看看
PX4在使用Nuttx为RTOS的时候,大多接口使用了系统上的API,但在一些高效率的场合还是会直接面向硬件直接编写驱动,比如这里的px4io_serial以及任务调度中提到的hrt。
–
我的微信公众号,文章同步更新,欢迎关注。