SimpleFOClibrary 2.4.0
Loading...
Searching...
No Matches
current_sense/hardware_specific/samd/samd21_mcu.cpp
Go to the documentation of this file.
1
2#include "samd21_mcu.h"
3
4#if defined(_SAMD21_)
5
6#include "../../hardware_api.h"
7#include "../../../drivers/hardware_specific/samd/samd_mcu.h"
8
9static int _pinA = NOT_SET, _pinB = NOT_SET, _pinC = NOT_SET;
10static uint16_t adc_raw[3] = {0, 0, 0};
11static uint8_t current_phase = 0; // which phase we're sampling
12static bool is_high_side = true; // low-side current sense
13
14#define _SAMD21_ADC_VOLTAGE 3.3f
15#define _SAMD21_ADC_RESOLUTION 4095.0f
16
17static void setupADCEventTriggerFromDriver(const SAMDHardwareDriverParams *par, const GenericCurrentSenseParams *cs_params) {
18
19 // --- Configure ADC module ---
20 ADC->CTRLA.bit.ENABLE = 0;
21 while (ADC->STATUS.bit.SYNCBUSY);
22
23 ADC->REFCTRL.reg = ADC_REFCTRL_REFSEL_INTVCC1;
24 ADC->CTRLB.reg = ADC_CTRLB_PRESCALER_DIV16 | ADC_CTRLB_RESSEL_12BIT;
25 ADC->CTRLB.bit.FREERUN = 0;
26 while (ADC->STATUS.bit.SYNCBUSY);
27
28 ADC->INPUTCTRL.bit.MUXPOS = g_APinDescription[_pinA].ulADCChannelNumber;
29 // there must be a way to trigger more than one ADC conversion at a time.
30 // SO far I was not able to do it.
31 // ADC->INPUTCTRL.bit.INPUTSCAN = 1; // N = number_of_channels - 1
32 // ADC->INPUTCTRL.bit.INPUTOFFSET = 0;
33 while (ADC->STATUS.bit.SYNCBUSY);
34
35 // Enable event start
36 ADC->EVCTRL.bit.STARTEI = 1;
37 ADC->INTENSET.bit.RESRDY = 1;
38 NVIC_EnableIRQ(ADC_IRQn);
39
40 // --- Configure Event System ---
41 uint8_t tcc_num = par->tccPinConfigurations[1]->tcc.tccn;
42
43 // --- Enable event output on the PWM timer (important!) ---
44 Tcc* tcc = nullptr;
45 switch (tcc_num) {
46 case 0: tcc = TCC0; break;
47 case 1: tcc = TCC1; break;
48 case 2: tcc = TCC2; break;
49 default: tcc = TCC0; break;
50 }
51
52 // We are enabling the overflow/underflow event output
53 // This is not ideal as it triggers twice per PWM cycle
54 // So we need to keep track if we are in high-side or low-side current sense
55 if (tcc) {
56 tcc->CTRLA.bit.ENABLE = 0;
57 while (tcc->SYNCBUSY.bit.ENABLE);
58 tcc->EVCTRL.reg |= TCC_EVCTRL_OVFEO;
59 tcc->CTRLA.bit.ENABLE = 1;
60 while (tcc->SYNCBUSY.bit.ENABLE);
61 }
62
63 // Configure the event channel to trigger on the TCC overflow
64 // and connect it to the ADC start event
65 uint8_t evgen = 0;
66 switch (tcc_num) {
67 case 0: evgen = EVSYS_ID_GEN_TCC0_OVF; break;
68 case 1: evgen = EVSYS_ID_GEN_TCC1_OVF; break;
69 case 2: evgen = EVSYS_ID_GEN_TCC2_OVF; break;
70 default: evgen = EVSYS_ID_GEN_TCC0_OVF; break;
71 }
72
73 PM->APBCMASK.reg |= PM_APBCMASK_EVSYS;
74 EVSYS->CHANNEL.reg = EVSYS_CHANNEL_CHANNEL(0)
75 | EVSYS_CHANNEL_EVGEN(evgen)
76 | EVSYS_CHANNEL_PATH_ASYNCHRONOUS;
77 EVSYS->USER.reg = EVSYS_USER_USER(EVSYS_ID_USER_ADC_START)
78 | EVSYS_USER_CHANNEL(1);
79
80 // Enable ADC
81 ADC->CTRLA.bit.ENABLE = 1;
82 while (ADC->STATUS.bit.SYNCBUSY);
83}
84
85
86// ADC interrupt (switch between A, B, C)
87void ADC_Handler() {
88 //digitalWrite(13,HIGH);
89 // check if we are in high-side or low-side current sense
90 is_high_side = !is_high_side;
91 if(is_high_side){
92 ADC->INTFLAG.reg = ADC_INTFLAG_RESRDY;
93 //digitalWrite(13,LOW);
94 return;
95 }
96
97 // read the result and switch to the next channel
98 if (ADC->INTFLAG.bit.RESRDY) {
99 uint16_t result = ADC->RESULT.reg;
100
101 adc_raw[current_phase] = result;
102 if (current_phase == 0) {
103 ADC->INPUTCTRL.bit.MUXPOS = g_APinDescription[_pinB].ulADCChannelNumber;
104 current_phase = 1;
105 } else if (current_phase == 1) {
106 if (_pinC >= 0) {
107 ADC->INPUTCTRL.bit.MUXPOS = g_APinDescription[_pinC].ulADCChannelNumber;
108 current_phase = 2;
109 } else {
110 ADC->INPUTCTRL.bit.MUXPOS = g_APinDescription[_pinA].ulADCChannelNumber;
111 current_phase = 0;
112 }
113 } else {
114 ADC->INPUTCTRL.bit.MUXPOS = g_APinDescription[_pinA].ulADCChannelNumber;
115 current_phase = 0;
116 }
117 ADC->INTFLAG.reg = ADC_INTFLAG_RESRDY;
118 }
119 //digitalWrite(13,LOW);
120}
121
122
123// ------- API functions -------
124
125void* _configureADCLowSide(const void* driver_params, const int pinA, const int pinB, const int pinC) {
126
127 if(_isset(_pinA) || _isset(_pinB) || _isset(_pinC)) {
128 SIMPLEFOC_DEBUG("SAMD-CUR: ERR: Pins already in use for current sensing!");
130 }
131
132 // --- Configure ADC pins ---
133 if (_isset(pinA)) pinMode(pinA, INPUT);
134 if (_isset(pinB)) pinMode(pinB, INPUT);
135 if (_isset(pinC)) pinMode(pinC, INPUT);
136
137 // save the pins for later use
138 // only one motor possible for now
139 _pinA = pinA;
140 _pinB = pinB;
141 _pinC = pinC;
142
144 .pins = { pinA, pinB, pinC },
145 .adc_voltage_conv = (_SAMD21_ADC_VOLTAGE) / (_SAMD21_ADC_RESOLUTION),
146
147 };
148 return params;
149}
150
151
152float _readADCVoltageLowSide(const int pin, const void* cs_params) {
153
154 // extract the ADC raw value for the given pin
155 float countsToVolts = ((GenericCurrentSenseParams*)cs_params)->adc_voltage_conv;
156
157 // read the value form the buffer
158 int i = 0;
159 for(auto p: ((GenericCurrentSenseParams*)cs_params)->pins) {
160 if (p == pin) return adc_raw[i] * countsToVolts;
161 i++;
162 }
163
164 return 0.0; // pin not available
165}
166
167void* _driverSyncLowSide(void* driver_params, void* cs_params) {
168
169 setupADCEventTriggerFromDriver((const SAMDHardwareDriverParams*)driver_params, (const GenericCurrentSenseParams*)cs_params);
170 return cs_params;
171}
172
173#endif
#define SIMPLEFOC_DEBUG(msg,...)
void * _driverSyncLowSide(void *driver_params, void *cs_params)
#define SIMPLEFOC_CURRENT_SENSE_INIT_FAILED
void * _configureADCLowSide(const void *driver_params, const int pinA, const int pinB, const int pinC=NOT_SET)
float _readADCVoltageLowSide(const int pinA, const void *cs_params)
const int const int const int pinC
GenericCurrentSenseParams * params
#define NOT_SET
Definition foc_utils.h:34
#define _isset(a)
Definition foc_utils.h:13