[2024.03.28.목] 인천인력개발원 하만 세미콘 아카데미
임베디드 시스템을 위한 SW 구조설계
8비트 타이머 / 카운터
1. 타이커/카운터 인터럽트
- 현재까지의 펄스 수: TCNTn레지스터에 저장
- 오버플로 인터럽트
- 최대로 셀 수 있는 펄스 이상이 되면 TCNTn레지스터가 0으로 바뀌며 발생
- 비교일치 인터럽트
- TCNTn의 값이 미리 설정된 OCRn레지스터값과 일치하면 발생
2. TCCR0 레지스터: 분주비 설정
- CLK 주파수: 16,000,000Hz
- TCNT는 최대 256, TCCR은 16 -> 256*26 = 4096 (≒4K)
- TCCR이 64면 16K -> 16KHz가 ATmega의 최대 주파수
- 따라서 분주비를 64로 설정

3. TIMSK 레지스터: 인터럽트 활성화
- EIMSK: External Input Mask
- TIMSK: Timer Mask


- TOIE0비트: 오버플로 인터럽트 활성화 비트(TcntOverflow)
- OCIE0비트: 비교일치 인터럽트 활성화 비트(Ocr Comp)
3. 비교 일치 인터럽트
- TCNTn값과 비교할 값을 OCRn 레지스터에 설정

실습 1: 타이머를 사용하여 진행시간 표시
1. test03 우클릭 - Add - New Item

2. C File 선택 후 Segment로 이름 지정


4. Segment.c에 붙여넣기
#define F_CPU 16000000L //Board CLK 정보(16MHz)
#include <avr/io.h>
#include <avr/delay.h>
#include <avr/interrupt.h>
#define OPTMAX 3
#define STATEMAX 3
uint8_t digit[] = { 0x3F, 0x06, 0x5B, 0x4F, 0x66, 0x6D, 0x7D, 0x27, 0x7F, 0x67, 0x77, 0x7c, 0x58, 0x5e, 0x79, 0x71 };
char arr[5]; //세그먼트 이미지 정보를 담을 안전공간
volatile int opt = 0, state = 0;
void seg(int sel, uint8_t c){
PORTC |= 0X0F;
PORTC &= ~(1 << (3-sel));
PORTD = c; //숫자 데이터 출력
_delay_ms(2);
}
void FND_4(char *inf){ //segment Image 배열
for (int i = 0; i < 4; i++){
seg(i, *(inf+i));
}
}
//16진수 segment image 배열
char* Display(unsigned long num){ //10진 정수를 입력받아 16진수 문자열로 변환 ex)65535 ==> 0xffff, 56506=>0xBCDA
int n1 = (num / 10) % 10; //A(10): 문자가 아닌 숫자
int n2 = (num / 100) % 10; //B(11)
int n3 = (num / 100) % 10; //C(12)
int n4 = num / 1000; //D(13)
arr[0] = digit[n1]; arr[1] = digit[n2]; arr[2] = digit[n3] + 0x80; arr[3] = digit[n4];
if ( num<10 ){
arr[3] = 0; arr[1] = 0; arr[2] = 0x80;
}
else if ( num<100 ){
arr[2] = 0x80; arr[3] = 0;
}
else if ( num<1000 ){
arr[3] = 0;
}
FND_4(arr);
return arr;
}
5. volatile 변수 외의 함수 선언 내용을 main-intr.c에서 제거 후 상단에 코드 입력
extern char* Display (unsigned long num);
의미: 외부 파일로부터 함수를 불러오는 코드
6. solution 우클릭- add - new project - test04-timer 생성

7. ATmega128 선택 후 OK
8. 헤더파일 만들기: test04-timer 우클릭 - add- file - myHeader.h파일 생성

9. 헤더파일 내에 #define F_CPU 16000000L 입력 후 저장
10. 모든 .c파일에 #include "myHeader.h"로 포함
- #include <> 사용: 시스템 헤더 포함
- #include " "사용: 사용자 정의 헤더 포함(현재 프로젝트와 같은 경로에 위치할 때 사용)
11. main-timer.c내에 헤더파일 include
#include "myHeader.h"
#include <avr/io.h>
#include <avr/delay.h>
#include <avr/interrupt.h>
12. main 함수 내에 인터럽트 활성화 코드 및 실행 코드(sei()) 작성
TIMSK |= 0x01; //Timer Interrupt Mask을 0000 0001b로 설정하여 오버플로 인터럽트 활성화 [Timer 0 TCNT Overflow interrupt]
TCCR0 |= 0x04; //16KHz를 구하기 위해 분주비 64 필요 -> 비트를 0000 0100b로 설정
SREG |= 0X80;
sei(); //인터럽트 실행 코드
13. ISR 추가
ISR(TIMER0_OVF_vect){
tcnt++;
if (tcnt >= 1000){
cnt++; tcnt = 0;//1초(1000밀리초)가 지나면 cnt값을 증가시켜라
}
}
[Segment.c 수정]: PORTC와 PORTD를 포인터 변수로 바꿔 입력


14. Segment.c 파일을 수정하여 분.초로 나타나도록 수정
char* Display(unsigned long num){ //10진 정수를 입력받아 16진수 문자열로 변환 ex)65535 ==> 0xffff, 56506=>0xBCDA
int n1 = num % 10; //A(10): 문자가 아닌 숫자
int n2 = (num / 10) % 6; //B(11)
int n3 = (num / 60) % 10; //C(12)
int n4 = (num / 600) % 6; //D(13)
arr[0] = digit[n1]; arr[1] = digit[n2]; arr[2] = digit[n3] + 0x80; arr[3] = digit[n4];
if ( num< 10 ){
arr[3] = 0; arr[1] = 0; arr[2] = 0x80;
}
else if ( num<60 ){
arr[2] = 0x80; arr[3] = 0;
}
else if ( num<600 ){
arr[3] = 0;
}
15. 실행 확인(10배속)
실습 2: 위 과정을 응용하여 주방 타이머 설계
- sw1: start/stop/continue 로 시간을 제어할 수 있도록 작동
- sw2: 시간이 초당 10씩 증가하여 처음 실행 시 원하는 시간을 맞출 수 있도록 설정
- sw3: 타이머 초기화
1. 버튼을 입력으로 설정하고, 외부 인터럽트가 작동하도록 메인 함수 내에 코드 작성
int main(void)
{
SegPortSet(&PORTD, &PORTC);
//7-Segment 사용 : 4 Module - C type
// Pin assign : PDx - Segment img, PCx - module sel
//Interrupt 사용 : INT4~INT6 (External Interrupt)
// Pin assign : PE4~PE6
DDRD = 0xFF;
DDRC = 0x0F;
DDRE = 0x00;
//인터럽트 설정
EIMSK = 0x70; //0111 0000 //INT 4~INT 6 활성화
EICRB = 0x2a; //4개의 B그룹(INT4~INT7)의 인터럽트 발생 시점 결정(00 10 10 10, 각 7 6 5 4에서의 INT발생 시점을 rising edge로 결정)
DDRD = 0XFF; //D포트의 모든 비트를 출력으로 설정
DDRC = 0X0F; //D포트의 네개 비트를 출력으로 설정
TIMSK |= 0x01; //Timer Interrupt Mask을 0000 0001b로 설정하여 오버플로 인터럽트 활성화 [Timer 0 TCNT Overflow interrupt]
TCCR0 |= 0x04; //16KHz를 구하기 위해 분주비 (Pre-Scaler) 64 필요 -> 비트를 0000 0100b로 설정
SREG |= 0x80;
sei();
while (1)
{
if(cnt > 0x10000) cnt = 0; //
Display(cnt);
}
}
2. 외부 인터럽트 작동 시 첫 번째 버튼을 누르면 시간이 올라가도록 타이머 인터럽트 함수 내에 코드 설정
ISR(TIMER0_OVF_vect){
if(opt){
tcnt++;
if (tcnt >= 1000){
cnt++; tcnt = 0;//1초(1000밀리초)가 지나면 cnt값을 증가시켜라
}
}
}
3. opt, set, reset설정값이 버튼에 따라 변경되도록 코드 상단에 volatile로 변수 선언

4. ISR 코드 작성
ISR(INT4_vect){ //INT4 인터럽트 처리 루틴: sw1
opt++;
if (opt >= 2) opt = 0;
}
ISR(INT5_vect){ //INT5 인터럽트 처리 루틴: sw2
set++;
if (set >= 2) set = 0;
}
ISR(INT6_vect){ //INT6 인터럽트 처리 루틴: sw3
cnt = 0;
}
ISR(TIMER0_OVF_vect){
if(opt){
tcnt++;
if (tcnt >= 1000){
cnt++; tcnt = 0;//1초(1000밀리초)가 지나면 cnt값을 증가시켜라
}
}
}
5. 현재까지 진행상황: START, STOP, CONTINUE기능과 RESET기능 구현
8비트 타이머 / 카운터 - 2
1. 타이머 2를 사용하는 경우, 다음과 같이 변경

2. 위와 같이 변경 후 타이머0을 주석 처리 후 실행 -> 비정상작동(진행시간에 변화)
- 0번 타이머와 2번 타이머의 분주기 위치 차이로 인해 발생


16비트 타이머 / 카운터

1. 16비트 타이머 / 카운터
- 오버플로 인터럽트: 0~(2^16-1)까지 카운트 가능
- 3개의 비교 일치 인터럽트 사용 가능
- 특정 사건 발생 시 현재 카운터 값이 TCNTn 레지스터 값 저장
2. 오버플로 인터럽트
- TCNTn 레지스터 = TCNTnH + TCNTnL
- 0~65,535까지 카운트 가능한 16비트 레지스터
- 분주를 하지 않으면 4.096MS간격의 인터럽트 발생
- 256으로 분주하면 약 1초 간격으로 인터럽트 발생

3. TCCR1B 레지스터
- 분주비 설정 레지스터


4. ETIMSK레지스터: 확장 타이머 레지스터

실습 3: 16비트 카운터/타이머 이용
1. 16비트의 타이머 마스크와 분주비를 다음과 같이 적용

2. ISR 코드를 변경: tcnt if문 제외(분주비가 256이므로 인터럽트가 1초마다 발생)
ISR(TIMER1_OVF_vect){
tcnt++;
//if (tcnt >= 1000){
cnt++; tcnt = 0;//1초(1000밀리초)가 지나면 cnt값을 증가시켜라
//}
}
실습 2: 주방 타이머 설계 이어서
- sw1: start/stop/continue 로 시간을 제어할 수 있도록 작동 - 설정 시간 도달시 LED 점멸
- sw2: 타이머 설정(시간을 분단위 UP/DOWN으로 설정)
- sw3: 타이머 초기화
- 타이머 표시 시간: 분.초
- 타이머 동작 순서
- 1번째 자릿수가 점멸하면 SW1을 눌러 숫자 증가
- SW2를 눌러 감소
- SW3을 눌러 입력 및 다음 자릿수 설정으로 넘어감
- 2, 3, 4번째 자리수도 동일하게 설정
- 4자리까지 설정이 완료되면 화면에 ----표시
- START(SW1)을 누르면 카운트 시작
- 설정 시간에 도달하면 LED 점멸
1. 현재까지의 진행 코드 및 상황: start/pause/continue, reset 구현 완료
/*
* test04-timer.c
*
* Created: 2024-03-28 오전 10:57:17
* Author : jong9
*/
#include "myHeader.h"
#include <avr/io.h>
#include <avr/delay.h>
#include <avr/interrupt.h>
#define _delay_t 500
volatile int opt=0, set=0, reset=0;
volatile unsigned long cnt = 0, tcnt = 0;
int main(void)
{
SegPortSet(&PORTD, &PORTC);
//7-Segment 사용 : 4 Module - C type
// Pin assign : PDx - Segment img, PCx - module sel
//Interrupt 사용 : INT4~INT6 (External Interrupt)
// Pin assign : PE4~PE6
DDRD = 0xFF;
DDRC = 0x0F;
DDRE = 0x00;
//인터럽트 설정
EIMSK = 0x70; //0111 0000 //INT 4~INT 6 활성화
EICRB = 0x2a; //4개의 B그룹(INT4~INT7)의 인터럽트 발생 시점 결정(00 10 10 10, 각 7 6 5 4에서의 INT발생 시점을 rising edge로 결정)
DDRD = 0XFF; //D포트의 모든 비트를 출력으로 설정
DDRC = 0X0F; //D포트의 네개 비트를 출력으로 설정
TIMSK |= 0x04; //Timer Interrupt Mask을 0000 0100b로 설정하여 오버플로 인터럽트 활성화 [Timer 1 TCNT Overflow interrupt]
//TIMSK |= 0x01; //Timer Interrupt Mask을 0000 0001b로 설정하여 오버플로 인터럽트 활성화 [Timer 0 TCNT Overflow interrupt]
//TIMSK |= 0x40; //Timer Interrupt Mask을 0000 0001b로 설정하여 오버플로 인터럽트 활성화 [Timer 2 TCNT Overflow interrupt]
TCCR1B |= 0x04; //16KHz를 구하기 위해 분주비 (Pre-Scaler) 64 필요 -> 비트를 0000 0100b로 설정 (타이머 1에 대한 분주기가 256)
//TCCR0 |= 0x04; //16KHz를 구하기 위해 분주비 (Pre-Scaler) 64 필요 -> 비트를 0000 0100b로 설정 (타이머 0에 대한 분주기)
//TCCR2 |= 0x04; //16KHz를 구하기 위해 분주비 (Pre-Scaler) 64 필요 -> 비트를 0000 0100b로 설정 (타이머 2에 대한 분주기)
SREG |= 0x80;
sei();
while (1)
{
if(cnt > 0x10000) cnt = 0; //
Display(cnt);
}
}
ISR(INT4_vect){ //INT4 인터럽트 처리 루틴: sw1 (start, pause, continue 버튼)
opt++;
if (opt >= 2) opt = 0;
}
ISR(INT5_vect){ //INT5 인터럽트 처리 루틴: sw2 (시간 설정 버튼)
set++;
if (set >= 2) set = 0;
}
ISR(INT6_vect){ //INT6 인터럽트 처리 루틴: sw3 (reset 버튼)
cnt = 0; opt = 0;
}
//ISR(TIMER0_OVF_vect){
//if(opt){
//tcnt++;
//if (tcnt >= 1000){
//cnt++; tcnt = 0;//1초(1000밀리초)가 지나면 cnt값을 증가시켜라
//}
//}
//}
ISR(TIMER1_OVF_vect){
tcnt++;
if(opt){
//if (tcnt >= 1000){
cnt++; tcnt = 0;//1초(1000밀리초)가 지나면 cnt값을 증가시켜라
//}
}
}
2. 스위치구문을 통해 초기 실행 시 화면이 깜빡거리며 시간 설정할 수 있도록 작성
switch (mode){ //처음 시작하면 mode가 0이므로 초기화면 코드
case 0: //처음 시간 설정 모드
switch(settimeth){
case 0:
timeup = 0; timedown = 0;
while(1){
time = timeup - timedown;
if(timeup <= 0){
display_character(settimeth, 1);
_delay_ms(500);
display_character(settimeth, 0);
_delay_ms(500);
}
else{
display_digit(settimeth, time);
_delay_ms(500);
display_character(settimeth, 1);
_delay_ms(500);
}
if(complete == 1) timearr[settimeth] = time; break;
}
display_character(settimeth, 1); settimeth++; complete = 0;
break;
case 1:
timeup = 0; timedown = 0;
while(1){
time = timeup - timedown;
if(timeup <= 0){
display_character(settimeth, 1);
_delay_ms(500);
display_character(settimeth, 0);
_delay_ms(500);
}
else{
display_digit(settimeth, time);
_delay_ms(500);
display_character(settimeth, 1);
_delay_ms(500);
}
if(complete == 1) timearr[settimeth] = time; break;
}
display_character(settimeth, 1); settimeth++; complete = 0;
break;
case 2:
timeup = 0; timedown = 0;
while(1){
time = timeup - timedown;
if(timeup <= 0){
display_character(settimeth, 1);
_delay_ms(500);
display_character(settimeth, 0);
_delay_ms(500);
}
else{
display_digit(settimeth, time);
_delay_ms(500);
display_character(settimeth, 1);
_delay_ms(500);
}
if(complete == 1) timearr[settimeth] = time; break;
}
display_character(settimeth, 1); settimeth++; complete = 0;
break;
case 3:
timeup = 0; timedown = 0;
while(1){
time = timeup - timedown;
if(timeup <= 0){
display_character(settimeth, 1);
_delay_ms(500);
display_character(settimeth, 0);
_delay_ms(500);
}
else{
display_digit(settimeth, time);
_delay_ms(500);
display_character(settimeth, 1);
_delay_ms(500);
}
if(complete == 1) timearr[settimeth] = time; break;
}
display_character(settimeth, 1); settimeth++; complete = 0;
break;
}
break;
case 1:
break;
}
if(cnt > 0x10000) cnt = 0; //
//Display(cnt);
}
'하만(Harman) 세미콘 반도체 설계 과정 > 임베디드 시스템을 위한 SW 구조설계' 카테고리의 다른 글
하만(Harman) 세미콘 아카데미 56일차 - SW 구조설계(Git 다운로드, GitHub 설정) (2) | 2024.05.31 |
---|---|
하만(Harman) 세미콘 아카데미 17일차 - SW 구조설계(주방 타이머 설계 이어서) (24) | 2024.03.29 |
하만(Harman) 세미콘 아카데미 15일차 - SW 구조설계(Pullup 저항, ATmega128 인터럽트, 스탑워치 설계) (20) | 2024.03.27 |
하만(Harman) 세미콘 아카데미 12일차 - SW 구조설계( 7세그먼트 - 16진수 출력, 스톱워치 구현) (23) | 2024.03.22 |