Official library for the FRDM-TFC shield
Dependents: TFC-TEST TFC-RACING-DEMO TFC-RACING-DEMO TFC-RACING-FSLMENTORMATTERS ... more
TFC.cpp
00001 #include "mbed.h" 00002 #include "TFC.h" 00003 00004 #define FTM1_CLK_PRESCALE 6// Prescale Selector value - see comments in Status Control (SC) section for more details 00005 #define SERVO_DEFAULT_PERIOD (float)(.020) // Desired Frequency of PWM Signal - Here 50Hz => 20ms period 00006 // use these to dial in servo steering to your particular servo 00007 #define SERVO_MIN_PULSE_WIDTH_DEFAULT (float)(.00095) // The number here should be be *pulse width* in seconds to move servo to its left limit 00008 #define SERVO_MAX_PULSE_WIDTH_DEFAULT (float)(.00160) // The number here should be be *pulse width* in seconds to move servo to its left limit 00009 00010 00011 #define FTM0_CLOCK (SystemCoreClock/2) 00012 #define FTM0_CLK_PRESCALE (0) // Prescale Selector value - see comments in Status Control (SC) section for more details 00013 #define FTM0_DEFAULT_SWITCHING_FREQUENCY (4000.0) 00014 00015 #define ADC_MAX_CODE (4095) 00016 00017 #define TAOS_CLK_HIGH PTE->PSOR = (1<<1) 00018 #define TAOS_CLK_LOW PTE->PCOR = (1<<1) 00019 #define TAOS_SI_HIGH PTD->PSOR = (1<<7) 00020 #define TAOS_SI_LOW PTD->PCOR = (1<<7) 00021 00022 #define ADC_STATE_INIT 0 00023 #define ADC_STATE_CAPTURE_POT_0 1 00024 #define ADC_STATE_CAPTURE_POT_1 2 00025 #define ADC_STATE_CAPTURE_BATTERY_LEVEL 3 00026 #define ADC_STATE_CAPTURE_LINE_SCAN 4 00027 00028 00029 #define TFC_POT_0_ADC_CHANNEL 13 00030 #define TFC_POT_1_ADC_CHANNEL 12 00031 #define TFC_BAT_SENSE_CHANNEL 4 00032 #define TFC_LINESCAN0_ADC_CHANNEL 6 00033 #define TFC_LINESCAN1_ADC_CHANNEL 7 00034 00035 00036 #define ADC0_irq_no 57 00037 #define ADC1_irq_no 58 00038 00039 #define ADC0_CHANA 19 // set to desired ADC0 channel trigger A 00040 #define ADC0_CHANB 20 // set to desired ADC0 channel trigger B 00041 00042 #define ADC1_CHANA 20 // set to desired ADC1 channel trigger A 20 defaults to potentiometer in TWRK60 00043 #define ADC1_CHANB 20 // set to desired ADC1 channel trigger B 00044 00045 #define ADC0_DLYA 0x2000 // ADC0 trigger A delay 00046 #define ADC0_DLYB 0x4000 // ADC0 trigger B delay 00047 #define ADC1_DLYA 0x6000 // ADC1 trigger A delay 00048 #define ADC1_DLYB 0x7fff // ADC1 trigger B delay 00049 00050 00051 #define ADC0A_DONE 0x01 00052 #define ADC0B_DONE 0x02 00053 #define ADC1A_DONE 0x04 00054 #define ADC1B_DONE 0x08 00055 00056 00057 // Bit shifting of bitfiled is already taken into account so 00058 // bitfiled values are always represented as relative to their position. 00059 00060 /************************* #Defines ******************************************/ 00061 00062 #define A 0x0 00063 #define B 0x1 00064 00065 /////// NOTE: the following defines relate to the ADC register definitions 00066 /////// and the content follows the reference manual, using the same symbols. 00067 00068 00069 //// ADCSC1 (register) 00070 00071 // Conversion Complete (COCO) mask 00072 #define COCO_COMPLETE ADC_SC1_COCO_MASK 00073 #define COCO_NOT 0x00 00074 00075 // ADC interrupts: enabled, or disabled. 00076 #define AIEN_ON ADC_SC1_AIEN_MASK 00077 #define AIEN_OFF 0x00 00078 00079 // Differential or Single ended ADC input 00080 #define DIFF_SINGLE 0x00 00081 #define DIFF_DIFFERENTIAL ADC_SC1_DIFF_MASK 00082 00083 //// ADCCFG1 00084 00085 // Power setting of ADC 00086 #define ADLPC_LOW ADC_CFG1_ADLPC_MASK 00087 #define ADLPC_NORMAL 0x00 00088 00089 // Clock divisor 00090 #define ADIV_1 0x00 00091 #define ADIV_2 0x01 00092 #define ADIV_4 0x02 00093 #define ADIV_8 0x03 00094 00095 // Long samle time, or Short sample time 00096 #define ADLSMP_LONG ADC_CFG1_ADLSMP_MASK 00097 #define ADLSMP_SHORT 0x00 00098 00099 // How many bits for the conversion? 8, 12, 10, or 16 (single ended). 00100 #define MODE_8 0x00 00101 #define MODE_12 0x01 00102 #define MODE_10 0x02 00103 #define MODE_16 0x03 00104 00105 00106 00107 // ADC Input Clock Source choice? Bus clock, Bus clock/2, "altclk", or the 00108 // ADC's own asynchronous clock for less noise 00109 #define ADICLK_BUS 0x00 00110 #define ADICLK_BUS_2 0x01 00111 #define ADICLK_ALTCLK 0x02 00112 #define ADICLK_ADACK 0x03 00113 00114 //// ADCCFG2 00115 00116 // Select between B or A channels 00117 #define MUXSEL_ADCB ADC_CFG2_MUXSEL_MASK 00118 #define MUXSEL_ADCA 0x00 00119 00120 // Ansync clock output enable: enable, or disable the output of it 00121 #define ADACKEN_ENABLED ADC_CFG2_ADACKEN_MASK 00122 #define ADACKEN_DISABLED 0x00 00123 00124 // High speed or low speed conversion mode 00125 #define ADHSC_HISPEED ADC_CFG2_ADHSC_MASK 00126 #define ADHSC_NORMAL 0x00 00127 00128 // Long Sample Time selector: 20, 12, 6, or 2 extra clocks for a longer sample time 00129 #define ADLSTS_20 0x00 00130 #define ADLSTS_12 0x01 00131 #define ADLSTS_6 0x02 00132 #define ADLSTS_2 0x03 00133 00134 ////ADCSC2 00135 00136 // Read-only status bit indicating conversion status 00137 #define ADACT_ACTIVE ADC_SC2_ADACT_MASK 00138 #define ADACT_INACTIVE 0x00 00139 00140 // Trigger for starting conversion: Hardware trigger, or software trigger. 00141 // For using PDB, the Hardware trigger option is selected. 00142 #define ADTRG_HW ADC_SC2_ADTRG_MASK 00143 #define ADTRG_SW 0x00 00144 00145 // ADC Compare Function Enable: Disabled, or Enabled. 00146 #define ACFE_DISABLED 0x00 00147 #define ACFE_ENABLED ADC_SC2_ACFE_MASK 00148 00149 // Compare Function Greater Than Enable: Greater, or Less. 00150 #define ACFGT_GREATER ADC_SC2_ACFGT_MASK 00151 #define ACFGT_LESS 0x00 00152 00153 // Compare Function Range Enable: Enabled or Disabled. 00154 #define ACREN_ENABLED ADC_SC2_ACREN_MASK 00155 #define ACREN_DISABLED 0x00 00156 00157 // DMA enable: enabled or disabled. 00158 #define DMAEN_ENABLED ADC_SC2_DMAEN_MASK 00159 #define DMAEN_DISABLED 0x00 00160 00161 // Voltage Reference selection for the ADC conversions 00162 // (***not*** the PGA which uses VREFO only). 00163 // VREFH and VREFL (0) , or VREFO (1). 00164 00165 #define REFSEL_EXT 0x00 00166 #define REFSEL_ALT 0x01 00167 #define REFSEL_RES 0x02 /* reserved */ 00168 #define REFSEL_RES_EXT 0x03 /* reserved but defaults to Vref */ 00169 00170 ////ADCSC3 00171 00172 // Calibration begin or off 00173 #define CAL_BEGIN ADC_SC3_CAL_MASK 00174 #define CAL_OFF 0x00 00175 00176 // Status indicating Calibration failed, or normal success 00177 #define CALF_FAIL ADC_SC3_CALF_MASK 00178 #define CALF_NORMAL 0x00 00179 00180 // ADC to continously convert, or do a sinle conversion 00181 #define ADCO_CONTINUOUS ADC_SC3_ADCO_MASK 00182 #define ADCO_SINGLE 0x00 00183 00184 // Averaging enabled in the ADC, or not. 00185 #define AVGE_ENABLED ADC_SC3_AVGE_MASK 00186 #define AVGE_DISABLED 0x00 00187 00188 // How many to average prior to "interrupting" the MCU? 4, 8, 16, or 32 00189 #define AVGS_4 0x00 00190 #define AVGS_8 0x01 00191 #define AVGS_16 0x02 00192 #define AVGS_32 0x03 00193 00194 ////PGA 00195 00196 // PGA enabled or not? 00197 #define PGAEN_ENABLED ADC_PGA_PGAEN_MASK 00198 #define PGAEN_DISABLED 0x00 00199 00200 // Chopper stabilization of the amplifier, or not. 00201 #define PGACHP_CHOP ADC_PGA_PGACHP_MASK 00202 #define PGACHP_NOCHOP 0x00 00203 00204 // PGA in low power mode, or normal mode. 00205 #define PGALP_LOW ADC_PGA_PGALP_MASK 00206 #define PGALP_NORMAL 0x00 00207 00208 // Gain of PGA. Selectable from 1 to 64. 00209 #define PGAG_1 0x00 00210 #define PGAG_2 0x01 00211 #define PGAG_4 0x02 00212 #define PGAG_8 0x03 00213 #define PGAG_16 0x04 00214 #define PGAG_32 0x05 00215 #define PGAG_64 0x06 00216 00217 00218 #define ADC_STATE_INIT 0 00219 #define ADC_STATE_CAPTURE_POT_0 1 00220 #define ADC_STATE_CAPTURE_POT_1 2 00221 #define ADC_STATE_CAPTURE_BATTERY_LEVEL 3 00222 #define ADC_STATE_CAPTURE_LINE_SCAN 4 00223 00224 00225 /////////// The above values fit into the structure below to select ADC/PGA 00226 /////////// configuration desired: 00227 00228 typedef struct adc_cfg { 00229 uint8_t CONFIG1; 00230 uint8_t CONFIG2; 00231 uint16_t COMPARE1; 00232 uint16_t COMPARE2; 00233 uint8_t STATUS2; 00234 uint8_t STATUS3; 00235 uint8_t STATUS1A; 00236 uint8_t STATUS1B; 00237 uint32_t PGA; 00238 } *tADC_ConfigPtr, tADC_Config ; 00239 00240 00241 #define CAL_BLK_NUMREC 18 00242 00243 typedef struct adc_cal { 00244 00245 uint16_t OFS; 00246 uint16_t PG; 00247 uint16_t MG; 00248 uint8_t CLPD; 00249 uint8_t CLPS; 00250 uint16_t CLP4; 00251 uint16_t CLP3; 00252 uint8_t CLP2; 00253 uint8_t CLP1; 00254 uint8_t CLP0; 00255 uint8_t dummy; 00256 uint8_t CLMD; 00257 uint8_t CLMS; 00258 uint16_t CLM4; 00259 uint16_t CLM3; 00260 uint8_t CLM2; 00261 uint8_t CLM1; 00262 uint8_t CLM0; 00263 } tADC_Cal_Blk ; 00264 00265 typedef struct ADC_MemMap { 00266 uint32_t SC1[2]; /**< ADC Status and Control Registers 1, array offset: 0x0, array step: 0x4 */ 00267 uint32_t CFG1; /**< ADC Configuration Register 1, offset: 0x8 */ 00268 uint32_t CFG2; /**< ADC Configuration Register 2, offset: 0xC */ 00269 uint32_t R[2]; /**< ADC Data Result Register, array offset: 0x10, array step: 0x4 */ 00270 uint32_t CV1; /**< Compare Value Registers, offset: 0x18 */ 00271 uint32_t CV2; /**< Compare Value Registers, offset: 0x1C */ 00272 uint32_t SC2; /**< Status and Control Register 2, offset: 0x20 */ 00273 uint32_t SC3; /**< Status and Control Register 3, offset: 0x24 */ 00274 uint32_t OFS; /**< ADC Offset Correction Register, offset: 0x28 */ 00275 uint32_t PG; /**< ADC Plus-Side Gain Register, offset: 0x2C */ 00276 uint32_t MG; /**< ADC Minus-Side Gain Register, offset: 0x30 */ 00277 uint32_t CLPD; /**< ADC Plus-Side General Calibration Value Register, offset: 0x34 */ 00278 uint32_t CLPS; /**< ADC Plus-Side General Calibration Value Register, offset: 0x38 */ 00279 uint32_t CLP4; /**< ADC Plus-Side General Calibration Value Register, offset: 0x3C */ 00280 uint32_t CLP3; /**< ADC Plus-Side General Calibration Value Register, offset: 0x40 */ 00281 uint32_t CLP2; /**< ADC Plus-Side General Calibration Value Register, offset: 0x44 */ 00282 uint32_t CLP1; /**< ADC Plus-Side General Calibration Value Register, offset: 0x48 */ 00283 uint32_t CLP0; /**< ADC Plus-Side General Calibration Value Register, offset: 0x4C */ 00284 uint8_t RESERVED_0[4]; 00285 uint32_t CLMD; /**< ADC Minus-Side General Calibration Value Register, offset: 0x54 */ 00286 uint32_t CLMS; /**< ADC Minus-Side General Calibration Value Register, offset: 0x58 */ 00287 uint32_t CLM4; /**< ADC Minus-Side General Calibration Value Register, offset: 0x5C */ 00288 uint32_t CLM3; /**< ADC Minus-Side General Calibration Value Register, offset: 0x60 */ 00289 uint32_t CLM2; /**< ADC Minus-Side General Calibration Value Register, offset: 0x64 */ 00290 uint32_t CLM1; /**< ADC Minus-Side General Calibration Value Register, offset: 0x68 */ 00291 uint32_t CLM0; /**< ADC Minus-Side General Calibration Value Register, offset: 0x6C */ 00292 } volatile *ADC_MemMapPtr; 00293 00294 00295 00296 /* ADC - Register accessors */ 00297 #define ADC_SC1_REG(base,index) ((base)->SC1[index]) 00298 #define ADC_CFG1_REG(base) ((base)->CFG1) 00299 #define ADC_CFG2_REG(base) ((base)->CFG2) 00300 #define ADC_R_REG(base,index) ((base)->R[index]) 00301 #define ADC_CV1_REG(base) ((base)->CV1) 00302 #define ADC_CV2_REG(base) ((base)->CV2) 00303 #define ADC_SC2_REG(base) ((base)->SC2) 00304 #define ADC_SC3_REG(base) ((base)->SC3) 00305 #define ADC_OFS_REG(base) ((base)->OFS) 00306 #define ADC_PG_REG(base) ((base)->PG) 00307 #define ADC_MG_REG(base) ((base)->MG) 00308 #define ADC_CLPD_REG(base) ((base)->CLPD) 00309 #define ADC_CLPS_REG(base) ((base)->CLPS) 00310 #define ADC_CLP4_REG(base) ((base)->CLP4) 00311 #define ADC_CLP3_REG(base) ((base)->CLP3) 00312 #define ADC_CLP2_REG(base) ((base)->CLP2) 00313 #define ADC_CLP1_REG(base) ((base)->CLP1) 00314 #define ADC_CLP0_REG(base) ((base)->CLP0) 00315 #define ADC_CLMD_REG(base) ((base)->CLMD) 00316 #define ADC_CLMS_REG(base) ((base)->CLMS) 00317 #define ADC_CLM4_REG(base) ((base)->CLM4) 00318 #define ADC_CLM3_REG(base) ((base)->CLM3) 00319 #define ADC_CLM2_REG(base) ((base)->CLM2) 00320 #define ADC_CLM1_REG(base) ((base)->CLM1) 00321 #define ADC_CLM0_REG(base) ((base)->CLM0) 00322 00323 #define ADC0_BASE_PTR ((ADC_MemMapPtr)0x4003B000u) 00324 /** Array initializer of ADC peripheral base pointers */ 00325 #define ADC_BASE_PTRS { ADC0_BASE_PTR } 00326 00327 00328 float _ServoDutyCycleMin; 00329 float _ServoDutyCycleMax; 00330 float _ServoPeriod; 00331 00332 volatile uint16_t QueuedServo0Val; 00333 volatile uint16_t QueuedServo1Val; 00334 00335 volatile uint16_t *LineScanImage0WorkingBuffer; 00336 volatile uint16_t *LineScanImage1WorkingBuffer; 00337 00338 volatile uint16_t LineScanImage0Buffer[2][128]; 00339 volatile uint16_t LineScanImage1Buffer[2][128]; 00340 volatile uint8_t LineScanWorkingBuffer; 00341 00342 volatile uint16_t * TFC_LineScanImage0; 00343 volatile uint16_t * TFC_LineScanImage1; 00344 volatile uint8_t TFC_LineScanImageReady; 00345 00346 volatile uint16_t PotADC_Value[2]; 00347 volatile uint16_t BatSenseADC_Value; 00348 volatile uint16_t CurrentADC_State; 00349 volatile uint8_t CurrentLineScanPixel; 00350 volatile uint8_t CurrentLineScanChannel; 00351 volatile uint32_t TFC_ServoTicker; 00352 00353 00354 void TFC_SetServoDutyCycle(uint8_t ServoNumber, float DutyCycle); 00355 void TFC_InitLineScanCamera(); 00356 uint8_t ADC_Cal(ADC_MemMapPtr adcmap); 00357 void ADC_Config_Alt(ADC_MemMapPtr adcmap, tADC_ConfigPtr ADC_CfgPtr); 00358 void ADC_Read_Cal(ADC_MemMapPtr adcmap, tADC_Cal_Blk *blk); 00359 void TFC_InitADC0(); 00360 void TFC_InitADC_System(); 00361 void TFC_GPIO_Init(); 00362 void ADC0_Handler(); 00363 void TPM1_Handler(); 00364 00365 00366 void TFC_Init() 00367 { 00368 00369 TFC_GPIO_Init(); 00370 00371 TFC_InitADC_System(); // Always call this before the Servo init function.... The IRQ for the Servo code modifies ADC registers and the clocks need enable to the ADC peripherals 1st! 00372 00373 TFC_InitLineScanCamera(); 00374 00375 TFC_InitServos(SERVO_MIN_PULSE_WIDTH_DEFAULT , SERVO_MAX_PULSE_WIDTH_DEFAULT, SERVO_DEFAULT_PERIOD); 00376 00377 TFC_ServoTicker = 0; 00378 00379 TFC_InitMotorPWM(FTM0_DEFAULT_SWITCHING_FREQUENCY); 00380 00381 } 00382 00383 00384 void TFC_GPIO_Init() 00385 { 00386 00387 //enable Clocks to all ports 00388 00389 SIM->SCGC5 |= SIM_SCGC5_PORTA_MASK | SIM_SCGC5_PORTB_MASK | SIM_SCGC5_PORTC_MASK | SIM_SCGC5_PORTD_MASK | SIM_SCGC5_PORTE_MASK; 00390 00391 //Setup Pins as GPIO 00392 PORTE->PCR[21] = PORT_PCR_MUX(1) | PORT_PCR_DSE_MASK; 00393 PORTE->PCR[20] = PORT_PCR_MUX(1); 00394 00395 //Port for Pushbuttons 00396 PORTC->PCR[13] = PORT_PCR_MUX(1); 00397 PORTC->PCR[17] = PORT_PCR_MUX(1); 00398 00399 00400 //Ports for DIP Switches 00401 PORTE->PCR[2] = PORT_PCR_MUX(1); 00402 PORTE->PCR[3] = PORT_PCR_MUX(1); 00403 PORTE->PCR[4] = PORT_PCR_MUX(1); 00404 PORTE->PCR[5] = PORT_PCR_MUX(1); 00405 00406 //Ports for LEDs 00407 PORTB->PCR[8] = PORT_PCR_MUX(1) | PORT_PCR_DSE_MASK; 00408 PORTB->PCR[9] = PORT_PCR_MUX(1) | PORT_PCR_DSE_MASK; 00409 PORTB->PCR[10] = PORT_PCR_MUX(1) | PORT_PCR_DSE_MASK; 00410 PORTB->PCR[11] = PORT_PCR_MUX(1) | PORT_PCR_DSE_MASK; 00411 00412 00413 //Setup the output pins 00414 PTE->PDDR = TFC_HBRIDGE_EN_LOC; 00415 PTB->PDDR = TFC_BAT_LED0_LOC | TFC_BAT_LED1_LOC | TFC_BAT_LED2_LOC | TFC_BAT_LED3_LOC; 00416 00417 TFC_SetBatteryLED(0); 00418 TFC_HBRIDGE_DISABLE; 00419 } 00420 00421 void TFC_SetBatteryLED(uint8_t Value) 00422 { 00423 if(Value & 0x01) 00424 TFC_BAT_LED0_ON; 00425 else 00426 TFC_BAT_LED0_OFF; 00427 00428 if(Value & 0x02) 00429 TFC_BAT_LED1_ON; 00430 else 00431 TFC_BAT_LED1_OFF; 00432 00433 if(Value & 0x04) 00434 TFC_BAT_LED2_ON; 00435 else 00436 TFC_BAT_LED2_OFF; 00437 00438 if(Value & 0x08) 00439 TFC_BAT_LED3_ON; 00440 else 00441 TFC_BAT_LED3_OFF; 00442 } 00443 00444 uint8_t TFC_GetDIP_Switch() 00445 { 00446 uint8_t DIP_Val=0; 00447 00448 DIP_Val = (PTE->PDIR>>2) & 0xF; 00449 00450 return DIP_Val; 00451 } 00452 00453 uint8_t TFC_ReadPushButton(uint8_t Index) 00454 { 00455 if(Index == 0) { 00456 return TFC_PUSH_BUTTON_0_PRESSED; 00457 } else { 00458 return TFC_PUSH_BUTTON_1_PRESSED; 00459 } 00460 } 00461 00462 extern "C" void TPM1_IRQHandler() 00463 { 00464 //Clear the overflow mask if set. According to the reference manual, we clear by writing a logic one! 00465 if(TPM1->SC & TPM_SC_TOF_MASK) 00466 TPM1->SC |= TPM_SC_TOF_MASK; 00467 00468 //Dump the queued values to the timer channels 00469 TPM1->CONTROLS[0].CnV = QueuedServo0Val; 00470 TPM1->CONTROLS[1].CnV = QueuedServo1Val; 00471 00472 00473 //Prime the next ADC capture cycle 00474 TAOS_SI_HIGH; 00475 //Prime the ADC pump and start capturing POT 0 00476 CurrentADC_State = ADC_STATE_CAPTURE_POT_0; 00477 00478 ADC0->CFG2 &= ~ADC_CFG2_MUXSEL_MASK; //Select the A side of the mux 00479 ADC0->SC1[0] = TFC_POT_0_ADC_CHANNEL | ADC_SC1_AIEN_MASK; //Start the State machine at POT0 00480 00481 //Flag that a new cervo cycle will start 00482 if (TFC_ServoTicker < 0xffffffff)//if servo tick less than max value, count up... 00483 TFC_ServoTicker++; 00484 00485 } 00486 00487 00488 void TFC_InitServos(float PulseWidthMin, float PulseWidthMax, float ServoPeriod) 00489 { 00490 00491 SIM->SCGC5 |= SIM_SCGC5_PORTB_MASK; 00492 00493 _ServoPeriod = ServoPeriod; 00494 _ServoDutyCycleMin = PulseWidthMin/ServoPeriod; 00495 _ServoDutyCycleMax = PulseWidthMax/ServoPeriod; 00496 00497 //Clock Setup for the TPM requires a couple steps. 00498 SIM->SCGC6 &= ~SIM_SCGC6_TPM1_MASK; 00499 //1st, set the clock mux 00500 //See Page 124 of f the KL25 Sub-Family Reference Manual, Rev. 3, September 2012 00501 SIM->SOPT2 |= SIM_SOPT2_PLLFLLSEL_MASK;// We Want MCGPLLCLK/2 (See Page 196 of the KL25 Sub-Family Reference Manual, Rev. 3, September 2012) 00502 SIM->SOPT2 &= ~(SIM_SOPT2_TPMSRC_MASK); 00503 SIM->SOPT2 |= SIM_SOPT2_TPMSRC(1); 00504 00505 //Enable the Clock to the FTM0 Module 00506 //See Page 207 of f the KL25 Sub-Family Reference Manual, Rev. 3, September 2012 00507 SIM->SCGC6 |= SIM_SCGC6_TPM1_MASK; 00508 00509 //The TPM Module has Clock. Now set up the peripheral 00510 00511 //Blow away the control registers to ensure that the counter is not running 00512 TPM1->SC = 0; 00513 TPM1->CONF = 0; 00514 00515 //While the counter is disabled we can setup the prescaler 00516 00517 TPM1->SC = TPM_SC_PS(FTM1_CLK_PRESCALE); 00518 TPM1->SC |= TPM_SC_TOIE_MASK; //Enable Interrupts for the Timer Overflow 00519 00520 //Setup the mod register to get the correct PWM Period 00521 00522 TPM1->MOD = (SystemCoreClock/(1<<(FTM1_CLK_PRESCALE))) * _ServoPeriod; 00523 //Setup Channels 0 and 1 00524 00525 TPM1->CONTROLS[0].CnSC = TPM_CnSC_MSB_MASK | TPM_CnSC_ELSB_MASK; 00526 TPM1->CONTROLS[1].CnSC = TPM_CnSC_MSB_MASK | TPM_CnSC_ELSB_MASK; 00527 00528 00529 //Set the Default duty cycle to servo neutral 00530 TFC_SetServo(0, 0.0); 00531 TFC_SetServo(1, 0.0); 00532 00533 //Enable the TPM COunter 00534 TPM1->SC |= TPM_SC_CMOD(1); 00535 00536 //Enable TPM1 IRQ on the NVIC 00537 00538 //NVIC_SetVector(TPM1_IRQn,(uint32_t)TPM1_Handler); 00539 NVIC_EnableIRQ(TPM1_IRQn); 00540 00541 //Enable the FTM functions on the the port 00542 00543 PORTB->PCR[0] = PORT_PCR_MUX(3); 00544 PORTB->PCR[1] = PORT_PCR_MUX(3); 00545 00546 } 00547 00548 00549 void TFC_SetServoDutyCycle(uint8_t ServoNumber, float DutyCycle) 00550 { 00551 switch(ServoNumber) { 00552 default: 00553 case 0: 00554 00555 QueuedServo0Val = TPM1->MOD * DutyCycle; 00556 00557 break; 00558 00559 case 1: 00560 00561 QueuedServo1Val = TPM1->MOD * DutyCycle; 00562 00563 break; 00564 } 00565 } 00566 00567 void TFC_SetServo(uint8_t ServoNumber, float Position) 00568 { 00569 TFC_SetServoDutyCycle(ServoNumber , 00570 ((Position + 1.0)/2) * ((_ServoDutyCycleMax - _ServoDutyCycleMin))+_ServoDutyCycleMin) ; 00571 00572 } 00573 00574 //******************************************************************************************************** 00575 //******************************************************************************************************** 00576 //******************************************************************************************************** 00577 // _____ _____ ______ _ _ _ _ _____ _______ _____ ____ _ _ _____ 00578 // /\ | __ \ / ____| | ____| | | | \ | |/ ____|__ __|_ _/ __ \| \ | |/ ____| 00579 // / \ | | | | | | |__ | | | | \| | | | | | || | | | \| | (___ 00580 // / /\ \ | | | | | | __| | | | | . ` | | | | | || | | | . ` |\___ \ 00581 // / ____ \| |__| | |____ | | | |__| | |\ | |____ | | _| || |__| | |\ |____) | 00582 // /_/ \_\_____/ \_____| |_| \____/|_| \_|\_____| |_| |_____\____/|_| \_|_____/ 00583 // ******************************************************************************************************** 00584 // ******************************************************************************************************** 00585 // ******************************************************************************************************** 00586 00587 00588 00589 00590 00591 uint8_t ADC_Cal(ADC_MemMapPtr adcmap) 00592 { 00593 00594 uint16_t cal_var; 00595 00596 ADC_SC2_REG(adcmap) &= ~ADC_SC2_ADTRG_MASK ; // Enable Software Conversion Trigger for Calibration Process - ADC0_SC2 = ADC0_SC2 | ADC_SC2_ADTRGW(0); 00597 ADC_SC3_REG(adcmap) &= ( ~ADC_SC3_ADCO_MASK & ~ADC_SC3_AVGS_MASK ); // set single conversion, clear avgs bitfield for next writing 00598 ADC_SC3_REG(adcmap) |= ( ADC_SC3_AVGE_MASK | ADC_SC3_AVGS(AVGS_32) ); // Turn averaging ON and set at max value ( 32 ) 00599 00600 00601 ADC_SC3_REG(adcmap) |= ADC_SC3_CAL_MASK ; // Start CAL 00602 while ( (ADC_SC1_REG(adcmap,A) & ADC_SC1_COCO_MASK ) == COCO_NOT ); // Wait calibration end 00603 00604 if ((ADC_SC3_REG(adcmap)& ADC_SC3_CALF_MASK) == CALF_FAIL ) { 00605 return(1); // Check for Calibration fail error and return 00606 } 00607 // Calculate plus-side calibration 00608 cal_var = 0x00; 00609 00610 cal_var = ADC_CLP0_REG(adcmap); 00611 cal_var += ADC_CLP1_REG(adcmap); 00612 cal_var += ADC_CLP2_REG(adcmap); 00613 cal_var += ADC_CLP3_REG(adcmap); 00614 cal_var += ADC_CLP4_REG(adcmap); 00615 cal_var += ADC_CLPS_REG(adcmap); 00616 00617 cal_var = cal_var/2; 00618 cal_var |= 0x8000; // Set MSB 00619 00620 ADC_PG_REG(adcmap) = ADC_PG_PG(cal_var); 00621 00622 00623 // Calculate minus-side calibration 00624 cal_var = 0x00; 00625 00626 cal_var = ADC_CLM0_REG(adcmap); 00627 cal_var += ADC_CLM1_REG(adcmap); 00628 cal_var += ADC_CLM2_REG(adcmap); 00629 cal_var += ADC_CLM3_REG(adcmap); 00630 cal_var += ADC_CLM4_REG(adcmap); 00631 cal_var += ADC_CLMS_REG(adcmap); 00632 00633 cal_var = cal_var/2; 00634 00635 cal_var |= 0x8000; // Set MSB 00636 00637 ADC_MG_REG(adcmap) = ADC_MG_MG(cal_var); 00638 00639 ADC_SC3_REG(adcmap) &= ~ADC_SC3_CAL_MASK ; /* Clear CAL bit */ 00640 00641 return(0); 00642 } 00643 00644 00645 void ADC_Config_Alt(ADC_MemMapPtr adcmap, tADC_ConfigPtr ADC_CfgPtr) 00646 { 00647 ADC_CFG1_REG(adcmap) = ADC_CfgPtr->CONFIG1; 00648 ADC_CFG2_REG(adcmap) = ADC_CfgPtr->CONFIG2; 00649 ADC_CV1_REG(adcmap) = ADC_CfgPtr->COMPARE1; 00650 ADC_CV2_REG(adcmap) = ADC_CfgPtr->COMPARE2; 00651 ADC_SC2_REG(adcmap) = ADC_CfgPtr->STATUS2; 00652 ADC_SC3_REG(adcmap) = ADC_CfgPtr->STATUS3; 00653 //ADC_PGA_REG(adcmap) = ADC_CfgPtr->PGA; 00654 ADC_SC1_REG(adcmap,A)= ADC_CfgPtr->STATUS1A; 00655 ADC_SC1_REG(adcmap,B)= ADC_CfgPtr->STATUS1B; 00656 } 00657 00658 00659 void ADC_Read_Cal(ADC_MemMapPtr adcmap, tADC_Cal_Blk *blk) 00660 { 00661 blk->OFS = ADC_OFS_REG(adcmap); 00662 blk->PG = ADC_PG_REG(adcmap); 00663 blk->MG = ADC_MG_REG(adcmap); 00664 blk->CLPD = ADC_CLPD_REG(adcmap); 00665 blk->CLPS = ADC_CLPS_REG(adcmap); 00666 blk->CLP4 = ADC_CLP4_REG(adcmap); 00667 blk->CLP3 = ADC_CLP3_REG(adcmap); 00668 blk->CLP2 = ADC_CLP2_REG(adcmap); 00669 blk->CLP1 = ADC_CLP1_REG(adcmap); 00670 blk->CLP0 = ADC_CLP0_REG(adcmap); 00671 blk->CLMD = ADC_CLMD_REG(adcmap); 00672 blk->CLMS = ADC_CLMS_REG(adcmap); 00673 blk->CLM4 = ADC_CLM4_REG(adcmap); 00674 blk->CLM3 = ADC_CLM3_REG(adcmap); 00675 blk->CLM2 = ADC_CLM2_REG(adcmap); 00676 blk->CLM1 = ADC_CLM1_REG(adcmap); 00677 blk->CLM0 = ADC_CLM0_REG(adcmap); 00678 00679 } 00680 00681 00682 void TFC_InitADC0() 00683 { 00684 tADC_Config Master_Adc0_Config; 00685 00686 00687 SIM->SCGC6 |= (SIM_SCGC6_ADC0_MASK); 00688 00689 //Lets calibrate the ADC. 1st setup how the channel will be used. 00690 00691 00692 Master_Adc0_Config.CONFIG1 = ADLPC_NORMAL //No low power mode 00693 | ADC_CFG1_ADIV(ADIV_4) //divide input by 4 00694 | ADLSMP_LONG //long sample time 00695 | ADC_CFG1_MODE(MODE_12)//single ended 8-bit conversion 00696 | ADC_CFG1_ADICLK(ADICLK_BUS); 00697 00698 Master_Adc0_Config.CONFIG2 = MUXSEL_ADCA // select the A side of the ADC channel. 00699 | ADACKEN_DISABLED 00700 | ADHSC_HISPEED 00701 | ADC_CFG2_ADLSTS(ADLSTS_2);//Extra long sample Time (20 extra clocks) 00702 00703 00704 Master_Adc0_Config.COMPARE1 = 00000; // Comparators don't matter for calibration 00705 Master_Adc0_Config.COMPARE1 = 0xFFFF; 00706 00707 Master_Adc0_Config.STATUS2 = ADTRG_HW //hardware triggers for calibration 00708 | ACFE_DISABLED //disable comparator 00709 | ACFGT_GREATER 00710 | ACREN_ENABLED 00711 | DMAEN_DISABLED //Disable DMA 00712 | ADC_SC2_REFSEL(REFSEL_EXT); //External Reference 00713 00714 Master_Adc0_Config.STATUS3 = CAL_OFF 00715 | ADCO_SINGLE 00716 | AVGE_ENABLED; 00717 // | ADC_SC3_AVGS(AVGS_4); 00718 00719 Master_Adc0_Config.PGA = 0; // Disable the PGA 00720 00721 00722 // Configure ADC as it will be used, but because ADC_SC1_ADCH is 31, 00723 // the ADC will be inactive. Channel 31 is just disable function. 00724 // There really is no channel 31. 00725 00726 Master_Adc0_Config.STATUS1A = AIEN_ON | DIFF_SINGLE | ADC_SC1_ADCH(31); 00727 00728 00729 ADC_Config_Alt(ADC0_BASE_PTR, &Master_Adc0_Config); // config ADC 00730 00731 // Calibrate the ADC in the configuration in which it will be used: 00732 ADC_Cal(ADC0_BASE_PTR); // do the calibration 00733 00734 00735 Master_Adc0_Config.STATUS2 = ACFE_DISABLED //disable comparator 00736 | ACFGT_GREATER 00737 | ACREN_ENABLED 00738 | DMAEN_DISABLED //Disable DMA 00739 | ADC_SC2_REFSEL(REFSEL_EXT); //External Reference 00740 00741 Master_Adc0_Config.STATUS3 = CAL_OFF 00742 | ADCO_SINGLE; 00743 00744 00745 00746 ADC_Config_Alt(ADC0_BASE_PTR, &Master_Adc0_Config); 00747 } 00748 00749 00750 void TFC_InitADC_System() 00751 { 00752 00753 TFC_InitADC0(); 00754 00755 00756 //All Adc processing of the Pots and linescan will be done in the ADC0 IRQ! 00757 //A state machine will scan through the channels. 00758 //This is done to automate the linescan capture on Channel 0 to ensure that timing is very even 00759 CurrentADC_State = ADC_STATE_INIT; 00760 00761 //The pump will be primed with the TPM1 interrupt. upon timeout/interrupt it will set the SI signal high 00762 //for the camera and then start the conversions for the pots. 00763 00764 // NVIC_SetVector(ADC0_IRQn,(uint32_t)ADC0_Handler); 00765 NVIC_EnableIRQ(ADC0_IRQn); 00766 00767 } 00768 00769 extern "C" void ADC0_IRQHandler() 00770 { 00771 uint8_t Junk; 00772 00773 switch(CurrentADC_State) { 00774 default: 00775 Junk = ADC0->R[0]; 00776 break; 00777 00778 case ADC_STATE_CAPTURE_POT_0: 00779 00780 PotADC_Value[0] = ADC0->R[0]; 00781 ADC0->CFG2 &= ~ADC_CFG2_MUXSEL_MASK; //Select the A side of the mux 00782 ADC0->SC1[0] = TFC_POT_1_ADC_CHANNEL | ADC_SC1_AIEN_MASK; 00783 CurrentADC_State = ADC_STATE_CAPTURE_POT_1; 00784 00785 break; 00786 00787 case ADC_STATE_CAPTURE_POT_1: 00788 00789 PotADC_Value[1] = ADC0->R[0]; 00790 ADC0->CFG2 |= ADC_CFG2_MUXSEL_MASK; //Select the B side of the mux 00791 ADC0->SC1[0] = TFC_BAT_SENSE_CHANNEL| ADC_SC1_AIEN_MASK; 00792 CurrentADC_State = ADC_STATE_CAPTURE_BATTERY_LEVEL; 00793 00794 break; 00795 00796 case ADC_STATE_CAPTURE_BATTERY_LEVEL: 00797 00798 BatSenseADC_Value = ADC0->R[0]; 00799 00800 //Now we will start the sequence for the Linescan camera 00801 00802 TAOS_CLK_HIGH; 00803 00804 for(Junk = 0; Junk<50; Junk++) { 00805 } 00806 00807 TAOS_SI_LOW; 00808 00809 00810 CurrentLineScanPixel = 0; 00811 CurrentLineScanChannel = 0; 00812 CurrentADC_State = ADC_STATE_CAPTURE_LINE_SCAN; 00813 ADC0->CFG2 |= ADC_CFG2_MUXSEL_MASK; //Select the B side of the mux 00814 ADC0->SC1[0] = TFC_LINESCAN0_ADC_CHANNEL | ADC_SC1_AIEN_MASK; 00815 00816 break; 00817 00818 case ADC_STATE_CAPTURE_LINE_SCAN: 00819 00820 if(CurrentLineScanPixel<128) { 00821 if(CurrentLineScanChannel == 0) { 00822 LineScanImage0WorkingBuffer[CurrentLineScanPixel] = ADC0->R[0]; 00823 ADC0->SC1[0] = TFC_LINESCAN1_ADC_CHANNEL | ADC_SC1_AIEN_MASK; 00824 CurrentLineScanChannel = 1; 00825 00826 } else { 00827 LineScanImage1WorkingBuffer[CurrentLineScanPixel] = ADC0->R[0]; 00828 ADC0->SC1[0] = TFC_LINESCAN0_ADC_CHANNEL | ADC_SC1_AIEN_MASK; 00829 CurrentLineScanChannel = 0; 00830 CurrentLineScanPixel++; 00831 00832 TAOS_CLK_LOW; 00833 for(Junk = 0; Junk<50; Junk++) { 00834 } 00835 TAOS_CLK_HIGH; 00836 00837 } 00838 00839 } else { 00840 // done with the capture sequence. we can wait for the PIT0 IRQ to restart 00841 00842 TAOS_CLK_HIGH; 00843 00844 for(Junk = 0; Junk<50; Junk++) { 00845 } 00846 00847 TAOS_CLK_LOW; 00848 CurrentADC_State = ADC_STATE_INIT; 00849 00850 //swap the buffer 00851 00852 if(LineScanWorkingBuffer == 0) { 00853 LineScanWorkingBuffer = 1; 00854 00855 LineScanImage0WorkingBuffer = &LineScanImage0Buffer[1][0]; 00856 LineScanImage1WorkingBuffer = &LineScanImage1Buffer[1][0]; 00857 00858 TFC_LineScanImage0 = &LineScanImage0Buffer[0][0]; 00859 TFC_LineScanImage1 = &LineScanImage1Buffer[0][0]; 00860 } else { 00861 LineScanWorkingBuffer = 0; 00862 LineScanImage0WorkingBuffer = &LineScanImage0Buffer[0][0]; 00863 LineScanImage1WorkingBuffer = &LineScanImage1Buffer[0][0]; 00864 00865 TFC_LineScanImage0 = &LineScanImage0Buffer[1][0]; 00866 TFC_LineScanImage1 = &LineScanImage1Buffer[1][0]; 00867 } 00868 00869 TFC_LineScanImageReady++; 00870 } 00871 00872 break; 00873 } 00874 00875 } 00876 00877 void TFC_InitLineScanCamera() 00878 { 00879 SIM->SCGC5 |= SIM_SCGC5_PORTE_MASK | SIM_SCGC5_PORTD_MASK; //Make sure the clock is enabled for PORTE; 00880 PORTE->PCR[1] = PORT_PCR_MUX(1) | PORT_PCR_DSE_MASK; //Enable GPIO on on the pin for the CLOCK Signal 00881 PORTD->PCR[7] = PORT_PCR_MUX(1) | PORT_PCR_DSE_MASK; //Enable GPIO on on the pin for SI signal 00882 00883 PORTD->PCR[5] = PORT_PCR_MUX(0); //Make sure AO signal goes to an analog input 00884 PORTD->PCR[6] = PORT_PCR_MUX(0); //Make sure AO signal goes to an analog input 00885 00886 //Make sure the Clock and SI pins are outputs 00887 PTD->PDDR |= (1<<7); 00888 PTE->PDDR |= (1<<1); 00889 00890 TAOS_CLK_LOW; 00891 TAOS_SI_LOW; 00892 00893 LineScanWorkingBuffer = 0; 00894 00895 LineScanImage0WorkingBuffer = &LineScanImage0Buffer[LineScanWorkingBuffer][0]; 00896 LineScanImage1WorkingBuffer = &LineScanImage1Buffer[LineScanWorkingBuffer][0]; 00897 00898 TFC_LineScanImage0 = &LineScanImage0Buffer[1][0]; 00899 TFC_LineScanImage1 = &LineScanImage1Buffer[1][0]; 00900 } 00901 00902 00903 00904 00905 00906 /** Initialized TPM0 to be used for generating PWM signals for the the dual drive motors. This method is called in the TFC constructor with a default value of 4000.0Hz 00907 * 00908 * @param SwitchingFrequency PWM Switching Frequency in floating point format. Pick something between 1000 and 9000. Maybe you can modulate it and make a tune. 00909 */ 00910 void TFC_InitMotorPWM(float SwitchingFrequency) 00911 { 00912 //Clock Setup for the TPM requires a couple steps. 00913 00914 //1st, set the clock mux 00915 //See Page 124 of f the KL25 Sub-Family Reference Manual, Rev. 3, September 2012 00916 SIM->SOPT2 |= SIM_SOPT2_PLLFLLSEL_MASK;// We Want MCGPLLCLK/2 (See Page 196 of the KL25 Sub-Family Reference Manual, Rev. 3, September 2012) 00917 SIM->SOPT2 &= ~(SIM_SOPT2_TPMSRC_MASK); 00918 SIM->SOPT2 |= SIM_SOPT2_TPMSRC(1); //We want the MCGPLLCLK/2 (See Page 196 of the KL25 Sub-Family Reference Manual, Rev. 3, September 2012) 00919 00920 00921 //Enable the Clock to the FTM0 Module 00922 //See Page 207 of f the KL25 Sub-Family Reference Manual, Rev. 3, September 2012 00923 SIM->SCGC6 |= SIM_SCGC6_TPM0_MASK; 00924 00925 //The TPM Module has Clock. Now set up the peripheral 00926 00927 //Blow away the control registers to ensure that the counter is not running 00928 TPM0->SC = 0; 00929 TPM0->CONF = 0; 00930 00931 //While the counter is disabled we can setup the prescaler 00932 00933 TPM0->SC = TPM_SC_PS(FTM0_CLK_PRESCALE); 00934 00935 //Setup the mod register to get the correct PWM Period 00936 00937 TPM0->MOD = (uint32_t)((float)(FTM0_CLOCK/(1<<FTM0_CLK_PRESCALE))/SwitchingFrequency); 00938 00939 //Setup Channels 0,1,2,3 00940 TPM0->CONTROLS[0].CnSC = TPM_CnSC_MSB_MASK | TPM_CnSC_ELSB_MASK; 00941 TPM0->CONTROLS[1].CnSC = TPM_CnSC_MSB_MASK | TPM_CnSC_ELSA_MASK; // invert the second PWM signal for a complimentary output; 00942 TPM0->CONTROLS[2].CnSC = TPM_CnSC_MSB_MASK | TPM_CnSC_ELSB_MASK; 00943 TPM0->CONTROLS[3].CnSC = TPM_CnSC_MSB_MASK | TPM_CnSC_ELSA_MASK; // invert the second PWM signal for a complimentary output; 00944 00945 //Enable the Counter 00946 00947 //Set the Default duty cycle to 50% duty cycle 00948 TFC_SetMotorPWM(0.0,0.0); 00949 00950 //Enable the TPM COunter 00951 TPM0->SC |= TPM_SC_CMOD(1); 00952 00953 //Enable the FTM functions on the the port 00954 PORTC->PCR[1] = PORT_PCR_MUX(4); 00955 PORTC->PCR[2] = PORT_PCR_MUX(4); 00956 PORTC->PCR[3] = PORT_PCR_MUX(4); 00957 PORTC->PCR[4] = PORT_PCR_MUX(4); 00958 00959 } 00960 00961 void TFC_SetMotorPWM(float MotorA , float MotorB) 00962 { 00963 if(MotorA>1.0) 00964 MotorA = 1.0; 00965 else if(MotorA<-1.0) 00966 MotorA = -1.0; 00967 00968 if(MotorB>1.0) 00969 MotorB = 1.0; 00970 else if(MotorB<-1.0) 00971 MotorB = -1.0; 00972 00973 TPM0->CONTROLS[2].CnV = (uint16_t) ((float)TPM0->MOD * (float)((MotorA + 1.0)/2.0)); 00974 TPM0->CONTROLS[3].CnV = TPM0->CONTROLS[2].CnV; 00975 TPM0->CONTROLS[0].CnV = (uint16_t) ((float)TPM0->MOD * (float)((MotorB + 1.0)/2.0)); 00976 TPM0->CONTROLS[1].CnV = TPM0->CONTROLS[0].CnV; 00977 00978 } 00979 00980 //Pot Reading is Scaled to return a value of -1.0 to 1.0 00981 float TFC_ReadPot(uint8_t Channel) 00982 { 00983 if(Channel == 0) 00984 return ((float)PotADC_Value[0]/-((float)ADC_MAX_CODE/2.0))+1.0; 00985 else 00986 return ((float)PotADC_Value[1]/-((float)ADC_MAX_CODE/2.0))+1.0; 00987 } 00988 00989 float TFC_ReadBatteryVoltage() 00990 { 00991 return (((float)BatSenseADC_Value/(float)(ADC_MAX_CODE)) * 3.0);// * ((47000.0+10000.0)/10000.0); 00992 } 00993 00994 00995 void TFC_SetBatteryLED_Level(uint8_t BattLevel) 00996 { 00997 switch(BattLevel) 00998 { 00999 default: 01000 case 0: 01001 TFC_BAT_LED0_OFF; 01002 TFC_BAT_LED1_OFF; 01003 TFC_BAT_LED2_OFF; 01004 TFC_BAT_LED3_OFF; 01005 break; 01006 01007 case 1: 01008 TFC_BAT_LED0_ON; 01009 TFC_BAT_LED1_OFF; 01010 TFC_BAT_LED2_OFF; 01011 TFC_BAT_LED3_OFF; 01012 break; 01013 01014 case 2: 01015 TFC_BAT_LED0_ON; 01016 TFC_BAT_LED1_ON; 01017 TFC_BAT_LED2_OFF; 01018 TFC_BAT_LED3_OFF; 01019 break; 01020 01021 case 3: 01022 TFC_BAT_LED0_ON; 01023 TFC_BAT_LED1_ON; 01024 TFC_BAT_LED2_ON; 01025 TFC_BAT_LED3_OFF; 01026 break; 01027 01028 case 4: 01029 TFC_BAT_LED0_ON; 01030 TFC_BAT_LED1_ON; 01031 TFC_BAT_LED2_ON; 01032 TFC_BAT_LED3_ON; 01033 break; 01034 01035 } 01036 }
Generated on Thu Jul 14 2022 19:34:25 by 1.7.2