MAX7456隨屏顯示(OSD)發(fā)生器具有SPI™兼容接口,本應(yīng)用筆記介紹了SPI接口的工作原理,文中還包含在微控制器內(nèi)逐位模擬SPI接口的控制器C程序。
MAX7456串行接口
MAX7456單通道單色隨屏顯示(OSD)發(fā)生器預(yù)裝了256個字符和圖形,并可通過SPI接口在線編程。通過SPI兼容串行接口可以設(shè)置工作模式、顯示存儲器以及字符存儲器。狀態(tài)(STAT)寄存器、顯示存儲器數(shù)據(jù)輸出(DMDO)寄存器和字符存儲器數(shù)據(jù)輸出(CMDO)寄存器都可讀,可以對其進(jìn)行寫操作和讀操作。關(guān)于MAX7456寄存器及存儲器結(jié)構(gòu)的詳細(xì)信息請參考數(shù)據(jù)資料和應(yīng)用筆記4117,"使用MAX7456存儲器和評估板文件生成定制字符和圖形"。
MAX7456支持高達(dá)10MHz接口時鐘(SCLK)。圖1為寫數(shù)據(jù)時序,圖2是從器件讀數(shù)據(jù)的時序。
寫寄存器時,拉低/CS可使能串行接口。在SCLK的上升沿從SDIN讀取數(shù)據(jù)。當(dāng)/CS變?yōu)楦唠娖綍r,數(shù)據(jù)鎖存到輸入寄存器。如果傳輸過程中/CS變高,程序終止(即數(shù)據(jù)不寫入寄存器)。/CS變低之后,器件等待從SDIN讀入第一個字節(jié),以確定正在執(zhí)行的數(shù)據(jù)傳輸類型。
讀寄存器時,如上文所述,拉低/CS。地址在SCLK的上升沿鎖入SDIN。然后數(shù)據(jù)在SCLK的下降沿從SDOUT輸出。
SPI命令長度為16位:最高8位(MSB)代表寄存器地址,最低8位(LSB)代表數(shù)據(jù)(圖1和2)。這種格式有兩個例外:
- 自動遞增寫模式,用于訪問顯示存儲器,是一個8位操作(圖3)。寫數(shù)據(jù)前必須寫入起始地址。對顯示存儲器執(zhí)行自動遞增寫命令時,8位地址由內(nèi)部產(chǎn)生,串口只需8位數(shù)據(jù),如圖3所示。
- 從顯示存儲器讀字符數(shù)據(jù)時,若處于16位工作模式,應(yīng)該是24位(8位地址+16位數(shù)據(jù))。
執(zhí)行讀操作時,只需要8位地址,如圖2所示。
圖1. 寫操作
圖2. 讀操作
圖3. 自動遞增寫操作
C程序
下文給出的C程序已針對MAXQ2000微控制器進(jìn)行了編譯,用于MAX7456評估(EV)板。本文給出了完整的程序例程。程序是自述文檔,幾乎沒有附加說明。C程序可從以下文件獲得:spi.c和MAX7456.h。
以下程序使用了SPI協(xié)議的標(biāo)準(zhǔn)定義,MAXQ2000處理器為SPI主機(jī),MAX7456是SPI從器件。
CS與MAX7456數(shù)據(jù)資料中的定義相同。
SDIN對應(yīng)于MOSI (主機(jī)出從器件入)。
SDOUT對應(yīng)于MOSI (主機(jī)入從器件出)。
SCLK對應(yīng)于CK。
前綴SPI_用于全部程序。
數(shù)據(jù)結(jié)構(gòu)
下文所示數(shù)據(jù)結(jié)構(gòu)可直接或逐位讀寫數(shù)據(jù),用于獨立訪問SPI端口。
/* Port 5 Output Register */
__no_init volatile __io union
{
unsigned char PO5;
struct
{
unsigned char bit0 : 1;
unsigned char bit1 : 1;
unsigned char bit2 : 1;
unsigned char bit3 : 1;
unsigned char bit4 : 1;
unsigned char bit5 : 1;
unsigned char bit6 : 1;
unsigned char bit7 : 1;
} PO5_bit;
}
上述代碼將一個單字節(jié)賦值給PO5,這是微控制器輸出端口的地址。然后將另一個字節(jié)賦值給相同的可以逐位訪問的存儲器地址。
因此,可用以下命令直接對該端口進(jìn)行尋址:
PO5 = 0x10;
或用以下命令逐位讀寫:
PO5_bit.bit4 = 1;
如果該程序用于其它處理器,該結(jié)構(gòu)需要重新編寫。
如果采用不支持位字段寬度的老式C編譯器,可用位布爾運算設(shè)置及清除位:
/* Portable bit-set and bit-clear macros. */ #define BIT_SET(sfr,bitmask) sfr |= (bitmask) #define BIT_CLR(sfr,bitmask) sfr &=~ (bitmask) #define BIT0 0x01 #define BIT1 0x02 #define BIT2 0x04 #define BIT3 0x08 #define BIT4 0x10 #define BIT5 0x20 #define BIT6 0x40 #define BIT7 0x80 example: BIT_SET(PO5,BIT0); BIT_CLR(PO5,BIT6);
宏
以下是一個簡單的編程技巧,使程序更容易移植:用宏定義控制器引腳排列,如下所示。
#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
用以上宏和數(shù)據(jù)結(jié)構(gòu)可以單獨置位及復(fù)位每個IO口,命令如下:
SPI_CS = 1;
改變宏時相應(yīng)引腳也將改變,將上述代碼用于其它設(shè)計時,如果SPI口引腳排列不同,或為了實現(xiàn)更理想的PCB布局而對引腳進(jìn)行重新排列,上述程序非常有用。
單字節(jié)寫操作程序
單字節(jié)寫操作(圖1)程序如下所示。如果可以保證在程序入口處的/CS和CK線狀態(tài)正確,可以去掉前兩條命令。
程序首先發(fā)送地址,然后發(fā)送數(shù)據(jù)。進(jìn)行兩次循環(huán)。采用單循環(huán)及16位數(shù)據(jù)存儲可以簡化程序。在MAXQ2000微控制器中執(zhí)行16位“int”所占用的時間比執(zhí)行8位“char”長,因此需進(jìn)行權(quán)衡考慮。
/********************************************* * 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; }
讀字節(jié)操作程序
讀字節(jié)操作(圖2)程序如下所示,與上述程序類似。首先發(fā)送地址,然后發(fā)送時鐘從MISO讀回數(shù)據(jù)。
/*********************************************
* 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
}
自動遞增模式下的寫字節(jié)操作程序
自動遞增模式下的寫字節(jié)操作(圖3)程序如下所示,與和上述單字節(jié)寫程序類似。首先發(fā)送地址,然后發(fā)送時鐘從MISO讀回數(shù)據(jù)。
/***********************************************
* 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"的全局變量數(shù)組。定義如下:
extern volatile unsigned char data[DATA_BUF_LENGTH]; DATA_BUF_LENGTH = 968
調(diào)用程序時,data[]包含顯示存儲器內(nèi)容,格式如下:
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結(jié)束,所以該模式下不能向顯示寄存器寫0xFF。如果需要寫OxFF,可以采用單字節(jié)寫指令。
/*************************************************
* 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);
}
寫字符存儲器程序
向字符存儲器寫一個字符的程序如下,每個字符占用18行,每行12像素,共216像素。由于每個字節(jié)定義4個像素,因此定義每一個字符需要54字節(jié)。字符數(shù)據(jù)位于程序入口處的data[] (與上述寫顯示存儲器的程序類似)。
寫字符存儲器時需要進(jìn)行一些附加說明,存儲器為非易失,因此,寫存儲器大約需要12ms,由MAX7456執(zhí)行。只有完整的54字節(jié)字符才可以寫入字符存儲器。
該器件包含一個54字節(jié)映射存儲器。首先把需要寫入的字符數(shù)據(jù)寫入映射存儲器,然后器件將該數(shù)據(jù)裝載到NVM字符存儲器。
用來寫字符存儲器的寄存器有以下幾種:
- 字符存儲器模式 = 0x08。向寄存器寫0xA0,使器件把映射存儲器的內(nèi)容裝載到NVM字符存儲器。
- 字符存儲器地址高位 = 0x09。包括了即將寫入字符的地址。
- 字符存儲器地址低位 = 0x0A。
- 字符存儲器數(shù)據(jù)輸入 = 0x0B。
- Status = 0xA0,讀取該寄存器以決定何時可以寫入字符存儲器。
在程序入口處,data[1]包括即將寫入字符的地址,data[2...54]包括字符數(shù)據(jù)。
向NVM字符存儲器寫字符時,首先寫字符地址。然后將每個字節(jié)寫入映射存儲器。寫映射存儲器時沒有自動遞增模式,所以每次寫操作必須寫入映射存儲器地址。向字符存儲器模式寄存器寫0xA0,可以把映射存儲器的內(nèi)容裝載到NVM字符存儲器。然后器件將狀態(tài)寄存器第5位置高,表明不能寫入字符存儲器。完成后,器件將該位復(fù)位至低。數(shù)據(jù)從映射存儲器移向字符存儲器時不能寫映射存儲器。
為了避免出現(xiàn)顯示器閃爍,在寫字符存儲器之前程序禁止了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的頭文件,以下代碼決定了器件的寄存器映射。
/**********************************************
* spiWriteRegAutoIncr
*
* Writes to an 8-bit register with the SPI port by using the MAX7456's autoincrement mode
*******************************************/
// MAX7456 VIDEO_MODE_0 register
#define VIDEO_MODE_0_WRITE 0x00
#define VIDEO_MODE_0_READ 0x80
#define VIDEO_MODE_0_40_PAL 0x40
#define VIDEO_MODE_0_20_NoAutoSync 0x20
#define VIDEO_MODE_0_10_SyncInt 0x10
#define VIDEO_MODE_0_08_EnOSD 0x08
#define VIDEO_MODE_0_04_UpdateVsync 0x04
#define VIDEO_MODE_0_02_Reset 0x02
#define VIDEO_MODE_0_01_EnVideo 0x01
// VIDEO MODE 0 bitmap
#define NTSC 0x00
#define PAL 0x40
#define AUTO_SYNC 0x00
#define EXT_SYNC 0x20
#define INT_SYNC 0x30
#define OSD_EN 0x08
#define VERT_SYNC_IMM 0x00
#define VERT_SYNC_VSYNC 0x04
#define SW_RESET 0x02
#define BUF_EN 0x00
#define BUF_DI 0x01
// MAX7456 VIDEO_MODE_1 register
#define VIDEO_MODE_1_WRITE 0x01
#define VIDEO_MODE_1_READ 0x81
// MAX7456 DM_MODE register
#define DM_MODE_WRITE 0x04
#define DM_MODE_READ 0x84
// MAX7456 DM_ADDRH register
#define DM_ADDRH_WRITE 0x05
#define DM_ADDRH_READ 0x85
// MAX7456 DM_ADDRL register
#define DM_ADDRL_WRITE 0x06
#define DM_ADDRL_READ 0x87
// MAX7456 DM_CODE_IN register
#define DM_CODE_IN_WRITE 0x07
#define DM_CODE_IN_READ 0x87
// MAX7456 DM_CODE_OUT register
#define DM_CODE_OUT_READ 0xB0
// MAX7456 FM_MODE register
#define FM_MODE_WRITE 0x08
#define FM_MODE_READ 0x88
// MAX7456 FM_ADDRH register
#define FM_ADDRH_WRITE 0x09
#define FM_ADDRH_READ 0x89
// MAX7456 FM_ADDRL register
#define FM_ADDRL_WRITE 0x0A
#define FM_ADDRL_READ 0x8A
// MAX7456 FM_DATA_IN register
#define FM_DATA_IN_WRITE 0x0B
#define FM_DATA_IN_READ 0x8B
// MAX7456 FM_DATA_OUT register
#define FM_DATA_OUT_READ 0xC0
// MAX7456 STATUS register
#define STATUS_READ 0xA0
#define STATUS_40_RESET_BUSY 0x40
#define STATUS_20_NVRAM_BUSY 0x20
#define STATUS_04_LOSS_OF_SYNC 0x04
#define STATUS_02_PAL_DETECTED 0x02
#define STATUS_01_NTSC_DETECTED 0x01
// MAX7456 requires clearing OSD Black Level
// register bit 0x10 after reset
#define OSDBL_WR 0x6C
#define OSDBL_RD 0xEC
#define OSDBL_10_DisableAutoBlackLevel 0x10
結(jié)論和性能
MAX7456評估板采用工作在20MHz時鐘的MAXQ2000微控制器,該微控制器包含內(nèi)部硬件SPI控制器。因此,MAX7456的SPI端口可以全速工作。上述軟件SPI程序工作速度低于硬件控制器。不過針對客戶缺少硬件SPI端口的工作環(huán)境,程序已優(yōu)化至最簡。
SPI是Motorola, Inc.的商標(biāo)。
電子發(fā)燒友App






























評論