// STM32 Nucleo-C031C6 반도체 챔버 5단계 배기 및 인터락 제어 시스템 (C031C6 빌드 성공 버전)
// 과목명: 반도체장비제어응용
#include <stdio.h>
#include <stdint.h>
#include <string.h>
// Wokwi 가상 환경 내부 내장 함수 직접 선언
extern uint32_t HAL_GetTick(void);
extern void HAL_Init(void);
// --- STM32C031C6 하드웨어 레지스터 주소 직접 정의 ---
#define RCC_BASE 0x40021000
#define RCC_IOPENR (*(volatile uint32_t*)(RCC_BASE + 0x34))
#define GPIOA_BASE 0x50000000
#define GPIOA_MODER (*(volatile uint32_t*)(GPIOA_BASE + 0x00))
#define GPIOA_PUPDR (*(volatile uint32_t*)(GPIOA_BASE + 0x0C))
#define GPIOA_IDR (*(volatile uint32_t*)(GPIOA_BASE + 0x10))
#define GPIOA_ODR (*(volatile uint32_t*)(GPIOA_BASE + 0x14))
#define GPIOA_BSRR (*(volatile uint32_t*)(GPIOA_BASE + 0x18))
// --- 하드웨어 핀 마스크 정의 ---
#define SERVO_PIN (1 << 0) // PA0: 배기 밸브 서보모터 (주황선)
#define LED_Y_PIN (1 << 6) // PA6: Yellow LED 주의등 (노란선)
#define LED_R_PIN (1 << 7) // PA7: Red LED 비상등 (빨간선)
#define BUZZER_PIN (1 << 8) // PA8: 비상 부저 사이렌 (보라선)
#define INTERLOCK_BTN_PIN (1 << 1) // PA1: 파란색 INTERLOCK 버튼 (파란선)
// --- 시스템 상태 제어 정의 (State Machine) ---
typedef enum {
STATE_RUN = 0,
STATE_WARNING,
STATE_EMERGENCY
} SystemState;
volatile SystemState current_state = STATE_RUN;
volatile uint32_t gas_raw_data = 0; // 가상의 가스 센서 계측 데이터
volatile float gas_ppm = 0.0f; // 변환 수식 연산 결과 (가스 농도)
volatile uint32_t system_time = 0; // 공정 가동 시간 (초)
void init_peripherals(void);
void set_servo_angle(uint8_t angle);
void handle_systick_emulation(void);
// 가상 1ms 지연 함수
void delay_ms(uint32_t ms) {
uint32_t start = HAL_GetTick();
while ((HAL_GetTick() - start) < ms) {
handle_systick_emulation();
}
}
// --- [RTOS 스케줄러 기능 모사] 타이머 스케줄링 시뮬레이터 ---
void handle_systick_emulation(void)
{
static uint32_t last_tick = 0;
static uint32_t tick_count = 0;
static uint32_t led_tick = 0;
uint32_t current_tick = HAL_GetTick();
if (current_tick == last_tick) return;
uint32_t elapsed = current_tick - last_tick;
last_tick = current_tick;
tick_count += elapsed;
// 1) 1초마다 공정 가동 시간 카운트 업 및 가스 누출 시뮬레이션
if (tick_count >= 1000)
{
tick_count = 0;
system_time++;
if (current_state != STATE_EMERGENCY)
{
gas_raw_data += 65; // 초당 가스 누출 속도
}
}
// 2) WARNING 상태일 때 Yellow LED를 0.5초(500ms) 간격으로 깜빡임
if (current_state == STATE_WARNING)
{
led_tick += elapsed;
if (led_tick >= 500)
{
led_tick = 0;
GPIOA_ODR ^= LED_Y_PIN; // Yellow LED 토글 (반전)
}
}
}
// --- 메인 제어 루프 ---
int main(void)
{
HAL_Init();
init_peripherals();
printf("\n==================================================\n");
printf(" [SYSTEM START] C031C6 Bare-metal Gas Control\n");
printf("==================================================\n");
// 초기 구동축(배기 밸브) 0도 정렬
set_servo_angle(0);
while (1)
{
handle_systick_emulation();
// [인터락 체크] 파란 버튼이 눌려 GND(0)가 되면 예외 없이 즉시 비상 단계 진입 (Fail-Safe)
if ((GPIOA_IDR & INTERLOCK_BTN_PIN) == 0)
{
current_state = STATE_EMERGENCY;
}
switch (current_state)
{
case STATE_RUN:
// 가상 데이터를 0~150ppm으로 스케일링
gas_ppm = ((float)gas_raw_data * 3.3f / 1023.0f) * 150.0f;
if (gas_ppm <= 20.0f) {
set_servo_angle(0); // 1단계: 0° 완전 폐쇄
}
else if (gas_ppm > 20.0f) {
current_state = STATE_WARNING; // 주의 단계로 천이
}
// HMI 모니터 콘솔 데이터 실시간 전송 (0.5초 간격)
if (HAL_GetTick() % 500 == 0) {
printf("[STATE_RUN] Time: %lus | Gas: %.2f ppm | Valve: 0 deg\n", system_time, gas_ppm);
delay_ms(1);
}
break;
case STATE_WARNING:
gas_ppm = ((float)gas_raw_data * 3.3f / 1023.0f) * 150.0f;
// 5단계 정밀 비례 유량 제어 (농도에 맞춰 45도 / 90도 / 135도 자동 조절)
if (gas_ppm > 20.0f && gas_ppm <= 50.0f) {
set_servo_angle(45); // 2단계: 45° 미세 배기
if (HAL_GetTick() % 500 == 0) {
printf("[WARNING LEVEL 1] Gas: %.2f ppm | Valve: 45 deg (25%% Open)\n", gas_ppm);
delay_ms(1);
}
}
else if (gas_ppm > 50.0f && gas_ppm <= 90.0f) {
set_servo_angle(90); // 3단계: 90° 중간 배기
if (HAL_GetTick() % 500 == 0) {
printf("[WARNING LEVEL 2] Gas: %.2f ppm | Valve: 90 deg (50%% Open)\n", gas_ppm);
delay_ms(1);
}
}
else if (gas_ppm > 90.0f && gas_ppm <= 130.0f) {
set_servo_angle(135); // 4단계: 135° 강력 배기
if (HAL_GetTick() % 500 == 0) {
printf("[WARNING LEVEL 3] Gas: %.2f ppm | Valve: 135 deg (75%% Open)\n", gas_ppm);
delay_ms(1);
}
}
else if (gas_ppm > 130.0f) {
current_state = STATE_EMERGENCY; // 임계치 초과시 자동 비상 셧다운
}
break;
case STATE_EMERGENCY:
// [최고 우선순위 인터락 세이프티 시퀀스 작동]
set_servo_angle(180); // 5단계: 배기 밸브 100% 완전 전면 개방 (180도)
GPIOA_BSRR = (LED_Y_PIN << 16); // Yellow LED 강제 끄기
GPIOA_BSRR = LED_R_PIN; // Red LED 비상등 완전히 켜기
GPIOA_BSRR = BUZZER_PIN; // 비상 사이렌 강제 가동 (삐-)
// 무한 루프 락을 통한 설비 셧다운 및 데이터 무결성 유지 (Data Hold)
while (1)
{
printf("🚨🚨 [CRITICAL INTERLOCK] EMERGENCY SHUTDOWN ACTIVATED! 🚨🚨\n");
printf("▶ SYSTEM LOG: 챔버 안전 진공 도어 강제 개방 또는 가스 치명적 누출 감지\n");
printf("▶ DEFENSE ACTION: 배기 밸브 [180 deg] 최대 전면 개방 가동\n");
printf("▶ DATA 무결성 HOLD: 인터락 시점 최종 계측 데이터 [ %.2f ppm ] 고정\n", gas_ppm);
printf("==================================================================\n");
delay_ms(1000);
}
break;
}
}
return 0;
}
// --- 소프트웨어 가상 서보모터 PWM 제어 구현 함수 ---
void set_servo_angle(uint8_t angle)
{
uint32_t pulse_width = 1000 + ((uint32_t)angle * 1000 / 180);
GPIOA_BSRR = SERVO_PIN; // High 출력
// C031C6 코어 속도에 맞춘 마이크로초 정밀 딜레이 루프
uint32_t count = pulse_width / 2;
while(count--) { __asm__("NOP"); }
GPIOA_BSRR = (SERVO_PIN << 16); // Low 출력
delay_ms(18); // 50Hz 서보 제어 주기 동기화
}
// --- 하드웨어 입출력 포트 레지스터 직접 세팅 함수 (에러 해결 부위!) ---
void init_peripherals(void)
{
// 1. GPIOA 포트 전원 공급 (C0 시리즈는 IOPENR 레지스터를 사용합니다)
RCC_IOPENR |= (1 << 0);
// 2. 출력 핀 설정 (PA0, PA6, PA7, PA8) ➡️ 구조체 에러 부위를 직접 주소 매칭으로 변경 완료!
GPIOA_MODER &= ~((3 << 0) | (3 << 12) | (3 << 14) | (3 << 16));
GPIOA_MODER |= ((1 << 0) | (1 << 12) | (1 << 14) | (1 << 16));
// 3. 입력 핀 설정 (PA1 인터락 버튼) ➡️ 입력 모드(00) 지정
GPIOA_MODER &= ~(3 << 2);
// PA1 내부 풀업(Pull-up) 저항 설정 (버튼 오픈 시 항상 High 전압 상태 유지)
GPIOA_PUPDR &= ~(3 << 2);
GPIOA_PUPDR |= (1 << 2);
}