1+ #include " teensy4_mcu.h"
2+ #include " ../../../drivers/hardware_specific/teensy/teensy4_mcu.h"
3+
4+ // if defined
5+ // - Teensy 4.0
6+ // - Teensy 4.1
7+ #if defined(__arm__) && defined(CORE_TEENSY) && ( defined(__IMXRT1062__) || defined(ARDUINO_TEENSY40) || defined(ARDUINO_TEENSY41) || defined(ARDUINO_TEENSY_MICROMOD) )
8+
9+ // function finding the TRIG event given the flexpwm timer and the submodule
10+ // returning -1 if the submodule is not valid or no trigger is available
11+ // allowing flexpwm1-4 and submodule 0-3
12+ //
13+ // the flags are defined in the imxrt.h file
14+ // https://github.com/PaulStoffregen/cores/blob/dd6aa8419ee173a0a6593eab669fbff54ed85f48/teensy4/imxrt.h#L9662
15+ int flextim__submodule_to_trig (IMXRT_FLEXPWM_t* flexpwm, int submodule){
16+ if (submodule <0 && submodule > 3 ) return -1 ;
17+ if (flexpwm == &IMXRT_FLEXPWM1){
18+ return XBARA1_IN_FLEXPWM1_PWM1_OUT_TRIG0 + submodule;
19+ }else if (flexpwm == &IMXRT_FLEXPWM2){
20+ return XBARA1_IN_FLEXPWM2_PWM1_OUT_TRIG0 + submodule;
21+ }else if (flexpwm == &IMXRT_FLEXPWM3){
22+ return XBARA1_IN_FLEXPWM3_PWM1_OUT_TRIG0 + submodule;
23+ }else if (flexpwm == &IMXRT_FLEXPWM4){
24+ return XBARA1_IN_FLEXPWM4_PWM1_OUT_TRIG0 + submodule;
25+ }
26+ return -1 ;
27+ }
28+
29+ volatile uint32_t val0, val1;
30+
31+ void read_currents (uint32_t *a, uint32_t *b){
32+ *a = val0;
33+ *b = val1;
34+ }
35+
36+ // interrupt service routine for the ADC_ETC0
37+ // reading the ADC values and clearing the interrupt
38+ void adcetc0_isr () {
39+ digitalWrite (30 ,HIGH);
40+ ADC_ETC_DONE0_1_IRQ |= 1 ; // clear
41+ val0 = ADC_ETC_TRIG0_RESULT_1_0 & 4095 ;
42+ val1 = (ADC_ETC_TRIG0_RESULT_1_0 >> 16 ) & 4095 ;
43+ asm (" dsb" );
44+ digitalWrite (30 ,LOW);
45+ }
46+
47+ // function initializing the ADC2
48+ // and the ADC_ETC trigger for the low side current sensing
49+ void adc1_init () {
50+ // Tried many configurations, but this seems to be best:
51+ ADC1_CFG = ADC_CFG_OVWREN // Allow overwriting of the next converted Data onto the existing
52+ | ADC_CFG_ADICLK (0 ) // input clock select - IPG clock
53+ | ADC_CFG_MODE (2 ) // 12-bit conversion 0 8-bit conversion 1 10-bit conversion 2 12-bit conversion
54+ | ADC_CFG_ADIV (2 ) // Input clock / 4
55+ | ADC_CFG_ADSTS (0 ) // Sample period (ADC clocks) = 3 if ADLSMP=0b
56+ | ADC_CFG_ADHSC // High speed operation
57+ | ADC_CFG_ADTRG; // Hardware trigger selected
58+
59+
60+ // Calibration of ADC1
61+ ADC1_GC |= ADC_GC_CAL; // begin cal ADC1
62+ while (ADC1_GC & ADC_GC_CAL) ;
63+
64+ ADC1_HC0 = 16 ; // ADC_ETC channel
65+ // use the second interrupt if necessary (for more than 2 channels)
66+ // ADC1_HC1 = 16;
67+ }
68+
69+ // function initializing the ADC2
70+ // and the ADC_ETC trigger for the low side current sensing
71+ void adc2_init (){
72+
73+ // configuring ADC2
74+ // Tried many configurations, but this seems to be best:
75+ ADC1_CFG = ADC_CFG_OVWREN // Allow overwriting of the next converted Data onto the existing
76+ | ADC_CFG_ADICLK (0 ) // input clock select - IPG clock
77+ | ADC_CFG_MODE (2 ) // 12-bit conversion 0 8-bit conversion 1 10-bit conversion 2 12-bit conversion
78+ | ADC_CFG_ADIV (2 ) // Input clock / 4
79+ | ADC_CFG_ADSTS (0 ) // Sample period (ADC clocks) = 3 if ADLSMP=0b
80+ | ADC_CFG_ADHSC // High speed operation
81+ | ADC_CFG_ADTRG; // Hardware trigger selected
82+
83+ // Calibration of ADC2
84+ ADC2_GC |= ADC_GC_CAL; // begin cal ADC2
85+ while (ADC2_GC & ADC_GC_CAL) ;
86+
87+ ADC2_HC0 = 16 ; // ADC_ETC channel
88+ // use the second interrupt if necessary (for more than 2 channels)
89+ // ADC2_HC1 = 16;
90+ }
91+
92+ void adc_etc_init (int pin1, int pin2) {
93+ ADC_ETC_CTRL &= ~(1 << 31 ); // SOFTRST
94+ ADC_ETC_CTRL = 0x40000001 ; // start with trigger 0
95+ ADC_ETC_TRIG0_CTRL = 0x100 ; // chainlength -1
96+
97+ // ADC1 7 8, chain channel, HWTS, IE, B2B
98+ // pg 3516, section 66.5.1.8
99+ ADC_ETC_TRIG0_CHAIN_1_0 =
100+ ADC_ETC_TRIG_CHAIN_IE1 (0 ) | // no interrupt on first or set 2 if interrupt when Done1
101+ ADC_ETC_TRIG_CHAIN_B2B1 | // Enable B2B, back to back ADC trigger
102+ ADC_ETC_TRIG_CHAIN_HWTS1 (1 ) |
103+ ADC_ETC_TRIG_CHAIN_CSEL1 (pin_to_channel[pin1]) | // ADC channel 8
104+ ADC_ETC_TRIG_CHAIN_IE0 (1 ) | // interrupt when Done0
105+ ADC_ETC_TRIG_CHAIN_B2B1 | // Enable B2B, back to back ADC trigger
106+ ADC_ETC_TRIG_CHAIN_HWTS0 (1 ) |
107+ ADC_ETC_TRIG_CHAIN_CSEL0 (pin_to_channel[pin2]); // ADC channel 7
108+
109+ attachInterruptVector (IRQ_ADC_ETC0, adcetc0_isr);
110+ NVIC_ENABLE_IRQ (IRQ_ADC_ETC0);
111+ // use the second interrupt if necessary (for more than 2 channels)
112+ // attachInterruptVector(IRQ_ADC_ETC1, adcetc1_isr);
113+ // NVIC_ENABLE_IRQ(IRQ_ADC_ETC1);
114+ }
115+
116+
117+ void xbar_connect (unsigned int input, unsigned int output)
118+ {
119+ if (input >= 88 ) return ;
120+ if (output >= 132 ) return ;
121+ volatile uint16_t *xbar = &XBARA1_SEL0 + (output / 2 );
122+ uint16_t val = *xbar;
123+ if (!(output & 1 )) {
124+ val = (val & 0xFF00 ) | input;
125+ } else {
126+ val = (val & 0x00FF ) | (input << 8 );
127+ }
128+ *xbar = val;
129+ }
130+ void xbar_init () {
131+ CCM_CCGR2 |= CCM_CCGR2_XBAR1 (CCM_CCGR_ON); // turn clock on for xbara1
132+ }
133+
134+
135+ // function reading an ADC value and returning the read voltage
136+ float _readADCVoltageLowSide (const int pinA, const void * cs_params){
137+
138+ GenericCurrentSenseParams* params = (GenericCurrentSenseParams*) cs_params;
139+ float adc_voltage_conv = params->adc_voltage_conv ;
140+ if (pinA == params->pins [0 ]) {
141+ return val0 * adc_voltage_conv;
142+ } else if (pinA == params->pins [1 ]) {
143+ return val1 * adc_voltage_conv;
144+ }
145+ return 0.0 ;
146+ }
147+
148+ // Configure low side for generic mcu
149+ // cannot do much but
150+ void * _configureADCLowSide (const void * driver_params, const int pinA,const int pinB,const int pinC){
151+ _UNUSED (driver_params);
152+
153+ pinMode (30 ,OUTPUT);
154+
155+ if ( _isset (pinA) ) pinMode (pinA, INPUT);
156+ if ( _isset (pinB) ) pinMode (pinB, INPUT);
157+ if ( _isset (pinC) ) pinMode (pinC, INPUT);
158+
159+ adc1_init ();
160+ adc_etc_init (pinA, pinB);
161+ xbar_init ();
162+ GenericCurrentSenseParams* params = new GenericCurrentSenseParams {
163+ .pins = { pinA, pinB, pinC },
164+ .adc_voltage_conv = (_ADC_VOLTAGE)/(_ADC_RESOLUTION)
165+ };
166+ return params;
167+ }
168+
169+ // sync driver and the adc
170+ void _driverSyncLowSide (void * driver_params, void * cs_params){
171+ Teensy4DriverParams* par = (Teensy4DriverParams*) driver_params;
172+ IMXRT_FLEXPWM_t* flexpwm = par->flextimers [0 ];
173+ int submodule = par->submodules [0 ];
174+
175+ // do xbar connect here
176+
177+ int xbar_trig_pwm = flextim__submodule_to_trig (flexpwm, submodule);
178+ if (xbar_trig_pwm<0 ) return ;
179+
180+ xbar_connect ((uint32_t )xbar_trig_pwm, XBARA1_OUT_ADC_ETC_TRIG00); // FlexPWM to adc_etc
181+
182+ // setup the ADC_ETC trigger
183+ flexpwm->SM [submodule].TCTRL = FLEXPWM_SMTCTRL_OUT_TRIG_EN (1 <<4 );
184+ // setup this val4 for interrupt on val5 match for ADC sync
185+ // reading two ARC takes about 5us. So put the interrupt 2.5us befor the center
186+ flexpwm->SM [submodule].VAL4 = -int (2.5e-6 *par->pwm_frequency *flexpwm->SM [submodule].VAL1 ) ; // 2.5us before center
187+
188+ }
189+
190+
191+ #endif
0 commit comments