임베디드 장치에서 TDD를 어떻게 수행합니까?


17

나는 프로그래밍에 익숙하지 않고 AVR에서 일부 저수준 C 및 ASM과 함께 일했지만 실제로 대규모 임베디드 C 프로젝트를 할 수는 없습니다.

Ruby의 TDD / BDD 철학에 의해 퇴보되어 사람들이 이와 같은 코드를 작성하고 테스트하는 방법을 이해할 수 없습니다. 나는 그것이 나쁜 코드라고 말하지 않고, 이것이 어떻게 작동하는지 이해하지 못합니다.

나는 저수준 프로그래밍에 대해 더 많이 알고 싶었지만, 내가 익숙한 완전히 다른 사고 방식처럼 보이기 때문에 어떻게 접근 해야하는지 전혀 모른다. 포인터 산술이나 메모리 할당 방법을 이해하는 데 어려움이 없지만 Ruby와 비교하여 복잡한 C / C ++ 코드가 어떻게 보이는지 알 수 없을 것 같습니다.

이미 Arduino 보드를 주문 했으므로 저수준 C에 더 많이 들어가고 실제로 올바르게 수행하는 방법을 이해하고 싶지만 고급 언어 규칙이 적용되지 않는 것처럼 보입니다.

임베디드 장치에서 또는 드라이버 또는 사용자 정의 부트 로더 등과 같은 것들을 개발할 때 TDD를 수행 할 수 있습니까?


3
안녕 다스, 우리는 당신이 C에 대한 두려움을 극복하는 데 도움을 줄 수는 없지만 임베디드 장치의 TDD에 대한 질문은 여기에 주제입니다.

답변:


18

먼저, 작성하지 않은 코드를 이해하는 것은 직접 작성하는 것보다 5 배 어렵다는 것을 알아야합니다. 프로덕션 코드를 읽음으로써 C를 배울 수는 있지만, 배우는 것보다 훨씬 오래 걸릴 것입니다.

Ruby의 TDD / BDD 철학에 의해 퇴보되어 사람들이 이와 같은 코드를 작성하고 테스트하는 방법을 이해할 수 없습니다. 나는 그것이 나쁜 코드라고 말하지 않고, 이것이 어떻게 작동하는지 이해하지 못합니다.

그것은 기술이다. 당신은 그것을 더 좋아집니다. 대부분의 C 프로그래머들은 사람들이 루비를 어떻게 사용하는지 이해하지 못하지만, 이것이 그들이 할 수 없다는 것을 의미하지는 않습니다.

임베디드 장치에서 또는 드라이버 또는 사용자 정의 부트 로더 등과 같은 것들을 개발할 때 TDD를 수행 할 수 있습니까?

글쎄, 주제에 관한 책이 있습니다 :

여기에 이미지 설명을 입력하십시오 땅벌이 할 수 있다면 당신도 할 수 있습니다!

다른 언어의 관행을 적용하는 것은 일반적으로 작동하지 않습니다. TDD는 매우 보편적입니다.


2
내 임베디드 시스템에서 본 모든 TDD는 스스로 쉽게 발견 할 수있는 오류를 쉽게 해결할 수있는 시스템의 오류 만 발견했습니다. 그들은 다른 칩과의 시간 의존적 상호 작용과 인터럽트 상호 작용을 통해 내가 도움이 필요한 것을 찾지 못했습니다.
Kortuk

3
이것은 어떤 종류의 시스템에서 작업하고 있는지에 달려 있습니다. 좋은 하드웨어 추상화와 함께 TDD를 사용하여 소프트웨어를 테스트하면 실제로 시간 의존적 상호 작용을 훨씬 더 쉽게 조롱 할 수 있습니다. 사람들이 자주 간과하는 또 다른 이점은 자동화 된 테스트를 언제든지 실행할 수 있으며 누군가 소프트웨어를 작동시키기 위해 로직 분석기로 장치에 앉아있을 필요가 없다는 것입니다. TDD 덕분에 현재 프로젝트만으로도 몇 주간의 디버깅 시간을 절약 할 수있었습니다. 종종 우리가 예상하지 못한 오류를 일으키는 것은 쉽게 발견 할 수있는 오류라고 생각합니다.
Nick Pascucci

또한 개발 및 테스트 외 대상을 허용합니다.
cp.engr

비 내장 C에 대한 TDD를 이해하기 위해이 을 따를 수 있습니까 ? 어떤 사용자 공간 C 프로그래밍?
overexchange

15

여기에 다양한 답변이 있습니다 ... 주로 다양한 방법으로 문제를 해결합니다.

저는 25 년 이상 다양한 언어로 내장 된 저수준 소프트웨어 및 펌웨어를 작성해 왔습니다. 주로 C (Ada, Occam2, PL / M 및 다양한 어셈블러로 전환)입니다.

오랜 사고와 시행 착오 끝에 결과를 상당히 빨리 얻고 테스트 래퍼와 하네스 (값을 추가하는 위치)를 만드는 것이 매우 쉬운 방법으로 정착했습니다.

이 방법은 다음과 같습니다.

  1. 사용하려는 각 주요 주변 장치에 대한 드라이버 또는 하드웨어 추상화 코드 장치를 작성하십시오. 또한 하나를 작성하여 프로세서를 초기화하고 모든 것을 설정하십시오 (친숙한 환경을 만듭니다). 일반적으로 소형 임베디드 프로세서 (예 : AVR)에는 10-20 개의 장치가있을 수 있습니다. 이는 초기화, 비 스케일 메모리 버퍼로의 A / D 변환, 비트 출력, 푸시 버튼 입력 (디 바운스가 샘플링되지 않음), 펄스 폭 변조 드라이버, UART / 간단한 직렬 드라이버 사용 인터럽트 및 소형 I / O 버퍼를위한 단위 일 수 있습니다. 예를 들어 EEPROM, EPROM 또는 기타 I2C / SPI 장치 용 I2C 또는 SPI 드라이버가 더있을 수 있습니다.

  2. 각 하드웨어 추상화 (HAL) / 드라이버 유닛에 대해 테스트 프로그램을 작성합니다. 이것은 직렬 포트 (UART)와 프로세서 초기화에 의존합니다. 따라서 첫 번째 테스트 프로그램은이 두 장치 만 사용하며 기본 입력 및 출력 만 수행합니다. 이를 통해 프로세서를 시작할 수 있고 직렬 디버그 지원 직렬 I / O 작업이 있는지 테스트 할 수 있습니다. 일단 작동하면 다른 HAL 테스트 프로그램을 개발하여 알려진 우수한 UART 및 INIT 장치 위에 구축합니다. 따라서 비트 입력을 읽고 직렬 디버그 터미널에서 16 진수, 10 진수 등 멋진 형식으로 표시하는 테스트 프로그램이있을 수 있습니다. 그런 다음 EEPROM 또는 EPROM 테스트 프로그램과 같이 더 크고 복잡한 것으로 이동할 수 있습니다. 이러한 메뉴를 대부분 사용하여 실행할 테스트를 선택하여 실행하고 결과를 확인할 수 있습니다. 나는 그것을 쓸 수 없지만 보통 나는

  3. HAL을 모두 실행하면 정기적 인 타이머 틱을 얻는 방법을 찾습니다. 이것은 일반적으로 4 ~ 20 ms 사이의 속도입니다. 이것은 인터럽트에서 생성 된 규칙적이어야합니다. 카운터의 롤오버 / 오버플로는 일반적으로이 작업을 수행하는 방법입니다. 그런 다음 인터럽트 핸들러는 바이트 크기 "세마포어"를 증가시킵니다. 이 시점에서 필요한 경우 전원 관리 기능을 사용할 수도 있습니다. 세마포어의 아이디어는 그 값이> 0이면 "메인 루프"를 실행해야한다는 것입니다.

  4. EXECUTIVE는 메인 루프를 실행합니다. 그것은 그 세마포어가 0이 아닌 것을 기다립니다 (I는이 세부 사항을 추상화합니다). 이 시점에서 카운터를 사용하여 이러한 진드기 (진드기 비율을 알고있는 CO)를 계산할 수 있으므로 현재 경영진 진드기가 1 초, 1 분 및 기타 일반적인 간격인지를 나타내는 플래그를 설정할 수 있습니다. 사용하고 싶을 수도 있습니다. 임원은 세마포어가> 0임을 알면 모든 "응용 프로그램"프로세스 "업데이트"기능을 통해 단일 패스를 실행합니다.

  5. 응용 프로그램 프로세스는 서로 효과적으로 배치되고 "업데이트"눈금으로 정기적으로 실행됩니다. 이것은 임원이 호출 한 기능 일뿐입니다. 이것은 아주 간단한 집에서 만든 RTOS를 사용하여 빈약 한 멀티 태스킹입니다.이 RTOS는 모든 응용 프로그램의 입력, 약간의 작업 및 종료에 의존합니다. 응용 프로그램은 자체 상태 변수를 유지해야하며 공정성을 강제 할 선제 적 운영 체제가 없기 때문에 계산을 오래 실행할 수 없습니다. 분명히 응용 프로그램의 실행 시간 (누적)은 주요 틱 기간보다 짧아야합니다.

위의 접근 방식은 쉽게 확장되어 비동기식으로 실행되는 통신 스택과 같은 메시지를 전달하고 통신 메시지를 응용 프로그램에 전달할 수 있습니다 ( "rx_message_handler"인 각 기능에 새 기능을 추가하고 다음과 같은 메시지 디스패처를 작성 함) 어느 응용 프로그램을 파견할지).

이 접근 방식은 이름을 지정하려는 거의 모든 통신 시스템에서 작동합니다. 많은 독점 시스템, 개방형 표준 통신 시스템 및 TCP / IP 스택에서도 작동합니다.

또한 잘 정의 된 인터페이스가있는 모듈 식 조각으로 구축 될 수 있다는 장점이 있습니다. 당신은 언제든지 조각을 꺼내고 다른 조각으로 대체 할 수 있습니다. 방법을 따라 각 지점에서 알려진 하층 부품 (아래 항목)을 기반으로하는 테스트 하네스 또는 핸들러를 추가 할 수 있습니다. 디자인의 약 30 % ~ 50 %가 일반적으로 쉽게 추가되는 특별히 작성된 단위 테스트를 추가하면 도움이 될 수 있습니다.

나는 이것을 한 걸음 더 나아가서 (이 작업을 수행 한 다른 사람이 훔친 아이디어) HAL 레이어를 PC의 동등한 것으로 바꿉니다. 예를 들어 PC에서 C / C ++ 및 winforms 또는 이와 유사한 것을 사용할 수 있으며 코드를주의해서 작성하면 각 인터페이스 (예 : EEPROM = PC 메모리로 읽은 디스크 파일)를 에뮬레이트 한 다음 PC에서 전체 임베디드 응용 프로그램을 실행할 수 있습니다. 친숙한 디버깅 환경을 사용하면 많은 시간과 노력을 절약 할 수 있습니다. 실제로 큰 프로젝트 만이 정도의 노력을 정당화 할 수 있습니다.

위의 설명은 임베디드 플랫폼에서 작업을 수행하는 방법에 고유하지 않은 것입니다. 나는 비슷한 많은 상업 조직을 만났습니다. 구현 방식은 일반적으로 구현 방식이 크게 다르지만 원칙은 종종 동일합니다.

위의 내용이 약간 맛이 났으면 좋겠습니다.이 접근 방식은 영구적으로 전원을 공급하는 100K 이상의 소스 라인을 통해 공격적인 배터리 관리를 통해 몇 kB로 실행되는 소형 임베디드 시스템에서 작동합니다. Windows CE와 같은 큰 OS에서 "내장"을 실행하면 위의 모든 내용이 중요하지 않습니다. 그러나 그것은 실제 임베디드 프로그래밍이 아닙니다.


2
타이밍 특성에 주로 관심이 있기 때문에 UART를 통해 테스트 할 수없는 대부분의 하드웨어 주변 장치. ADC 샘플링 속도, PWM 듀티 사이클, 다른 직렬 주변 장치 (SPI, CAN 등)의 동작 또는 단순히 프로그램의 일부 부분의 실행 시간을 확인하려는 경우에는이를 통해이 작업을 수행 할 수 없습니다 UART. 모든 임베디드 펌웨어 테스트에는 오실로스코프가 포함되어 있습니다. 임베디드 시스템이 없으면 임베디드 시스템을 프로그래밍 할 수 없습니다.

1
네, 물론입니다. 나는 단지 그 것을 언급하는 것을 잊었다. 그러나 UART를 설치하고 실행 한 후에는 테스트 또는 테스트 사례 (문제가 중요 함)를 매우 쉽게 설정하고 사물을 자극하고 사용자 입력을 허용하며 결과를 얻고 친숙한 방식으로 표시 할 수 있습니다. 이 + 당신의 CRO는 인생을 매우 쉽게 만듭니다.
quick_now

2

선택한 예제와 같이 여러 플랫폼에 대한 증분 개발 및 최적화의 오랜 역사를 가진 코드는 일반적으로 읽기가 더 어렵습니다.

C에 관한 것은 실제로 광범위한 API 풍부 성과 하드웨어 성능 (및 부족)에 걸쳐 플랫폼을 확장 할 수 있다는 것입니다. MacVim은 오늘날 일반적인 스마트 폰보다 메모리와 프로세서 성능이 1000 배 이상 낮은 시스템에서 반응이 좋았습니다. 루비 코드가 가능합니까? 그것이 당신이 선택한 성숙한 C 예제보다 단순하게 보일 수있는 이유 중 하나입니다.


2

나는 지난 9 년 동안 대부분 C 프로그래머로 지 냈으며 최근에 일부 Ruby on Rails 프론트 엔드에서 작업하고있다.

C에서 작업하는 것은 자동화 된 창고 (일반적으로 수십만 파운드, 최대 2 백만 파운드)를 제어하기위한 중간 크기의 맞춤형 시스템입니다. 기능의 예는 약간의 응답 시간 요구 사항과 더 높은 수준의웨어 하우스 워크 플로 관리로 기계와 인터페이스하는 사용자 지정 인 메모리 데이터베이스입니다.

우선 TDD를하지 않습니다. 단위 테스트를 소개하는 데 여러 번 시도했지만 C에서는 적어도 사용자 정의 소프트웨어를 개발할 때보 다 가치가 있습니다. 그러나 C에서 TDD가 Ruby보다 훨씬 덜 필요하다고 말하고 싶습니다. 주로 C가 컴파일되고 경고없이 컴파일되는 경우 Rails의 rspec 자동 생성 스캐 폴딩 테스트와 비슷한 테스트를 이미 수행 한 것입니다. 단위 테스트가없는 루비는 불가능합니다.

그러나 내가 말할 것은 C는 어떤 사람들이 만드는 것처럼 열심히 할 필요가 없다는 것입니다. 대부분의 C 표준 라이브러리는 이해할 수없는 함수 이름의 혼란이며 많은 C 프로그램이이 규칙을 따릅니다. 표준 라이브러리 기능에 대한 래퍼가 많지 않다는 사실이 기쁩니다 (표준 strncpy 대신 ST_Copy, regcomp / regexec 대신 ST_PatternMatch, iconv_open / iconv / iconv_close 대신 CHARSET_Convert 등). 우리의 사내 C 코드는 내가 읽은 다른 대부분의 것보다 나에게 더 잘 읽 힙니다.

그러나 다른 고급 언어의 규칙이 적용되지 않는다고 말하면 동의하지 않습니다. 좋은 C 코드는 객체 지향을 많이 느낀다. 자원에 대한 핸들을 초기화하고 핸들을 인수로 전달하는 일부 함수를 호출하여 결국 자원을 해제하는 패턴이 종종 나타납니다. 실제로 객체 지향 프로그래밍의 디자인 원칙은 주로 사람들이 절차 적 언어로하는 좋은 일에서 비롯되었습니다.

C가 정말로 복잡 해지는 시간은 종종 기본적으로 매우 낮은 수준의 장치 드라이버 및 OS 커널과 같은 일을 할 때 종종 있습니다. 더 높은 수준의 시스템을 작성할 때 C의 더 높은 수준의 기능을 사용하고 낮은 수준의 복잡성을 피할 수 있습니다.

살펴볼만한 흥미로운 점 중 하나는 Ruby 용 C 소스 코드입니다. Ruby API 문서 (http://www.ruby-doc.org/core-1.9.3/)에서 다양한 메소드의 소스 코드를 클릭하여 볼 수 있습니다. 흥미로운 점은이 코드가 아주 멋지고 우아해 보인다는 것입니다. 상상만큼 복잡해 보이지는 않습니다.


" ... C의 상위 레벨 기능을 사용할 수도 있습니다. " ;-)
alk

나는 비트 드라이버보다 높은 레벨을 의미하며 장치 드라이버 유형 코드에서 볼 수있는 포인터 마법사에 대한 포인터입니다! 그리고 함수 호출의 오버 헤드에 대해 걱정하지 않는다면 합리적으로 높은 수준의 C 코드를 만들 수 있습니다.
asc99c

" ... 당신은 정말로 합리적으로 높은 수준의 C 코드를 만들 수 있습니다. ", 물론, 나는 그것에 전적으로 동의합니다. 그러나 " ... 더 높은 수준의 기능 ... "은 C의 것이 아니지만 여러분의 머릿속에 있지 않습니까?
alk

2

내가 한 것은 장치 독립적 코드와 장치 독립적 코드를 분리 한 다음 장치 독립적 코드를 테스트하는 것입니다. 좋은 모듈 성과 훈련을 통해 대부분 잘 테스트 된 코드베이스를 만들 수 있습니다.


2

당신이 할 수없는 이유가 없습니다. 문제는 다른 유형의 개발에서와 같이 멋진 "기성품"단위 테스트 프레임 워크가 없을 수 있다는 것입니다. 괜찮아. 테스트에 "자신의 롤"방식을 사용해야한다는 의미입니다.

예를 들어 A / D 변환기에 "가짜 입력"을 생성하도록 계측을 프로그래밍해야 할 수도 있고 임베디드 장치가 응답하도록 "가짜 데이터"스트림을 생성해야 할 수도 있습니다.

"TDD"라는 단어를 사용하는 것에 대한 저항에 부딪 치면 "DVT"(디자인 검증 테스트)라고 부르면 EE가 아이디어를보다 편안하게 만들 수 있습니다.


0

임베디드 장치에서 또는 드라이버 또는 사용자 정의 부트 로더 등과 같은 것들을 개발할 때 TDD를 수행 할 수 있습니까?

얼마 전에 ARM CPU 용 첫 번째 레벨 부트 로더를 작성해야했습니다. 실제로이 CPU를 판매하는 사람들 중 하나가 있습니다. 그리고 부트 로더가 부트 로더를 부팅하는 방식을 사용했습니다. 그러나 두 파일을 하나가 아닌 NOR 플래시로 플래시해야하므로 부트 로더의 크기를 첫 번째 부트 로더로 빌드하고 부트 로더 등을 변경할 때마다 다시 빌드해야했습니다.

그래서 부트 로더의 기능을 우리의 기능으로 통합하기로 결정했습니다. 상용 코드이기 때문에 모든 것이 예상대로 작동하는지 확인해야했습니다. 그래서 QEMU 를 수정 하여 해당 CPU의 IP 블록 (모두가 아니라 부트 로더에 닿는 것만)을 에뮬레이트하고 QEMU에 코드를 추가하여 PLL, UART, SRAM 컨트롤러 및 곧. 그런 다음이 CPU를 지원하도록 부트 로더를 업그레이드 한 후 부트 로더와 에뮬레이터에 제공하는 출력을 비교하면 몇 가지 버그를 발견하는 데 도움이됩니다. 이 코드는 부분적으로 ARM 어셈블러, 부분적으로 C로 작성되었습니다. 또한 수정 된 QEMU를 사용하면 JTAG와 실제 ARM CPU를 사용하여 잡을 수없는 버그 하나를 발견 할 수있었습니다.

따라서 C 및 어셈블러에서도 테스트를 사용할 수 있습니다.


-2

예, 임베디드 소프트웨어에서 TDD를 수행 할 수 있습니다. 그것이 불가능하거나 관련이 없거나 적용 할 수 없다고 말하는 사람들은 정확하지 않습니다. TDD는 다른 소프트웨어와 마찬가지로 임베디드로 얻을 수있는 중요한 가치가 있습니다.

대상에서 테스트를 실행하는 것이 아니라 하드웨어 종속성을 추상화하고 호스트 PC에서 컴파일 및 실행하는 것이 가장 좋습니다.

TDD를 수행 할 때 많은 테스트를 작성하고 실행하게됩니다. 이를 위해 소프트웨어가 필요합니다. 자동 테스트 검색 및 모의 생성을 통해 쉽고 빠르게 테스트 프레임 워크를 만들 수 있습니다.

C의 가장 좋은 옵션은 Ceedling입니다. 여기에 내가 쓴 글이 있습니다 :

http://www.electronvector.com/blog/try-embedded-test-driven-development-right-now-with-ceedling

그리고 그것은 루비로 만들어졌습니다! 당신은 알 필요가 없습니다 모든 하지만 그것을 사용하는 루비를.


답은 독자적으로있을 것으로 예상됩니다. 독자가 외부 자료를 찾아가 물질을 발견하도록 강요하는 것은 Stack Exchange에서 눈살을 찌푸리게합니다 . 사이트 품질 표준에 맞도록 편집을 고려하십시오
gnat

Ceedling에는 비동기 이벤트를 지원하는 메커니즘이 있습니까? 실시간 임베디드 애플리케이션의 가장 어려운 측면 중 하나는 스스로 모델링하기 어려운 매우 복잡한 시스템으로부터 입력을받는 것입니다.
Jay Elston

@Jay 그것은 그것을 지원하기 위해 특별히 아무것도 없습니다. 그러나 나는 조롱으로 그것을 테스트하고 그것을 지원하기 위해 아키텍처를 설정함으로써 성공했습니다. 예를 들어, 최근 인터럽트 기반 이벤트가 대기열에 들어간 다음 "이벤트 핸들러"상태 시스템에서 소비되는 프로젝트를 진행했습니다. 이것은 본질적으로 이벤트가 발생할 때마다 호출되는 함수입니다. 해당 함수를 테스트 할 때 큐에서 이벤트를 가져 오는 함수 호출을 조롱하여 시스템에서 발생하는 모든 이벤트를 시뮬레이션 할 수 있습니다. 시운전도 여기에 도움이됩니다.
cherno
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.