Skip to content

Commit a26b0e2

Browse files
committed
platform/build_hat: Add support for LEDs.
Add a new PWM driver for the Pico and use it to control the LEDs on the Build HAT. Even though the LEDs are separate (not a multicolor LED), we still treat is as a single LED to be consistent with the other hubs (i.e. so that hub.light.on() works the same on all hubs). Closes: pybricks/support#2477
1 parent 18abf52 commit a26b0e2

10 files changed

Lines changed: 176 additions & 9 deletions

File tree

.vscode/c_cpp_properties.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -359,7 +359,8 @@
359359
"-fsingle-precision-constant"
360360
],
361361
"cStandard": "c11",
362-
"intelliSenseMode": "gcc-arm"
362+
"intelliSenseMode": "gcc-arm",
363+
"compilerPath": "arm-none-eabi-gcc"
363364
},
364365
{
365366
"name": "pbio/test",

bricks/_common/sources.mk

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -169,6 +169,7 @@ PBIO_SRC_C = $(addprefix lib/pbio/,\
169169
drv/pwm/pwm_core.c \
170170
drv/pwm/pwm_ev3.c \
171171
drv/pwm/pwm_lp50xx_stm32.c \
172+
drv/pwm/pwm_pico.c \
172173
drv/pwm/pwm_stm32_tim.c \
173174
drv/pwm/pwm_test.c \
174175
drv/pwm/pwm_tlc5955_stm32.c \

lib/pbio/drv/pwm/pwm_core.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
#include "pwm_lp50xx_stm32.h"
1717
#include "pwm_tlc5955_stm32.h"
1818
#include "pwm_ev3.h"
19+
#include "pwm_pico.h"
1920
#include "pwm.h"
2021

2122

@@ -28,6 +29,7 @@ void pbdrv_pwm_init(void) {
2829
pbdrv_pwm_stm32_tim_init(pbdrv_pwm_dev);
2930
pbdrv_pwm_test_init(pbdrv_pwm_dev);
3031
pbdrv_pwm_lp50xx_stm32_init(pbdrv_pwm_dev);
32+
pbdrv_pwm_pico_init(pbdrv_pwm_dev);
3133
pbdrv_pwm_tlc5955_stm32_init(pbdrv_pwm_dev);
3234
pbdrv_pwm_tiam1808_init(pbdrv_pwm_dev);
3335
}

lib/pbio/drv/pwm/pwm_pico.c

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
// SPDX-License-Identifier: MIT
2+
// Copyright (c) 2026 The Pybricks Authors
3+
4+
#include <pbdrv/config.h>
5+
6+
#if PBDRV_CONFIG_PWM_PICO
7+
8+
#include <pbdrv/pwm.h>
9+
#include <pbdrv/gpio.h>
10+
#include <pbio/error.h>
11+
12+
#include "../drv/pwm/pwm.h"
13+
#include "../drv/pwm/pwm_pico.h"
14+
#include "../../drv/led/led_pwm.h"
15+
16+
#include "hardware/gpio.h"
17+
#include "hardware/pwm.h"
18+
19+
static pbio_error_t pbdrv_pwm_pico_set_duty(pbdrv_pwm_dev_t *dev, uint32_t ch, uint32_t value) {
20+
// Blue not available.
21+
if (ch == PBDRV_LED_PWM_CHANNEL_INVALID) {
22+
return PBIO_SUCCESS;
23+
}
24+
25+
if (ch >= PBDRV_CONFIG_PWM_PICO_NUM_CHANNELS) {
26+
return PBIO_ERROR_INVALID_ARG;
27+
}
28+
29+
const pbdrv_pwm_pico_platform_data_t *pdata = dev->pdata;
30+
const pbdrv_pwm_pico_channel_t *channel = &pdata->channels[ch];
31+
32+
pwm_set_gpio_level(channel->gpio, value);
33+
34+
return PBIO_SUCCESS;
35+
}
36+
37+
static const pbdrv_pwm_driver_funcs_t pbdrv_pwm_pico_funcs = {
38+
.set_duty = pbdrv_pwm_pico_set_duty,
39+
};
40+
41+
void pbdrv_pwm_pico_init(pbdrv_pwm_dev_t *devs) {
42+
const pbdrv_pwm_pico_platform_data_t *pdata = &pbdrv_pwm_pico_platform_data;
43+
44+
devs[pdata->id].funcs = &pbdrv_pwm_pico_funcs;
45+
devs[pdata->id].pdata = pdata;
46+
47+
// Set GPIO alt modes for the PRU
48+
for (int i = 0; i < PBDRV_CONFIG_PWM_PICO_NUM_CHANNELS; i++) {
49+
const pbdrv_pwm_pico_channel_t *channel = &pdata->channels[i];
50+
51+
// Tell the LED pin that the PWM is in charge of its value.
52+
gpio_set_function(channel->gpio, GPIO_FUNC_PWM);
53+
// Figure out which slice we just connected to the LED pin
54+
uint slice_num = pwm_gpio_to_slice_num(channel->gpio);
55+
56+
// Get some sensible defaults for the slice configuration. By default,
57+
// the counter is allowed to wrap over its maximum range (0 to 2**16-1)
58+
pwm_config config = pwm_get_default_config();
59+
// Set divider, reduces counter clock to sysclock/this value
60+
pwm_config_set_clkdiv(&config, 4.f);
61+
// Load the configuration into our PWM slice, and set it running.
62+
pwm_init(slice_num, &config, true);
63+
}
64+
}
65+
66+
#endif // PBDRV_CONFIG_PWM_PICO

lib/pbio/drv/pwm/pwm_pico.h

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
// SPDX-License-Identifier: MIT
2+
// Copyright (c) 2026 The Pybricks Authors
3+
4+
// Hooks for unit picos.
5+
6+
#ifndef _INTERNAL_PBDRV_PWM_PICO_H_
7+
#define _INTERNAL_PBDRV_PWM_PICO_H_
8+
9+
#include <pbdrv/config.h>
10+
11+
#if PBDRV_CONFIG_PWM_PICO
12+
13+
#include <stdint.h>
14+
15+
#include <pbdrv/pwm.h>
16+
17+
/** Platform-specific device information. */
18+
19+
typedef struct {
20+
uint8_t gpio;
21+
} pbdrv_pwm_pico_channel_t;
22+
23+
typedef struct {
24+
uint8_t id;
25+
pbdrv_pwm_pico_channel_t channels[PBDRV_CONFIG_PWM_PICO_NUM_CHANNELS];
26+
} pbdrv_pwm_pico_platform_data_t;
27+
28+
// Defined in platform.c
29+
extern const pbdrv_pwm_pico_platform_data_t pbdrv_pwm_pico_platform_data;
30+
31+
void pbdrv_pwm_pico_init(pbdrv_pwm_dev_t *devs);
32+
33+
#else // PBDRV_CONFIG_PWM_PICO
34+
35+
#define pbdrv_pwm_pico_init(dev)
36+
37+
#endif // PBDRV_CONFIG_PWM_PICO
38+
39+
#endif // _INTERNAL_PBDRV_PWM_PICO_H_

lib/pbio/platform/build_hat/pbdrvconfig.h

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -20,14 +20,18 @@
2020
#define PBDRV_CONFIG_IOPORT_HAS_UART (1)
2121
// TODO: Implement IOPORT driver for Build HAT
2222

23-
#define PBDRV_CONFIG_LED (0)
24-
// TODO: Implement LED driver for Build HAT (should be similar to EV3)
23+
#define PBDRV_CONFIG_LED (1)
24+
#define PBDRV_CONFIG_LED_NUM_DEV (1)
25+
#define PBDRV_CONFIG_LED_PWM (1)
26+
#define PBDRV_CONFIG_LED_PWM_NUM_DEV (1)
2527

2628
#define PBDRV_CONFIG_MOTOR_DRIVER (0)
2729
// TODO: Implement motor driver for Build HAT
2830

29-
#define PBDRV_CONFIG_PWM (0)
30-
// TODO: Implement PWM driver for Build HAT
31+
#define PBDRV_CONFIG_PWM (1)
32+
#define PBDRV_CONFIG_PWM_NUM_DEV (1)
33+
#define PBDRV_CONFIG_PWM_PICO (1)
34+
#define PBDRV_CONFIG_PWM_PICO_NUM_CHANNELS (2)
3135

3236
#define PBDRV_CONFIG_RANDOM (0)
3337
// TODO: Implement RANDOM driver for Build HAT

lib/pbio/platform/build_hat/pbioconfig.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
#define PBIO_CONFIG_DCMOTOR_NUM_DEV (4)
77
#define PBIO_CONFIG_DRIVEBASE_SPIKE (0)
88
#define PBIO_CONFIG_IMU (0) // TODO
9-
#define PBIO_CONFIG_LIGHT (0) // TODO
9+
#define PBIO_CONFIG_LIGHT (1)
1010
#define PBIO_CONFIG_LOGGER (1)
1111
#define PBIO_CONFIG_MOTOR_PROCESS (1)
1212
#define PBIO_CONFIG_PORT (0) // TODO

lib/pbio/platform/build_hat/pbsysconfig.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818
#define PBSYS_CONFIG_STORAGE (0) // TODO ?
1919
#define PBSYS_CONFIG_STORAGE_NUM_SLOTS (1)
2020
#define PBSYS_CONFIG_STORAGE_USER_DATA_SIZE (128)
21-
#define PBSYS_CONFIG_STATUS_LIGHT (0) // TODO
21+
#define PBSYS_CONFIG_STATUS_LIGHT (1)
2222
#define PBSYS_CONFIG_STATUS_LIGHT_BATTERY (0)
2323
#define PBSYS_CONFIG_STATUS_LIGHT_BLUETOOTH (0)
2424
#define PBSYS_CONFIG_STATUS_LIGHT_STATE_ANIMATIONS (1)

lib/pbio/platform/build_hat/platform.c

Lines changed: 50 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,63 @@
11
// SPDX-License-Identifier: MIT
2-
// Copyright (c) 2025 The Pybricks Authors
2+
// Copyright (c) 2025-2026 The Pybricks Authors
33

44
#include <pbdrv/gpio.h>
55

6+
#include "../drv/led/led_pwm.h"
7+
#include "../drv/pwm/pwm_pico.h"
8+
69
#include "hardware/gpio.h"
710

11+
enum {
12+
PWM_DEV_0,
13+
};
14+
15+
enum {
16+
LED_DEV_0_STATUS,
17+
};
18+
819
const pbdrv_gpio_t pbdrv_ioport_platform_data_vcc_pin = {
920
.pin = 17,
1021
};
1122

23+
// LED
24+
25+
static const pbdrv_led_pwm_platform_color_t pbdrv_led_pwm_color = {
26+
.r_factor = 1000,
27+
.g_factor = 1000,
28+
.b_factor = 0,
29+
// Tuned with scale_factor=17 so pure red/green are as close as possible
30+
// to UINT16_MAX without exceeding it.
31+
.r_brightness = 83,
32+
.g_brightness = 83,
33+
.b_brightness = 0,
34+
};
35+
36+
const pbdrv_led_pwm_platform_data_t pbdrv_led_pwm_platform_data[PBDRV_CONFIG_LED_PWM_NUM_DEV] = {
37+
{
38+
.color = &pbdrv_led_pwm_color,
39+
.id = LED_DEV_0_STATUS,
40+
.r_id = PWM_DEV_0,
41+
.r_ch = 0,
42+
.g_id = PWM_DEV_0,
43+
.g_ch = 1,
44+
// Blue not available.
45+
.b_id = PWM_DEV_0,
46+
.b_ch = PBDRV_LED_PWM_CHANNEL_INVALID,
47+
.scale_factor = 17,
48+
},
49+
};
50+
51+
// PWM
52+
53+
const pbdrv_pwm_pico_platform_data_t pbdrv_pwm_pico_platform_data = {
54+
.id = PWM_DEV_0,
55+
.channels = {
56+
{ .gpio = 14 }, // LED0
57+
{ .gpio = 15 }, // LED1
58+
}
59+
};
60+
1261
int main(void) {
1362
extern void pbsys_main(void);
1463
pbsys_main();

pybricks/hubs/pb_type_buildhat.c

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
11
// SPDX-License-Identifier: MIT
2-
// Copyright (c) 2025 The Pybricks Authors
2+
// Copyright (c) 2025-2026 The Pybricks Authors
33

44
#include "py/mpconfig.h"
55

66
#if PYBRICKS_PY_HUBS && PYBRICKS_HUB_BUILDHAT
77

8+
#include <pbsys/light.h>
9+
810
#include <pybricks/common.h>
911
#include <pybricks/hubs.h>
1012

@@ -13,16 +15,19 @@
1315

1416
typedef struct _hubs_BuildHat_obj_t {
1517
mp_obj_base_t base;
18+
mp_obj_t light;
1619
mp_obj_t system;
1720
} hubs_BuildHat_obj_t;
1821

1922
static mp_obj_t hubs_BuildHat_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args) {
2023
hubs_BuildHat_obj_t *self = mp_obj_malloc(hubs_BuildHat_obj_t, type);
24+
self->light = common_ColorLight_internal_obj_new(pbsys_status_light_main);
2125
self->system = MP_OBJ_FROM_PTR(&pb_type_System);
2226
return MP_OBJ_FROM_PTR(self);
2327
}
2428

2529
static const pb_attr_dict_entry_t hubs_BuildHat_attr_dict[] = {
30+
PB_DEFINE_CONST_ATTR_RO(MP_QSTR_light, hubs_BuildHat_obj_t, light),
2631
PB_DEFINE_CONST_ATTR_RO(MP_QSTR_system, hubs_BuildHat_obj_t, system),
2732
PB_ATTR_DICT_SENTINEL
2833
};

0 commit comments

Comments
 (0)