먼저 RTOS를 이용하여 저번 포스팅에서의 1번 문제를 해결해보자
일단 FreeRTOS를 설정해주자
해당 부분은 다음 블로그에 전에 작성해두었으니 참고 바란다.
참고로 우리는 2개의 Task만 사용할것이다.
https://kksp12y.tistory.com/83
코드는 아래와 같이 만들었다,
StartDefaultTask에 osDelay(5)를 준 이유는 딜레이를 주지않으면 Task2가 CPU를 점유할 수 없다.
따라서 점유할 시간을 주었다.
/* USER CODE END Header_StartDefaultTask */
void StartDefaultTask(void const * argument)
{
/* USER CODE BEGIN 5 */
/* Infinite loop */
for(;;)
{
display_number(score, 50);
osDelay(5);
}
/* USER CODE END 5 */
}
/* USER CODE BEGIN Header_StartTask02 */
/**
* @brief Function implementing the myTask02 thread.
* @param argument: Not used
* @retval None
*/
/* USER CODE END Header_StartTask02 */
void StartTask02(void const * argument)
{
/* USER CODE BEGIN StartTask02 */
/* Infinite loop */
for(;;)
{
if (state != 0) {
state = 0;
HAL_GPIO_WritePin(GPIOA, GPIO_PA3_LED_Pin, GPIO_PIN_SET); // LED ON
score += gunshot_1;
osDelay(200); // 500ms
} else {
HAL_GPIO_WritePin(GPIOA, GPIO_PA3_LED_Pin, GPIO_PIN_RESET); // LED OFF
}
if (state_1 != 0) {
state_1 = 0;
HAL_GPIO_WritePin(GPIOA, GPIO_PA4_LED_Pin, GPIO_PIN_SET); // LED ON
score += gunshot_10;
osDelay(200); // 500ms
} else {
HAL_GPIO_WritePin(GPIOA, GPIO_PA4_LED_Pin, GPIO_PIN_RESET); // LED OFF
}
}
/* USER CODE END StartTask02 */
}
실행 결과는 다음 영상과 같다.
https://www.youtube.com/shorts/NdaZm3j9XoA
여전히 채터링은 해결되지 않았다.
이제 채터링 부분을 해결해보겠다.
이전에 오실로스코프로 찍어보았는데도 역시 채터링은 존재하였기 때문에 해결해야한다고 생각했다.
SW적으로 해결하려고 한다.
인터럽트의 채터링을 방지하기위해 콜백함수를 수정해주자
uint32_t current_tick_1;
uint32_t current_tick_2;
uint32_t old_tick_1;
uint32_t old_tick_2;
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin){
if (GPIO_Pin == han_Pin) {
current_tick_1 = HAL_GetTick();
} else if(GPIO_Pin == han2_Pin) {
current_tick_2 = HAL_GetTick();
}
// current_tick_1과 old_tick_1의 차이를 계산할 때 오버플로우를 고려
int32_t diff_1 = (int32_t)(current_tick_1 - old_tick_1);
if (diff_1 < 0) { // 오버플로우가 발생한 경우
diff_1 += UINT32_MAX + 1; // 최대값을 더해서 차이를 계산
}
if(GPIO_Pin == han_Pin && diff_1 > 200) {
state++;
old_tick_1 = current_tick_1;
}
// current_tick_2와 old_tick_2의 차이를 계산할 때 오버플로우를 고려
int32_t diff_2 = (int32_t)(current_tick_2 - old_tick_2);
if (diff_2 < 0) { // 오버플로우가 발생한 경우
diff_2 += UINT32_MAX + 1; // 최대값을 더해서 차이를 계산
}
if(GPIO_Pin == han2_Pin && diff_2 > 200) {
state_1++;
old_tick_2 = current_tick_2;
}
}
변수를 추가해줬는데 이는 현재 틱시간과 이전 틱시간을 사용하여 200ms동안 들어오는 인터럽트는 무시하는 것이다.
궁금한게 여러개 있을 수 있다.
1. HAL_GetTick()이란?
2. 변수를 uint32_t로 해놓고 왜 함수안에서 int32_t로 형변환하지?
1번에 대한 답은 시간이다. cpu시간이라고 보면 된다. 아두이노 IDE를 사용하면 millis()을 많이 사용해 봤을 거다.
그거랑 같다고 보면 된다.
2번에 대한 답은 HAL_GetTick()이 반환하는 값이 uint32_t로 반환하기 때문이다.
그래서 받는 변수도 그 변수로 설정해주고, 이후 int32_t로 변환해준다.
여기서 왜 변환하냐면 uint32_t로 계속 시스템을 돌리면 언젠간 오버플로우가 발생한다.
그것을 방지하기 위해 int32_t로 설정하여 정상적으로 계산이 되게끔하기 위해서이다.
간단한 예를 들면 uint8_t라고 가정하자.
이것은 8비트이기에 256까지 나타낸다.
우리가 200ms를 계산하니
현재 시간은 200, 이전 시간은 0
현재 - 이전 = 200 이기 때문에 state가 1증가한다
그리고 이전 = 200, 현재는 다시 흘러간다.
현재는 200에서 흐르다가 255를 넘는 순간 256이 되고, 0부터 남은 시간이 다시흘러 현재에서 200을 뺀게 200이 나올때 까지 진행한다.
그럼 절떄 if문을 진입하지 못하게 된다.
결론은 오버플로우떄문에 형변환을 진행했다.
이렇게 세그먼트와 진동 감지 센서는 마무리가 되었다.
'STM32' 카테고리의 다른 글
STM32 버튼을 이용한 7세그먼트 초기화 (1) | 2024.12.25 |
---|---|
STM32로 진동 감지 센서 + 7세그먼트 제어 - 1 (1) | 2024.12.23 |
STM32로 DFR0027를 다루어 보기 (0) | 2024.12.22 |
STM32 익히기 2) (0) | 2024.12.05 |
STM32 익히기 1) (0) | 2024.12.02 |