MEGAWIN

MG82F6D Series I2C Master

롬돌이 2022. 12. 6. 13:04
반응형

1. 프로그램 개요

P22Green 0 LED, P24 Red LED, P26Green 1 LED가 연결되어 있다.

4096바이트 용량의 EEPROM(24C32)을 읽고/쓰는 테스트를 진행한다.

 

2. 회로도

                         

 

 

 

 

3. Code

main routine

void main()

{

InitSystem();                                      // 시스템 초기화

           SendStr("\nStart");                              // 시리얼로 “Start” 문자 송신

           LED_G_0=0;LED_G_1=0;LED_R=0;              // LED ALL ON

           DelayXms(1000);                                  // 1초 딜레이

           LED_G_0=1;LED_G_1=1;LED_R=1;              // LED ALL OFF

           rand = 0x80;                                      // 랜덤 변수 초기화 0x80

           TWI0TestAddr.W=0x0000;                       // TWI0 테스트 어드레스 : 0x0000

           EE_TWI0.EEStatus=EE_IDLE;                    // EE TWI0 Status : IDLE

           EE_TWI0.EEMode=EE_MODE_READ;           // EE TWI0 Mode : READ

           TWI0TestTime=TWI0_TEST_INTERVAL_TIME; // EE TWI Test Time : 100ms 타이머 변수, 400msTWI0 타이머 값으로 사용한다.

 

while(1)

{

                     rand++;                                 // rand는 계속 1 씩 상승된다.

if(LedTime==0)                         // LED 타이머

{

                                LED_R = ~LED_R;           // 200ms 마다 LED_R은 토글 된다.

                                LedTime=200;

                     }

                     TWI0_TestProc();                       // TWI 테스트

}

}

 

⑵ 시스템 초기화 루틴

/***********************************************************************************

*Function:   void InitSystem(void)

*Description:           Initialize MCU

*Input:  

*Output:    

*************************************************************************************/

void InitSystem(void)

{

           InitPort();

           InitClock();

           InitTWI0();

           InitTimer0();

           InitUart0_T1();

           InitInterrupt();

 

           INT_EnAll();                                                          // Enable global interrupt

}

- 포트 설정 수행한다.

 

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, P26LED 구동을 위하여 Push-Pull type로 설정한다.

           PORT_SetP4OpenDrainPu(BIT0|BIT1);                                                    // Set P40,P41as open-drain with pull-high,For I2C

è P40, P41 Pull Up을 갖는 Open Drain타입으로 설정한다. I2C 핀으로 사용한다.

}

*** 사용된 매크로함수는 “API_Macro_MG82FG6D16.H”에서 찾아볼 수 있다.

\Megawin 8051\(EN)MG82F6D16_SampleCode_v1.20\MG82F6D16_GPIO_I2C_Master\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 Clock12MHz로 선언되어 있으면

#if (MCU_CPUCLK==MCU_SYSCLK)                    // CPU ClockSystem 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 clockSystem Clock와 같거나 1/2로 설정할

// 수 있다.

#endif

#endif

- CLK_SetCKCON0(IHRCO_12MHz|CPUCLK_SYSCLK_DIV_1|SYSCLK_MCKDO_DIV_1);

CKCON0 레지스터의 각 비트를 설정한다.

CKCON0.7 : AFS, Alternated Frequency Selection, 내부 클럭 IHRCO12MHz(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();

}

 

I2C 초기화

/***********************************************************************************

*Function:   void InitTWI0(void)

*Description:   Initialize TWI0

*Input:  

*Output:    

*************************************************************************************/

void InitTWI0()

{

           TWI0_Clear();

           TWI0_SetClock(TWI0_CLK_SYSCLK_32);

           TWI0_SetUseP40P41();

           TWI0_Enable();

}

- 제어레지스터 클리어

#define TWI0_Clear()                                 SICON=0

SICON(TWI0 제어레지스터)를 클리어 시킨다.

 

- I2C 클럭 주파수설정

#define TWI0_SetClock(x)                          SICON=(SICON&B01111100)|((x&0x03)|((x<<5)&B10000000))

CR2, CR1, CR0를 이용하여 I2C 클럭을 설정한다.

#define TWI0_CLK_SYSCLK_32         2         // SYSCLK/32

예제에서는 System Clock1/32을 클럭으로 사용하도록 하였다. 12MHz의 시스템 클럭일 경우 375kHzI2C 클럭을 사용한다.

 

- P40(SCL), P41(SDA) 설정

#define TWI0_SetUseP40P41()                               SFRPI=0;AUXR3=(AUXR3&(~(TWIPS1|TWIPS0)))

AUXR3 레지스터의 TWIPS1, 0을 클리어 시켜 I2C 핀으로 P40, P41을 매핑 시킨다.

 

- I2C 동작 시작

#define TWI0_Enable()                                                              SICON=SICON|(ENSI)

SICON 레지스터의 ENSI를 셋 시켜 I2C를 동작 시킨다.

 

 

⑹ 타이머 0 초기화

/***********************************************************************************

*Function:   void InitTimer0()

*Description:   Initialize Timer0

*Input:  

*Output:                 

*************************************************************************************/

void InitTimer0(void)

{

           TM_SetT0Mode_1_16BIT_TIMER();                           // TIMER0 Mode: 16-bit

           TM_SetT0Clock_SYSCLKDiv12();                            // TIMER0 Clock source: SYSCLK/12

           TM_SetT0Gate_Disable();                                                 // TIMER0 disable gate

 

           TM_SetT0LowByte(TIMER_12T_1ms_TL);                 // Sets TL0 value

           TM_SetT0HighByte(TIMER_12T_1ms_TH);                 // Sets TH0 value

          

           TM_EnableT0();                                                                         // Enable TIMER0

}

 

- 타이머 0의 모드 설정

#define TM_SetT0Mode_1_16BIT_TIMER()                           TMOD=(TMOD&(~(T0M0|T0M1)))|(T0M0)

T0M1=0, T0M0=1로하여 모드 116비트 타이머로 설정한다.

- 타이머 0의 클럭 설정

#define TM_SetT0Clock_SYSCLKDiv12()   AUXR3=(AUXR3&(~T0XL));AUXR2=(AUXR2&(~T0X12));TMOD=(TMOD&(~T0C_T))

T0XL = 0, T0X12 = 0, T0C/T = 0으로 저장하여 타이머 0의 클럭을 (SYSCLK/12)로 결정한다.

SYSCLK 12MHz이므로, 타이머 0의 클럭은 1MHz이다. 따라서 타이머 01us마다 1씩 증가한다.

 

- 게이트 사용하지 않음

#define TM_SetT0Gate_Disable()                                                TMOD=TMOD&(~T0GATE)

타이머 0Gate를 사용하지 않는 것으로 설정한다.

 

- 1ms 타이머로 설정

#define TM_SetT0HighByte(x)                               TH0=x

#define TM_SetT0LowByte(x)                               TL0=x

타이머 01ms 타이머가 되도록 TH0, TL0에 초기값을 저장한다.

 

- 타이머 0 시작

#define TM_EnableT0()                              TR0=1

TR0를 셋 시켜 타이머 0을 동작 시킨다.

 

UART0초기화 èTimer 1 Overflow를 이용한 Baud Rate 설정

/***********************************************************************************

*Function:   void InitUart0_T1(void)

*Description:          Initialize Uart0, The source of baud rate was T1

*Input:  

*Output:    

*************************************************************************************/

void InitUart0_T1(void)

{

         UART0_SetMode8bitUARTVar();                       // UART0 Mode: 8-bit, Variable B.R.

         UART0_EnReception();                                            // Enable reception

         UART0_SetBRGFromT1();                                // B.R. source:T1

         UART0_SetT1BaudRateX2();                            // B.R. x2

 

         TM_SetT1Mode_2_8BIT_AUTORELOAD();

         TM_SetT1Clock_SYSCLK();

         TM_SetT1Gate_Disable();

 

    // Sets B.R. value

         TM_SetT1HighByte(T1_BRGRL_9600_2X_12000000_1T);

         TM_SetT1LowByte(T1_BRGRL_9600_2X_12000000_1T);

 

         TM_EnableT1();

}

 

- UART0 MODE 설정

#define UART0_SetMode8bitUARTVar()                       S0CFG=S0CFG&(~SM30);SM00_FE=0;SM10=1

S0CFG 레지스터의 SM30bit, S0CON 레지스터의 SM00/FE, SM10 비트의 조합으로 여러가지 시리얼 포트 동작 모드를 설정할 수 있다. 그 중 본 예제는 8-bit UART, Variable Baud Rate 특성인 MODE 1을 사용한다.

 

- UART0 Receive Enable

#define UART0_EnReception()                                           REN0=1

UART0이 시리얼 통신으로 데이터를 받아들일 수 있도록 설정한다. S0CON 레지스터의 REN0를 셋(=1) 시킨다.

- UART0 Baud RateTimer 1을 이용해서 만든다.

#define UART0_SetBRGFromT1()                                        SFRPI=1;T2MOD1=T2MOD1&(~TL2IS);SFRPI=0;RCLK_TF2L=0;TCLK_TL2IE=0

SFR PAGE 1에 위치한 T2MOD1 레지스터의 TL2IS 플래그를 클리어 시켜 T2CON5, 4번 비트가 RCLK, TCLK 제어 비트로 동작 할 수 있도록 한다.

RCLK 비트를 0으로 만들어 Timer 1Overflow UART0의 수신 Clock으로 사용될 수 있도록 설정한다.

TCLK 비트를 0으로 만들어 Timer 1OverflowUART0의 송신 Clock으로 사용될 수 있도록 설정한다.

 

- Double Baud Rate 설정

#define UART0_SetT1BaudRateX2()                            PCON0=PCON0|(SMOD1);S0CFG=S0CFG&(~SMOD2)

PCON0 레지스터의 SMOD1 플래그를 셋 시켜 UART0 MODE 1, 2, 3Double Baud RateEnable 시킨다.

S0CFG 레지스터의 SMOD2 플래그를 클리어 시켜 USRT0 Extra Double Baud Rate Disable 시킨다.

 

- Timer 1을 모드 2 8-bit Auto Reload 모드로 설정.

#define TM_SetT1Mode_2_8BIT_AUTORELOAD()            TMOD=(TMOD&(~(T1M0|T1M1)))|(T1M1)

타이머 1을 모드 2로 설정한다.(T1M0 = 0, T1M1 = 1) 8-bit 타이머로 자동 재적재 기능(Automatic Reload)으로 동작한다.

 

- Timer 1의 클럭 설정

#define TM_SetT1Clock_SYSCLK()                                       AUXR2=(AUXR2|(T1X12));TMOD=(TMOD&(~T1C_T))

AUXR2 레지스터의 T1X12를 셋, TMOD 레지스터의 T1C/TClear 시킴으로써 Timer 1Clock Source SYSCLK로 설정한다.

- Gate Disable(타이머클럭선택, 타이머 트리거 방법 선택)

#define TM_SetT1Gate_Disable()                                        TMOD=TMOD&(~T1GATE)

TMOD레지스터의 T1GATE 비트는 TIMER 1의 클럭 소스를 결정하는 곳과 TIMER 1의 트리거 방법을 설정하는 곳에 사용된다. T1GATE Clear 시킴으로써 TIMER 1 클럭 소스를 System Clock or (System Clock/12) 중 하나로 선택할 수 있고, 클럭이 타이머에 입력되는 것(트리거시그널)을 제어하는 것을 TR1비트로 설정할 수도 있다.

 

- 타이머 1의 상/하위 레지스터의 값을 저장

TM_SetT1HighByte(T1_BRGRL_9600_2X_12000000_1T);

TM_SetT1LowByte(T1_BRGRL_9600_2X_12000000_1T);

타이머 1 UP Counter로 동작하고 0xFF에서 0x00으로 바뀔 때 Overflow 이벤트가 발생한다.

자동 재적재 기능을 갖는 Mode 2 8-bit timer로 설정하였기 때문에 실제 동작하는 카운터는 TL1레지스터이고, TH1은 재적재할 카운터 값을 저장한다. TL1레지스터의 값이 0xFF에서 0x00으로 변하면서 Overflow 이벤트가 발생하면 TH1에 저장되어 있는 데이터가 자동으로 TL1에 재적재 된다.

12MHz 클럭을 사용하고, T1X12=1, SMOD2=0, SMOD1 = 1으로 설정하였으므로 재적재 될 값은 178(0xB2)이다.(데이터시트상의 표 참조)

 

- 타이머 1 Enable(시작)

TM_EnableT1();

마지막으로 TR1 비트를 셋 시켜 타이머 1을 동작 시킨다.

 

è (EN)MG82F6D16_Datasheet_V051.PDF17. Serial Port 0(UART0)를 참조한다.

è (EN)MG82F6D17_Datasheet_V051.PDF18. Serial Port 0(UART0)를 참조한다.

è (EN)MG82F6D64/32_Datasheet_V051.PDF18. Serial Port 0(UART0)를 참조한다.

 

⑻ 인터럽트 설정

/***********************************************************************************

*Function:   void InitInterrupt(void)

*Description:           Initialize Interrupt

*Input:  

*Output:    

*************************************************************************************/

void InitInterrupt(void)

{

           INT_EnTIMER0();                                                             // Enable TIMER0 interrupt

           INT_EnTWI0();                                                      // Enable TWI0 interrupt

}        

- 타이머 0 인터럽트를 설정한다.

#define INT_EnTIMER0()                  ET0=1

 

- I2C 인터럽트를 설정한다.

#define INT_EnTWI0()           EIE1=EIE1|ETWI0

 

- Global Interrupt Enable 비트 셋

#define INT_EnAll()                         EA=1              // Global enable

선언한 인터럽트들이 동작하려면 EA 비트를 셋 시켜야한다.

 

 

 

I2C 테스트 프로세스

void TWI0_TestProc(void)

{

           u8 i;

          

           if(EE_TWI0.EEStatus==EE_IDLE)        // 상태가 IDEL 이면

           {

                     if(TWI0TestTime==0)              // 테스트 딜레이 시간이 다 지나면

                     {

                                if(EE_TWI0.EEMode!=EE_MODE_WRITE)                  // EE ModeWrite가 아닐 때

                                { // Start write test

                                          SendStr("\nTWI0 Write EEPROM Start...");  // 시리얼로 쓰기 모드의 시작을 알려주고

                                          SendHex(TWI0TestAddr.B.BHigh);                          // 테스트 어드레스를 전송한다.

                                          SendHex(TWI0TestAddr.B.BLow);

                                          i=0;

                                          for(i=0;i<DATA_LENGTH;i++)

                                          {

                                                     TWI0WriteBuf[i]=i+rand;                   // 버퍼에 16(DATA_LENGTH)의 랜덤 값을 저장하고

                                                     i++;

                                          }

                                          // 지정된 어드레스에 16개의 데이터를 라이팅한다.

                                          TWI0_StartWriteBuf(I2C_SLAVE_ADDR, TWI0TestAddr.W, &TWI0WriteBuf[0],DATA_LENGTH);

                                         

                                }

                                else               // EE ModeWrite모드이면

                                {

                                          SendStr("\nTWI0 Read EEPROM Start...");  // 시리얼로 읽기 모드의 시작을 알리고

                                          SendHex(TWI0TestAddr.B.BHigh);                          // 테스트 어드레스를 전송한다.

                                          SendHex(TWI0TestAddr.B.BLow);

                                          // 16개의 데이터를 읽기 버퍼에 저장한다.

                                          TWI0_StartReadBuf(I2C_SLAVE_ADDR, TWI0TestAddr.W, &TWI0ReadBuf[0],DATA_LENGTH);

                                          TWI0TestAddr.W=TWI0TestAddr.W+DATA_LENGTH;  // 어드레스에 데이터 길이 16을 더한다.

                                          if(TWI0TestAddr.W>=0x1000)  // 만약 어드레스 값이 4096이상이면

                                          {

                                                     TWI0TestAddr.W=0;               // 어드레스에 0을 저장한다.

                                          }

                                }

                     }

           }

           else if(EE_TWI0.EEStatus==EE_SEND_STOP)           // EEStatus SEND STOP이면

           {

                     TWI0TestTime=TWI0_TEST_INTERVAL_TIME;  // 테스트 타임에 내부 인터벌 타임을 저장하고

                     if(EE_TWI0.EEFlag != EE_SUCCESS)            // SUCCESS 상태가 아니면

                     {

                                SendStr("\nTWI0 Err: 0x");                         // TWI0 에러 코드를 시리얼로 송신한다.

                                SendHex(EE_TWI0.EEFlag);

                     }

                     else                                                                    // SUCCESS 상태이면

                     {

                                if(EE_TWI0.EEMode==EE_MODE_READ)        // ModeREAD 이면

                                {

                                          for(i=0;i<DATA_LENGTH;i++)   // 데이터 길이 만큼 버퍼의 데이터를 비교한다.

                                          {

                                                     if(TWI0WriteBuf[i]!=TWI0ReadBuf[i]) break;  // 만약 쓰기와 읽기의 버퍼가 틀리다면

                                          }                                                                                   // 비교 루틴을 탈출한다.

                                          if(i>=DATA_LENGTH)                        // i가 데이터 길이 이상이면

                                          {// verify success                                     // 데이터 비교가 성공적이고

                                                     LED_G_0=0;                                     // LED_G_0ON 시키고

                                                     SendStr("\nTWI0 WR Success!!");   // 시리얼로 “TWI0 WR Success!!”문자를 보낸다.

                                                     DelayXms(50);                     // 50ms 딜레이 후

                                                     LED_G_0=1;                                     // LED_G_0OFF 시킨다.

                                          }

                                          else

                                          {// verify fail                                             // i가 데이터 길이 미만이면

                                                     SendStr("\nTWI0 Verify Fail!! 0x");   // “TWI0 Verify Fail!! 0xXX”로 표시하고, 몇번째 버퍼에서

                                                     SendHex(i);                                    // 에러인지 시리얼로 출력시킨다.

                                          }

                                }        

                                else     // Success인데 상태가 READ가 아니면

                                {

                                          SendStr("\nTWI0 Wrtie Done!");                 // “TWI0 Write Done!”를 전송하고

                                          DelayXms(5);                                            // 5ms 딜레이 후에

                                          TWI0TestTime=0;                                       // TWI0TestTime을 클리어 시킨다.

                                }

                     }

                     EE_TWI0.EEStatus=EE_IDLE;                                  // IDLE 상태로 저장한다.

           }

           else     // EEStatus IDLE, SEND_STOP이 아닐 때

           {

                     if(TWI0OvTime==0)                // TWI0OvTime0이면

                     { //

                                SendStr("\nTWI0 Fail-->TWI0_Reset..");                // TWI0 FailàTWI0_Reset..을 전송한다.

                                InitTWI0();                                       // TWI0를 초기화 시키고

                                EE_TWI0.EEStatus=EE_IDLE;  // IDLE 상태로 저장하고

                                EE_TWI0.EEMode=EE_MODE_READ;  // READ 모드로 설정하고

                                TWI0TestTime=TWI0_TEST_INTERVAL_TIME;  // TWI0TestTime은 다시 400ms로 저장한다.

                     }

           }

}

 

TWI0 쓰기 동작

/***********************************************************************************

*Function:   void TWI0_StartWriteBuf(u8 DevAddr,u16 RegStartAddr,u8 *pByte)

*Description:

*                                         Star TWI0,send data to Slave

*Input:

*                                         u8 DevAddr: Slave address

*                                         u16 RegStartAddr: Slave data address

*                                         u8 *pBuf:  Address of the buffer to be send

*                                         u8 Len:  Data length

*Output:

*************************************************************************************/

void TWI0_StartWriteBuf(u8 DevAddr,u16 RegStartAddr,u8 *pBuf,u8 Len)

{

          

           EE_TWI0.EEMode=EE_MODE_WRITE;

           EE_TWI0.EEDataBuf=pBuf;

           EE_TWI0.EEDeviceAddr=DevAddr;

           EE_TWI0.EEDataAddr=RegStartAddr;

           EE_TWI0.EEDataLength=Len;

           EE_TWI0.EEStatus=EE_SEND_START;

           // start TWI0

           TWI0OvTime=IIC_OVTIME_MAX;

           TWI0_SendSTART();  

}

- EEModeEE_MODE_WRITE모드로 저장

- 버퍼 전달, 디바이스 어드레스 저장, 데이터를 저장할 Start address를 저장, 쓰기 데이터의 길이 저장

- EEStatus EE_SEND_START로 저장하고, I2C Over Time Max를 저장한다.

- 이 후 START 조건을 발생시킨다.

#define TWI0_SendSTART()                        SICON |= STA // Send Start

- START 조건 발생 후 SI가 셋 되어 TWI0 인터럽트가 발생하고

- SISTA0x08이 확인 되면 START 조건이 정상적으로 발생했다는 것이고

- SICONSTA를 클리어 시켜주고, Device Address를 전송한다.

- 이 후 EEStatus EE_SEND_SLA를 저장한다.

- 디바이스 어드레스 전송 후 SISTA0x18이 확인 되면 정상적인 ACK가 수신된 것이고, 이 때 데이터 어드레스의 상위 바이트를 전송한다. 그리고, EEStatus EE_WRITE_ADDR_H를 저장한다.

- 다시 SISTA0x28이 확인되면 EE_WRITE_ADDR_H일 경우 데이터 어드레스의 하위 바이트를 전송하고, EEStatusEE_WRITE_ADDR_L을 저장한다.

- 다시 SISTA0x28이 확인되면 EE_WRITE_ADDR_L일 경우 EEMode EE_MODE_READ일 경우에는 다시 STA를 셋 시켜 START조건을 만들지만, 그렇지 않을 경우에는 AA를 셋 시키고, EEStatusEE_WRITE_DATA를 저장하고, 첫 번째 전송 데이터를 전송한다.

- 다시 SISTA0x28이 확인 되면 EEStatusEE_WRITE_DATA일 경우에는 전송하고자하는 데이터가 다 전송 될 때 까지 반복해서 데이터를 전송하고, 완료 될 경우에는 STOP 조건을 발생시키고, EEFlagEE_SUCCESS를 저장하고, EEStatus EE_SEND_STOP을 저장한다.

- 인터럽트 루틴 확인.

 

 

TWI0 읽기 동작

/***********************************************************************************

*Function:   void TWI0_StartReadBuf(u8 DevAddr,u16 RegStartAddr,u8 *pByte)

*Description:

*                                         Star TWI0,read data from Slave

*Input:

*                                         u8 DevAddr: Slave address

*                                         u16 RegStartAddr: Slave data address

*                                         u8 *pBuf:  Address of the buffer to be save

*                                         u8 Len: Data length

*Output:

*************************************************************************************/

void TWI0_StartReadBuf(u8 DevAddr,u16 RegStartAddr,u8 *pBuf,u8 Len)

{

           EE_TWI0.EEMode=EE_MODE_READ;

           EE_TWI0.EEDataBuf=pBuf;

           EE_TWI0.EEDeviceAddr=DevAddr;

           EE_TWI0.EEDataAddr=RegStartAddr;

           EE_TWI0.EEDataLength=Len;

           EE_TWI0.EEStatus=EE_SEND_START;

           // start TWI0

           TWI0OvTime=IIC_OVTIME_MAX;

           TWI0_SendSTART();  

}

- EEMode EE_MODE_READ를 저장하고,

- 읽은 데이터를 저장할 버퍼를 지정하고

- 디바이스 어드레스를 저장하고

- 읽을 부분의 시작 주소를 저장하고

- 읽어들일 데이터의 길이를 저장한다.

- I2C Over Time Max를 저장하고

- START 조건을 발생시킨다.(TWI0_SendSTART();)

- START 조건 발생 후 SI가 셋 되어 TWI0 인터럽트가 발생하고

- SISTA0x08이 확인 되면 START 조건이 정상적으로 발생했다는 것이고

- SICONSTA를 클리어 시켜주고, Device Address를 전송한다.

- 이 후 EEStatus EE_SEND_SLA를 저장한다.

- 디바이스 어드레스 전송 후 SISTA0x18이 확인 되면 정상적인 ACK가 수신된 것이고, 이 때 데이터 어드레스의 상위 바이트를 전송한다. 그리고, EEStatus EE_WRITE_ADDR_H를 저장한다.

- 다시 SISTA0x28이 확인되면 EE_WRITE_ADDR_H일 경우 데이터 어드레스의 하위 바이트를 전송하고, EEStatusEE_WRITE_ADDR_L을 저장한다.

- 다시 SISTA0x28이 확인되면 EE_WRITE_ADDR_L일 경우 EEMode EE_MODE_READ일 경우에는 다시 Start 조건을 발생시켜 SISTA0x10이 발생하게 한다.

- 그 뒤 다시 디바이스어드레스를 Read 플래그를 결합하여 전송하고

- SISTA0x48이 저장되면 종료 쪽으로 진행되고, 0x40이 저장되면 데이터 수신으로 진행한다.

- 0x40이 수신되면 SICONAA비트를 셋 시키고, 수신 인덱스를 클리어 시키고 EEStatusEE_READ_DATA로 저장한다.

- 데이터가 수신되면 SISTA0x50이 되고 데이터 인덱스가 읽을 데이터의 수 만큼이 될 때까지 데이트를 읽어들인다.

 

* 현재의 인터럽트 루틴에서는 디바이스 어드레스를 전송하는 것 까지는 함께 사용하나 Write는 그 뒤 바로 동작하지만 ReadSTART 조건을 다시 만들어서 통신을 다시 시작한다.

** 교육의 목적에서 그렇게 해 둘 수 는 있으나 EEModeWrite or Read로 분리가 되므로 Read 쪽을 수정하는 것이 좋을 수도 있다.

 

 

TWI0 Interrupt Service Routine

* 이 인터럽트 서비스 루틴은 MEGAWIN에서 기본으로 제공한 것이고, 각자의 취향에 맞는 동작을 설정 해야 한다.

** 본 예제에 맞도록 설계해 둔 것이므로 특정 목적에는 맞지 않을 수 있다.

/***********************************************************************************

*Function:   void INT_TWSI(void)

*Description:   TWI0  Interrupt handler

                      

*Input:  

*Output:    

*************************************************************************************/

void INT_TWI0() interrupt INT_VECTOR_TWI0

{

           if(SISTA==0x08)          // START 조건이 생성되었다면

           { // Tx:Start

                     SICON=SICON&(~STA);        // START 조건 생성용 플래그를 클리어 시키고

                     SIDAT=EE_TWI0.EEDeviceAddr&0xFE;          // 디바이스어드레스에 Write를 추가하고

                     EE_TWI0.EEStatus=EE_SEND_SLA;              // 디바이스어드레스 전송으로 상태를 저장한다.

           }

           else if(SISTA==0x18)                        // 디바이스 어드레스 전송이 정상적이고, ACK를 수신했으면

{         // Tx:SLA+W Rx:ACK

// To send RegAddr high

                     SICON=SICON|(AA);             // AA를 셋 시키고

SIDAT=(u8)(EE_TWI0.EEDataAddr>>8);         // 데이터 어드레스 상위 바이트를 전송한다.

                     EE_TWI0.EEStatus=EE_WRITE_ADDR_H;        // 어드레스 상위바이트 쓰기로 상태를 저장한다.

}

else if(SISTA==0x20)                        // 디바이스 어드레스를 전송했으나 NACK가 수신되면

{        // Tx:SLA+W Rx:NACK

                     EE_TWI0.EEFlag=EE_TWI0.EEStatus+0x80;   // EEStatus0x80을 더해서 에러임을 표기한다.

                     EE_TWI0.EEStatus=EE_SEND_STOP; // STOP 조건을 전송하는 상태로 저장하고

                     SICON=SICON|(STO);                                // TWI 버스에 STOP 조건을 전송한다.

}                  

else if(SISTA==0x28)             // 데이터를 전송한 뒤 ACK를 수신하면

{ // Tx:DAT Rx:ACK

                     if(EE_TWI0.EEStatus==EE_WRITE_ADDR_H)   // 데이터 어드레스 상위 바이트 쓰기 상태였으면

                     {

                                // To send RegAddr LOW

                                SICON=SICON|(AA);                       // AA를 셋 시키고

                                SIDAT=(u8)(EE_TWI0.EEDataAddr);    // 데이터 어드레스의 하위 바이트를 전송하고

                                EE_TWI0.EEStatus=EE_WRITE_ADDR_L;        // 데이터어드레스 하위바이트 쓰기 상태로 저장한다.

                     }

                     else if(EE_TWI0.EEStatus==EE_WRITE_ADDR_L)       // 데이터 어드레스 하위 바이트 쓰기 상태였으면

                     {

                                // Send RegAddr Done

                                if(EE_TWI0.EEMode==EE_MODE_READ)        // READ 모드일 경우에는

                                {

                                          // Read mode: To Tx ReStart;

                                          SICON=SICON|(STA);           // START를 다시 발생시키고

                                          EE_TWI0.EEStatus=EE_SEND_RESTART;      // RESTART 상태로 저장한다.

                                }

                                else                                                         // WRITE 모드일 경우에는

                                {

                                          // Write mode: To Tx Data;  

                                          SICON=SICON|(AA);                       // AA를 셋 시키고

                                          EE_TWI0.EEStatus=EE_WRITE_DATA; // 데이터 라이트 상태로 저장하고

                                          EE_TWI0.EEDataInx=0;                                // 데이터 길이를 0으로 설정하고

                                          SIDAT=EE_TWI0.EEDataBuf[EE_TWI0.EEDataInx];    // 버퍼의 데이터를 전송한다.

                                          EE_TWI0.EEDataInx++;          // 바로 데이터의 길이를 1 증가시킨다.

                                }

                     }

                     else if(EE_TWI0.EEStatus==EE_WRITE_DATA)          // 데이터라이트 상태이면

                     {

                                if(EE_TWI0.EEDataInx>=EE_TWI0.EEDataLength)     // 라이팅 된 데이터 길이가 전송할 데이터 길이 이상이면

                                {

                                          SICON=SICON|(STO);                     // STOP 조건을 발생시키고

                                          EE_TWI0.EEFlag= EE_SUCCESS;       // EE_SUCCESS를 저장하고.

                                          EE_TWI0.EEStatus=EE_SEND_STOP; // EE_SEND_STOP상태를 저장한다.

                                }

                                else               // 아직 써야 할 데이터가 남아 있다면

                                {        

                                          SICON=SICON|(AA);                       // AA를 셋 시키고

                                          SIDAT=EE_TWI0.EEDataBuf[EE_TWI0.EEDataInx];    // 데이터를 전송하고

                                          EE_TWI0.EEDataInx++;                     // 전송된 데이터의 길이를 1증가시킨다.

                                }

                     }

                     else               // STATUS가 그 이외의 것이면

                     {

                                SICON=SICON|(STO);                                // STOP 조건을 발생시키고

                                EE_TWI0.EEFlag=EE_TWI0.EEStatus+0x80;   // STATUS0x80을 저장하고

                                EE_TWI0.EEStatus=EE_SEND_STOP;           // EE_SEND_STOP상태를 저장한다.

                     }

}

else if(SISTA==0x30)             //

{ // Tx:DAT Rx:NACK           // NACK가 수신되면

                     EE_TWI0.EEFlag= EE_TWI0.EEStatus+0x80;  // 현재 상태에 0x80을 더하고

                     if(EE_TWI0.EEStatus==EE_WRITE_DATA)       // 데이터 라이트 상태였으면

                     {

                                if(EE_TWI0.EEDataInx==EE_TWI0.EEDataLength)      // 써야 할 만큼 쓰여졌다면

                                {

                                          EE_TWI0.EEFlag= EE_SUCCESS;       // EEFlag EE_SUCCESS로 저장하고

                                }

                     }

                     SICON=SICON|(STO);                                // STOP 조건을 발생시키고

                     EE_TWI0.EEStatus=EE_SEND_STOP;           // EE_SEND_STOP상태를 저장한다.

}

           else if(SISTA==0x10)             // RESTART가 완료되면

           { // Tx:ReStart

                     SICON=SICON&(~STA);        // SICONSTA를 클리어 시키고

                     SIDAT=EE_TWI0.EEDeviceAddr|0x01; // 디바이스어드레스에 READ플래그를 더해서 전송한다.

                     EE_TWI0.EEStatus=EE_SEND_SLA;              // 디바이스어드레스 전송 상태로 저장하고

           }

else if(SISTA==0x40)             // 디바이스읽기 명령 전송에서 ACK가 수신되면

{ // Tx:SLA+R Rx:ACK

                     SICON=SICON|(AA);  // AA를 셋 시키고

                     EE_TWI0.EEDataInx=0;          // 데이터 길이를 클리어 한 뒤

                     EE_TWI0.EEStatus=EE_READ_DATA;  // 데이터 읽기 상태로 저장한다.

        }

        else if(SISTA==0x48)             // 디바이스읽기 명령 전송 시 NACK가 수신되면

         { // Tx:SLA+R Rx:NACK

                     EE_TWI0.EEFlag=EE_TWI0.EEStatus+0x80;   // FlagSTATUS+0x80으로 저장해서 에러를 표시하고

                     EE_TWI0.EEStatus=EE_SEND_STOP; // 전송 중지 상태로 저장하고

                     SICON=SICON|(STO);           // STOP 조건을 발생시킨다.

}

else if(SISTA==0x50)             // Master/Receive에서 데이터를 읽을 때

{ // Rx:DAT Tx:ACK

                     if(EE_TWI0.EEStatus==EE_READ_DATA)        // 데이터 읽기 상태이면

                     {        

                                if(EE_TWI0.EEDataInx>=EE_TWI0.EEDataLength)     // 읽어야할 데이터 수 이상 읽었으면

                                {        

                                          SICON=SICON|(STO);                                // STOP 조건을 발생시키고

                                          EE_TWI0.EEFlag= EE_SUCCESS;                 // EEFlagEE_SUCCESS를 저장하고

                                          EE_TWI0.EEStatus=EE_SEND_STOP; // STOP 전송 상태로 저장한다.

                                }

                                else     // 아직 덜 읽었다면

                                {

                                          EE_TWI0.EEDataBuf[EE_TWI0.EEDataInx]=SIDAT;    // 데이터를 읽고

                                          EE_TWI0.EEDataInx++;          // 데이터 바이트수를 1증가시키고

                                          if(EE_TWI0.EEDataInx>=EE_TWI0.EEDataLength-1)  // (읽을 데이터-1) 이상을 읽었으면

                                          {

                                                     SICON=SICON&(~AA);          // AA를 클리어 시켜주고

                                          }

                                          else

                                          {

                                                     SICON=SICON|(AA);  // 아니면 AA를 셋 시킨다.

                                          }

                                }

                     }

                     else               // 그 외의 상태이면

                     {

                                EE_TWI0.EEFlag=EE_TWI0.EEStatus+0x80;   // EEFlagSTATUS+0x80을 저장해서 에러임을 알리고

                                EE_TWI0.EEStatus=EE_SEND_STOP; // STOP전송 상태로 저장하고

                                SICON=SICON|(STO);                     // STOP 조건을 발생시킨다.

                     }

}

else if(SISTA==0x58)             // 데이터을 읽었을 때 NACK가 수신되면

{ // Rx:DAT Tx:NACK

                     EE_TWI0.EEFlag= EE_TWI0.EEStatus+0x80;  // EEFlagSTATUS+0x80을 더해 에러임을 저장하고

                     if(EE_TWI0.EEStatus==EE_READ_DATA)        // 읽기 모드이면

                     {

                                EE_TWI0.EEDataBuf[EE_TWI0.EEDataInx]=SIDAT;    // 데이터를 버퍼에 저장하고

                                EE_TWI0.EEDataInx++;          // 읽은 데이터 수를 1증가시키고

                                if(EE_TWI0.EEDataInx==EE_TWI0.EEDataLength) // 그 값이 읽어야 할 데이터 수와 같으면

                                {

                                          EE_TWI0.EEFlag= EE_SUCCESS;       // EEFlag EE_SUCCESS를 저장하고

                                }

                     }

                     SICON=SICON|(STO);           //STOP 조건을 발생시키고

                     EE_TWI0.EEStatus=EE_SEND_STOP;  // STOP전송 상태로 저장하고

}

           TWI0OvTime=IIC_OVTIME_MAX;         // over time 시간을 다시 저장하고

           SICON=SICON&(~SI);           // SI 플래그를 클리어 시킨다.

}

 

 

 

 

 

 

 

4. 프로그램 실행

*** Keil compiler가 인스톨되어 있어야함 ***

해당 Example 폴더를 찾아가 KeilPrj폴더를 Open 한다.

\Megawin 8051\(EN)MG82F6D16_SampleCode_v1.20\ MG82F6D16_I2C_Master\KeilPrj

 

해당 폴더의 Keil project 파일을 더블 클릭하여 실행시킨다.(MG82F6D16_DEMO.uvproj)

 

Rebuild 아이콘을 클릭하여 프로젝트를 컴파일 한다.

 

Demo BoardUSB Connector를 연결하여 전원을 인가하고, 전원 스위치를 ON시키고, OCD ICE를 연결한 상태에서 위 이미지의 Start/Stop Debug Session(Ctrl+F5) 버튼을 눌러 컴파일된 프로젝트의 디버그 데이터를 다운로드 시킨다.(컴파일 시 에러가 발생하지 않아야함)

 

 

다운로드 후 Run(F5) 버튼을 클릭하면 프로그램이 동작한다.

5. 동작 영상

https://youtube.com/shorts/5ADv1_tC9gw?feature=share

 

예제의 빠른 테스트를 위하여 Write 후 계산된 어드레스가 0x0100 이상이면 어드레스를 0x0000으로 리셋 시키도록 함.

반응형