You are here

ADC Vector Arrangement

4 posts / 0 new
Last post
javmarina
Offline
Last seen: 3 years 9 months ago
Joined: 2019-10-30 00:13
Posts: 5
ADC Vector Arrangement

I want to customize the VESC firmware for my custom hardware configuration. I have set up most things, but I have issues configuring the ADC vector. Specifically, I don't fully understand the hw_setup_adc_channels() function and the relationship between vector index, pins, ADC device and channels. How can I configure the vector and the pads according to my needs?

I have SENS1...SENS3, CURR1 and CURR2 (2 shunts), EXT for throttle, SIN and COS for encoder (maybe EXT2 and EXT3?), TEMP_MOTOR, TEMP_MOS and AN_IN (Vbus sense).

I also don't fully understanf injected channels and Vrefint.

Thanks in advance.

Teslafly
Offline
Last seen: 1 year 3 weeks ago
Joined: 2017-09-21 08:55
Posts: 17

so yeah, the adc channels in the firmware are a bit confusing beacuse they need to be set up to continuously sample via DMA.

I recently had to do this with my own hardware so i can probably give some advice.



since you are only using 2 current channels, I'll start with the 4.2 hw.

 

first in hw_xx.c (hw_40.c used)

you have within hw_init_gpio(void)

	// ADC Pins
	palSetPadMode(GPIOA, 0, PAL_MODE_INPUT_ANALOG);
	palSetPadMode(GPIOA, 1, PAL_MODE_INPUT_ANALOG);
	palSetPadMode(GPIOA, 2, PAL_MODE_INPUT_ANALOG);
	palSetPadMode(GPIOA, 3, PAL_MODE_INPUT_ANALOG);
	palSetPadMode(GPIOA, 4, PAL_MODE_INPUT_ANALOG);
	palSetPadMode(GPIOA, 5, PAL_MODE_INPUT_ANALOG);
	palSetPadMode(GPIOA, 6, PAL_MODE_INPUT_ANALOG);

	palSetPadMode(GPIOB, 0, PAL_MODE_INPUT_ANALOG);
	palSetPadMode(GPIOB, 1, PAL_MODE_INPUT_ANALOG);

	palSetPadMode(GPIOC, 0, PAL_MODE_INPUT_ANALOG);
	palSetPadMode(GPIOC, 1, PAL_MODE_INPUT_ANALOG);
	palSetPadMode(GPIOC, 2, PAL_MODE_INPUT_ANALOG);
	palSetPadMode(GPIOC, 3, PAL_MODE_INPUT_ANALOG);
	palSetPadMode(GPIOC, 5, PAL_MODE_INPUT_ANALOG);

You need to set up all your analog i/o to be input_analog.

If possible when assigning your analog pins, you want to put your phase voltages and current sense on inputs with adc 123, and everything else on the leftovers.

Though in the case of the 4.2hw, the current sensors are on pins with adc 12, which is fine because of the two shunts,

but if you have 3 you really need it to be on adc pins with access to all 3 samplers.

btw, 3 shunts are superior to two if possible and give a better signal quality. Phase shunts are better still because you can filter them as well as double sample in the high and low side.

 

then in in hw_xx.h, you have (from hw_40.h)

/*
 * ADC Vector
 *
 * 0:	IN0		SENS3
 * 1:	IN1		SENS2
 * 2:	IN2		SENS1
 * 3:	IN5		CURR2
 * 4:	IN6		CURR1
 * 5:	IN3		NC
 * 6:	VREFINT
 * 7:	IN11	NC
 * 8:	IN12	AN_IN
 * 9:	IN13	NC  (TEMP_MOSFET to make it compile)
 * 10:	IN15	ADC_EXT
 * 11:	IN10	TEMP_MOTOR
 */

#define HW_ADC_CHANNELS			12
#define HW_ADC_INJ_CHANNELS		2
#define HW_ADC_NBR_CONV			4

// ADC Indexes
#define ADC_IND_SENS1			2
#define ADC_IND_SENS2			1
#define ADC_IND_SENS3			0
#define ADC_IND_CURR1			4
#define ADC_IND_CURR2			3
#define ADC_IND_VIN_SENS		8
#define ADC_IND_EXT				10
#define ADC_IND_TEMP_MOS		9
#define ADC_IND_VREFINT			6
#define ADC_IND_TEMP_MOTOR		11

 

The ADC vector comment block is the way to keep your head on straight with all of this.

The first column is the index(vector) of the adc channel in the sampling array. When you use "ADC_Value[adc_ind]", this is the number you are referencing.

The index per input doesn't matter and can move. they just have to match up later.

The second column is the adc pin mux identity. use the schematic to find out which input the pin is.

For example, pin 17 (pa3, ADC123_IN2), is connected to mux input 2,

The third column is the input name. basically match up your schematic signal names to the input mux index and assign them to a incrementing vector number.

HW_ADC_CHANNELS is the number of inputs you have. it is your highest adc vector 11 + 1 (0 indexed), so 12 here.

This determines the size of the adc sampling array.

HW_ADC_INJ_CHANNELS is the number of current sensor channels you have. so 2 for you.
 
HW_ADC_NBR_CONV is the size of your "blocks" in the next part. In 1,2,3,4.  4 is the max.
 
The defines following this are just matching the names to the acd index. so all rows of the first and third columns of ADC vector comment block.
 
 
The last part is the most confusing.
void hw_setup_adc_channels(void) {
	// ADC1 regular channels
	ADC_RegularChannelConfig(ADC1, ADC_Channel_0, 1, ADC_SampleTime_15Cycles); // index 0
	ADC_RegularChannelConfig(ADC1, ADC_Channel_5, 2, ADC_SampleTime_15Cycles); // index 3
	ADC_RegularChannelConfig(ADC1, ADC_Channel_Vrefint, 3, ADC_SampleTime_15Cycles); // index 6
	ADC_RegularChannelConfig(ADC1, ADC_Channel_13, 4, ADC_SampleTime_15Cycles); // index 9

	// ADC2 regular channels
	ADC_RegularChannelConfig(ADC2, ADC_Channel_1, 1, ADC_SampleTime_15Cycles); // index 1
	ADC_RegularChannelConfig(ADC2, ADC_Channel_6, 2, ADC_SampleTime_15Cycles); // index 4
	ADC_RegularChannelConfig(ADC2, ADC_Channel_11, 3, ADC_SampleTime_15Cycles); // index 7
	ADC_RegularChannelConfig(ADC2, ADC_Channel_15, 4, ADC_SampleTime_15Cycles); // index 10

	// ADC3 regular channels
	ADC_RegularChannelConfig(ADC3, ADC_Channel_2, 1, ADC_SampleTime_15Cycles); // index 2
	ADC_RegularChannelConfig(ADC3, ADC_Channel_3, 2, ADC_SampleTime_15Cycles); // index 5
	ADC_RegularChannelConfig(ADC3, ADC_Channel_12, 3, ADC_SampleTime_15Cycles); // index 8
	ADC_RegularChannelConfig(ADC3, ADC_Channel_10, 4, ADC_SampleTime_15Cycles); // index 11

	// Injected channels
	ADC_InjectedChannelConfig(ADC1, ADC_Channel_6, 1, ADC_SampleTime_15Cycles); // index n/a
	ADC_InjectedChannelConfig(ADC2, ADC_Channel_5, 1, ADC_SampleTime_15Cycles); // index n/a
	ADC_InjectedChannelConfig(ADC1, ADC_Channel_5, 2, ADC_SampleTime_15Cycles); // index n/a
	ADC_InjectedChannelConfig(ADC2, ADC_Channel_6, 2, ADC_SampleTime_15Cycles); // index n/a
}

The regular channels are the regularly sampled channels. these are every single analog signal.

They must match up with an adc they are connected to (only a problem for ones connected to adc_12 pins)

Split your channels up into equal sized blocks, make sure that phase voltages and current inputs are split into different blocks.

aka, adc1 has phasev1 and curr1, adc2 had phasev2 and curr2, etc.

The blocks must also be of equal size. if you have a smaller block, add an additional current sensor sample at the end. you can see this in the vesc 6 config.

There is no order to the blocks, except that current sensor samples should be first and phase voltage samples second.

next is the structure of :

ADC_RegularChannelConfig(ADC3, ADC_Channel_10, 4, ADC_SampleTime_15Cycles);

  • ADC3 is the adc being used to sample that pin,
  • ADC_Channel_10 is the the mux IN channel of the pin. (row 2 of adc vector comment block,

    in this case it maps to pin 8, PC0(ADC123_IN10), which is PHASE_V_1_ADC)
  • 4 is the sample number on that adc converter. it should increase between successive

    channel configs for regular, and increase in blocks of simultaneous samples for injected.
    • to convert this number to the index defined in "adc indexes", index = ({sample num} -1)*3 + ({adc chan #} -1)
    • or refer to the index commments in the code block above. the pattern should be evident.
ADC_RegularChannelConfig(ADC converter, mux input channel, index of samples on that ADC, fastest adc sample time);

 

 

Injected channels are the current sensors. Since they must be read more frequently, sampling events are injected into the DMA stream.

you need to add all possible combinations of sampling simultaneously.

so in this case channels 5 and 6 are the current sense inputs, sample index 1 measures chan5 with adc2 and chan6 with adc1. index 2 does the inverse.

 

If possible, use a current limited supply to test your board, and make sure vesc tool values of phase voltage and current in

sampled data are sane if you spin the motor by hand or inject a voltage into one of the phases.

 

If you are still a bit confused, I recommend looking at the hw 4.2 (HW_40), hw 6, and axiom schematics and hardware configs to see how they differ.

 

If you come up with modifications for this code but are still having problems getting things to sample correctly, post these 3 blocks of code and your mcu schematic page to the thread and I'll try to find the problem.

 

javmarina
Offline
Last seen: 3 years 9 months ago
Joined: 2019-10-30 00:13
Posts: 5

Hi Teslafly!

Thank you so much for your detailed explanation. You have helped me a lot.

While I configure the hardware profile, I have some questions to ask. First of all, you said "There is no order to the blocks, except that current sensor samples should be first and phase voltage samples second." but I see that all hardware profiles have SENS (voltage) first and then CURR (current). Maybe you made a mistake there? The same applies to "if you have a smaller block, add an additional current sensor sample at the end. you can see this in the vesc 6 config", aren't they SENS inputs instead?

On the other hand, I have read the STM32F405xx/407xx datasheet and have seen "ADC123" and "ADC12" a lot, as well as in your explanation. I guess those pins are internally connected to the same channel (i.e. multiplexer input) on some ADC drivers simultaneously. Am I right? For example, PC0 has ADC123_IN10, so does that mean that this pin is connected to input 10 of all three multiplexers? Same for ADC12. There aren't pins solely connected to ADC1 or ADC2, right?

The last question is: even though Vrefint is defined and the ADC_Channel_Vrefint is configured in the code, it doesn't seem to be used anywhere, but why?

I'm trying to design the hardware based on the STM32F407 DISCOVERY development board. Not all pins can be used (particularly, PA0 is not available for ADC sampling). For that reason, I'm having a hard time assigning all pins to the analog inputs. I will let you know when more issues arise.

Thank you so much!

 

EDIT: 

I finally did it. This is the code now:

/*
 * ADC Vector
 *
 * 0:	IN0		SENS1		PA0
 * 1:	IN1		SENS2		PA1
 * 2:	IN2		SENS3		PA2
 * 3:	IN11	CURR1		PC1
 * 4:	IN12	CURR2		PC2
 * 5:	IN3		ADC_EXT		PA3		// Throttle (app_adc.c)
 * 6:	IN8		ADC_SIN		PB0		// Encoder SIN
 * 7:	IN9		ADC_COS		PB1		// Encoder COS
 * 8:	IN10	AN_IN		PC0		// Battery voltage
 * 9:	IN17	Vrefint		-
 * 10:	IN15	TEMP_MOS	PC5
 * 11:	IN13	TEMP_MOTOR	PC3
 */

#define HW_ADC_CHANNELS			12	// ADC array size
#define HW_ADC_INJ_CHANNELS		2	// Number of current sensors
#define HW_ADC_NBR_CONV			4	// Basically, HW_ADC_CHANNELS/3: number of blocks in hw_setup_adc_channels()

// ADC Indexes
// Must correspond to the above comment
#define ADC_IND_SENS1			0
#define ADC_IND_SENS2			1
#define ADC_IND_SENS3			2
#define ADC_IND_CURR1			3
#define ADC_IND_CURR2			4
#define ADC_IND_EXT				5
#define ADC_IND_SIN				6
#define ADC_IND_COS				7
#define ADC_IND_VIN_SENS		8
#define ADC_IND_VREFINT			9
#define ADC_IND_TEMP_MOS		10
#define ADC_IND_TEMP_MOTOR		11
void hw_setup_adc_channels(void) {
	// ADC1 regular channels
	ADC_RegularChannelConfig(ADC1, ADC_Channel_0, 1, ADC_SampleTime_15Cycles);		// 0	SENS1
	ADC_RegularChannelConfig(ADC1, ADC_Channel_11, 2, ADC_SampleTime_15Cycles);		// 3	CURR1
	ADC_RegularChannelConfig(ADC1, ADC_Channel_8, 3, ADC_SampleTime_15Cycles);		// 6	SIN
	ADC_RegularChannelConfig(ADC1, ADC_Channel_Vrefint, 4, ADC_SampleTime_15Cycles);// 9	Vrefint

	// ADC2 regular channels
	ADC_RegularChannelConfig(ADC2, ADC_Channel_1, 1, ADC_SampleTime_15Cycles);	// 1	SENS2
	ADC_RegularChannelConfig(ADC2, ADC_Channel_12, 2, ADC_SampleTime_15Cycles);	// 4	CURR2
	ADC_RegularChannelConfig(ADC2, ADC_Channel_9, 3, ADC_SampleTime_15Cycles);	// 7	COS
	ADC_RegularChannelConfig(ADC2, ADC_Channel_15, 4, ADC_SampleTime_15Cycles);	// 10	TEMP_MOS

	// ADC3 regular channels
	ADC_RegularChannelConfig(ADC3, ADC_Channel_2, 1, ADC_SampleTime_15Cycles);	// 2	SENS3
	ADC_RegularChannelConfig(ADC3, ADC_Channel_3, 2, ADC_SampleTime_15Cycles);	// 5	EXT
	ADC_RegularChannelConfig(ADC3, ADC_Channel_10, 3, ADC_SampleTime_15Cycles);	// 8	AN_IN
	ADC_RegularChannelConfig(ADC3, ADC_Channel_13, 4, ADC_SampleTime_15Cycles);	// 11	TEMP_MOTOR

	// Injected channels
	ADC_InjectedChannelConfig(ADC1, ADC_Channel_12, 1, ADC_SampleTime_15Cycles);
	ADC_InjectedChannelConfig(ADC2, ADC_Channel_11, 1, ADC_SampleTime_15Cycles);
	ADC_InjectedChannelConfig(ADC1, ADC_Channel_11, 2, ADC_SampleTime_15Cycles);
	ADC_InjectedChannelConfig(ADC2, ADC_Channel_12, 2, ADC_SampleTime_15Cycles);
}
	// ADC Pins
	palSetPadMode(GPIOA, 0, PAL_MODE_INPUT_ANALOG);
	palSetPadMode(GPIOA, 1, PAL_MODE_INPUT_ANALOG);
	palSetPadMode(GPIOA, 2, PAL_MODE_INPUT_ANALOG);
	palSetPadMode(GPIOA, 3, PAL_MODE_INPUT_ANALOG);

	palSetPadMode(GPIOB, 0, PAL_MODE_INPUT_ANALOG);
	palSetPadMode(GPIOB, 1, PAL_MODE_INPUT_ANALOG);

	palSetPadMode(GPIOC, 0, PAL_MODE_INPUT_ANALOG);
	palSetPadMode(GPIOC, 1, PAL_MODE_INPUT_ANALOG);
	palSetPadMode(GPIOC, 2, PAL_MODE_INPUT_ANALOG);
	palSetPadMode(GPIOC, 3, PAL_MODE_INPUT_ANALOG);
	palSetPadMode(GPIOC, 5, PAL_MODE_INPUT_ANALOG);

Can you check if there are issues?

CTSchorsch
Offline
Last seen: 4 months 2 weeks ago
VESC Free
Joined: 2018-07-13 09:55
Posts: 101

Hi Teslafly,

i have some similar problems here with my custom hardware. i think it is a adc problem too.

the realtime data in VESC Tool flickers. voltage jumps from 0 to 36V, duty cycle jumps from -200 to +200 and so on. i think that i have a misconfiguration of my ADC readout.

index 13 and 14 are dummy reads to fill the blocks in the adc configuration

/*
 * ADC Vector
 *
 * 0  (1):  IN0     SENS1
 * 1  (2):  IN1     SENS2
 * 2  (3):  IN2     SENS3
 * 3  (1):  IN10    CURR1
 * 4  (2):  IN11    CURR2
 * 5  (3):  IN12    CURR3
 * 6  (1):  IN5     ADC_EXT1
 * 7  (2):  IN6     ADC_EXT2
 * 8  (3):  IN3     TEMP_MOS
 * 9  (1):  IN14    TEMP_MOTOR
 * 10 (2):  IN15
 * 11 (3):  IN13    AN_IN
 * 12 (1):  Vrefint
 * 13 (2):  IN5     ADC_EXT1
 * 14 (3):  IN6     ADC_EXT2
 */

#define HW_ADC_CHANNELS         15
#define HW_ADC_INJ_CHANNELS     3
#define HW_ADC_NBR_CONV         5

// ADC Indexes
#define ADC_IND_SENS1           0
#define ADC_IND_SENS2           1
#define ADC_IND_SENS3           2
#define ADC_IND_CURR1           3
#define ADC_IND_CURR2           4
#define ADC_IND_CURR3           5
#define ADC_IND_VIN_SENS        11
#define ADC_IND_EXT             6
#define ADC_IND_EXT2            7
#define ADC_IND_EXT3            10
#define ADC_IND_TEMP_MOS        8
#define ADC_IND_TEMP_MOTOR      9
#define ADC_IND_VREFINT         12
    // ADC1 regular channels
    ADC_RegularChannelConfig(ADC1, ADC_Channel_0, 1, ADC_SampleTime_15Cycles);
    ADC_RegularChannelConfig(ADC1, ADC_Channel_10, 2, ADC_SampleTime_15Cycles);
    ADC_RegularChannelConfig(ADC1, ADC_Channel_5, 3, ADC_SampleTime_15Cycles);
    ADC_RegularChannelConfig(ADC1, ADC_Channel_14, 4, ADC_SampleTime_15Cycles);
    ADC_RegularChannelConfig(ADC1, ADC_Channel_Vrefint, 5, ADC_SampleTime_15Cycles);

    // ADC2 regular channels
    ADC_RegularChannelConfig(ADC2, ADC_Channel_1, 1, ADC_SampleTime_15Cycles);
    ADC_RegularChannelConfig(ADC2, ADC_Channel_11, 2, ADC_SampleTime_15Cycles);
    ADC_RegularChannelConfig(ADC2, ADC_Channel_6, 3, ADC_SampleTime_15Cycles);
    ADC_RegularChannelConfig(ADC2, ADC_Channel_15, 4, ADC_SampleTime_15Cycles);
    ADC_RegularChannelConfig(ADC2, ADC_Channel_6, 5, ADC_SampleTime_15Cycles);

    // ADC3 regular channels
    ADC_RegularChannelConfig(ADC3, ADC_Channel_2, 1, ADC_SampleTime_15Cycles);
    ADC_RegularChannelConfig(ADC3, ADC_Channel_12, 2, ADC_SampleTime_15Cycles);
    ADC_RegularChannelConfig(ADC3, ADC_Channel_3, 3, ADC_SampleTime_15Cycles);
    ADC_RegularChannelConfig(ADC3, ADC_Channel_13, 4, ADC_SampleTime_15Cycles);
    ADC_RegularChannelConfig(ADC3, ADC_Channel_5, 5, ADC_SampleTime_15Cycles);

    // Injected channels
    ADC_InjectedChannelConfig(ADC1, ADC_Channel_10, 1, ADC_SampleTime_15Cycles);
    ADC_InjectedChannelConfig(ADC2, ADC_Channel_11, 1, ADC_SampleTime_15Cycles);
    ADC_InjectedChannelConfig(ADC3, ADC_Channel_12, 1, ADC_SampleTime_15Cycles);
    ADC_InjectedChannelConfig(ADC1, ADC_Channel_10, 2, ADC_SampleTime_15Cycles);
    ADC_InjectedChannelConfig(ADC2, ADC_Channel_11, 2, ADC_SampleTime_15Cycles);
    ADC_InjectedChannelConfig(ADC3, ADC_Channel_12, 2, ADC_SampleTime_15Cycles);
    ADC_InjectedChannelConfig(ADC1, ADC_Channel_10, 3, ADC_SampleTime_15Cycles);
    ADC_InjectedChannelConfig(ADC2, ADC_Channel_11, 3, ADC_SampleTime_15Cycles);
    ADC_InjectedChannelConfig(ADC3, ADC_Channel_12, 3, ADC_SampleTime_15Cycles);

Is there anything strange in this config ?

thank in advance

Georg