본 자료는
1. SmartGPIO의 입력 성능 측정이 필요한 이유
2. SmartGPIO의 입력 신호 처리 방식에 따른 장단점
3. 입력 확인 주기(Input Scan Cycle Time)가 가변인 이유
4. 장치별 응용 프로그램 환경에 따른 최대 입력 신호 측정
4-1. 입력 확인 주기(Input Scan Cycle Time)를 확인하기 위한 처리 방식
4-2. 테스트 방법 설명
5. 제품별 테스트 결과
순서로 진행합니다.
출력 성능의 측정은 출력 신호의 측정으로 쉽게 확인이 가능하나 입력 성능의 측정은 입력 신호가 인가되어도 입력 처리를 했는지 확인해야 하는 문제가 있습니다. 또한, 입력 처리는 CPU의 가용 상태에 따라 입력 처리 시간이 달라질 수 있어 입력 신호를 놓칠 수 있습니다. 이러한 상태를 확인하기 위해 작성된 장치 응용 프로그램 환경에서 처리 가능한 입력 신호의 폭(Width)을 확인하는 측정이 필요합니다. 본 자료는 입력 신호 처리 방법 5가지를 소개하고, 그중 4가지를 예시로 테스트 방법을 제시하고 측정합니다.
2. SmartGPIO의 입력 신호 처리 방식에 따른 장단점
SmartGPIO의 입력 신호를 처리하는 방법을 CASE별로 나눠 각 방식에 따른 장단점을 설명하고 있습니다.
CASE-1. Port Change Event(OnPortDatasChange)방식
포트의 값이 변경될 때마다 호출되는 이벤트로 사용자 인터페이스의 반응 처리를 자유롭게 처리하면서 입력 신호의 변화를 처리하는 경우 사용됩니다.
장점 |
- 사용자 인터페이스 응답이 가능하고 코드 처리가 간단함 - 응답 성능은 OnPortDatasChangeCapture 이벤트 방식에 비해 좋음 |
---|---|
단점 |
- Polling 방식보다 입력 처리가 약간 늦을 수 있음 - 연속적인 신호나 처리 코드의 복잡도가 높은 경우 입력 신호가 누락될 수 있음 |
포트의 값이 변경될 때마다 Queue에 카운트를 추가하고, Queue의 데이터 수만큼 이벤트가 호출됩니다. Queue에 빠르게 추가되는 데이터를 놓치지 않고 최대한 정밀하게 입력 받기 위해
Port Change Scan Process의 Scan Speed(CPU에 할당되는 시간)를 조절하는 과정이 필요합니다. Queue에 데이터가 추가되고 있는 시점에 Scan Speed를 늘리고(High),
데이터의 출력이 종료되어 Queue에 데이터가 추가되지 않는 시점에 Scan Speed를 줄여(Low) CPU 할당률을 기본 상태로 돌려야 합니다.
이러한 Scan Speed는 SetPortWatchIdleInterval()와 SetPortCaptureScanSpeedAutoStart() 메소드로 조절할 수 있습니다. SetPortWatchIdleInterval() 메소드의 경우 Queue의 상태를 판단하여
Scan Speed를 High나 Low로 직접 조절해야 하지만, SetPortCaptureScanSpeedAutoStart() 메소드의 경우 Queue의 상태를 내부적으로 판단해주어 Scan Speed를 자동으로 조절해주는 편리함이 있습니다.
장점 | - 일정 시간동안 빠른 속도의 연속적인 입력 신호를 처리하기에 적합하여 입력 신호의 누락이 거의 발생되지 않음 |
---|---|
단점 |
- OnPortDatasChange 이벤트(CASE-1)에 비해 응답 성능이 떨어짐 - 카운팅 용도로만 사용할 수 있음(32bit 범위 내) |
smartThread를 통해 반복적으로 Port의 입력 신호가 변경될 때를 즉시 알 수 있으며 이에 따른 사용자 인터페이스(UI)를 갱신할 수 있습니다. CPU 사용률이 중요하지 않으며 사용자 인터페이스(UI)를 즉시 갱신해야 할 경우 사용됩니다.
장점 | - 입력값 변경에 따른 사용자 인터페이스(UI)를 즉시 갱신할 수 있음 |
---|---|
단점 | - CPU 사용률이 100%가 되기 때문에 UI 응답성이 떨어짐 |
Timer나 반복문을 사용해 값을 계속 읽어서 값의 변경을 확인하고, 처리하는 방식입니다. 중요한 데이터를 입력받는 경우 사용됩니다.
장점 | Port Change Event 보다 입력 처리가 빠름 |
---|---|
단점 | Loop 문으로 입력을 Check 하므로 사용자 인터페이스(UI) 입력 및 처리가 불가능함 |
별도의 InputCounter블록이 입력 신호의 상승 엣지마다 값을 카운트하여 저장하고, SmartInputCounter-Block으로 그 값을 읽는 방식입니다. OnPortDatasChangeCapture Event보다 더 고속으로 일정 시간 동안 카운팅해야 하는 경우, OnPortDatasChangeCapture Evnet방식의 범위인 32bit보다 더 많은 수를 카운팅해야 하는 경우 사용됩니다.
장점 |
- OnPortDatasChangeCapture 보다도 성능이 약 17배 빠름(IEC1000-Series 기준) • InputCounter : 680kHz • OnPortDatasChangeCapture : 39.02kHz(단순한 방법 기준) - 에러가 발생하는 경우 예외 처리를 할 수 있음 - 최대 64bit 범위를 카운팅할 수 있음 |
---|---|
단점 |
- 카운팅 외 입력에 따른 별도의 처리는 할 수 없음 - 별도의 옵션 제품을 구입해야함 |
3. 입력 확인 주기(Input Scan Cycle Time)가 가변인 이유
입력 처리는 CPU의 가용 상태에 따라 입력 처리 시간이 달라질 수 있습니다. CPU는 단일 코어이므로 백그라운드에서 돌아가는 많은 작업(Task)이 실행되는 동안 터치 등의 입력을 통해서 별도의 작업이 수행되는 경우 CPU의 가용 상태가 변경되어 입력 확인 주기는 길어질 수 있습니다. 또한, 사용자가 구현한 프로그램의 입력 처리가 복잡한 경우에도 가변 길이가 더 길어질 수 있습니다. 즉, 사용자 프로그램의 성능과 시스템의 동작 방식에 따라 측정값이 달라질 수 있습니다.
4. 장치별 응용 프로그램 환경에 따른 최대 입력 신호 측정
4-1. 입력 확인 주기(Input Scan Cycle Time)를 확인하기 위한 처리 방식 CASE-1. Port Change Event(OnPortDatasChange) 방식
// 입력 신호를 카운팅 할 변수
private int m_iCount;
// A포트 감지 시작
private void btnStart_Click(object sender, EventArgs e)
{
labCount.Text = m_iCount.ToString();
// 복잡한 방식일 경우
if(chkCPUStress.Checked == true)
{
smartTimer1.Start();
// SmartGPIO의 Port를 A로 설정하고 입력Pin 설정
smartGPIO1.PortSelect = SmartX.SmartGPIO.PORTID.PORTA;
smartGPIO1.DirPin1 = SmartX.SmartGPIO.PINDIR.INPUT;
// A포트 상태 변경 감지를 시작하는 메서드
smartGPIO1.PortWatchStart();
// 감지 종료
private void btnStop_Click(object sender, EventArgs e)
{
smartTimer1.Stop();
// A포트의 상태 변경이 감지되었을 경우 발생되는 이벤트
private void smartGPIO1_OnPortDatasChange(int iPortDatas)
{
SmartX.SmartGPIO.PORTPIN iBit = smartGPIO1.PortDetection(SmartX.SmartGPIO.PORTID.PORTA, SmartX.SmartGPIO.TRIGGERMODE.HighActive, iPortDatas, SmartX.SmartGPIO.PORTPIN.PIN1);
// Counting
if (iBit == SmartX.SmartGPIO.PORTPIN.PIN1)
{
labCount.Text = (++m_iCount).ToString();
private void smartTimer1_Tick(object sender, EventArgs e)
{
smartDraw1.Chart.PutData((int)(100 * Math.Sin((double)m_iCount / 50)));
smartListBox1.AddItem(m_iCount.ToString());
CASE-2. Port Capture Event(OnPortDatasChangeCapture)방식
private int m_iCount;
private void butStart_Click(object sender, EventArgs e)
{
labCount.Text = m_iCount.ToString();
// SmartGPIO의 Port를 A로 설정하고 입력Pin 설정
smartGPIO1.PortSelect = SmartX.SmartGPIO.PORTID.PORTA;
smartGPIO1.DirPin1 = SmartX.SmartGPIO.PINDIR.INPUT;
smartGPIO1.PortDetection_Initialize();
smartGPIO1.SetPortCaptureScanSpeedAutoStart((int)udScanSpeedHigh.Value, (int)udScanSpeedLow.Value);
smartGPIO1.PortWatchStart();
private void butStop_Click(object sender, EventArgs e)
{
smartGPIO1.PortWatchStop();
// Scan Speed 자동 변환 종료
smartGPIO1.SetPortCaptureScanSpeedAutoStop();
private void smartGPIO1_OnPortDatasChangeCapture(int iPortDatas)
{
SmartX.SmartGPIO.PORTPIN iBit = smartGPIO1.PortDetection(SmartX.SmartGPIO.PORTID.PORTA, SmartX.SmartGPIO.TRIGGERMODE.HighActive, iPortDatas, SmartX.SmartGPIO.PORTPIN.PIN0);
// Counting
if (iBit == SmartX.SmartGPIO.PORTPIN.PIN1)
{
labCount.Text = (++m_iCount).ToString();
private void smartTimer1_Tick(object sender, EventArgs e)
{
smartDraw1.Chart.PutData((int)(100 * Math.Sin((double)m_iCount / 50)));
smartListBox1.AddItem(m_iCount.ToString());
CASE-3. Work Thread 방식
// CPU의 부하 여부를 나타냄(true : 복잡한 방법 / false : 단순한 방법)
private bool m_bCPUStress = false;
private int m_iCounting = 0;
// 입력 신호 저장하는 리스트
private List<int> m_listChartDatas = new List<int>();
private void Form1_Load(object sender, EventArgs e)
{
smartThread1.WorkerReportsProgress = true;
// SmartGPIO의 Port를 A로 설정하고 입력Pin 설정
smartGPIO1.PortSelect = SmartX.SmartGPIO.PORTID.PORTA;
smartGPIO1.DirPin1 = SmartX.SmartGPIO.PINDIR.INPUT;
smartGPIO1.PortDetection_Initialize();
private void btnHardStart_Click(object sender, EventArgs e)
{
m_bCPUStress = true;
smartThread1.Start();
private void btnEasyStart_Click(object sender, EventArgs e)
{
m_bCPUStress = false;
smartThread1.Start();
private void smartThread1_ThreadFunction(SmartX.ThreadArgs args)
{
SmartX.SmartGPIO.PORTPIN iBit = smartGPIO1.PortDetection(SmartX.SmartGPIO.PORTID.PORTA, SmartX.SmartGPIO.TRIGGERMODE.HighActive, SmartX.SmartGPIO.PORTPIN.PIN0);
if (iBit == SmartX.SmartGPIO.PORTPIN.PIN1)
{
m_listChartDatas.Add(m_iCounting++);
// 1,000번마다 UI 갱신
if (m_iCounting % 1000 == 0)
{
smartThread1.ReportProgress(m_iCounting);
}
args.bEnd = false;
private void smartThread1_UIThreadFunction(SmartX.ThreadArgs args)
{
lbCounting.Text = args.iProgressPercentage.ToString();
// 복잡한 방법 : SmartListBox와 SmartDraw에 데이터 출력하여 CPU에 부하를 줌
if (m_bCPUStress == true)
{
{
smartDraw1.Chart.PutData((int)(100 * Math.Sin((double)iAddData / 50)));
smartListBox1.AddItem(iAddData.ToString());
m_listChartDatas.Clear();
smartDraw1.Chart.ChartNowDraw();
smartListBox1.Refresh();
CASE-4.Polling 방식
private int m_icounting = 0;
private void Form1_Load(object sender, EventArgs e)
{
smartGPIO1.PortSelect = SmartX.SmartGPIO.PORTID.PORTA;
// PortA의 모든 Pin을 입력으로 설정
smartGPIO1.DirPin1 = SmartX.SmartGPIO.PINDIR.INPUT;
smartGPIO1.PortDetection_Initialize();
private void btnStart_Click(object sender, EventArgs e)
{
{
int iPortDatas = smartGPIO1.PortDatas;
// 신호 감지 시점을 선택하여 확인한다.
SmartX.SmartGPIO.PORTPIN iBit = smartGPIO1.PortDetection(SmartX.SmartGPIO.PORTID.PORTA, SmartX.SmartGPIO.TRIGGERMODE.HighActive, iPortDatas);
if (iBit == SmartX.SmartGPIO.PORTPIN.PIN1)
{
smartGPIO1.PortDetection_Update(iPortDatas);
if (m_iCount == 10000)
{
labCounting.Text = m_icounting.ToString();
4-2. 테스트 방법 설명
출력 장치에서 출력 신호를 모두 입력받을 수 있는 최적의 출력 주기를 조절해 10,000번 신호를 토글시키고, 입력 장치에서 smartGPIO1.PortDetection()메소드를 사용해 토글되는 신호를 카운트합니다. 본 테스트는 단순 방법과 복잡 방법으로 분류하여 진행했습니다. 단순 방법은 출력된 값을 입력받아 카운팅을 사용자 인터페이스(UI)에 나타내는 작업만 하지만 복잡 방법은 단순 방법에 아래의 코드를 추가하여 CPU에 부하를 더했습니다.
smartDraw1.Chart.PutData((int)(100 * Math.Sin((double)m_iCount / 50)));
smartListBox1.AddItem(m_iCount.ToString());
5. 제품별 테스트 결과
smartGPIO 입력 처리 관련하여 테스트 결과 및 과정에서 "입력 성능은 Windows CE의 멀티태스킹 환경에 따라 결과 해석이 복잡함"을 확인할 수 있습니다. 이는 단일 코어 CPU에서 백그라운드 프로세서의 영향으로 CPU의 사용량에 따라 입력 성능의 차이를 나타내는 것으로 확인되었으며 이에 따라 개발하는 프로젝트에 맞게 입력 성능을 확인하여 사용하시는 것을 권장합니다. 또한, 아래의 표는 입력 속도가 빠른 순으로 작성되었으며,각 CASE 별 테스트 데이터는 참고용으로만 사용하시기 바랍니다.
해당 테스트 결과는 사용하는 환경에 따라 결과값이 달라질 수 있으므로 참고용으로 사용하시기 바랍니다.
출력 제품 | IEC1000-Series | |||
---|---|---|---|---|
입력 확인 제품 | IEC667-Series | IEC1000-Series | ||
단순 | 복잡 | 단순 | 복잡 | |
최대 입력 주파수 | 10.86Hz | 2.26Hz | 11.9Hz | 3.24Hz |
CPU 사용량 | 6~8% | 41~45% | 3~5% | 10~12% |
- Scan Speed Low 값은 어떠한 값으로 설정하여도 결과값에 영향을 주지 않습니다. 적절한 임의의 값으로 설정하여 사용하시기 바랍니다.
- Scan Speed가 높으면 IEC667-Series의 경우 사용자 인터페이스(UI)의 반응성이 낮지만 IEC1000-Series의 경우 비교적 사용자 인터페이스(UI)의 반응성이 높습니다.
iScanSpeedHigh | 100,000 | |||
---|---|---|---|---|
iScanSpeedLow | 100 | |||
출력 제품 | IEC1000-Series | |||
입력 확인 제품 | IEC667-Series | IEC1000-Series | ||
단순 | 복잡 | 단순 | 복잡 | |
최대 입력 주파수 | 11.58kHz | 17.79kHz | 35.4kHz | 20.9kHz |
CPU 사용량 | 100% |
출력 제품 | IEC1000-Series | |||
---|---|---|---|---|
입력 확인 제품 | IEC667-Series | IEC1000-Series | ||
단순 | 복잡 | 단순 | 복잡 | |
최대 입력 주파수 | 10.63Hz | 2.64Hz | 12.19Hz | 6.09Hz |
CPU 사용량 | 100% |
출력 제품 | IEC1000-Series | |
---|---|---|
입력 확인 제품 | IEC667-Series | IEC1000-Series |
단순 | 복잡 | |
최대 입력 주파수 | 15.15Hz | 250.01Hz |
CPU 사용량 | 100% |