PIC 마이크로 컨트롤러에서의 멀티 태스킹


17

오늘날 멀티 태스킹이 중요합니다. 마이크로 컨트롤러와 임베디드 프로그래밍에서 어떻게 달성 할 수 있을지 궁금합니다. PIC 마이크로 컨트롤러를 기반으로하는 시스템을 설계하고 있습니다. C를 사용하여 MplabX IDE에서 펌웨어를 디자인 한 다음 Visual Studio에서 C #을 사용하여 응용 프로그램을 디자인했습니다.

병렬 작업을 구현하기 위해 데스크톱의 C # 프로그래밍에서 스레드를 사용하는 데 익숙해 졌으므로 마이크로 컨트롤러 코드에서 동일한 작업을 수행하는 방법이 있습니까? MplabX IDE는 제공 pthreads.h하지만 구현되지 않은 스텁입니다. FreeRTOS 지원이 있지만이를 사용하면 코드가 더 복잡해집니다. 일부 포럼에 따르면 인터럽트를 멀티 태스킹으로 사용할 수도 있지만 인터럽트가 스레드와 동일하다고 생각하지 않습니다.

UART에 일부 데이터를 전송하는 동시에 시스템을 유선 네트워크를 통해 웹 사이트로 보내야하는 시스템을 설계하고 있습니다. 사용자는 웹 사이트를 통해 출력을 제어 할 수 있지만 2-3 초의 지연으로 출력이 켜지거나 꺼집니다. 이것이 제가 직면 한 문제입니다. 마이크로 컨트롤러에 멀티 태스킹을위한 솔루션이 있습니까?


스레드는 프로세스의 일부이고 프로세스는 OS에서만 사용되므로 스레드는 OS를 실행하는 프로세서에서만 사용할 수 있습니다.
TicTacToe

@ Zola 네 맞습니다. 그러나 컨트롤러의 경우는 어떻습니까?
항공기


1
진정한 멀티 태스킹이 필요한 이유를 설명하고 라운드 로빈 작업 방식이나 select () 루프 또는 이와 유사한 방식으로 소프트웨어를 합리적으로 구현할 수없는 이유를 설명 할 수 있습니까?
whatsisname

2
글쎄, 내가 이미 말했듯이, 나는 uart로 데이터를 보내고 받고 있으며 동시에 이더넷으로 데이터를 보내고 받고 있습니다. 이 외에도 시간과 함께 SD 카드에 데이터를 저장해야하므로 DS1307 RTC와 EEPROM도 포함됩니다. 지금까지 UART는 1 개 뿐이지 만 며칠 후에 3 개의 UART 모듈에서 데이터를 보내고받을 수 있습니다. 웹 사이트는 원격지에 설치된 5 개의 서로 다른 시스템으로부터 데이터를 수신합니다. 이 모든 것은 평행해야하지만 평행하지는 않지만 몇 초 지연되어야합니다. !
항공기

답변:


20

멀티 태스킹 운영 체제에는 선점 및 협력의 두 가지 주요 유형이 있습니다. 둘 다 시스템에서 여러 작업을 정의 할 수 있지만 차이점은 작업 전환의 작동 방식입니다. 물론 단일 코어 프로세서에서는 한 번에 하나의 작업 만 실제로 실행됩니다.

두 가지 유형의 멀티 태스킹 OS는 각 작업마다 별도의 스택이 필요합니다. 따라서 이는 두 가지 사항을 의미합니다. 첫째, 프로세서는 스택을 RAM의 어느 곳에 나 배치 할 수 있으므로 스택 포인터 (SP)를 움직일 수있는 명령이 있습니다. PIC. 이것은 PIC10, 12 및 16 시리즈를 제외합니다.

거의 전적으로 C로 OS를 작성할 수 있지만 SP가 이동하는 작업 스위처는 어셈블리에 있어야합니다. 여러 차례에 걸쳐 PIC24, PIC32, 8051 및 80x86 용 작업 전환기를 작성했습니다. 내장은 프로세서의 아키텍처에 따라 상당히 다릅니다.

두 번째 요구 사항은 여러 스택을 제공하기에 충분한 RAM이 있어야한다는 것입니다. 일반적으로 스택에 적어도 몇 백 바이트를 원합니다. 그러나 작업 당 128 바이트로도 8 개의 스택에는 1K 바이트의 RAM이 필요합니다.하지만 각 작업마다 동일한 크기의 스택을 할당 할 필요는 없습니다. 현재 작업을 처리하기 위해 충분한 스택이 필요하고 중첩 된 서브 루틴을 호출 할 때 인터럽트 호출을위한 스택 공간도 필요합니다.

각 작업에 얼마나 많은 스택을 사용하고 있는지 결정하는 간단한 방법이 있습니다. 예를 들어 모든 스택을 특정 값 (예 : 0x55)으로 초기화하고 잠시 동안 시스템을 실행 한 다음 메모리를 중지하고 검사 할 수 있습니다.

어떤 종류의 PIC를 사용하고 싶은지는 말하지 않습니다. 대부분의 PIC24 및 PIC32에는 멀티 태스킹 OS를 실행할 수있는 충분한 공간이 있습니다. PIC18 (RAM에 스택이있는 유일한 8 비트 PIC)의 최대 RAM 크기는 4K입니다. 꽤 괜찮습니다.

협력적인 멀티 태스킹 (둘 중 더 간단한)을 사용하면 작업 전환이 작업을 제어 할 수있는 경우에만 작업 전환이 이루어집니다. 이는 작업이 I / O 요청 또는 타이머 호출과 같이 대기 할 일부 기능을 수행하기 위해 OS 루틴을 호출해야 할 때마다 발생합니다. 따라서 모든 레지스터와 상태 정보를 저장할 필요가 없으므로 OS가 스택을 쉽게 전환 할 수 있습니다. SP를 다른 작업으로 전환 할 수 있습니다 (실행할 다른 작업이없는 경우 유휴 스택이 있음) 주어진 통제). 현재 작업이 OS 호출을 할 필요는 없지만 잠시 동안 실행 된 경우 시스템 응답을 유지하기 위해 자발적으로 제어권을 포기해야합니다.

협력적인 멀티 태스킹의 문제는 작업이 제어권을 포기하지 않으면 시스템을 손상시킬 수 있다는 것입니다. 제어가 제공되는 인터럽트 루틴 만 실행할 수 있으므로 OS가 잠기는 것처럼 보입니다. 이것이이 시스템의 "협동적인"측면입니다. 작업 전환이 수행 될 때만 재설정되는 워치 독 타이머가 구현되면 이러한 잘못된 작업을 잡을 수 있습니다.

Windows 3.1 및 이전 버전은 협력적인 운영 체제 였기 때문에 부분적으로 성능이 그렇게 좋지 않았습니다.

선점 형 멀티 태스킹은 구현하기가 더 어렵습니다. 여기서 작업은 수동으로 제어를 포기할 필요는 없지만 각 작업에 최대 실행 시간 (예 : 10ms)을 부여한 다음 다음 실행 가능한 작업 (있는 경우)으로 작업 전환을 수행합니다. 이를 위해서는 임의로 작업을 중지하고 모든 상태 정보를 저장 한 다음 SP를 다른 작업으로 전환하고 시작해야합니다. 이로 인해 작업 전환기가 더 복잡해지고 더 많은 스택이 필요하며 시스템 속도가 약간 느려집니다.

협동 및 선점 형 멀티 태스킹의 경우 언제든지 실행중인 작업을 일시적으로 선점하는 인터럽트가 발생할 수 있습니다.

수퍼 캣이 의견에서 지적한 것처럼, 협업 멀티 태스킹의 장점 중 하나는 리소스를 공유하기가 쉽다는 것입니다 (예 : 멀티 채널 ADC와 같은 하드웨어 또는 링크 된 목록 수정과 같은 소프트웨어). 때로는 두 작업이 동시에 동일한 리소스에 액세스하려고합니다. 선점 예약을 사용하면 OS가 한 작업 중간에 리소스를 사용하여 작업을 전환 할 수 있습니다. 따라서 다른 태스크가 들어 와서 동일한 자원에 액세스하지 못하게하려면 잠금 이 필요합니다. 협력적인 멀티 태스킹을 사용하면 작업이 자체적으로 OS로 다시 릴리스 될시기를 제어하므로이 작업이 필요하지 않습니다.


3
협업 멀티 태스킹의 장점은 대부분의 경우 잠금을 사용하여 자원에 대한 액세스를 조정할 필요가 없다는 것입니다. 작업이 제어권을 포기할 때마다 항상 자원을 공유 가능한 상태로 유지하는 것으로 충분합니다. 선점 형 멀티 태스킹은 다른 작업에 필요한 리소스에 대한 잠금을 보유한 상태에서 작업이 전환 될 경우 훨씬 더 복잡합니다. 어떤 경우에는 두 번째 작업이 협동 시스템에서보다 더 오래 차단 될 수 있습니다. 잠금을 유지하는 작업은 시스템을 전담했기 때문입니다 ...
supercat

1
... (선점 시스템에서) 잠금이 필요한 작업을 완료하는 데 필요한 모든 리소스를 통해 보호 된 개체를 두 번째 작업에서 사용할 수 있습니다.
supercat

1
협력적인 멀티 태스킹에는 훈련이 필요하지만, 협력적인 멀티 태 스커에서는 사전 요구 사항보다 타이밍 요구 사항을 충족시키는 것이 더 쉬울 수 있습니다. 작업 스위치를 가로 질러 잠금이 거의 필요하지 않기 때문에 작업이 양보없이 10ms를 넘지 않아야하는 5 개의 작업 라운드 로빈 작업 스위치 시스템 인 "작업 X가 긴급하게 "실행해야합니다. 다음에 실행하십시오.", 작업 X가 실행되기 전에 신호를 보낸 후 10ms 이상을 기다리지 않아도됩니다. 대조적으로, 어떤 작업이 어떤 작업 X에 대한 잠금을 요구한다면 ...
supercat

1
... 필수적이지만 선제 적 스위처가 릴리스하기 전에 스위치를 끄면 X는 CPU 스케줄러가 첫 번째 작업을 실행할 때까지 유용한 것을 수행하지 못할 수 있습니다. 스케줄러에 우선 순위 반전을 인식하고 처리하는 논리가 포함되어 있지 않으면 첫 번째 태스크가 비즈니스를 완료하고 잠금을 해제하는 데 시간이 걸릴 수 있습니다. 이러한 문제는 해결할 수 없지만,이를 해결하기 위해서는 협동 시스템에서 피할 수있는 많은 복잡성이 필요합니다. 협동 시스템은 하나의 문제를 제외하고는 훌륭하게 작동합니다 : ...
supercat

3
연속적으로 코딩하는 경우 협력 적으로 여러 스택이 필요하지 않습니다. 본질적으로 코드는 함수로 나뉘어집니다 void foo(void* context). 컨트롤러 로직 (커널)은 큐의 포인터와 함수 포인터 쌍을 하나씩 끌어 당겨 한 번에 하나씩 호출합니다. 이 함수는 컨텍스트를 사용하여 변수를 저장 한 다음 큐에 연속을 제출할 수 있습니다. 이러한 기능은 다른 작업이 CPU에서 순간을 갖도록 신속하게 복귀해야합니다. 이것은 단일 스택 만 필요한 이벤트 기반 방법입니다.
ratchet freak

16

스레딩은 운영 체제에서 제공합니다. 임베디드 세계에는 일반적으로 OS ( "베어 메탈")가 없습니다. 따라서 다음 옵션이 남습니다.

  • 기본 메인 폴링 루프. 주요 기능에는 작업 1을 수행 한 다음 작업 2를 수행하는 while (1)이 있습니다 ...
  • 메인 루프 + ISR 플래그 : 시간이 중요한 기능을 수행 한 다음 작업에 서비스가 필요한 플래그 변수를 통해 메인 루프에 경고하는 ISR이 있습니다. 아마도 ISR은 순환 버퍼에 새로운 문자를 넣은 다음 데이터를 처리 할 준비가되었을 때 데이터를 처리하도록 메인 루프에 지시합니다.
  • 모든 ISR : 여기서 많은 논리가 ISR에서 실행됩니다. 여러 우선 순위 수준을 가진 ARM과 같은 최신 컨트롤러에서. 이것은 강력한 "스레드와 같은"체계를 제공 할 수 있지만 디버그하기가 혼동 될 수 있으므로 중요한 타이밍 제약 조건에 대해서만 예약해야합니다.
  • RTOS : RTOS 커널 (타이머 ISR에 의해 촉진됨)을 통해 여러 실행 스레드간에 전환 할 수 있습니다. FreeRTOS를 언급했습니다.

귀하의 응용 프로그램에 적합한 가장 간단한 위의 구성표를 사용하는 것이 좋습니다. 당신이 묘사 한 것에서, 나는 메인 루프가 패킷을 생성하고 원형 버퍼에 넣을 것입니다. 그런 다음 버퍼가 전송 될 때까지 이전 바이트가 전송 될 때마다 실행되고 더 많은 버퍼 내용을 기다리는 UART ISR 기반 드라이버가 있어야합니다. 이더넷에 대한 비슷한 접근 방식.


3
이것은 문제의 근본을 해결하기 때문에 매우 유용한 답변입니다 (솔루션이 아닌 작은 임베디드 시스템에서 멀티 태스킹하는 방법). 원래 질문에 어떻게 적용 할 수 있는지에 대한 단락은 아마도 시나리오에 대한 각 장단점을 포함하여 훌륭 할 것입니다.
David

8

실제 소프트웨어 멀티 태스킹을 수행하는 단일 코어 프로세서에서는 불가능합니다. 따라서 여러 작업간에 한 가지 방법으로 전환해야합니다. 다른 RTOS가이를 처리하고 있습니다. 스케줄러가 있으며 시스템 틱을 기반으로 여러 작업을 전환하여 멀티 태스킹 기능을 제공합니다.

이를 수행하는 데 필요한 개념 (컨텍스트 저장 및 복원)은 매우 복잡하므로 수동으로 수행하는 것은 어려울 수 있으며 코드를 더 복잡하게 만들며 이전에 해본 적이 없기 때문에 오류가 발생합니다. 내 조언은 FreeRTOS와 마찬가지로 테스트 된 RTOS를 사용하는 것입니다.

인터럽트는 멀티 태스킹 수준을 제공한다고 언급했습니다. 이것은 사실입니다. 인터럽트는 현재 프로그램을 언제든지 중단하고 코드를 실행합니다. 우선 순위가 낮은 하나의 작업과 스케줄러의 한 번의 조각 내에서 완료되는 우선 순위가 높은 다른 작업 시스템과 비교할 수 있습니다.

따라서 UART를 통해 몇 개의 패킷을 보내는 반복 타이머에 대한 인터럽트 처리기를 작성할 수 있습니다. 그런 다음 나머지 프로그램을 몇 밀리 초 동안 실행하고 다음 몇 바이트를 보냅니다. 그렇게하면 제한된 멀티 태스킹 기능을 얻을 수 있습니다. 그러나 약간 긴 인터럽트가 발생하여 나쁜 것일 수 있습니다.

단일 코어 MCU에서 동시에 여러 작업을 수행 할 수있는 유일한 방법은 DMA와 주변 장치가 코어와 독립적으로 작동하므로 DMA와 MCU를 사용하는 것입니다. 둘 다 활성화되어 있습니다). 따라서 DMA가 바이트를 UART로 섞는 동안 코어는 자유롭게 물건을 이더넷으로 보낼 수 있습니다.


2
고마워, DMA는 흥미있는 소리. 꼭 찾아 볼게요!
항공기

모든 PIC 시리즈에 DMA가있는 것은 아닙니다.
Matt Young

1
나는 PIC32를 사용하고있다;)
항공기

6

다른 답변은 이미 가장 많이 사용되는 옵션 (메인 루프, ISR, RTOS)을 설명했습니다. 절충안으로 또 다른 옵션이 있습니다 : Protothreads . 기본적으로 RTOS를 "에뮬레이트"하기 위해 메인 루프와 일부 C 매크로를 사용하는 스레드를위한 매우 가벼운 lib입니다. 물론 전체 OS는 아니지만 "간단한"스레드에는 유용 ​​할 수 있습니다.


어디서 Windows 용 소스 코드를 다운로드 할 수 있습니까? 나는 리눅스에서만 사용할 수 있다고 생각합니다.!
항공기

@CZAbhinav OS에 독립적이어야하며 여기 에서 최신 다운로드를 얻을 수 있습니다 .
erebos

나는 지금 창문에 있고 MplabX를 사용하고 있는데, 여기서는 유용하지 않다고 생각합니다. 어쨌든 감사합니다.!
항공기

프로토스 레드에 대해 들어 보지 못했지만 흥미로운 기법처럼 들립니다.
아스날

@CZAbhinav 무슨 소리 야? C 코드이며 운영 체제와 관련이 없습니다.
Matt Young

3

최소한의 시간 분할 RTOS를위한 기본 설계는 여러 마이크로 제품군에서 크게 바뀌지 않았습니다. 기본적으로 상태 머신을 구동하는 타이머 인터럽트입니다. 인터럽트 서비스 루틴은 OS 커널이고 메인 루프의 switch 문은 사용자 작업입니다. 장치 드라이버는 I / O 인터럽트에 대한 인터럽트 서비스 루틴입니다.

기본 구조는 다음과 같습니다.

unsigned char tick;

void interrupt HANDLER(void) {
    device_driver_A();
    device_driver_B();
    if(T0IF)
    {
        TMR0 = TICK_1MS;
        T0IF = 0;   // reset timer interrupt
        tick ++;
    }
}

void main(void)
{
    init();

    while (1) {
        // periodic tasks:
        if (tick % 10 == 0) { // roughly every 10 ms
            task_A();
            task_B();    
        }
        if (tick % 55 == 0) { // roughly every 55 ms
            task_C();
            task_D();    
        }

        // tasks that need to run every loop:
        task_E();
        task_F();
    }
}

이것은 기본적으로 협력적인 멀티 태스킹 시스템입니다. 작업은 무한 루프에 들어 가지 않도록 작성되었지만 작업이 이벤트 루프 내에서 실행되므로 무한 루프가 암시되므로 신경 쓰지 않습니다. 이것은 자바 스크립트 또는 이동과 같은 이벤트 지향 / 비 차단 언어와 유사한 프로그래밍 스타일입니다.

내 RC 송신기 소프트웨어에서 이러한 스타일의 아키텍처의 예를 볼 수 있습니다 (예, 실제로 RC 비행기를 비행하는 데 사용하므로 비행기를 추락시키고 잠재적으로 사람들을 죽이는 것을 방지하는 데 다소 안전해야합니다) : https://github.com / slebetman / 그림 - txmod . 기본적으로 3 개의 작업-상태 저장 장치 드라이버로 구현 된 2 개의 실시간 작업 (ppmio 항목 참조)과 믹싱 논리를 구현하는 1 개의 백그라운드 작업이 있습니다. 따라서 기본적으로 2 개의 I / O 스레드가 있다는 점에서 웹 서버와 유사합니다.


1
나는 '협력적인 멀티 태스킹'이라고 부르지 않을 것입니다. 왜냐하면 여러 가지 작업을 수행해야하는 다른 마이크로 컨트롤러 프로그램과 실질적으로 다르지 않기 때문입니다.
whatsisname

2

질문에 임베디드 RTOS의 사용에 대해 구체적으로 묻는다는 점을 알고 있지만, 더 광범위한 질문은 "임베디드 플랫폼에서 멀티 태스킹을 달성하는 방법"이라는 것입니다.

적어도 한동안 임베디드 RTOS를 사용하는 것을 잊어 버리는 것이 좋습니다. 간단한 작업 스케줄러와 상태 머신으로 구성된 매우 간단한 프로그래밍 기술을 사용하여 작업 '동시성'을 얻는 방법에 대해 먼저 알아야한다고 생각하기 때문에이 방법을 권장합니다.

개념을 간단하게 설명하기 위해 수행해야하는 각 작업 모듈 (예 : 각 '작업')에는 해당 모듈이 몇 가지 작업을 수행하기 위해 주기적으로 호출 ( '틱')해야하는 특정 기능이 있습니다. 모듈은 자체 현재 상태를 유지합니다. 그런 다음 모듈 함수를 호출하는 기본 무한 루프 (스케줄러)가 있습니다.

조잡한 삽화 :

for(;;)
{
    main_lcd_ui_tick();
    networking_tick();
}


...

// In your LCD UI module:
void main_lcd_ui_tick(void)
{
    check_for_key_presses();
    update_lcd();
}

...

// In your networking module:
void networking_tick(void)
{
    //'Tick' the TCP/IP library. In this example, I'm periodically
    //calling the main function for Keil's TCP/IP library.
    main_TcpNet();
}

메인 스케줄러 루프에서 주기적으로 메인 스테이트 머신 기능을 호출하는 이와 같은 단일 스레드 프로그래밍 구조는 임베디드 프로그래밍에 보편적으로 사용되므로 OP를 먼저 익숙해지기 전에 OP가 익숙하고 편안하게 사용하는 것이 좋습니다. RTOS 작업 / 스레드.

하드웨어 LCD 인터페이스, 내부 웹 서버, 전자 메일 클라이언트, DDNS 클라이언트, VOIP 및 기타 여러 기능이있는 내장 장치 유형을 사용합니다. RTOS (Keil RTX)를 사용하지만 사용 된 개별 스레드 (태스크)의 수는 매우 적으며 대부분의 '멀티 태스킹'은 위에서 설명한대로 달성됩니다.

이 개념을 보여주는 라이브러리의 몇 가지 예를 제공하려면 다음을 수행하십시오.

  1. Keil 네트워킹 라이브러리. 전체 TCP / IP 스택은 단일 스레드로 실행될 수 있습니다. main_TcpNet ()을 주기적으로 호출하면 TCP / IP 스택과 라이브러리 (예 : 웹 서버)에서 컴파일 한 기타 네트워킹 옵션이 반복됩니다. http://www.keil.com/support/man/docs/rlarm/rlarm_main_tcpnet.htm을 참조 하십시오 . 분명히, 어떤 상황에서는 (이 답변의 범위를 벗어난 것 같습니다) 스레드를 사용하는 것이 유리하거나 필요하기 시작하는 시점에 도달합니다 (특히 BSD 소켓을 사용하는 경우). (추가 참고 사항 : 새로운 V5 MDK-ARM은 실제로 전용 이더넷 스레드를 생성하지만 설명을 제공하려고합니다.)

  2. Linphone VOIP 라이브러리. Linphone 라이브러리 자체는 단일 스레드입니다. iterate()충분한 간격으로 함수를 호출합니다 . http://www.linphone.org/docs/liblinphone-javadoc/org/linphone/core/LinphoneCore.html#iterate ()를 참조 하십시오 . (내가 임베디드 Linux 플랫폼에서 이것을 사용하고 linphone의 의존성 라이브러리가 의심 할 여지없이 스레드를 생성했기 때문에 좋지 않은 예이지만 다시 한 번 요점을 설명합니다.)

OP에 의해 요약 된 특정 문제로 돌아가서, 문제는 UART 통신이 일부 네트워킹 (TCP / IP를 통해 패킷 전송)과 동시에 발생해야한다는 사실로 보입니다. 실제로 어떤 네트워킹 라이브러리를 사용하고 있는지 모르겠지만 자주 호출 해야하는 주요 기능이 있다고 가정합니다. UART 데이터 전송 / 수신을 처리하는 코드를 유사한 방식으로 구성하려면 주 함수를 주기적으로 호출하여 반복 할 수있는 상태 머신과 같은 코드를 작성해야합니다.


2
이 멋진 설명에 감사드립니다. 마이크로 칩에서 제공하는 TCP / IP 라이브러리를 사용하고 있으며 매우 복잡한 코드입니다. 어떻게 든 그것을 부품으로 나누고 내 요구 사항에 따라 사용할 수있게했습니다. 나는 당신의 접근법 중 하나를 시도 할 것입니다.!
항공기

RTOS를 사용하면 많은 상황에서 인생이 더 쉬워집니다. 필자가보기에 스레드 (태스크)를 사용하면 작업을 상태 머신으로 분류하지 않아도되므로 프로그래밍 작업이 훨씬 쉬워집니다. 대신 C # 프로그램에서와 마찬가지로 작업 코드 만 작성하면됩니다. 작업 코드는 존재하는 유일한 것처럼 생성됩니다. 두 가지 접근 방식을 모두 탐색하는 것이 필수적이며, 더 많은 임베디드 프로그래밍을 수행 할 때 각 상황에 가장 적합한 접근 방식을 느끼기 시작합니다.
Trevor 페이지

또한 스레딩 옵션을 선호합니다. :)
항공기
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.