/* Der SW-Ausgang (GPIO0) wird aktiviert, wenn Fin an GPIO1 >= 7 MHz und wieder abgeschaltet bei 6,9 MHz. Beide Werte sind einstellbar. Das Abfrageintervall betraegt 1 ms, weshalb der PWM0-Zaehler bei >= 65 MHz ueberlaufen und falsche Ergebnisse liefern wuerde. Daher wird das Eingangssignal PWM0_B mit PWM0_VORTEILER halbiert. Die max. Eingangsfrequenz beträgt deshalb 130 MHz. 2023-05-19 Michael Nowak http://www.mino-elektronik.de Alle Angaben ohne Gewaehr ! */ #include #define BIT(x) (1< ms #define PWM0_VORTEILER 2 // bis 130 MHz @ 1 ms #define LOCAL_LED 25 #define SW_AUSGANG 0 // GPIO0 auf Pico-Board Pin0 #define FIN_PIN 1 // GPIO1 auf Pico-Board Pin1 #define DEF_SYSCLOCK 300000000 // default #define MHZ_12 12000000 // default #define MHZ_FAKTOR 1000000 // 1e6 #define PIO_TIME_DIV 4 // PIO-divider #define REF_DIV 2 // PLL-Schrittweite 6 MHZ @ 12 MHz bzw. 5 MHz @ 10 MHz #define PLL_DIV1 5 // force high F_vco #define PLL_DIV2 2 // default = 2; 1 => turbo-mode uint32_t SystemCoreClock = (DEF_SYSCLOCK); // Vorgabewert uint32_t F_xtal = MHZ_12; // onboard default volatile uint16_t PWM0_alt, PWM0_eff, PWM0_reg; volatile int temp; // ms-Zähler zur Kontrolle void kurz_warten(void) // fuer kurze Verzoegerungen { volatile int i = 1000; while(i--); } // XOSC aktivieren, SystemCoreClock setzen und Taktsignale aktivieren void init_clock(void) { volatile int i; VREG_AND_CHIP_RESET_SET-> VREG = 15<VREG & VREG_AND_CHIP_RESET_VREG_ROK_Msk)); kurz_warten(); // Vreg abwarten XOSC->CTRL = (XOSC_CTRL_FREQ_RANGE_1_15MHZ << XOSC_CTRL_FREQ_RANGE_Pos); XOSC->STARTUP = 100; // ca. 2 ms bei 12 MHz XOSC_SET->CTRL = (XOSC_CTRL_ENABLE_ENABLE << XOSC_CTRL_ENABLE_Pos); while (!(XOSC->STATUS & XOSC_STATUS_STABLE_Msk)); RESETS_CLR->RESET = RESETS_RESET_pll_sys_Msk; while (!(RESETS->RESET_DONE & RESETS_RESET_pll_sys_Msk)); PLL_SYS->CS = (REF_DIV << PLL_SYS_CS_REFDIV_Pos); // PLL-Aufloesung vorgeben PLL_SYS->FBDIV_INT = (SystemCoreClock*PLL_DIV2*REF_DIV)/(F_xtal/PLL_DIV1); // PLL-VCO typ. 1310 MHz // eff. Taktfrequenz ermitteln PLL_SYS->PRIM = (PLL_DIV1 << PLL_SYS_PRIM_POSTDIV1_Pos) | (PLL_DIV2 << PLL_SYS_PRIM_POSTDIV2_Pos); SystemCoreClock = PLL_SYS->FBDIV_INT *(F_xtal/REF_DIV) / (PLL_DIV1*PLL_DIV2); PLL_SYS_CLR->PWR = PLL_SYS_PWR_VCOPD_Msk | PLL_SYS_PWR_PD_Msk; while (!(PLL_SYS->CS & PLL_SYS_CS_LOCK_Msk)); PLL_SYS_CLR->PWR = PLL_SYS_PWR_POSTDIVPD_Msk; CLOCKS->CLK_REF_CTRL = (CLOCKS_CLK_REF_CTRL_SRC_xosc_clksrc << CLOCKS_CLK_REF_CTRL_SRC_Pos); CLOCKS->CLK_SYS_CTRL = (CLOCKS_CLK_SYS_CTRL_AUXSRC_clksrc_pll_sys << CLOCKS_CLK_SYS_CTRL_AUXSRC_Pos) | (CLOCKS_CLK_SYS_CTRL_SRC_clksrc_clk_sys_aux << CLOCKS_CLK_SYS_CTRL_SRC_Pos); CLOCKS->CLK_PERI_CTRL = CLOCKS_CLK_PERI_CTRL_ENABLE_Msk | (CLOCKS_CLK_PERI_CTRL_AUXSRC_clk_sys << CLOCKS_CLK_PERI_CTRL_AUXSRC_Pos); CLOCKS->CLK_ADC_CTRL = CLOCKS_CLK_ADC_CTRL_ENABLE_Msk | (CLOCKS_CLK_ADC_CTRL_AUXSRC_xosc_clksrc << CLOCKS_CLK_ADC_CTRL_AUXSRC_Pos); // enable GPIO RESETS_CLR->RESET = RESETS_RESET_io_bank0_Msk | RESETS_RESET_pads_bank0_Msk; while((RESETS->RESET_DONE & (RESETS_RESET_io_bank0_Msk | RESETS_RESET_pads_bank0_Msk)) != (RESETS_RESET_io_bank0_Msk | RESETS_RESET_pads_bank0_Msk)); } // ms-Raster fuer Messablauf erzeugen // 1 kHz per PWM1 void init_PWM_01(void) { RESETS_CLR->RESET = RESETS_RESET_pwm_Msk; while (!(RESETS->RESET_DONE & RESETS_RESET_pwm_Msk)); IO_BANK0->GPIO0_CTRL = IO_BANK0_GPIO0_CTRL_FUNCSEL_sio_0; // Signal-Ausgang IO_BANK0->GPIO25_CTRL = IO_BANK0_GPIO0_CTRL_FUNCSEL_sio_0; // lokal LED-Ausgang SIO->GPIO_OE_SET = BIT(LOCAL_LED) | BIT(SW_AUSGANG); PWM->CH1_DIV = 100 << PWM_CH0_DIV_INT_Pos; // Vorteiler / 100 PWM->CH1_TOP = SystemCoreClock/100/MS1_TEILER-1; // 1 kHz PWM->CH1_CSR = 1; // enable ms-timer // Fin mit PWM->CH0_CTR zaehlen IO_BANK0->GPIO1_CTRL = IO_BANK0_GPIO1_CTRL_FUNCSEL_pwm_b_0; // Eingang PWM0B PWM->CH0_DIV = PWM0_VORTEILER << PWM_CH0_DIV_INT_Pos; // Fin halbieren PWM->CH0_CSR = PWM_CH0_CSR_DIVMODE_rise << PWM_CH0_CSR_DIVMODE_Pos | // +Flanke PWM_CH0_CSR_EN_Msk; } // stabile Zustaende ohne HW-Reset void reset_io() { // reset diverse Peripherie beim programmstart RESETS_SET->RESET = RESETS_RESET_io_bank0_Msk | RESETS_RESET_pwm_Msk | RESETS_RESET_pads_bank0_Msk; SIO->GPIO_OE_CLR = 0xffffffff; // alle loeschen kurz_warten(); } int main() { reset_io(); init_clock(); init_PWM_01(); while(1) { // naechste Millisekunde abwarten if(PWM->INTR & BIT(MS1_TIMER)) { // 1 kHZ Raster per PWM_CH1 PWM->INTR = BIT(MS1_TIMER); // clear flag PWM0_reg = PWM->CH0_CTR; PWM0_eff = (PWM0_reg - PWM0_alt) * PWM0_VORTEILER; PWM0_alt = PWM0_reg; temp++; // testweise if(PWM0_eff >= FIN_SCHWELLE) { // Schwelle erreicht SIO->GPIO_OUT_SET = BIT(LOCAL_LED) | BIT(SW_AUSGANG); // on } else if(PWM0_eff <= FIN_SCHWELLE-HYSTERESE) { // Schwelle unterschritten SIO->GPIO_OUT_CLR = BIT(LOCAL_LED) | BIT(SW_AUSGANG); // off } } } }