/*
Routines om de LCD-module te bedienen
Pros 2007
*/
#define LCD_DDR DDRC
#define LCD_PORT PORTC
#define LCD_RS PC4
#define LCD_E PC5
#define LCD_E_DDR DDRC
#define LCD_RS_PORT PORTC
#define LCD_E_PORT PORTC
// instruction register bit positions
#define LCD_CLR 0 // DB0: clear display
#define LCD_HOME 1 // DB1: return to home position
#define LCD_ENTRY_MODE 2 // DB2: set entry mode
#define LCD_ENTRY_INC 1 // DB1: 1=increment, 0=decrement
#define LCD_ENTRY_SHIFT 0 // DB2: 1=display shift on
#define LCD_ON 3 // DB3: turn lcd/cursor on
#define LCD_ON_DISPLAY 2 // DB2: turn display on
#define LCD_ON_CURSOR 1 // DB1: turn cursor on
#define LCD_ON_BLINK 0 // DB0: blinking cursor ?
#define LCD_MOVE 4 // DB4: move cursor/display
#define LCD_MOVE_DISP 3 // DB3: move display (0-> cursor) ?
#define LCD_MOVE_RIGHT 2 // DB2: move right (0-> left) ?
#define LCD_FUNCTION 5 // DB5: function set
#define LCD_FUNCTION_8BIT 4 // DB4: set 8BIT mode (0->4BIT mode)
#define LCD_FUNCTION_2LINES 3 // DB3: two lines (0->one line)
#define LCD_FUNCTION_10DOTS 2 // DB2: 5x10 font (0->5x7 font)
#define LCD_CGRAM 6 // DB6: set CG RAM address
#define LCD_DDRAM 7 // DB7: set DD RAM address
#define LCD_BUSY 7 // DB7: LCD is busy
// set entry mode: display shift on/off, dec/inc cursor move direction
#define LCD_ENTRY_DEC 0x04 // display shift off, dec cursor move dir
#define LCD_ENTRY_DEC_SHIFT 0x05 // display shift on, dec cursor move dir
#define LCD_ENTRY_INC_ 0x06 // display shift off, inc cursor move dir
#define LCD_ENTRY_INC_SHIFT 0x07 // display shift on, inc cursor move dir
// display on/off, cursor on/off, blinking char at cursor position
#define LCD_DISP_OFF 0x08 // display off
#define LCD_DISP_ON 0x0C // display on, cursor off
#define LCD_DISP_ON_BLINK 0x0D // display on, cursor off, blink char
#define LCD_DISP_ON_CURSOR 0x0E // display on, cursor on
#define LCD_DISP_ON_CURSOR_BLINK 0x0F // display on, cursor on, blink char
// move cursor/shift display
#define LCD_MOVE_CURSOR_LEFT 0x10 // move cursor left (decrement)
#define LCD_MOVE_CURSOR_RIGHT 0x14 // move cursor right (increment)
#define LCD_MOVE_DISP_LEFT 0x18 // shift display left
#define LCD_MOVE_DISP_RIGHT 0x1C // shift display right
// function set: set interface data length and number of display lines
#define LCD_FUNCTION_4BIT_2LINES 0x28 // 4-bit interface, dual line, 5x7 dots
#define LCD_FUNCTION_8BIT_1LINE 0x30 // 8-bit interface, single line, 5x7 dots
#define LCD_MODE_DEFAULT ((1<<LCD_ENTRY_MODE) | (1<<LCD_ENTRY_INC))
// normally you do not change the following
#define LCD_LINES 2 // visible lines
#define LCD_LINE_LENGTH 0x40 // internal line length
#define LCD_START_LINE1 0x00 // DDRAM address of first char of line 1
#define LCD_START_LINE2 0x40 // DDRAM address of first char of line 2
#define LCD_FUNCTION_DEFAULT LCD_FUNCTION_4BIT_2LINES
#define lcd_puts_P(__s) lcd_puts_p(P(__s))
#define lcd_cmd_mode() cbi(LCD_RS_PORT, LCD_RS) // RS=0 command mode
#define lcd_data_mode() sbi(LCD_RS_PORT, LCD_RS) // RS=1 data mode
#define lcd_command(x) lcd_write(x, 0) // Geef LCD-commando
#define lcd_putc(x) lcd_write(x, 1) // Stuur karakter naar LCD
static void lcd_out_low(unsigned char d)
{ // output low nibble
sbi(LCD_E_PORT, LCD_E); // E hoog
LCD_PORT &= 0xF0; // Bit0 ... Bit3 = 0
d &= 0x0F;
LCD_PORT |= d;
delay(4);
cbi(LCD_E_PORT, LCD_E); // E laag
}
static void lcd_out_high(unsigned char d)
{ // output high nibble
sbi(LCD_E_PORT, LCD_E); // E hoog
LCD_PORT &= 0xF0; // Bit0 ... Bit3 = 0
d = d >> 4; // D7 ... D4 -> D3 ... D0
d &= 0x0F;
LCD_PORT |= d;
delay(4);
cbi(LCD_E_PORT, LCD_E); // E laag
}
static void lcd_write(unsigned char data, unsigned char rs)
{
if (rs == TXT) {
if (data == 0xB5) { // µ?
data = 0xE4; // Daar gebruikt de LCD-module een ander teken voor
}
lcd_data_mode(); // RS=1: write data
} else {
lcd_cmd_mode(); // RS=0: write instruction
}
lcd_out_high(data); // output high nibble first
delay(4);
lcd_out_low(data); // output low nibble
_delay_us(40);
}
void lcd_clrscr(void)
{
lcd_command(1 << LCD_CLR); // clear lcd and set cursor to home position
delay_ms(2);
}
void lcd_home(void)
{
lcd_command(1 << LCD_HOME); // set cursor to home position
delay_ms(2);
}
void lcd_puts(const char *s)
{
while (*s) { // print string on lcd
lcd_putc(*s);
s++;
}
}
void lcd_puts_p(const prog_char * progmem_s)
{
register char c;
while ((c = PRG_RDB(progmem_s++))) { // print string from program memory on lcd
lcd_putc(c);
}
}
// Ga naar regel y (1 ... 4), karakter x (1 ... 20)
void lcd_gotoyx(unsigned char y, unsigned char x)
{
x--;
x += linestart[y];
lcd_command((1 << LCD_DDRAM) + x); // goto position (x,y)
}
void lcd_clearline(unsigned char line)
{
lcd_command((1 << LCD_DDRAM) + linestart[line]);
for (line = 0; line < 20; line++) {
lcd_putc(' ');
}
}
// Print een string op regel y, plaats x
void lcd_puts_at(unsigned char y, unsigned char x, const char *s)
{
x--;
x += linestart[y];
lcd_command((1 << LCD_DDRAM) + x); // goto position (x,y)
while (*s) { // print string on lcd
lcd_putc(*s);
s++;
}
}
// initialize display and select type of cursor
// dispAttr: LCD_DISP_OFF, LCD_DISP_ON, LCD_DISP_ON_CURSOR, LCD_DISP_CURSOR_BLINK
void lcd_init(unsigned char dispAttr)
{
LCD_DDR |= 0x3F; // PB7 ... PB4 = lcd_data
sbi(LCD_E_DDR, LCD_E);
sbi(LCD_E_DDR, LCD_RS);
sbi(LCD_E_PORT, LCD_E); // E hoog
lcd_cmd_mode();
delay_ms(24); // wait 16ms or more after power-on
lcd_out_high(LCD_FUNCTION_8BIT_1LINE); // initial write to lcd is 8bit
delay_ms(6); // delay, busy flag can't be checked here
lcd_out_high(LCD_FUNCTION_8BIT_1LINE);
_delay_us(110); // delay, busy flag can't be checked here
lcd_out_high(LCD_FUNCTION_8BIT_1LINE);
_delay_us(64); // delay, busy flag can't be checked here
lcd_out_high(LCD_FUNCTION_4BIT_2LINES); // set IO mode to 4bit
// from now the lcd only accepts 4 bit I/O, we can use lcd_command()
lcd_command(LCD_FUNCTION_DEFAULT); // function set: display lines
lcd_command(LCD_DISP_OFF); // display off
lcd_clrscr(); // display clear
lcd_command(LCD_MODE_DEFAULT); // set entry mode
lcd_command(dispAttr); // display/cursor control
}
void display_dat_item(unsigned char item)
{
unsigned char tientallen, eenheden;
tientallen = item / 10;
eenheden = item - (tientallen * 10);
lcd_write(tientallen + '0', TXT); // Tientallen op scherm plaatsen
lcd_write(eenheden + '0', TXT); // Eenheden op scherm plaatsen
}
// Verstuur een unsigned int als ascii-string naar de LCD-module
// dp bepaalt hoeveel decimale tekens er na de dp moeten komen (0 = geen dp)
void dec2LCD(unsigned int getal, char dp)
{
unsigned int tmp, deeltal = 10000;
char cnt, zeroflag = 0;
for (cnt = 5; cnt > 0; cnt--) {
if (dp == cnt) { // dp plaatsen?
if (zeroflag == 0) { // reeds cijfers verzonden?
lcd_write('0', TXT); // nee, dan eerst een 0
}
lcd_write('.', TXT);
zeroflag = 1; // na dp alle nullen afdrukken
}
tmp = 0;
while (deeltal > getal) {
getal -= deeltal;
tmp++;
}
if (tmp > 0) { // als dat geen 0 is
zeroflag = 1; // worden alle volgende decaden afgedrukt
lcd_write(tmp + '0', TXT); // ascii-teken verzenden
} else if (zeroflag == 1) { // Is dit de eerste 0?
lcd_write('0', TXT); // nee; '0' verzenden
}
deeltal /= 10; // deeltal aanpassen voor volgende decade
}
}
// Plaats een unsigned long op het LCD
void UL2LCD(unsigned long getal)
{
unsigned long deeltal = 1000000000UL;
char tmp, cnt, zeroflag = 0;
for (cnt = 10; cnt > 1; cnt--) {
tmp = 0;
while (getal >= deeltal) {
tmp++;
getal -= deeltal; // decade berekenen
}
if (tmp > 0) { // als dat geen 0 is
zeroflag = 1; // worden alle volgende decaden afgedrukt
lcd_putc(tmp + '0'); // ascii-teken verzenden
} else if (zeroflag == 1) { // Is dit de eerste 0?
lcd_putc('0'); // nee; '0' verzenden
}
deeltal /= 10UL; // deeltal aanpassen voor volgende decade
}
lcd_putc((unsigned char) getal + '0');
}
// Verstuur de cijfers na de komma (duizendsten) op het LCD
void DS2LCD(unsigned long getal)
{
char tmp;
if (getal > 999) {
return;
}
tmp = 0;
while (getal >= 100) {
tmp++;
getal -= 100; // decade berekenen
}
lcd_putc(tmp + '0'); // ascii-teken verzenden
tmp = 0;
while (getal >= 10) {
tmp++;
getal -= 10; // decade berekenen
}
lcd_putc(tmp + '0'); // ascii-teken verzenden
lcd_putc((unsigned char) getal + '0');
}
void float2LCD(double getal)
{
unsigned long eenheden;
unsigned long duizendsten;
eenheden = (unsigned long) getal;
getal -= (double) eenheden;
duizendsten = (unsigned long) (getal *= 1000.0);
UL2LCD(eenheden);
lcd_putc('.');
DS2LCD((unsigned long) duizendsten);
}
// Toon alle gegevens op het display
// F: 12345678.90Hz
// P: 50%
// DDS: 15000000Hz
// Mode: frequency
// Mode: duty-cycle
void display_ALL(void)
{
lcd_clrscr();
lcd_puts_P("Fr: ");
float2LCD(OutputFrequency);
lcd_puts_P("Hz");
lcd_puts_at(2, 1, "Pb: ");
float2LCD(pulsbreedte);
lcd_putc('%');
switch (KB_mode) {
case FREQ:
lcd_puts_at(3, 1, "Frequentie:");
break;
case PULS:
lcd_puts_at(3, 1, "Pulsbreedte:");
break;
case SW_START:
lcd_puts_at(3, 1, "Sweep-frequentie 1:");
break;
case SW_END:
lcd_puts_at(3, 1, "Sweep-frequentie 2:");
break;
case SW_DELAY:
lcd_puts_at(3, 1, "Sweep-delay in µS:");
break;
case SW_STEP:
lcd_puts_at(3, 1, "Sweep-step in Hz:");
break;
case SWEEP:
lcd_puts_at(3, 1, "Start sweep now?");
break;
default:
break;
}
lcd_puts_at(4, 1, " ");
}