max7456随屏显示(osd)发生器具有spi™兼容接口,本应用笔记介绍了spi接口的工作原理,文中还包含在微控制器内逐位模拟spi接口的控制器c程序。
max7456串行接口
max7456单通道单色随屏显示(osd)发生器预装了256个字符和图形,并可通过spi接口在线编程。通过spi兼容串行接口可以设置工作模式、显示存储器以及字符存储器。状态(stat)寄存器、显示存储器数据输出(dmdo)寄存器和字符存储器数据输出(cmdo)寄存器都可读,可以对其进行写操作和读操作。关于max7456寄存器及存储器结构的详细信息请参考数据资料和应用笔记4117,使用max7456存储器和评估板文件生成定制字符和图形。
max7456支持高达10mhz接口时钟(sclk)。图1为写数据时序,图2是从器件读数据的时序。
写寄存器时,拉低/cs可使能串行接口。在sclk的上升沿从sdin读取数据。当/cs变为高电平时,数据锁存到输入寄存器。如果传输过程中/cs变高,程序终止(即数据不写入寄存器)。/cs变低之后,器件等待从sdin读入第一个字节,以确定正在执行的数据传输类型。
读寄存器时,如上文所述,拉低/cs。地址在sclk的上升沿锁入sdin。然后数据在sclk的下降沿从sdout输出。
spi命令长度为16位:最高8位(msb)代表寄存器地址,最低8位(lsb)代表数据(图1和2)。这种格式有两个例外:
自动递增写模式,用于访问显示存储器,是一个8位操作(图3)。写数据前必须写入起始地址。对显示存储器执行自动递增写命令时,8位地址由内部产生,串口只需8位数据,如图3所示。
从显示存储器读字符数据时,若处于16位工作模式,应该是24位(8位地址+16位数据)。
执行读操作时,只需要8位地址,如图2所示。
图1. 写操作
图2. 读操作
图3. 自动递增写操作
c程序
下文给出的c程序已针对maxq2000微控制器进行了编译,用于max7456评估(ev)板。本文给出了完整的程序例程。程序是自述文档,几乎没有附加说明。c程序可从以下文件获得:spi.c和max7456.h。
以下程序使用了spi协议的标准定义,maxq2000处理器为spi主机,max7456是spi从器件。
cs与max7456数据资料中的定义相同。
sdin对应于mosi (主机出从器件入)。
sdout对应于mosi (主机入从器件出)。
sclk对应于ck。
前缀spi_用于全部程序。
数据结构
下文所示数据结构可直接或逐位读写数据,用于独立访问spi端口。c++和一些较新的c编译器支持位字段联合/结构语句)。
/* 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;
}
上述代码将一个单字节赋值给po5,这是微控制器输出端口的地址。然后将另一个字节赋值给相同的可以逐位访问的存储器地址。
因此,可用以下命令直接对该端口进行寻址:
po5 = 0x10;
或用以下命令逐位读写:
po5_bit.bit4 = 1;
如果该程序用于其它处理器,该结构需要重新编写。
如果采用不支持位字段宽度的老式c编译器,可用位布尔运算设置及清除位:
/* 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
用以上宏和数据结构可以单独置位及复位每个io口,命令如下:
spi_cs = 1;
改变宏时相应引脚也将改变,将上述代码用于其它设计时,如果spi口引脚排列不同,或为了实现更理想的pcb布局而对引脚进行重新排列,上述程序非常有用。
单字节写操作程序
单字节写操作(图1)程序如下所示。如果可以保证在程序入口处的/cs和ck线状态正确,可以去掉前两条命令。
程序首先发送地址,然后发送数据。进行两次循环。采用单循环及16位数据存储可以简化程序。在maxq2000微控制器中执行16位“int”所占用的时间比执行8位“char”长,因此需进行权衡考虑。
/*********************************************
* 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 chars.
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的全局变量数组。定义如下:
extern volatile unsigned char data[data_buf_length];
data_buf_length = 968
调用程序时,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。如果需要写oxff,可以采用单字节写指令。
/*************************************************
* 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像素。由于每个字节定义4个像素,因此定义每一个字符需要54字节。字符数据位于程序入口处的data[] (与上述写显示存储器的程序类似)。
写字符存储器时需要进行一些附加说明,存储器为非易失,因此,写存储器大约需要12ms,由max7456执行。只有完整的54字节字符才可以写入字符存储器。
该器件包含一个54字节映射存储器。首先把需要写入的字符数据写入映射存储器,然后器件将该数据装载到nvm字符存储器。
用来写字符存储器的寄存器有以下几种:
字符存储器模式 = 0x08。向寄存器写0xa0,使器件把映射存储器的内容装载到nvm字符存储器。
字符存储器地址高位 = 0x09。包括了即将写入字符的地址。
字符存储器地址低位 = 0x0a。
字符存储器数据输入 = 0x0b。
status = 0xa0,读取该寄存器以决定何时可以写入字符存储器。
在程序入口处,data[1]包括即将写入字符的地址,data[2...54]包括字符数据。
向nvm字符存储器写字符时,首先写字符地址。然后将每个字节写入映射存储器。写映射存储器时没有自动递增模式,所以每次写操作必须写入映射存储器地址。向字符存储器模式寄存器写0xa0,可以把映射存储器的内容装载到nvm字符存储器。然后器件将状态寄存器第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的头文件,以下代码决定了器件的寄存器映射。
/**********************************************
* 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
结论和性能
max7456评估板采用工作在20mhz时钟的maxq2000微控制器,该微控制器包含内部硬件spi控制器。因此,max7456的spi端口可以全速工作。上述软件spi程序工作速度低于硬件控制器。不过针对客户缺少硬件spi端口的工作环境,程序已优化至最简。
spi是motorola, inc.的商标。
粉色版iPhone13近六成被男性购买 不愧是猛男粉
凌特-48V热插拔控制器具有板载ADC和I2C接口
索尼VPL-HW48家用投影机上手 万元级的全高清画质神作
中国移动与四川大学华西第二医院打造出了5G+MEC智慧医疗行业专网
为什么比特币总是消息滞后于涨跌
SPI接口的工作原理
对人工智能2018年最大惊喜及2019年预测
夏普 PM2.5小型检测传感器 10秒内检测PM2.5浓度
虹科案例|市场上第一个快速无损用于晶圆质量控制的太赫兹系统
涡流的基本知识科普
华虹半导体助力大容量 MCU 解决方案,多方面缩小芯片面积
伺服电机的参数设置
完全集成的八通道高压发送/接收(T/R)开关—MAX4936/MAX4937
新能源汽车的持续火爆,拉动寒锐钴业净利润暴增22倍
为了对付诺基亚6,华为发布P8 lite新机,太针对了!
高速连拍 快人一步丨Lexar SILVER PRO V60高速存储卡新品上市
巴特沃斯滤波器频率设计及增益多项式方程
嵌入式ICE-RT逻辑和嵌入式跟踪宏核(ETMS)系列
分立式仪表放大器与集成仪表放大器的区别
贝塔射线扬尘监测仪工作原理