[Embedded] 쓰레드 흉내내기(장치 통합)

강지원·2025년 3월 27일

STM32 강의

목록 보기
33/33
post-thumbnail

FND에 온도 표시

문제점 1. 동시에 LED가 모든 칸이 켜지지 않음

동시에 불이 들어올 수 없을까?
While문 안에서 계속 FND를 점유하는 문제점이 생김

해결방법 :
타이머 인터럽트 사용
타이머(Timer) 인터럽트를 사용해 일정 주기마다 7세그먼트를 갱신하면 메인 루프에서 다른 작업을 수행 가능
7세그먼트 갱신 작업은 인터럽트에서 수행하고, 메인 루프에서는 다른 작업 처리

해결하기

1. TIM3 설정하기

현재 보드 클럭 72MHz

72MHz / 72 = 1MHz
1MHz / 100 = 10KHz
100ms 마다 타이머 인터럽트 발생

2. 우선순위 설정

7-segment의 우선순위 = 10으로 설정(별로 안중요함)
업로드중..

3. 타이머 인터럽트 설정

/* stm32f1xx_it.c */
void TIM3_IRQHandler(void) {
	/* USER CODE BEGIN TIM3_IRQn 0 */

	digit4_temper((int)getCurrentTemper() * 10); /* USER CODE END TIM3_IRQn 0 */
	HAL_TIM_IRQHandler(&htim3);
	/* USER CODE BEGIN TIM3_IRQn 1 */

	/* USER CODE END TIM3_IRQn 1 */
}

digit4_temper() 수정

/* fnd_controller.c */
static uint8_t m_tempercount = 0;
void digit4_temper(int temper) {
	int n1, n2, n3, n4;
	n1 = (int) temper % 10;
	n2 = (int) (temper % 100) / 10;
	n3 = (int) (temper % 1000) / 100;
	n4 = (int) (temper % 10000) / 1000;

	switch (m_tempercount) {
	case 0:
		send2(_LED_0F[n1], 0b0001);
		break;
	case 1:
		send2(_LED_0F[n2] & 0x7F, 0b0010);
		break;
	case 2:
		if (temper > 99)
			send2(_LED_0F[n3], 0b0100);
		break;
	case 3:
		if (temper > 999)
			send2(_LED_0F[n4], 0b1000);
		break;
	default:
		break;
	}

	m_tempercount++;

	if (temper > 999 && m_tempercount >= 4) {
		m_tempercount = 0;
	} else if (temper > 99 && m_tempercount >= 3) {
		m_tempercount = 0;
	} else if (temper <= 99 && m_tempercount >= 2) {
		m_tempercount = 0;
	}
}

쓰레드 문제 해결방안

인터럽트(Interrupt) 와 비동기 처리(Non-blocking) 를 통해 쓰레드와 비슷한 효과를 낼 예정이다.

  1. 온도 정보를 가져오는 통신 구간 - 우선순위 1

  2. 온도정보를 가져오는 동안 7세그먼트를 제어하지 않는다

  3. 너무 오랫동안 온도정보를 가져오는 시간을 가지면, 7세그먼트가 안켜지는 시간이 너무 길다.
    그래서 온도정보를 가져오는 부분을
    1) 분해하고
    2) 간추린다

  4. 분해를 해서 중간중간에 7세그먼트를 키는 부분이 동작하도록 한다.

🔎 1. 온도 정보 가져오는 통신 구간의 우선순위를 높이는 이유

온도 정보는 실시간 데이터이기 때문에 정확하고 빠르게 가져와야 합니다. 온도 정보가 지연되면 다음과 같은 문제가 발생할 수 있습니다:

화면에 표시되는 온도가 실제 값과 차이가 생길 수 있음

온도 제어가 필요한 경우, 값이 늦게 업데이트되면 오차 발생 가능

✅ 따라서 온도 정보를 가져오는 작업의 우선순위를 높이면 온도 데이터의 정확성과 일관성을 유지할 수 있습니다.

🔎 2. 온도 정보를 가져오는 동안 7세그먼트를 제어하지 않는 이유

온도 정보를 가져오는 동안 7세그먼트를 제어하면 다음과 같은 충돌이 발생할 수 있습니다:

온도 정보 통신 중 인터럽트 발생 시, 7세그먼트를 갱신하려고 하면 통신 지연 발생 가능

온도 값이 제대로 반영되지 않거나 7세그먼트가 깜빡이거나 오작동할 가능성 있음

✅ 따라서 온도 정보를 가져오는 동안은 7세그먼트를 잠시 멈추고 통신이 끝나면 다시 제어해야 합니다.

🔎 3. 너무 오랫동안 온도 정보를 가져오면 7세그먼트 표시 지연 발생

온도 정보를 가져오는 데 시간이 오래 걸리면 다음과 같은 문제가 발생합니다:

7세그먼트가 업데이트되지 않아 화면이 멈춘 것처럼 보일 수 있음

사용자 입장에서는 온도 표시가 늦어져 불편함을 느낄 수 있음

✅ 따라서 온도 정보를 가져오는 시간을 줄여야 실시간 응답성과 화면의 매끄러운 갱신이 가능합니다.

🔎 4. 온도 정보 가져오는 작업을 분해하고 간추리는 이유

온도 정보를 가져오는 작업을 한 번에 수행하면 다음과 같은 문제가 발생합니다:

온도 정보 취득이 끝날 때까지 7세그먼트 갱신이 멈춤

시스템이 블로킹(Blocking) 상태가 되어 다른 작업이 멈추게 됨

→ 온도 정보를 분해하고 간추리면 다음과 같은 장점이 있습니다:
✔️ 온도 정보 취득과 7세그먼트 갱신이 번갈아 수행됨
✔️ 온도 정보 취득 중에도 7세그먼트 갱신이 지속되기 때문에 화면 깜빡임 방지
✔️ 실시간 응답성이 개선됨

💡 결국, 온도 정보 취득과 7세그먼트 갱신을 동시에 원활하게 처리하려면

✅ 온도 정보 취득의 우선순위를 높이고
✅ 온도 취득 작업을 나누고
✅ 취득 중간에 7세그먼트를 갱신하면
👉 실시간 응답성과 안정성이 개선됨

결국 "온도 취득 작업 분리 + 인터럽트를 통한 동시 처리" 가 핵심입니다. 😎

온도정보 가져오는 부분 분해

bool	Ds18b20_Init(void)
{
	uint8_t	Ds18b20TryToFind=5;
	do
	{
		OneWire_Init(&OneWire,_DS18B20_GPIO ,_DS18B20_PIN);
		TempSensorCount = 0;	
		while(HAL_GetTick() < 3000)
			Ds18b20Delay(100);
		OneWireDevices = OneWire_First(&OneWire);
		while (OneWireDevices)
		{
			Ds18b20Delay(100);
			TempSensorCount++;
			OneWire_GetFullROM(&OneWire, ds18b20[TempSensorCount-1].Address);
			OneWireDevices = OneWire_Next(&OneWire);
		}
		if(TempSensorCount>0)
			break;
		Ds18b20TryToFind--;
	}while(Ds18b20TryToFind>0);
	if(Ds18b20TryToFind==0)
		return false;
	for (uint8_t i = 0; i < TempSensorCount; i++)
	{
		Ds18b20Delay(50);
    DS18B20_SetResolution(&OneWire, ds18b20[i].Address, DS18B20_Resolution_12bits);
		Ds18b20Delay(50);
    DS18B20_DisableAlarmTemperature(&OneWire,  ds18b20[i].Address);
  }
	return true;
}

init 수정본

uint16_t isTemperSensorInit(void){
	return m_init;
}
bool Ds18b20_Init_Simple(void) {
	m_init = 0;
	OneWire_Init(&OneWire, _DS18B20_GPIO, _DS18B20_PIN);
	//OneWire_First(&OneWire);
	OneWire.ROM_NO[0] = 0x28;
	OneWire.ROM_NO[1] = 0x1f;
	OneWire.ROM_NO[2] = 0x46;
	OneWire.ROM_NO[3] = 0x46;
	OneWire.ROM_NO[4] = 0xd4;
	OneWire.ROM_NO[5] = 0xca;
	OneWire.ROM_NO[6] = 0x24;
	OneWire.ROM_NO[7] = 0x6b;

	OneWire_GetFullROM(&OneWire, temperSensor.Address);

	Ds18b20Delay(50);
	DS18B20_SetResolution(&OneWire, temperSensor.Address,DS18B20_Resolution_12bits);
	Ds18b20Delay(50);
	DS18B20_DisableAlarmTemperature(&OneWire, temperSensor.Address);
	
	m_init = 1;
	return true;
}

온도정보 가져오는 부분 분해

bool Ds18b20_ManualConvert(void) {
#if (_DS18B20_USE_FREERTOS==1)
	Ds18b20StartConvert=1;
	while(Ds18b20StartConvert==1)
		Ds18b20Delay(10);
	if(Ds18b20Timeout==0)
		return false;
	else
		return true;	
	#else	
	Ds18b20Timeout = _DS18B20_CONVERT_TIMEOUT_MS / 10;
	DS18B20_StartAll(&OneWire);
	Ds18b20Delay(100);
	while (!DS18B20_AllDone(&OneWire)) {
		Ds18b20Delay(10);
		Ds18b20Timeout -= 1;
		if (Ds18b20Timeout == 0)
			break;
	}
	if (Ds18b20Timeout > 0) {
		for (uint8_t i = 0; i < TempSensorCount; i++) {
			Ds18b20Delay(100);
			ds18b20[i].DataIsValid = DS18B20_Read(&OneWire, ds18b20[i].Address,
					&ds18b20[i].Temperature);
		}
	} else {
		for (uint8_t i = 0; i < TempSensorCount; i++)
			ds18b20[i].DataIsValid = false;
	}
	if (Ds18b20Timeout == 0)
		return false;
	else
		return true;
#endif
}

0개의 댓글