#include "ch32fun.h" #include // ========================================== // PIN CONFIGURATION (CH32V003J4M6 - SOP8) // ========================================== // Package Pin 1: PD6 (FLOW_PULSE_MCU) -> EXTI Input // Package Pin 3: PA2 (ALARM_TRIGGER) -> GPIO Output // Package Pin 5: PC1 (I2C_SDA) -> Bitbang or HW I2C // Package Pin 6: PC2 (I2C_SCL) -> Bitbang or HW I2C // Package Pin 7: PC4 (BUTTON_ADC) -> ADC Channel 2 Input // Package Pin 8: PD1 (SWIO) -> Programming & Logging #define FLOW_PIN 6 // PD6 = bit 6 of GPIOD #define ALARM_PIN 2 // PA2 = bit 2 of GPIOA #define BUTTON_PIN 4 // PC4 = bit 4 of GPIOC // Constants #define FLOW_CONVERSION_FACTOR 5.5 // L/min = Hz / 5.5 (Adjust for ZJ-S401) // Button ladder #define ADC_UP_BTN_MAX 15 // UP connects direct to GND -> ~0V -> ADC ~0 #define ADC_UP_BTN_MIN 0 #define ADC_DOWN_BTN_MAX 200 // DOWN connects via 1k -> ~0.45V -> ADC ~93 #define ADC_DOWN_BTN_MIN 50 #define DEBOUNCE_DELAY_MS 200 // Globals volatile uint32_t pulse_count = 0; uint32_t flow_rate_ml_min = 0; // Stored in mL/min for integer precision uint32_t threshold_ml_min = 2000; // Default: 2.0 L/min uint8_t alarm_active = 0; // Track state to prevent log spam (0 = OK, 1 = ALARM) // ========================================== // INTERRUPT HANDLER (Flow Sensor Pulses) // ========================================== void EXTI7_0_IRQHandler(void) __attribute__((interrupt)); void EXTI7_0_IRQHandler(void) { if (EXTI->INTFR & (1 << FLOW_PIN)) { pulse_count++; EXTI->INTFR = (1 << FLOW_PIN); // Clear interrupt flag } } // ========================================== // HARDWARE INITIALIZATION // ========================================== void Init_Hardware(void) { // 1. Enable Clocks for GPIOA, GPIOC, GPIOD, ADC, AFIO RCC->APB2PCENR |= RCC_APB2Periph_GPIOA | RCC_APB2Periph_GPIOC | RCC_APB2Periph_GPIOD | RCC_APB2Periph_ADC1 | RCC_APB2Periph_AFIO; // [NEW FIX] Set ADC Clock Prescaler to DIV8 (48MHz / 8 = 6MHz). Must be <= 14MHz! RCC->CFGR0 &= ~RCC_ADCPRE; // Clear prescaler bits RCC->CFGR0 |= RCC_ADCPRE_DIV8; // Set to divide by 8 // 2. Configure ALARM_TRIGGER Output (PA2) - Push-Pull GPIOA->CFGLR &= ~(0xF << (4 * ALARM_PIN)); GPIOA->CFGLR |= (GPIO_Speed_10MHz | GPIO_CNF_OUT_PP) << (4 * ALARM_PIN); GPIOA->BSHR = (1 << (16 + ALARM_PIN)); // 3. Configure FLOW_PULSE_MCU Input (PD6) - Pull-up GPIOD->CFGLR &= ~(0xF << (4 * FLOW_PIN)); GPIOD->CFGLR |= (GPIO_CNF_IN_PUPD) << (4 * FLOW_PIN); GPIOD->BSHR = (1 << FLOW_PIN); AFIO->EXTICR |= (0b11 << (FLOW_PIN * 2)); EXTI->INTENR |= (1 << FLOW_PIN); EXTI->FTENR |= (1 << FLOW_PIN); NVIC_EnableIRQ(EXTI7_0_IRQn); // 4. Configure BUTTON_ADC Input (PC4 / AIN2) - Analog mode GPIOC->CFGLR &= ~(0xF << (4 * BUTTON_PIN)); // Setup ADC ADC1->CTLR2 |= ADC_ADON; ADC1->RSQR3 = 2; ADC1->SAMPTR2 = 7 << (3 * 2); // [NEW FIX] Must configure EXTTRIG and EXTSEL for SWSTART to actually work ADC1->CTLR2 |= ADC_EXTSEL; // Select SWSTART trigger (111) ADC1->CTLR2 |= ADC_EXTTRIG; // Enable external trigger ADC1->CTLR2 |= ADC_RSTCAL; while(ADC1->CTLR2 & ADC_RSTCAL); ADC1->CTLR2 |= ADC_CAL; while(ADC1->CTLR2 & ADC_CAL); } // ========================================== // HELPER FUNCTIONS // ========================================== uint16_t Read_ADC(void) { ADC1->CTLR2 |= ADC_SWSTART; while(!(ADC1->STATR & ADC_EOC)); return ADC1->RDATAR; } void Update_Display(uint32_t current_flow, uint32_t threshold) { // TODO: Implement OLED I2C drawing here using ch32fun's I2C bitbang } // ========================================== // MAIN LOOP // ========================================== int main() { SystemInit(); Init_Hardware(); printf("\r\n==================================\r\n"); printf(" CNC Flow Controller Initialized\r\n"); printf("==================================\r\n"); printf("[INFO] Active Threshold: %lu mL/min\r\n", threshold_ml_min); uint32_t last_time = SysTick->CNT; uint32_t last_button_time = 0; while(1) { uint32_t current_time = SysTick->CNT; uint32_t ticks_per_sec = DELAY_US_TIME * 1000000; // = FUNCONF_SYSTEM_CORE_CLOCK // --- 1. EVALUATE FLOW EVERY 1 SECOND --- if ((current_time - last_time) >= ticks_per_sec) { last_time = current_time; // Copy and reset pulse count safely NVIC_DisableIRQ(EXTI7_0_IRQn); uint32_t hz = pulse_count; pulse_count = 0; NVIC_EnableIRQ(EXTI7_0_IRQn); // Calculate Flow: L/min = Hz / 5.5 => mL/min = (Hz * 10000) / 55 flow_rate_ml_min = (hz * 10000) / 55; // --- 2. FAILSAFE LOGIC & LOGGING --- if (flow_rate_ml_min >= threshold_ml_min) { // Flow OK -> Drive ALARM_PIN High (MOSFET ON, CNC OK) GPIOA->BSHR = (1 << ALARM_PIN); if (alarm_active == 1) { alarm_active = 0; printf("[INFO] Flow restored: %lu mL/min. Alarm CLEARED.\r\n", flow_rate_ml_min); } } else { // Flow Low -> Drive ALARM_PIN Low (MOSFET OFF, CNC Alarm) GPIOA->BSHR = (1 << (16 + ALARM_PIN)); if (alarm_active == 0) { alarm_active = 1; printf("[WARN] Low flow detected: %lu mL/min (Thr: %lu). Alarm TRIGGERED!\r\n", flow_rate_ml_min, threshold_ml_min); } } Update_Display(flow_rate_ml_min, threshold_ml_min); } // --- 3. BUTTON HANDLING (ADC Resistor Ladder) --- uint16_t adc_val = Read_ADC(); if ((current_time - last_button_time) > (DEBOUNCE_DELAY_MS * DELAY_US_TIME * 1000)) { if (adc_val >= ADC_UP_BTN_MIN && adc_val <= ADC_UP_BTN_MAX) { // Button UP pressed threshold_ml_min += 100; last_button_time = current_time; printf("[SET] Threshold increased to: %lu mL/min\r\n", threshold_ml_min); Update_Display(flow_rate_ml_min, threshold_ml_min); } else if (adc_val >= ADC_DOWN_BTN_MIN && adc_val <= ADC_DOWN_BTN_MAX) { // Button DOWN pressed if (threshold_ml_min >= 100) threshold_ml_min -= 100; last_button_time = current_time; printf("[SET] Threshold decreased to: %lu mL/min\r\n", threshold_ml_min); Update_Display(flow_rate_ml_min, threshold_ml_min); } } } }