Skip to content

Commit e1f4292

Browse files
Merge pull request #79 from maxlem/dev
Changes for SAMD debug output and SAMD current sensing (work in progress)
2 parents 0b7839f + bb7d29c commit e1f4292

File tree

10 files changed

+486
-109
lines changed

10 files changed

+486
-109
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: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ void LowsideCurrentSense::calibrateOffsets(){
3434
offset_ic= 0;
3535
// read the adc voltage 1000 times ( arbitrary number )
3636
for (int i = 0; i < 1000; i++) {
37+
_startADC3PinConversionLowSide();
3738
offset_ia += _readADCVoltageLowSide(pinA);
3839
offset_ib += _readADCVoltageLowSide(pinB);
3940
if(_isset(pinC)) offset_ic += _readADCVoltageLowSide(pinC);
@@ -48,6 +49,7 @@ void LowsideCurrentSense::calibrateOffsets(){
4849
// read all three phase currents (if possible 2 or 3)
4950
PhaseCurrent_s LowsideCurrentSense::getPhaseCurrents(){
5051
PhaseCurrent_s current;
52+
_startADC3PinConversionLowSide();
5153
current.a = (_readADCVoltageLowSide(pinA) - offset_ia)*gain_a;// amps
5254
current.b = (_readADCVoltageLowSide(pinB) - offset_ib)*gain_b;// amps
5355
current.c = (!_isset(pinC)) ? 0 : (_readADCVoltageLowSide(pinC) - offset_ic)*gain_c; // amps

src/current_sense/hardware_api.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,9 @@ void _configureADCInline(const int pinA,const int pinB,const int pinC = NOT_SET)
2828
* @param pinC - adc pin C
2929
*/
3030
void _configureADCLowSide(const int pinA,const int pinB,const int pinC = NOT_SET);
31+
32+
void _startADC3PinConversionLowSide();
33+
3134
/**
3235
* function reading an ADC value and returning the read voltage
3336
*

src/current_sense/hardware_specific/esp32_mcu.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,9 @@ float _readADCVoltageLowSide(const int pin){
3030
return raw_adc * _ADC_CONV;
3131
}
3232

33+
void _startADC3PinConversionLowSide(){
34+
35+
}
3336

3437
// function reading an ADC value and returning the read voltage
3538
void _configureADCLowSide(const int pinA,const int pinB,const int pinC){
Lines changed: 304 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,304 @@
1+
2+
#include "samd21_mcu.h"
3+
#include "../hardware_api.h"
4+
5+
6+
static bool freeRunning = false;
7+
static int _pinA, _pinB, _pinC;
8+
static uint16_t a = 0xFFFF, b = 0xFFFF, c = 0xFFFF; // updated by adcStopWithDMA when configured in freerunning mode
9+
static SAMDCurrentSenseADCDMA instance;
10+
/**
11+
* function reading an ADC value and returning the read voltage
12+
*
13+
* @param pinA - adc pin A
14+
* @param pinB - adc pin B
15+
* @param pinC - adc pin C
16+
*/
17+
void _configureADCLowSide(const int pinA,const int pinB,const int pinC)
18+
{
19+
_pinA = pinA;
20+
_pinB = pinB;
21+
_pinC = pinC;
22+
freeRunning = true;
23+
instance.init(pinA, pinB, pinC);
24+
25+
}
26+
void _startADC3PinConversionLowSide()
27+
{
28+
instance.startADCScan();
29+
}
30+
/**
31+
* function reading an ADC value and returning the read voltage
32+
*
33+
* @param pinA - the arduino pin to be read (it has to be ADC pin)
34+
*/
35+
float _readADCVoltageLowSide(const int pinA)
36+
{
37+
instance.readResults(a, b, c);
38+
39+
if(pinA == _pinA)
40+
return instance.toVolts(a);
41+
if(pinA == _pinB)
42+
return instance.toVolts(b);
43+
if(pinA == _pinC)
44+
return instance.toVolts(c);
45+
46+
return NAN;
47+
}
48+
49+
/**
50+
* function syncing the Driver with the ADC for the LowSide Sensing
51+
*/
52+
void _driverSyncLowSide()
53+
{
54+
SIMPLEFOC_SAMD_DEBUG_SERIAL.println(F("TODO! _driverSyncLowSide() is not implemented"));
55+
instance.startADCScan();
56+
//TODO: hook with PWM interrupts
57+
}
58+
59+
60+
61+
62+
63+
64+
65+
66+
67+
68+
// Credit: significant portions of this code were pulled from Paul Gould's git https://github.com/gouldpa/FOC-Arduino-Brushless
69+
70+
static void adcStopWithDMA(void);
71+
static void adcStartWithDMA(void);
72+
73+
/**
74+
* @brief ADC sync wait
75+
* @retval void
76+
*/
77+
static __inline__ void ADCsync() __attribute__((always_inline, unused));
78+
static void ADCsync() {
79+
while (ADC->STATUS.bit.SYNCBUSY == 1); //Just wait till the ADC is free
80+
}
81+
82+
// ADC DMA sequential free running (6) with Interrupts /////////////////
83+
84+
SAMDCurrentSenseADCDMA * SAMDCurrentSenseADCDMA::getHardwareAPIInstance()
85+
{
86+
87+
return &instance;
88+
}
89+
90+
SAMDCurrentSenseADCDMA::SAMDCurrentSenseADCDMA()
91+
{
92+
}
93+
94+
void SAMDCurrentSenseADCDMA::init(int pinA, int pinB, int pinC, int pinAREF, float voltageAREF, uint32_t adcBits, uint32_t channelDMA)
95+
{
96+
this->pinA = pinA;
97+
this->pinB = pinB;
98+
this->pinC = pinC;
99+
this->pinAREF = pinAREF;
100+
this->channelDMA = channelDMA;
101+
this->voltageAREF = voltageAREF;
102+
this->maxCountsADC = 1 << adcBits;
103+
countsToVolts = ( voltageAREF / maxCountsADC );
104+
105+
initPins();
106+
initADC();
107+
initDMA();
108+
startADCScan(); //so we have something to read next time we call readResults()
109+
}
110+
111+
112+
void SAMDCurrentSenseADCDMA::startADCScan(){
113+
adcToDMATransfer(adcBuffer + oneBeforeFirstAIN, BufferSize);
114+
adcStartWithDMA();
115+
}
116+
117+
bool SAMDCurrentSenseADCDMA::readResults(uint16_t & a, uint16_t & b, uint16_t & c){
118+
if(ADC->CTRLA.bit.ENABLE)
119+
return false;
120+
uint32_t ainA = g_APinDescription[pinA].ulADCChannelNumber;
121+
uint32_t ainB = g_APinDescription[pinB].ulADCChannelNumber;
122+
a = adcBuffer[ainA];
123+
b = adcBuffer[ainB];
124+
if(_isset(pinC))
125+
{
126+
uint32_t ainC = g_APinDescription[pinC].ulADCChannelNumber;
127+
c = adcBuffer[ainC];
128+
}
129+
return true;
130+
}
131+
132+
133+
float SAMDCurrentSenseADCDMA::toVolts(uint16_t counts) {
134+
return counts * countsToVolts;
135+
}
136+
137+
void SAMDCurrentSenseADCDMA::initPins(){
138+
139+
pinMode(pinAREF, INPUT);
140+
pinMode(pinA, INPUT);
141+
pinMode(pinB, INPUT);
142+
143+
uint32_t ainA = g_APinDescription[pinA].ulADCChannelNumber;
144+
uint32_t ainB = g_APinDescription[pinB].ulADCChannelNumber;
145+
firstAIN = min(ainA, ainB);
146+
lastAIN = max(ainA, ainB);
147+
if( _isset(pinC) )
148+
{
149+
uint32_t ainC = g_APinDescription[pinC].ulADCChannelNumber;
150+
pinMode(pinC, INPUT);
151+
firstAIN = min(firstAIN, ainC);
152+
lastAIN = max(lastAIN, ainC);
153+
}
154+
155+
oneBeforeFirstAIN = firstAIN - 1; //hack to discard noisy first readout
156+
BufferSize = lastAIN - oneBeforeFirstAIN + 1;
157+
158+
}
159+
160+
void SAMDCurrentSenseADCDMA::initADC(){
161+
162+
analogRead(pinA); // do some pin init pinPeripheral()
163+
analogRead(pinB); // do some pin init pinPeripheral()
164+
analogRead(pinC); // do some pin init pinPeripheral()
165+
166+
ADC->CTRLA.bit.ENABLE = 0x00; // Disable ADC
167+
ADCsync();
168+
//ADC->REFCTRL.bit.REFSEL = ADC_REFCTRL_REFSEL_INTVCC0_Val; // 2.2297 V Supply VDDANA
169+
ADC->INPUTCTRL.bit.GAIN = ADC_INPUTCTRL_GAIN_1X_Val; // Gain select as 1X
170+
// ADC->INPUTCTRL.bit.GAIN = ADC_INPUTCTRL_GAIN_DIV2_Val; // default
171+
ADC->REFCTRL.bit.REFSEL = ADC_REFCTRL_REFSEL_AREFA;
172+
// ADC->REFCTRL.bit.REFSEL = ADC_REFCTRL_REFSEL_INTVCC0;
173+
ADCsync(); // ref 31.6.16
174+
175+
/*
176+
Bits 19:16 – INPUTSCAN[3:0]: Number of Input Channels Included in Scan
177+
This register gives the number of input sources included in the pin scan. The number of input sources included is
178+
INPUTSCAN + 1. The input channels included are in the range from MUXPOS + INPUTOFFSET to MUXPOS +
179+
INPUTOFFSET + INPUTSCAN.
180+
The range of the scan mode must not exceed the number of input channels available on the device.
181+
Bits 4:0 – MUXPOS[4:0]: Positive Mux Input Selection
182+
These bits define the Mux selection for the positive ADC input. Table 32-14 shows the possible input selections. If
183+
the internal bandgap voltage or temperature sensor input channel is selected, then the Sampling Time Length bit
184+
group in the SamplingControl register must be written.
185+
Table 32-14. Positive Mux Input Selection
186+
MUXPOS[4:0] Group configuration Description
187+
0x00 PIN0 ADC AIN0 pin
188+
0x01 PIN1 ADC AIN1 pin
189+
0x02 PIN2 ADC AIN2 pin
190+
0x03 PIN3 ADC AIN3 pin
191+
0x04 PIN4 ADC AIN4 pin
192+
0x05 PIN5 ADC AIN5 pin
193+
0x06 PIN6 ADC AIN6 pin
194+
0x07 PIN7 ADC AIN7 pin
195+
0x08 PIN8 ADC AIN8 pin
196+
0x09 PIN9 ADC AIN9 pin
197+
0x0A PIN10 ADC AIN10 pin
198+
0x0B PIN11 ADC AIN11 pin
199+
0x0C PIN12 ADC AIN12 pin
200+
0x0D PIN13 ADC AIN13 pin
201+
0x0E PIN14 ADC AIN14 pin
202+
0x0F PIN15 ADC AIN15 pin
203+
0x10 PIN16 ADC AIN16 pin
204+
0x11 PIN17 ADC AIN17 pin
205+
0x12 PIN18 ADC AIN18 pin
206+
0x13 PIN19 ADC AIN19 pin
207+
0x14-0x17 Reserved
208+
0x18 TEMP Temperature reference
209+
0x19 BANDGAP Bandgap voltage
210+
0x1A SCALEDCOREVCC 1/4 scaled core supply
211+
0x1B SCALEDIOVCC 1/4 scaled I/O supply
212+
0x1C DAC DAC output
213+
0x1D-0x1F Reserved
214+
*/
215+
ADC->INPUTCTRL.bit.MUXPOS = oneBeforeFirstAIN;
216+
ADCsync();
217+
ADC->INPUTCTRL.bit.INPUTSCAN = lastAIN; // so the adc will scan from oneBeforeFirstAIN to lastAIN (inclusive)
218+
ADCsync();
219+
ADC->INPUTCTRL.bit.INPUTOFFSET = 0; //input scan cursor
220+
ADCsync();
221+
ADC->AVGCTRL.reg = 0x00 ; //no averaging
222+
ADC->SAMPCTRL.reg = 0x05; ; //sample length in 1/2 CLK_ADC cycles, see GCLK_ADC and ADC_CTRLB_PRESCALER_DIV16
223+
// according to the specsheet: f_GCLK_ADC ADC input clock frequency 48 MHz, so same as fCPU
224+
ADCsync();
225+
ADC->CTRLB.reg = ADC_CTRLB_PRESCALER_DIV16 | ADC_CTRLB_FREERUN | ADC_CTRLB_RESSEL_12BIT;
226+
ADCsync();
227+
}
228+
229+
volatile dmacdescriptor wrb[12] __attribute__ ((aligned (16)));
230+
231+
void SAMDCurrentSenseADCDMA::initDMA() {
232+
// probably on by default
233+
PM->AHBMASK.reg |= PM_AHBMASK_DMAC ;
234+
PM->APBBMASK.reg |= PM_APBBMASK_DMAC ;
235+
NVIC_EnableIRQ( DMAC_IRQn ) ;
236+
DMAC->BASEADDR.reg = (uint32_t)descriptor_section;
237+
DMAC->WRBADDR.reg = (uint32_t)wrb;
238+
DMAC->CTRL.reg = DMAC_CTRL_DMAENABLE | DMAC_CTRL_LVLEN(0xf);
239+
}
240+
241+
242+
void SAMDCurrentSenseADCDMA::adcToDMATransfer(void *rxdata, uint32_t hwords) {
243+
244+
DMAC->CHID.reg = DMAC_CHID_ID(channelDMA);
245+
DMAC->CHCTRLA.reg &= ~DMAC_CHCTRLA_ENABLE;
246+
DMAC->CHCTRLA.reg = DMAC_CHCTRLA_SWRST;
247+
DMAC->SWTRIGCTRL.reg &= (uint32_t)(~(1 << channelDMA));
248+
249+
DMAC->CHCTRLB.reg = DMAC_CHCTRLB_LVL(0)
250+
| DMAC_CHCTRLB_TRIGSRC(ADC_DMAC_ID_RESRDY)
251+
| DMAC_CHCTRLB_TRIGACT_BEAT;
252+
DMAC->CHINTENSET.reg = DMAC_CHINTENSET_MASK ; // enable all 3 interrupts
253+
descriptor.descaddr = 0;
254+
descriptor.srcaddr = (uint32_t) &ADC->RESULT.reg;
255+
descriptor.btcnt = hwords;
256+
descriptor.dstaddr = (uint32_t)rxdata + hwords*2; // end address
257+
descriptor.btctrl = DMAC_BTCTRL_BEATSIZE_HWORD | DMAC_BTCTRL_DSTINC | DMAC_BTCTRL_VALID;
258+
memcpy(&descriptor_section[channelDMA],&descriptor, sizeof(dmacdescriptor));
259+
260+
// start channel
261+
DMAC->CHID.reg = DMAC_CHID_ID(channelDMA);
262+
DMAC->CHCTRLA.reg |= DMAC_CHCTRLA_ENABLE;
263+
}
264+
265+
266+
int iii = 0;
267+
268+
void adcStopWithDMA(void){
269+
ADCsync();
270+
ADC->CTRLA.bit.ENABLE = 0x00;
271+
// ADCsync();
272+
// if(iii++ % 1000 == 0)
273+
// {
274+
// SIMPLEFOC_SAMD_DEBUG_SERIAL.print(a);
275+
// SIMPLEFOC_SAMD_DEBUG_SERIAL.print(" :: ");
276+
// SIMPLEFOC_SAMD_DEBUG_SERIAL.print(b);
277+
// SIMPLEFOC_SAMD_DEBUG_SERIAL.print(" :: ");
278+
// SIMPLEFOC_SAMD_DEBUG_SERIAL.print(c);
279+
// SIMPLEFOC_SAMD_DEBUG_SERIAL.println("yo!");
280+
// }
281+
282+
283+
}
284+
285+
void adcStartWithDMA(void){
286+
ADCsync();
287+
ADC->INPUTCTRL.bit.INPUTOFFSET = 0;
288+
ADCsync();
289+
ADC->SWTRIG.bit.FLUSH = 1;
290+
ADCsync();
291+
ADC->CTRLA.bit.ENABLE = 0x01;
292+
ADCsync();
293+
}
294+
295+
void DMAC_Handler() {
296+
uint8_t active_channel;
297+
active_channel = DMAC->INTPEND.reg & DMAC_INTPEND_ID_Msk; // get channel number
298+
DMAC->CHID.reg = DMAC_CHID_ID(active_channel);
299+
adcStopWithDMA();
300+
DMAC->CHINTFLAG.reg = DMAC_CHINTENCLR_TCMPL; // clear
301+
DMAC->CHINTFLAG.reg = DMAC_CHINTENCLR_TERR;
302+
DMAC->CHINTFLAG.reg = DMAC_CHINTENCLR_SUSP;
303+
304+
}

0 commit comments

Comments
 (0)