Skip to content

Commit 7d3c011

Browse files
committed
wrapped samd21 adc/dma implementation in an ugly class
1 parent db7d865 commit 7d3c011

File tree

5 files changed

+168
-117
lines changed

5 files changed

+168
-117
lines changed

src/SimpleFOC.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,7 @@ void loop() {
109109
#include "drivers/StepperDriver4PWM.h"
110110
#include "drivers/StepperDriver2PWM.h"
111111
#include "current_sense/InlineCurrentSense.h"
112+
#include "current_sense/LowSideCurrentSense.h"
112113
#include "communication/Commander.h"
113114
#include "communication/StepDirListener.h"
114115

src/current_sense/LowSideCurrentSense.cpp

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,9 @@
55
// - phA - A phase adc pin
66
// - phB - B phase adc pin
77
// - phC - C phase adc pin (optional)
8-
LowSideCurrentSense::LowSideCurrentSense(float _shunt_resistor, float _gain, int _pinA, int _pinB, int _pinC){
8+
LowSideCurrentSense::LowSideCurrentSense(float _shunt_resistor, float _gain, int _pinA, int _pinB, int _pinC):
9+
adc(_pinA, _pinB, _pinC)
10+
{
911
pinA = _pinA;
1012
pinB = _pinB;
1113
pinC = _pinC;
@@ -21,9 +23,7 @@ LowSideCurrentSense::LowSideCurrentSense(float _shunt_resistor, float _gain, int
2123

2224
// Inline sensor init function
2325
void LowSideCurrentSense::init(){
24-
// configure ADC variables
25-
_configure3PinsDMA(pinA,pinB,pinC);
26-
_start3PinsDMA(); //start next acuisition
26+
adc.init();
2727
// calibrate zero offsets
2828
// calibrateOffsets();
2929
}
@@ -54,8 +54,9 @@ PhaseCurrent_s LowSideCurrentSense::getPhaseCurrents(){
5454
// current.a = (_readADCVoltage(pinA) - offset_ia)*gain_a;// amps
5555
// current.b = (_readADCVoltage(pinB) - offset_ib)*gain_b;// amps
5656
// current.c = (!_isset(pinC)) ? 0 : (_readADCVoltage(pinC) - offset_ic)*gain_c; // amps
57-
_read3PinsDMA(pinA, pinB, pinC, current.a, current.b, current.c);
58-
_start3PinsDMA();
57+
58+
adc._read3PinsDMA(pinA, pinB, pinC, current.a, current.b, current.c);
59+
adc._start3PinsDMA();
5960

6061
return current;
6162
}

src/current_sense/LowSideCurrentSense.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
#include "../common/time_utils.h"
77
#include "../common/base_classes/CurrentSense.h"
88
#include "hardware_api.h"
9+
#include "hardware_specific/samd21_mcu.h"
910

1011
class LowSideCurrentSense: public CurrentSense{
1112
public:
@@ -52,6 +53,8 @@ class LowSideCurrentSense: public CurrentSense{
5253
double offset_ib; //!< zero current B voltage value (center of the adc reading)
5354
double offset_ic; //!< zero current C voltage value (center of the adc reading)
5455

56+
SAMDCurrentSensceADC adc;
57+
5558
};
5659

5760
#endif
Lines changed: 97 additions & 111 deletions
Original file line numberDiff line numberDiff line change
@@ -1,35 +1,57 @@
11

2-
#include "../hardware_api.h"
2+
#include "samd21_mcu.h"
33

4-
// this code was pulled from Paul Gould's git https://github.com/gouldpa/FOC-Arduino-Brushless
5-
static uint32_t ADC_OneBeforeFirstPin; // hack to discard first noisy readout
6-
static uint32_t ADC_FirstPin; // PA04
7-
static uint32_t ADC_LastPin; // PA06
8-
static uint32_t BufferSize = 0;
94

10-
uint16_t adcBuffer[20];
5+
void adc_stop_with_DMA(void);
6+
void adc_start_with_DMA(void);
117

8+
/**
9+
* @brief ADC sync wait
10+
* @retval void
11+
*/
12+
static __inline__ void ADCsync() __attribute__((always_inline, unused));
13+
static void ADCsync() {
14+
while (ADC->STATUS.bit.SYNCBUSY == 1); //Just wait till the ADC is free
15+
}
16+
17+
// ADC DMA sequential free running (6) with Interrupts /////////////////
18+
19+
typedef struct {
20+
uint16_t btctrl;
21+
uint16_t btcnt;
22+
uint32_t srcaddr;
23+
uint32_t dstaddr;
24+
uint32_t descaddr;
25+
} dmacdescriptor ;
26+
volatile dmacdescriptor wrb[12] __attribute__ ((aligned (16)));
27+
dmacdescriptor descriptor_section[12] __attribute__ ((aligned (16)));
28+
dmacdescriptor descriptor __attribute__ ((aligned (16)));
29+
DmacDescriptor *desc; // DMA descriptor address (so we can change contents)
1230

13-
static uint32_t ADC_pinA = A4;
14-
static uint32_t ADC_pinB = A5;
15-
static uint32_t ADC_pinC = 8;
1631

1732

18-
#define _ADC_VOLTAGE 3.3 // we use ADC_REFCTRL_REFSEL_INTVCC1_Val
19-
#define _ADC_RESOLUTION (1 << 12)
20-
#define ADC_CONV_ ( _ADC_VOLTAGE / _ADC_RESOLUTION )
33+
SAMDCurrentSensceADC::SAMDCurrentSensceADC(int pinA, int pinB, int pinC, float arefaVoltage, uint32_t adcBits)
34+
: _ADC_VOLTAGE(arefaVoltage), _ADC_RESOLUTION(1 << adcBits)
35+
{
36+
ADC_CONV_ = ( _ADC_VOLTAGE / _ADC_RESOLUTION );
37+
this->pinA = pinA;
38+
this->pinB = pinB;
39+
this->pinC = pinC;
40+
}
41+
42+
void SAMDCurrentSensceADC::init()
43+
{
44+
_configure3PinsDMA();
45+
_start3PinsDMA(); //s
46+
}
2147

22-
void adc_dma(void *rxdata, size_t hwords);
23-
void adc_init();
24-
void adc_start_with_DMA();
25-
void dma_init();
2648

27-
void _start3PinsDMA()
49+
void SAMDCurrentSensceADC::_start3PinsDMA()
2850
{
2951
adc_dma(adcBuffer + ADC_OneBeforeFirstPin, BufferSize);
3052
adc_start_with_DMA();
3153
}
32-
void _read3PinsDMA(const int pinA,const int pinB,const int pinC, float & a, float & b, float & c)
54+
void SAMDCurrentSensceADC::_read3PinsDMA(const int pinA,const int pinB,const int pinC, float & a, float & b, float & c)
3355
{
3456
while(ADC->CTRLA.bit.ENABLE) ;
3557
uint32_t adcA = g_APinDescription[pinA].ulADCChannelNumber;
@@ -44,7 +66,7 @@ void _read3PinsDMA(const int pinA,const int pinB,const int pinC, float & a, floa
4466
}
4567

4668
// function reading an ADC value and returning the read voltage
47-
void _configure3PinsDMA(const int pinA,const int pinB,const int pinC){
69+
void SAMDCurrentSensceADC::_configure3PinsDMA(){
4870

4971
uint32_t adcA = g_APinDescription[pinA].ulADCChannelNumber;
5072
uint32_t adcB = g_APinDescription[pinB].ulADCChannelNumber;
@@ -65,52 +87,24 @@ void _configure3PinsDMA(const int pinA,const int pinB,const int pinC){
6587
ADC_OneBeforeFirstPin = ADC_FirstPin - 1; //hack to discard noisy first readout
6688
BufferSize = ADC_LastPin - ADC_OneBeforeFirstPin + 1;
6789

68-
ADC_pinA = pinA;
69-
ADC_pinB = pinB;
70-
ADC_pinC = pinC;
71-
7290
// ADC and DMA
7391
adc_init();
7492
dma_init();
7593
}
7694

7795

7896

79-
// ADC DMA sequential free running (6) with Interrupts /////////////////
80-
81-
typedef struct {
82-
uint16_t btctrl;
83-
uint16_t btcnt;
84-
uint32_t srcaddr;
85-
uint32_t dstaddr;
86-
uint32_t descaddr;
87-
} dmacdescriptor ;
88-
volatile dmacdescriptor wrb[12] __attribute__ ((aligned (16)));
89-
dmacdescriptor descriptor_section[12] __attribute__ ((aligned (16)));
90-
dmacdescriptor descriptor __attribute__ ((aligned (16)));
91-
DmacDescriptor *desc; // DMA descriptor address (so we can change contents)
92-
93-
static uint32_t ADC_DMA_chnl = 3; // DMA channel
9497

9598

96-
/**
97-
* @brief ADC sync wait
98-
* @retval void
99-
*/
100-
static __inline__ void ADCsync() __attribute__((always_inline, unused));
101-
static void ADCsync() {
102-
while (ADC->STATUS.bit.SYNCBUSY == 1); //Just wait till the ADC is free
103-
}
104-
10599
/**
106100
* @brief Initialize ADC
107101
* @retval void
108102
*/
109-
void adc_init(){
103+
void SAMDCurrentSensceADC::adc_init(){
110104

111-
auto a = analogRead(ADC_pinA); // do some pin init pinPeripheral()
112-
auto b = analogRead(ADC_pinB); // do some pin init pinPeripheral()
113-
auto c = analogRead(ADC_pinC); // do some pin init pinPeripheral()
105+
analogRead(pinA); // do some pin init pinPeripheral()
106+
analogRead(pinB); // do some pin init pinPeripheral()
107+
analogRead(pinC); // do some pin init pinPeripheral()
114108

115109
ADC->CTRLA.bit.ENABLE = 0x00; // Disable ADC
116110
ADCsync();
@@ -121,62 +115,55 @@ void adc_init(){
121115
// ADC->REFCTRL.bit.REFSEL = ADC_REFCTRL_REFSEL_INTVCC0;
122116
ADCsync(); // ref 31.6.16
123117

124-
a = analogRead(ADC_pinA); // do some pin init pinPeripheral()
125-
b = analogRead(ADC_pinB); // do some pin init pinPeripheral()
126-
c = analogRead(ADC_pinC); // do some pin init pinPeripheral()
127-
SerialUSB.println("--------------");
128-
SerialUSB.println(a);
129-
SerialUSB.println(b);
130-
SerialUSB.println(c);
131-
SerialUSB.println("--------------");
132-
/*
133-
Bits 19:16 – INPUTSCAN[3:0]: Number of Input Channels Included in Scan
134-
This register gives the number of input sources included in the pin scan. The number of input sources included is
135-
INPUTSCAN + 1. The input channels included are in the range from MUXPOS + INPUTOFFSET to MUXPOS +
136-
INPUTOFFSET + INPUTSCAN.
137-
The range of the scan mode must not exceed the number of input channels available on the device.
138-
Bits 4:0 – MUXPOS[4:0]: Positive Mux Input Selection
139-
These bits define the Mux selection for the positive ADC input. Table 32-14 shows the possible input selections. If
140-
the internal bandgap voltage or temperature sensor input channel is selected, then the Sampling Time Length bit
141-
group in the SamplingControl register must be written.
142-
Table 32-14. Positive Mux Input Selection
143-
MUXPOS[4:0] Group configuration Description
144-
0x00 PIN0 ADC AIN0 pin
145-
0x01 PIN1 ADC AIN1 pin
146-
0x02 PIN2 ADC AIN2 pin
147-
0x03 PIN3 ADC AIN3 pin
148-
0x04 PIN4 ADC AIN4 pin
149-
0x05 PIN5 ADC AIN5 pin
150-
0x06 PIN6 ADC AIN6 pin
151-
0x07 PIN7 ADC AIN7 pin
152-
0x08 PIN8 ADC AIN8 pin
153-
0x09 PIN9 ADC AIN9 pin
154-
0x0A PIN10 ADC AIN10 pin
155-
0x0B PIN11 ADC AIN11 pin
156-
0x0C PIN12 ADC AIN12 pin
157-
0x0D PIN13 ADC AIN13 pin
158-
0x0E PIN14 ADC AIN14 pin
159-
0x0F PIN15 ADC AIN15 pin
160-
0x10 PIN16 ADC AIN16 pin
161-
0x11 PIN17 ADC AIN17 pin
162-
0x12 PIN18 ADC AIN18 pin
163-
0x13 PIN19 ADC AIN19 pin
164-
0x14-0x17 Reserved
165-
0x18 TEMP Temperature reference
166-
0x19 BANDGAP Bandgap voltage
167-
0x1A SCALEDCOREVCC 1/4 scaled core supply
168-
0x1B SCALEDIOVCC 1/4 scaled I/O supply
169-
0x1C DAC DAC output
170-
0x1D-0x1F Reserved
171-
*/
118+
/*
119+
Bits 19:16 – INPUTSCAN[3:0]: Number of Input Channels Included in Scan
120+
This register gives the number of input sources included in the pin scan. The number of input sources included is
121+
INPUTSCAN + 1. The input channels included are in the range from MUXPOS + INPUTOFFSET to MUXPOS +
122+
INPUTOFFSET + INPUTSCAN.
123+
The range of the scan mode must not exceed the number of input channels available on the device.
124+
Bits 4:0 – MUXPOS[4:0]: Positive Mux Input Selection
125+
These bits define the Mux selection for the positive ADC input. Table 32-14 shows the possible input selections. If
126+
the internal bandgap voltage or temperature sensor input channel is selected, then the Sampling Time Length bit
127+
group in the SamplingControl register must be written.
128+
Table 32-14. Positive Mux Input Selection
129+
MUXPOS[4:0] Group configuration Description
130+
0x00 PIN0 ADC AIN0 pin
131+
0x01 PIN1 ADC AIN1 pin
132+
0x02 PIN2 ADC AIN2 pin
133+
0x03 PIN3 ADC AIN3 pin
134+
0x04 PIN4 ADC AIN4 pin
135+
0x05 PIN5 ADC AIN5 pin
136+
0x06 PIN6 ADC AIN6 pin
137+
0x07 PIN7 ADC AIN7 pin
138+
0x08 PIN8 ADC AIN8 pin
139+
0x09 PIN9 ADC AIN9 pin
140+
0x0A PIN10 ADC AIN10 pin
141+
0x0B PIN11 ADC AIN11 pin
142+
0x0C PIN12 ADC AIN12 pin
143+
0x0D PIN13 ADC AIN13 pin
144+
0x0E PIN14 ADC AIN14 pin
145+
0x0F PIN15 ADC AIN15 pin
146+
0x10 PIN16 ADC AIN16 pin
147+
0x11 PIN17 ADC AIN17 pin
148+
0x12 PIN18 ADC AIN18 pin
149+
0x13 PIN19 ADC AIN19 pin
150+
0x14-0x17 Reserved
151+
0x18 TEMP Temperature reference
152+
0x19 BANDGAP Bandgap voltage
153+
0x1A SCALEDCOREVCC 1/4 scaled core supply
154+
0x1B SCALEDIOVCC 1/4 scaled I/O supply
155+
0x1C DAC DAC output
156+
0x1D-0x1F Reserved
157+
*/
172158
ADC->INPUTCTRL.bit.MUXPOS = ADC_OneBeforeFirstPin;
173159
ADCsync();
174160
ADC->INPUTCTRL.bit.INPUTSCAN = ADC_LastPin; // so the adc will scan from AIN[1] to AIN[ADC_Number+1]
175161
ADCsync();
176162
ADC->INPUTCTRL.bit.INPUTOFFSET = 0; //input scan cursor
177163
ADCsync();
178164
ADC->AVGCTRL.reg = 0x00 ; //no averaging
179-
ADC->SAMPCTRL.reg = 0x05; ; //sample length in 1/2 CLK_ADC cycles
165+
ADC->SAMPCTRL.reg = 0x05; ; //sample length in 1/2 CLK_ADC cycles, see GCLK_ADC and ADC_CTRLB_PRESCALER_DIV16
166+
// according to the specsheet: f_GCLK_ADC ADC input clock frequency 48 MHz, so same as fCPU
180167
ADCsync();
181168
ADC->CTRLB.reg = ADC_CTRLB_PRESCALER_DIV16 | ADC_CTRLB_FREERUN | ADC_CTRLB_RESSEL_12BIT;
182169
ADCsync();
@@ -187,7 +174,7 @@ MUXPOS[4:0] Group configuration Description
187174
* @brief dma_init
188175
* @retval void
189176
*/
190-
void dma_init() {
177+
void SAMDCurrentSensceADC::dma_init() {
191178
// probably on by default
192179
PM->AHBMASK.reg |= PM_AHBMASK_DMAC ;
193180
PM->APBBMASK.reg |= PM_APBBMASK_DMAC ;
@@ -201,15 +188,15 @@ void dma_init() {
201188
* @brief adc_dma
202189
* @retval void
203190
*/
204-
void adc_dma(void *rxdata, size_t hwords) {
191+
void SAMDCurrentSensceADC::adc_dma(void *rxdata, size_t hwords) {
205192
uint32_t temp_CHCTRLB_reg;
206193

207194
DMAC->CHID.reg = DMAC_CHID_ID(ADC_DMA_chnl);
208195
DMAC->CHCTRLA.reg &= ~DMAC_CHCTRLA_ENABLE;
209196
DMAC->CHCTRLA.reg = DMAC_CHCTRLA_SWRST;
210197
DMAC->SWTRIGCTRL.reg &= (uint32_t)(~(1 << ADC_DMA_chnl));
211198
temp_CHCTRLB_reg = DMAC_CHCTRLB_LVL(0) |
212-
DMAC_CHCTRLB_TRIGSRC(ADC_DMAC_ID_RESRDY) | DMAC_CHCTRLB_TRIGACT_BEAT;
199+
DMAC_CHCTRLB_TRIGSRC(ADC_DMAC_ID_RESRDY) | DMAC_CHCTRLB_TRIGACT_BEAT;
213200
DMAC->CHCTRLB.reg = temp_CHCTRLB_reg;
214201
DMAC->CHINTENSET.reg = DMAC_CHINTENSET_MASK ; // enable all 3 interrupts
215202
descriptor.descaddr = 0;
@@ -224,6 +211,9 @@ DMAC_CHCTRLB_TRIGSRC(ADC_DMAC_ID_RESRDY) | DMAC_CHCTRLB_TRIGACT_BEAT;
224211
DMAC->CHCTRLA.reg |= DMAC_CHCTRLA_ENABLE;
225212
}
226213

214+
215+
216+
227217
/**
228218
* @brief adc_stop_with_DMA
229219
* @retval void
@@ -241,16 +231,12 @@ void adc_stop_with_DMA(void)
241231
void adc_start_with_DMA(void)
242232
{
243233
// SerialUSB.println("strating DMA...");
244-
245-
246-
ADC->INPUTCTRL.bit.MUXPOS = ADC_OneBeforeFirstPin;
247-
ADC->INPUTCTRL.bit.INPUTSCAN = ADC_LastPin;
234+
// ADC->INPUTCTRL.bit.MUXPOS = ADC_OneBeforeFirstPin;
235+
// ADC->INPUTCTRL.bit.INPUTSCAN = ADC_LastPin;
248236
ADC->INPUTCTRL.bit.INPUTOFFSET = 0;
249237
ADC->SWTRIG.bit.FLUSH = 1;
250238
ADC->CTRLA.bit.ENABLE = 0x01;
251239
}
252-
253-
254240
/**
255241
* @brief DMAC_Handler
256242
* @retval void
@@ -263,4 +249,4 @@ void DMAC_Handler() {
263249
DMAC->CHINTFLAG.reg = DMAC_CHINTENCLR_TCMPL; // clear
264250
DMAC->CHINTFLAG.reg = DMAC_CHINTENCLR_TERR;
265251
DMAC->CHINTFLAG.reg = DMAC_CHINTENCLR_SUSP;
266-
}
252+
}

0 commit comments

Comments
 (0)