Added support for pulses with large offsets that cross the wrap level

This commit is contained in:
ZodiusInfuser 2022-03-06 22:48:13 +00:00
parent c4e39fd426
commit 0838483d2b
2 changed files with 73 additions and 9 deletions

View File

@ -33,6 +33,7 @@ struct Sequence {
Sequence sequences[NUM_BUFFERS];
uint sequence_index = 0;
volatile uint32_t looping_mask[NUM_BUFFERS];
volatile uint read_index = 0;
volatile uint last_written_index = 0;
@ -53,6 +54,10 @@ void __isr pwm_dma_handler() {
if(last_written_index != read_index) {
read_index = last_written_index;
}
else {
// We're looping the same data so make the start mask match the end mask
sequences[read_index].data[0].mask = looping_mask[read_index];
}
uint32_t transitions = sequences[read_index].size * 2;
uint32_t* buffer = (uint32_t *)sequences[read_index].data;
@ -88,6 +93,7 @@ PWMCluster::PWMCluster(PIO pio, uint sm, uint pin_mask)
for(uint channel = 0; channel < NUM_BANK0_GPIOS; channel++) {
channel_levels[channel] = 0u;
channel_offsets[channel] = 0u;
channel_overruns[channel] = 0u;
}
}
@ -109,6 +115,7 @@ PWMCluster::PWMCluster(PIO pio, uint sm, uint pin_base, uint pin_count)
for(uint channel = 0; channel < NUM_BANK0_GPIOS; channel++) {
channel_levels[channel] = 0u;
channel_offsets[channel] = 0u;
channel_overruns[channel] = 0u;
}
}
@ -130,6 +137,7 @@ PWMCluster::PWMCluster(PIO pio, uint sm, std::initializer_list<uint8_t> pins)
for(uint channel = 0; channel < NUM_BANK0_GPIOS; channel++) {
channel_levels[channel] = 0u;
channel_offsets[channel] = 0u;
channel_overruns[channel] = 0u;
}
}
@ -256,6 +264,7 @@ bool PWMCluster::init() {
Sequence& seq = sequences[i];
seq = Sequence();
seq.data[0].delay = 10; // Need to set a delay otherwise a lockup occurs when first changing frequency
looping_mask[i] = 0x00;
}
// Manually call the handler once, to trigger the first transfer
@ -323,22 +332,48 @@ void PWMCluster::load_pwm() {
TransitionData transitions[64];
uint data_size = 0;
uint pin_states = channel_polarities;
// Go through each channel that we are assigned to
for(uint channel = 0; channel < NUM_BANK0_GPIOS; channel++) {
if(bit_in_mask(channel, pin_mask)) {
// Get the channel polarity, remembering that true means inverted
bool polarity = bit_in_mask(channel, channel_polarities);
// If the level is greater than zero, add a transition to high
if(channel_levels[channel] > 0) {
PWMCluster::sorted_insert(transitions, data_size, TransitionData(channel, channel_offsets[channel], !polarity));
//if((channel_offsets[channel] < wrap_level) && (channel_offsets[channel] + channel_levels[channel] >= wrap_level)) {
// PWMCluster::sorted_insert(transitions, data_size, TransitionData(channel, 0, !polarity)) // Adds an initial state for PWMs that have their end offset beyond the transition line
//}
uint channel_start = channel_offsets[channel];
uint channel_end = (channel_offsets[channel] + channel_levels[channel]) % wrap_level;
// Did the previous sequence overrun the wrap level?
if(channel_overruns[channel] > 0) {
// Set this channel's initial state to "high" (or "low" if inverted)
if(polarity)
pin_states &= ~(1u << channel);
else
pin_states |= (1u << channel);
// Check for a few edge cases when pulses change length across the wrap level
// Not entirely sure I understand which statements does what, but they seem to work
if((channel_end >= channel_start) || (channel_overruns[channel] > channel_end)) {
// Add a transition to "low" (or "high" if inverted) at the overrun level of the previous sequence
PWMCluster::sorted_insert(transitions, data_size, TransitionData(channel, channel_overruns[channel], polarity));
}
channel_overruns[channel] = 0u;
}
// If the level is less than the wrap, add a transition to low
if(channel_levels[channel] > 0 && channel_start < wrap_level) {
// Add a transition to "high" (or "low" if inverted) at the start level
PWMCluster::sorted_insert(transitions, data_size, TransitionData(channel, channel_start, !polarity));
// If the channel has overrun the wrap level, record by how much
if(channel_end < channel_start) {
channel_overruns[channel] = channel_end;
}
}
if(channel_levels[channel] < wrap_level) {
PWMCluster::sorted_insert(transitions, data_size, TransitionData(channel, channel_offsets[channel] + channel_levels[channel], polarity));
// Add a transition to "low" (or "high" if inverted) at the end level
PWMCluster::sorted_insert(transitions, data_size, TransitionData(channel, channel_end, polarity));
}
}
}
@ -371,7 +406,6 @@ void PWMCluster::load_pwm() {
seq.size = 0; // Reset the sequence, otherwise we end up appending and weird things happen
if(data_size > 0) {
uint pin_states = channel_polarities;
uint data_index = 0;
uint current_level = 0;
@ -388,6 +422,8 @@ void PWMCluster::load_pwm() {
pin_states |= (1u << transitions[data_index].channel);
else
pin_states &= ~(1u << transitions[data_index].channel);
//printf("L[%d] = %ld, P = %d\n", data_index, transitions[data_index].level, pin_states);
}
data_index++; // Move on to the next transition
@ -402,10 +438,34 @@ void PWMCluster::load_pwm() {
// Add the transition to the sequence
seq.data[seq.size].mask = pin_states;
seq.data[seq.size].delay = (next_level - current_level) - 1;
//printf("S = %ld, M = %ld, D = %ld\n", seq.size, seq.data[seq.size].mask, seq.data[seq.size].delay + 1);
seq.size++;
current_level = next_level;
}
// Now the sequence has been generated, calculate what the pin state should be between looping cycles
data_index = 0;
do {
// Is the level of this transition at the current level being checked?
if(transitions[data_index].level <= 0) {
// Yes, so add the transition state to the pin states mask, if it's not a dummy transition
if(!transitions[data_index].dummy) {
if(transitions[data_index].state)
pin_states |= (1u << transitions[data_index].channel);
else
pin_states &= ~(1u << transitions[data_index].channel);
}
data_index++; // Move on to the next transition
}
else {
break;
}
} while(data_index < data_size);
// Record the looping pin states
looping_mask[write_index] = pin_states;
}
else {
// There were no transitions (either because there was a zero wrap, or no channels because there was a zero wrap?),
@ -413,6 +473,8 @@ void PWMCluster::load_pwm() {
seq.data[seq.size].mask = 0u;
seq.data[seq.size].delay = wrap_level - 1;
seq.size++;
looping_mask[write_index] = 0x00;
}
// Update the last written index so that the next DMA interrupt picks up the new sequence

View File

@ -55,6 +55,8 @@ namespace pimoroni {
uint channel_polarities;
uint wrap_level;
uint channel_overruns[NUM_BANK0_GPIOS];
public:
static bool calculate_pwm_factors(float freq, uint32_t& top_out, uint16_t& div16_out);
private: