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(). Nor do they support filtering or
multiple userspace threads accessing the same sensor in a convenient
way.

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.

Best regards,
Matt

Reply via email to