⑹ 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을 사용하기로하였다. SYSCLK는 48MHz이므로 SPI Clock은 6MHz가 된다.
⑺ 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 0을 16비트 타이머로 설정한다.(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
- 타이머 0의 Gate를 Disable 시킨다.
#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을 모드 1인 16 비트 타이머로 설정한다.(T1M1 = 0, T1M0 = 1)
- Timer 1의 클럭 설정
#define TM_SetT1Clock_SYSCLKDiv48() AUXR2=(AUXR2|(T1X12));TMOD=(TMOD|(T1C_T))
타이머 1의 클럭을 시스템 클럭/48로 설정한다.(T1X12 = 1, T1C/T = 0)
- 타이머 1의 Gate를 Disable 시킨다.
#define TM_SetT1Gate_Disable() TMOD=TMOD&(~T1GATE)
- 타이머 1 카운터 클리어
#define TM_SetT1HighByte(x) TH1=x
#define TM_SetT1LowByte(x) TL1=x
타이머 1의 카운터 레지스터를 클리어 시킨다.
* 타이머 1은 65536us마다 T1OF를 발생시킨다.
** 타이머 0은 65536us 마다 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--; // SPIOvTime가 0이 아니면 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 Time을 20ms로 설정한다.
Re_Flash_Write_Enable:
/* Write enable */
SPI_nSS = 0; // SPI Slave Select Pin을 Low로 만들고
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 Pin을 Low로 만들고
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) // SPIvTime이 0이 아닐경우에는
{
goto Re_Flash_Write_Enable; // 다시 Write Enable 명령 전송루틴을 수행한다.
}
else // SPvTime이 0일 경우에는
{ // 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(); // Flash를 Write Enable 상태로 만들고
SPI_nSS = 0; // SPI Slave Select Pin을 Low로 만들고
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 Pin을 Low로 만들고
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) // SPIOvTime이 0이 아닐경우
{
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 Pin을 Low로 만들고
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 Pin을 Low로 만들고
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 Pin을 Low로 만들고
/* 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.PDF의 18. Serial Peripheral Interface(SPI)를 참조한다.
è (EN)MG82F6D17_Datasheet_V051.PDF의 19. Serial Peripheral Interface(SPI)를 참조한다.
è (EN)MG82F6D64/32_Datasheet_V051.PDF의 19. 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 Board에 USB Connector를 연결하여 전원을 인가하고, 전원 스위치를 ON시키고, OCD ICE를 연결한 상태에서 위 이미지의 Start/Stop Debug Session(Ctrl+F5) 버튼을 눌러 컴파일된 프로젝트의 디버그 데이터를 다운로드 시킨다.(컴파일 시 에러가 발생하지 않아야함)
다운로드 후 Run(F5) 버튼을 클릭하면 프로그램이 동작한다.
5. 동작 영상
'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 |