252 lines
4.7 KiB
C
252 lines
4.7 KiB
C
/*
|
|
* "THE BEER-WARE LICENSE" (Revision 42):
|
|
* Martin Wenger <martin.wenger@arcormail.de> and Stefan Rupp <struppi@struppi.name>
|
|
* wrote this file. As long as you retain this notice you can do whatever you want
|
|
* with this stuff. If we meet some day, and you think this stuff is worth it,
|
|
* you can buy me/us a beer in return.
|
|
* (c) 2005-2010 Martin Wenger, Stefan Rupp
|
|
* (c) 2014 Stefan Rupp
|
|
*/
|
|
|
|
#include <avr/io.h>
|
|
#include <inttypes.h>
|
|
#include <string.h>
|
|
#include <compat/ina90.h> // ==> _NOP()
|
|
|
|
#include "timer.h"
|
|
#include "lcd.h"
|
|
|
|
#define LCD_DATA_PIN (PINA)
|
|
#define LCD_DATA_PORT (PORTA)
|
|
#define LCD_DATA_DIR (DDRA)
|
|
#define LCD_DATA_MASK (0x0f)
|
|
|
|
|
|
|
|
#if (F_CPU <= 4000000UL)
|
|
#define WAIT_250_ns() _NOP()
|
|
#elif (F_CPU <= 8000000UL)
|
|
#define WAIT_250_ns() _NOP();_NOP()
|
|
#elif (F_CPU <= 12000000UL)
|
|
#define WAIT_250_ns() _NOP();_NOP();_NOP()
|
|
#elif (F_CPU <= 16000000UL)
|
|
#define WAIT_250_ns() _NOP();_NOP();_NOP();_NOP()
|
|
#elif (F_CPU <= 20000000UL)
|
|
#define WAIT_250_ns() _NOP();_NOP();_NOP();_NOP();_NOP()
|
|
#else
|
|
#include <util/delay.h>
|
|
#define WAIT_250_ns() _delay_us(1)
|
|
#endif
|
|
|
|
#define LCD_DATA_MODE() (PORTA |= (1<<PA4))
|
|
#define LCD_CMD_MODE() (PORTA &= ~(1<<PA4))
|
|
|
|
#define LCD_SET_ENABLE() (PORTA |= (1<<PA6))
|
|
#define LCD_RESET_ENABLE() (PORTA &= ~(1<<PA6))
|
|
|
|
// !!!!!!!
|
|
#define LCD_SET_READ() LCD_DATA_DIR &= ~(LCD_DATA_MASK);LCD_DATA_PORT &= ~(LCD_DATA_MASK);PORTA |= (1<<PA5)
|
|
#define LCD_SET_WRITE() PORTA &= ~(1<<PA5);LCD_DATA_DIR |= LCD_DATA_MASK
|
|
// !!!!!!!
|
|
|
|
|
|
#define T_TIMEOUT (10)
|
|
|
|
static uint8_t lcd_initialized;
|
|
|
|
|
|
static inline uint8_t lcd_read_byte(uint8_t RS)
|
|
{
|
|
uint8_t byte;
|
|
|
|
if (RS)
|
|
LCD_DATA_MODE();
|
|
else
|
|
LCD_CMD_MODE();
|
|
|
|
LCD_SET_READ();
|
|
|
|
LCD_SET_ENABLE();
|
|
WAIT_250_ns();
|
|
byte = (LCD_DATA_PIN<<4);
|
|
LCD_RESET_ENABLE();
|
|
|
|
WAIT_250_ns();
|
|
WAIT_250_ns();
|
|
|
|
LCD_SET_ENABLE();
|
|
WAIT_250_ns();
|
|
byte |= LCD_DATA_PIN & 0x0f;
|
|
LCD_RESET_ENABLE();
|
|
|
|
return byte;
|
|
}
|
|
|
|
|
|
static inline int8_t lcd_wait_while_busy(void)
|
|
{
|
|
|
|
uint8_t byte;
|
|
uint32_t t_enter = timer_get();
|
|
|
|
do {
|
|
if (timer_get() > t_enter + T_TIMEOUT) {
|
|
return 0;
|
|
}
|
|
byte = lcd_read_byte(0);
|
|
} while (byte & 0x80);
|
|
return 1;
|
|
}
|
|
|
|
|
|
static inline void send_nibble(uint8_t nibble)
|
|
{
|
|
LCD_SET_WRITE();
|
|
LCD_DATA_PORT = (LCD_DATA_PORT & 0xf0) | (0x0f & nibble);
|
|
LCD_SET_ENABLE();
|
|
WAIT_250_ns();
|
|
LCD_RESET_ENABLE();
|
|
}
|
|
|
|
|
|
/**
|
|
* Send one byte to the LCD
|
|
* \param byte The byte to send to the LCD
|
|
* \param RS specifies whether byte sent is data (RS==1) or command (RS==0)
|
|
*/
|
|
static inline int8_t lcd_send_byte(uint8_t byte, uint8_t RS)
|
|
{
|
|
if (lcd_wait_while_busy() ) {
|
|
if (RS)
|
|
LCD_DATA_MODE();
|
|
else
|
|
LCD_CMD_MODE();
|
|
|
|
send_nibble(byte >> 4);
|
|
_NOP();
|
|
send_nibble(0x0f & byte);
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
int lcd_get(void)
|
|
{
|
|
if ( lcd_wait_while_busy() ) {
|
|
return lcd_read_byte(1);
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
int lcd_put(char c)
|
|
{
|
|
if (!lcd_initialized) {
|
|
return 0;
|
|
}
|
|
return lcd_send_byte(c, 1);
|
|
}
|
|
|
|
void lcd_puts(char *s)
|
|
{
|
|
if (!lcd_initialized) {
|
|
return;
|
|
}
|
|
|
|
while (*s) {
|
|
//lcd_put(*s++);
|
|
if (!lcd_send_byte(*s++, 1)) {
|
|
return;
|
|
}
|
|
}
|
|
return;
|
|
}
|
|
|
|
void lcd_cursor(uint8_t blink)
|
|
{
|
|
if (blink) {
|
|
// enable display with cursor + blinking
|
|
lcd_send_byte(0x0f, 0);
|
|
}
|
|
else {
|
|
// enable display, no cursor, no blinking
|
|
lcd_send_byte(0x0c, 0);
|
|
return;
|
|
}
|
|
}
|
|
|
|
void lcd_init(void)
|
|
{
|
|
/* Initialize both ATmega and Display
|
|
* After return from this function,
|
|
* display we be set to 4-bit data mode
|
|
*/
|
|
|
|
lcd_initialized = 0;
|
|
|
|
// wait until LCD gets ready to accept instructions.
|
|
timer_wait(50);
|
|
|
|
// set CONTROL Pins as outputs:
|
|
DDRA |= (0x70);
|
|
|
|
LCD_CMD_MODE();
|
|
LCD_SET_WRITE();
|
|
|
|
// set LCD to 8-bit mode
|
|
send_nibble(0x03);
|
|
|
|
timer_wait(5);
|
|
send_nibble(0x03);
|
|
|
|
timer_wait(5);
|
|
send_nibble(0x03);
|
|
|
|
// LCD definitely is in 8-bit mode now
|
|
|
|
// set module to 4-bit mode now
|
|
// Remember! As we're in 8-bit mode now,
|
|
// send_nibble is the right thing, not send_byte!
|
|
timer_wait(5);
|
|
send_nibble(0x02);
|
|
timer_wait(5);
|
|
|
|
// set to 5 x 8 dots per character,
|
|
// 16 characters per line, 2 lines
|
|
int success = lcd_send_byte(0x28, 0);
|
|
|
|
if ( !success ) {
|
|
return;
|
|
}
|
|
|
|
// enable display, no cursor, no blinking
|
|
//lcd_send_byte(0x0c, 0);
|
|
// enable display with cursor + blinking
|
|
//lcd_send_byte(0x0f, 0);
|
|
lcd_cursor(1);
|
|
|
|
// Set display to
|
|
// + Move cursor (and do not shift display)
|
|
// + Move right
|
|
lcd_send_byte(0x04, 0);
|
|
|
|
// clear dislay
|
|
lcd_clear();
|
|
|
|
lcd_initialized = 1;
|
|
|
|
return;
|
|
}
|
|
|
|
void lcd_clear(void)
|
|
{
|
|
lcd_send_byte(0x01, 0);
|
|
}
|
|
|
|
void lcd_locate(uint8_t row, uint8_t col)
|
|
{
|
|
if (row)
|
|
col += 0x40;
|
|
lcd_send_byte(0x80 + col, 0);
|
|
}
|