1 /* microbit.c - BBC micro:bit specific hooks */
2 
3 /*
4  * Copyright (c) 2017 Intel Corporation
5  *
6  * SPDX-License-Identifier: Apache-2.0
7  */
8 
9 #include <soc.h>
10 #include <zephyr/sys/printk.h>
11 #include <ctype.h>
12 #include <zephyr/drivers/gpio.h>
13 #include <zephyr/drivers/pwm.h>
14 
15 #include <zephyr/display/mb_display.h>
16 
17 #include <zephyr/bluetooth/mesh.h>
18 
19 #include "board.h"
20 
21 #define SCROLL_SPEED   300
22 
23 #define BUZZER_PWM_CHANNEL 0
24 #define BEEP_DURATION  K_MSEC(60)
25 
26 #define SEQ_PER_BIT  976
27 #define SEQ_PAGE     (NRF_FICR->CODEPAGESIZE * (NRF_FICR->CODESIZE - 1))
28 #define SEQ_MAX      (NRF_FICR->CODEPAGESIZE * 8 * SEQ_PER_BIT)
29 
30 static const struct gpio_dt_spec button_a =
31 	GPIO_DT_SPEC_GET(DT_NODELABEL(buttona), gpios);
32 static const struct gpio_dt_spec button_b =
33 	GPIO_DT_SPEC_GET(DT_NODELABEL(buttonb), gpios);
34 static const struct device *const nvm =
35 	DEVICE_DT_GET(DT_CHOSEN(zephyr_flash_controller));
36 static const struct device *const pwm =
37 	DEVICE_DT_GET_ANY(nordic_nrf_sw_pwm);
38 
39 static struct k_work button_work;
40 
button_send_pressed(struct k_work * work)41 static void button_send_pressed(struct k_work *work)
42 {
43 	printk("button_send_pressed()\n");
44 	board_button_1_pressed();
45 }
46 
button_pressed(const struct device * dev,struct gpio_callback * cb,uint32_t pins)47 static void button_pressed(const struct device *dev, struct gpio_callback *cb,
48 			   uint32_t pins)
49 {
50 	struct mb_display *disp = mb_display_get();
51 
52 	if (pins & BIT(button_a.pin)) {
53 		k_work_submit(&button_work);
54 	} else {
55 		uint16_t target = board_set_target();
56 
57 		if (target > 0x0009) {
58 			mb_display_print(disp, MB_DISPLAY_MODE_SINGLE,
59 					 2 * MSEC_PER_SEC, "A");
60 		} else {
61 			mb_display_print(disp, MB_DISPLAY_MODE_SINGLE,
62 					 2 * MSEC_PER_SEC, "%X", (target & 0xf));
63 		}
64 	}
65 }
66 
67 static const struct {
68 	char  note;
69 	uint32_t period;
70 	uint32_t sharp;
71 } period_map[] = {
72 	{ 'C',  3822,  3608 },
73 	{ 'D',  3405,  3214 },
74 	{ 'E',  3034,  3034 },
75 	{ 'F',  2863,  2703 },
76 	{ 'G',  2551,  2407 },
77 	{ 'A',  2273,  2145 },
78 	{ 'B',  2025,  2025 },
79 };
80 
get_period(char note,bool sharp)81 static uint32_t get_period(char note, bool sharp)
82 {
83 	int i;
84 
85 	if (note == ' ') {
86 		return 0;
87 	}
88 
89 	for (i = 0; i < ARRAY_SIZE(period_map); i++) {
90 		if (period_map[i].note != note) {
91 			continue;
92 		}
93 
94 		if (sharp) {
95 			return period_map[i].sharp;
96 		} else {
97 			return period_map[i].period;
98 		}
99 	}
100 
101 	return 1500;
102 }
103 
board_play_tune(const char * str)104 void board_play_tune(const char *str)
105 {
106 	while (*str) {
107 		uint32_t period, duration = 0U;
108 
109 		while (*str && isdigit((unsigned char)*str) == 0) {
110 			str++;
111 		}
112 
113 		while (isdigit((unsigned char)*str) != 0) {
114 			duration *= 10U;
115 			duration += *str - '0';
116 			str++;
117 		}
118 
119 		if (!*str) {
120 			break;
121 		}
122 
123 		if (str[1] == '#') {
124 			period = get_period(*str, true);
125 			str += 2;
126 		} else {
127 			period = get_period(*str, false);
128 			str++;
129 		}
130 
131 		if (period) {
132 			pwm_set(pwm, BUZZER_PWM_CHANNEL, PWM_USEC(period),
133 				PWM_USEC(period) / 2U, 0);
134 		}
135 
136 		k_sleep(K_MSEC(duration));
137 
138 		/* Disable the PWM */
139 		pwm_set(pwm, BUZZER_PWM_CHANNEL, 0, 0, 0);
140 	}
141 }
142 
board_heartbeat(uint8_t hops,uint16_t feat)143 void board_heartbeat(uint8_t hops, uint16_t feat)
144 {
145 	struct mb_display *disp = mb_display_get();
146 	const struct mb_image hops_img[] = {
147 		MB_IMAGE({ 1, 1, 1, 1, 1 },
148 			 { 1, 1, 1, 1, 1 },
149 			 { 1, 1, 1, 1, 1 },
150 			 { 1, 1, 1, 1, 1 },
151 			 { 1, 1, 1, 1, 1 }),
152 		MB_IMAGE({ 1, 1, 1, 1, 1 },
153 			 { 1, 1, 1, 1, 1 },
154 			 { 1, 1, 0, 1, 1 },
155 			 { 1, 1, 1, 1, 1 },
156 			 { 1, 1, 1, 1, 1 }),
157 		MB_IMAGE({ 1, 1, 1, 1, 1 },
158 			 { 1, 0, 0, 0, 1 },
159 			 { 1, 0, 0, 0, 1 },
160 			 { 1, 0, 0, 0, 1 },
161 			 { 1, 1, 1, 1, 1 }),
162 		MB_IMAGE({ 1, 1, 1, 1, 1 },
163 			 { 1, 0, 0, 0, 1 },
164 			 { 1, 0, 0, 0, 1 },
165 			 { 1, 0, 0, 0, 1 },
166 			 { 1, 1, 1, 1, 1 }),
167 		MB_IMAGE({ 1, 0, 1, 0, 1 },
168 			 { 0, 0, 0, 0, 0 },
169 			 { 1, 0, 0, 0, 1 },
170 			 { 0, 0, 0, 0, 0 },
171 			 { 1, 0, 1, 0, 1 })
172 	};
173 
174 	printk("%u hops\n", hops);
175 
176 	if (hops) {
177 		hops = MIN(hops, ARRAY_SIZE(hops_img));
178 		mb_display_image(disp, MB_DISPLAY_MODE_SINGLE, 2 * MSEC_PER_SEC,
179 				 &hops_img[hops - 1], 1);
180 	}
181 }
182 
board_other_dev_pressed(uint16_t addr)183 void board_other_dev_pressed(uint16_t addr)
184 {
185 	struct mb_display *disp = mb_display_get();
186 
187 	printk("board_other_dev_pressed(0x%04x)\n", addr);
188 
189 	mb_display_print(disp, MB_DISPLAY_MODE_SINGLE, 2 * MSEC_PER_SEC, "%X",
190 			 (addr & 0xf));
191 }
192 
board_attention(bool attention)193 void board_attention(bool attention)
194 {
195 	struct mb_display *disp = mb_display_get();
196 	static const struct mb_image attn_img[] = {
197 		MB_IMAGE({ 0, 0, 0, 0, 0 },
198 			 { 0, 0, 0, 0, 0 },
199 			 { 0, 0, 1, 0, 0 },
200 			 { 0, 0, 0, 0, 0 },
201 			 { 0, 0, 0, 0, 0 }),
202 		MB_IMAGE({ 0, 0, 0, 0, 0 },
203 			 { 0, 1, 1, 1, 0 },
204 			 { 0, 1, 1, 1, 0 },
205 			 { 0, 1, 1, 1, 0 },
206 			 { 0, 0, 0, 0, 0 }),
207 		MB_IMAGE({ 1, 1, 1, 1, 1 },
208 			 { 1, 1, 1, 1, 1 },
209 			 { 1, 1, 0, 1, 1 },
210 			 { 1, 1, 1, 1, 1 },
211 			 { 1, 1, 1, 1, 1 }),
212 		MB_IMAGE({ 1, 1, 1, 1, 1 },
213 			 { 1, 0, 0, 0, 1 },
214 			 { 1, 0, 0, 0, 1 },
215 			 { 1, 0, 0, 0, 1 },
216 			 { 1, 1, 1, 1, 1 }),
217 	};
218 
219 	if (attention) {
220 		mb_display_image(disp,
221 				 MB_DISPLAY_MODE_DEFAULT | MB_DISPLAY_FLAG_LOOP,
222 				 150, attn_img, ARRAY_SIZE(attn_img));
223 	} else {
224 		mb_display_stop(disp);
225 	}
226 }
227 
configure_button(const struct gpio_dt_spec * button)228 static int configure_button(const struct gpio_dt_spec *button)
229 {
230 	int err;
231 
232 	err = gpio_pin_configure_dt(button, GPIO_INPUT);
233 	if (err) {
234 		return err;
235 	}
236 	return gpio_pin_interrupt_configure_dt(button, GPIO_INT_EDGE_TO_ACTIVE);
237 }
238 
configure_buttons(void)239 static int configure_buttons(void)
240 {
241 	static struct gpio_callback button_cb;
242 	int err;
243 
244 	k_work_init(&button_work, button_send_pressed);
245 
246 	err = configure_button(&button_a);
247 	if (err) {
248 		return err;
249 	}
250 
251 	err = configure_button(&button_b);
252 	if (err) {
253 		return err;
254 	}
255 
256 	if (button_a.port != button_b.port) {
257 		/* These should be the same device on this board. */
258 		return -EINVAL;
259 	}
260 
261 	gpio_init_callback(&button_cb, button_pressed,
262 			   BIT(button_a.pin) | BIT(button_b.pin));
263 	return gpio_add_callback(button_a.port, &button_cb);
264 }
265 
board_init(uint16_t * addr)266 int board_init(uint16_t *addr)
267 {
268 	struct mb_display *disp = mb_display_get();
269 
270 	if (!(device_is_ready(nvm) && device_is_ready(pwm) &&
271 	      gpio_is_ready_dt(&button_a) &&
272 	      gpio_is_ready_dt(&button_b))) {
273 		printk("One or more devices are not ready\n");
274 		return -ENODEV;
275 	}
276 
277 	*addr = NRF_UICR->CUSTOMER[0];
278 	if (!*addr || *addr == 0xffff) {
279 #if defined(NODE_ADDR)
280 		*addr = NODE_ADDR;
281 #else
282 		*addr = 0x0b0c;
283 #endif
284 	}
285 
286 	mb_display_print(disp, MB_DISPLAY_MODE_DEFAULT, SCROLL_SPEED,
287 			 "0x%04x", *addr);
288 
289 	return configure_buttons();
290 }
291