1 /*
2 * Copyright (c) 2017 Intel Corporation
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7 #include <zephyr/kernel.h>
8 #include <zephyr/sys/printk.h>
9 #include <zephyr/drivers/gpio.h>
10 #include <zephyr/drivers/pwm.h>
11 #include <zephyr/device.h>
12
13 #include <zephyr/display/mb_display.h>
14
15 #define PERIOD_MIN PWM_USEC(50)
16 #define PERIOD_MAX PWM_USEC(3900)
17
18 #define BEEP_DURATION K_MSEC(60)
19
20 #define NS_TO_HZ(_ns) (NSEC_PER_SEC / (_ns))
21
22 static const struct pwm_dt_spec pwm = PWM_DT_SPEC_GET(DT_PATH(zephyr_user));
23
24 static uint32_t period;
25 static struct k_work beep_work;
26 static volatile bool beep_active;
27
28 static const struct gpio_dt_spec sw0_gpio = GPIO_DT_SPEC_GET(DT_ALIAS(sw0), gpios);
29 static const struct gpio_dt_spec sw1_gpio = GPIO_DT_SPEC_GET(DT_ALIAS(sw1), gpios);
30
31 /* ensure SW0 & SW1 are on same gpio controller */
32 BUILD_ASSERT(DT_SAME_NODE(DT_GPIO_CTLR(DT_ALIAS(sw0), gpios), DT_GPIO_CTLR(DT_ALIAS(sw1), gpios)));
33
beep(struct k_work * work)34 static void beep(struct k_work *work)
35 {
36 /* The "period / 2" pulse duration gives 50% duty cycle, which
37 * should result in the maximum sound volume.
38 */
39 pwm_set_dt(&pwm, period, period / 2U);
40 k_sleep(BEEP_DURATION);
41
42 /* Disable the PWM */
43 pwm_set_pulse_dt(&pwm, 0);
44
45 /* Ensure there's a clear silent period between two tones */
46 k_sleep(K_MSEC(50));
47 beep_active = false;
48 }
49
button_pressed(const struct device * dev,struct gpio_callback * cb,uint32_t pins)50 static void button_pressed(const struct device *dev, struct gpio_callback *cb,
51 uint32_t pins)
52 {
53 struct mb_display *disp;
54
55 if (beep_active) {
56 printk("Button press while beeping\n");
57 return;
58 }
59
60 beep_active = true;
61
62 if (pins & BIT(sw0_gpio.pin)) {
63 printk("A pressed\n");
64 if (period < PERIOD_MAX) {
65 period += PWM_USEC(50U);
66 }
67 } else {
68 printk("B pressed\n");
69 if (period > PERIOD_MIN) {
70 period -= PWM_USEC(50U);
71 }
72 }
73
74 printk("Period is %u us (%u Hz)\n", period / NSEC_PER_USEC,
75 NS_TO_HZ(period));
76
77 disp = mb_display_get();
78 mb_display_print(disp, MB_DISPLAY_MODE_DEFAULT, 500, "%uHz",
79 NS_TO_HZ(period));
80
81 k_work_submit(&beep_work);
82 }
83
main(void)84 int main(void)
85 {
86 static struct gpio_callback button_cb_data;
87
88 if (!pwm_is_ready_dt(&pwm)) {
89 printk("%s: device not ready.\n", pwm.dev->name);
90 return 0;
91 }
92
93 /* since sw0_gpio.port == sw1_gpio.port, we only need to check ready once */
94 if (!gpio_is_ready_dt(&sw0_gpio)) {
95 printk("%s: device not ready.\n", sw0_gpio.port->name);
96 return 0;
97 }
98
99 period = pwm.period;
100
101 gpio_pin_configure_dt(&sw0_gpio, GPIO_INPUT);
102 gpio_pin_configure_dt(&sw1_gpio, GPIO_INPUT);
103
104 gpio_pin_interrupt_configure_dt(&sw0_gpio, GPIO_INT_EDGE_TO_ACTIVE);
105 gpio_pin_interrupt_configure_dt(&sw1_gpio, GPIO_INT_EDGE_TO_ACTIVE);
106
107 gpio_init_callback(&button_cb_data, button_pressed,
108 BIT(sw0_gpio.pin) | BIT(sw1_gpio.pin));
109
110 k_work_init(&beep_work, beep);
111 /* Notify with a beep that we've started */
112 k_work_submit(&beep_work);
113
114 gpio_add_callback(sw0_gpio.port, &button_cb_data);
115 return 0;
116 }
117