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