#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) #define ADC_UP_BTN_MAX 200 // Button 1 range: 50..200 (center ~93) #define ADC_UP_BTN_MIN 20 #define ADC_DOWN_BTN_MAX 15 // Button 2 range: 0..15 (should read ~0) #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; // 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)); // Default to ALARM STATE (Low = MOSFET OFF) // 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); // Enable pull-up // Setup EXTI on PD6 (CH32V003 has single EXTICR, 2 bits per pin, Port D = 0b11) AFIO->EXTICR |= (0b11 << (FLOW_PIN * 2)); // PD6: Port D=0b11, bits [13:12] EXTI->INTENR |= (1 << FLOW_PIN); // Enable EXTI interrupt on line 6 EXTI->FTENR |= (1 << FLOW_PIN); // Falling edge trigger NVIC_EnableIRQ(EXTI7_0_IRQn); // Enable interrupt in NVIC // 4. Configure BUTTON_ADC Input (PC4 / AIN2) - Analog mode (no pull) GPIOC->CFGLR &= ~(0xF << (4 * BUTTON_PIN)); // Setup ADC ADC1->CTLR2 |= ADC_ADON; // Turn on ADC ADC1->RSQR3 = 2; // PC4 is ADC Channel 2 (AIN2) ADC1->SAMPTR2 = 7 << (3 * 2); // Max sampling time for CH2 ADC1->CTLR2 |= ADC_RSTCAL; // Reset & calibrate 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) --- // Thresholds match actual voltages: no-press ~1023, BTN1 ~93, BTN2 ~0. 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 1: voltage divider 10k+1k -> ~0.30V -> ADC ~93 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_MAX) { // Button 2: direct to GND -> ~0V -> ADC ~0 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); } } } }