Add support for Sensor Watch Pro (#412)

* Sensor Watch Pro board definition

* Disable hot-plugging on SWCLK

* Add rainbow test

* Move interrupt mapping to board config

* New color definition for Pro boards in makefile
This commit is contained in:
Jose Castillo 2024-07-06 11:07:02 -04:00 committed by GitHub
parent 89a2af92ef
commit d98d14d236
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
15 changed files with 282 additions and 38 deletions

View file

@ -0,0 +1,64 @@
#include <stdio.h>
#include <string.h>
#include "watch.h"
void app_init(void) {
}
void app_wake_from_backup(void) {
}
void app_setup(void) {
watch_enable_leds();
}
void app_prepare_for_standby(void) {
}
void app_wake_from_standby(void) {
}
bool app_loop(void) {
static uint8_t red = 0;
static uint8_t green = 0;
static uint8_t blue = 255;
static uint8_t phase = 0;
switch (phase) {
case 0:
red++;
if (red == 255) phase = 1;
break;
case 1:
green++;
if (green == 255) phase = 2;
break;
case 2:
red--;
if (red == 0) phase = 3;
break;
case 3:
blue++;
if (blue == 255) phase = 4;
break;
case 4:
green--;
if (green == 0) phase = 5;
break;
case 5:
red++;
if (red == 255) phase = 6;
break;
case 6:
blue--;
if (blue == 0) {
phase = 1;
}
break;
}
watch_set_led_color_rgb(red, green, blue);
delay_ms(2);
return false;
}

View file

@ -0,0 +1,10 @@
TOP = ../../..
include $(TOP)/make.mk
INCLUDES += \
-I../
SRCS += \
../app.c
include $(TOP)/rules.mk

View file

@ -120,4 +120,8 @@
#define D0 GPIO(GPIO_PORTB, 3)
#define D1 GPIO(GPIO_PORTB, 0)
// interrupt mapping
#define EXT_IRQ_AMOUNT 6
#define CONFIG_EIC_EXTINT_MAP {0, PIN_PB00}, {1, PIN_PB01}, {2, PIN_PA02}, {3, PIN_PB03}, {5, PIN_PB05}, {7, PIN_PA07},
#endif // PINS_H_INCLUDED

View file

@ -81,4 +81,8 @@
#define D0 GPIO(GPIO_PORTB, 3)
#define D1 GPIO(GPIO_PORTB, 0)
// interrupt mapping
#define EXT_IRQ_AMOUNT 6
#define CONFIG_EIC_EXTINT_MAP {0, PIN_PB00}, {1, PIN_PB01}, {2, PIN_PA02}, {3, PIN_PB03}, {6, PIN_PA22}, {7, PIN_PA23},
#endif // PINS_H_INCLUDED

View file

@ -81,4 +81,8 @@
#define D0 GPIO(GPIO_PORTB, 3)
#define D1 GPIO(GPIO_PORTB, 0)
// interrupt mapping
#define EXT_IRQ_AMOUNT 6
#define CONFIG_EIC_EXTINT_MAP {0, PIN_PB00}, {1, PIN_PB01}, {2, PIN_PA02}, {3, PIN_PB03}, {6, PIN_PA22}, {7, PIN_PA23},
#endif // PINS_H_INCLUDED

View file

@ -81,4 +81,8 @@
#define D0 GPIO(GPIO_PORTB, 3)
#define D1 GPIO(GPIO_PORTB, 0)
// interrupt mapping
#define EXT_IRQ_AMOUNT 6
#define CONFIG_EIC_EXTINT_MAP {0, PIN_PB00}, {1, PIN_PB01}, {2, PIN_PA02}, {3, PIN_PB03}, {6, PIN_PA22}, {7, PIN_PA23},
#endif // PINS_H_INCLUDED

View file

@ -0,0 +1,122 @@
#ifndef PINS_H_INCLUDED
#define PINS_H_INCLUDED
// Detects if we are on USB power.
#define VBUS_DET GPIO(GPIO_PORTB, 5)
// Buttons
#define BTN_ALARM GPIO(GPIO_PORTA, 2)
#define WATCH_BTN_ALARM_EIC_CHANNEL 2
#define BTN_LIGHT GPIO(GPIO_PORTA, 30)
#define WATCH_BTN_LIGHT_EIC_CHANNEL 10
#define BTN_MODE GPIO(GPIO_PORTA, 31)
#define WATCH_BTN_MODE_EIC_CHANNEL 11
// Buzzer
#define BUZZER GPIO(GPIO_PORTA, 27)
#define WATCH_BUZZER_TCC_PINMUX PINMUX_PA27F_TCC0_WO5
#define WATCH_BUZZER_TCC_CHANNEL 1
// LEDs
#define WATCH_INVERT_LED_POLARITY
#define RED GPIO(GPIO_PORTA, 12)
#define WATCH_RED_TCC_CHANNEL 2
#define WATCH_RED_TCC_PINMUX PINMUX_PA12F_TCC0_WO6
#define BLUE GPIO(GPIO_PORTA, 13)
#define WATCH_BLUE_TCC_CHANNEL 3
#define WATCH_BLUE_TCC_PINMUX PINMUX_PA13F_TCC0_WO7
#define GREEN GPIO(GPIO_PORTA, 22)
#define WATCH_GREEN_TCC_CHANNEL 0
#define WATCH_GREEN_TCC_PINMUX PINMUX_PA22F_TCC0_WO4
// Segment LCD
#define SLCD0 GPIO(GPIO_PORTB, 6)
#define SLCD1 GPIO(GPIO_PORTB, 7)
#define SLCD2 GPIO(GPIO_PORTB, 8)
#define SLCD3 GPIO(GPIO_PORTB, 9)
#define SLCD4 GPIO(GPIO_PORTA, 5)
#define SLCD5 GPIO(GPIO_PORTA, 6)
#define SLCD6 GPIO(GPIO_PORTA, 7)
#define SLCD7 GPIO(GPIO_PORTA, 8)
#define SLCD8 GPIO(GPIO_PORTA, 9)
#define SLCD9 GPIO(GPIO_PORTA, 10)
#define SLCD10 GPIO(GPIO_PORTA, 11)
#define SLCD11 GPIO(GPIO_PORTB, 11)
#define SLCD12 GPIO(GPIO_PORTB, 12)
#define SLCD13 GPIO(GPIO_PORTB, 13)
#define SLCD14 GPIO(GPIO_PORTB, 14)
#define SLCD15 GPIO(GPIO_PORTB, 15)
#define SLCD16 GPIO(GPIO_PORTA, 14)
#define SLCD17 GPIO(GPIO_PORTA, 15)
#define SLCD18 GPIO(GPIO_PORTA, 16)
#define SLCD19 GPIO(GPIO_PORTA, 17)
#define SLCD20 GPIO(GPIO_PORTA, 18)
#define SLCD21 GPIO(GPIO_PORTA, 19)
#define SLCD22 GPIO(GPIO_PORTB, 16)
#define SLCD23 GPIO(GPIO_PORTB, 17)
#define SLCD24 GPIO(GPIO_PORTA, 20)
#define SLCD25 GPIO(GPIO_PORTA, 21)
#define SLCD26 GPIO(GPIO_PORTA, 23)
// This board uses a slightly different pin mapping from the standard watch, and it's not enough to
// just declare the pins. We also have to set the LCD Pin Enable register with the SLCD pins we're
// using. These numbers are not port/pin numbers, but the "SLCD/LP[x]" numbers in the pinmux table.
// If not defined in pins.h, the LCD driver will fall back to the pin mapping in hpl_slcd_config.h.
// LPENL is for pins SLCD/LP[0..31].
#define CONF_SLCD_LPENL (\
(uint32_t)1 << 0 | \
(uint32_t)1 << 1 | \
(uint32_t)1 << 2 | \
(uint32_t)1 << 3 | \
(uint32_t)1 << 5 | \
(uint32_t)1 << 6 | \
(uint32_t)1 << 7 | \
(uint32_t)1 << 11 | \
(uint32_t)1 << 12 | \
(uint32_t)1 << 13 | \
(uint32_t)1 << 14 | \
(uint32_t)1 << 21 | \
(uint32_t)1 << 22 | \
(uint32_t)1 << 23 | \
(uint32_t)1 << 24 | \
(uint32_t)1 << 25 | \
(uint32_t)1 << 30 | \
(uint32_t)1 << 31 | 0)
// LPENH is for pins SLCD/LP[32..51], where bit 0 represents pin 32.
#define CONF_SLCD_LPENH (\
(uint32_t)1 << (32 - 32) | \
(uint32_t)1 << (33 - 32) | \
(uint32_t)1 << (34 - 32) | \
(uint32_t)1 << (35 - 32) | \
(uint32_t)1 << (42 - 32) | \
(uint32_t)1 << (43 - 32) | \
(uint32_t)1 << (48 - 32) | \
(uint32_t)1 << (49 - 32) | \
(uint32_t)1 << (51 - 32) | 0)
// 9-pin connector
#define A0 GPIO(GPIO_PORTB, 4)
#define WATCH_A0_EIC_CHANNEL 4
#define A1 GPIO(GPIO_PORTB, 1)
#define WATCH_A1_EIC_CHANNEL 1
#define A2 GPIO(GPIO_PORTB, 2)
#define WATCH_A2_EIC_CHANNEL 2
#define A3 GPIO(GPIO_PORTB, 3)
#define WATCH_A3_EIC_CHANNEL 3
#define A4 GPIO(GPIO_PORTB, 0)
#define WATCH_A4_EIC_CHANNEL 0
#define SDA GPIO(GPIO_PORTB, 30)
#define SCL GPIO(GPIO_PORTB, 31)
// aliases for as A3/A4; these were mentioned as D0/D1 in early documentation.
#define D0 GPIO(GPIO_PORTB, 3)
#define D1 GPIO(GPIO_PORTB, 0)
// interrupt mapping
#define EXT_IRQ_AMOUNT 6
#define CONFIG_EIC_EXTINT_MAP {0, PIN_PB00}, {1, PIN_PB01}, {2, PIN_PA02}, {3, PIN_PB03}, {10, PIN_PA30}, {11, PIN_PA31},
#endif // PINS_H_INCLUDED

28
make.mk
View file

@ -6,7 +6,19 @@ BUILD = ./build-sim
endif
BIN = watch
ifndef BOARD
ifndef COLOR
$(error Set the COLOR variable to RED, BLUE, GREEN or PRO depending on what board you have.)
endif
COLOR_VALID := $(filter $(COLOR),RED BLUE GREEN PRO)
ifeq ($(COLOR_VALID),)
$(error COLOR must be RED, BLUE, GREEN or PRO)
endif
ifeq ($(COLOR), PRO)
override BOARD = OSO-SWAT-C1-00
else
override BOARD = OSO-SWAT-A1-05
endif
@ -205,20 +217,6 @@ SRCS += \
endif
ifeq ($(LED), BLUE)
CFLAGS += -DWATCH_IS_BLUE_BOARD
endif
ifndef COLOR
$(error Set the COLOR variable to RED, BLUE, or GREEN depending on what board you have.)
endif
COLOR_VALID := $(filter $(COLOR),RED BLUE GREEN)
ifeq ($(COLOR_VALID),)
$(error COLOR must be RED, BLUE, or GREEN)
endif
ifeq ($(COLOR), BLUE)
CFLAGS += -DWATCH_IS_BLUE_BOARD
endif

View file

@ -37,6 +37,7 @@
#include <string.h>
#include <utils.h>
#include <utils_assert.h>
#include "pins.h"
#ifdef __MINGW32__
#define ffs __builtin_ffs
@ -62,16 +63,6 @@ static int ffs(int v)
#define INVALID_EXTINT_NUMBER 0xFF
#define INVALID_PIN_NUMBER 0xFFFFFFFF
#ifndef CONFIG_EIC_EXTINT_MAP
/** Dummy mapping to pass compiling. */
#define CONFIG_EIC_EXTINT_MAP \
{ \
INVALID_EXTINT_NUMBER, INVALID_PIN_NUMBER \
}
#endif
#define EXT_IRQ_AMOUNT 6
/**
* \brief EXTINTx and pin number map
*/

View file

@ -35,10 +35,24 @@ void watch_disable_leds(void) {
}
void watch_set_led_color(uint8_t red, uint8_t green) {
#ifdef WATCH_BLUE_TCC_CHANNEL
watch_set_led_color_rgb(red, green, 0);
#else
watch_set_led_color_rgb(red, green, green);
#endif
}
void watch_set_led_color_rgb(uint8_t red, uint8_t green, uint8_t blue) {
#ifndef WATCH_BLUE_TCC_CHANNEL
(void) blue; // silence warning
#endif
if (hri_tcc_get_CTRLA_reg(TCC0, TCC_CTRLA_ENABLE)) {
uint32_t period = hri_tcc_get_PER_reg(TCC0, TCC_PER_MASK);
hri_tcc_write_CCBUF_reg(TCC0, WATCH_RED_TCC_CHANNEL, ((period * red * 1000ull) / 255000ull));
hri_tcc_write_CCBUF_reg(TCC0, WATCH_GREEN_TCC_CHANNEL, ((period * green * 1000ull) / 255000ull));
#ifdef WATCH_BLUE_TCC_CHANNEL
hri_tcc_write_CCBUF_reg(TCC0, WATCH_BLUE_TCC_CHANNEL, ((period * blue * 1000ull) / 255000ull));
#endif
}
}

View file

@ -31,6 +31,11 @@ void _watch_init(void) {
// disable the LED pin (it may have been enabled by the bootloader)
watch_disable_digital_output(GPIO(GPIO_PORTA, 20));
// disable debugger hot-plugging
gpio_set_pin_function(SWCLK, GPIO_PIN_FUNCTION_OFF);
gpio_set_pin_direction(SWCLK, GPIO_DIRECTION_OFF);
gpio_set_pin_pull_mode(SWCLK, GPIO_PULL_OFF);
// RAM should be back-biased in STANDBY
PM->STDBYCFG.bit.BBIASHS = 1;
@ -152,11 +157,14 @@ void _watch_enable_tcc(void) {
// period (i.e. a square wave with a 50% duty cycle).
// * LEDs on CC[2] and CC[3] can be set to any value from 0 (off) to PER (fully on).
hri_tcc_write_WAVE_reg(TCC0, TCC_WAVE_WAVEGEN_NPWM);
#ifdef WATCH_INVERT_LED_POLARITY
// This is here for the dev board, which uses a common anode LED (instead of common cathode like the actual watch).
#ifdef WATCH_INVERT_LED_POLARITY
// This is here for the dev board and Pro, which use a common anode LED (instead of common cathode like the actual watch).
hri_tcc_set_WAVE_reg(TCC0, (1 << (TCC_WAVE_POL0_Pos + WATCH_RED_TCC_CHANNEL)) |
#ifdef WATCH_BLUE_TCC_CHANNEL
(1 << (TCC_WAVE_POL0_Pos + WATCH_BLUE_TCC_CHANNEL)) |
#endif // WATCH_BLUE_TCC_CHANNEL
(1 << (TCC_WAVE_POL0_Pos + WATCH_GREEN_TCC_CHANNEL)));
#endif
#endif // WATCH_INVERT_LED_POLARITY
// The buzzer will set the period depending on the tone it wants to play, but we have to set some period here to
// get the LED working. Almost any period will do, tho it should be below 20000 (i.e. 50 Hz) to avoid flickering.
hri_tcc_write_PER_reg(TCC0, 1024);
@ -164,6 +172,9 @@ void _watch_enable_tcc(void) {
hri_tcc_write_CC_reg(TCC0, WATCH_BUZZER_TCC_CHANNEL, 0);
hri_tcc_write_CC_reg(TCC0, WATCH_RED_TCC_CHANNEL, 0);
hri_tcc_write_CC_reg(TCC0, WATCH_GREEN_TCC_CHANNEL, 0);
#ifdef WATCH_BLUE_TCC_CHANNEL
hri_tcc_write_CC_reg(TCC0, WATCH_BLUE_TCC_CHANNEL, 0);
#endif
// Enable the TCC
hri_tcc_set_CTRLA_ENABLE_bit(TCC0);
hri_tcc_wait_for_sync(TCC0, TCC_SYNCBUSY_ENABLE);
@ -173,6 +184,10 @@ void _watch_enable_tcc(void) {
gpio_set_pin_function(RED, WATCH_RED_TCC_PINMUX);
gpio_set_pin_direction(GREEN, GPIO_DIRECTION_OUT);
gpio_set_pin_function(GREEN, WATCH_GREEN_TCC_PINMUX);
#ifdef WATCH_BLUE_TCC_CHANNEL
gpio_set_pin_direction(BLUE, GPIO_DIRECTION_OUT);
gpio_set_pin_function(BLUE, WATCH_BLUE_TCC_PINMUX);
#endif
}
void _watch_disable_tcc(void) {
@ -183,6 +198,10 @@ void _watch_disable_tcc(void) {
gpio_set_pin_function(RED, GPIO_PIN_FUNCTION_OFF);
gpio_set_pin_direction(GREEN, GPIO_DIRECTION_OFF);
gpio_set_pin_function(GREEN, GPIO_PIN_FUNCTION_OFF);
#ifdef WATCH_BLUE_TCC_CHANNEL
gpio_set_pin_direction(BLUE, GPIO_DIRECTION_OFF);
gpio_set_pin_function(BLUE, GPIO_PIN_FUNCTION_OFF);
#endif
// disable the TCC
hri_tcc_clear_CTRLA_ENABLE_bit(TCC0);

View file

@ -481,7 +481,7 @@
// <i> Indicates whether the external interrupt 10 filter is enabled or not
// <id> eic_arch_filten10
#ifndef CONF_EIC_FILTEN10
#define CONF_EIC_FILTEN10 0
#define CONF_EIC_FILTEN10 1
#endif
// <q> External Interrupt 10 Event Output Enable
@ -523,7 +523,7 @@
// <i> Indicates whether the external interrupt 11 filter is enabled or not
// <id> eic_arch_filten11
#ifndef CONF_EIC_FILTEN11
#define CONF_EIC_FILTEN11 0
#define CONF_EIC_FILTEN11 1
#endif
// <q> External Interrupt 11 Event Output Enable
@ -723,14 +723,6 @@
// </e>
// this is still a hack: if the user wants to use PA02 (alarm button) as an RTC interrupt pin and PB02 (9-pin A2) on the EIC, we don't support that.
// TODO item: refactor out our reliance on the ASF external interrupt driver. - joey 11/30
#ifdef CRYSTALLESS
#define CONFIG_EIC_EXTINT_MAP {0, PIN_PB00}, {1, PIN_PB01}, {2, PIN_PA02}, {3, PIN_PB03}, {5, PIN_PB05}, {7, PIN_PA07},
#else
#define CONFIG_EIC_EXTINT_MAP {0, PIN_PB00}, {1, PIN_PB01}, {2, PIN_PA02}, {3, PIN_PB03}, {6, PIN_PA22}, {7, PIN_PA23},
#endif
// <<< end of configuration section >>>
#endif // HPL_EIC_CONFIG_H

View file

@ -30,6 +30,9 @@
#include "driver_init.h"
#include "pins.h"
#define SWCLK GPIO(GPIO_PORTA, 30)
#define SWDIO GPIO(GPIO_PORTA, 31)
#ifdef __EMSCRIPTEN__
#include "watch_main_loop.h"
#endif // __EMSCRIPTEN__

View file

@ -63,6 +63,16 @@ void watch_disable_leds(void);
*/
void watch_set_led_color(uint8_t red, uint8_t green);
/** @brief On boards with an RGB LED, sets the LED to a custom color by modulating each output's duty cycle.
* @param red The red value from 0-255.
* @param green The green value from 0-255.
* @param blue The blue value from 0-255.
* @note If you are displaying a custom color, you will need to prevent your app from going to sleep
* while the LED is on; otherwise, the color will not display correctly. You can do this by
* returning false in your app_loop method.
*/
void watch_set_led_color_rgb(uint8_t red, uint8_t green, uint8_t blue);
/** @brief Sets the red LED to full brightness, and turns the green LED off.
* @details Of the two LED's in the RG bi-color LED, the red LED is the less power-efficient one (~4.5 mA).
*/

View file

@ -44,6 +44,11 @@ void watch_set_led_color(uint8_t red, uint8_t green) {
}, red, green);
}
void watch_set_led_color_rgb(uint8_t red, uint8_t green, uint8_t blue) {
(void) blue;
watch_set_led_color(red, green);
}
void watch_set_led_red(void) {
watch_set_led_color(255, 0);
}