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