I have a driver for a Piezo buzzer running on bare metal that I would like to port to the nRF connect SDK. The existing PWM-based driver in Zephyr does not fulfill my requirements. My driver uses two PPI channels, two GPIOTE channels and one timer instance and I would like to implement it the Zephyr way, i.e. with a proper driver and a device tree overlay defining the hardware.
I've attached the overlay for my custom board (custom_board.dts), the device tree bindings for my piezo buffer driver (buzzer-piezo.yaml) and the prototype driver implementation (piezo.c).
With this initial attempt, I get all sorts of cryptic errors, mainly about missing specifier cell sizes. Unfortunately, the documentation on this matter is thin and I don't find relevant sample code. The few samples using the PPI seem to just rely on the nrfx APIs and hard-code the channels etc. into the application code. I would really prefer to do it the Zephyr way, using device tree definitions.
Why are there no drivers for PPI and GPIOTE in the connect SDK? These seem to be essential peripherals that I use in almost all my nRF-based projects.
Is my approach possible at all without writing full drivers for PPI and GPIOTE?
Can you help me to complete/fix my code or tell me a different approach?
#define DT_DRV_COMPAT buzzer_piezo #include <zephyr/device.h> #include <zephyr/devicetree.h> #include <zephyr/drivers/gpio.h> #include <zephyr/kernel.h> #include <zephyr/logging/log.h> #include <app/drivers/buzzer.h> LOG_MODULE_REGISTER(buzzer_piezo, CONFIG_BUZZER_LOG_LEVEL); struct buzzer_piezo_data { bool state; }; struct buzzer_piezo_config { struct gpio_dt_spec pin1, pin2; unsigned int ppi_channel1, ppi_channel2; unsigned int gpiote_channel1, gpiote_channel2; struct device *timer_dev; struct device *ppi; struct device *gpiote; }; static int buzzer_piezo_set(const struct device *dev, bool state) { const struct buzzer_piezo_config *config = dev->config; struct buzzer_piezo_data *data = dev->data; return 0; } static const struct buzzer_driver_api buzzer_piezo_api = { .set = &buzzer_piezo_set, }; static int buzzer_piezo_init(const struct device *dev) { const struct buzzer_piezo_config *config = dev->config; struct buzzer_piezo_data *data = dev->data; return 0; } #define BUZZER_PIEZO_DEFINE(inst) \ static struct buzzer_piezo_data data##inst; \ \ static const struct buzzer_piezo_config config##inst = { \ .pin1 = GPIO_DT_SPEC_INST_GET_BY_IDX(inst, gpios, 0), \ .pin2 = GPIO_DT_SPEC_INST_GET_BY_IDX(inst, gpios, 1), \ .gpiote_channel1 = ?, \ .gpiote_channel2 = ?, \ .ppi_channel1 = ?, \ .ppi_channel2 = ?, \ .ppi = ?, \ .gpiote = ? }; \ \ DEVICE_DT_INST_DEFINE(inst, buzzer_piezo_init, NULL, &data##inst, \ &config##inst, POST_KERNEL, \ CONFIG_BUZZER_INIT_PRIORITY, &buzzer_piezo_api); DT_INST_FOREACH_STATUS_OKAY(BUZZER_PIEZO_DEFINE)