먼저, 산업 경제학의 철칙부터 살펴 보겠습니다. 시스템 가동 중단은 분당 수백에서 수천 달러에 이르는 엄청난 비용을 발생시킵니다. 그래서 PLC와 같은 산업용 부품들은 이러한 가동 중단을 최소화할 수 있게 유지보수가 쉽도록 설계되어 있습니다. 이러한 사실은 래더 로직(LL)과 같은 IEC 61131-3 표준 언어에도 그대로 적용됩니다. 이 언어들은 로직을 시각적으로 표현함으로써 가독성을 높였으며, 기술자가 다루기 쉽도록 로직 상태를 거의 실시간으로 확인할 수 있게 설계되어 있습니다.
이미 성능이 검증된 PLC 언어를 고수해야 하는 분명한 이유들은 차고 넘칩니다. 하지만, 여러분이 여전히 sprintf()를 사용하는 자부심 강한 K&R C 프로그래머라면, PLC와 산업용 PC 간의 경계가 무너져 통합된 최신 기술을 활용하고 싶어질 것입니다. 나아가 데이터 공유(클라우드), 사이버 보안, 편리한 업데이트가 강조되는 최신 네트워크 장치의 확장성과 기능도 이용해보고 싶을 것입니다.
핵심 요약
- PLC 스캔 사이클은 입력을 읽고, 사용자 프로그램을 실행한 뒤, 출력을 기록하는 과정을 순차적으로 진행합니다. 이 과정에서 사용자 프로그램이 실제 입출력을 직접 읽거나 쓰지는 않으며, 이는 런타임 오류를 최소화하기 위한 핵심 원칙입니다.
- 일부 최신 PLC는 C 언어로 프로그래밍할 수 있을 뿐만 아니라, PLC와 PC의 경계도 모호해지고 있습니다. Revolution Pi와 같은 일부 하이브리드 PLC는 자유도가 굉장히 높아서, 많은 다양한 프로그래밍 언어를 지원합니다.
- 가능하다면 PLC 전용으로 개발된 래더 로직(LL)과 구조화 텍스트(ST) 등의 IEC 61131-3 표준 언어를 사용해야 합니다. 그러나, 프로그래밍 경험을 그대로 살릴 수 있고, 클라우드 저장소 및 사이버 보안 등 점점 보편화되는 인더스트리 4.0의 네트워크 환경을 활용할 수 있다는 점에서 C 언어 역시 고려할 가치가 있습니다.
이 글은 산업 자동화 및 제어에 대한 현장 가이드 시리즈의 일부입니다.
위치: Program It → PLC
난이도:
학생 - 난이도 설명 참조
저자: Aaron Dahlen | 전기공학 석사 | 시니어 애플리케이션 엔지니어, 디지키
최종 업데이트: 2026년 3월 06일
Main 함수 내부에서의 PLC 스캔 사이클
main() 함수를 최대한 깔끔하게 정리하기 위해 구조를 잡아보겠습니다. 예를 들어, 대표적인 래치 회로를 다음과 같이 표현한 코드가 여러분이 보기엔 쓸만해 보이나요? 그림 1은 이 아이디어를 시각적으로 보여주고 있습니다.
void loop( ) {
readInputs( );
if (SS1 & PB_START & !PB_STOP) {
FAN = ON;
PL_GREEN = ON;
PL_RED = OFF;
}
if (!SS1 | PB_STOP) {
FAN = OFF;
PL_GREEN = OFF;
PL_RED = ON;
}
writeOutputs( );
}
가능한 한 입출력 함수 호출을 추상화하여 감추고, 입출력 변수의 이름은 역할을 바로 알아볼 수 있도록 단순하게 붙였습니다. 여러 면에서 투박한 (죄송합니다, 완벽한) C 언어를 PLC의 구조화 텍스트(ST)에 더 가깝게 다듬었습니다. 이 코드 목록에 내포된 X 매크로 기법을 다룬 이 선행학습 글을 참고해 주시기 바랍니다. 해당 글에서는 코드를 이렇게 단순하게 만들 수 있었던 추상화에 대해 설명합니다. 래더 로직 관점에서 PLC 메모리 구조를 설명하는 이 글도 함께 참고할 만합니다.
그림 1. 시계에 비유한 PLC 프로그램 스캔 사이클 중 수행되는 동작.
PLC 스캔 사이클 패턴
이 코드는 그림 1과 같이 루프가 시작될 때 입력이 고정되고 끝날 때 출력이 반영되는 PLC 방식의 스캔 사이클 구조를 전제로 작성되었습니다. 어쩌면 여러분은 이미 이 방식을 사용해 입력의 스냅샷을 찍은 뒤 이를 바탕으로 프로그램을 실행하고 계실지 모릅니다. 루프가 도는 동안에는 동일한 데이터 세트를 기준으로 코드가 돌아가야 하므로 이는 매우 중요한 구조입니다. 예를 들어, 루프의 시작 부분에서 PB_START 변수를 처리하는 코드가 있고, 루프가 끝날 때쯤 동일한 PB_START 변수를 처리하는 추가 코드가 있다고 가정해 보겠습니다. 만약 루프 중간에 PB_START 값이 바뀌면 프로그램에 찾아내기 힘든 미묘한 버그가 발생할 수 있습니다.
또한, PLC 스캔 사이클 구조는 특정 유형의 버그를 방지해 줍니다. 예를 들어, 루프가 시작하는 코드의 윗부분에 있는 어떤 조건문 안에서 MOTOR_1 값이 업데이트되었는데, 그 아래쪽 코드에서 프로그래머가 이전 MOTOR_1 값을 사용했다고 가정해 보겠습니다. 이렇게 되면, 조건문의 구조에 따라 출력이 입력에 영향을 받을 수도 있고 아닐 수도 있어서, 시스템의 동작을 예측할 수 없게 됩니다. 재현이 어려운 이 비결정적 버그(non-deterministic bug)는 발생 조건을 찾아내기가 매우 어렵기 때문에, 문제를 해결하는 데 상당한 애를 먹을 수 있습니다.
모듈화와 캡슐화
가장 핵심적인 교훈은 우리가 입력과 출력을 직접 처리하지 않는다는 점입니다. 대신, 프로그램이 (입력의 스냅샷을 저장한) 내부 메모리 구조를 바탕으로 처리합니다. readInputs()와 writeOutputs() 함수 또는 메서드는 코드 곳곳에 흩어져 지저분해질 수 있는 모든 digitalRead()와 digitalWrite() 함수들을 한곳으로 모아 줍니다. 이렇게 함수들을 한곳으로 모으면 예측하기 쉬운 결정론적 동작을 보장합니다.
기술 팁: PLC 스캔 사이클 구조가 가장 빠르거나 자원을 가장 효율적으로 쓰는 프로그래밍 방법이 아닌 것은 사실입니다. 하지만 프로그래밍의 위험성들을 고려한다면, 이 방식을 굳이 피할 이유가 없습니다.
만약 초고속 처리가 필요하다면, volatile 변수를 사용해 마이크로컨트롤러의 인터럽트 서비스 루틴(ISR)을 구현하고 main()과 ISR 사이에 원자적 데이터 전송(atomic data transfer)이 되도록 처리해야 합니다.
기술 팁: 그러나 여전히 프로그램에 버그가 발생할 수 있습니다. 보다 자세한 내용은 PLC 동작의 기본 규칙인 '마지막에 적힌 명령이 우선한다(last rung wins)'는 개념을 참고바랍니다. 예컨대 MOTOR_1과 같은 내부 변수를 변경하는 코드가 여러 줄이면 예상치 못한 오작동이 일어날 수 있습니다. 명확히 하자면, 이러한 유형의 버그가 프로그래머 입장에서는 뜻밖이겠지만, 시스템 자체는 정해진 규칙대로 동작한 결과이므로 비교적 쉽게 문제를 해결할 수 있습니다.
입출력 변수명의 단일 관리
선행학습 글에서 다룬 내용을 다시 반복하지는 않겠습니다. 하지만 프로그램의 가독성, 그리고 readInputs() 및 writeOutputs() 함수/메서드와 함께 이름이 붙은 변수를 사용하는 것이 얼마나 중요한지 아무리 강조해도 지나치지 않습니다. 입출력 목록을 한곳에 모아 관리하는 X 매크로 기법은 코드를 깨끗하게 유지하기 위한 핵심 요소입니다. 버그를 방지하려면 PB_START와 MOTOR_1 같은 변수를 단 한 번만 정의해야 합니다. 그런 다음 포트를 초기화하고 읽는 작업과 출력에 반영하는 반복적인 작업들을 자동화합니다.
기술 팁: 프로그래밍 교과서에 나오는 내용과는 달리, PLC 프로그래밍에는 전역 변수가 사용됩니다. 메인 루프를 복잡하지 않고 깔끔하게 유지할 수 있다는 점만으로도 전역 변수를 사용할 이유는 충분합니다. 전역 변수는 여전히 주로 입출력과 관련하여 필요한 경우에만 제한적으로 사용되니 걱정하지 않아도 됩니다.
다음 단계
PLC 및 C 프로그래밍에 대해 보다 자세히 알아보기 위해 다음을 실천해 보십시오:
- PLC 애플리케이션의 수명은 수십 년 단위로 측정된다는 점을 염두에 두고, 여러분이 작성한 코드의 수명 주기를 되돌아보십시오. 본인의 이해를 고집하지 말고, 미래에 PLC를 유지보수할 기술자들과 미래에 여러분의 코드를 수정하게 될 엔지니어들의 요구사항를 감안하여 적절한 타협점을 찾아 보십시오.
- 바람직한 코드 구조와 기법을 파악하기 위해 IEC 61131-3 표준 언어를 학습하십시오. 그리고 우수 사례들을 여러분의 C 프로그램에 적용해 보십시오.
- 여러분의 임베디드 애플리케이션에 맞는 스타일 가이드를 만들고 지속적으로 다듬어 나가십시오.
- 여기에 타이머도 함께 결합해 보십시오. 이 게시글은 구조화 텍스트에서 차용한 기법을 보여줍니다.
글을 맺으며
스캔 사이클은 PLC 프로그래밍의 강력한 특징입니다. 입출력을 직접 처리하지 않고 스캔 시점의 데이터만 고정해 처리하는 '스냅샷 모델’은 예기치 못한 버그를 방지하는 데 크게 기여합니다. 또한 프로그램을 깔끔하게 정리해 주어 코드를 읽고 유지보수하기 훨씬 편하게 만듭니다.
다시 한번 강조하고 싶은 부분은 IEC 61131-3 언어들과 이 언어들이 자체적으로 제공하는 문제 해결 도구의 힘입니다. 이들은 시스템 문제를 해결하는 데 필요한 프로그래밍 경험의 진입 장벽을 낮춰줍니다. 이는 설비가 언젠가 고장 났을 때 기업의 최종 수익에 매우 중요한 영향을 미칠 수 있습니다. 반면, 기존 PLC들은 도커(Docker)를 거의 지원하지 않기 때문에, C 또는 C에서 파생된 고급 언어들은 네트워킹 관점에서 더 유리할 수 있습니다.
감사합니다.
APDahlen
산업 제어 시스템에 대해 더 알아보기
이 글이 도움이 되셨다면, 다음 주제들도 함께 살펴보시기 바랍니다:
디지키 검색
- 전체 카탈로그: 산업 자동화 및 제어
저자 소개
미합중국 해안경비대(USCG) 소령(LCDR)으로 전역한 Aaron Dahlen은 디지키에서 애플리케이션 엔지니어로 근무하고 있습니다. 27년간의 군 복무 동안 기술자 및 엔지니어로서 쌓아온 그 만의 전자 및 자동화에 대한 지식은 12년간의 교단을 통해 (상호 연계되어) 더욱 향상되었습니다. 미네소타 주립대학, Mankato에서 전기공학 석사(MSEE) 학위를 받은 Dahlen은 ABET(Accreditation Board for Engineering and Technology, 미국 공학 기술 인증 위원회) 공인 전기공학 과정을 가르치고, EET(Electrical Engineering Technology, 전기공학 기술) 과정의 프로그램 조정관으로 일했으며, 군 전자 기술자에게 부품 수준의 수리에 대해 가르쳤습니다. 미네소타 주 북부의 집으로 돌아와 이런 류의 연구와 글쓰기를 즐기고 있습니다.
