앞의 글 미세먼지 센서 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 |
STM32F103AIR.zip