해보기는 했어?

오늘 안됐다고 포기하지 말고 용기내서 내일 다시 해 보자.

MEGAWIN

MG82F6D SPI Flash-2

롬돌이 2022. 12. 22. 09:53
반응형

SPI 통신 초기화 – SPI 마스터 설정

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

*Function:   void InitSPI(void)

*Description:   Initialize SPI Master

*Input:  

*Output:    

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

void InitSPI(void)

{

           SPI_Enable();                                                                                       // Enable SPI

           SPI_SelectMASTERByMSTRbit();                                                 // Set to MASTER

           SPI_SetClock(SPI_CLK_SYSCLK_8);                                             // Set Clock SYSCLK/8 48M/8=6M

           SPI_SetCPOL_0();                                                                                 // CPOL=0

           SPI_SetDataMSB();                                                                               // Data MSB

           SPI_SetCPHA_0();                                                                                 // CPHA=0

           SPI_SetUseP14P15P16P17();                                                       // IO Port: nSS/P14,MOSI/P15,MISO/P16,SPICLK/P17

}

- SPI 활성화

#define SPI_Enable()                                                      SPCON=SPCON|(SPEN)

SPCON 레지스터의 SPEN비트를 셋 시켜서 SPI를 동작 시킨다.

 

- 마스터 선택 및 nSS 동작 방법 선택

#define SPI_SelectMASTERByMSTRbit()                           SPCON=SPCON|(SSIG|MSTR)                            

SPCON 레지스터의 MSTR 비트를 셋 시켜서 SPI MASTER MODE를 선택하고, SSIG를 셋 시킴으로써 마스터 또는 슬레이브로 선택되어 있더라도 nSS 핀은 마스터가 동작시킨다.(자동으로 동작하지 않는다.)

 

- SPI Clock 설정

#define SPI_SetClock(x)                                                  SPCON=(SPCON&B11111100)|(x&0x03);SPSTAT=(x>>2)

SPCON 레지스터의 1, 0 비트와 SPSTAT 레지스터의 0번 비트를 이용해서 총 8가지의 클럭을 선택해서 사용할 수 있다.

본 예제에서는 SYSCLK/8을 사용하기로하였다. SYSCLK48MHz이므로 SPI Clock6MHz가 된다.

 

 

Timer 0, 1, 2 초기화

 

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

*Function:   void InitTimer0(void)

*Description:   Initialize Timer0

*Input:  

*Output:    

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

void InitTimer0(void)

{

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

           TM_SetT0Clock_T1OF();                                                   // TIMER0 Clock source: T1OF

           TM_SetT0Gate_Disable();

           TM_SetT0LowByte(0);                                                     // Sets TL0 value

           TM_SetT0HighByte(0);                                           // Sets TH0 value

           //TM_EnableT0();                                                            // Enable TIMER0

}

- Timer 0 모드 설정

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

Timer 016비트 타이머로 설정한다.(T0M1 = 0, T0M0 = 1)

 

 

- 타이머 0의 클럭 설정

#define TM_SetT0Clock_T1OF()           AUXR3=(AUXR3|(T0XL));AUXR2=(AUXR2|(T0X12));TMOD=(TMOD|(T0C_T))

Timer 0 Clock T1OF로 설정하기 위하여 T0XL, T0X12, T0C/T 비트를 셋 시킨다.

T1OF : 타이머 1 Overflow flag

 

- 타이머 0GateDisable 시킨다.

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

 

- 타이머 0 카운터 클리어

#define TM_SetT0HighByte(x)                               TH0=x

#define TM_SetT0LowByte(x)                               TL0=x

타이머 0의 카운터 레지스터를 클리어 시킨다.

 

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

*Function:   void InitTimer1(void)

*Description:   Initialize Timer1

*Input:  

*Output:    

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

void InitTimer1(void)

{

           TM_SetT1Mode_1_16BIT_TIMER();                           // TIMER1 Mode: 16-bit timer

           TM_SetT1Clock_SYSCLKDiv48();                            // TIMER1 Clock source: SYSCLK/48

           TM_SetT1Gate_Disable();

           TM_SetT1LowByte(0);                                                     // Sets TL1 value

           TM_SetT1HighByte(0);                                           // Sets TH1 value

           //TM_EnableT1();                                                            // Enable T1

}

- Timer 1의 모드 설정

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

타이머 1을 모드 116 비트 타이머로 설정한다.(T1M1 = 0, T1M0 = 1)

 

- Timer 1의 클럭 설정

#define TM_SetT1Clock_SYSCLKDiv48()                                      AUXR2=(AUXR2|(T1X12));TMOD=(TMOD|(T1C_T))

타이머 1의 클럭을 시스템 클럭/48로 설정한다.(T1X12 = 1, T1C/T = 0)

 

- 타이머 1GateDisable 시킨다.

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

 

- 타이머 1 카운터 클리어

#define TM_SetT1HighByte(x)                               TH1=x

#define TM_SetT1LowByte(x)                               TL1=x

타이머 1의 카운터 레지스터를 클리어 시킨다.

 

 

* 타이머 165536us마다 T1OF를 발생시킨다.

** 타이머 065536us 마다 1씩 증가한다.

 

 

 

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

*Function:   void InitTimer2(void)

*Description:   Initialize Timer2

*Input:  

*Output:    

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

void InitTimer2(void)

{

           TM_SetT2Mode_AutoRLandExI();                           // TIMER2 Mode: 16-bit Auto-Reload timer

           TM_SetT2Clock_SYSCLKDiv12();                            // TIMER2 Clock source: SYSCLK/12

          

           TM_SetT2LowByte(TIMER_12T_10ms_TL);                // Sets TL2 value

           TM_SetT2HighByte(TIMER_12T_10ms_TH);                // Sets TH1 value

           TM_SetT2RLHighByte(TIMER_12T_10ms_TH); // Sets RCAP2H

           TM_SetT2RLLowByte(TIMER_12T_10ms_TL); // Sets RCAP2L

           TM_EnableT2();                                                                         // Enable T2

}

 

- 타이머 2는 모드 설정

#define TM_SetT2Mode_AutoRLandExI()                          CP_RL2=0;T2MOD=T2MOD&(~T2MS0)

T2SPl = 0(default), T2MS1 = 0(default), T2MS0 = 0, CP/RL2 = 0

타이머 2는 모드 0으로 자동 재적재 및 외부 인터럽트로 설정한다.

 

- 타이머 2 클럭 설정

#define TM_SetT2Clock_SYSCLKDiv12()                 C_T2=0;T2MOD=T2MOD&(~T2X12);SFRPI=1;T2MOD1=T2MOD1&(~T2CKS);SFRPI=0

CP/RL2 = 0, T2X12 = 0, T2CKS = 0으로 설정하여 시스템 클럭의 1/12를 사용하도록 한다.

 

- Timer 2의 초기 값과 재적재를 위한 초기값을 저장한다.

#define TM_SetT2HighByte(x)                               TH2=x

#define TM_SetT2LowByte(x)                               TL2=x

 

#define TM_SetT2RLHighByte(x)                 RCAP2H=x

#define TM_SetT2RLLowByte(x)                 RCAP2L=x

 

자동 재적재 값은 아래와 같이 계산된다.

#define TIMER_12T_10ms_TH ((65536-(u16)(float)(10000*((float)(MCU_SYSCLK)/(float)(12000000)))) /256)                       

#define TIMER_12T_10ms_TL ((65536-(u16)(float)(10000*((float)(MCU_SYSCLK)/(float)(12000000)))) %256)

 

- Timer 2 동작

#define TM_EnableT2()                              TR2=1

 

⑻ 인터럽트 초기화

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

*Function:   void InitInterrupt(void)

*Description:   Initialize Interrupt

*Input:  

*Output:    

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

void InitInterrupt(void)

{

           INT_EnTIMER2();

}

#define INT_EnTIMER2()                  ET2=1

타이머 2 인터럽트를 사용하기 위하여 초기화 시킨다.

Timer 2 인터럽트

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

*Function:   void INT_T2(void)

*Description:   T2  Interrupt handler

*                                        10ms

*Input:  

*Output:    

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

void INT_T2(void) interrupt INT_VECTOR_T2

{

           if(TF2)                                                                 // Timer 2 Over Flag가 셋 되어 있으면

           {

                     TF2=0;                                                      // 소프트웨어적으로 클리어 시키고

                     if(SPIOvTime!=0) SPIOvTime--;                   // SPIOvTime0이 아니면 1 감소 시킨다.

           }

           if(EXF2)                                                               // 캡쳐 또는 리로드가 T2EX 핀으로부터 발생할 경우

           {

                     EXF2=0;                                                    // 플래그를 클리어 시킨다.

           }

}

- 타이머 2는 앞선 설정에서 10ms마다 인터럽트가 발생하도록 해 두었다.

 

 

SPI Flash Write Enable

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

*Function:                 u8 SPI_Flash_Write_Enable (void)

*Description: Enable write Flash

 

*Input:

*Output:                   0: success  other:fail

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

u8 SPI_Flash_Write_Enable(void)

{

           u8 RDAT;                                        // 8비트 데이터를 선언하고

 

           // set over time 2*10=20ms

SPIOvTime=2;                                 // SPI Over Time20ms로 설정한다.

Re_Flash_Write_Enable:

/* Write enable */

           SPI_nSS  = 0;                      // SPI Slave Select PinLow로 만들고

           SPITransceiver(0x06);           // SPI“0x06”을 전송한다. 시리얼 플래시에 Write Enable 명령을 전송한다.

           SPI_nSS  = 1;                      // SPI Slave Select Pin High로 만든다

 

           nop();

           nop();

           nop();

          

/* Read status register */

           SPI_nSS  = 0;                      // SPI Slave Select PinLow로 만들고

           SPITransceiver(0x05);           // RDSR, SPI“0x05”를 전송한다. Status Register 읽기 명령어이다.

 

  

           RDAT = SPITransceiver(Dummy_Data); // Dummy Data를 전송하면서 데이터를 읽는다. Get received data

 

           SPI_nSS  = 1;                      // SPI Slave Select Pin High로 만든다

          

/* Check WEL == 1 */

           if((RDAT & 0x02) == 0x00)                // Status 데이터를 확인하여 “no write enable”상태이면

           {

                     if(SPIOvTime!=0)                   // SPIvTime0이 아닐경우에는

                     {

                                goto Re_Flash_Write_Enable; // 다시 Write Enable 명령 전송루틴을 수행한다.

                     }

                     else                                     // SPvTime0일 경우에는

                     { // over time

                                return 1;                    // OverTime으로 Fail을 전송한다.

                     }

           }

           else                                               // Status 데이터를 확인하여 “write enable”상태이면

           {

                     return 0;                              // Success를 전송한다.

           }

}

 

Status 데이터 쓰기

 

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

*Function:                 void SPI_Flash_Write_Status(u8 uStatus)

*Description:   Write status

 

*Input:                      u8 uStatus:status

 

*Output:

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

void SPI_Flash_Write_Status(u8 uStatus)

{

SPI_Flash_Write_Enable();      // FlashWrite Enable 상태로 만들고

           SPI_nSS  = 0;            // SPI Slave Select PinLow로 만들고

           SPITransceiver(0x01); // Write Status Register(WRSR) command, Status 레지스터 데이터 쓰기 명령을 전송하고

               

/* Write Status */

SPITransceiver(uStatus);      // Status 데이터를 라이팅 한다.

           SPI_nSS  = 1;            // SPI Slave Select Pin High로 만든다

}

 

Block Erase

 

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

*Function:                 u8 SPI_Flash_Block_Erase(u32 Addr)

*Description:   Flash block erase

 

*Input:                      u32 Addr: start addresss.

*Output:                   0: success  other:fail

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

u8 SPI_Flash_Block_Erase(u32 Addr)

{

if(SPI_Flash_Write_Enable()==1)                   // Write Enable 명령을 먼저 수행하고

{

return 1;                                         // 에러이면 기능을 수행하지 않고 리턴한다.

}

   

           SPI_nSS  = 0;                                 // SPI Slave Select PinLow로 만들고

           SPITransceiver(0xD8);                   // Block Erase command, 블록 지우기 명령어 전송

// or

// SPITransceiver(0x52);                 // Block Erase command, 블록 지우기 명령어 전송

           /* Write address */    // (1024x1024)byte 용량이므로 어드레스는 0x00000000 ~ 0x000FFFFF 까지이므로 3바이트의 어드레스를

// 전송하면 된다.

SPITransceiver((u8)(Addr>>16));                 // Block 시작 어드레스 1 전송

SPITransceiver((u8)(Addr>>8));                   // Block 시작 어드레스 2 전송

SPITransceiver((u8)(Addr));                        // Block 시작 어드레스 3 전송

           SPI_nSS  = 1;                                 // SPI Slave Select Pin High로 만든다

  

           // set over time 210*10=2.1s            // 블록지우기 시간은 최대 2초이다.

SPIOvTime=210;

         while(SPIOvTime!=0)                                  // SPIOvTime0이 아닐경우

         {

                     if(SPI_Flash_Check_Busy()==0)               // Wait Block Erase complete

                     {                                                   // 블록 지우기가 완료 될 때 까지 기다리다가

                                return 0;                              // Success를 전송한다.

                     }

           }

           return 2;                                                    // 아니면 블록 지우기 Fail을 전송한다.

}

 

⑿ 플래시가 동작 중인지 확인하기

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

*Function:                 u8 SPI_Flash_Check_Busy(void)

*Description:   check busy

 

*Input:

*Output:                   0: Free  1:busy

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

u8 SPI_Flash_Check_Busy(void)

{

           u8 RDAT;

   

/* Read status register */

           SPI_nSS  = 0;                      // SPI Slave Select PinLow로 만들고

           SPITransceiver(0x05);           // RDSR, SPI“0x05”를 전송한다. Status Register 읽기 명령어이다.

 

  

           RDAT = SPITransceiver(Dummy_Data); // Dummy Data를 전송하면서 데이터를 읽는다. Get received data

 

           SPI_nSS  = 1;                      // SPI Slave Select Pin High로 만든다

 

           if((RDAT & 0x01) == 0x01)                //WIP, write in progress

           {

                     return 1;                              // 동작 중이면 1을 반환하고

           }

           else

           {

                     return 0;                              // 동작 중이 아니면 0을 반환한다.

           }

}

 

 

 

 

⒀ 페이지 쓰기

 

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

*Function:                 u8 SPI_Flash_Page_Program(u32 Addr, u8 *DataSource, u16 Length)

*Description:   Write n bytes to Flash

 

*Input:                      u32 Addr: Start address. 0x00000000 ~ 0xFFFFFFFF.

*                                         u8 *DataSource: Buffer address

*                                         u16 Length: Buffer length, MAX.256 bytes

*Output:                   0: success  other:fail

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

u8 SPI_Flash_Page_Program(u32 Addr, u8 *DataSource, u16 Length)        // 1페이지 라이팅 시 최대 256 바이트까지 쓸 수 있다.

{

 if(SPI_Flash_Write_Enable()==1)                  // Write Enable 명령을 먼저 수행하고

{

return 1;                                         // 에러이면 기능을 수행하지 않고 리턴한다.

}

   

           SPI_nSS  = 0;                                 // SPI Slave Select PinLow로 만들고

           SPITransceiver( 0x02);                               // Write command, Page program 명령어를 전송하고

          

/* Write address */

SPITransceiver((u8)(Addr>>16));                 // Page write 시작 어드레스 1 전송

SPITransceiver((u8)(Addr>>8));                   // Page write 시작 어드레스 2 전송

SPITransceiver((u8)(Addr));                        // Page write 시작 어드레스 3 전송

          

           while(Length!=0)                                        // Write Data, 쓰기 데이터 수가 0이 아니면

           {

SPITransceiver(*DataSource);          // 데이터 포인터의 데이터를 전송하고

DataSource++;                                // 데이터 포인터를 1 증가시켜 다음 데이터를 지시하도록 하고

                     Length--;                                       // 쓰기 데이터 수는 1 감소시킨다.

           }

          

           SPI_nSS  = 1;                                 // SPI Slave Select Pin High로 만든다

          

           // set over time 2*10=20ms

        SPIOvTime=2;

         while(SPIOvTime!=0)                                  // 데이터 전송 완료 후

         {

                     if(SPI_Flash_Check_Busy()==0)         // Wait program complete, 쓰기가 종료 될 때까지 기다린다.

                     {

                                return 0;

                     }

           }

           return 2;

}

 

⒁ 여러바이트 읽기

 

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

*Function:                 void SPI_Flash_MultiBytesRead (u32 Address, u8 *BufferAddreass, u32 Length)

*Description:   Read n bytes from flash

*

*Input:                     u32 Address: Address 0x00000000 ~ 0xFFFFFFFF

*                                        u8 *BufferAddreass: Buffer address

*                                        u16 Length: Buffer length

*Output:

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

void SPI_Flash_MultiBytesRead (u32 Address, u8 *BufferAddreass, u16 Length)

{

           SPI_nSS = 0;                         // SPI Slave Select PinLow로 만들고

/* Write command */

SPITransceiver(0x03); // nSS 핀이 High가 될 때까지 여러 바이트를 읽을 것이라는 명령

          

/* Write address */

SPITransceiver((u8)(Addr>>16));                 // 여러 바이트 읽기 시작 어드레스 1 전송

SPITransceiver((u8)(Addr>>8));                   // 여러 바이트 읽기 시작 어드레스 2 전송

SPITransceiver((u8)(Addr));                        // 여러 바이트 읽기 시작 어드레스 3 전송

 

/* Read data */

while(Length !=0)                                       // 데이터 수가 0이 아니면

           {

                      *BufferAddreass=SPITransceiver(Dummy_Data);      // 더미 데이터를 전송하여 데이터를 읽고

BufferAddreass ++;                                               // 데이터 버퍼의 어드레스를 1 증가시키고

Length--;                                                            // 데이터 수는 1 감소시킨다.

           }

           SPI_nSS = 1;                                              // 데이터를 다 읽으면 SPI Slave Select Pin High로 만든다

}

 

è (EN)MG82F6D16_Datasheet_V051.PDF18. Serial Peripheral Interface(SPI)를 참조한다.

è (EN)MG82F6D17_Datasheet_V051.PDF19. Serial Peripheral Interface(SPI)를 참조한다.

è (EN)MG82F6D64/32_Datasheet_V051.PDF19. Serial Peripheral Interface(SPI)를 참조한다.

 

4. 프로그램 실행

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

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

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

 

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

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

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

 

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

 

 

5. 동작 영상

https://youtu.be/NnshvPHh1_A

 

 

 

 

 

 

반응형

'MEGAWIN' 카테고리의 다른 글

MG82F6D Series TIM_T2_Mode0_AutoRL_AndEXI  (0) 2022.12.27
MG82F6D SPI Master  (1) 2022.12.26
MG82F6D SPI Flash-1  (0) 2022.12.22
MG82F6D Series PCA COPM Buf  (0) 2022.12.21
MG82F6D Series PCA Capture Buf  (0) 2022.12.20