MAX7456 온 스크린 디스플레이에서 SPI 인터페이스를 사용하기 위한 C 코드 소프트웨어 루틴
개요: MAX7456 온 스크린 디스플레이 (OSD) 발생기에는 SPI™ 호환 제어 인터페이스가 내장되어 있다. 이 애플리케이션 노트에서는 SPI 인터페이스의 동작에 대해 설명하고, 마이크로컨트롤러가 비트 뱅잉(bit-banged)된 SPI 인터페이스를 통해 부품을 제어하는 데 사용할 수 있는 C 코드를 제공한다.
The MAX7456 직렬 인터페이스
MAX7456 단일 채널, 모노크롬 온 스크린 디스플레이 (OSD) 발생기에는 256개의 문자와 그래픽이 탑재되어 있으며 SPI 포트를 사용하여 인서킷 재프로그래밍이 가능하다. SPI 호환 직렬 인터페이스는 동작 모드, 디스플레이 메모리 및 문자 메모리를 프로그래밍한다. 읽기 기능을 통해 상태(STAT), 디스플레이 메모리 데이터 출력(DMDO), 문자 메모리 데이터 출력 (CMDO) 레지스터의 쓰기 확인과 읽기가 모두 가능하다. MAX7456 레지스터와 메모리 구성에 대한 자세한 내용은 제품 데이터 시트와 애플리케이션 노트 4117, "MAX7456의 메모리 및 EV 킷 파일 형식을 사용한 맞춤형 문자와 그래픽 생성"을 참조한다.
MAX7456은 최대 10MHz까지 인터페이스 클록(SCLK)을 지원한다. 그림 1은 소자로부터의 데이터 쓰기를 보여주며 그림 2는 데이터 읽기를 보여준다.
레지스터에 쓰려면 액티브 로우 CS를 로우로 구동하여 직렬 인터페이스를 동작시킨다. 데이터는 SCLK의 상승 에지에서 SDIN에 클록 인 (clock in) 된다. 액티브 로우 CS가 하이로 전환되면 데이터는 입력 레지스터로 래치된다. 만약 액티브 로우 CS가 전송 중에 하이가 되면 시퀀스가 취소된다(즉 데이터가 레지스터에 기록되지 않는다). 액티브 로우 CS가 로우로 구동된 후 소자는 첫 번째 바이트가 SDIN에 클록 인 될 때까지 기다린 다음 실행 중인 데이터 전송 유형을 식별한다.
레지스터를 읽으려면 앞에서 설명했듯이 액티브 로우 CS를 로우로 구동한다. 어드레스는 위 설명대로 SCLK의 상승 에지에서 SDIN에 클록 인 된다. 그런 다음 데이터는 SCLK의 하강 에지에서 SDOUT에 클록 아웃 된다.
SPI 명령은 16비트 길이를 갖는데, 8개의 최상위 비트(MSB)는 레지스터 어드레스를 나타내고 8개의 최하위 비트(LSB)는 데이터를 나타낸다(그림 1과 2). 이러한 구성에는 다음 두 가지 예외가 있다.
디스플레이 메모리 액세스에 사용되는 자동 증가 쓰기 모드는 단일 8비트 동작이다(그림 3). 시작 어드레스는 데이터를 쓰기 전에 설정되어야 한다. 디스플레이 메모리에 자동 증가 쓰기를 수행할 경우 8비트 어드레스는 내부에서 생성되므로 직렬 인터페이스는 그림 3과 같이 8비트 데이터만 요구한다.
디스플레이 메모리로부터 문자 데이터 읽기는 16비트 동작 모드일 때 24비트 동작(8비트 어드레스 + 16비트 데이터)이다.
읽기 동작을 수행할 때에는 8비트 어드레스만 필요하다(그림 2 참조).
그림 1. 쓰기 동작
그림 2. 읽기 동작
그림 3. 자동 증가 모드에서 쓰기 동작
C 코드 루틴
아래에 설명되는 C 코드는 MAX7456 EV 킷에 사용되는 MAXQ2000 마이크로컨트롤러용으로 컴파일된 것이다. 이 애플리케이션 노트는 완전한 소프트웨어 루틴 세트를 제공한다. 루틴은 자체적으로 설명되어 있어 추가적인 설명이 거의 필요하지 않다. 아래의 C 코드는 spi.c 및 MAX7456.h파일로도 제공된다.
이 코드는 SPI 라인의 표준 명명법을 따른다. MAXQ2000 프로세서는 SPI 마스터이고 MAX7456은 SPI 슬레이브이다.
CS는 MAX7456 데이터 시트에 사용되는 기호와 같다.
SDIN은 MOSI로 표시한다(마스터 출력 슬레이브 입력).
SDOUT은 MOSI로 표시한다(마스터 입력 슬레이브 출력).
SCLK는 CK로 표시한다.
프리픽스 (prefix) SPI_는 모든 라인에 사용된다.
데이터 구조
아래에 보이는 데이터 구조는 데이터에 직접 또는 비트별로 액세스하는 데 사용된다. 비트별 액세스는 SPI 포트 핀에 개별적으로 액세스할 때 사용된다. (C++ 및 일부 새로운 C 컴파일러는 비트 필드 (bit-field) union/struct 구문을 지원한다).
다음은 루틴의 이동성을 높이는 간단한 방법으로 다음과 같이 매크로를 사용하여 컨트롤러 핀 할당을 정의한다.
#define SPI_CS PO5_bit.bit4 // PO5_bit.bit4 = active-low CS—chip select
#define SPI_MOSI PO5_bit.bit5 // PO5_bit.bit5 = MOSI—master out slave in,
// data to MAX7456
#define SPI_MISO PI5_bit.bit7 // PO5_bit.bit7 = MISO—master in slave out,
// data from MAX7456
#define SPI_CK PO5_bit.bit6 // PO5_bit.bit6 = SCK - SPI clock
이 매크로와 위의 데이터 구조를 사용하면 다음과 같은 명령으로 IO 포트의 각 핀을 개별적으로 설정 및 리셋할 수 있다.
SPI_CS = 1;
매크로를 변경하여 핀을 이동시킬 수 있다. 이 방법은 SPI 포트에 다른 핀을 할당하는 다른 설계에 코드를 사용해야 하거나 PCB 라우팅을 개선하기 위해 핀을 재할당할 필요가 있을 때 유용하다.
단일 바이트 쓰기를 위한 코드
단일 바이트 쓰기 동작(그림 1)을 위한 코드는 아래에 나와 있다. 엔트리에 액티브 로우 CS와 CK 라인이 올바른 상태에 있는 것이 확실하다면 처음 두 명령은 생략할 수 있다.
루틴은 먼저 어드레스를 전송하고 이어서 데이터를 전송한다. 이를 위해 2개의 루프가 사용된다. 단일 루프와 16비트 데이터 저장소를 사용하여 루틴을 간소화할 수 있다. MAXQ2000 마이크로컨트롤러에서 16비트 "int"를 순환하는 것이 8비트 "char"를 순환하는 것보다 오랜 시간이 걸리므로 trade-off된다.
/**************************************************************************************
* spiWriteReg
*
* Writes to an 8-bit register with the SPI port
**************************************************************************************/
void spiWriteReg(const unsigned char regAddr, const unsigned char regData)
{
unsigned char SPICount; // Counter used to clock out the data
unsigned char SPIData; // Define a data structure for the SPI data
SPI_CS = 1; // Make sure we start with active-low CS high
SPI_CK = 0; // and CK low
SPIData = regAddr; // Preload the data to be sent with Address
SPI_CS = 0; // Set active-low CS low to start the SPI cycle
// Although SPIData could be implemented as an "int",
// resulting in one
// loop, the routines run faster when two loops
// are implemented with
// SPIData implemented as two "char"s.
for (SPICount = 0; SPICount < 8; SPICount++) // Prepare to clock out the Address byte
{
if (SPIData & 0x80) // Check for a 1
SPI_MOSI = 1; // and set the MOSI line appropriately
else
SPI_MOSI = 0;
SPI_CK = 1; // Toggle the clock line
SPI_CK = 0;
SPIData <<= 1; // Rotate to get the next bit
} // and loop back to send the next bit
// Repeat for the Data byte
SPIData = regData; // Preload the data to be sent with Data
for (SPICount = 0; SPICount < 8; SPICount++)
{
if (SPIData & 0x80)
SPI_MOSI = 1;
else
SPI_MOSI = 0;
SPI_CK = 1;
SPI_CK = 0;
SPIData <<= 1;
}
SPI_CS = 1;
SPI_MOSI = 0;
}
바이트 읽기 동작을 위한 코드
바이트 읽기 동작(그림 2)을 위한 코드는 아래에 나와 있다. 이 코드는 위의 루틴과 유사하다. 먼저 어드레스가 전송되고 이어서 클록을 토글링한 다음 MISO 라인에서 데이터를 읽어들여 인식한다.
/**************************************************************************************
* spiReadReg
*
* Reads an 8-bit register with the SPI port.
* Data is returned.
**************************************************************************************/
unsigned char spiReadReg (const unsigned char regAddr)
{
unsigned char SPICount; // Counter used to clock out the data
unsigned char SPIData;
SPI_CS = 1; // Make sure we start with active-low CS high
SPI_CK = 0; // and CK low
SPIData = regAddr; // Preload the data to be sent with Address and Data
SPI_CS = 0; // Set active-low CS low to start the SPI cycle
for (SPICount = 0; SPICount < 8; SPICount++) // Prepare to clock out the Address and Data
{
if (SPIData & 0x80)
SPI_MOSI = 1;
else
SPI_MOSI = 0;
SPI_CK = 1;
SPI_CK = 0;
SPIData <<= 1;
} // and loop back to send the next bit
SPI_MOSI = 0; // Reset the MOSI data line
SPIData = 0;
for (SPICount = 0; SPICount < 8; SPICount++) // Prepare to clock in the data to be read
{
SPIData <<=1; // Rotate the data
SPI_CK = 1; // Raise the clock to clock the data out of the MAX7456
SPIData += SPI_MISO; // Read the data bit
SPI_CK = 0; // Drop the clock ready for the next bit
} // and loop back
SPI_CS = 1; // Raise CS
return ((unsigned char)SPIData); // Finally return the read data
}
자동 증가를 사용하는 바이트 쓰기 동작을 위한 코드
자동 증가 함수를 사용하는 바이트 쓰기 동작(그림 3)을 위한 코드는 아래에 나와 있다. 이 코드 역시 위의 단일 바이트 쓰기 루틴과 유사하다. 먼저 어드레스가 전송되고 이어서 클록을 토글링한 다음 MISO 라인에서 데이터를 읽어들여 인식한다.
/**************************************************************************************
* spiWriteRegAutoIncr
*
* Writes to an 8-bit register with the SPI port using the MAX7456's autoincrement mode
**************************************************************************************/
void spiWriteRegAutoIncr(const unsigned char regData)
{
unsigned char SPICount; // Counter used to clock out the data
unsigned char SPIData; // Define a data structure for the SPI data.
SPI_CS = 1; // Make sure we start with active-low CS high
SPI_CK = 0; // and CK low
SPIData = regData; // Preload the data to be sent with Address and Data
SPI_CS = 0; // Set active-low CS low to start the SPI cycle
for (SPICount = 0; SPICount < 8; SPICount++) // Prepare to clock out the Address and Data
{
if (SPIData & 0x80)
SPI_MOSI = 1;
else
SPI_MOSI = 0;
SPI_CK = 1;
SPI_CK = 0;
SPIData <<= 1;
} // and loop back to send the next bit
SPI_MOSI = 0; // Reset the MOSI data line
}
자동 증가를 사용하여 디스플레이 메모리에 쓰기를 위한 코드
다음 루틴은 자동 증가 함수를 사용하여 디스플레이 메모리에 쓴다. 코드는 "data"라는 전체 변수 어레이를 사용하며, 다음과 같이 정의된다.
data[0] = ignored (contains a command byte used by the EV kit GUI software)
data[1] = character byte 1
data[2] = attribute byte 1
data[3] = character byte 2
data[4] = attribute byte 2
etc.
자동 증가 모드는 0xFF를 쓰면 종료된다. 따라서 이 모드에서는 문자 0xFF를 디스플레이에 쓸 수 없다. 이 문자를 써야 할 경우에는 단일 바이트 쓰기를 사용할 수 있다.
/**************************************************************************************
* spiWriteCM
*
* Writes to the Display Memory (960 bytes) from "data" extern.
* 960 = 16 rows × 30 columns × 2 planes {char vs. attr} screen-position-indexed memory
**************************************************************************************/
void spiWriteCM() // On entry: global data[1..960]
// contains char+attr bytes
// (optionally terminated by 0xFF data)
// First, write data[1,3,5,...] Character plane;
// MAX7456 WriteReg(0x05,0x41)
// "Character Memory Address High";
// 0x02:Attribute bytes;
// 0x01:character memory address msb
{
volatile unsigned int Index = 0x0001; // Index for lookup into
// data[1..960]
spiWriteReg(DM_ADDRH_WRITE,0x00); // initialise the Display Memory high-byte
spiWriteReg(DM_ADDRL_WRITE,0x00); // and the low-byte
spiWriteReg(DM_MODE_WRITE ,0x41); // MAX7456 WriteReg(0x04,0x41) "Display Memory Mode";
// 0x40:Perform 8-bit operation; 0x01:AutoIncrement
Do // Loop to write the character data
{
if (data[Index] == 0xFF) { // Check for the break character
break; } // and finish if found
spiWriteRegAutoIncr(data[Index]); // Write the character
Index += 2; // Increment the index to the next character,
// skipping over the attribute
} while(Index < 0x03C1); // 0x03C1 = 961
// and loop back to send the next character
spiWriteRegAutoIncr(0xFF); // Write the "escape character" to end AutoIncrement
// mode
spiWriteReg(DM_ADDRH_WRITE,0x02); // Second, write data[2,4,6,...]
// Attribute plane; MAX7456
// WriteReg(0x05,0x41)
// "Character Memory Address High";
// 0x02:Attribute bytes; 0x01:character memory address
// msb
spiWriteReg(DM_ADDRL_WRITE,0x00);
spiWriteReg(DM_MODE_WRITE,0x41); // MAX7456 WriteReg(0x04,0x41) "Character Memory
// Mode"; 0x40:Perform 8-bit operation; 0x01:Auto-
// Increment
Index = 0x0002;
do
{
if (data[Index] == 0xFF)
break;
spiWriteRegAutoIncr(data[Index]);
Index += 2;
} while(Index < 0x03C1);
spiWriteRegAutoIncr(0xFF);
}
문자 메모리에 쓰기를 위한 코드
다음 루틴은 문자 메모리에 단일 문자를 쓴다. 각 문자는 12픽셀 x 18라인으로 총 216픽셀이다. 각 바이트는 4픽셀을 정의하므로 각 문자를 정의하는 데에는 54바이트가 필요하다. 문자에 대한 데이터는 입력과 동시에 data[]에 임시 저장된다. (이 루틴은 위의 디스플레이 메모리에 쓰기를 위한 루틴과 유사하다).
문자 메모리에 쓰기는 따로 약간의 설명을 할 필요가 있다. 메모리는 비휘발성이며 문자 메모리에 쓰기는 약 12ms가 소요되고 MAX7456 자체적으로 수행된다. 54바이트의 전체 문자만 문자 메모리에 쓸 수 있다.
이 소자에는 54바이트 섀도우 메모리가 포함되어 있다. 먼저 이 메모리는 쓰여지는 문자 데이터로 채워진다. 그런 다음 소자는 이 데이터를 NVM 문자 메모리에 쓰도록 트리거된다.
문자 메모리에 쓸 때는 다음과 같은 레지스터가 사용된다.
Character Memory Mode = 0x08. 소자가 NVM 문자 메모리에 섀도우 메모리를 쓰도록 트리거하려면 이 레지스터에 0xA0을 쓴다.
Character Memory Address High = 0x09. 여기에는 쓰여지는 문자 어드레스가 포함된다.
Character Memory Address Low = 0x0A.
Character Memory Data In = 0x0B
Status = 0xA0. 이 상태를 읽어 문자 메모리에 언제 쓸 수 있는지를 결정한다.
엔트리에서 data[1]은 쓰여지는 문자의 어드레스를 포함하며, data[2...54]는 문자에 대한 데이터를 포함한다.
NVM 문자 메모리에 문자를 쓰려면 먼저 문자의 어드레스를 쓴다. 이어 각 바이트를 섀도우 메모리에 쓴다. 섀도우 메모리에 쓰기의 경우 자동 증가 모드가 없으므로 섀도우 메모리 내의 어드레스는 매번 써야 한다. 그런 다음 Character Memory Mode 레지스터에 0xA0을 쓰면 섀도우 메모리를 NVM 문자 메모리에 쓸 수 있다. 이제 소자는 Status 레지스터 비트 5를 하이로 설정하여 문자 메모리에 쓸 수 없음을 표시한다. 쓰기가 완료되면 소자는 이 비트를 로우로 리셋한다. 내용이 문자 메모리에 전송되는 동안 섀도우 메모리에 쓰기를 시도해서는 안 된다.
디스플레이 플리커 발생을 방지하기 위해 문자 메모리에 쓰기 전에 루틴에서 OSD를 디스에이블한다.
/**************************************************************************************
* spiWriteFM
*
* Writes to the Character Memory (54 bytes) from "data" extern
**************************************************************************************/
void spiWriteFM()
{
unsigned char Index;
spiWriteReg(VIDEO_MODE_0_WRITE,spiReadReg
(VIDEO_MODE_0_READ) & 0xF7); // Clear bit 0x08 to DISABLE the OSD display
spiWriteReg(FM_ADDRH_WRITE,data[1]); // Write the address of the character to be written
// MAX7456 glyph tile definition
// length = 0x36 = 54 bytes
// MAX7456 64-byte Shadow RAM accessed
// through FM_DATA_.. FM_ADDR.. contains a single
// character/glyph-tile shape
for(Index = 0x00; Index < 0x36; Index++)
{
spiWriteReg(FM_ADDRL_WRITE,Index); // Write the address within the shadow RAM
spiWriteReg(FM_DATA_IN_WRITE,data[Index + 2]); // Write the data to the shadow RAM
}
spiWriteReg(FM_MODE_WRITE, 0xA0); // MAX7456 "Font Memory Mode" write 0xA0 triggers
// copy from 64-byte Shadow RAM to NV array.
while ((spiReadReg(STATUS_READ) & 0x20) != 0x00); // Wait while NV Memory status is BUSY
// MAX7456 0xA0 status bit 0x20: NV Memory Status
// Busy/~Ready
}
MAX7456을 위한 헤더 파일
다음 리스트는 MAX7456을 위한 헤더 파일이다. 이 코드는 소자의 레지스터 맵을 정의한다.
MAX7456 EV 킷은 20MHz 클록으로 실행되는 MAXQ2000 마이크로컨트롤러를 사용한다. 이 마이크로컨트롤러에는 하드웨어 SPI 컨트롤러가 내장되어 있어 MAX7456의 SPI 포트를 풀 스피드로 실행할 수 있다. 위의 소프트웨어 SPI 루틴은 하드웨어 컨트롤러보다 속도가 느리다. 그러나 고객 애플리케이션에 SPI 포트가 없는 경우 이동성을 위해 루틴이 최적화되었다.
SPI는 Motorola, Inc.의 상표이다.
의견을 보내주세요! 위 내용이 도움이 되셨나요? 여러분의 의견을 기다립니다 — Maxim은 보내주신 정정이나 제안사항을 반영하고 있습니다.
이 페이지를 평가하고 의견을 보내주십시오.