이글의 전부 또는 일부, 사진, 소스프로그램 등은 저작자의 동의 없이는 상업적인 사용을 금지합니다. 또한, 비상업적인 목적이라하더라도 출처를 밝히지 않고 게시하는 것은 금지합니다.



$19 정도에 미세 먼지 센서 SDS011을 구입했습니다.




이 센서는 미세먼지 PM2.5와 PM10을 측정하여 serial로 전달해줍니다. 통신 파라미터는 다음과 같습니다.

통신 속도    : 9600bps
패리티        : 없음
데이터 비트 : 8
스톱비트     : 1

SDS011이 전달하는 정보는 다음과 같습니다.




1초에 한 번, 길이가 10바이트로 고정된 패킷이 옵니다. 이렇게 고정된 길이의 패킷이 오는 것은 HAL 드라이버의 UART 인터럽트를 적용하기에 딱 좋은 상황입니다. 그런데 실제로 10자를 단위로 인터럽트가 걸리도록 설정해 보았는데, 무엇이 잘못되었는지 콜백 함수인 HAL_UART_RxCpltCallback() 함수를 호출하지 않습니다. 1자를 단위로 인터럽트가 발생하도록 하면 잘 동작하는데, 2자 이상 받은 후에 인터럽트가 발생하도록 하면 콜백 함수를 호출하지 않습니다. 한 글자씩 받다가 10자가 되면 main() 함수의 while() 문에서 처리하도록 프로그래밍하겠습니다.

앞의 글 DHT22(AM2302) 온도 습도 사용하기에서 만든 프로젝트를 확장해서 MGG19264 그래픽 lcd에 미세먼지 정보를 출력해 보겠습니다.



1. 프로젝트 만들기


STM32CubeMX에서 앞의 글에서 만든 STM32F130DTH22.ioc를 연 후에, USART1을 사용하도록 다음과 같이 변경합니다.



[USART1]의 [Mode]를 Asyncronous로 지정했고, [Parameter Settings]에서 Baud Rate를 9600으로 지정했습니다. [NVIC Settings]에서 USART1 global interrupt를 Enable 시켰습니다.


STM32CubeMX의 맨 위 [File] 메뉴에서 [Save Project As] 항목을 클릭하여 다음과 같이 변경하여 저장합니다.



프로젝트 명이 다음의 그림과 같이 바뀐 것을 확인하고, [GENERATE CODE] 버튼을 눌러 코드를를 생산합니다. 본 글에서는 프로젝트명을 STM32F103AIR로 정했습니다.




2. 파일 복사 및 코드 추가하기



앞의 글DHT22(AM2302) 온도 습도 사용하기에서 만든 프로젝트로부터 다음의 파일들을 복사합니다.

[Inc] 폴더 안의 헤더 파일 4개(AM2302.h, GlcdDisplay.h, M19264Display.h, ST7565Font.h)를 새 프로젝트의 [Inc] 폴더로, [Src] 폴더 안의 소스프로그램 4개(AM2302.c, GlcdDisplay.c, M19264Display.c, ST7565Font.c)를 새 프로젝트의 [Src] 폴더로 각각 복사합니다.

main.h, main.c, stm32f1xx_it.c 등 3 개의 파일을 수정할 예정입니다.

1)main.h 파일에 다음의 내용들을 추가합니다.

/* USER CODE BEGIN EFP */
extern uint8_t intFlag;
extern uint8_t SDS011Msg[];
extern uint8_t SDS011idx;

/* USER CODE END EFP */

/* USER CODE BEGIN Private defines */
#define SDS011                        huart1
#define SDS011_MESSAGE_LENGTH         10

#define NO_INTERRUPT                  0
#define INT_TIM2_AM2302               (1 << 0)
#define INT_UART_SDS011               (1 << 1)

#define TEMP_SECOND_COL               (M19264_RESOLUTION_X / 2)  //  column position for temp
#define TEMP_VALUE_COL                40                         // column position for temp value
#define QUEUE_BUFFER_LENGTH           1024                       // queue size
/* USER CODE END Private defines */


인터럽트를 처리하기 위한 전역 변수 intFlag을 선언했고, 매크로 들을 정의했습니다

2)main.c 파일에 다음 내용들을 입력합니다. 주석은 입력할 위치를 알리기 위해 같이 나열합니다.

/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include "M19264Display.h"
#include "GlcdDisplay.h"
#include "AM2302.h"

/* USER CODE END Includes */

/* Private macro ----------------------------------------------------------------*/
/* USER CODE BEGIN PM */
#define PM25_GOOD                150
#define PM25_COMMON              350
#define PM25_BAD                 750
#define PM10_GOOD                300
#define PM10_COMMON              800
#define PM10_BAD                 1000 

/* USER CODE END PM */

/* USER CODE BEGIN PV */
uint8_t intFlag;
uint8_t SDS011Msg[10] = {0,};
uint8_t SDS011idx = 0;

/* USER CODE END PV */

/* USER CODE BEGIN PFP */
uint8_t DisplayChar(uint8_t ch);

/* USER CODE END PFP */


main() 함수 내에 다음과 같은 코드를 입력합니다.

  /* USER CODE BEGIN 1 */
  uint8_t ret;
  AM2302DATA data;
  int16_t pm25, pm10;
  char* pState[] = {"좋음", "보통", "나쁨", "매우나쁨"};
  char *p1, *p2;

  /* USER CODE END 1 */

  HAL_Delay(2000);
  intFlag = NO_INTERRUPT;
  HAL_TIM_Base_Start_IT(&htim2);
  GlcdInitialize();
  HAL_UART_Receive_IT(&SDS011, SDS011Msg, 1);

  /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
    if(intFlag & INT_TIM2_AM2302)
    {
      intFlag &= ~INT_TIM2_AM2302;
      ret = AM2302ReadData(&data);
      if(ret == AM2302_SUCCESS)
      {
        GlcdGraphicGotoxy(0, 0);
        printf("¿Âµµ:%3d.%d\n", data.temp / 10, data.temp % 10);
        GlcdGraphicGotoxy(TEMP_SECOND_COL, 0);
        printf("½Àµµ:%3d.%d\n", data.humid / 10, data.humid % 10);
      }
      else
      {
        GlcdGraphicClear();
        printf("Read Error(%d)\n", ret);
      }
    }
    if(intFlag & INT_UART_SDS011)
    {
      intFlag &= ~INT_UART_SDS011;
      SDS011idx = 0;
      pm25 = SDS011Msg[3] * 256 + SDS011Msg[2];
      pm10 = SDS011Msg[5] * 256 + SDS011Msg[4];
      for(int i = 0;i < 10;i++) SDS011Msg[i] = 0;
      GlcdGraphicGotoxy(0, 16);
      printf("PM25:%3d.%d\n", pm25 / 10, pm25 % 10);
      GlcdGraphicGotoxy(TEMP_SECOND_COL, 16);
      printf("PM10:%3d.%d\n", pm10 / 10, pm10 % 10);
      p1 = pState[pm25 <= PM25_GOOD ? 0:(pm25 <= PM25_COMMON ? 1:(pm25 <= PM25_BAD ? 2:3))];
      p2 = pState[pm10 <= PM10_GOOD ? 0:(pm25 <= PM10_COMMON ? 1:(pm25 <= PM10_BAD ? 2:3))];
      GlcdGraphicGotoxy(TEMP_VALUE_COL - 3 * ASCII_FONTSIZE_X, 32);
      printf("%8s\n",  p1);
      GlcdGraphicGotoxy(TEMP_SECOND_COL + TEMP_VALUE_COL - 3 * ASCII_FONTSIZE_X, 32);
      printf("%8s\n",  p2);
    }
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
  }



변수 pm25는 초미세먼지 (2.5um의 농도 * 10)의 값을 갖고 있는 변수이고, pm10은 미세먼지 (10um의 농도 * 10)의 값을 갖고 있는 변수입니다. 각각의 농도에 따라 미세농도의 예보 기준에 의하여 "좋음", "보통", "나쁨", "매우나쁨"으로 판정을 내립니다. 미세먼지 예보 기준은 서울시 미세먼지 정보센터에서 찾아왔습니다.



3)st32f1xx_it.c 파일을 열어 USART1의 인터럽트 핸들러 USART1_IRQHandler()이 다음과 같도록 입력합니다.

void USART1_IRQHandler(void)
{
  /* USER CODE BEGIN USART1_IRQn 0 */
  if ((__HAL_UART_GET_FLAG(&huart1, UART_FLAG_RXNE) != RESET))
  {
    SDS011Msg[SDS011idx] = (uint8_t)(SDS011.Instance->DR & (uint8_t)0x00FF);
    if(SDS011idx < SDS011_MESSAGE_LENGTH - 1) SDS011idx++;
    else
    {
      intFlag |= INT_UART_SDS011;
    }
  }
  __HAL_UART_CLEAR_PEFLAG(&huart1); /* clear event flag */
  return;
  /* USER CODE END USART1_IRQn 0 */
  HAL_UART_IRQHandler(&huart1);
  /* USER CODE BEGIN USART1_IRQn 1 */

  /* USER CODE END USART1_IRQn 1 */
}


main() 함수에서 SDS011이 연결된 USART1로 데이터가 들어오면 USART1 인터럽트가 발생하도록 처리했습니다. USART1인터럽트가 발생하면 인터럽트 핸들러 USART1_IRQHandler() 함수가 호출됩니다. 이 핸들러에서는 새로 들어온 데이터를 SDS011Msg[SDS011idx]에 넣습니다. SDS011idx 변수는 (현재까지 들어온 데이터 수 - 1)의 값을 가지고 있습니다. 이 변수의 값이 9보다 작으면 SDS011idx의 값을 1증가 시키고 인터럽트 핸들러를 마칩니다. 이 변수의 값이 9 이상이면 10개의 데이터가 다 들어온 것이므로 main() 함수에서 처리하도록 infFlag의 값을 설정하고 인터럽트 핸들러를 마칩니다. SDS011로부터 도착한 정보는 위에서 입력한 main() 함수의 while() 문 안의 if(intFlag & INT_UART_SDS011) 문 안에서 처리합니다.



동작하는 사진입니다.


소스 프로그램을 zip 파일로 압축하여 올립니다.


STM32F103AIR.zip


블로그 이미지

엠쿠스

Microprocessor(STM32, AVR)로 무엇인가를 만들어 보고자 학습 중입니다.

,