Ever-so-slightly adjustable duty on SAMD51
This commit is contained in:
parent
ed2e701871
commit
93ea4fa10d
4 changed files with 74 additions and 0 deletions
|
|
@ -242,3 +242,7 @@ _PM_CUSTOM_BLAST If defined, instructs core code to not compile
|
|||
#if !defined(_PM_PORT_TYPE)
|
||||
#define _PM_PORT_TYPE uint32_t ///< PORT register size/type
|
||||
#endif
|
||||
|
||||
#if !defined(_PM_maxDuty)
|
||||
#define _PM_maxDuty 0 ///< Max duty cycle setting (where supported)
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -196,6 +196,59 @@ uint32_t _PM_timerStop(Protomatter_core *core) {
|
|||
return count;
|
||||
}
|
||||
|
||||
#define _PM_CUSTOM_BLAST // Disable blast_*() functions in core.c
|
||||
|
||||
#define _PM_maxDuty 2
|
||||
extern uint8_t _PM_duty;
|
||||
|
||||
// Concurrent PORT accesses incur a 1-cycle delay, so these NOPs keep the
|
||||
// unrolled part of the loop at a constant 17.24 MHz (@ 120 MHz F_CPU).
|
||||
// Could remove them and perhaps add a fourth duty configuration, but
|
||||
// the tradeoff is a slower loop at min & max duty settings.
|
||||
#define PEW \
|
||||
asm("nop"); \
|
||||
*toggle = *data++; \
|
||||
asm("nop"); \
|
||||
*ptr[0] = clock; \
|
||||
*ptr[1] = clock; \
|
||||
*ptr[2] = clock;
|
||||
|
||||
static void blast_byte(Protomatter_core *core, uint8_t *data) {
|
||||
// If here, it was established in begin() that the RGB data bits and
|
||||
// clock are all within the same byte of a PORT register, else we'd be
|
||||
// in the word- or long-blasting functions now. So we just need an
|
||||
// 8-bit pointer to the PORT.
|
||||
uint8_t *toggle = (uint8_t *)core->toggleReg + core->portOffset;
|
||||
uint16_t chunks = core->chainBits / 8;
|
||||
|
||||
// PORT has already been initialized with RGB data + clock bits
|
||||
// all LOW, so we don't need to initialize that state here.
|
||||
|
||||
uint8_t *ptr[_PM_maxDuty + 1], bucket, clock = core->clockMask;
|
||||
for (uint8_t i=0; i<=_PM_maxDuty; i++) {
|
||||
ptr[i] = (_PM_duty == (_PM_maxDuty - i)) ? toggle : &bucket;
|
||||
}
|
||||
|
||||
do {
|
||||
PEW PEW PEW PEW PEW PEW PEW PEW
|
||||
} while(--chunks);
|
||||
|
||||
// Want the PORT left with RGB data and clock LOW on function exit
|
||||
// (so it's easier to see on 'scope, and to prime it for the next call).
|
||||
// This is implicit in the no-toggle case (due to how the PEW macro
|
||||
// works), but toggle case requires explicitly clearing those bits.
|
||||
// rgbAndClockMask is an 8-bit value when toggling, hence offset here.
|
||||
*((volatile uint8_t *)core->clearReg + core->portOffset) =
|
||||
core->rgbAndClockMask;
|
||||
}
|
||||
|
||||
static void blast_word(Protomatter_core *core, uint16_t *data) {
|
||||
}
|
||||
|
||||
static void blast_long(Protomatter_core *core, uint32_t *data) {
|
||||
}
|
||||
|
||||
#if 0
|
||||
// See notes in core.c before the "blast" functions.
|
||||
// The NOP counts here were derived by monitoring on a fast logic analyzer,
|
||||
// aiming for 20 MHz clock at 50% duty cycle (for the unrolled parts of the
|
||||
|
|
@ -220,6 +273,7 @@ uint32_t _PM_timerStop(Protomatter_core *core) {
|
|||
#define _PM_clockHoldHigh asm("nop");
|
||||
#define _PM_clockHoldLow asm("nop; nop");
|
||||
#endif
|
||||
#endif // 0
|
||||
|
||||
#define _PM_minMinPeriod 160
|
||||
|
||||
|
|
|
|||
|
|
@ -922,6 +922,12 @@ static void _PM_resetFM6126A(Protomatter_core *core) {
|
|||
_PM_rgbState(core, 0); // Set all RGB low so port toggle can work
|
||||
}
|
||||
|
||||
uint8_t _PM_duty = 0;
|
||||
|
||||
void _PM_setDuty(uint8_t d) {
|
||||
_PM_duty = (d > _PM_maxDuty) ? _PM_maxDuty : d;
|
||||
}
|
||||
|
||||
#if defined(ARDUINO) || defined(CIRCUITPY)
|
||||
|
||||
// Arduino and CircuitPython happen to use the same internal canvas
|
||||
|
|
|
|||
10
src/core.h
10
src/core.h
|
|
@ -255,6 +255,16 @@ extern uint32_t _PM_timerGetCount(Protomatter_core *core);
|
|||
*/
|
||||
extern void _PM_swapbuffer_maybe(Protomatter_core *core);
|
||||
|
||||
/*!
|
||||
@brief Adjust duty cycle of HUB75 clock signal. This is not supported on
|
||||
all architectures.
|
||||
@param d Duty setting, 0 minimum. Increasing values generate higher clock
|
||||
duty cycles at the same frequency. Arbitrary granular units, max
|
||||
varies by architecture and CPU speed, if supported at all.
|
||||
e.g. SAMD51 @ 120 MHz supports 0 (~50% duty) through 2 (~75%).
|
||||
*/
|
||||
extern void _PM_setDuty(uint8_t d);
|
||||
|
||||
#if defined(ARDUINO) || defined(CIRCUITPY)
|
||||
|
||||
/*!
|
||||
|
|
|
|||
Loading…
Reference in a new issue