/* microbit.c - BBC micro:bit specific hooks */ /* * Copyright (c) 2017 Intel Corporation * * SPDX-License-Identifier: Apache-2.0 */ #include #include #include #include #include #include #include #include "board.h" #define SCROLL_SPEED 300 #define BUZZER_PWM_CHANNEL 0 #define BEEP_DURATION K_MSEC(60) #define SEQ_PER_BIT 976 #define SEQ_PAGE (NRF_FICR->CODEPAGESIZE * (NRF_FICR->CODESIZE - 1)) #define SEQ_MAX (NRF_FICR->CODEPAGESIZE * 8 * SEQ_PER_BIT) static const struct gpio_dt_spec button_a = GPIO_DT_SPEC_GET(DT_NODELABEL(buttona), gpios); static const struct gpio_dt_spec button_b = GPIO_DT_SPEC_GET(DT_NODELABEL(buttonb), gpios); static const struct device *const nvm = DEVICE_DT_GET(DT_CHOSEN(zephyr_flash_controller)); static const struct device *const pwm = DEVICE_DT_GET_ANY(nordic_nrf_sw_pwm); static struct k_work button_work; static void button_send_pressed(struct k_work *work) { printk("button_send_pressed()\n"); board_button_1_pressed(); } static void button_pressed(const struct device *dev, struct gpio_callback *cb, uint32_t pins) { struct mb_display *disp = mb_display_get(); if (pins & BIT(button_a.pin)) { k_work_submit(&button_work); } else { uint16_t target = board_set_target(); if (target > 0x0009) { mb_display_print(disp, MB_DISPLAY_MODE_SINGLE, 2 * MSEC_PER_SEC, "A"); } else { mb_display_print(disp, MB_DISPLAY_MODE_SINGLE, 2 * MSEC_PER_SEC, "%X", (target & 0xf)); } } } static const struct { char note; uint32_t period; uint32_t sharp; } period_map[] = { { 'C', 3822, 3608 }, { 'D', 3405, 3214 }, { 'E', 3034, 3034 }, { 'F', 2863, 2703 }, { 'G', 2551, 2407 }, { 'A', 2273, 2145 }, { 'B', 2025, 2025 }, }; static uint32_t get_period(char note, bool sharp) { int i; if (note == ' ') { return 0; } for (i = 0; i < ARRAY_SIZE(period_map); i++) { if (period_map[i].note != note) { continue; } if (sharp) { return period_map[i].sharp; } else { return period_map[i].period; } } return 1500; } void board_play_tune(const char *str) { while (*str) { uint32_t period, duration = 0U; while (*str && isdigit((unsigned char)*str) == 0) { str++; } while (isdigit((unsigned char)*str) != 0) { duration *= 10U; duration += *str - '0'; str++; } if (!*str) { break; } if (str[1] == '#') { period = get_period(*str, true); str += 2; } else { period = get_period(*str, false); str++; } if (period) { pwm_set(pwm, BUZZER_PWM_CHANNEL, PWM_USEC(period), PWM_USEC(period) / 2U, 0); } k_sleep(K_MSEC(duration)); /* Disable the PWM */ pwm_set(pwm, BUZZER_PWM_CHANNEL, 0, 0, 0); } } void board_heartbeat(uint8_t hops, uint16_t feat) { struct mb_display *disp = mb_display_get(); const struct mb_image hops_img[] = { MB_IMAGE({ 1, 1, 1, 1, 1 }, { 1, 1, 1, 1, 1 }, { 1, 1, 1, 1, 1 }, { 1, 1, 1, 1, 1 }, { 1, 1, 1, 1, 1 }), MB_IMAGE({ 1, 1, 1, 1, 1 }, { 1, 1, 1, 1, 1 }, { 1, 1, 0, 1, 1 }, { 1, 1, 1, 1, 1 }, { 1, 1, 1, 1, 1 }), MB_IMAGE({ 1, 1, 1, 1, 1 }, { 1, 0, 0, 0, 1 }, { 1, 0, 0, 0, 1 }, { 1, 0, 0, 0, 1 }, { 1, 1, 1, 1, 1 }), MB_IMAGE({ 1, 1, 1, 1, 1 }, { 1, 0, 0, 0, 1 }, { 1, 0, 0, 0, 1 }, { 1, 0, 0, 0, 1 }, { 1, 1, 1, 1, 1 }), MB_IMAGE({ 1, 0, 1, 0, 1 }, { 0, 0, 0, 0, 0 }, { 1, 0, 0, 0, 1 }, { 0, 0, 0, 0, 0 }, { 1, 0, 1, 0, 1 }) }; printk("%u hops\n", hops); if (hops) { hops = MIN(hops, ARRAY_SIZE(hops_img)); mb_display_image(disp, MB_DISPLAY_MODE_SINGLE, 2 * MSEC_PER_SEC, &hops_img[hops - 1], 1); } } void board_other_dev_pressed(uint16_t addr) { struct mb_display *disp = mb_display_get(); printk("board_other_dev_pressed(0x%04x)\n", addr); mb_display_print(disp, MB_DISPLAY_MODE_SINGLE, 2 * MSEC_PER_SEC, "%X", (addr & 0xf)); } void board_attention(bool attention) { struct mb_display *disp = mb_display_get(); static const struct mb_image attn_img[] = { MB_IMAGE({ 0, 0, 0, 0, 0 }, { 0, 0, 0, 0, 0 }, { 0, 0, 1, 0, 0 }, { 0, 0, 0, 0, 0 }, { 0, 0, 0, 0, 0 }), MB_IMAGE({ 0, 0, 0, 0, 0 }, { 0, 1, 1, 1, 0 }, { 0, 1, 1, 1, 0 }, { 0, 1, 1, 1, 0 }, { 0, 0, 0, 0, 0 }), MB_IMAGE({ 1, 1, 1, 1, 1 }, { 1, 1, 1, 1, 1 }, { 1, 1, 0, 1, 1 }, { 1, 1, 1, 1, 1 }, { 1, 1, 1, 1, 1 }), MB_IMAGE({ 1, 1, 1, 1, 1 }, { 1, 0, 0, 0, 1 }, { 1, 0, 0, 0, 1 }, { 1, 0, 0, 0, 1 }, { 1, 1, 1, 1, 1 }), }; if (attention) { mb_display_image(disp, MB_DISPLAY_MODE_DEFAULT | MB_DISPLAY_FLAG_LOOP, 150, attn_img, ARRAY_SIZE(attn_img)); } else { mb_display_stop(disp); } } static int configure_button(const struct gpio_dt_spec *button) { int err; err = gpio_pin_configure_dt(button, GPIO_INPUT); if (err) { return err; } return gpio_pin_interrupt_configure_dt(button, GPIO_INT_EDGE_TO_ACTIVE); } static int configure_buttons(void) { static struct gpio_callback button_cb; int err; k_work_init(&button_work, button_send_pressed); err = configure_button(&button_a); if (err) { return err; } err = configure_button(&button_b); if (err) { return err; } if (button_a.port != button_b.port) { /* These should be the same device on this board. */ return -EINVAL; } gpio_init_callback(&button_cb, button_pressed, BIT(button_a.pin) | BIT(button_b.pin)); return gpio_add_callback(button_a.port, &button_cb); } int board_init(uint16_t *addr) { struct mb_display *disp = mb_display_get(); if (!(device_is_ready(nvm) && device_is_ready(pwm) && gpio_is_ready_dt(&button_a) && gpio_is_ready_dt(&button_b))) { printk("One or more devices are not ready\n"); return -ENODEV; } *addr = NRF_UICR->CUSTOMER[0]; if (!*addr || *addr == 0xffff) { #if defined(NODE_ADDR) *addr = NODE_ADDR; #else *addr = 0x0b0c; #endif } mb_display_print(disp, MB_DISPLAY_MODE_DEFAULT, SCROLL_SPEED, "0x%04x", *addr); return configure_buttons(); }