Skip to content

A couple of small improvements #236

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Jan 3, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
176 changes: 85 additions & 91 deletions src/current_sense/hardware_specific/rp2040/rp2040_mcu.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,11 @@ alignas(32) const uint32_t trigger_value = ADC_CS_START_ONCE_BITS; // start once
float _readADCVoltageInline(const int pinA, const void* cs_params) {
// not super-happy with this. Here we have to return 1 phase current at a time, when actually we want to
// return readings from the same ADC conversion run. The ADC on RP2040 is anyway in round robin mode :-(
// like this we have block interrupts 3x instead of just once, and of course have the chance of reading across
// like this we either have to block interrupts, or of course have the chance of reading across
// new ADC conversions, which probably won't improve the accuracy.

if (pinA>=26 && pinA<=29 && engine.channelsEnabled[pinA-26]) {
return engine.lastResults[pinA-26]*engine.adc_conv;
return engine.lastResults.raw[pinA-26]*engine.adc_conv;
}

// otherwise return NaN
Expand All @@ -47,43 +47,44 @@ void* _configureADCInline(const void *driver_params, const int pinA, const int p
};


void* _configureADCLowSide(const void *driver_params, const int pinA, const int pinB, const int pinC) {
if( _isset(pinA) )
engine.addPin(pinA);
if( _isset(pinB) )
engine.addPin(pinB);
if( _isset(pinC) )
engine.addPin(pinC);
engine.setPWMTrigger(((RP2040DriverParams*)driver_params)->slice[0]);
engine.init();
engine.start();
return &engine;
};
// not supported at the moment
// void* _configureADCLowSide(const void *driver_params, const int pinA, const int pinB, const int pinC) {
// if( _isset(pinA) )
// engine.addPin(pinA);
// if( _isset(pinB) )
// engine.addPin(pinB);
// if( _isset(pinC) )
// engine.addPin(pinC);
// engine.setPWMTrigger(((RP2040DriverParams*)driver_params)->slice[0]);
// engine.init();
// engine.start();
// return &engine;
// };


void _startADC3PinConversionLowSide() {
// what is this for?
};
// void _startADC3PinConversionLowSide() {
// // what is this for?
// };


float _readADCVoltageLowSide(const int pinA, const void* cs_params) {
// not super-happy with this. Here we have to return 1 phase current at a time, when actually we want to
// return readings from the same ADC conversion run. The ADC on RP2040 is anyway in round robin mode :-(
// like this we have block interrupts 3x instead of just once, and of course have the chance of reading across
// new ADC conversions, which probably won't improve the accuracy.
// float _readADCVoltageLowSide(const int pinA, const void* cs_params) {
// // not super-happy with this. Here we have to return 1 phase current at a time, when actually we want to
// // return readings from the same ADC conversion run. The ADC on RP2040 is anyway in round robin mode :-(
// // like this we have block interrupts 3x instead of just once, and of course have the chance of reading across
// // new ADC conversions, which probably won't improve the accuracy.

if (pinA>=26 && pinA<=29 && engine.channelsEnabled[pinA-26]) {
return engine.lastResults[pinA-26]*engine.adc_conv;
}
// if (pinA>=26 && pinA<=29 && engine.channelsEnabled[pinA-26]) {
// return engine.lastResults[pinA-26]*engine.adc_conv;
// }

// otherwise return NaN
return NAN;
};
// // otherwise return NaN
// return NAN;
// };


void _driverSyncLowSide(void* driver_params, void* cs_params) {
// nothing to do
};
// void _driverSyncLowSide(void* driver_params, void* cs_params) {
// // nothing to do
// };



Expand All @@ -93,22 +94,19 @@ void _adcConversionFinishedHandler() {
// conversion of all channels finished. copy results.
volatile uint8_t* from = engine.samples;
if (engine.channelsEnabled[0])
engine.lastResults[0] = (*from++);
engine.lastResults.raw[0] = (*from++);
if (engine.channelsEnabled[1])
engine.lastResults[1] = (*from++);
engine.lastResults.raw[1] = (*from++);
if (engine.channelsEnabled[2])
engine.lastResults[2] = (*from++);
engine.lastResults.raw[2] = (*from++);
if (engine.channelsEnabled[3])
engine.lastResults[3] = (*from++);
// TODO clear interrupt? dma_hw->ints0 = 1u << channel;
//irq_clear(DMA_IRQ_0);
//dma_channel_acknowledge_irq0(engine.copyDMAChannel);
// dma_start_channel_mask( (1u << engine.readDMAChannel) | (1u << engine.copyDMAChannel) );
engine.lastResults.raw[3] = (*from++);
//dma_channel_acknowledge_irq0(engine.readDMAChannel);
dma_hw->ints0 = 1u << engine.readDMAChannel;
//dma_start_channel_mask( (1u << engine.readDMAChannel) );
dma_channel_set_write_addr(engine.readDMAChannel, engine.samples, true);
if (engine.triggerPWMSlice>=0)
dma_channel_set_trans_count(engine.triggerDMAChannel, 1, true);
// if (engine.triggerPWMSlice>=0)
// dma_channel_set_trans_count(engine.triggerDMAChannel, 1, true);
rp2040_intcount++;
};

Expand All @@ -127,7 +125,7 @@ RP2040ADCEngine::RP2040ADCEngine() {



void RP2040ADCEngine::addPin(int pin){
void RP2040ADCEngine::addPin(int pin) {
if (pin>=26 && pin<=29)
channelsEnabled[pin-26] = true;
else
Expand All @@ -136,14 +134,14 @@ void RP2040ADCEngine::addPin(int pin){



void RP2040ADCEngine::setPWMTrigger(uint slice){
triggerPWMSlice = slice;
};
// void RP2040ADCEngine::setPWMTrigger(uint slice){
// triggerPWMSlice = slice;
// };




bool RP2040ADCEngine::init(){
bool RP2040ADCEngine::init() {
if (initialized)
return true;

Expand Down Expand Up @@ -192,42 +190,26 @@ bool RP2040ADCEngine::init(){
dma_channel_set_irq0_enabled(readDMAChannel, true);
irq_add_shared_handler(DMA_IRQ_0, _adcConversionFinishedHandler, PICO_SHARED_IRQ_HANDLER_DEFAULT_ORDER_PRIORITY);

// copyDMAChannel = dma_claim_unused_channel(true);
// dma_channel_config cc2 = dma_channel_get_default_config(copyDMAChannel);
// channel_config_set_transfer_data_size(&cc2, DMA_SIZE_32);
// channel_config_set_read_increment(&cc2, false);
// channel_config_set_write_increment(&cc2, false);
// channel_config_set_chain_to(&cc2, readDMAChannel);
// channel_config_set_irq_quiet(&cc2, false);
// dma_channel_configure(copyDMAChannel,
// &cc2,
// nextResults, // dest
// samples, // source
// 1, // count
// false // defer start
// );
// dma_channel_set_irq0_enabled(copyDMAChannel, true);
// irq_add_shared_handler(DMA_IRQ_0, _adcConversionFinishedHandler, PICO_SHARED_IRQ_HANDLER_DEFAULT_ORDER_PRIORITY);
SIMPLEFOC_DEBUG("RP2040-CUR: DMA init");

if (triggerPWMSlice>=0) { // if we have a trigger
triggerDMAChannel = dma_claim_unused_channel(true);
dma_channel_config cc3 = dma_channel_get_default_config(triggerDMAChannel);
channel_config_set_transfer_data_size(&cc3, DMA_SIZE_32);
channel_config_set_read_increment(&cc3, false);
channel_config_set_write_increment(&cc3, false);
channel_config_set_irq_quiet(&cc3, true);
channel_config_set_dreq(&cc3, DREQ_PWM_WRAP0+triggerPWMSlice); //pwm_get_dreq(triggerPWMSlice));
pwm_set_irq_enabled(triggerPWMSlice, true);
dma_channel_configure(triggerDMAChannel,
&cc3,
hw_set_alias_untyped(&adc_hw->cs), // dest
&trigger_value, // source
1, // count
true // defer start
);
SIMPLEFOC_DEBUG("RP2040-CUR: PWM trigger init slice ", triggerPWMSlice);
}
// if (triggerPWMSlice>=0) { // if we have a trigger
// triggerDMAChannel = dma_claim_unused_channel(true);
// dma_channel_config cc3 = dma_channel_get_default_config(triggerDMAChannel);
// channel_config_set_transfer_data_size(&cc3, DMA_SIZE_32);
// channel_config_set_read_increment(&cc3, false);
// channel_config_set_write_increment(&cc3, false);
// channel_config_set_irq_quiet(&cc3, true);
// channel_config_set_dreq(&cc3, DREQ_PWM_WRAP0+triggerPWMSlice); //pwm_get_dreq(triggerPWMSlice));
// pwm_set_irq_enabled(triggerPWMSlice, true);
// dma_channel_configure(triggerDMAChannel,
// &cc3,
// hw_set_alias_untyped(&adc_hw->cs), // dest
// &trigger_value, // source
// 1, // count
// true // defer start
// );
// SIMPLEFOC_DEBUG("RP2040-CUR: PWM trigger init slice ", triggerPWMSlice);
// }

initialized = true;
return initialized;
Expand All @@ -236,33 +218,45 @@ bool RP2040ADCEngine::init(){



void RP2040ADCEngine::start(){
void RP2040ADCEngine::start() {
SIMPLEFOC_DEBUG("RP2040-CUR: ADC engine starting");
irq_set_enabled(DMA_IRQ_0, true);
dma_start_channel_mask( (1u << readDMAChannel) ); // | (1u << copyDMAChannel));
dma_start_channel_mask( (1u << readDMAChannel) );
for (int i=0;i<4;i++) {
if (channelsEnabled[i]) {
adc_select_input(i); // set input to first enabled channel
break;
}
}
if (triggerPWMSlice>=0) {
dma_start_channel_mask( (1u << triggerDMAChannel) );
//hw_set_bits(&adc_hw->cs, trigger_value);
}
else
adc_run(true);
// if (triggerPWMSlice>=0) {
// dma_start_channel_mask( (1u << triggerDMAChannel) );
// //hw_set_bits(&adc_hw->cs, trigger_value);
// }
// else
adc_run(true);
SIMPLEFOC_DEBUG("RP2040-CUR: ADC engine started");
};

void RP2040ADCEngine::stop(){



void RP2040ADCEngine::stop() {
adc_run(false);
dma_channel_abort(readDMAChannel);
if (triggerPWMSlice>=0)
dma_channel_abort(triggerDMAChannel);
// if (triggerPWMSlice>=0)
// dma_channel_abort(triggerDMAChannel);
adc_fifo_drain();
SIMPLEFOC_DEBUG("RP2040-CUR: ADC engine stopped");
};



ADCResults RP2040ADCEngine::getLastResults() {
ADCResults r;
r.value = lastResults.value;
return r;
};



#endif
47 changes: 26 additions & 21 deletions src/current_sense/hardware_specific/rp2040/rp2040_mcu.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,35 +17,29 @@
* To use the other ADC channels, use them via this engine. Use addPin() to add them to the conversion, and getLastResult()
* to retrieve their value at any time.
*
* For motor current sensing, the engine supports both inline sensing and low-side sensing.
* For motor current sensing, the engine supports inline sensing only.
*
* Inline sensing is supported by offering a user-selectable fixed ADC sampling rate, which can be set between 500kHz and 1Hz.
* After starting the engine it will continuously sample and provide new values at the configured rate.
*
* Low-side sensing is supported by configuring a trigger from the PWM signal. The trigger happens at the middle-point of the
* up/down counting PWM, which is the mid-point of the on-period.
* So in the case of low-side sensing, all ADC channels are converted at the rate of the PWM frequency.
* Low-side sensing is currently not supported.
*
* The SimpleFOC PWM driver for RP2040 syncs all the slices, so the PWM trigger is applied to the first used slice. For current
* sensing to work correctly, all PWM slices have to be set to the same PWM frequency.
* In theory, two motors could be sensed using 2 shunts on each motor. In practice, due to the slow conversion rate of the RP2040's
* ADC, this would mean 8us conversion time, which would have to fit in the low-side on-time even at low duty-cycles... It remains
* to be seen how well this can work, or if it works at all, but presumably the PWM frequency would have to be quite low.
*
* TODO we need the mid-point of the low-side, which is actually the beginning/end of the PWM cycle - hmmmm...
* In theory, two motors could be sensed using 2 shunts on each motor.
*
* Note that if using other ADC channels along with the motor current sensing, those channels will be subject to the same conversion schedule as the motor's ADC channels, i.e. convert at the same fixed rate in case
* of inline sensing, or based on the motor PWM in case of PWM-triggered low-side sensing.
* of inline sensing.
*
* Solution to trigger ADC conversion from PWM via DMA:
* use the PWM wrap as a DREQ to a DMA channel, and have the DMA channel write to the ADC's CS register to trigger an ADC sample.
* Unfortunately, I could not get this to work, so no low side sensing for the moment.
*
* Solution for ADC conversion:
* ADC converts all channels in round-robin mode, and writes to FIFO. FIFO is emptied by a DMA which triggers after N conversions,
* where N is the number of ADC channels used. So this DMA copies all the values from one round-robin conversion. This first DMA
* triggers a second DMA which does a 32bit copy of all converted values (up to 4 channels x 8bit) at once, and triggers an interrupt.
* The interrupt routine copies the values to the output buffer.
* where N is the number of ADC channels used. So this DMA copies all the values from one round-robin conversion.
*
*
* TODO think about whether the second DMA is needed
*/


Expand All @@ -54,32 +48,43 @@
#define SIMPLEFOC_RP2040_ADC_VDDA 3.3f
#endif


union ADCResults {
uint32_t value;
uint8_t raw[4];
struct {
uint8_t ch0;
uint8_t ch1;
uint8_t ch2;
uint8_t ch3;
};
};


class RP2040ADCEngine {

public:
RP2040ADCEngine();
void addPin(int pin);
void setPWMTrigger(uint slice);
//void setPWMTrigger(uint slice);

bool init();
void start();
void stop();

void getLastResult();

void handleADCUpdate();
ADCResults getLastResults(); // TODO find a better API and representation for this

int samples_per_second = 0; // leave at 0 to convert in tight loop
float adc_conv = (SIMPLEFOC_RP2040_ADC_VDDA / SIMPLEFOC_RP2040_ADC_RESOLUTION); // conversion from raw ADC to float

int triggerPWMSlice = -1;
//int triggerPWMSlice = -1;
bool initialized;
uint readDMAChannel;
//uint copyDMAChannel;
uint triggerDMAChannel;
//uint triggerDMAChannel;

bool channelsEnabled[4];
volatile uint8_t samples[4];
volatile uint8_t lastResults[4];
volatile ADCResults lastResults;
//alignas(32) volatile uint8_t nextResults[4];
};
2 changes: 1 addition & 1 deletion src/drivers/hardware_specific/atmega/atmega2560_mcu.cpp
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
#include "../../hardware_api.h"

#if defined(__AVR_ATmega2560__)
#if defined(__AVR_ATmega2560__) || defined(AVR_ATmega1280)

#define _PWM_FREQUENCY 32000
#define _PWM_FREQUENCY_MAX 32000
Expand Down
2 changes: 1 addition & 1 deletion src/drivers/hardware_specific/rp2040/rp2040_mcu.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@

#include "../../hardware_api.h"
#include "./rp2040_mcu.h"

#include "hardware/pwm.h"

#define _PWM_FREQUENCY 24000
#define _PWM_FREQUENCY_MAX 66000
Expand Down