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