On Thu, Mar 17, 2022 at 1:55 PM Matthew Trescott <matthewtresc...@gmail.com> wrote:
> Hi everyone, > > I was thinking about how to implement the software for my project > (control module for a Formula SAE car) and realized that the basic ADC > driver model is a bit inconvenient and creates a lot of extra overhead > in userspace threads when implementing advanced features. Some > features I think would make sense in an abstract “channels” driver > model that would benefit many controls applications: > > - Only wake up a thread when a value has changed "significantly." > > - Support channels generated from bilinear/bicubic interpolation using a > table. > > - Share the same ADC values among multiple threads with different > priorities, like a high-priority safing algorithm, a medium priority > control algorithm, and a low-priority CAN broadcast thread. > > - Share the same ADC values among threads with different needs—the > three listed above only care about the latest value, while a > datalogging thread that saves to flash memory would want to save data > in chunks. > > - Implement software filters and allow binding to userspace control > algorithms for Kalman filtering. > > - Allow different threads to subscribe to epoll events at different rates. > > - Allow data received via an offboard, runtime-configurable serial bus > like CAN or UART to be assigned a meaningful name and seamlessly used > for userspace control functions just like ADC measurements, with a > userspace helper thread de-serializing the messages as they arrive. > > - Allow the application to wait for two or more channels to be consistent. > > - Avoid unnecessary copies and support ADC DMA transfers. > > ~~~~~~~~ > The model I have in mind is something like this: > > - Channels are char-devices; e.g. /dev/channels/throttle-position-1 > > - A userspace application that only cares about the latest measurement > would do something like this to get a pointer that always points to > the latest channel value and avoid the overhead of read(): > > uint32_t *throttle_position_1; > int tps1_fd; > tps1_fd = open("/dev/channels/throttle-position-1"); > ioctl(tps1_fd, CHANIOC_SUBSCRIBE, (unsigned long)&throttle_position_1); > ioctl(tps1_fd, CHANIOC_SET_FILTER, ...); > poll(...); > // Do some calculations with *throttle_position_1 > // Drive some output > > - A periodic userspace task that works with bulk data would read() > from the ring buffer, or get a pointer to the ring buffer with an > ioctl (not sure which would be better) > > - New data is copied to the ring buffer in the low- or high-priority work > queue. > > - Boardctl or board-init logic is responsible for setting up special > ADC triggering (e.g. from a PWM module) and telling the ADC backend > driver to register itself as a channel. DMA can be set up so that ADC > measurements are directly copied to the "latest value" location if no > filtering or conversion is needed. > > - Software-generated channels are created with a syscall; the > userspace thread can write to the resulting char device and the data > will be stored in the ring buffer and wake up any threads waiting on > the software-defined channel. > > ~~~~ > > Looking for prior art, I noticed that there are the Common Sensor > Register Interace and Sensor Cluster Driver Interface > (drivers/sensors/README.txt). However, these don't cover ADCs or > address the overhead of having a multiple-input control thread needing > to use both epoll() and read(). We are working on this to provide multiple read/poll support. The basic idea is that: 1. Upper half driver hold FIFO shared by all client 2. Each client hold the offset into the share FIFO The benefit is that we don't need to maintain multiple queues and avoid the memory duplication. Nor do they support filtering or > Kernel side only support the resample filter to handle the different sample frequency request: 1. Upper half collect the sample frequency from all client 2. Forward the fast request to the lower half 3. Down sample to match slow client in the read callback We think that other advanced filter or algorithm is better to put to the userspace, and will provide an interface compatible with uORB: https://docs.px4.io/master/en/middleware/uorb.html multiple userspace threads accessing the same sensor in a convenient > way. > > This will be supported in the new update. > However, something like this might already exist, just not in mainline > NuttX. If you know of such a thing or have feedback on this idea, > please let me know. Otherwise, I'll move ahead with working on it. > > In the new update, we will also provide the virtual(userspace) sensor concept, which mean that you can: 1. Register the predefined sensor through sensor_register inside kernel 2. Register the custom sensor through sensor_custom_register kernel 3. Register the filter/algo output as virtual sensor through ioctl(like pmtx) from userspace userspace can even open a sensor registered by another core(e.g. sensorhub) in one SoC. > Best regards, > Matt >