'임베디드 시스템'에 해당되는 글 4건

  1. 2018.01.31 왜 RTOS가 필요한가?
  2. 2017.12.24 Qt 프로그램 소개
  3. 2017.12.19 Cortex-M4 환경에서 ChibiOS 사용한 기초 예제
  4. 2017.03.18 인터럽트와 volatile 지시자 1


실시간 시스템(real time system)은 임의의 정보가 시스템 내/외부에서 이벤트 혹은 인터럽트(interrupt)의 형태로 시스템에 입력되었을 때 주어진 시간 안에 작업이 완료되어 결과가 주어져야 하는 시스템입니다. 즉, 이벤트 발생과 처리가 실시간으로 이루어지는 시스템입니다. 물론 CPU의 처리 속도를 증가시킬 수도 있지만 속도는 느리더라도 이벤트 처리 시간을 보장할 수 있도록 하여야 한다는 의미입니다.


RTOS(Real Time Operating System) 없이 펌웨어를 제작하여도 모든 작업을 할 수는 있습니다. 예전의 시스템에서 펌웨어(firmware)는 비교적 간단해서 OS의 개념을 적용하지 않고 순차적인 프로그램만으로 작성했어도 그리 문제가 되지 않았습니다. 하지만 최근에 점점 더 소형기기에 들어가는 기능이 다양해지고 Windows OS처럼 이벤트가 수ms 정도에 처리되어도 가능한 상황과 달리, 임베디드 시스템(embedded system)에서는 훨씬 빠른 응답을 요구하여 순차적인 프로그래밍 방식으로는 기능을 구현하기에 어려워졌고 시간도 많이 걸려 다음의 장점으로 여러개의 태스크(task)로 분할하여 개발할 필요가 있다는 것입니다. 왜냐면 여러가지 일을 할 수 있는 순차적인 프로그램 방법으로 구현하면 프로그램의 복잡도가 일의 갯수의 승수에 비례하기 때문입니다.


1. 코드의 개발, 수정, 유지, 보수가 보다 용이합니다.

2. 이벤트에 대해 보다 신속하게 응답할 수 있습니다. - 인터럽트가 발생하면, 진행 중인 태스크 대신 인터럽트를 처리할 태스크를 우선적으로 실행할 수 있습니다.

3. 시스템의 신뢰도와 성능을 높일 수 있습니다.


한편 우선 순위 방식에는 2가지가 있는데, 우선 순위가 높은 태스크가 우선 순위가 낮은 태스크를 잠시 멈추게 하고 프로세서를 차지할 수 있는 권한이 부여되면 최근 대부분 OS가 그렇듯 선점형 우선순위(Preemptive)가 됩니다. 반면에 우선 순위가 높다 하더라도 우선 순위가 낮은 태스크가 완료할 때까지 기다려 주는 방식을 Windows 95이전의 OS와 같은 비선점형 우선순위(Non-preemptive)라 부릅니다.


임베디드 시스템에서는 주로 선점형을 사용하는데, 우선 순위에 따라 프로세서의 사용 권한을 조정하는 것을 스케줄링(Scheduling)라고 부릅니다. 이와 같이 진행 중인 태스크가 바뀌게 되면 컨텍스트(Context)라고 불리는 기존의 태스크에 대한 정보를 저장하고 새로운 태스크를 불러오는 동작을 해야 하는데 이를 컨텍스트 스위칭(context switching)이라고 합니다. 또한 진행 중인 태스크들의 각종 정보를 태스크 컨트롤 블럭(TCB: Task control block)이라고 부릅니다. 다음은 선점형 OS에서 사용하는 스케줄링 방식입니다.


1. Priority Scheduling Algorithm

2. Round-Robin Scheduling Algorithm


이처럼 예전에는 하드웨어 성능과 크기의 제약으로 OS 없이 단순한 기능만 수행 가능했던 것이 하드웨어 성능의 향상으로 다양한 일이 요구되었습니다. 이에 RTOS라는 OS 개념을 도입하여 각각의 태스크에 대한 스케줄링으로 자원을 효율적으로 사용할 수 있게 하고 복잡한 일들을 태스크로 나눔으로써 일을 단순화 시킬 수 있다는 것입니다. 뿐만 아니라 다음과 같은 까다로운 문제가 발생하는데, 이들 문제를 직접해결하기 보다는 멀티태스킹(multi-tasking; 엄밀하게 multi-threadingOS를 도입하는 것이 훨씬 효과적이라는 것입니다. 근래에는 멀티태스킹 뿐 아니라 Network, File System, GUI도 구현하고 있습니다.


1. 태스크간 경쟁의 관리

2. 데드락(deadlock)

3. 우선 순위 역전(priority inversion)

4. 재진입 문제(reentrancy)

5. 태스크간 통신


※ 일반적인 OS vs. RTOS

    • 효율성 / 시간 제약성 : 일반 OS 경우에 태스크들 사이에 효율성을 유지하려고 하지만 real-time OS에서는 태스크에 시간 제약성이 존재하고 이런 시간 제약성 때문에 효율성을 무시하는 경우가 발생하며 효율성은 고려하지 않습니다.

    • 공평성 / 우선순위 : 일반 OS 경우 여러 명의 사용자가 쓰는 경우에는 각 사용자들이 실행하는 프로그램이 태스크로서 수행이 되고 대개의 경우에는 각 태스크가 공평성을 유지하려고 한다. 그러나 real-time OS에서 태스크는 대개 우선순위가 차이가 있도록 하며 이때 태스크 사이의 공평성은 고려하지 않습니다.



결론적으로 
RTOS는 시간의 정확성을 보장하는 멀티태스킹 호출과 인터럽트에 대한 반응시간의 최대값을 보장할 수 있고 실행시간의 편차가 작아야 하는데 즉, 작업의 소요시간을 예측할 수 있어야 합니다. 우선순위가 높은 일이 우선적으로 자원을 분배하여 시간제한 내에 끝날 수 있도록 해야 합니다.


1. 다수의 작업에 우선 순위를 두어 멀티태스킹을 지원합니다.

2. 짧은 interrupt lattency interrupt latency는 인터럽트가 걸려서 인터럽트 핸들러에 도착하기까지의 시간이며 이벤트에 의한 반응 속도가 빠릅니다.

3. 적은 용량의 kernel 사용 - 작고 유연한 구조(10 ~ 50 KB 수준)



'Embedded Programming > Environment' 카테고리의 다른 글

Firmware와 RTOS의 차이점  (0) 2017.12.16
Posted by Nature & Life
Software Programming/Qt2017. 12. 24. 16:05


임베디드(Embedded) 시스템을 제어하거나 테스트하기 위한 프로그램 개발에는 여러 프로그램들이 사용되어질 수 있습니다. 예를 들어 Windows 환경에서 API나 MFC를 이용하여 개발한 경우, Linux와 같은 다른 환경에서 사용하기 위해서는 다시 그 환경에 맞는 툴을 사용하여 다시 제작하여야 합니다. 그러나 Qt는 cross platform으로 Unix, Linux 그리고 Windows, MAC OS X 등등 다양한 platform과 iOS나 Android 조차에서도 다시 컴파일하면 하면 된다는 것입니다.


역사적으로 Qt는 1995년 최초 버전이 공식 배포 되었고 이후 20년간 Qt는 산업 흐름에 맞추어 기능이 확장되고 성능이 개선되어 왔으며, 해당 기간 동안 전세계 1백만명이 넘는 개발자들에 의해서 검증된 안정적이고 성숙된 제품이 되었습니다. 현재 Qt 는 GUI 개발과 더불어 응용 프로그램 개발 전반을 커버하는 1,300여개의 풍부한 라이브러리들과 다양한 개발 도구를 갖춘 종합적인 개발 프레임워크 제품으로 완성되었다고 알려집니다.



기존의 개발 환경에서는 '볼륨' 버튼도 직접 제작하거나 별도의 라이브러리를 구입해야 하지만, Qt에서는 고급스러운 GUI 환경을 덕택에 가져다 쓰면 되고 상업적인 목적이 아닌 이상 소스 코드 채 배포되며 무료이면서 어떤 제한도 없다는 것입니다. 예를 들어, Qt만의 전통적이며 강력한 GUI 개발 기능은 Qt Widget으로부터 새롭게 지원되고 있는 Qt 자체의 GUI 개발용 스크립트 언어인 Qt Quick/QML로 이어졌고, QML을 이용하면 기존의 MFC 등의 개발 도구로는 구현이 매우 어려운 에니메이션이나 3D 인터페이스와 같은 자연스러운 그래픽 효과들도 쉽고 빠르게 구현할 수 있다는 것입니다.


뿐만 아니라, 프로그램 개발에 필수적인 멀티 스레드 프로세스 통신, 네트워크, OpenGL의 3D 그래픽, HTML5 지원 통합 웹엔진, XML 데이터베이스 등 C++ 개발자가 개발 중에 필요한 요구 사항도 다양한 라이브러리에 모듈형식으로 기능별로 제공한다는 것입니다. 또한 스마트폰과 태블릿 환경에서 요구되는 기존의 물리적 버튼과 원시적인 액정 스크린이 아닌 스마트폰과 같은 터치 패널 디스플레이 탑재와 고급스럽고 풍부한 표현을 가진 UI/HMI를 총망라 하고 있다는 것입니다.


Qt 에서는 'Qt Creator' 라는 통합 IDE 환경을 이용하여 코드를 작성하게 하며 GUI 화면을 직접 디자인하고 이를 개발코드와 직접 연결시킬 수 있는 'Qt Designer', GUI 개발을 편리하게 해주는 Qt 자체의 스크립트 언어인 'QML'과 UI 개발을 할 수 있는 'Qt Quick(Qt User Interface Creation Kit', Qt로 제작된 GUI 화면의 상세한 리소스 프로파일링이 가능한 'Qt Quick Profiler', Qt 라이브러리에 대한 상세한 도움말 문서인 'Qt Assistant'를 포함합니다. 또한 Visual Stuido나 Eclipse등 친숙한 개발 환경에서도 플러그인 하여 이용할 수도 있다는 것입니다.



Qt는 C++을 기반으로 하여 주로 C++을 사용하지만, 파이썬(Python), 루비(Ruby), C, 펄(Perl), 파스칼(Pascal)과도 연동됩니다. C 보다 C++을 사용할 경우 확장이 용이하다는 장점이 있습니다. 그러므로 클래스(class) 등의 C++의 기본 개념 지식이 먼저 요구됩니다.


https://www.qt.io/



'Software Programming > Qt' 카테고리의 다른 글

UART(Serial) 통신 예제  (2) 2018.03.03
Hello world 예제  (0) 2018.03.02
Posted by Nature & Life
Embedded Programming/ChibiOS2017. 12. 19. 19:29


http://www.playembedded.org/blog/en/2017/08/24/demos-chibios-stm32/

http://www.playembedded.org/blog/en/2016/10/29/explanation-multithreading-chibios/


Cortex-M4 MCU(ST32F4)를 환경에서 Real Time OS(RTOS)인 ChibiOS를 이용한 임베디드(embedded) 시스템의 코딩의 예제입니다. 참고하시기 바랍니다.

 

주요 헤더 파일입니다.

chconf.h

Kernel에 관련된 세팅이 포함되며 예를 들어 시스템 Timer와 Kernel 파라미터, 디버깅에 연관된 스위치 등입니다.

/*===========================================================================*/
/**
* @name Debug options
* @{
*/
/*===========================================================================*/
 
/**
* @brief   Debug option, kernel statistics.
*
* @note    The default is @p FALSE.
*/
#define CH_DBG_STATISTICS                   FALSE
 
/**
* @brief   Debug option, system state check.
* @details If enabled the correct call protocol for system APIs is checked
*          at runtime.
*
* @note    The default is @p FALSE.
*/
#define CH_DBG_SYSTEM_STATE_CHECK           FALSE
 
/**
* @brief   Debug option, parameters checks.
* @details If enabled then the checks on the API functions input
*          parameters are activated.
*
* @note    The default is @p FALSE.
*/
#define CH_DBG_ENABLE_CHECKS                FALSE
 
/**
* @brief   Debug option, consistency checks.
* @details If enabled then all the assertions in the kernel code are
*          activated. This includes consistency checks inside the kernel,
*          runtime anomalies and port-defined checks.
*
* @note    The default is @p FALSE.
*/
#define CH_DBG_ENABLE_ASSERTS               FALSE
 
/**
* @brief   Debug option, trace buffer.
* @details If enabled then the trace buffer is activated.
*
* @note    The default is @p CH_DBG_TRACE_MASK_DISABLED.
*/
#define CH_DBG_TRACE_MASK                   CH_DBG_TRACE_MASK_DISABLED
 
/**
* @brief   Trace buffer entries.
* @note    The trace buffer is only allocated if @p CH_DBG_TRACE_MASK is
*          different from @p CH_DBG_TRACE_MASK_DISABLED.
*/
#define CH_DBG_TRACE_BUFFER_SIZE            128
 
/**
* @brief   Debug option, stack checks.
* @details If enabled then a runtime stack check is performed.
*
* @note    The default is @p FALSE.
* @note    The stack check is performed in a architecture/port dependent way.
*          It may not be implemented or some ports.
* @note    The default failure mode is to halt the system with the global
*          @p panic_msg variable set to @p NULL.
*/
#define CH_DBG_ENABLE_STACK_CHECK           FALSE
 
/**
* @brief   Debug option, stacks initialization.
* @details If enabled then the threads working area is filled with a byte
*          value when a thread is created. This can be useful for the
*          runtime measurement of the used stack.
*
* @note    The default is @p FALSE.
*/
#define CH_DBG_FILL_THREADS                 FALSE
 
/**
* @brief   Debug option, threads profiling.
* @details If enabled then a field is added to the @p thread_t structure that
*          counts the system ticks occurred while executing the thread.
*
* @note    The default is @p FALSE.
* @note    This debug option is not currently compatible with the
*          tickless mode.
*/
#define CH_DBG_THREADS_PROFILING            FALSE
 

/** @} */


halconf.h

ChibiOS/HAL 드라이버에 관련된 특성을 포함합니다. 예를 들어, PAL, CAN, ADC, DAC, PWM, Serial 드라이버 등이 해당됩니다. 참고로 사용하지 않는 드라이버는 disable 시켜야 합니다.


/**
* @brief   Enables the PAL subsystem.
*/
#if !defined(HAL_USE_PAL) || defined(__DOXYGEN__)
#define HAL_USE_PAL                 TRUE
#endif
 
/**
* @brief   Enables the ADC subsystem.
*/
#if !defined(HAL_USE_ADC) || defined(__DOXYGEN__)
#define HAL_USE_ADC                 FALSE
#endif
 
/**
* @brief   Enables the CAN subsystem.
*/
#if !defined(HAL_USE_CAN) || defined(__DOXYGEN__)
#define HAL_USE_CAN                 TRUE
#endif
 
/**
* @brief   Enables the DAC subsystem.
*/
#if !defined(HAL_USE_DAC) || defined(__DOXYGEN__)
#define HAL_USE_DAC                 FALSE

#endif


mcuconf.h

MCU에 관련된 모든 설정을 포함합니다. 따라서 MCU에 따라 다릅니다. Clock 트리, DMA, IRQ 우선순위 등이 예입니다.


ChibiOS의 가장 중요한 특성 중의 하나는 멀티스레딩(multi-threading)입니다. 이는 Kernel이 한가지 이상의 스레드를 독립적으로 나란히 동시에 실행하는 것입니다. 스레드는 실제의 함수이고 시작되었을 때 우선순위 레벨(priority level)과 working area에 연관됩니다. Working area는 스레드에 할당된 메모리의 부분이고 우선 순위는 상대적인 숫자로 나타냅니다.


모든 스레드는 그 루프 내에 sleep과 suspending 함수를 가져야 합니다. 이런 방식으로 CPU의 소유권은 여러 스레드 사이에서 스위치 되고 스케쥴은 진행될 수 있습니다. ChibiOS는 다른 sleep 함수를 제공합니다. 다음 함수는 XXX [ms] 시간 동안 스레드를 suspending 합니다. 이 함수는 ChibiOS 이하의 os\rt\include\chthreads.h에 선언되어 있습니다.


chThdSleepMilliseconds(<xxx>);


Makefile

Embitz와 같은 IDE 환경을 사용시 일일이 지정해야 하고 IDE 툴마다 다를 수 있지만, 전통적인 Unix 환경에서 Makefile을 이용한 방법이 일목요연하고 유지관리가 용이하기 때문에 Makefile을 이용한 빌드 기준으로 설명합니다.


ChibiOS를 사용하기 위해서는 ChibiOS가 설치된 상대 경로의 지정이 필요합니다. 

# Imported source files and paths
PROJECT = build
CHIBIOS = ../../..
MCU = cortex-m4
TRGT = arm-none-eabi-


참고로 'build'는 프로젝트 이름이며 MCU는 cortex-m4이고 TRGT는 GNU Toolchain을 사용함을 의미합니다.

main.c

static THD_WORKING_AREA(waThread1, 128);
static THD_FUNCTION(Thread1, arg) {
 
  (void)arg;
  chRegSetThreadName("Thread1");
  while (true) {
    palTogglePad(GPIOA, GPIOA_LED_GREEN);
    chThdSleepMilliseconds(200);
  }
}
 
int main(void) {
  halInit();
  chSysInit();
 
  chThdCreateStatic(waThread1, sizeof(waThread1), NORMALPRIO + 1, Thread1, NULL);
  while (true) {
    palTogglePad(GPIOA, GPIOA_LED_RED);
    chThdSleepMilliseconds(375);
  }

}


위의 예제에서 128byte의 크기를 갖는 waThread1라는 이름의 working area를 선언하고 Thread1이라는 함수를 선언합니다. 위의 두 라인은 단지 메모리와 함수를 선언한 것이며 main() 함수에서 thread 생성을 시켜야 사용할 수 있습니다. THE_FUNCTION의 arg는 함수 내부 코드로 전달되는 인자입니다.


chRegSetThreadName("blinker");


위 함수는 루프 이전에 단 한번 실행됩니다. 이 함수는 ChibiOS 이하의 os\rt\include\chregistry.h에 선언되어 있습니다.



main()함수 내의 2가지 함수가 실행되는데 이는 시스템 초기화를 실행하는 것으로, hallnit()는 ChibiOS/HAL과 HAL 서브 시스템을 초기화 하는 것이며 chSysInit()는 ChibiOS/RT의 API와 Kernel을 초기화합니다. 이후에는 main() 함수의 스레드와 다른 모든 스레드가 실행될 준비가 되지 않았을 때 실행되는 idle 스레드가 생성됩니다. 또한 이들 초기화 이전에 어떤 API의 실행은 하지 말아야 합니다. ChibiOS/RT 그리고 ChibiOS/HAL에 근거한 모든 프로그램은 위와 동일한 방식으로 시작합니다. 여기서 halInit() 함수는 ChibiOS 이하의 os\hal\include\hal.h에 그리고 chSysInit() 함수는 ChibiOS 이하의 os\rt\include\chsys.h에 선언되어 있습니다.


palTogglePad() 함수는 ChibiOS 이하의 os\hal\include\hal_pal.h에 선언되어 있으며, 첫번째 인자는 포트이고 두번째 인자는 그 포트 내에서의 패드 숫자입니다. 따라서 이 함수는 해당 포트의 해당 패드에 그 논리적인 상태 즉, LED를 토글(toggle)합니다.



chThdCreateStatic(waThread1, sizeof(waThread1), NORMALPRIO + 1, Thread1, NULL);


위의 스레드의 생성 함수는 5개의 인자가 필요하며 첫번째는 working area의 포인트이며 마지막 인자는 Thread1 함수로 전달될 파라미터로 이 경우에는 아무것도 보내지 않습니다. 이 함수는 ChibiOS 이하의 os\rt\include\chthreads.h에 선언되어 있습니다.


이 경우에 3개의 스레드인, Thead1, main, idle가 있으며 즉, Kernel은 3가지 스레드를 관리게 되는데 개념적인 timing 플로우는 다음과 같습니다.



위 그림에서 스레드는 y축 상에 우선순위에 따라 실행되고 x축은 시간이 됩니다. chSysInit()이 실행될 때 main() 함수는 main이라는 스레드가 되고 Thread1의 생성될 때까지 실행됩니다. Thread1이 생성된 후에는 main와 Thread1이 준비된 상태가 됩니다. 


ChibiOS의 우선순위는 정수이고 LOWPRIO와 HIGHPRIO의 범위를 가지며 main 스레드는 NORMALPRIO의 우선 순위를 가지며 전체 범위의 중간 값을 가집니다. Thread1과 main() 함수 내에 chThdSleepMilliseconds() 함수는 시간을 지연시키지만 그 동안 CPU의 소유권을 다음의 우선순위 스레드에 넘기게 됩니다.



'Embedded Programming > ChibiOS' 카테고리의 다른 글

ChibiOS/RT 설치  (1) 2018.02.25
ChibiOS/RT의 특징  (0) 2018.02.25
Posted by Nature & Life
Embedded Lecture/Arduino2017. 3. 18. 16:33


AVR 칩을 사용하는 Arduino Uno 보드 혹은 다른 임베디드(Embedded) 시스템에서 전역 변수(globlal variable)는 주메모리인 SRAM에 저장되고, 지역 변수(local variable)는 레지스터(register) 영역에 저장됩니다. 그러나 인터럽트 서비스 루틴(ISR)에서의 지역 변수는 SRAM의 Stack 영역에 저장되므로 빠른 인터럽트 처리를 위해서 루틴 내에 잦은 지역 변수의 사용은 바람직하지 않다는 것입니다.


사용자의 코드를 컴파일러(Compiler)는 나름데로 최적화시키는데, 예를 들어 사용자가 전역 변수 지정을 하였지만 그 값이 시종일관 변하지 않는 변수는 컴파일러가 임의로 상수처럼 여겨 레지스터 영역에 저장하게 한다는 것입니다. 물론 한정된 레지스터 영역의 리소스를 낭비하는 것 아니냐고 생각할 수 있지만, 컴파일러 입장에서는 빠른 실행 속도를 위해서 SRAM 보다는 레지스터 영역에 저장한다는 것입니다.


그러나 임베디드 시스템의 경우에 인터럽트 서비스 루틴에서 전역 변수의 갱신이 있었다 하더라도, 컴파일러는 실제 런타임 시 처럼 외부에서 인터럽트가 걸리는 상황이 아니므로 그 값은 변하지 않는다고 생각하여 결국 레지스터에 저장하고 주메모리 상에 변경된 값의 반영이 없다는 것입니다.


이런 상황을 위해서 지시자(directive)인 'volatile'의 선언은 변수형 앞에 두어 컴파일러가 임의로 판단하여 그 변수를 레지스터에 할당하지 못하도록 한다는 것입니다. 즉, 어떤 변수를 volatile로 지정하면 그 변수는 레지스터의 임시 저장소가 아니라 SRAM에서 직접 읽어오도록 컴파일한다는 것입니다.


결론적으로 Arduino 코딩의 경우 보통은 volatile로 정의할 필요는 없으나 인터럽트 서비스 루틴 내부에서 그 값이 변경되는 변수는 반드시 volatile로 선언해야 실시간으로 변경되는 데이터 값을 ISR 루틴 외부에서 읽어올 수 있게 된다는 것입니다.


int pin = 13;

volatile int state = LOW;


void setup()

{

  pinMode(pin, OUTPUT);

  attachInterrupt(0, blink, CHANGE);

}


void loop()

{

  digitalWrite(pin, state);

}


void blink()

{

  state = !state;

}


위의 예제에서 blink() 함수는 인터럽트 서비스 루틴으로 0번 인터럽트 핀의 상태가 변할 때마다 13번에 연결된 LED를 교대로 점멸하게 됩니다. 만일 state 변수에 volatile 선언이 없었다면, 비록 blink() 함수 내에 state 값이 변하더라도 이 함수를 벗어나서는 state 값이 갱신되지 않기 때문에 0번 인터럽트의 상태 변화에 LED의 토글(toggle) 현상이 보이지 않는다는 것입니다.



'Embedded Lecture > Arduino' 카테고리의 다른 글

아날로그 입력 및 온도계 예제  (0) 2017.03.19
아날로그 입력  (0) 2017.03.18
아두이노의 TWI(I2C) 통신  (0) 2017.03.18
아두이노의 시리얼 통신  (0) 2017.03.18
인터럽트의 처리(1)  (0) 2017.03.12
Posted by Nature & Life