1. 프로그램 개요
P22는 Green 0 LED, P24는 Red LED, P26은 Green 1 LED가 연결되어 있다.
외부 인터럽트 입력에 의해 Power Down Mode에서 시스템이 깨어나는 것을 보여준다.
INT0, INT1, INT2가 사용되며, 시스템이 깨어 났을 때 어느 인터럽트가 입력되었는지를 LED에 연결해서 확인할 수 있도록 해 준다.
PCA0 타이머는 지속적으로 동작을 하고 있고, 모듈 0, 모듈 1에서 비교 값으로 설정된 것과 동일한 타이머 값이 되면 해당 모듈의 인터럽트가 발생하도록 한다.
모듈 0은 2000us, 모듈 1은 4000us 마다 인터럽트가 발생하게 되어 있고, 인터럽트 때 마다 할당된 LED가 토글된다.
2. 회로도
3. Code
⑴ main routine
void main()
{
InitSystem(); // 시스템 초기화
LED_G_0=0;LED_R=0;LED_G_1=0; // LED ALL ON
DelayXms(1000); // 1초 딜레이
LED_G_0=1;LED_R=1;LED_G_1=1; // LED ALL OFF
while(1)
{
DelayXms(200); // 200ms 딜레이
LED_G_0=!LED_G_0; // LED_G 토글
}
}
⑵ 시스템 초기화 루틴
/***********************************************************************************
*Function: void InitSystem(void)
*Description: Initialize MCU
*Input:
*Output:
*************************************************************************************/
void InitSystem(void)
{
InitClock(); // 12MHz, SYSCKL = CPUCLK
InitPort(); // 포트 설정
InitPCA(); // PCA 설정
InitInterrupt(); // 인터럽트 설정
INT_EnAll(); // 글로벌 인터럽트 인에이블
}
- 포트 설정을 수행한다.
- Clock 설정.
- PCA 설정
- 인터럽트 동작 설정
⑶ Port 초기화
/***********************************************************************************
*Function: void InitPort(void)
*Description: Initialize IO Port
*Input:
*Output:
*************************************************************************************/
void InitPort(void)
{
PORT_SetP2PushPull(BIT2|BIT4|BIT6); // Set P22,P24,P26 as Push-Pull,For LED.
è P22, P24, P26은 LED 구동을 위하여 Push-Pull type로 설정한다.
}
*** 사용된 매크로함수는 “API_Macro_MG82FG6D16.H”에서 찾아볼 수 있다.
\Megawin 8051\(EN)MG82F6D16_SampleCode_v1.20\MG82F6D16_PCA_Timer_16bit\code\include
⑷ 클럭 초기화 루틴 è 시스템 클럭 및 내부 클럭을 설정한다.
프로그램의 루틴 자체는 복잡하게 많이 설정해 두었으나 그 구조는 아래와 같다.
MCU_SYSCLK의 값은 11059200, 12000000, 22118400, 24000000, 29491200, 32000000, 44236800, 48000000로 설정이 가능하며, 각각 프로그램 상단에
#define MCU_SYSCLK 12000000
와 같이 선언해주었다. 그리고, 바로
#define MCU_CPUCLK (MCU_SYSCLK)
로 선언하여 System 클럭과 CPU 클럭을 동일하게 사용하기로 선언하였다. 물론 사용자의 선택에 따라 틀려질 수 있으므로 어플리케이션에 따라 선언하면 된다.
#if (MCU_SYSCLK==12000000) // System Clock를 12MHz로 선언되어 있으면
#if (MCU_CPUCLK==MCU_SYSCLK) // CPU Clock가 System Clock와 동일하면
// SysClk=12MHz CpuClk=12MHz
CLK_SetCKCON0(IHRCO_12MHz|CPUCLK_SYSCLK_DIV_1|SYSCLK_MCKDO_DIV_1); // 이와 같이 설정하고
#else
// SysClk=12MHz CpuClk=6MHz
CLK_SetCKCON0(IHRCO_12MHz|CPUCLK_SYSCLK_DIV_2|SYSCLK_MCKDO_DIV_1); // CPU clock는 System Clock와 같거나 1/2로 설정할
// 수 있다.
#endif
#endif
- CLK_SetCKCON0(IHRCO_12MHz|CPUCLK_SYSCLK_DIV_1|SYSCLK_MCKDO_DIV_1);
CKCON0 레지스터의 각 비트를 설정한다.
CKCON0.7 : AFS, Alternated Frequency Selection, 내부 클럭 IHRCO를 12MHz(AFS = 0), 또는 11.059MHz(AFS = 1)로 설정함
CKCON0.3 : CCKS, CPU Clock Select, 0:CPU CLOCK = System Clock, 1:CPU CLOCK = System Clock/2
CKCON0.0 ~ 2 : Programable System Clock Select. SYSCLK_MCKDO_DIV_1(System Clock = Master Clock Divider Output)
/***********************************************************************************
*Function: void InitClock(void)
*Description:
* Initialize clock
*Input:
*Output:
*************************************************************************************/
void InitClock(void)
{
#if (MCU_SYSCLK==11059200)
#if (MCU_CPUCLK==MCU_SYSCLK)
// SysClk=11.0592MHz CpuClk=11.0592MHz
CLK_SetCKCON0(IHRCO_110592MHz|CPUCLK_SYSCLK_DIV_1|SYSCLK_MCKDO_DIV_1);
#else
// SysClk=11.0592MHz CpuClk=5.5296MHz
CLK_SetCKCON0(IHRCO_110592MHz|CPUCLK_SYSCLK_DIV_2|SYSCLK_MCKDO_DIV_1);
#endif
#endif
#if (MCU_SYSCLK==12000000)
#if (MCU_CPUCLK==MCU_SYSCLK)
// SysClk=12MHz CpuClk=12MHz
CLK_SetCKCON0(IHRCO_12MHz|CPUCLK_SYSCLK_DIV_1|SYSCLK_MCKDO_DIV_1);
#else
// SysClk=12MHz CpuClk=6MHz
CLK_SetCKCON0(IHRCO_12MHz|CPUCLK_SYSCLK_DIV_2|SYSCLK_MCKDO_DIV_1);
#endif
#endif
#if (MCU_SYSCLK==22118400)
#if (MCU_CPUCLK==MCU_SYSCLK)
// SysClk=22.1184MHz CpuClk=22.1184MHz
CLK_SetCKCON0(IHRCO_110592MHz|CPUCLK_SYSCLK_DIV_1|SYSCLK_MCKDO_DIV_1|ENABLE_CKM|CKM_OSCIN_DIV_2);
DelayXus(100);
// IHRCO, MCK=CKMIx4, OSCin=IHRCO
CLK_SetCKCON2(ENABLE_IHRCO|MCK_CKMI_X4|OSCIn_IHRCO);
#else
// SysClk=22.1184MHz CpuClk=11.0592MHz
CLK_SetCKCON0(IHRCO_110592MHz|CPUCLK_SYSCLK_DIV_2|SYSCLK_MCKDO_DIV_1|ENABLE_CKM|CKM_OSCIN_DIV_2);
DelayXus(100);
// IHRCO, MCK=CKMIx4, OSCin=IHRCO
CLK_SetCKCON2(ENABLE_IHRCO|MCK_CKMI_X4|OSCIn_IHRCO);
#endif
#endif
#if (MCU_SYSCLK==24000000)
#if (MCU_CPUCLK==MCU_SYSCLK)
// SysClk=24MHz CpuClk=24MHz
CLK_SetCKCON0(IHRCO_12MHz|CPUCLK_SYSCLK_DIV_1|SYSCLK_MCKDO_DIV_1|ENABLE_CKM|CKM_OSCIN_DIV_2);
DelayXus(100);
// IHRCO, MCK=CKMIx4, OSCin=IHRCO
CLK_SetCKCON2(ENABLE_IHRCO|MCK_CKMI_X4|OSCIn_IHRCO);
#else
// SysClk=24MHz CpuClk=12MHz
CLK_SetCKCON0(IHRCO_12MHz|CPUCLK_SYSCLK_DIV_2|SYSCLK_MCKDO_DIV_1|ENABLE_CKM|CKM_OSCIN_DIV_2);
DelayXus(100);
// IHRCO, MCK=CKMIx4, OSCin=IHRCO
CLK_SetCKCON2(ENABLE_IHRCO|MCK_CKMI_X4|OSCIn_IHRCO);
#endif
#endif
#if (MCU_SYSCLK==29491200)
#if (MCU_CPUCLK==MCU_SYSCLK)
// Cpuclk high speed
CLK_SetCpuCLK_HighSpeed();
// SysClk=29.491200MHz CpuClk=29.491200MHz
CLK_SetCKCON0(IHRCO_110592MHz|CPUCLK_SYSCLK_DIV_1|SYSCLK_MCKDO_DIV_1|ENABLE_CKM|CKM_OSCIN_DIV_2);
DelayXus(100);
// IHRCO, MCK=CKMIx5.33, OSCin=IHRCO
CLK_SetCKCON2(ENABLE_IHRCO|MCK_CKMI_X533|OSCIn_IHRCO);
#else
// SysClk=29.491200MHz CpuClk=14.7456MHz
CLK_SetCKCON0(IHRCO_110592MHz|CPUCLK_SYSCLK_DIV_2|SYSCLK_MCKDO_DIV_1|ENABLE_CKM|CKM_OSCIN_DIV_2);
DelayXus(100);
// IHRCO, MCK=CKMIx5.33, OSCin=IHRCO
CLK_SetCKCON2(ENABLE_IHRCO|MCK_CKMI_X533|OSCIn_IHRCO);
#endif
#endif
#if (MCU_SYSCLK==32000000)
#if (MCU_CPUCLK==MCU_SYSCLK)
// Cpuclk high speed
CLK_SetCpuCLK_HighSpeed();
// SysClk=32MHz CpuClk=32MHz
CLK_SetCKCON0(IHRCO_12MHz|CPUCLK_SYSCLK_DIV_1|SYSCLK_MCKDO_DIV_1|ENABLE_CKM|CKM_OSCIN_DIV_2);
DelayXus(100);
// IHRCO, MCK=CKMIx5.33, OSCin=IHRCO
CLK_SetCKCON2(ENABLE_IHRCO|MCK_CKMI_X533|OSCIn_IHRCO);
#else
// SysClk=32MHz CpuClk=16MHz
CLK_SetCKCON0(IHRCO_12MHz|CPUCLK_SYSCLK_DIV_2|SYSCLK_MCKDO_DIV_1|ENABLE_CKM|CKM_OSCIN_DIV_2);
DelayXus(100);
// IHRCO, MCK=CKMIx5.33, OSCin=IHRCO
CLK_SetCKCON2(ENABLE_IHRCO|MCK_CKMI_X533|OSCIn_IHRCO);
#endif
#endif
#if (MCU_SYSCLK==44236800)
// SysClk=44.2368MHz CpuClk=22.1184MHz
CLK_SetCKCON0(IHRCO_110592MHz|CPUCLK_SYSCLK_DIV_1|SYSCLK_MCKDO_DIV_1|ENABLE_CKM|CKM_OSCIN_DIV_2);
DelayXus(100);
// IHRCO, MCK=CKMIx8, OSCin=IHRCO
CLK_SetCKCON2(ENABLE_IHRCO|MCK_CKMI_X8|OSCIn_IHRCO);
#endif
#if (MCU_SYSCLK==48000000)
// SysClk=48MHz CpuClk=24MHz
CLK_SetCKCON0(IHRCO_12MHz|CPUCLK_SYSCLK_DIV_2|SYSCLK_MCKDO_DIV_1|ENABLE_CKM|CKM_OSCIN_DIV_2);
DelayXus(100);
// IHRCO, MCK=CKMIx8, OSCin=IHRCO
CLK_SetCKCON2(ENABLE_IHRCO|MCK_CKMI_X8|OSCIn_IHRCO);
#endif
// P60 Output MCK/4
//CLK_P60OC_MCKDiv4();
}
⑸ PCA 초기화
/***********************************************************************************
*Function: void InitPCA(void)
*Description: Initialize PCA for 16bit Software Timer
*Input:
*Output:
*************************************************************************************/
void InitPCA(void)
{
PCA_SetCLOCK_SYSCLKdiv12(); // PCA clock: SysClk/12
PCA_CH0_SetMode_16BitSoftwareTimer(); // set CH0 16bit Software Timer
PCA_CH2_SetMode_16BitSoftwareTimer(); // set CH2 16bit Software Timer
PCA_SetCounter(0); // 카운터 초기값을 0x0000으로 저장
PCA_SetCounterReload(0); // 카운터 리로드 값을 0x0000으로 저장
PCA_CH0_SetValue_L(LOBYTE(2000)); // CH0 하위바이트와 상위바이트 초기값 저장
PCA_CH0_SetValue_H(HIBYTE(2000));
PCA_CH2_SetValue_L(LOBYTE(4000)); // CH1 하위 바이트와 상위 바이트 초기값 저장
PCA_CH2_SetValue_H(HIBYTE(4000));
PCA_CH0_EnInterrupt(); // Enable CH0 interrupt
PCA_CH2_EnInterrupt(); // Enable CH2 interrupt
PCA_EnPCACounter(); // Enable PCA counter
}
- PCA 클럭 설정
#define PCA_SetCLOCK_SYSCLKdiv12() CMOD=CMOD&(~(CPS2|CPS1|CPS0))
CPS2~0을 이용해서 PCA0 Clock source를 정의 한다. 예제에서는 000으로 저장하여 내부클럭(시스템클럭 / 12)으로 설정하였다. 시스템 클럭이 12MHz이므로 PCA0는 1MHz의 클럭으로 동작한다.(1us 마다 1 상승)
- PCA CH0를 16비트 소프트웨어 타이머로 설정
#define PCA_CH0_SetMode_16BitSoftwareTimer() CCAPM0=ECOM0|MAT0
CCAPM0(PCA0 모듈 비교/캡쳐레지스터0)의 ECOM0와 MAT0를 셋 시켜준다.
ECOM0는 디지털 비교기 기능을 활성화 시킨 것이고, MAT0는 이 모듈의 비교/캡쳐 레지스터의 값과 PCA0의 값이 동일하게 되면 CCON 레지스터의 CCF0이 셋 된다.
- PCA CH2을 16비트 소프트웨어 타이머로 설정
#define PCA_CH2_SetMode_16BitSoftwareTimer() CCAPM2=ECOM2|MAT2
CCAPM2(PCA0 모듈 비교/캡쳐레지스터2)의 ECOM2와 MAT2를 셋 시켜준다.
ECOM2는 디지털 비교기 기능을 활성화 시킨 것이고, MAT2는 이 모듈의 비교/캡쳐 레지스터의 값과 PCA0의 값이 동일하게 되면 CCON 레지스터의 CCF2이 셋 된다.
- PCA0 소프트웨어 타이머의 초기값 저장
#define PCA_SetCounter(x) CH=HIBYTE(x);CL=LOBYTE(x)
16비트 타이머로 동작하며, 초기 값은 0x0000으로 저장하였다.
- PCA0 소프트웨어 타이머의 Reload 값 저장
#define PCA_SetCounterReload(x) CHRL=HIBYTE(x);CLRL=LOBYTE(x)
PCA0의 소프트웨어 타이머가 Overflow가 발생하면 CH,CL에 자동 재적재할 CHRL, CLRL 값을 저장
- CH0 비교값 하위 바이트 저장
#define PCA_CH0_SetValue_L(x) CCAP0L=x // 0
- CH0 비교 값 상위 바이트 저장
#define PCA_CH0_SetValue_H(x) CCAP0H=x // 0
- CH2 비교 값 하위 바이트 저장
#define PCA_CH2_SetValue_L(x) CCAP2L=x // 1
- CH2 비교 값 상위 바이트 저장
#define PCA_CH2_SetValue_H(x) CCAP2H=x // 1
- 인터럽트 설정
#define PCA_CH0_EnInterrupt() CCAPM0=CCAPM0|ECCF0
#define PCA_CH2_EnInterrupt() CCAPM2=CCAPM2|ECCF2
CH0, CH1 인터럽트를 설정함. PCA0 카운터 값과 각 채널의 비교 값이 동일해 지면 인터럽트를 발생시킨다.
- PCA 카운터 동작
#define PCA_EnPCACounter() CR=1
CR1 비트를 셋 시켜 PCA 카운터를 동작 시킨다. 이 비트를 클리어 시키면 PCA 카운터가 정지된다.
⑹ 인터럽트 초기화
/***********************************************************************************
*Function: void InitInterrupt(void)
*Description: Initialize Interrupt
*Input:
*Output:
*************************************************************************************/
void InitInterrupt(void)
{
INT_EnPCA(); // Enable PCA interrupt
}
- PCA 인터럽트가 동작할 수 있도록 EPCA 비트를 셋 시킨다.
#define INT_EnPCA() EIE1=EIE1|EPCA
⑺ Enable Global Interrupt
#define INT_EnAll() EA=1 // Global enable
사용자가 설정한 시스템 인터럽트를 Enable 시킨다.
⑻ 인터럽트 루틴
/***********************************************************************************
*Function: void INT_PCA(void)
*Description: PCA Interrupt handler
*Input:
*Output:
*************************************************************************************/
void INT_PCA(void) interrupt INT_VECTOR_PCA
{
WordTypeDef wTemp;
if(CF) // PCA0 카운터가 Overflow가 발생하면 해당 플래그가 셋 된다.
{
CF=0; // Softwar 로 클리어시켜준다.
}
if(CCF0) // CH0가 동일한 값으로 되면
{
CCF0=0; // 해당 플래그를 클리어시키고
wTemp.B.BLow=CCAP0L; // 현재의 비교 값을 버퍼에 저장하고
wTemp.B.BHigh=CCAP0H;
wTemp.W=wTemp.W+2000; // 그 값에 2000을 더하고
CCAP0L=wTemp.B.BLow; // 다시 비교 값 레지스터에 저장한다.
CCAP0H=wTemp.B.BHigh;
LED_R=!LED_R; // LED_R 을 토글 시킨다.
}
if(CCF2) // CH2가 동일한 값으로 되면
{
CCF2=0; // 해당 플래그를 클리어시키고
wTemp.B.BLow=CCAP2L; // 현재의 비교 값을 버퍼에 저장하고
wTemp.B.BHigh=CCAP2H;
wTemp.W=wTemp.W+4000; // 그 값에 4000을 더하고
CCAP2L=wTemp.B.BLow; // 다시 비교 값 레지스터에 저장한다.
CCAP2H=wTemp.B.BHigh;
LED_G_1=!LED_G_1; // LED_G_1을 토글 시킨다.
}
}
4. 프로그램 실행
*** Keil compiler가 인스톨되어 있어야함 ***
해당 Example 폴더를 찾아가 KeilPrj폴더를 Open 한다.
\Megawin 8051\(EN)MG82F6D16_SampleCode_v1.20\ MG82F6D16_PCA_Timer_16bit\KeilPrj
해당 폴더의 Keil project 파일을 더블 클릭하여 실행시킨다.(MG82F6D16_DEMO.uvproj)
Rebuild 아이콘을 클릭하여 프로젝트를 컴파일 한다.
Demo Board에 USB Connector를 연결하여 전원을 인가하고, 전원 스위치를 ON시키고, OCD ICE를 연결한 상태에서 위 이미지의 Start/Stop Debug Session(Ctrl+F5) 버튼을 눌러 컴파일된 프로젝트의 디버그 데이터를 다운로드 시킨다.(컴파일 시 에러가 발생하지 않아야함)
다운로드 후 Run(F5) 버튼을 클릭하면 프로그램이 동작한다.
5. 동작 영상
'MEGAWIN' 카테고리의 다른 글
MG82F6D Series RTC Timer (0) | 2022.12.01 |
---|---|
MG82F6D PCA PWM 16bit (0) | 2022.11.30 |
MG82F6D Series BOD (0) | 2022.11.28 |
MG82F6D Series INT nINTx Wake UP (0) | 2022.11.25 |
MG82F6D Series CMP (0) | 2022.11.24 |