1
+ #include " teensy4_mcu.h"
2
+ #include " ../../../drivers/hardware_specific/teensy/teensy4_mcu.h"
3
+ // #include "../../../common/lowpass_filter.h"
4
+ #include " ../../../common/foc_utils.h"
5
+ #include " ../../../communication/SimpleFOCDebug.h"
6
+
7
+ // if defined
8
+ // - Teensy 4.0
9
+ // - Teensy 4.1
10
+ #if defined(__arm__) && defined(CORE_TEENSY) && ( defined(__IMXRT1062__) || defined(ARDUINO_TEENSY40) || defined(ARDUINO_TEENSY41) || defined(ARDUINO_TEENSY_MICROMOD) )
11
+
12
+ // #define TEENSY4_ADC_INTERRUPT_DEBUG
13
+
14
+
15
+ volatile uint32_t val0, val1, val2;
16
+
17
+ // #define _BANDWIDTH_CS 10000.0f // [Hz] bandwidth for the current sense
18
+ // LowPassFilter lp1 = LowPassFilter(1.0/_BANDWIDTH_CS);
19
+ // LowPassFilter lp2 = LowPassFilter(1.0/_BANDWIDTH_CS);
20
+ // LowPassFilter lp3 = LowPassFilter(1.0/_BANDWIDTH_CS);
21
+
22
+ void read_currents (uint32_t *a, uint32_t *b, uint32_t *c=nullptr ){
23
+ *a = val0;
24
+ *b = val1;
25
+ *c = val2;
26
+ }
27
+
28
+ // interrupt service routine for the ADC_ETC0
29
+ // reading the ADC values and clearing the interrupt
30
+ void adcetc0_isr () {
31
+ #ifdef TEENSY4_ADC_INTERRUPT_DEBUG
32
+ digitalWrite (30 ,HIGH);
33
+ #endif
34
+ // page 3509 , section 66.5.1.3.3
35
+ ADC_ETC_DONE0_1_IRQ |= 1 ; // clear Done0 for trg0 at 1st bit
36
+ // val0 = lp1(ADC_ETC_TRIG0_RESULT_1_0 & 4095);
37
+ val0 = (ADC_ETC_TRIG0_RESULT_1_0 & 4095 );
38
+ // val1 = lp2((ADC_ETC_TRIG0_RESULT_1_0 >> 16) & 4095);
39
+ val1 = (ADC_ETC_TRIG0_RESULT_1_0 >> 16 ) & 4095 ;
40
+ #ifdef TEENSY4_ADC_INTERRUPT_DEBUG
41
+ digitalWrite (30 ,LOW);
42
+ #endif
43
+ }
44
+
45
+
46
+ void adcetc1_isr () {
47
+ #ifdef TEENSY4_ADC_INTERRUPT_DEBUG
48
+ digitalWrite (30 ,HIGH);
49
+ #endif
50
+ // page 3509 , section 66.5.1.3.3
51
+ ADC_ETC_DONE0_1_IRQ |= 1 << 16 ; // clear Done1 for trg0 at 16th bit
52
+ val2 = ADC_ETC_TRIG0_RESULT_3_2 & 4095 ;
53
+ // val2 = lp3( ADC_ETC_TRIG0_RESULT_3_2 & 4095);
54
+ #ifdef TEENSY4_ADC_INTERRUPT_DEBUG
55
+ digitalWrite (30 ,LOW);
56
+ #endif
57
+ }
58
+
59
+ // function initializing the ADC2
60
+ // and the ADC_ETC trigger for the low side current sensing
61
+ void adc1_init (int pin1, int pin2, int pin3=NOT_SET) {
62
+ // Tried many configurations, but this seems to be best:
63
+ ADC1_CFG = ADC_CFG_OVWREN // Allow overwriting of the next converted Data onto the existing
64
+ | ADC_CFG_ADICLK (0 ) // input clock select - IPG clock
65
+ | ADC_CFG_MODE (2 ) // 12-bit conversion 0 8-bit conversion 1 10-bit conversion 2 12-bit conversion
66
+ | ADC_CFG_ADIV (2 ) // Input clock / 2 (0 for /1, 1 for /2 and 2 for / 4) (1 is faster and maybe with some filtering could provide better results but 2 for now)
67
+ | ADC_CFG_ADSTS (0 ) // Sample period (ADC clocks) = 3 if ADLSMP=0b
68
+ | ADC_CFG_ADHSC // High speed operation
69
+ | ADC_CFG_ADTRG; // Hardware trigger selected
70
+
71
+
72
+ // Calibration of ADC1
73
+ ADC1_GC |= ADC_GC_CAL; // begin cal ADC1
74
+ while (ADC1_GC & ADC_GC_CAL) ;
75
+
76
+ ADC1_HC0 = 16 ; // ADC_ETC channel
77
+ // use the second interrupt if necessary (for more than 2 channels)
78
+ if (_isset (pin3)) {
79
+ ADC1_HC1 = 16 ;
80
+ }
81
+ }
82
+
83
+ // function initializing the ADC2
84
+ // and the ADC_ETC trigger for the low side current sensing
85
+ void adc2_init (){
86
+
87
+ // configuring ADC2
88
+ // Tried many configurations, but this seems to be best:
89
+ ADC1_CFG = ADC_CFG_OVWREN // Allow overwriting of the next converted Data onto the existing
90
+ | ADC_CFG_ADICLK (0 ) // input clock select - IPG clock
91
+ | ADC_CFG_MODE (2 ) // 12-bit conversion 0 8-bit conversion 1 10-bit conversion 2 12-bit conversion
92
+ | ADC_CFG_ADIV (2 ) // Input clock / 2 (0 for /1, 1 for /2 and 2 for / 4)
93
+ | ADC_CFG_ADSTS (0 ) // Sample period (ADC clocks) = 3 if ADLSMP=0b
94
+ | ADC_CFG_ADHSC // High speed operation
95
+ | ADC_CFG_ADTRG; // Hardware trigger selected
96
+
97
+ // Calibration of ADC2
98
+ ADC2_GC |= ADC_GC_CAL; // begin cal ADC2
99
+ while (ADC2_GC & ADC_GC_CAL) ;
100
+
101
+ ADC2_HC0 = 16 ; // ADC_ETC channel
102
+ // use the second interrupt if necessary (for more than 2 channels)
103
+ // ADC2_HC1 = 16;
104
+ }
105
+
106
+ // function initializing the ADC_ETC trigger for the low side current sensing
107
+ // it uses only the ADC1
108
+ // if the pin3 is not set it uses only 2 channels
109
+ void adc_etc_init (int pin1, int pin2, int pin3=NOT_SET) {
110
+ ADC_ETC_CTRL &= ~(1 << 31 ); // SOFTRST
111
+ ADC_ETC_CTRL = 0x40000001 ; // start with trigger 0
112
+ ADC_ETC_TRIG0_CTRL = ADC_ETC_TRIG_CTRL_TRIG_CHAIN ( _isset (pin3) ? 2 : 1 ) ; // 2 if 3 channels, 1 if 2 channels
113
+
114
+ // ADC1 7 8, chain channel, HWTS, IE, B2B
115
+ // pg 3516, section 66.5.1.8
116
+ ADC_ETC_TRIG0_CHAIN_1_0 =
117
+ ADC_ETC_TRIG_CHAIN_IE1 (0 ) | // no interrupt on first or set 2 if interrupt when Done1
118
+ ADC_ETC_TRIG_CHAIN_B2B1 | // Enable B2B, back to back ADC trigger
119
+ ADC_ETC_TRIG_CHAIN_HWTS1 (1 ) |
120
+ ADC_ETC_TRIG_CHAIN_CSEL1 (pin_to_channel[pin1]) | // ADC channel 8
121
+ ADC_ETC_TRIG_CHAIN_IE0 (1 ) | // interrupt when Done0
122
+ ADC_ETC_TRIG_CHAIN_B2B1 | // Enable B2B, back to back ADC trigger
123
+ ADC_ETC_TRIG_CHAIN_HWTS0 (1 ) |
124
+ ADC_ETC_TRIG_CHAIN_CSEL0 (pin_to_channel[pin2]); // ADC channel 7
125
+
126
+ attachInterruptVector (IRQ_ADC_ETC0, adcetc0_isr);
127
+ NVIC_ENABLE_IRQ (IRQ_ADC_ETC0);
128
+ // use the second interrupt if necessary (for more than 2 channels)
129
+ if (_isset (pin3)) {
130
+ ADC_ETC_TRIG0_CHAIN_3_2 =
131
+ ADC_ETC_TRIG_CHAIN_IE0 (2 ) | // interrupt when Done1
132
+ ADC_ETC_TRIG_CHAIN_B2B0 | // Enable B2B, back to back ADC trigger
133
+ ADC_ETC_TRIG_CHAIN_HWTS0 (1 ) |
134
+ ADC_ETC_TRIG_CHAIN_CSEL0 (pin_to_channel[pin3]);
135
+
136
+ attachInterruptVector (IRQ_ADC_ETC1, adcetc1_isr);
137
+ NVIC_ENABLE_IRQ (IRQ_ADC_ETC1);
138
+ }
139
+ }
140
+
141
+
142
+
143
+ // function reading an ADC value and returning the read voltage
144
+ float _readADCVoltageLowSide (const int pinA, const void * cs_params){
145
+
146
+ if (!_isset (pinA)) return 0.0 ; // if the pin is not set return 0
147
+ GenericCurrentSenseParams* params = (GenericCurrentSenseParams*) cs_params;
148
+ float adc_voltage_conv = params->adc_voltage_conv ;
149
+ if (pinA == params->pins [0 ]) {
150
+ return val0 * adc_voltage_conv;
151
+ } else if (pinA == params->pins [1 ]) {
152
+ return val1 * adc_voltage_conv;
153
+ }else if (pinA == params->pins [2 ]) {
154
+ return val2 * adc_voltage_conv;
155
+ }
156
+ return 0.0 ;
157
+ }
158
+
159
+ // Configure low side for generic mcu
160
+ // cannot do much but
161
+ void * _configureADCLowSide (const void * driver_params, const int pinA,const int pinB,const int pinC){
162
+ Teensy4DriverParams* par = (Teensy4DriverParams*) ((TeensyDriverParams*)driver_params)->additional_params ;
163
+ if (par == nullptr ){
164
+ SIMPLEFOC_DEBUG (" TEENSY-CS: Low side current sense failed, driver not supported!" );
165
+ return SIMPLEFOC_CURRENT_SENSE_INIT_FAILED;
166
+ }
167
+
168
+ SIMPLEFOC_DEBUG (" TEENSY-CS: Configuring low side current sense!" );
169
+
170
+ #ifdef TEENSY4_ADC_INTERRUPT_DEBUG
171
+ pinMode (30 ,OUTPUT);
172
+ #endif
173
+
174
+ if ( _isset (pinA) ) pinMode (pinA, INPUT);
175
+ if ( _isset (pinB) ) pinMode (pinB, INPUT);
176
+ if ( _isset (pinC) ) pinMode (pinC, INPUT);
177
+
178
+ // check if either of the pins are not set
179
+ // and dont use it if it isn't
180
+ int pin_count = 0 ;
181
+ int pins[3 ] = {NOT_SET, NOT_SET, NOT_SET};
182
+ if (_isset (pinA)) pins[pin_count++] = pinA;
183
+ if (_isset (pinB)) pins[pin_count++] = pinB;
184
+ if (_isset (pinC)) pins[pin_count++] = pinC;
185
+
186
+
187
+ adc1_init (pins[0 ], pins[1 ], pins[2 ]);
188
+ adc_etc_init (pins[0 ], pins[1 ], pins[2 ]);
189
+
190
+ xbar_init ();
191
+
192
+ GenericCurrentSenseParams* params = new GenericCurrentSenseParams {
193
+ .pins = {pins[0 ], pins[1 ], pins[2 ] },
194
+ .adc_voltage_conv = (_ADC_VOLTAGE)/(_ADC_RESOLUTION)
195
+ };
196
+ return params;
197
+ }
198
+
199
+ // sync driver and the adc
200
+ void _driverSyncLowSide (void * driver_params, void * cs_params){
201
+ Teensy4DriverParams* par = (Teensy4DriverParams*) ((TeensyDriverParams*)driver_params)->additional_params ;
202
+ IMXRT_FLEXPWM_t* flexpwm = par->flextimers [0 ];
203
+ int submodule = par->submodules [0 ];
204
+
205
+ SIMPLEFOC_DEBUG (" TEENSY-CS: Syncing low side current sense!" );
206
+ char buff[50 ];
207
+ sprintf (buff, " TEENSY-CS: Syncing to FlexPWM: %d, Submodule: %d" , flexpwm_to_index (flexpwm), submodule);
208
+ SIMPLEFOC_DEBUG (buff);
209
+
210
+ // find the xbar trigger for the flexpwm
211
+ int xbar_trig_pwm = flexpwm_submodule_to_trig (flexpwm, submodule);
212
+ if (xbar_trig_pwm<0 ) return ;
213
+
214
+ // allow theFlexPWM to trigger the ADC_ETC
215
+ xbar_connect ((uint32_t )xbar_trig_pwm, XBARA1_OUT_ADC_ETC_TRIG00); // FlexPWM to adc_etc
216
+
217
+ // setup the ADC_ETC trigger to be triggered by the FlexPWM channel 1 (val1)
218
+ // This val1 interrupt on match is in the center of the PWM
219
+ flexpwm->SM [submodule].TCTRL = FLEXPWM_SMTCTRL_OUT_TRIG_EN (1 <<1 );
220
+
221
+
222
+ // if needed the interrupt can be moved to some other point in the PWM cycle by using an addional val register example: VAL4
223
+ // setup the ADC_ETC trigger to be triggered by the FlexPWM channel 4 (val4)
224
+ // flexpwm->SM[submodule].TCTRL = FLEXPWM_SMTCTRL_OUT_TRIG_EN(1<<4);
225
+ // setup this val4 for interrupt on match for ADC sync
226
+ // this code assumes that the val4 is not used for anything else!
227
+ // reading two ADC takes about 2.5us. So put the interrupt 2.5us befor the center
228
+ // flexpwm->SM[submodule].VAL4 = int(flexpwm->SM[submodule].VAL1*(1.0f - 2.5e-6*par->pwm_frequency)) ; // 2.5us before center
229
+
230
+
231
+ #ifdef TEENSY4_ADC_INTERRUPT_DEBUG
232
+ // pin 4 observes out trigger line for 'scope
233
+ xbar_connect (xbar_trig_pwm, XBARA1_OUT_IOMUX_XBAR_INOUT08) ;
234
+ IOMUXC_GPR_GPR6 |= IOMUXC_GPR_GPR6_IOMUXC_XBAR_DIR_SEL_8 ; // select output mode for INOUT8
235
+ // Select alt 3 for EMC_06 (XBAR), rather than original 5 (GPIO)
236
+ CORE_PIN4_CONFIG = 3 ; // shorthand for IOMUXC_SW_MUX_CTL_PAD_GPIO_EMC_06 = 3 ;
237
+ // turn up drive & speed as very short pulse
238
+ IOMUXC_SW_PAD_CTL_PAD_GPIO_EMC_06 = IOMUXC_PAD_DSE (7 ) | IOMUXC_PAD_SPEED (3 ) | IOMUXC_PAD_SRE ;
239
+ #endif
240
+
241
+ }
242
+
243
+
244
+ #endif
0 commit comments