📄 display.c
字号:
//++
//display.c - MP3 LCD/VFD Display functions
//
// Copyright (C) 2005 by Spare Time Gizmos. All rights reserved.
//
// This file is part of the Spare Time Gizmos' MP3 Player firmware.
//
// This firmware is free software; you can redistribute it and/or modify it
// under the terms of the GNU General Public License as published by the Free
// Software Foundation; either version 2 of the License, or (at your option)
// any later version.
//
// This program is distributed in the hope that it will be useful, but WITHOUT
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
// more details.
//
// You should have received a copy of the GNU General Public License along with
// this program; if not, write to the Free Software Foundation, Inc., 59 Temple
// Place, Suite 330, Boston, MA 02111-1307 USA.
//
//DESCRIPTION:
// This module provides a simple interface to a generic display module, which
// may be either a Hitachi 44780 style generic text LCD, or a parallel ASCII
// vacuum flourescent display. These displays maintain a cursor and current
// cursor location, but in general they (especially the LCDs) do not implement
// any terminal emulations, not even basic things like carriage return, line
// feed, tab, etc. Thus you may call DisplayChar() several times in a row and
// assume that the characters are displayed right to left, but if you want to
// move to the next line you need to use one of the cursor position functions
// to move the cursor there. A line feed or CRLF won't do it.
//
// Needless to say, the LCD doesn't implement scrolling either. Your only
// option is to erase the entire display and repaint. Also note that this
// code does not enforce any display size limits, so it's up to the application
// code not to exceed the display width and height. The DISPLAY_WIDTH and
// DISPLAY_HEIGHT pre-processor constants are just the thing you need for this.
//
//REVISION HISTORY:
// dd-mmm-yy who description
// 18-May-05 RLA New file.
// 13-Jul-05 RLA Add a small delay to WaitBusy() for VFDs...
// 20-Jul-05 RLA Add the copyright sign to the LCD's CGRAM ...
// 12-Oct-05 RLA For VFDs, home cursor THEN clear screen
// 15-Oct-05 RLA Remove DISPLAY_TYPE and add LCD/VFD conditionals
// 20-Oct-05 RLA Fix a timing bug in the VFD DisplayWrite()
//
//--
// Include files...
#include <stdio.h> // needed so DBGOUT(()) can find printf!
#include <stdarg.h> // va_alist(), va_end(), etc...
#include "standard.h" // standard types - BYTE, WORD, BOOL, etc
#include "reg66x.h" // declarations for Philips 89C66x processors
#include "player.h" // project wide (hardware configuration) declarations
#include "debug.h" // debugging stuff and ResetWDT()
#include "post.h" // POST codes for Fail()
#include "timer.h" // DELAYUS() macro and DelayMS() function
#include "display.h" // declarations for this module
// This array of eight bytes defines a custom character (a copyright symbol)
// for the Hitachi 44780 LCD controller. The VFD character set already contains
// a copyright sign, so this is unnecessary there...
#ifdef LCD
PRIVATE CODE const BYTE m_abCopyrightSymbol[8] = {
0x0E, 0x11, 0x17, 0x19, 0x17, 0x11, 0x0E, 0x00
};
#endif
// Local storage...
PRIVATE XDATA char m_acDisplayBuffer[DISPLAY_WIDTH+1];
//++
// This routine will write one byte to an LCD display. For an LCD display,
// bRS selects the control register (bRS=1) or the data register (bRS=0).
// bData gives the actual 8 bit data to be sent to the display. Note that
// this routine does NOT check the busy flag before sending the data
// - that's the responsibility of the caller!
//
// VFD displays are similar, however in this case there is only a data port
// and no register select flag is used. Also, VFD displays use the opposite
// polarity for the ENABLE signal (active low for VFDs rather than active high
// for LCDs)...
//--
#ifdef LCD
PRIVATE void DisplayWrite (BYTE bRS, BYTE bData)
{
DISPLAY_RW = 0; DISPLAY_RS = (bRS != 0); DISPLAY_DATA = bData;
DISPLAY_ENABLE = 1; DELAYUS(2); DISPLAY_ENABLE = 0;
}
#endif
#ifdef VFD
PRIVATE void DisplayWrite (BYTE bData)
{
INT_OFF;
DISPLAY_RW = 1; DISPLAY_ENABLE = 0; DISPLAY_DATA = bData;
DISPLAY_RW = 0; DISPLAY_RW = 1;
DISPLAY_ENABLE = 1; DISPLAY_RW = 0;
INT_ON;
//DISPLAY_RW = 0; DISPLAY_DATA = bData;
//DISPLAY_ENABLE = 0; DELAYUS(2); DISPLAY_ENABLE = 1;
}
#endif
//++
// This routine will monitor the display's BUSY flag and (contrary to
// what the name might lead you to believe :-) it will wait for the display
// to NOT be busy.
//
// LCD displays implement the BUSY flag as the MSB of the status register,
// which must be explicitly read by setting the RW bit, clearing the register
// select, and then enabling the display.
//
// VFD displays have a completely separate BUSY flag which is connected to
// it's own port pin. We can simply test this pin without any other effort.
//
// Note that both versions implement a timeout which will eventually POST
// if the BUSY flag never clears...
//--
PRIVATE void WaitBusy (void)
{
WORD wTimeout = 0;
#ifdef LCD
// Set the RW bit and clear RS to read the status register, then set the data
// port to all 1s so that it can be used as an input.
DISPLAY_RW = 1; DISPLAY_RS = 0; DISPLAY_DATA = 0xFF; DISPLAY_ENABLE = 1;
while (TRUE) {
DELAYUS(1);
if ((DISPLAY_DATA & 0x80) == 0) break;
if (++wTimeout == 0) POST(PC_DISPLAY, FALSE);
}
// Disable the display and return...
DISPLAY_ENABLE = 0;
#endif
#ifdef VFD
// VFDs have an explicit status line that makes things much easier...
while (TRUE) {
// I had a lot of trouble with glitches on the DISPLAY_BUSY signal,
// especially with a faster 89C66x clock (the faster CPU clock doesn't
// cause the glitches - it just makes it easier for the software to find
// them!) The only fix is to sample DISPLAY_BUSY and, if we find it
// cleared, wait a few microseconds and then sample again to be sure we
// get the same result...
if (!DISPLAY_BUSY) {
DELAYUS(2); if (!DISPLAY_BUSY) return;
}
if (++wTimeout == 0) POST(PC_DISPLAY, FALSE);
}
#endif
}
//++
// This routine will intitialize the display and clear the screen. Normally
// it's called only once, during startup, but it could be called again if
// necessary. Note that it contains several programmed delays to wait for the
// LCD to initialize, so it isn't especially fast!
//
// Although we never explicitly read back from the display, we do explicitly
// check the display's BUSY flag for both LCD and VFD displays. If no display
// is connected (or if it's broken somehow) then we will eventually time out and
// POST() via the WaitBusy() routine...
//--
PUBLIC void InitializeDisplay (void)
{
#ifdef LCD
BYTE i;
// LCD displays are notorious for being difficult to initialize, particularly
// when the power supply startup is noisy or slow and glitches the display's
// microprocessor. The solution is to follow the data book initialization
// procedure explicity, which is exactly what we do here (in this case it's the
// Optrex data book we're following)...
//
// Also note that LCDs have no hardware reset, so the DISPLAY_RESET signal
// does nothing. And finally, for LCD dispays the ENABLE signal is active
// high, and so we initialize it to low...
DISPLAY_ENABLE = 0; DelayMS(15);
// At this point it's not safe to depend on the display's BUSY flag, and
// we must make do with programmed delays instead...
DisplayWrite(CONTROL_REGISTER, LCD_FUNCTION_SET|LCD_8BIT); DelayMS(5);
DisplayWrite(CONTROL_REGISTER, LCD_FUNCTION_SET|LCD_8BIT); DELAYUS(160);
// Now we can start using the BUSY flag...
DisplayWrite(CONTROL_REGISTER, LCD_FUNCTION_SET|LCD_8BIT); WaitBusy();
#if (DISPLAY_HEIGHT == 1)
DisplayWrite(CONTROL_REGISTER, LCD_FUNCTION_SET|LCD_8BIT); WaitBusy();
#else
DisplayWrite(CONTROL_REGISTER, LCD_FUNCTION_SET|LCD_8BIT|LCD_TWO_LINES); WaitBusy();
#endif
DisplayWrite(CONTROL_REGISTER, LCD_DISPLAY_CONTROL|LCD_DISPLAY_OFF); WaitBusy();
DisplayWrite(CONTROL_REGISTER, LCD_CLEAR_DISPLAY); WaitBusy();
DisplayWrite(CONTROL_REGISTER, LCD_CURSOR_MODE_SET); WaitBusy();
DisplayWrite(CONTROL_REGISTER, LCD_ENTRY_MODE_SET|LCD_INCREMENT_CURSOR); WaitBusy();
DisplayWrite(CONTROL_REGISTER, LCD_DISPLAY_CONTROL|LCD_DISPLAY_ON); WaitBusy();
// Load the character generate RAM on the 44780...
DisplayWrite(CONTROL_REGISTER, LCD_LOAD_CGRAM); WaitBusy();
for (i = 0; i < sizeof(m_abCopyrightSymbol); ++i) {
// Note that the actual character generator data is written to the LCD's
// _data_ port, not the control port!
DisplayWrite(DATA_REGISTER, m_abCopyrightSymbol[i]); WaitBusy();
}
#endif
#ifdef VFD
// VFD displays have a hardware RESET pin, and also have no especially nasty
// startup conditions, so our life is much easier. One gotcha, however, is that
// VFD displays use an active low ENABLE signal, so we initialize it to HIGH.
DISPLAY_ENABLE = 1;
#ifdef PCBV1
DISPLAY_RESET = 0; DelayMS(20); DISPLAY_RESET = 1;
#endif
DelayMS(20);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -