1. 프로그램 개요
P22는 Green 0 LED, P24는 Red LED, P26은 Green 1 LED가 연결되어 있다.
SPI 통신으로 MX25L3206 IC를 Read/Write 테스트함.
- Flash ID 읽기
- Flash Erase
- Flash Write
- Flash Read
2. 회로도
3. Code
⑴ main routine
void main()
{
u32 i;
bit bOK;
DWordTypeDef dwTemp;
InitSystem(); // 시스템 초기화
INT_EnAll(); // EA = 1,
SendStr("\nHello!"); // 시리얼로 Hello 전송, program start
LED_G_0=0;LED_G_1=0;LED_R=0; // LED ALL ON
DelayXms(1000); // 1초 딜레이
LED_G_0=0;LED_G_1=1;LED_R=1; // LED ALL OFF
dwTemp.DW=SPI_Flash_Read_ID(); // dwTemp.DW에 Flash ID 읽은 것을 저장
SendStr("\nFlash ID: "); // 시리얼로 Flash ID 전송
SendHex(dwTemp.B.BMHigh); // RDID는 1byte Manufacture ID, 2byte Memory ID(type, density)로 구성
SendHex(dwTemp.B.BMLow); // 0xC2, 0x20, 0x16
SendHex(dwTemp.B.BLow);
// Clear write protect
SPI_Flash_Write_Status(0x00); // Flash Status 초기화
if(dwTemp.DW == FLASH_ID) // 읽은 Flash ID가 0x00C22016 이 맞으면
{
bOK=TRUE; // bOK플래그에 TRUE를 저장
// Test
SendStr("\nFlash Erase..."); // Flash를 지운다.
i=FLASH_TEST_SIZE_MAX/FLASH_BLOCK_SIZE; // (용량최대1024*1024)/(블록크기64kB 64*1024)
// Erase Flash // i = 16이 된다.
dwTemp.DW=FLASH_TEST_START_ADDRESS; // Flash의 시작 주소는 0x00000000 이다.
ResetCounter(); // timer 0, 1의 카운터를 0으로 클리어시킴
while(i!=0)
{
LED_G_1 = !LED_G_1; // LED_G_1을 토글시킴
if(SPI_Flash_Block_Erase(dwTemp.DW)!=0) // 블록 Erase
{
bOK=FALSE;
break;
}
dwTemp.DW=dwTemp.DW+FLASH_BLOCK_SIZE; // Erase 후 다음 블록 주소를 저장
i--; // 블록 카운트를 1 감소 시킴
}
SendCounter(); // Erase에 소요된 시간을 시리얼로 전송함
for(i=0;i<256;i++) // 테스트 버퍼에 0 ~ 255까지 저장
{
TestBuf[i]=i;
}
// Write Flash
SendStr("\nFlash Write 1M");
i=FLASH_TEST_SIZE_MAX/256; // (용량최대1024*1024)/256 = 4096
dwTemp.DW=FLASH_TEST_START_ADDRESS; // Flash start address = 0을 DW에 저장
ResetCounter(); // 카운터를 초기화 시킴
while(i!=0)
{
LED_G_1 = !LED_G_1; // LED_G_1을 토글
if(SPI_Flash_Page_Program(dwTemp.DW,&TestBuf[0],256)!=0) // 선택된 페이지 어드레스에서부터 테스트 버퍼의
{ // 데이터를 256바이트 저장한다.
bOK=FALSE; //
break;
}
dwTemp.DW=dwTemp.DW+256; // 저장을 시작할 주소에 +256을 하고
i--; // 저장 회수를 -1한다.
}
SendCounter(); // 저장이 끝나면 소요된 시간을 시리얼로 전송한다.
// Read Flash
SendStr("\nFlash Read 1M");
i=FLASH_TEST_SIZE_MAX/512; // (용량최대1024*1024)/512 = 2048
dwTemp.DW=FLASH_TEST_START_ADDRESS; // Flash start address = 0
ResetCounter(); // 카운터 초기화
while(i!=0)
{
LED_G_1 = !LED_G_1; // LED_G_1토글
SPI_Flash_MultiBytesRead(dwTemp.DW,&TestBuf[0],512); // 지정된 주소로부터 512바이트를 읽어 테스트 버퍼에저장
dwTemp.DW=dwTemp.DW+512; // 주소에 +512
i--; // 블록 카운트 -1
}
SendCounter(); // 읽기에 소요된 시간을 시리얼로 전송함
}
else
{
SendStr("\nFlash isn't MX25L3206! "); // 연결된 플래시가 틀리면
bOK=FALSE;
}
while(1)
{ // Erase-Write-Read 동작이 모두 완료되면 LED_G_0을 토글 시키고
DelayXms(100); // 에러가 있었으면 LED_R을 토글 시킨다.
if(bOK)
{
LED_G_0 = !LED_G_0;
}
else
{
LED_R = !LED_R;
}
}
}
⑵ 시스템 초기화 루틴
/***********************************************************************************
*Function: void InitSystem(void)
*Description: Initialize MCU
*Input:
*Output:
*************************************************************************************/
void InitSystem(void)
{
InitPort();
InitClock();
InitSPI_Master();
InitUart0_S0BRG();
InitTimer0();
InitTimer1();
InitTimer2();
InitInterrupt();
}
- 포트 초기화
- 클럭 초기화
- SPI 초기화
- S0BRG를 이용한 UART0 설정
- 타이머 0, 1, 2, 초기화
- 인터럽트 초기화
⑶ 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 연결
PORT_SetP3PushPull(BIT3); // Set P33 as Push-Pull, For nSS
è P33을 Push-Pull type으로 설정. nSS 핀으로 사용된다. Slave 통신을 활성화 시키는 핀
PORT_SetP1PushPull(BIT4|BIT5|BIT7); // Set P14(nSS),P15(MOSI),P17(SPICLK) as push-pull for output.
è P14, P15, P17을 PushPull type으로 설정하고, SPI 통신 핀으로 사용한다. P33은 뭐지?
PORT_SetP1OpenDrainPu(BIT6); // Set P16(MISO) as open-drain with pull-high for digital input
è P16은 Pull Up을 가지는 Open Drain으로 설정한다. P16은 입력(MISO)핀이다.
}
*** 사용된 매크로함수는 “API_Macro_MG82FG6D16.H”에서 찾아볼 수 있다.
\Megawin 8051\(EN)MG82F6D16_SampleCode_v1.20\MG82F6D16_SPI_Flash\code\include
⑷ 클럭 초기화 루틴 è 시스템 클럭 및 내부 클럭을 설정한다.
프로그램의 루틴 자체는 복잡하게 많이 설정해 두었으나 그 구조는 아래와 같다.
MCU_SYSCLK의 값은 11059200, 12000000, 22118400, 24000000, 29491200, 32000000, 44236800, 48000000로 설정이 가능하며, 각각 프로그램 상단에
#define MCU_SYSCLK 48000000
와 같이 선언해주었다. 그리고, 바로
#define MCU_CPUCLK (MCU_SYSCLK/2)
로 선언하여 (System 클럭/2)과 CPU 클럭을 동일하게 사용하기로 선언하였다. 물론 사용자의 선택에 따라 틀려질 수 있으므로 어플리케이션에 따라 선언하면 된다.
#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
- CLK_SetCKCON0(IHRCO_12MHz|CPUCLK_SYSCLK_DIV_2|SYSCLK_MCKDO_DIV_1|ENABLE_CKM|CKM_OSCIN_DIV_2);
CKCON0 레지스터의 각 비트를 설정한다.
CKCON0.7 : AFS, Alternated Frequency Selection, 내부 클럭 IHRCO를 12MHz(AFS = 0), 또는 11.059MHz(AFS = 1)로 설정함
CKCON0.6 : ENCKM, X8 클럭 멀티플라이어를 동작시킴
CKCON0.5~4 : OSCin이 12MHz이므로 01을 입력한다.
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)
- CLK_SetCKCON2(ENABLE_IHRCO|MCK_CKMI_X8|OSCIn_IHRCO);
IHRCO를 동작시키고, CKMI x8을 동작 시키고, OSCIn을 IHRCO로 선택한다.
/***********************************************************************************
*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();
}
⑸ UART0초기화 èS0를 이용해서 UART0의 Baud Rate를 설정한다.
/***********************************************************************************
*Function: void InitUart0_S0BRG(void)
*Description: Initialize Uart0, The source of baud rate was S0BRG
*Input:
*Output:
*************************************************************************************/
void InitUart0_S0BRG(void)
{
UART0_SetAccess_S0CR1(); // Enable access S0CR1
UART0_SetMode8bitUARTVar(); // UART0 Mode: 8-bit, Variable B.R.
UART0_EnReception(); // Enable reception
UART0_SetBRGFromS0BRG(); // B.R. source:S0BRG
UART0_SetS0BRGBaudRateX2(); // B.R. x2
UART0_SetS0BRGSelSYSCLK(); // S0BRG clock source: SYSCLK
// Sets B.R. value
UART0_SetS0BRGValue(S0BRG_BRGRL_9600_2X_12000000_1T);
UART0_EnS0BRG(); // Enable S0BRG
}
- UART0의 MODE 설정
#define UART0_SetAccess_S0CR1() S0CFG=S0CFG|(SMOD3)
S0CFG 레지스터의 0번 비트인 SMOD3을 셋 시킴으로써 S0CR1을 제어가능하게 된다. CPU는 S0CR1을 읽고 쓰기 위해SFR어드레스 0xB9을 제어한다.
- UART0를 MODE 1으로 설정한다.
#define UART0_SetMode8bitUARTVar() S0CFG=S0CFG&(~SM30);SM00_FE=0;SM10=1
SM30 비트 클리어, SM00비트 클리어, SM10 비트 셋 시킴으로서 MODE 1으로 설정한다. Mode 1은 8-bit UART이고, Baud Rate는 가변이 된다.
- UART0의 Receive Enable
#define UART0_EnReception() REN0=1
UART0이 시리얼 통신으로 데이터를 받아들일 수 있도록 설정한다. S0CON 레지스터의 REN0를 셋(=1) 시킨다.
- UART0의 Baud Rate를 S0BRG를 이용한다.
#define UART0_SetBRGFromS0BRG() S0CR1=S0CR1|(S0TCK|S0RCK)
앞서 S0CR1을 제어 가능하도록 설정해 두었으며, S0TCK, S0RCK를 셋 시킴으로써 S0BRG의 Overflow를 UART0의 송신/수신 클럭으로 사용하도록 설정한다.
- Baud Rate를 2배로 설정한다.
#define UART0_SetS0BRGBaudRateX2() PCON0=PCON0&(~SMOD1);S0CFG=S0CFG|(SMOD2)
SMOD1을 클리어 시켜 Double Baud Rate를 Disable 시키고, SMOD2를 셋 시켜 향상된 기능의 UART를 사용한다. Baud Rate는 Double Baud Rate x 2가 된다.
- S0BRG의 클럭 설정
#define UART0_SetS0BRGSelSYSCLK() S0CR1=S0CR1|(S0TX12)
S0BRG의 클럭을 SYSCLK를 사용하도록 한다.
- Baud Rate : 9600 설정
#define S0BRG_BRGRL_9600_2X_12000000_1T 0xB2 // 0.16%
#define UART0_SetS0BRGValue(x) S0BRT=x;S0BRC=x
위의 수식으로 계산 시 S0BRT는 177.875가 나온다. 따라서 9600 Baud Rate를 위해서는 178(0xB2)을 S0BRT, S0BRC에 초기값으로 저장한다.
- S0BRG를 동작시킨다.
#define UART0_EnS0BRG() S0CR1=S0CR1|S0TR
è (EN)MG82F6D16_Datasheet_V051.PDF의 17. Serial Port 0(UART0)를 참조한다.
è (EN)MG82F6D17_Datasheet_V051.PDF의 18. Serial Port 0(UART0)를 참조한다.
è (EN)MG82F6D64/32_Datasheet_V051.PDF의 18. Serial Port 0(UART0)를 참조한다.
* 길게써서 그런건지...내 컴이 구형이라 그런건지 일정 용량 이상으로 글을 쓰면 Tistory 에디터가 먹통이 된다.
** 그래서 내용이 긴 것은 중간에 짤라야겠다.
'MEGAWIN' 카테고리의 다른 글
MG82F6D SPI Master (1) | 2022.12.26 |
---|---|
MG82F6D SPI Flash-2 (0) | 2022.12.22 |
MG82F6D Series PCA COPM Buf (0) | 2022.12.21 |
MG82F6D Series PCA Capture Buf (0) | 2022.12.20 |
MG82F6D Series INT KBI Wake UP (0) | 2022.12.19 |