FND 설계하기 : 10000진 Counter를 FND에 출력하도록 설계, 1번 버튼을 누르면 Counter Stop, 2번 버튼 누르면 Counter Start, 3번 버튼을 누르면 숫자가 Clear 되도록 설계
FND. c Code :
FND의 메인 함수에는, FND를 켜기 위해 SEL 신호를 통해 Common 단자를 GPIO_WritePin 함수를 통해 PIN_RESET을 시켜줍니다(Annode Type). 또한, 들어오는 값을 Segment에 출력하기 위해 Decoding이 필요하기 때문에 switch - case 문을 통해 값을 변경해 줍니다.
https://github.com/Heeju99/embedded/blob/main/250619_LED_Button1/Src/driver/FND/FND.c
embedded/250619_LED_Button1/Src/driver/FND/FND.c at main · Heeju99/embedded
Contribute to Heeju99/embedded development by creating an account on GitHub.
github.com
ap_main.c Code :
버튼의 입력으로 10000bit Counter의 STOP, RUN, CLEAR 하도록 FSM을 설계해 줍니다. 기본적으로 STOP State에서 시작하며, Start 버튼이 입력되면 10000bit Counter가 시작되며 digit_split을 통해 FND의 각각의 자리에 출력됩니다. 이때, Stop 버튼을 입력하면 stop state로 천이하고, FND에 출력되는 값은 정지하게 됩니다. Clear Button을 입력하면 clear state로 천이되며 FND에 출력되는 값이 0으로 초기화 됩니다.
https://github.com/Heeju99/embedded/blob/main/250619_LED_Button1/Src/ap/ap_main.c
embedded/250619_LED_Button1/Src/ap/ap_main.c at main · Heeju99/embedded
Contribute to Heeju99/embedded development by creating an account on GitHub.
github.com
동작 영상 :
Button (위 -> 아래) : STOP Button, START Button, Clear Button
기존의 STOP 상태에서, 버튼을 차례대로 입력하며 counter가 Run, Stop , Clear 되는 모습.
인터럽트 (Interrupt) : main 함수에서 계속 진행을 하다가, 잠깐 중단하고 다른 일을 처리하고 복귀
-> (기존의 FND 코드에서는 FND 4자리가 모두 한번에 실행되어 출력이 이상하게 됨 -> 인터럽트를 통한 각 FND 자리를 출력하도록 하여 정상적으로 출력하게 하기 위함)
Nested Vectored Interrupt Controller (NVIC) : 모든 인터럽트를 관리, 중첩된 인터럽트 처리 가능
ㄴ 인터럽트가 발생하면 해당 인터럽트의 Address(Vector)로 들어가, 내부 함수를 수행하고 다시 복귀
ㄴ Vector : 해당 Adress에 실행할 함수가 존재
교수님과 함께한 FND Code:
#include "FND_prof.h"
enum {DIGIT_1, DIGIT_10, DIGIT_100, DIGIT_1000};
typedef struct {
GPIO_TypeDef *GPIOx;
uint32_t pinNum;
}FND_TypeDef;
FND_TypeDef fndDigitCom[4] =
{
{GPIOA, 12},// digit_1 D4
{GPIOC, 5}, // digit_10 D3
{GPIOC, 6}, // digit_100 D2
{GPIOC, 8} // digit_1000 D1
};
FND_TypeDef fndPin[8] =
{
{GPIOA, 11}, // A
{GPIOB, 12}, // B
{GPIOB, 2}, // C
{GPIOB, 1}, // D
{GPIOB, 15}, // E
{GPIOB, 14}, // F
{GPIOB, 13}, // G
{GPIOC, 4} // DP
};
//외부에서 건드리지 못하게 막을 함수는 static 처리하고 header file에 포함 X
//static void FND_DispOff(int fndPos);
static void FND_DispOn(int fndPos);
static void FND_DispDigit(uint16_t digit);
static void FND_DispOffALL();
static uint16_t fndDispNum = 0; // FND display main data(외부 파일에서 접근XXX)
void FND_Init()
{
for(int i=0; i<4; i++){
GPIO_Init(fndDigitCom[i].GPIOx, fndDigitCom[i].pinNum, OUTPUT);
}
for(int i=0; i<8; i++){
GPIO_Init(fndPin[i].GPIOx, fndPin[i].pinNum, OUTPUT);
}
}
// write fndDisNum
void FND_WriteData(uint16_t data)
{
fndDispNum = data;
}
// read fndDisNum
uint16_t FND_ReadData()
{
return fndDispNum;
}
// display fndDisNum
void FND_DisplayData()
{
static int digitPos = 0; // digit 자리 표현 변수
// interrupt 발생하면 한 자리씩 출력한다.
digitPos = (digitPos + 1) % 4; // 0~3까지 출력 반복
switch(digitPos)
{
// 계속 켜져있으면 FND 켜져있을 때 데이터가 바뀌면서 잔상이 생긴다
// -> FND를 끄고 데이터를 바꾸고 다시 킨다.
case DIGIT_1:
FND_DispOffALL();
FND_DispDigit(fndDispNum%10);
FND_DispOn(digitPos);
break;
case DIGIT_10:
FND_DispOffALL();
FND_DispDigit(fndDispNum/10%10);
FND_DispOn(digitPos);
break;
case DIGIT_100:
FND_DispOffALL();
FND_DispDigit(fndDispNum/100%10);
FND_DispOn(digitPos);
break;
case DIGIT_1000:
FND_DispOffALL();
FND_DispDigit(fndDispNum/1000%10);
FND_DispOn(digitPos);
break;
}
}
void FND_DispOn(int fndPos)
{
// GPIO_WritePin(GPIOx, PiNum, RESET); - Cathode type(Anode type은 반대)
GPIO_WritePin(fndDigitCom[fndPos].GPIOx, fndDigitCom[fndPos].pinNum, PIN_RESET);
}
void FND_DispOffALL()
{
for(int i=0; i<4; i++){
GPIO_WritePin(fndDigitCom[i].GPIOx, fndDigitCom[i].pinNum, PIN_SET);
}
}
void FND_DispDigit(uint16_t digit)
{
const uint8_t segFont[10] = {
0x3F, // 0
0x06, // 1
0x5B, // 2
0x4F, // 3
0x66, // 4
0x6D, // 5
0x7D, // 6
0x07, // 7
0x7F, // 8
0x6F // 9
};
for(int i=0; i<8; i++){
if(!(segFont[digit] & (1<<i))){
GPIO_WritePin(fndPin[i].GPIOx, fndPin[i].pinNum, PIN_RESET); // data = 0 -> offCathode type(Anode type은 반대)
}
else{
GPIO_WritePin(fndPin[i].GPIOx, fndPin[i].pinNum, PIN_SET); // data = 1 -> onCathode type(Anode type은 반대)
}
}
}
Timer :
APB1의 TIM2의 Address를 사용하여 시스템클럭을 분주해서 사용합니다.
Clock -> Prescaler -> CNT++ ===> CNT VS ARR
ARR(Auto Reload Register)의 값과 Counter의 값이 같아지면 Counter = 0;을 실행하며 Update Interrupt를 발생시킵니다.
내부 클럭 : 16MHz -> Prescaler : 1MHz -> Counter : 1KHz를 통해 1KHz의 클럭을 만들어주고, 이를 Timer로 활용하여
TIM_TypeDef 구조체를 이용하여 Timer Interrupt를 구성해 주었습니다.
과제:
Watch 모드 : 12.00을 시작으로 FND에 출력하며, 1분을 주기로 Counter + 1 && 0.5초 간격 DotProduct 점멸
StopWatch 모드 : FND의 <분|. 초 | 초. |밀리초>를 출력하도록 설계 && 0.05초 간격 DotProduct 점멸
Switch : 스위치를 통해 2개의 모드를 변경 + StopWatch 모드에서 Run/ Stop/ Clear FSM 변경
ㄴ 맨위 버튼 : Mode 변경 버튼
ㄴ 중간 버튼 : RUN / STOP 버튼 (Run - Stop state 천이 조건)
ㄴ 마지막 버튼 : Clear 버튼, 숫자를 0으로 초기화
코드 :
FND.c
void FND_DispDigit(uint16_t digit, uint16_t dot)에 DP값도 출력하는 segFontDP[10] 생성
+ for문을 통해 fndPin[8]에 대해 값을 활성화 할 수 있도록 설계 + FND_Dot 함수를 통해 dp값을 받을 수 있도록 설계
ap_main.c
FSM과 같은 설계를 통해 버튼으로 모드 변경 + 기능(Run, Stop, Clear) 구현
flag를 생성하여 1KHz Timer를 카운트하여 Stopwatch일 때는 0.5초 간격 dp 점멸, Wathc일 때는 0.05초 간격 점멸
ㄴ Watch -> FND_Dot(0, 0, dot_data, 0) , StopWatch -> FND_Dot(0, dot_data1, 0, dot_data1)
100MHz를 분주하기 위해 psc =16, arr = 1000으로 설정 -> 1ms TIM_Init(TIM2, 16-1,1000-1) 활용
https://github.com/Heeju99/embedded/tree/main/250620_FND_TIM_Interrupt/Src
embedded/250620_FND_TIM_Interrupt/Src at main · Heeju99/embedded
Contribute to Heeju99/embedded development by creating an account on GitHub.
github.com
동작 영상 :
처음 시작 = "12.00"을 출력하는 Watch 모드 -> 맨 위 버튼 입력 -> StopWatch 모드
가운데 버튼 입력 -> StopWatch 시작, 다시 가운데 버튼 입력 -> StopWatch 멈춤
마지막 버튼 입력 -> StopWatch 값 Clear, 모두 0
맨 위 버튼 입력 -> Watch 모드로 변경
'[HARMAN] 세미콘 아카데미 > Embedded' 카테고리의 다른 글
[Harman 세미콘 아카데미] Day_92(Embedded) (0) | 2025.06.24 |
---|---|
[Harman 세미콘 아카데미] Day_91(Embedded) (0) | 2025.06.23 |
[Harman 세미콘 아카데미] Day_89(Embedded) (0) | 2025.06.19 |
[Harman 세미콘 아카데미] Day_88(Embedded) (0) | 2025.06.18 |
[Harman 세미콘 아카데미] Day_87(Embedded) (0) | 2025.06.17 |