이 튜토리얼에서는
ESP32, Arduino 코어 및 타이머 인터럽트를 사용하여
DHT22 센서에서 온도 측정값을 얻는 방법을 확인합니다.
테스트는 ESP32 개발 보드에 통합된 DFRobot의 ESP32 모듈 과
DFRobot DHT22 모듈을 사용 하여 수행되었습니다 .
소개
이 튜토리얼에서는
ESP32, Arduino 코어 및 타이머 인터럽트를 사용하여
DHT22 센서에서 온도 측정값을 얻는 방법을 확인합니다.
ESP32의 DHT22와 상호 작용하려면 보조 라이브러리가 필요합니다.
설치 방법과 ESP32를 DHT22에 연결하는 방법을 설명하는 이 튜토리얼을 확인하십시오 .
ESP32 타이머에 대한 소개는 이 이전 게시물을 확인하세요 .
아래 코드를 이해하는 데 중요한 타이머 개념을 자세히 설명합니다.
ESP32 타이머에 대해 기억해야 할 한 가지 중요한 점은
타이머가 64비트 카운터와 16비트 프리스케일러 로 구현된다는 것 입니다.
따라서 나중에 코딩 섹션에서 인터럽트가 트리거되어야 하는
카운터 값을 설정하여 타이머 인터럽트를 구성할 것입니다.
작업과 인터럽트 서비스 루틴 간의 동기화를 소개하는 자습서는 여기 를 확인 하십시오 .
해당 자습서에서 볼 수 있듯이 이러한 동기화를 달성하기 위해 FreeRTOS 세마포를 활용합니다.
구현 측면에서 코드는 주기적으로 DHT22 센서에서 측정값을 읽습니다.
그럼에도 불구하고 폴링 또는 Arduino 지연에 의존하는 대신
타이머 인터럽트를 사용하여 측정의 주기성을 구현합니다.
그러나 인터럽트 처리 기능은 가능한 한 빨리 실행되어야 하므로
해당 기능 내에서 DHT22와 통신해서는 안 됩니다.
따라서 인터럽트는 새로운 측정을 얻을 시간이 될 때
Arduino 메인 루프에 신호를 보내는 역할만 합니다.
우리는 각 측정 사이에 10초의 간격을 사용할 것입니다.
이는 기계 명령과 클럭 주파수에 대해 생각할 때 많은 시간입니다.
따라서 측정 사이에 메인 루프를 활성 상태로 두어
귀중한 CPU 시간을 낭비하는 것은 이치에 맞지 않습니다.
따라서 세마포어를 사용하여 다음 인터럽트를 기다리는 동안
메인 루프를 "차단" 상태로 유지하고 스케줄러가 다른 작업을 실행할 수 있는 CPU를 남겨둡니다.
전용 FreeRTOS 작업을 사용할 수도 있지만
이 예에서는 측정값만 가져오고 인쇄하므로
메인 루프를 사용하여 코드를 더 간단하게 유지합니다.
당연히 이 차단 아키텍처는
애플리케이션이 DHT22와 상호 작용하는 것 외에
다른 작업을 수행하지 않는 간단한 예제에 많은 것을 추가하지 않습니다.
그럼에도 불구하고 여러 작업이 동시에 실행될 수 있는
실제 응용 프로그램 사용 사례에서는
아무 것도 하지 않을 때 작업에서 CPU를 해제하는 것이 매우 중요합니다.
마지막 소개에서 DHT22는 온도 및 습도 측정을 모두 허용한다는 점을 고려하십시오.
그럼에도 불구하고 단순성과 동기화에 초점을 맞추기 위해 온도 측정값만 가져옵니다.
언급된 센서와의 상호 작용을 용이하게 하기 위해 필요한 모든 전자 장치가 이미 있고
ESP32에 대한 연결을 용이하게 하는 배선 터미널을 노출하는 DFRobot DHT22 모듈 을 사용하고 있습니다.
테스트는 ESP32 개발 보드에 통합된 DFRobot의 ESP32 모듈을 사용하여 수행되었습니다 .
코드
Includes and global variables
우리가 할 첫 번째 일은 DHTesp.h 라이브러리를 포함하는 것이므로
ESP32와 이 센서 간의 통신 프로토콜의 하위 수준 세부 정보에 대해 걱정할 필요 없이
상위 수준 API를 사용하여 DHT22와 상호 작용할 수 있습니다.
#include "DHTesp.h"
그런 다음 센서에서 측정값을 가져오는 데 사용할 DHTesp 클래스의 개체가 필요합니다 .
아래에서 볼 수 있듯이 이 개체의 일부 메서드를 호출하여 이를 수행합니다.
DHTesp dht;
타이머를 구성할 수 있으려면 hw_timer_t 유형의 변수에 대한 포인터를 선언해야 합니다 .
이것은 나중에 타이머를 구성하는 데 사용됩니다.
hw_timer_t * timer;
전역 변수 선언을 완료하려면
메인 루프와 인터럽트 서비스 루틴을 동기화하는 데 사용할 세마포어가 필요합니다.
SemaphoreHandle_t syncSemaphore;
설정 기능으로 이동하여 직렬 연결을 열어 시작하여
나중에 DHT22에서 얻은 측정값을 출력할 수 있습니다.
Serial.begin(115200);
다음으로 세마포어를 생성하여 전역 변수에 할당하여
메인 루프와 인터럽트 서비스 루틴을 동기화하는 데 사용할 수 있습니다.
xSemaphoreCreateBinary 함수를 호출하여 이진 세마포어를 생성 합니다.
이 함수는 인수를 취하지 않으며 다음 세마포어 관련 호출에서 사용해야 하는
SemaphoreHandle_t 를 반환 합니다.
FreeRTOS에는 세마포 계산 과 같은 다른 유형의 세마포가 있지만
바이너리만 필요한 간단한 동기화를 수행할 것이기 때문에 주의하십시오.
고려해야 할 또 다른 중요한 사항은
인터럽트를 설정하기 전에 항상 동기화 프리미티브를 초기화하여
예상보다 빨리 인터럽트가 발생하고 초기화되지 않은 세마포어를 사용하는 것을 방지하는 것입니다.
syncSemaphore = xSemaphoreCreateBinary();
인터럽트를 구성하기 전에 DHT22 센서 인터페이스를 초기화해야 합니다.
DHTesp 객체에서 setup 메소드를 호출하고 센서에 연결된 마이크로컨트롤러 핀의 번호를 입력으로 전달
하여 이를 수행 합니다.
나는 핀 27을 사용하고 있지만 다른 사람들과 함께 시도할 수 있습니다.
추가 인수를 전달하지 않으면
라이브러리가 ESP32에 연결된 센서를 자동으로 감지하려고 시도합니다.
이 절차는 라이브러리가 다른 센서를 지원하기 때문에 수행됩니다.
이 자동 감지에 문제 가 있는 경우 여기 에 정의된 값 DHT22 를
이 메서드의 두 번째 인수로 명시적으로 전달하는 것이 좋습니다 .
dht.setup(27);
다음으로 타이머를 구성하고 인터럽트에 바인딩합니다.
ESP32 타이머에 대한 자세한 내용은 이미 여기 에서 다루었 으므로
아직 익숙하지 않은 경우 해당 게시물을 살펴보십시오.
따라서 이전에 선언한 전역 변수에 저장할
hw_timer_t 유형의 구조에 대한 포인터를 반환하는
timerBegin 메서드를 호출하여 타이머를 초기화합니다 .
첫 번째 입력으로 사용하려는 타이머 번호를 전달해야 합니다.
ESP32에는 4개의 하드웨어 타이머가 있으며 0에서 3까지의 숫자를 전달하여
사용할 타이머를 지정해야 합니다. 타이머 0을 사용할 것입니다.
두 번째 인수로 프리스케일러 값을 전달해야
ESP32 타이머의 클록 신호 주파수를 2에서 65536(프리스케일러는 16비트)의 인수로 나눌 수 있습니다.
타이머의 ESP32 보드 클럭 주파수는 일반적으로 80MHz(Firebeetle 보드에서 사용되는 값)이므로
80으로 나눌 수 있으며 1MHz 의 신호를 얻습니다 .
이 값을 사용하면 타이머의 카운터가 1초마다 1000000 번 증가한다는 의미입니다 .
즉, 카운터 값이 마이크로초마다 증가함을 의미합니다 .
따라서 나중에 인터럽트를 트리거할 카운터 값을 지정할 때
마이크로초 단위로 값을 설정합니다.
timerBegin 메서드에 전달할 세 번째 값은
카운터가 카운트 업 또는 다운(각각 true 및 false)해야 하는지 여부를
간단히 나타내는 부울 값입니다. 카운트업하도록 설정하겠습니다.
timer = timerBegin(0, 80, true);
인터럽트를 트리거해야 하는 값을 구성하지 않았기 때문에
이 시점에서 타이머는 아직 활성화되지 않았습니다.
하지만 그렇게 하기 전에 인터럽트 처리 함수에 바인딩해야 합니다.
timerAttachInterrupt 함수 를 호출하여 이를 수행합니다.
첫 번째 입력으로 이전 호출에서 얻은 초기화된 타이머에 대한 포인터를 받습니다.
두 번째 입력으로 인터럽트 서비스 루틴 함수의 주소를 수신합니다 .
이를 onTimer 라고 합니다. 아래에서 구현을 확인하겠습니다.
세 번째 인수로 생성할 인터럽트가 에지(true)인지 레벨(거짓)인지를 나타내는
부울 값을 전달해야 합니다. 여기 또는 여기 에서 차이점을 확인할 수 있습니다 .
생성된 인터럽트가 에지 유형이 되도록 값을 true로 전달합니다.
timerAttachInterrupt(timer, &onTimer, true);
그런 다음 인터럽트를 트리거할 카운터 값을 설정해야 합니다.
timerAlarmWrite 함수를 호출하여 이를 수행 합니다.
첫 번째 입력으로 이전에 저장한 hw_timer_t 변수에 대한 포인터를 전달합니다 .
두 번째 매개변수로 인터럽트를 트리거할 카운터 값을 지정해야 합니다.
인터럽트가 10초마다 트리거되어야 한다고 가정합니다.
프리스케일러를 사용하여 마이크로초 단위로 값을 지정하므로 10000000 을 전달 합니다.
DHT22 최소 샘플링 기간을 준수하는 한 다른 값으로 시도할 수 있습니다
.
세 번째이자 마지막 인수로 인터럽트 생성시
카운터가 자동으로 다시 로드되어야 하는지 여부를 나타내는 부울 값을 전달해야 합니다.
true 값을 전달하므로 다시 로드되고 타이머가 주기적으로 계속 실행됩니다.
timerAlarmWrite(timer, 10000000, true);
마지막으로 timerAlarmEnable 함수 를 호출하고
타이머에 대한 포인터를 입력으로 전달 하여 타이머를 활성화합니다 .
timerAlarmEnable(timer);
최종 setup() 함수는 아래에서 볼 수 있습니다.
void setup() {
Serial.begin(115200);
syncSemaphore = xSemaphoreCreateBinary();
dht.setup(27);
timer = timerBegin(0, 80, true);
timerAttachInterrupt(timer, &onTimer, true);
timerAlarmWrite(timer, 10000000, true);
timerAlarmEnable(timer);
}
Main loop
메인 루프로 이동하여 여기에서 센서 측정을 처리합니다.
그럼에도 불구하고 우리는 Arduino 지연 기능을 사용하는 대신
인터럽트에 의존할 것이므로 우리의 접근 방식은
DHT22와의 상호 작용에 대한 이전 자습서 와 약간 다릅니다 .
그래서 우리가 할 첫 번째 일은 세마포어를 얻는 것입니다.
세마포어에 사용할 수 있는 단위가 없으면 메인 루프는 사용할 수 있게 될 때까지
차단해야 하므로 스케줄러가 다른 작업에 할당할 수 있도록 CPU를 해제해야 합니다.
xSemaphoreTake 함수를 호출하여 세마포어를 얻으려고 시도할 것입니다.
첫 번째 입력으로 세마포어를 전달하고 두 번째로 portMAX_DELAY 값을 전달 하여
장치를 사용할 수 있게 될 때까지 작업이 무기한 차단된 상태로 유지되도록 합니다.
xSemaphoreTake(syncSemaphore, portMAX_DELAY);
실행이 이 지점을 지나면 인터럽트가 발생하고
메인 루프가 차단 해제되었음을 의미합니다.
즉, DHT22에서 다른 온도 측정값을 얻을 시간입니다.
float를 반환할 dht 객체 의 getTemperature 메서드를 호출하여 이를 수행합니다 .
우리는 그 값을 직렬 포트에 출력할 것입니다.
float temperature = dht.getTemperature();
Serial.print("Temperature: ");
Serial.println(temperature);
그 후, 메인 루프는 처음으로 돌아가서 세마포어를 다시 얻으려고 시도하고
새로운 인터럽트가 발생할 때까지 잠겨 있습니다.
전체 Arduino 루프는 다음과 같습니다.
void loop() {
xSemaphoreTake(syncSemaphore, portMAX_DELAY);
float temperature = dht.getTemperature();
Serial.print("Temperature: ");
Serial.println(temperature);
}
Interrupt Service Routine
끝내기 위해 Interrupt Service Routine 구현을 작성해야 합니다.
먼저 IRAM_ATTR 속성을 선언에 추가해야 컴파일러에 의해 IRAM에 배치 된다는
이전 게시물 을 상기 해야 합니다 .
구현 측면에서 매우 간단합니다.
인터럽트가 발생하면 Arduino 메인 루프만 차단 해제하기를 원하므로
센서에서 읽을 코드를 실행합니다.
우리가 보았듯이 메인 루프는 세마포어에서 차단되어
해당 세마포어에 장치가 추가될 때까지 무기한 기다립니다.
따라서 우리가 해야 할 유일한 일은
xSemaphoreGiveFromISR 함수를 호출 하여 세마포어에 단위를 추가하는 것입니다.
따라서 새 반복을 실행하기 위해 메인 루프를 차단 해제합니다.
은 "참고 FromISR " 를 이 내부에서 호출하는 것이 안전 표시하는 함수 이름의 끝에서 나는 nterrupt S 서 비 스 R의 outine. ISR 내부에서는 xSemaphoreGive 함수를 사용해서는 안 되며 항상 xSemaphoreGiveFromISR 함수를 사용해야 합니다.
이 함수는 첫 번째 입력으로 세마포어를 수신하고 두 번째로 사용할 필요가 없는 인수를 수신하므로 이를 NULL로 설정할 수 있습니다. 두 번째 인수에 대한 자세한 내용은 여기에서 확인할 수 있습니다 .
void IRAM_ATTR onTimer() {
xSemaphoreGiveFromISR(syncSemaphore, NULL);
}
전체 코드
최종 전체 코드는 아래에서 볼 수 있습니다.
#include "DHTesp.h"
DHTesp dht;
hw_timer_t * timer;
SemaphoreHandle_t syncSemaphore;
void IRAM_ATTR onTimer() {
xSemaphoreGiveFromISR(syncSemaphore, NULL);
}
void setup() {
Serial.begin(115200);
syncSemaphore = xSemaphoreCreateBinary();
dht.setup(27);
timer = timerBegin(0, 80, true);
timerAttachInterrupt(timer, &onTimer, true);
timerAlarmWrite(timer, 10000000, true);
timerAlarmEnable(timer);
}
void loop() {
xSemaphoreTake(syncSemaphore, portMAX_DELAY);
float temperature = dht.getTemperature();
Serial.print("Temperature: ");
Serial.println(temperature);
}
코드 테스트
코드를 테스트하려면
장치와 DHT22 센서 사이의 배선이 이미 완료되고 전원이 켜져 있다고
가정하고 코드를 컴파일하고 Arduino 코어를 사용하여 ESP32에 업로드하십시오.
절차가 완료되면 Arduino IDE 직렬 모니터를 엽니다.
아래 그림 1과 같이 주기적으로 측정값 인쇄를 시작해야 합니다.
그림 1 - DHT22 측정값을 보여주는 프로그램 출력.
'ESP32' 카테고리의 다른 글
ESP8266: Connecting to MQTT broker (0) | 2021.08.16 |
---|---|
ESP32: Publishing messages to MQTT topic (0) | 2021.08.16 |
ESP32: Connecting to a WiFi network (0) | 2021.08.16 |
ESP32 아두이노: WebSerial (0) | 2021.08.14 |
ESP32: Ticker library (0) | 2021.08.14 |