diff --git a/bricks/_common/arm_none_eabi.mk b/bricks/_common/arm_none_eabi.mk index 1e730adaf..b22d6cce1 100644 --- a/bricks/_common/arm_none_eabi.mk +++ b/bricks/_common/arm_none_eabi.mk @@ -564,6 +564,7 @@ OBJ += $(addprefix $(BUILD)/, $(TI_AM1808_USB_SRC_C:.c=.o)) OBJ += $(addprefix $(BUILD)/, $(EV3_SRC_S:.S=.o)) $(addprefix $(BUILD)/, $(EV3_SRC_S:.S=.o)): CFLAGS += -D__ASSEMBLY__ OBJ += $(BUILD)/pru_suart.bin.o +OBJ += $(BUILD)/pru_ledpwm.bin.o endif # List of sources for qstr extraction @@ -637,6 +638,11 @@ $(BUILD)/u-boot.bin: $(Q)mkdir -p $(dir $@) $(Q)curl -sL -o $@ https://github.com/pybricks/u-boot/releases/download/pybricks/v2.0.1/u-boot.bin $(Q)echo "86ddad84f64d8aea85b4315fc1414bdec0bb0d46c92dbd3db45ed599e3a994cb $@" | sha256sum -c --strict +$(BUILD)/pru_ledpwm.bin: + $(ECHO) "Downloading pru_ledpwm.bin" + $(Q)mkdir -p $(dir $@) + $(Q)curl -sL -o $@ https://github.com/pybricks/pybricks-pru/releases/download/v0.0.1/pru_ledpwm.bin + $(Q)echo "c0138addb8ebb3d0f531499b6f45ccc71f524afbb6ce55ca3ab462a001ec28d2 $@" | sha256sum -c --strict MAKE_BOOTABLE_IMAGE = $(PBTOP)/bricks/ev3/make_bootable_image.py @@ -668,6 +674,9 @@ $(BUILD)/firmware.zip: $(ZIP_FILES) $(BUILD)/pru_suart.bin.o: $(PBTOP)/lib/pbio/drv/uart/uart_ev3_pru_lib/pru_suart.bin $(Q)$(OBJCOPY) -I binary -O elf32-littlearm -B arm \ --rename-section .data=.pru0,alloc,load,readonly,data,contents $^ $@ +$(BUILD)/pru_ledpwm.bin.o: $(BUILD)/pru_ledpwm.bin + $(Q)$(OBJCOPY) -I binary -O elf32-littlearm -B arm \ + --rename-section .data=.pru1,alloc,load,readonly,data,contents $^ $@ # firmware in DFU format $(BUILD)/%.dfu: $(BUILD)/%-base.bin diff --git a/lib/pbio/drv/led/led_pwm.h b/lib/pbio/drv/led/led_pwm.h index 8b93bf567..69839bc28 100644 --- a/lib/pbio/drv/led/led_pwm.h +++ b/lib/pbio/drv/led/led_pwm.h @@ -52,6 +52,8 @@ typedef struct { uint8_t scale_factor; } pbdrv_led_pwm_platform_data_t; +#define PBDRV_LED_PWM_CHANNEL_INVALID 0xff + // defined in platform/*/platform.c extern const pbdrv_led_pwm_platform_data_t pbdrv_led_pwm_platform_data[PBDRV_CONFIG_LED_PWM_NUM_DEV]; diff --git a/lib/pbio/drv/pwm/pwm_ev3.c b/lib/pbio/drv/pwm/pwm_ev3.c index a5eee10c5..494697d55 100644 --- a/lib/pbio/drv/pwm/pwm_ev3.c +++ b/lib/pbio/drv/pwm/pwm_ev3.c @@ -6,29 +6,37 @@ #if PBDRV_CONFIG_PWM_TIAM1808 #include +#include #include #include #include +#include +#include +#include +#include +#include +#include + #include "../drv/pwm/pwm.h" +#include "../../drv/led/led_pwm.h" #include "../../drv/pwm/pwm_ev3.h" #include "../../drv/gpio/gpio_ev3.h" +// This needs to match the interface expected by the PRU firmware +typedef struct shared_ram { + uint8_t pwms[PBDRV_PWM_EV3_NUM_CHANNELS]; +} shared_ram; +static volatile shared_ram pru1_shared_ram __attribute__((section(".shared1"))); + static pbio_error_t pbdrv_pwm_tiam1808_set_duty(pbdrv_pwm_dev_t *dev, uint32_t ch, uint32_t value) { // Blue not available. - if (ch == 2) { + if (ch == PBDRV_LED_PWM_CHANNEL_INVALID) { return PBIO_SUCCESS; } - pbdrv_pwm_tiam1808_platform_data_t *priv = dev->priv; - - // TODO: implement PWM. Just use GPIO for now. - pbdrv_gpio_t *gpio = (ch == 0) ? &priv->gpio_red : &priv->gpio_green; - if (value) { - pbdrv_gpio_out_high(gpio); - } else { - pbdrv_gpio_out_low(gpio); - } + + pru1_shared_ram.pwms[ch] = value; return PBIO_SUCCESS; } @@ -36,13 +44,37 @@ static const pbdrv_pwm_driver_funcs_t pbdrv_pwm_tiam1808_funcs = { .set_duty = pbdrv_pwm_tiam1808_set_duty, }; +extern char _pru1_start; +extern char _pru1_end; + +#define PINMUX_ALT_PRU1 4 + void pbdrv_pwm_tiam1808_init(pbdrv_pwm_dev_t *devs) { - for (int i = 0; i < PBDRV_CONFIG_PWM_TIAM1808_NUM_DEV; i++) { - devs[i].funcs = &pbdrv_pwm_tiam1808_funcs; - devs[i].priv = (pbdrv_pwm_tiam1808_platform_data_t *)&pbdrv_pwm_tiam1808_platform_data[i]; + // Enable Timer0 "34" half to count up to 256 * 256 + // This is used by the PRU to time the PWM + TimerPreScalarCount34Set(SOC_TMR_0_REGS, 0); + TimerPeriodSet(SOC_TMR_0_REGS, TMR_TIMER34, 256 * 256 - 1); + TimerEnable(SOC_TMR_0_REGS, TMR_TIMER34, TMR_ENABLE_CONT); + + // Clear shared command memory + memset((void *)&pru1_shared_ram, 0, sizeof(shared_ram)); + + // Enable PRU1 and load its firmware + PSCModuleControl(SOC_PSC_0_REGS, HW_PSC_PRU, PSC_POWERDOMAIN_ALWAYS_ON, PSC_MDCTL_NEXT_ENABLE); + PRUSSDRVPruDisable(1); + PRUSSDRVPruReset(1); + unsigned int *fw_start = (unsigned int *)&_pru1_start; + uint32_t fw_sz = &_pru1_end - &_pru1_start; + PRUSSDRVPruWriteMemory(PRUSS0_PRU1_IRAM, 0, fw_start, fw_sz); + // Set constant table C30 to point to shared memory + PRUSSDRVPruSetCTable(1, 30, (((uint32_t)&pru1_shared_ram) >> 8) & 0xffff); + PRUSSDRVPruEnable(1); + + devs[0].funcs = &pbdrv_pwm_tiam1808_funcs; - pbdrv_gpio_out_low(&pbdrv_pwm_tiam1808_platform_data[i].gpio_red); - pbdrv_gpio_out_low(&pbdrv_pwm_tiam1808_platform_data[i].gpio_green); + // Set GPIO alt modes for the PRU + for (int j = 0; j < PBDRV_PWM_EV3_NUM_CHANNELS; j++) { + pbdrv_gpio_alt(&pbdrv_pwm_tiam1808_platform_data.gpios[j], PINMUX_ALT_PRU1); } } diff --git a/lib/pbio/drv/pwm/pwm_ev3.h b/lib/pbio/drv/pwm/pwm_ev3.h index 4e6e514a0..5a9550e32 100644 --- a/lib/pbio/drv/pwm/pwm_ev3.h +++ b/lib/pbio/drv/pwm/pwm_ev3.h @@ -13,16 +13,16 @@ #include #include +#define PBDRV_PWM_EV3_NUM_CHANNELS 4 + /** Platform-specific device information. */ typedef struct { uint8_t id; - pbdrv_gpio_t gpio_red; - pbdrv_gpio_t gpio_green; + pbdrv_gpio_t gpios[PBDRV_PWM_EV3_NUM_CHANNELS]; } pbdrv_pwm_tiam1808_platform_data_t; // Defined in platform.c -extern const pbdrv_pwm_tiam1808_platform_data_t - pbdrv_pwm_tiam1808_platform_data[PBDRV_CONFIG_PWM_TIAM1808_NUM_DEV]; +extern const pbdrv_pwm_tiam1808_platform_data_t pbdrv_pwm_tiam1808_platform_data; void pbdrv_pwm_tiam1808_init(pbdrv_pwm_dev_t *devs); diff --git a/lib/pbio/platform/ev3/pbdrvconfig.h b/lib/pbio/platform/ev3/pbdrvconfig.h index 9ef46f313..89cc99731 100644 --- a/lib/pbio/platform/ev3/pbdrvconfig.h +++ b/lib/pbio/platform/ev3/pbdrvconfig.h @@ -66,7 +66,6 @@ #define PBDRV_CONFIG_PWM (1) #define PBDRV_CONFIG_PWM_NUM_DEV (2) #define PBDRV_CONFIG_PWM_TIAM1808 (1) -#define PBDRV_CONFIG_PWM_TIAM1808_NUM_DEV (2) #define PBDRV_CONFIG_RESET (1) #define PBDRV_CONFIG_RESET_EV3 (1) diff --git a/lib/pbio/platform/ev3/platform.c b/lib/pbio/platform/ev3/platform.c index d5d53659f..581b4d41e 100644 --- a/lib/pbio/platform/ev3/platform.c +++ b/lib/pbio/platform/ev3/platform.c @@ -72,8 +72,7 @@ enum { }; enum { - PWM_DEV_0_TODO, - PWM_DEV_1_TODO, + PWM_DEV_0, }; // LED @@ -87,55 +86,53 @@ const pbdrv_led_dual_platform_data_t pbdrv_led_dual_platform_data[PBDRV_CONFIG_L }; static const pbdrv_led_pwm_platform_color_t pbdrv_led_pwm_color = { - // TODO: PWM not yet implemented, so these values not used. - .r_factor = 1000, - .g_factor = 170, - .b_factor = 200, - .r_brightness = 174, - .g_brightness = 1590, - .b_brightness = 327, + // The red LED is much stronger than the green one, so leave green at 1000 + // and adjust red until yellow looks good. + .r_factor = 150, + .g_factor = 1000, + .b_factor = 0, + // 1250 allows max brightness for an 8-bit PWM size (with scale_factor = 1). + // Increase to limit max brightness. Going lower will cause overflow. + .r_brightness = 1250, + .g_brightness = 1250, + .b_brightness = 0, }; const pbdrv_led_pwm_platform_data_t pbdrv_led_pwm_platform_data[PBDRV_CONFIG_LED_PWM_NUM_DEV] = { { .color = &pbdrv_led_pwm_color, .id = LED_DEV_1_STATUS_LEFT, - .r_id = PWM_DEV_0_TODO, + .r_id = PWM_DEV_0, .r_ch = 0, - .g_id = PWM_DEV_0_TODO, + .g_id = PWM_DEV_0, .g_ch = 1, // Blue not available. - .b_id = PWM_DEV_0_TODO, - .b_ch = 2, - // TODO: PWM not yet implemented, so these values not used. - .scale_factor = 35, + .b_id = PWM_DEV_0, + .b_ch = PBDRV_LED_PWM_CHANNEL_INVALID, + .scale_factor = 1, }, { .color = &pbdrv_led_pwm_color, .id = LED_DEV_2_STATUS_RIGHT, - .r_id = PWM_DEV_1_TODO, - .r_ch = 0, - .g_id = PWM_DEV_1_TODO, - .g_ch = 1, + .r_id = PWM_DEV_0, + .r_ch = 2, + .g_id = PWM_DEV_0, + .g_ch = 3, // Blue not available. - .b_id = PWM_DEV_1_TODO, - .b_ch = 2, - // TODO: PWM not yet implemented, so these values not used. - .scale_factor = 35, + .b_id = PWM_DEV_0, + .b_ch = PBDRV_LED_PWM_CHANNEL_INVALID, + .scale_factor = 1, }, }; -const pbdrv_pwm_tiam1808_platform_data_t - pbdrv_pwm_tiam1808_platform_data[PBDRV_CONFIG_PWM_TIAM1808_NUM_DEV] = { - { - .id = PWM_DEV_0_TODO, - .gpio_red = PBDRV_GPIO_EV3_PIN(13, 11, 8, 6, 13), - .gpio_green = PBDRV_GPIO_EV3_PIN(14, 3, 0, 6, 7), - }, - { - .id = PWM_DEV_1_TODO, - .gpio_red = PBDRV_GPIO_EV3_PIN(13, 15, 12, 6, 12), - .gpio_green = PBDRV_GPIO_EV3_PIN(13, 7, 4, 6, 14), +const pbdrv_pwm_tiam1808_platform_data_t pbdrv_pwm_tiam1808_platform_data = +{ + .id = PWM_DEV_0, + .gpios = { + PBDRV_GPIO_EV3_PIN(13, 11, 8, 6, 13), + PBDRV_GPIO_EV3_PIN(14, 3, 0, 6, 7), + PBDRV_GPIO_EV3_PIN(13, 15, 12, 6, 12), + PBDRV_GPIO_EV3_PIN(13, 7, 4, 6, 14), }, }; diff --git a/lib/pbio/platform/ev3/platform.ld b/lib/pbio/platform/ev3/platform.ld index 5e4989172..463e2dd54 100644 --- a/lib/pbio/platform/ev3/platform.ld +++ b/lib/pbio/platform/ev3/platform.ld @@ -5,7 +5,8 @@ ENTRY(Entry) MEMORY { - SRAM (rwx) : ORIGIN = 0x80000000, LENGTH = 128K + SRAM_PRU0 (rw) : ORIGIN = 0x80000000, LENGTH = 64K + SRAM_PRU1 (rw) : ORIGIN = 0x80010000, LENGTH = 64K DDR_unused (rwx) : ORIGIN = 0xC0000000, LENGTH = 0x8000 DDR (rwx) : ORIGIN = 0xC0008000, LENGTH = (64M - 0x8000) ARM_LRAM (rwx) : ORIGIN = 0xFFFF0000, LENGTH = 8K @@ -38,6 +39,10 @@ SECTIONS KEEP(*(.pru0)); . = ALIGN(4); _pru0_end = .; + _pru1_start = .; + KEEP(*(.pru1)); + . = ALIGN(4); + _pru1_end = .; } >DDR .data : @@ -79,4 +84,14 @@ SECTIONS . = . + _minimal_stack_size; /* will cause linker error if there is not enough space for stack. */ . = ALIGN(4); } > DDR + + /* Shared on-chip SRAM */ + .shared0 (NOLOAD) : + { + *(.shared0) + } > SRAM_PRU0 + .shared1 (NOLOAD) : + { + *(.shared1) + } > SRAM_PRU1 } diff --git a/lib/tiam1808/drivers/pruss.c b/lib/tiam1808/drivers/pruss.c index be0ed6333..e565bb316 100644 --- a/lib/tiam1808/drivers/pruss.c +++ b/lib/tiam1808/drivers/pruss.c @@ -332,6 +332,53 @@ void PRUSSDRVIntcSetHmr(unsigned short channel, | (((host) & 0xF) << (((channel) & 0x3) << 3)); } +/** + * \brief Set PRU constant table entries + * + * \param pruNum PRU instance number[0 or 1]. + * \param ctable_idx Index into constants table + * \param value Value to set + * + * \return 0 in case of success, -1 otherwise. + */ +int PRUSSDRVPruSetCTable(unsigned int pruNum, + unsigned int ctable_idx, + unsigned int value) +{ + unsigned int regAddr; + + if(pruNum == 0) + regAddr = PRU0CONTROL_PHYS_BASE; + else if(pruNum == 1) + regAddr = PRU1CONTROL_PHYS_BASE; + else + return -1; + + switch (ctable_idx) { + case 24: + HWREG(regAddr + PRU_CONTABBLKIDX0) = (HWREG(regAddr + PRU_CONTABBLKIDX0) & ~0xf) | (value & 0xf); + break; + case 25: + HWREG(regAddr + PRU_CONTABBLKIDX0) = (HWREG(regAddr + PRU_CONTABBLKIDX0) & ~0xf0000) | ((value & 0xf) << 16); + break; + case 28: + HWREG(regAddr + PRU_CONTABPROPTR0) = (HWREG(regAddr + PRU_CONTABPROPTR0) & ~0xffff) | (value & 0xffff); + break; + case 29: + HWREG(regAddr + PRU_CONTABPROPTR0) = (HWREG(regAddr + PRU_CONTABPROPTR0) & ~0xffff0000) | ((value & 0xffff) << 16); + break; + case 30: + HWREG(regAddr + PRU_CONTABPROPTR1) = (HWREG(regAddr + PRU_CONTABPROPTR1) & ~0xffff) | (value & 0xffff); + break; + case 31: + HWREG(regAddr + PRU_CONTABPROPTR1) = (HWREG(regAddr + PRU_CONTABPROPTR1) & ~0xffff0000) | ((value & 0xffff) << 16); + break; + default: + return -1; + } + return 0; +} + void ICSS_Init(void) { /* ICSS PRCM Enable */ diff --git a/lib/tiam1808/tiam1808/pruss.h b/lib/tiam1808/tiam1808/pruss.h index f328d1faf..2964077eb 100644 --- a/lib/tiam1808/tiam1808/pruss.h +++ b/lib/tiam1808/tiam1808/pruss.h @@ -91,6 +91,13 @@ extern "C" { #define PRU1IRAM_PHYS_BASE 0x01C3C000 #endif +/* + * PRUSS control and status registers offsets + */ +#define PRU_CONTABBLKIDX0 0x20 +#define PRU_CONTABPROPTR0 0x28 +#define PRU_CONTABPROPTR1 0x2C + #define PRU_EVTOUT_0 0 #define PRU_EVTOUT_1 1 #define PRU_EVTOUT_2 2 @@ -258,6 +265,10 @@ void PRUSSDRVIntcSetCmr(unsigned short sysevt, void PRUSSDRVIntcSetHmr(unsigned short channel, unsigned short host); +int PRUSSDRVPruSetCTable(unsigned int pruNum, + unsigned int ctable_idx, + unsigned int value); + void ICSS_Init(void); // #ifdef AM33XX