前々回でAVRをLCDにつなぎ、前回はAVRをシリアルポートにつないでみたので、今回はその2つをまとめてSerial2LCDモジュールを作ってみます。

プログラム
まず、Eclipseに新しいプロジェクトを作り、そこに、LCDライブラリから「lcd.h」「lcd.c」を、UARTライブラリから「uart.h」「uart.c」をコピーしてきます。そして、前回、前々回と同様の設定の変更をします。(今回もクロックは8Mhzで使います。)

一部、前と違うのは、、、
#define LCD_WRAP_LINES 1

を設定して、行末まで来たら改行するようにしています。

そして、新しいソースコード「main.c」を新規作成して、(前回、前々回の)サンプルコードを参考に、シリアルから読んだデータをLCDに表示するプログラムを作りました。
(ファイルmain.c)

/*
* main.c
*
* Created on: 2009/05/05
* Author: ado
*/

#include
#include
#include
#include

#include "lcd.h"
#include "uart.h"

/* 9600 baud */
#define UART_BAUD_RATE 9600


int main(void)
{
unsigned int c;
char *statup_str = PSTR("LCD online.
");

/* initialize display, cursor off */
lcd_init(LCD_DISP_ON_CURSOR_BLINK);
uart_init( UART_BAUD_SELECT(UART_BAUD_RATE,F_CPU) );

lcd_puts_p(statup_str);
uart_puts_p(statup_str);

sei();

for(;;)
{
/*
* Get received character from ringbuffer
* uart_getc() returns in the lower byte the received character and
* in the higher byte (bitmask) the last receive error
* UART_NO_DATA is returned when no data is available.
*
*/
c = uart_getc();
if ( c & UART_NO_DATA )
{
/*
* no data available from UART
*/
}
else
{
/*
* new data available from UART
* check for Frame or Overrun error
*/
if ( c & UART_FRAME_ERROR )
{
/* Framing Error detected, i.e no stop bit detected */
uart_puts_P("UART Frame Error: ");
}
if ( c & UART_OVERRUN_ERROR )
{
/*
* Overrun, a character already present in the UART UDR register was
* not read by the interrupt handler before the next character arrived,
* one or more received characters have been dropped
*/
uart_puts_P("UART Overrun Error: ");
}
if ( c & UART_BUFFER_OVERFLOW )
{
/*
* We are not reading the receive buffer fast enough,
* one or more received character have been dropped
*/
uart_puts_P("Buffer overflow error: ");
}
/*
* send received character back
*/
lcd_putc( (unsigned char)c );
}
}
}

(中盤はほとんど、サンプルのコード、そのままだ、、、)

LCDライブラリの改良
上のプログラムでも動きますが、LCDライブラリにちょっと手を加えてみました。改良したのは、、、
1)改行コードはCRのみの対応だったが、CR、LF、CRLFに対応した。
2)2行目(最終行)の次の行に行くときは、単に1行めから始めていたが、スクロール(2行目を1行目にコピー)して2行目をクリアするようにした。

(diffのみ)

--- lcd.c 2009-05-06 18:54:01.000000000 +0900
+++ new-lcd.c 2009-05-06 18:55:02.000000000 +0900
@@ -300,6 +300,36 @@


/*************************************************************************
+copy one line. (from_addr to to_addr, LCD_DISP_LENGTH bytes)
+*************************************************************************/
+static void lcd_scroll_line(uint8_t from_addr, uint8_t to_addr)
+{
+ register uint8_t i;
+ register char c;
+
+ for (i=0; i < LCD_DISP_LENGTH; i++) {
+ lcd_command(_BV(LCD_DDRAM) +from_addr +i);
+ lcd_waitbusy();
+ c = lcd_read(1);
+ lcd_command(_BV(LCD_DDRAM) +to_addr +i);
+ lcd_data(c);
+ }
+}/* lcd_scroll_line */
+
+/*************************************************************************
+clear one line (from line_addr, LCD_DISP_LENGTH bytes)
+*************************************************************************/
+static void lcd_clear_line(uint8_t line_addr)
+{
+ register uint8_t i;
+
+ lcd_command((1< + for (i=0; i < LCD_DISP_LENGTH; i++) {
+ lcd_putc(' ');
+ }
+}/* lcd_clear_lastline */
+
+/*************************************************************************
Move cursor to the start of next line or to the first line if the cursor
is already on the last line.
*************************************************************************/
@@ -309,13 +339,17 @@


#if LCD_LINES==1
+ lcd_clear_line(LCD_START_LINE1);
addressCounter = 0;
#endif
#if LCD_LINES==2
if ( pos < (LCD_START_LINE2) )
addressCounter = LCD_START_LINE2;
- else
- addressCounter = LCD_START_LINE1;
+ else {
+ lcd_scroll_line(LCD_START_LINE2, LCD_START_LINE1);
+ lcd_clear_line(LCD_START_LINE2);
+ addressCounter = LCD_START_LINE2;
+ }
#endif
#if LCD_LINES==4
#if KS0073_4LINES_MODE
@@ -325,8 +359,13 @@
addressCounter = LCD_START_LINE3;
else if ( (pos >= LCD_START_LINE3) && (pos < LCD_START_LINE4) )
addressCounter = LCD_START_LINE4;
- else
- addressCounter = LCD_START_LINE1;
+ else {
+ lcd_scroll_line(LCD_START_LINE2, LCD_START_LINE1);
+ lcd_scroll_line(LCD_START_LINE3, LCD_START_LINE2);
+ lcd_scroll_line(LCD_START_LINE4, LCD_START_LINE3);
+ lcd_clear_line(LCD_START_LINE4);
+ addressCounter = LCD_START_LINE4;
+ }
#else
if ( pos < LCD_START_LINE3 )
addressCounter = LCD_START_LINE2;
@@ -334,8 +373,13 @@
addressCounter = LCD_START_LINE3;
else if ( (pos >= LCD_START_LINE3) && (pos < LCD_START_LINE2) )
addressCounter = LCD_START_LINE4;
- else
- addressCounter = LCD_START_LINE1;
+ else {
+ lcd_scroll_line(LCD_START_LINE2, LCD_START_LINE1);
+ lcd_scroll_line(LCD_START_LINE3, LCD_START_LINE2);
+ lcd_scroll_line(LCD_START_LINE4, LCD_START_LINE3);
+ lcd_clear_line(LCD_START_LINE4);
+ addressCounter = LCD_START_LINE4;
+ }
#endif
#endif
lcd_command((1< @@ -437,15 +481,20 @@
void lcd_putc(char c)
{
uint8_t pos;
-
+ static uint8_t is_cr_seen = 0;

pos = lcd_waitbusy(); // read busy-flag and address counter
- if (c=='
')
+ if (c=='
')
{
lcd_newline(pos);
- }
- else
+ is_cr_seen = 1;
+ } else if (c=='
') {
+ if (!is_cr_seen)
+ lcd_newline(pos);
+ is_cr_seen = 0;
+ } else
{
+ is_cr_seen = 0;
#if LCD_WRAP_LINES==1
#if LCD_LINES==1
if ( pos == LCD_START_LINE1+LCD_DISP_LENGTH ) {


回路 + ついでに基盤も作る
回路は、前回、前々回の2つを合わせた物を作ります。(LCD接続+RX/TX)

で、ブレッドボードに組み上げて、このプログラムでテストして動くのを確認した後、(これはそれなりに汎用的に使えそうなので)ユニバーサル基盤に組み上げてみました。

LCD


できたー!