implemented display

This commit is contained in:
cpu
2026-05-19 01:49:23 +02:00
parent c0273190d1
commit 45f80bc6b1

View File

@@ -4,33 +4,121 @@
// ==========================================
// 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 FLOW_CONVERSION_FACTOR 5.5
#define DEBOUNCE_DELAY_MS 200
// Fixed ADC limits for the resistor 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 divider (~0.45V -> ADC ~93)
#define ADC_DOWN_BTN_MIN 50
// 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)
uint32_t flow_rate_ml_min = 0;
uint32_t threshold_ml_min = 2000;
uint8_t alarm_active = 0;
// ==========================================
// COMPACT 5x8 FONT (ASCII 32 to 126)
// ==========================================
const uint8_t font5x8[] = {
0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x4f,0x00,0x00, 0x00,0x07,0x00,0x07,0x00, 0x14,0x7f,0x14,0x7f,0x14,
0x24,0x2a,0x7f,0x2a,0x12, 0x23,0x13,0x08,0x64,0x62, 0x36,0x49,0x55,0x22,0x50, 0x00,0x05,0x03,0x00,0x00,
0x00,0x1c,0x22,0x41,0x00, 0x00,0x41,0x22,0x1c,0x00, 0x08,0x2a,0x1c,0x2a,0x08, 0x08,0x08,0x3e,0x08,0x08,
0x00,0x50,0x30,0x00,0x00, 0x08,0x08,0x08,0x08,0x08, 0x00,0x60,0x60,0x00,0x00, 0x20,0x10,0x08,0x04,0x02,
0x3e,0x51,0x49,0x45,0x3e, 0x00,0x42,0x7f,0x40,0x00, 0x42,0x61,0x51,0x49,0x46, 0x21,0x41,0x45,0x4b,0x31,
0x18,0x14,0x12,0x7f,0x10, 0x27,0x45,0x45,0x45,0x39, 0x3c,0x4a,0x49,0x49,0x30, 0x01,0x71,0x09,0x05,0x03,
0x36,0x49,0x49,0x49,0x36, 0x06,0x49,0x49,0x29,0x1e, 0x00,0x36,0x36,0x00,0x00, 0x00,0x56,0x36,0x00,0x00,
0x00,0x08,0x14,0x22,0x41, 0x14,0x14,0x14,0x14,0x14, 0x41,0x22,0x14,0x08,0x00, 0x02,0x01,0x51,0x09,0x06,
0x32,0x49,0x79,0x41,0x3e, 0x7e,0x11,0x11,0x11,0x7e, 0x7f,0x49,0x49,0x49,0x36, 0x3e,0x41,0x41,0x41,0x22,
0x7f,0x41,0x41,0x22,0x1c, 0x7f,0x49,0x49,0x49,0x41, 0x7f,0x09,0x09,0x01,0x01, 0x3e,0x41,0x41,0x51,0x32,
0x7f,0x08,0x08,0x08,0x7f, 0x00,0x41,0x7f,0x41,0x00, 0x20,0x40,0x41,0x3f,0x01, 0x7f,0x08,0x14,0x22,0x41,
0x7f,0x40,0x40,0x40,0x40, 0x7f,0x02,0x04,0x02,0x7f, 0x7f,0x04,0x08,0x10,0x7f, 0x3e,0x41,0x41,0x41,0x3e,
0x7f,0x09,0x09,0x09,0x06, 0x3e,0x41,0x51,0x21,0x5e, 0x7f,0x09,0x19,0x29,0x46, 0x46,0x49,0x49,0x49,0x31,
0x01,0x01,0x7f,0x01,0x01, 0x3f,0x40,0x40,0x40,0x3f, 0x1f,0x20,0x40,0x20,0x1f, 0x3f,0x40,0x38,0x40,0x3f,
0x63,0x14,0x08,0x14,0x63, 0x03,0x04,0x78,0x04,0x03, 0x61,0x51,0x49,0x45,0x43, 0x00,0x7f,0x41,0x41,0x00,
0x02,0x04,0x08,0x10,0x20, 0x00,0x41,0x41,0x7f,0x00, 0x04,0x02,0x01,0x02,0x04, 0x40,0x40,0x40,0x40,0x40,
0x00,0x01,0x02,0x04,0x00, 0x20,0x54,0x54,0x54,0x78, 0x7f,0x48,0x44,0x44,0x38, 0x38,0x44,0x44,0x44,0x20,
0x38,0x44,0x44,0x48,0x7f, 0x38,0x54,0x54,0x54,0x18, 0x08,0x7e,0x09,0x01,0x02, 0x08,0x14,0x54,0x54,0x3c,
0x7f,0x08,0x04,0x04,0x78, 0x00,0x44,0x7d,0x40,0x00, 0x20,0x40,0x44,0x3d,0x00, 0x7f,0x10,0x28,0x44,0x00,
0x00,0x41,0x7f,0x40,0x00, 0x7c,0x04,0x18,0x04,0x78, 0x7c,0x08,0x04,0x04,0x78, 0x38,0x44,0x44,0x44,0x38,
0x7c,0x14,0x14,0x14,0x08, 0x08,0x14,0x14,0x18,0x7c, 0x7c,0x08,0x04,0x04,0x08, 0x48,0x54,0x54,0x54,0x20,
0x04,0x3f,0x44,0x40,0x20, 0x3c,0x40,0x40,0x20,0x7c, 0x1c,0x20,0x40,0x20,0x1c, 0x3c,0x40,0x30,0x40,0x3c,
0x44,0x28,0x10,0x28,0x44, 0x0c,0x50,0x50,0x50,0x3c, 0x44,0x64,0x54,0x4c,0x44, 0x00,0x08,0x36,0x41,0x00,
0x00,0x00,0x7f,0x00,0x00, 0x00,0x41,0x36,0x08,0x00, 0x08,0x08,0x2a,0x1c,0x08, 0x08,0x1c,0x2a,0x08,0x08
};
// ==========================================
// I2C & SSD1306 DRIVER
// ==========================================
#define OLED_ADDR 0x3C
// Resilient I2C Write with Timeouts (prevents MCU freezing if OLED is disconnected)
void I2C_Write(uint8_t addr, uint8_t reg, uint8_t data) {
uint16_t timeout;
timeout = 10000; while((I2C1->STAR2 & I2C_STAR2_BUSY) && --timeout);
if (!timeout) return;
I2C1->CTLR1 |= I2C_CTLR1_START;
timeout = 10000; while(!(I2C1->STAR1 & I2C_STAR1_SB) && --timeout);
I2C1->DATAR = addr << 1;
timeout = 10000; while(!(I2C1->STAR1 & I2C_STAR1_ADDR) && --timeout);
(void)I2C1->STAR2; // Read STAR2 to clear ADDR flag
I2C1->DATAR = reg;
timeout = 10000; while(!(I2C1->STAR1 & I2C_STAR1_TXE) && --timeout);
I2C1->DATAR = data;
timeout = 10000; while(!(I2C1->STAR1 & I2C_STAR1_BTF) && --timeout);
I2C1->CTLR1 |= I2C_CTLR1_STOP;
}
void OLED_Cmd(uint8_t cmd) { I2C_Write(OLED_ADDR, 0x00, cmd); }
void OLED_Data(uint8_t data) { I2C_Write(OLED_ADDR, 0x40, data); }
void OLED_Init(void) {
const uint8_t init_sequence[] = {
0xAE, 0xD5, 0x80, 0xA8, 0x3F, 0xD3, 0x00, 0x40,
0x8D, 0x14, 0x20, 0x02, 0xA1, 0xC8, 0xDA, 0x12,
0x81, 0xCF, 0xD9, 0xF1, 0xDB, 0x40, 0xA4, 0xA6, 0xAF
};
for(int i=0; i<sizeof(init_sequence); i++) OLED_Cmd(init_sequence[i]);
}
void OLED_SetPos(uint8_t x, uint8_t y) {
OLED_Cmd(0xB0 + y);
OLED_Cmd(((x & 0xF0) >> 4) | 0x10);
OLED_Cmd(x & 0x0F);
}
void OLED_Clear(void) {
for(uint8_t y=0; y<8; y++) {
OLED_SetPos(0, y);
for(uint8_t x=0; x<128; x++) OLED_Data(0x00);
}
}
// Print string using 5x8 font (scaled to 6x8 with spacing)
void OLED_Print(uint8_t x, uint8_t y, const char* str) {
OLED_SetPos(x, y);
while(*str) {
if (*str >= 32 && *str <= 126) {
uint16_t idx = (*str - 32) * 5;
for(uint8_t i=0; i<5; i++) OLED_Data(font5x8[idx + i]);
OLED_Data(0x00); // 1px spacing
}
str++;
}
}
// ==========================================
// INTERRUPT HANDLER (Flow Sensor Pulses)
@@ -39,7 +127,7 @@ 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
EXTI->INTFR = (1 << FLOW_PIN);
}
}
@@ -47,44 +135,52 @@ void EXTI7_0_IRQHandler(void) {
// HARDWARE INITIALIZATION
// ==========================================
void Init_Hardware(void) {
// 1. Enable Clocks for GPIOA, GPIOC, GPIOD, ADC, AFIO
// 1. Enable Clocks
RCC->APB2PCENR |= RCC_APB2Periph_GPIOA | RCC_APB2Periph_GPIOC | RCC_APB2Periph_GPIOD | RCC_APB2Periph_ADC1 | RCC_APB2Periph_AFIO;
RCC->APB1PCENR |= RCC_APB1Periph_I2C1;
// [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 I2C Pins (PC1 SDA, PC2 SCL) -> AF Open Drain, 10MHz
GPIOC->CFGLR &= ~(0xFF << 4);
GPIOC->CFGLR |= (0xDD << 4); // 0xD = Alternate Function Open Drain (11) + 10Mhz (01)
// 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. Init Hardware I2C (100kHz @ 48MHz SysClk)
I2C1->CTLR1 |= I2C_CTLR1_SWRST;
I2C1->CTLR1 &= ~I2C_CTLR1_SWRST;
I2C1->CTLR2 = 48;
I2C1->CKCFGR = 240;
I2C1->CTLR1 |= I2C_CTLR1_PE;
// 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);
// 4. ADC Setup (Fixed Clocks & SW Trigger Setup)
RCC->CFGR0 &= ~RCC_ADCPRE;
RCC->CFGR0 |= RCC_ADCPRE_DIV8; // CH32V003 ADC max is 14MHz, 48/8=6MHz is safe
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
GPIOC->CFGLR &= ~(0xF << (4 * BUTTON_PIN)); // Analog mode (no pull)
ADC1->CTLR2 |= ADC_ADON;
ADC1->RSQR3 = 2;
ADC1->RSQR3 = 2; // AIN2
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_EXTSEL; // [FIX] Select SWSTART trigger (111)
ADC1->CTLR2 |= ADC_EXTTRIG; // [FIX] Enable external trigger for SW to work
ADC1->CTLR2 |= ADC_RSTCAL;
while(ADC1->CTLR2 & ADC_RSTCAL);
ADC1->CTLR2 |= ADC_CAL;
while(ADC1->CTLR2 & ADC_CAL);
// 5. Output / Input Pins
GPIOA->CFGLR &= ~(0xF << (4 * ALARM_PIN));
GPIOA->CFGLR |= (GPIO_Speed_10MHz | GPIO_CNF_OUT_PP) << (4 * ALARM_PIN);
GPIOA->BSHR = (1 << (16 + ALARM_PIN));
GPIOD->CFGLR &= ~(0xF << (4 * FLOW_PIN));
GPIOD->CFGLR |= (GPIO_CNF_IN_PUPD) << (4 * FLOW_PIN);
GPIOD->BSHR = (1 << FLOW_PIN);
// 6. EXTI Interrupt
AFIO->EXTICR |= (0b11 << (FLOW_PIN * 2));
EXTI->INTENR |= (1 << FLOW_PIN);
EXTI->FTENR |= (1 << FLOW_PIN);
NVIC_EnableIRQ(EXTI7_0_IRQn);
}
// ==========================================
@@ -97,7 +193,25 @@ uint16_t Read_ADC(void) {
}
void Update_Display(uint32_t current_flow, uint32_t threshold) {
// TODO: Implement OLED I2C drawing here using ch32fun's I2C bitbang
char buf[32];
// Page 0 (Top line)
OLED_Print(16, 0, "CNC WATER FLOW");
// Page 2 (Current Flow)
snprintf(buf, sizeof(buf), "Flow: %4lu mL/m", current_flow);
OLED_Print(0, 3, buf);
// Page 4 (Threshold)
snprintf(buf, sizeof(buf), "Lim : %4lu mL/m", threshold);
OLED_Print(0, 5, buf);
// Page 6 (Status)
if (current_flow >= threshold) {
OLED_Print(0, 7, "Status: SYSTEM OK ");
} else {
OLED_Print(0, 7, "Status: ALARM !!! ");
}
}
// ==========================================
@@ -107,6 +221,10 @@ int main() {
SystemInit();
Init_Hardware();
OLED_Init();
OLED_Clear();
Update_Display(flow_rate_ml_min, threshold_ml_min);
printf("\r\n==================================\r\n");
printf(" CNC Flow Controller Initialized\r\n");
printf("==================================\r\n");
@@ -117,57 +235,46 @@ int main() {
while(1) {
uint32_t current_time = SysTick->CNT;
uint32_t ticks_per_sec = DELAY_US_TIME * 1000000; // = FUNCONF_SYSTEM_CORE_CLOCK
uint32_t ticks_per_sec = DELAY_US_TIME * 1000000;
// --- 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 ---
// Failsafe Logic
if (flow_rate_ml_min >= threshold_ml_min) {
// Flow OK -> Drive ALARM_PIN High (MOSFET ON, CNC OK)
GPIOA->BSHR = (1 << ALARM_PIN);
GPIOA->BSHR = (1 << ALARM_PIN); // Alarm OK (MOSFET ON)
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));
} } else {
GPIOA->BSHR = (1 << (16 + ALARM_PIN)); // Alarm Triggered (MOSFET OFF)
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) ---
// --- 2. BUTTON HANDLING ---
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);