[Mission]
📌 Mission
: 조이스틱을 이용한 밝기 제어
M1. 조이스틱을 이용하여 밝기를 제어한다.
M2. ADC 기능을 활용하여 조이스틱 입력 처리를 구현한다.
(Timer, Interrupt, DMA 방식 중 택1)
M3. FreeRTOS를 사용하여 조이스틱의 입력에 따라 LED 밝기를 조절하고
LED를 점멸 속도를 조정하는 두 가지 동시 작업을 관리한다.
수행 기간 : 6 hours
[Result]
📌 Flow Chart
Flow Chart Description
1. 각 기능들 초기화
2. Read Joystick Value
3. zVal = 1이면, LED Off
zVal = 0이면, LED On
4. xVal : Blink Speed Control
5. yVal : Brightness Control
📌 Pin Configuration
📌 Pin Setting
GPIO Setting
조이스틱의 z(swtich) 값을 입력 받아야 하므로 Input mode
플로팅 현상 방지를 위해 내부 풀업 설정
ADC Setting
ADC Conversion 1회마다 x, y 값을 하나씩 번갈아가며 받아올 것이므로 Discontinuous conversion mode Enable
Resolution(해상도)이 12bit이므로 Sampling Time은 그보다 큰 84 cycle로 설정
Interrupt 방식으로 값을 불러올 것이므로 Globle Interrupt Enavle
PWM Setting
RTOS Setting
1. Default Task : 인터럽트 발생 반복
2. LED Speed : LED Bling speed control
3. LED Lum : LED Brightness control
4, Time Base Source : TIM1(systick보다 HAL_TIM이 더 정확하다.)
5. NEWLIB Reentrant : Enable
📌 Code Description
Initial Code
// 변수 선언
/* USER CODE BEGIN PV */
int xVal;
int yVal;
int zVal;
int count;
int led_OnOff;
/* USER CODE END PV */
// Initialization
MX_GPIO_Init();
MX_ADC1_Init();
MX_TIM11_Init();
HAL_ADC_Start_IT(&hadc1);
HAL_TIM_PWM_Start(&htim11, TIM_CHANNEL_1);
Callback
void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef* hadc)
{
if(count == 0) xVal = HAL_ADC_GetValue(&hadc1);
else if(count == 1)
{
yVal = HAL_ADC_GetValue(&hadc1);
}
count++; // callback 1회 호출 시마다 value 하나씩 가져옴
if(count > 1) count = 0;
zVal = HAL_GPIO_ReadPin(z_Value_GPIO_Port, z_Value_Pin);
HAL_ADC_Start_IT(&hadc1);
}
1. ADC Callback 함수에서 HAL_ADC_Start()를 사용 못하는 이유는
이미 컨버젼이 완료된 이후에 Callback 함수가 호출되기 때문이다.
따라서 start 없이 GetValue 함수를 각각 호출해줘야 한다.
2. Count 변수를 활용하여 Interrupt 1회 당 x, y 값을 번갈아가며 입력받도록 코딩
3. Callback 함수는 한 번 호출되면 Interrupt Flag가 리셋되기 때문에
HAL_ADC_Start_IT(); 를 다시 호출해주어야 계속해서 인터럽트가 발생할 수 있다.
Task
void StartDefaultTask(void const * argument)
{
for(;;)
{
HAL_ADC_Start_IT(&hadc1);
osDelay(5);
}
}
Callback 함수는 인터럽트가 발생하면 Flag가 리셋되기 떄문에
Default Task로 ADC Interrupt의 반복 실행이 필요하다.
void led_spd_cntr(void const * argument)
{
int led_spd = 0;
for(;;)
{
led_spd = xVal/2;
if(led_spd <= 100) led_spd = 100;
osDelay(led_spd);
led_OnOff = ~led_OnOff;
}
}
조이스틱으로 입력되는 x값에 따라서 led_speed가 조정되도록 설정했고,
이 변수에 따라 Delay가 달라진다.
Delay 이후에 led_OnOff 변수를 토글시켜
LED가 딜레이에 따라 점멸하도록 설계하였다.
void led_lum_cntr(void const * argument)
{
int led_lum = 0;
for(;;)
{
htim11.Instance->CCR1 = led_lum;
if(zVal) led_lum = 0;
else
{
if(led_OnOff) led_lum = 0;
else led_lum = yVal*1000/4095;
osDelay(30);
}
}
}
조이스틱으로 입력되는 y값에 따라서 Led_lum 변수가 조정되도록 설정했고,
이 변수에 따라 CCR값이 변화하며, 이렇게 변화한 CCR 값에 따라 LED 밝기가 결정된다.
또한 조이스틱 버튼이 눌렸을 때,(zVal = 1)만 LED가 동작하도록 On/Off 기능을 추가했다.
📌 Operation
LED Blink Speed Control
LED Brightness Control
LED On/Off
📌 Review
Error
LED 점멸 속도와 밝기 조절 기능을 나눠서 시뮬레이션해보던 중,
둘 다 조이스틱의 y값만 받아서 동작하는 오류를 발견했다.
ADC Setting 중, Discontinious Conversion mode를 Disable 시켜놓은 것이 원인이었고
이로 인해 인터럽트 발생 시 xVal, yVal 변수에 y값만 받아오고 있었다.
해당 부분을 Enable로 수정하니, 오류가 해결되고 정상적으로 동작하였다.
아쉬웠던 점
짧은 기간에 수행된 프로젝트다 보니,
추가로 구현해보고 싶었던 기능들을 구상만 해놓고 구현하지 못해 아쉬움이 남았다.
- 여러 개의 LED를 동시에 컨트롤
- RGB LED로 모든 색을 표현하는 기능
- I2C LCD로 현재 밝기 및 속도 표시 기능
미리 실력을 더 쌓아뒀더라면,
짧은 시간일지라도 위 기능들을 충분히 구현하지 않았을까? 하는
생각이 들었고 이를 자기반성의 기회로 여겨 더욱 더 발전하는
내가 되는 계기로 삼고자 한다.