/** * @brief 设置PDB转换频率 * @retval None */ static void _PDB_SetCounterPeriod(uint32_t srcClock, uint32_t timeInUs) { static const uint8_t MULT[4] = {1, 10, 20, 40}; uint32_t clkDiv,mult; uint32_t i,j; for(i=0;i<8;i++) { for(j=0;j<4;j++) { if((srcClock/1000000)*timeInUs/((1<<i)*MULT[j]) < 0xFFFF) { clkDiv = i; mult = j; break; } } } LIB_TRACE("clkDiv:%d Mult:%d\r\n",clkDiv, mult); /* clk div */ PDB0->SC &= ~PDB_SC_PRESCALER_MASK; PDB0->SC |= PDB_SC_PRESCALER(clkDiv); /* muti */ PDB0->SC &= ~PDB_SC_MULT_MASK; PDB0->SC |= PDB_SC_MULT(mult); LIB_TRACE("MOD:%d\r\n", (srcClock/1000000)*timeInUs/((1<<clkDiv)*MULT[mult])); PDB0->MOD = (srcClock/1000000)*timeInUs/((1<<clkDiv)*MULT[mult]); PDB0->IDLY = (srcClock/1000000)*timeInUs/((1<<clkDiv)*MULT[mult]); }
/** * initialize PDB 0 hardware * * count is the number of 1/16384 second time periods * continuous is a boolean value which determines if the counter will work in one-shot or * continuous mode */ void PDB0Init(uint16_t count, int continuous) { /* Enable the clock for PDB0 (PDBTimer 0) using the SIM_SCGC6 register * (System Clock Gating Control Register 6) (See 12.2.14 on page 344 of * the K70 Sub-Family Reference Manual, Rev. 2, Dec 2011) */ SIM_SCGC6 |= SIM_SCGC6_PDB_MASK; /* Disable PDBTimer 0 and clear any pending PDB Interrupt Flag using * the PDB0_SC register (Status and Control register for PDBTimer 0) * (See 43.3.1 on page 1199 of the K70 Sub-Family Reference Manual, * Rev. 2, Dec 2011) */ PDB0_SC = 0; /* With the MCGOUTCLK = FLL_Factor*IRC (which is 32768*640), and with the * peripheral clock divider set to 10, we end up with a peripheral clock of * 2,097,152 Hz. Setting the prescaler to divide by 128 yields a counter * frequency of 16384 Hz */ /* Set the Clock 2 (peripheral clock) output divider value to 10 using * the SIM_CLKDIV1 register (System Clock Divider Register 1) * (See 12.2.16 on page 347 of the K70 Sub-Family Reference Manual, * Rev. 2, Dec 2011) */ SIM_CLKDIV1 = (SIM_CLKDIV1 & ~SIM_CLKDIV1_OUTDIV2_MASK) | SIM_CLKDIV1_OUTDIV2(SIM_CLKDIV1_OUTDIV_DIVIDE_BY_10); /* Load timer count (16-bit value) into the modulo register */ PDB0_MOD = count; /* Load timer count (16-bit value) into the interrupt delay register */ PDB0_IDLY = count; /* Prescaler to divide by 128, Software trigger is selected, * PDB interrupt enabled, Multiplication factor is 1, * Continuous mode, Load OK, Enable the PDB */ PDB0_SC = PDB_SC_PRESCALER(PDB_SC_PRESCALER_DIVIDE_BY_128) | PDB_SC_TRGSEL(PDB_SC_TRGSEL_SOFTWARE_TRIGGER) | PDB_SC_PDBIE_MASK | PDB_SC_MULT(PDB_SC_MULT_BY_1) | (continuous ? PDB_SC_CONT_MASK : 0) | PDB_SC_LDOK_MASK | PDB_SC_PDBEN_MASK; /* Set the counter value (16-bit value) in the counter register */ PDB0_CNT = 0; /* Enable interrupts from PDB0 and set its interrupt priority */ NVICEnableIRQ(PDB0_IRQ_NUMBER, PDB0_INTERRUPT_PRIORITY); }
/*FUNCTION********************************************************************* * * Function Name : PDB_HAL_ConfigTimer * Description : Configure the PDB timer. * *END*************************************************************************/ pdb_status_t PDB_HAL_ConfigTimer(PDB_Type * base, const pdb_timer_config_t *configPtr) { uint32_t sc; if (!configPtr) { return kStatus_PDB_InvalidArgument; } sc = PDB_RD_SC(base); sc &= ~( PDB_SC_LDMOD_MASK | PDB_SC_PDBEIE_MASK | PDB_SC_PRESCALER_MASK | PDB_SC_TRGSEL_MASK | PDB_SC_MULT_MASK | PDB_SC_CONT_MASK | PDB_SC_DMAEN_MASK | PDB_SC_PDBIE_MASK ); sc |= PDB_SC_LDMOD((uint32_t)(configPtr->loadValueMode)); if (configPtr->seqErrIntEnable) { sc |= PDB_SC_PDBEIE_MASK; } sc |= PDB_SC_PRESCALER((uint32_t)(configPtr->clkPreDiv)); sc |= PDB_SC_TRGSEL((uint32_t)(configPtr->triggerInput)); sc |= PDB_SC_MULT((uint32_t)(configPtr->clkPreMultFactor)); if (configPtr->continuousModeEnable) { sc |= PDB_SC_CONT_MASK; } if (configPtr->dmaEnable) { sc |= PDB_SC_DMAEN_MASK; } if (configPtr->intEnable) { sc |= PDB_SC_PDBIE_MASK; } PDB_WR_SC(base, sc); return kStatus_PDB_Success; }
void CIO::startInt() { // Initialise ADC0 conversion to be triggered by the PDB ADC0_CFG1 = ADC_CFG1_ADIV(1) | ADC_CFG1_ADICLK(1) | ADC_CFG1_MODE(1) | ADC_CFG1_ADLSMP; // Single-ended 12 bits, long sample time ADC0_CFG2 = ADC_CFG2_MUXSEL | ADC_CFG2_ADLSTS(2); // Select channels ADxxxb ADC0_SC2 = ADC_SC2_REFSEL(1) | ADC_SC2_ADTRG; // Voltage ref internal, hardware trigger ADC0_SC3 = ADC_SC3_AVGE | ADC_SC3_AVGS(0); // Enable averaging, 4 samples ADC0_SC1A = ADC_SC1_AIEN | 5; // Enable ADC interrupt, use A0 NVIC_ENABLE_IRQ(IRQ_ADC0); // Setup PDB for ADC0 at 24 kHz SIM_SCGC6 |= SIM_SCGC6_PDB; // Enable PDB clock #if F_BUS == 60000000 // 60 MHz for the Teensy 3.5/3.6 PDB0_MOD = 2500; // Timer period for 60 MHz bus #else // 48 MHz for the Teensy 3.1/3.2 PDB0_MOD = 2000; // Timer period for 48 MHz bus #endif PDB0_IDLY = 0; // Interrupt delay PDB0_CH0C1 = PDB_CH0C1_TOS | PDB_CH0C1_EN; // Enable pre-trigger PDB0_SC = PDB_SC_TRGSEL(15) | PDB_SC_PDBEN | PDB_SC_PDBIE | PDB_SC_CONT | PDB_SC_PRESCALER(7) | PDB_SC_MULT(1) | PDB_SC_LDOK; PDB0_SC |= PDB_SC_SWTRIG; // Software trigger (reset and restart counter) #if defined(SEND_RSSI_DATA) // Initialise ADC1 conversion to be triggered by the PDB // Setup interrupt on ADC1 conversion finished // Setup PDB for ADC1 at 24 kHz #endif // Initialise the DAC SIM_SCGC2 |= SIM_SCGC2_DAC0; DAC0_C0 = DAC_C0_DACEN | DAC_C0_DACRFS; // 1.2V VDDA is DACREF_2 digitalWrite(PIN_PTT, m_pttInvert ? HIGH : LOW); digitalWrite(PIN_COSLED, LOW); digitalWrite(PIN_LED, HIGH); }
/*********************************************************************************************** 功能:PDB 初始化 形参:PDB_InitStruct: PDB初始化结构 返回:0 详解:0 ************************************************************************************************/ void PDB_Init(PDB_InitTypeDef * PDB_InitStruct) { uint8_t i; uint32_t p; //参数检查 assert_param(IS_PDB_CONT_MODE(PDB_InitStruct->PDB_ContinuousMode)); assert_param(IS_PDB_LDMOD(PDB_InitStruct->PDB_LoadMode)); //使能PDB时钟 SIM->SCGC6 |= SIM_SCGC6_PDB_MASK ; //清0状态寄存器 PDB0->SC = 0x00; //设置触发源 PDB0->SC |= PDB_SC_TRGSEL(PDB_InitStruct->PDB_TriggerSourceSelect); //连续或者单次工作 (PDB_InitStruct->PDB_ContinuousMode == PDB_CONT_MODE_ONESHOT)?(PDB0->SC &= ~PDB_SC_CONT_MASK):(PDB0->SC |= PDB_SC_CONT_MASK); //计算配置分频数 p = ((CPUInfo.BusClock)*PDB_InitStruct->PDB_Period)/65535; for(i=0;i<8;i++) { if(p/(1<<i) < 40) break; } if(i > 7) i = 7; //设置分频率 PDB0->SC |= PDB_SC_MULT(PDB_MULT_40); PDB0->SC |= PDB_SC_PRESCALER(i); //设置MOD和 IDLY PDB0->MOD = ((PDB_InitStruct->PDB_Period)*(CPUInfo.BusClock/1000))/(40*(1<<i)); PDB0->IDLY = ((PDB_InitStruct->PDB_Period)*(CPUInfo.BusClock/1000))/(40*(1<<i)); //使能PDB PDB0->SC |= PDB_SC_PDBEN_MASK; //PDB开始工作 PDB0->SC |= PDB_SC_LDOK_MASK; //使能软件触发 PDB0->SC |= PDB_SC_SWTRIG_MASK; }
void PDB_Init(PDB_Type *base, const pdb_config_t *config) { assert(NULL != config); uint32_t tmp32; /* Enable the clock. */ CLOCK_EnableClock(s_pdbClocks[PDB_GetInstance(base)]); /* Configure. */ /* PDBx_SC. */ tmp32 = base->SC & ~(PDB_SC_LDMOD_MASK | PDB_SC_PRESCALER_MASK | PDB_SC_TRGSEL_MASK | PDB_SC_MULT_MASK | PDB_SC_CONT_MASK); tmp32 |= PDB_SC_LDMOD(config->loadValueMode) | PDB_SC_PRESCALER(config->prescalerDivider) | PDB_SC_TRGSEL(config->triggerInputSource) | PDB_SC_MULT(config->dividerMultiplicationFactor); if (config->enableContinuousMode) { tmp32 |= PDB_SC_CONT_MASK; } base->SC = tmp32; PDB_Enable(base, true); /* Enable the PDB module. */ }
uint8_t Hw_Trig_Test(void) { // Notes: // PDB settings : continous mode, started by sotware trigger. // This means that once the software "pulls the trigger" by setting a certain bit, the PDB starts counting // and handing out four triggers per cycle of its counter. // PDB settings: CH0_DLY0, CH0_DLY1 , CH1_DLY0, CH1_DLY1 // set to different values to distinguish effect on ADCx_Ry register // need to provide 4 different voltages to convert at two ADC0 and two ADC1 input channels // PDB counter clock prescaled to allow time for printf's and slow down things to they are visible, each trigger. // Using adiclk= BUS , and adidiv/4 to get 12,5MHz on Tower demonstration. // visibility of PDB start trigger is obtained by generating a toggling edge on // GPIOxx with PDBisr set to trigger immediatly at zero value of PDB counter. // Conversion end of each ADC and channel within the ADC ( A,B ) will be done by // toggling second GPIO pin inside ADCisr ( this pin is also reset by PDB isr ) // GPIO PIN to low voltage .. this macro sets the PIN low. PIN_LOW // Initialize PIN1 and PIN2 GPIO outputs Init_Gpio2(); // Disable ADC and PDB interrupts disable_irq(ADC0_irq_no) ; // not ready for this interrupt yet. Plug vector first. disable_irq(ADC1_irq_no) ; // not ready for this interrupt yet. Plug vector first. disable_irq(PDB_irq_no) ; // not ready for this interrupt yet. Plug vector first. // Dynamic interrupt vector modification whilst those interruts are disabled __VECTOR_RAM[73] = (uint32)adc0_isr; // plug isr into vector table in case not there already __VECTOR_RAM[74] = (uint32)adc1_isr; // plug isr into vector table in case not there already __VECTOR_RAM[88] = (uint32)pdb_isr; // plug isr into vector table in case not there already // The System Integration Module largely determines the role of the different ball map locations on Kinetis. // When an external pin is used, the System Integration Module should be consulted and invoked as needed. // System integration module registers start with SIM_ // Turn on the ADC0 and ADC1 clocks as well as the PDB clocks to test ADC triggered by PDB SIM_SCGC6 |= (SIM_SCGC6_ADC0_MASK ); SIM_SCGC3 |= (SIM_SCGC3_ADC1_MASK ); SIM_SCGC6 |= SIM_SCGC6_PDB_MASK ; // Configure System Integration Module for defaults as far as ADC SIM_SOPT7 &= ~(SIM_SOPT7_ADC1ALTTRGEN_MASK | // selects PDB not ALT trigger SIM_SOPT7_ADC1PRETRGSEL_MASK | SIM_SOPT7_ADC0ALTTRGEN_MASK | // selects PDB not ALT trigger SIM_SOPT7_ADC0ALTTRGEN_MASK) ; SIM_SOPT7 = SIM_SOPT7_ADC0TRGSEL(0); // applies only in case of ALT trigger, in which case // PDB external pin input trigger for ADC SIM_SOPT7 = SIM_SOPT7_ADC1TRGSEL(0); // same for both ADCs ///////////////////////////////////////////////////////////////////////////////////////// //PDB configured below 以下是PDB配置 // Configure the Peripheral Delay Block (PDB): // enable PDB, pdb counter clock = busclock / 20 , continous triggers, sw trigger , and use prescaler too PDB0_SC = PDB_SC_CONT_MASK // Contintuous, rather than one-shot, mode | PDB_SC_PDBEN_MASK // PDB enabled | PDB_SC_PDBIE_MASK // PDB Interrupt Enable | PDB_SC_PRESCALER(0x5) // Slow down the period of the PDB for testing | PDB_SC_TRGSEL(0xf) // Trigger source is Software Trigger to be invoked in this file | PDB_SC_MULT(2); // Multiplication factor 20 for the prescale divider for the counter clock // the software trigger, PDB_SC_SWTRIG_MASK is not triggered at this time. PDB0_IDLY = 0x0000; // need to trigger interrupt every counter reset which happens when modulus reached PDB0_MOD = 0xffff; // largest period possible with the slections above, so slow you can see each conversion. // channel 0 pretrigger 0 and 1 enabled and delayed PDB0_CH0C1 = PDB_C1_EN(0x01) | PDB_C1_TOS(0x01) | PDB_C1_EN(0x02) | PDB_C1_TOS(0x02) ; PDB0_CH0DLY0 = ADC0_DLYA ; PDB0_CH0DLY1 = ADC0_DLYB ; // channel 1 pretrigger 0 and 1 enabled and delayed PDB0_CH1C1 = PDB_C1_EN(0x01) | PDB_C1_TOS(0x01) | PDB_C1_EN(0x02) | PDB_C1_TOS(0x02) ; PDB0_CH1DLY0 = ADC1_DLYA ; PDB0_CH1DLY1 = ADC1_DLYB ; PDB0_SC = PDB_SC_CONT_MASK // Contintuous, rather than one-shot, mode | PDB_SC_PDBEN_MASK // PDB enabled | PDB_SC_PDBIE_MASK // PDB Interrupt Enable | PDB_SC_PRESCALER(0x5) // Slow down the period of the PDB for testing | PDB_SC_TRGSEL(0xf) // Trigger source is Software Trigger to be invoked in this file | PDB_SC_MULT(2) // Multiplication factor 20 for the prescale divider for the counter clock | PDB_SC_LDOK_MASK; // Need to ok the loading or it will not load certain regsiters! // the software trigger, PDB_SC_SWTRIG_MASK is not triggered at this time. //PDB configured above 以上是PDB配置 ///////////////////////////////////////////////////////////////////////////////////////// //ADC configured below 以下是ADC配置 // setup the initial ADC default configuration Master_Adc_Config.CONFIG1 = ADLPC_NORMAL | ADC_CFG1_ADIV(ADIV_4) | ADLSMP_LONG | ADC_CFG1_MODE(MODE_16) | ADC_CFG1_ADICLK(ADICLK_BUS); Master_Adc_Config.CONFIG2 = MUXSEL_ADCA | ADACKEN_DISABLED | ADHSC_HISPEED | ADC_CFG2_ADLSTS(ADLSTS_20) ; Master_Adc_Config.COMPARE1 = 0x1234u ; // can be anything Master_Adc_Config.COMPARE2 = 0x5678u ; // can be anything // since not using // compare feature Master_Adc_Config.STATUS2 = ADTRG_HW | ACFE_DISABLED | ACFGT_GREATER | ACREN_ENABLED | DMAEN_DISABLED | ADC_SC2_REFSEL(REFSEL_EXT); Master_Adc_Config.STATUS3 = CAL_OFF | ADCO_SINGLE | AVGE_ENABLED | ADC_SC3_AVGS(AVGS_32); Master_Adc_Config.PGA = PGAEN_DISABLED | PGACHP_NOCHOP | PGALP_NORMAL | ADC_PGA_PGAG(PGAG_64); Master_Adc_Config.STATUS1A = AIEN_OFF | DIFF_SINGLE | ADC_SC1_ADCH(31); Master_Adc_Config.STATUS1B = AIEN_OFF | DIFF_SINGLE | ADC_SC1_ADCH(31); // Configure ADC as it will be used, but becuase ADC_SC1_ADCH is 31, // the ADC will be inactive. Channel 31 is just disable function. // There really is no channel 31. ADC_Config_Alt(ADC0_BASE_PTR, &Master_Adc_Config); // config ADC // Calibrate the ADC in the configuration in which it will be used: ADC_Cal(ADC0_BASE_PTR); // do the calibration // The structure still has the desired configuration. So restore it. // Why restore it? The calibration makes some adjustments to the // configuration of the ADC. The are now undone: // config the ADC again to desired conditions ADC_Config_Alt(ADC0_BASE_PTR, &Master_Adc_Config); // REPEAT for BOTH ADC's. However we will only 'use' the results from // the ADC wired to the Potentiometer on the Kinetis Tower Card. // Repeating for ADC1: ADC_Config_Alt(ADC1_BASE_PTR, &Master_Adc_Config); // config ADC ADC_Cal(ADC1_BASE_PTR); // do the calibration // ADC_Read_Cal(ADC1_BASE_PTR,&CalibrationStore[0]); // store the cal // config the ADC again to default conditions ADC_Config_Alt(ADC1_BASE_PTR, &Master_Adc_Config); // ***************************************************************************** // ADC0 and ADC1 using the PDB trigger in ping pong // ***************************************************************************** // use interrupts, single ended mode, and real channel numbers now: Master_Adc_Config.STATUS1A = AIEN_ON | DIFF_SINGLE | ADC_SC1_ADCH(ADC0_CHANA); Master_Adc_Config.STATUS1B = AIEN_ON | DIFF_SINGLE | ADC_SC1_ADCH(ADC0_CHANB); ADC_Config_Alt(ADC0_BASE_PTR, &Master_Adc_Config); // config ADC0 Master_Adc_Config.STATUS1A = AIEN_ON | DIFF_SINGLE | ADC_SC1_ADCH(ADC1_CHANA); Master_Adc_Config.STATUS1B = AIEN_ON | DIFF_SINGLE | ADC_SC1_ADCH(ADC1_CHANB); ADC_Config_Alt(ADC1_BASE_PTR, &Master_Adc_Config); // config ADC1 // Note that three different balls are being sampled: // ADC0_CHANA not used in this demo, but readings are shown // ADC0_CHANB not used in this demo, but readings are shown // ADC1_CHANA POT channel set the same as the following for demo: 20 // ADC1_CHANB POT channel set the same as the above for demo: 20 // The potentiometer is only on ADC1. That is the one used // to calculate the change of the potentiometer below. while(char_present()) in_char(); // flush terminal buffer printf ("\n\n\n"); printf("********************************************************\n"); printf("* Running ADC0 & ADC1 HARDWARE TRIGGER by PDB *\n"); printf("* The one PDB is triggering both ADC0 and ADC1 *\n"); printf("* ADC1 A,B is the POT. Vary the POT setting. *\n"); printf("* Hit any key to exit (ADC0 readings not used) *\n"); printf("********************************************************\n"); printf ("\n\n"); // Enable the ADC and PDB interrupts in NVIC enable_irq(ADC0_irq_no) ; // ready for this interrupt. enable_irq(ADC1_irq_no) ; // ready for this interrupt. enable_irq(PDB_irq_no) ; // ready for this interrupt. // In case previous test did not end with interrupts enabled, enable used ones. EnableInterrupts ; cycle_flags=0; PDB0_SC |= PDB_SC_SWTRIG_MASK ; // kick off the PDB - just once //The system is now working!!!! The PDB is *continuously* triggering ADC // conversions. Now, to display the results! The line above // was the SOFTWARE TRIGGER... // The demo will continue as long as no character is pressed on the terminal. while(!char_present()) // as long as no operater intervention, keep running this: { while( cycle_flags != ( ADC0A_DONE | ADC0B_DONE | ADC1A_DONE | ADC1B_DONE )); // wait for one complete cycle printf("R0A=%6d R0B=%6d R1A=%6d R1B=%6d POT=%6d\r", result0A,result0B,result1A,result1B, exponentially_filtered_result1); } // disable the PDB PDB0_SC = 0 ; // Disable the ADC and PDB interrupts in NVIC disable_irq(ADC0_irq_no) ; // through with this interrupt. disable_irq(ADC1_irq_no) ; // through with this interrupt. disable_irq(PDB_irq_no) ; // through with this interrupt. printf ("\n\n\n"); printf("********************************************************\n"); printf("* Demonstration ended at operator request *\n"); printf("* ADC0 & ADC1 PDB TRIGGER DEMO COMPLETE *\n"); printf("********************************************************\n"); printf ("\n\n"); return 0; }
} mod >>= prescaler; if(mult>0) { mod /= 10; mod >>= (mult-1); } } setHardwareTrigger(); // trigger ADC with hardware PDB0_IDLY = 1; // the pdb interrupt happens when IDLY is equal to CNT+1 PDB0_MOD = (uint16_t)(mod-1); PDB0_SC = PDB_CONFIG | PDB_SC_PRESCALER(prescaler) | PDB_SC_MULT(mult) | PDB_SC_LDOK; // load all new values PDB0_SC = PDB_CONFIG | PDB_SC_PRESCALER(prescaler) | PDB_SC_MULT(mult) | PDB_SC_SWTRIG; // start the counter! *PDB0_CHnC1 = PDB_CHnC1_TOS_1 | PDB_CHnC1_EN_1; // enable pretrigger 0 (SC1A) NVIC_ENABLE_IRQ(IRQ_PDB); } void ADC_Module::stopPDB() { if (!(SIM_SCGC6 & SIM_SCGC6_PDB)) { // if PDB clock wasn't on, return setSoftwareTrigger(); return; } PDB0_SC = 0;