앞의 글 미세먼지 센서 SDS011 사용하기의 프로그램에서 몇 가지 문제가 발생해서 약간 개선합니다.
첫번째 문제점은 USART1_IRQHandler() 함수에서 무조건 10개의 데이터를 받아서 표시하기 때문에, 첫 바이트부터 받지 못하면 엉뚱한 값이 표시되는 점입니다. 이를 해결하기 위해서 USART1_IRQHandler() 함수를 다음과 같이 수정합니다.
void USART1_IRQHandler(void) { /* USER CODE BEGIN USART1_IRQn 0 */ if ((__HAL_UART_GET_FLAG(&SDS011, UART_FLAG_RXNE) != RESET)) { SDS011Msg[SDS011idx] = (uint8_t)(SDS011.Instance->DR & (uint8_t)0x00FF); if(SDS011idx == 0) { if(SDS011Msg[SDS011idx] == 0xAA) SDS011idx = 1; } else if(SDS011idx == 1) { SDS011idx = (SDS011Msg[SDS011idx] == 0xC0 ? 2:0); } else if(SDS011idx < SDS011_MESSAGE_LENGTH - 1) SDS011idx++; else { intFlag |= INT_UART_SDS011; } } __HAL_UART_CLEAR_PEFLAG(&SDS011); /* 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 */ }
받은 데이터를 검사해서 첫바이트와 두번째 바이트를 검사하여, 헤더 값인 0xAA, 0xC0와 같은지 확인하고 같지 않으면 다시 처음부터 대기하도록 합니다.
위의 방법은 아주 간단하게 처리하는 방법입니다. 이 방법 보다는 stm32f407을 다루면서 썼던 글 STM32F407로 ESP8266 제어하기에서 다룬 Queue를 이용하는 방법을 사용하는 것이 더 좋은 해결 방법이라고 생각합니다.
두번째 문제점은 잘못된 정보를 출력하고 난 다음에 그 잘못된 정보가 지워지지 않고 남아 있는 경우가 발생한다는 점입니다. 오류 메시지를 출력하고 나서 그 메시지를 지우지 않기 때문에, 화면에 쓸데 없는 정보가 남아 있게 됩니다. 이 문제를 해결하기 위해서 출력하기 전에 출력할 영역을 먼저 지울 필요가 있습니다. 이 문제점을 해결하기 위해서 그래픽 lcd의 일정 영역을 지우는 함수를 만들 필요가 있습니다. 이 함수 이름을 일단 GlcdGraphicClearBox()로 정합니다.
그래픽 lcd의 일정 영역을 지우는 함수 GlcdGraphicClearBox()의 함수 원형을 다음과 같이 M19264Display.h에에 선언하고, 함수는 M19264Display.c 파일 안에 작성합니다.
우선 M19264Display.h에 추가할 내용입니다.
void GlcdGraphicClearBox(uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2);
M19264Display.c에 추가할 내용입니다.
void GlcdGraphicClearBox(uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2) { uint16_t i, j; if(x2 < x1) { i = x2; x2 = x1; x1 = i; } if(y2 < y1) { i = y2; y2 = y1; y1 = i; } y1 = (y1 / BITSPERBYTE) * BITSPERBYTE; y2 = (y2 / BITSPERBYTE + 1) * BITSPERBYTE - 1; for(j = y1;j < y2;j += BITSPERBYTE) { for(i = x1;i < x2;i++) { GlcdGraphicGotoxy(i, j); GlcdWriteData(0); } } GlcdGraphicGotoxy(x1, y1); }
main() 함수 안의 while() 문을 다음과 같이 수정합니다.
while (1) { if(intFlag & INT_TIM2_AM2302) { intFlag &= ~INT_TIM2_AM2302; ret = AM2302ReadData(&data); if(ret == AM2302_SUCCESS) { GlcdGraphicClearBox(0, 0, 191, 15); //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]; GlcdGraphicClearBox(0, 16, 191, 47); //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 */ }
세번째 문제점은 굳이 DisplayChar() 함수를 사용할 필요가 없었다는 점입니다. printf() 함수를 쓰기 위해 _write() 함수를 정의해서 사용했습니다. 이전의 _write() 함수는 DisplayChar() 함수를 호출했는데, DisplayChar() 함수의 내용이 이미 GlcdGraphicPuts() 함수에 구현되어 있기 때문에, DisplayChar() 함수를 삭제하고 다음과 같이 사용하도록 했습니다.
/* USER CODE BEGIN 4 */ int _write(int file, char *ptr, int len) { HAL_GPIO_WritePin(LED_GPIO_Port, LED_Pin, RESET); *(ptr + len) = 0; GlcdGraphicPuts((uint8_t *)ptr); HAL_GPIO_WritePin(LED_GPIO_Port, LED_Pin, SET); return len; } /* USER CODE END 4 */
동작하는 소스프로그램입니다.
'STM32F103' 카테고리의 다른 글
STM32F103 CDC 사용하기 (0) | 2018.12.28 |
---|---|
STM32F103으로 ESP8266 제어하기 - USART 프로그래밍 (0) | 2018.12.25 |
미세먼지 센서 SDS011 사용하기 (0) | 2018.12.12 |
DHT22(AM2302) 온도 습도 센서 사용하기 (2) | 2018.12.11 |
ST7565P GLCD 제어하기(제3편) - printf() 사용하기 (10) | 2018.12.10 |