MCU에서 단위 시간(분기)에 따라 Task를 호출하는 기능
Timer를 이용해 RTOS의 주기 Task 스케줄 구현이 가능함.
Counter overflow나 underflow(UI)가 발생하거나
특정 count(Capture & Compare) 넘거나 내려가면 UI가 발생해
Task를 호출함. (그 시간을 재거나, 그 만큼의 PWM 신호를 만들거나)
즉 Counter 1은 1us를 뜻하고, 2^16-1해서(0~65535)로
이 경우 65535us까지 셀 수가 있음.

고정밀, 긴 주기(64bit)를 요구하는 Apps를 위한 Timer Module
64bit up-counting counter
reset 직후 f_STM에 따라 자동으로 Count
STM은 RO
초기화는 Power On Reset/Application Reset에 따라 초기화 가능
Max of f_STM = 100MHz
2개의 SRN 노드가 STM과 연결되어 Interrupt 발생 가능함.

상위 32Bit를 Capture하는 Register가 존재하고
4bit씩 시작 값이 다르게 쓸 수 있도록 6개의 Register를 두었다.
Compare Register가 2개 존재한다.
값을 비교하여 어떤 일을 할 수는 있겠다
일반적으로는 STM_TIM0과 STM_TIM6을 써서 64bit의 시간을 세는데 쓴다.
Core별로 STM을 갖고 있어서 총 3개의 STM을 갖게 된다.

STM_CAP은 STM_TIM Register를 읽으면 자동으로 System_Timer의 상위 32bit가 저장됨
따라서 STM_TIM0 Register를 읽은 후, STM_CAP을 왼쪽으로 Shift해주고 OR연산 쳐주면 됨
uint64 getTimeUs(void)
{
uint64 result;
float32 frequency = 100000000.0f;
/* Read 64-bit System Timer */
result = MODULE_STM0.TIM0.U;
result |= ((uint64)MODULE_STM0.CAP.U) << 32;
/* return microseconds */
return result / (frequency / 1000000);
}

기본 Clock이 100MHz고
MODULE_STM0.TIM0.U로 값을 불러옴
위에서 설명했다 싶이, Clock상
Reset시 이미 10ns마다 1씩 counter(tick)가 올라감.
10ns는 10^-8이다. (0.01us)
그러면 1us를 받으려면 100 tick이 지나야 10^-6이 된다.
그래서 return을 1us로 return할 수가 있음.
여하튼 이 Format으로 여러 초 단위를 셀 수 있다.
STM이 특정 값을 가질 때 Triggering하기 위해 사용
STM_CMP0, STM_CMP1 2개의 Register로 구성됨.


reset : 0x00

reset : 0x00

Compare Match Interrupt가 이렇게 되는 구조인데

그것을 설정하는 레지스터는 ICR에 있다.
CMP0EN : Compare Interrupt 사용 유무(Enable, CMP0의 IR사용)
(0 : Disable interrupt,
1 : Enable Interrupt)
CMP0IR : Interrupt 발생 유무 (CMP0의 IR 감지 Flag bit)
(0 : Event is not detected,
1 : Event is detected)
CMP0OS : Interrupt Output 선택 (STMIR 0/1 선택)
(0 : STMIR0,
1:STMIR1)


STM으로 1초마다 Interrupt 걸기
#include "main.h"
#define ISR_PRIORITY_STM0 50
IFX_INTERRUPT(Stm0IsrHandler, 0, ISR_PRIORITY_STM0);
void Stm0IsrHandler(void)
{
MODULE_STM0.CMP[0].U = (unsigned int)(getTimeUs() + 1000000) * 100;
my_printf("STM!\n");
}
void main (void)
{
SYSTEM_Init();
MODULE_STM0.CMCON.B.MSIZE0 = 31; /* Compare 32 bit size */
MODULE_STM0.CMCON.B.MSTART0 = 0; /* Compare start at 0 bit */
MODULE_STM0.ICR.B.CMP0OS = 0; /* Interrupt Output 0 */
MODULE_SRC.STM.STM[0].SR[0].B.TOS = 0; /* CPU 0 */
MODULE_SRC.STM.STM[0].SR[0].B.SRPN = ISR_PRIORITY_STM0;
MODULE_SRC.STM.STM[0].SR[0].B.CLRR = 1; /* Clear Interrupt Req. */
MODULE_SRC.STM.STM[0].SR[0].B.SRE = 1; /* Enable Interrupt */
MODULE_STM0.ISCR.B.CMP0IRR = 1U; /* Clear Interrupt Req. */
MODULE_STM0.ICR.B.CMP0EN = 1U; /* Enable Interrupt */
/* Set Compare register to current time + 1s */
MODULE_STM0.CMP[0].U = (unsigned int)(getTimeUs() + 1000000) * 100;
while (1);
}

STM은 3개고 CPU0에는 STM[0]를 들고 있음.
물론 us단위로 오차는 갖겠지
STM은 타 Timer 대비 사용하기가 간편해서 이런식으로 하여, End-Start해서 수행 시간을 대충 측정할 수도 있다.
uint64 getTime10Ns(void){
uint64 result;
result = MODULE_STM0.TIM0.U;
result |= ((uint64)MODULE_STM0.CAP.U) << 32;
return result;
}
10ns(1tick)을 곧바로 return한다.
혹은
uint64 getTimeMs(void)
{
uint64 result;
float32 frequency = 100000000.0f;
/* Read 64-bit System Timer */
result = MODULE_STM0.TIM0.U;
result |= ((uint64)MODULE_STM0.CAP.U) << 32;
/* return milliseconds */
return result / (frequency / 1000);
}
이렇게 해서 ms를 얻을 수도 있다.
GPT12 Module은 GPT1 / GPT2 Unit Block으로 구성

2개의 보조 Timer와 1개의 Core Timer로 이루어져 있다.
보면 Up/Down 설정도 있고, Mode 설정도 있고
Capture/Reload을 주고 받을 수도 있고
T3 OUT으로 Interrupt 발생시 Toggle Latch가 동작하여
이를 다른 것의 입력이나 Pin-Out으로도 쓸 수 있고
당연히 IR(SR1)도 쓸 수 있다.
보조 Timer지만 T2, T4도 IR(SR0/SR2)를 보낼 수가 있다.

T3 Timer Mode는 다음과 같이 동작한다.

순서는 다음과 같이 동작한다.
f_Tx = f_GPT(SPB) / BPS1 * 2 ^ TxI
T3UD 로 U/D 결정
T3R = 1일때, Timer 시작
특정 조건으로 Interrupt 가능
f_GPT = f_SPB = 100MHz (기본 시스템 클럭)
f_GPT를 BPS1, T3I Bit field를 참고해 Prescale하여 f_T3를 계산
ex) T3I = 000B, BPS1 = 01B 로 설정했을 때,
→ 100MHz / 4 = 25.0MHz (= f_T3)

이건, Factor별 f_T3


Parameter로 분주를 많이할수록 당연히 해상도도 올라가고 정확도도 올라간다.
대신 주기가 길어지겠지

Timer T2/T3/T4 Register
T2, T3, T4 Timer Register Bit field 내용은 모두 동일

보면 16bit라 65535까지밖에 못 센다. STM에 비해 현저히 낮긴함
T3I [2:0] : T3의 Input Parameter 설정
T3가 Timer 모드인 경우, Prescaler로 동작
000 ~ 111 : 1, 2, 4, 8, ... , 128 (2의 exponential이라 그럼)
T3M [5:3] : Timer T3의 동작 모드 설정
000 : Timer Mode
001 : Counter Mode
이 외에도 Gated 등등 많음
T3R [6] : Timer T3 실행 Flag
0 : T3 정지
1 : T3 시작

이거 참고


T2M [5:3] : T2/T4 모드 설정
000 : Timer Mode
100 : Reload Mode
101 : Capture Mode
여기도 등등 많은 Mode들이 존재한다.
T2I [2:0] : Timer 모드마다 다르게 사용됨
Timer 모드에서는 마찬가지로 Prescaler로 사용
Reload/Caputer에선 Trigger Edge 설정을 위한 register로 사용
예를 들어 111이면, Rising/Falling edge Trigger 모두 사용
나머지는 T3CON과 거의 같다.
T3 Gated Timer Mode는 다음과 같이 동작한다.

And Gate가 추가되어 Enable을 컨트롤한다고 생각하면 쉽다.
Gate Control이 추가 외부 TxIN 신호가 Enable되는 경우에만
Timer를 동작시킬수있음

이건 T3 Counter Mode로 Rising/Falling Edge를 Counting해준다.
외부 TxIN을 Clock으로 설정하여 Timer를 동작시키는 모드

GPT2의 CAP/REL은 한 순간에 한 쪽 값밖에 못가진다.

f_GPT = f_SPB = 100MHz (기본 SCLK) :
f_GPT를 BPS1,T6I Bit field를 참고하여 Prescale하여 f_T6 를 계산
ex) T6I = 000B, BPS2 = 00B 로 설정했을 때,
→ 100MHz / 4 = 25.0MHz (= f_T6)

T2, T3, T4와 똑같이 16bit Timer임



T6I[2:0] : T6의 Input Parameter 설정
Timer인 경우, Prescaler로 동작
000 ~ 111 : T3I와 동일 동작
T6M[5:3] : T6 동작 모드 설정
000 : Timer
001 : Counter
010 : Gated Mode with ...
011 : Gated Mode with ...
T6R[6] : T6의 실행 Flag
0 : T6 정지
1 : T6 시작
둘 다 Compare기능이 없어서 그냥 Core의 Underflow / Overflow로 UI를 건다.
T6 COUNTER MODE :

T6 GATED COUNTER MODE :

T6 TIMER MODE :

1ms를 얼마만큼의 Timer Count로 기준을 삼을 것인가
T6I = 000, BPS2 = 00으로 설정시
100MHz / 4 = 25MHz(f_T6)
25MHz라면, 초당 2500,0000 tick이 움직이는것
주파수와 주기의 역수 관계로 주기를 구할 수 있음.
여하튼 10^-3이 되면, 25000 tick이고 이는 1ms
10^-6이 되면 25 tick이고 이는 1us가 된다.
GPT2 T6 Timer를 25MHz Clock으로 분주해 사용시,
250Tick마다 호출되면 10us마다 Interrupt가 발생하게 됨.
즉, T6 Counter Value를 250으로 설정해주고
Reload(CAPREL register)값을 250으로해서 Reload해주면 된다.
static volatile unsigned int cntDelay = 0;
IFX_INTERRUPT(IsrGpt2T6Handler, 0, ISR_PRIORITY_GPT2T6_TIMER);
void IsrGpt2T6Handler(void)
{
cntDelay++;
}
void runGpt12_T6()
{
IfxGpt12_T6_run(&MODULE_GPT120, IfxGpt12_TimerRun_start);
}
void stopGpt12_T6()
{
IfxGpt12_T6_run(&MODULE_GPT120, IfxGpt12_TimerRun_stop);
}
void gpt2_init(void)
{
/* Initialize the Timer T6 for delay_ms */
MODULE_GPT120.T6CON.B.BPS2 = 0x0; /* Set GPT2 block prescaler: 4 */
MODULE_GPT120.T6CON.B.T6M = 0x0; /* Set T6 to timer mode */
MODULE_GPT120.T6CON.B.T6UD = 0x1; /* Set T6 count direction(down) */
MODULE_GPT120.T6CON.B.T6I = 0x0; /* Set T6 input prescaler(2^0=1) */
MODULE_GPT120.T6CON.B.T6OE = 0x1; /* Overflow/Underflow Output Enable */
MODULE_GPT120.T6CON.B.T6SR = 0x1; /* Reload from register CAPREL Enabled */
MODULE_GPT120.T6.U = 250u; /* Set T6 start value (10us) */
MODULE_GPT120.CAPREL.U = 250u; /* Set CAPREL reload value */
/* Initialize the interrupt */
volatile Ifx_SRC_SRCR *src;
src = (volatile Ifx_SRC_SRCR*) (&MODULE_SRC.GPT12.GPT12[0].T6);
src->B.SRPN = ISR_PRIORITY_GPT2T6_TIMER;
src->B.TOS = 0;
src->B.CLRR = 1; /* clear request */
src->B.SRE = 1; /* interrupt enable */
runGpt12_T6();
}
CAPREL은 한 번에 한 쪽으로 밖에 동작하지 못하기 때문에 Getter/Setter를 만들어 주면 좋긴 하겠다.
IFX_INTERRUPT(IsrGpt2T6Handler, 0, ISR_PRIORITY_GPT2T6_TIMER);
void IsrGpt2T6Handler(void)
{
cntDelay++;
if (cntDelay == 100000) {
GPIO_ToggleLed(1);
cntDelay = 0;
}
}
사실 핸들러만 수정해주면 됨. 10us마다 ISR이 호출되고
1초가 되려면 10^-5이 10이 되어야 하니까 100000번 증가하면 1초
물론 당연히 오차 있음. 그리고 다시 Counter값 0으로 내려줌.
별건 없고 그냥 ISR에서 GPIO쪽만 건드려줌
static volatile unsigned int g_Cnt10us = 0;
void Task_100ms(void) {
/* Task_100ms: 현재SW2가눌려져있으면 LED2를켜고, 그렇지않으면끔*/
if (GPIO_getSW2()) { GPIO_SetLed(2, 1); }
else { GPIO_SetLed(2, 0); }
}
void Task_200ms(void) { /* Task_200ms: 시리얼 통신으로 문자열을 출력함*/
my_printf("Task 200ms Called!\n");
}
void Task_500ms(void) { /* Task_500ms: LED1 반전시킴*/
GPIO_ToggleLed(1);
}
IFX_INTERRUPT(IsrGpt2T6Handler, 0, ISR_PRIORITY_GPT2T6_TIMER);
void IsrGpt2T6Handler(void) {
g_Cnt10us++;
if (g_Cnt10us % 10000 == 0) { Task_100ms(); } /* 100ms마다호출*/
if (g_Cnt10us % 20000 == 0) { Task_200ms(); } /* 200ms 마다호출*/
if (g_Cnt10us % 50000 == 0) { Task_500ms(); } /* 500ms 마다호출*/
}
GPT를 사용해 멀티태스킹 하도록 ISR을 수정하여 등록함.
참고로 RTOS는 아니니까 착각하면 안된다.
RTOS 역할의 일부분도 못하는 정도임
스케쥴링도 없고 등등...
2번의 Beep은 1회 진동
1초 동안 131번 진동한다면, 3옥타브 도(Hz)의 음계가 나감

기존 GPIO에서 수행했던 Buzzer는 하드코딩에
Core가 loop에 붙잡히며 다른 일을 할 수가 없음. (Polling)
사실 Polling이라고도 애매한게 뭔가 확인한다기보단 그냥 잡혀있는거라
여하튼 CPU 사용률이 100%나 되는 코드
다양한 기능을 동시 구동하기 위해 main()과 별도로
Interrupt 방식으로 걸어줘야함.
물론 PWM으로 Duty 설정도 가능하다.
이를 GPT1의 T2 Reload 기능을 통해 구현해보자.
T3는 underflow가 나면 Toggle Latch에서 Signal이 발생하여
T2에 전달되고, T2는 T3의 이 Signal의 Rising/Falling을 감지해
T2의 값을 T3에 Reload시킴.
만약 T3에 U/D를 번갈아가며 Reload한다면, Duty비 제어도 가능하다.
f_GPT = f_SPB = 100MHz
f_GPT를 BPS1, T3I Bit field를 참고하여
Prescale하여 f_T3 를 계산
ex) T3I = 101B, BPS1 = 10B 로 설정했을 때,
→ 100MHz / 1024 = 97.65625 KHz (Ttick = 10.24 us)
130Hz = 약 7692us고
1 tick이 10.24us니까 3846/10.24 = 375로 Counter 설정시 130Hz
마찬가지로 5옥타브 도는 523Hz
1 Tick은 그대로 10.24us이기에 956/10.24 = 93.36
즉 Timer value를 93으로 Reload/Set해주고 Down count 시켜줌
그리고 T2의 T2I에서 T3OTL(Latch) Rising/Falling Edge를 선택하도록 111(7) 값을 넣어주면 된다.

void gpt1_init(void)
{
/* Initialize the Timer T3 (PWM) */
MODULE_GPT120.T3CON.B.BPS1 = 0x2; /* Set GPT1 block prescaler: 32 */
MODULE_GPT120.T3CON.B.T3M = 0x0; /* Set T3 to timer mode */
MODULE_GPT120.T3CON.B.T3UD = 0x1; /* Set T3 count direction(down) */
MODULE_GPT120.T3CON.B.T3I = 0x5; /* Set T3 input prescaler(2^5=32) */
/* Calculate dutyUpTime and dutyDownTime for reloading timer T3 */
MODULE_GPT120.T3.U = 93; /* Set timer T3 value */
/* Timer T2: reloads the value DutyDownTime in timer T3 */
MODULE_GPT120.T2CON.B.T2M = 0x4; /* Set the timer T2 in reload mode */
MODULE_GPT120.T2CON.B.T2I = 0x7; /* Reload Input Mode : Rising/Falling Edge T3OTL */
MODULE_GPT120.T2.U = 93;
/* Initialize the interrupt */
volatile Ifx_SRC_SRCR *src;
src = (volatile Ifx_SRC_SRCR*) (&MODULE_SRC.GPT12.GPT12[0].T3);
src->B.SRPN = ISR_PRIORITY_GPT1T3_TIMER;
src->B.TOS = 0;
src->B.CLRR = 1; /* clear request */
src->B.SRE = 1; /* interrupt enable */
runGpt12_T3();
}
IFX_INTERRUPT(IsrGpt1T3Handler_Beep, 0, ISR_PRIORITY_GPT1T3_TIMER);
void IsrGpt1T3Handler_Beep(void)
{
MODULE_P02.OUT.B.P3 ^= 1;
}
ISR call되면 XOR로 끄고 켜고를 반복하며 소리를 내도록 해줌.
main.c에선 Init만 하면 알아서 나간다.
차량 후진 장애물 경고부저
gpt12.c
int beepCnt = 0;
int beepOnOff = 0;
IFX_INTERRUPT(IsrGpt1T3Handler_Beep, 0, ISR_PRIORITY_GPT1T3_TIMER);
void IsrGpt1T3Handler_Beep(void)
{
if ((beepCnt < beepOnOff) || (beepOnOff == 1)) {
MODULE_P02.OUT.B.P3 ^= 1;
}
else if (beepCnt < beepOnOff * 2) {
MODULE_P02.OUT.B.P3 = 0;
}
else {
beepCnt = 0;
}
beepCnt++;
}
void setBeepCycle(int cycle)
{
beepOnOff = cycle;
}
#include "main.h"
int main(void) {
SYSTEM_Init();
while (1) {
setBeepCycle(260);// on/off every 1s (3846us * 260)
delay_ms(5000);
setBeepCycle(50);
delay_ms(3000);
setBeepCycle(10);
delay_ms(3000);
setBeepCycle(1);// always on
delay_ms(3000);
setBeepCycle(0);// always off
delay_ms(3000);
}
return 0;
}