Files
Flow_Controller/firmware/main.c
2026-05-19 01:34:33 +02:00

178 lines
6.6 KiB
C

#include "ch32fun.h"
#include <stdio.h>
// ==========================================
// 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);
}
}
}
}