Clock divider calculation
Closed this issue · 1 comments
Hello,
i'm trying to connect to a ws2812e led strip with my pi_pico board.
I'm having a rather terrible level shifter on my setup which leads to messy signals, the level shifters struggles to push the voltage to 5v5 on the output side which leads to most of the pwm signal beeing closer to 3v3 for most of the high phases of the signal.
That's my problem really but just for the explanation.
The issue is i tested this setup with the ws2812 micropython library and had no issues with my led strip.
Once i switched to rust and your library, i started to notice issues with the signal and incorrect outputs on my led strips.
After diving into both code bases pio state machine setup i noticed a difference in calculating the clock divider value.
this is how the c implementation of the micropython code calculates the divider values.
// Frequency given in Hz, compute clkdiv from it.
uint64_t div = (uint64_t)clock_get_hz(clk_sys) * 256ULL / (uint64_t)args[ARG_freq].u_int;
if (!(div >= 1 * 256 && div <= 65536 * 256)) {
mp_raise_ValueError(MP_ERROR_TEXT("freq out of range"));
}
clkdiv_int = div / 256;
clkdiv_frac = div & 0xff;
and this is your solution.
// Configure the PIO state machine.
let bit_freq = FREQ * CYCLES_PER_BIT;
let mut int = clock_freq / bit_freq;
let rem = clock_freq - (int * bit_freq);
let frac = (rem * 256) / bit_freq;
assert!(
(1..=65536).contains(&int) && (int != 65536 || frac == 0),
"(System Clock / {}) must be within [1.0, 65536.0].",
bit_freq.to_kHz()
);
// 65536.0 is represented as 0 in the pio's clock divider
if int == 65536 {
int = 0;
}
// Using lossy conversion because range have been checked
let int: u16 = int as u16;
let frac: u8 = frac as u8;
the problem with your solution might be that dividing the clock freq and after that multiplying it by 256 is that the calculation increases the interger maths error.
By expanding the calculation to 64 bits as in the python base solution and then multiplying it by 256 and dividing it by 256 again after the divisions is that the incluence of calcualtion errors goes down.
After i have to confess rather clumsily trying to port the python code to rust i came up with this solution and experience no further errors in my led output.
let bit_freq = FREQ * CYCLES_PER_BIT;
let div: u64 = ((clock_freq.to_Hz() as u64) * 256u64) / (bit_freq.to_Hz() as u64);
assert!(div >= 1 * 256 && div <= 65536 * 256,
"(System Clock / {}) must be within [1.0, 65536.0].",
bit_freq.to_kHz()
);
// Using lossy conversion because range have been checked
let int: u16 = (div / 256u64) as u16;
let frac: u8 = div as u8;
Have to test this further as my setups still isn't working. Only with an active debugger connection...