Test #2 (PWM buzzer):
I have CM3, CM3+, CM4, and CM4S devices with an electro-mechanical buzzer. The buzzer is a cheap way to sound alarms and add audio feedback when a touchscreen screen is touched. It needs to be able to create a clean, consistent, beep using a PWM signal. Currently, on those devices I'm currently using pigpio, which uses a very clever DMA trick to produce a near perfect beep. But pigpio can't be used on the CM5. Although PIO would be the better choice, it would be convenient if I could use the same code on all compute module devices. Currently I can't, but maybe with PREEMPT_RT I can. Let's test.
Device
CM4S-Lite 1GB RAM
Configuration
isolcpus=domain,managed_irq,3 irqaffinity=0-2 rcu_nocbs=3 added to cmdline.txt
Control
Using pigpio to generated a 2kHz beep. The priority of the pigpio service was not changed, and CPU isolation was not utilized, as neither is necessary to achieve a nice clean beep. Even if the Chromium browser is launched, in an attempt to cause disturbance, the beep remains clean and consistent.
Code:Audio:
https://drive.google.com/file/d/1yqyx9j ... drive_link
Experiment:
Using ligpiod to generated the 2kHz beep. Note that in this test, I'm employing both a sleep and a spinwait together to delay for the necessary 250us. I have observed in other experiments that this works best because waking up from sleep has unpredictable timing.
Procedure:
1. Start program with the command ./beep
2. About 5 seconds in, run sudo chrt -f -p 99 `pidof beep` && sudo taskset -cp 3 `pidof beep` to change the process's priority and run it on the isolated CPU.
3. About 10 seconds in, start the Chromium web browser in an attempt to introduce some disturbance.
Code:kernel8 Audio:
https://drive.google.com/file/d/1Kfv7EG ... drive_link
kernel8_rt Audio:
https://drive.google.com/file/d/15xOzX5 ... drive_link
Results
* There is a clear difference between pigpio and a pure software PWM. pigpio is much better.
* I don't notice much of a difference between kernel8 and kernel8_rt. Launching the chromium browser clearly adds disturbance even when using kernel8_rt.
Conclusion
* chrt, i.e. real-time scheduling is essential. Although not demonstrated in this post, taskset helps a little to reduce disturbance when the Chromium browser is launched.
* For software PWM on the CM4, kerne8_rt can't beat pigpio.
* I don't notice any significant difference between kernel8 and kernel8_rt for this specific use case if FIFO scheduling and CPU isolation are employed for both.
I have CM3, CM3+, CM4, and CM4S devices with an electro-mechanical buzzer. The buzzer is a cheap way to sound alarms and add audio feedback when a touchscreen screen is touched. It needs to be able to create a clean, consistent, beep using a PWM signal. Currently, on those devices I'm currently using pigpio, which uses a very clever DMA trick to produce a near perfect beep. But pigpio can't be used on the CM5. Although PIO would be the better choice, it would be convenient if I could use the same code on all compute module devices. Currently I can't, but maybe with PREEMPT_RT I can. Let's test.
Device
CM4S-Lite 1GB RAM
Configuration
isolcpus=domain,managed_irq,3 irqaffinity=0-2 rcu_nocbs=3 added to cmdline.txt
Control
Using pigpio to generated a 2kHz beep. The priority of the pigpio service was not changed, and CPU isolation was not utilized, as neither is necessary to achieve a nice clean beep. Even if the Chromium browser is launched, in an attempt to cause disturbance, the beep remains clean and consistent.
Code:
Code:
#include <pigpiod_if2.h>#include <thread> using namespace std; #define PIN 30 int main(){ auto instance = pigpio_start(NULL, NULL); // 2000Hz set_PWM_frequency(instance, PIN, 2000); // 128/255 = 50% duty set_PWM_dutycycle(instance, PIN, 128); // play for 10 seconds this_thread::sleep_for(chrono::milliseconds(10000)); set_PWM_dutycycle(instance, PIN, 0); pigpio_stop(instance); return 0;}https://drive.google.com/file/d/1yqyx9j ... drive_link
Experiment:
Using ligpiod to generated the 2kHz beep. Note that in this test, I'm employing both a sleep and a spinwait together to delay for the necessary 250us. I have observed in other experiments that this works best because waking up from sleep has unpredictable timing.
Procedure:
1. Start program with the command ./beep
2. About 5 seconds in, run sudo chrt -f -p 99 `pidof beep` && sudo taskset -cp 3 `pidof beep` to change the process's priority and run it on the isolated CPU.
3. About 10 seconds in, start the Chromium web browser in an attempt to introduce some disturbance.
Code:
Code:
#include <gpiod.h>#include <stdio.h>#include <stdlib.h>#include <unistd.h>#include <signal.h>#include <errno.h>// GPIO chip and line settings#define GPIO_CHIP "gpiochip0"#define GPIO_LINE 30// Global variables for cleanupstruct gpiod_chip *chip = NULL;struct gpiod_line *line = NULL;void cleanup(int signum) { if (line) { gpiod_line_set_value(line, 0); // Set GPIO low gpiod_line_release(line); } if (chip) { gpiod_chip_close(chip); } printf("\nCleaned up and exiting.\n"); exit(0);}void spin_wait(int us) { struct timespec start, current; clock_gettime(CLOCK_MONOTONIC, &start); long target_ns = us * 1000; // in nanoseconds do { clock_gettime(CLOCK_MONOTONIC, ¤t); } while ((current.tv_sec - start.tv_sec) * 1000000000L + (current.tv_nsec - start.tv_nsec) < target_ns);}int main() { // Set up signal handler for Ctrl+C signal(SIGINT, cleanup); // Open GPIO chip chip = gpiod_chip_open_by_name(GPIO_CHIP); if (!chip) { perror("Failed to open GPIO chip"); return 1; } // Get GPIO line line = gpiod_chip_get_line(chip, GPIO_LINE); if (!line) { perror("Failed to get GPIO line"); gpiod_chip_close(chip); return 1; } // Request line as output int ret = gpiod_line_request_output(line, "toggle_gpio", 0); if (ret < 0) { perror("Failed to request GPIO as output"); gpiod_line_release(line); gpiod_chip_close(chip); return 1; } // Toggle GPIO at 2kHz (500us period, 250us per state) while (1) { gpiod_line_set_value(line, 1); // Set high usleep(150); // Wait 250us spin_wait(100); gpiod_line_set_value(line, 0); // Set low usleep(150); // Wait 250us spin_wait(100); } // Unreachable due to infinite loop, cleanup handled by signal return 0;}https://drive.google.com/file/d/1Kfv7EG ... drive_link
kernel8_rt Audio:
https://drive.google.com/file/d/15xOzX5 ... drive_link
Results
* There is a clear difference between pigpio and a pure software PWM. pigpio is much better.
* I don't notice much of a difference between kernel8 and kernel8_rt. Launching the chromium browser clearly adds disturbance even when using kernel8_rt.
Conclusion
* chrt, i.e. real-time scheduling is essential. Although not demonstrated in this post, taskset helps a little to reduce disturbance when the Chromium browser is launched.
* For software PWM on the CM4, kerne8_rt can't beat pigpio.
* I don't notice any significant difference between kernel8 and kernel8_rt for this specific use case if FIFO scheduling and CPU isolation are employed for both.
Statistics: Posted by JinShil — Thu Jun 05, 2025 11:36 pm