package Device::VFD::GP1022; use strict; use warnings; our $VERSION = '0.01'; our $SERIAL; use Carp; if ($^O =~ /win/i && $^O !~ /darwin/) { $SERIAL = 'Win32::SerialPort'; } else { $SERIAL = 'Device::SerialPort'; } eval "require $SERIAL"; $@ and carp "$SERIAL is not installed"; use Device::VFD::GP1022::Message; sub new { my $class = shift; my %opts = (@_ == 1) ? ( port => $_[0] ) : @_; $opts{port} or carp 'port option dose not exist'; $opts{size} ||= '264x24'; my $self = bless { opts => \%opts, }, $class; eval { $self->{serial} = ref($opts{serial}) ? $opts{serial} : $class->setup_serial(%opts); }; $@ and carp 'serial port init error'; $self->{encode} = ref($opts{encode}) ? $opts{encode} : Device::VFD::GP1022::Encode->new; $self->full; $self; } sub encode { shift->{encode} } sub serial { shift->{serial} } sub setup_serial { my($class, %opts) = @_; my $config = $opts{port_config} || {}; my $serial = $SERIAL->new($opts{port}) or die; $serial->user_msg(1) or die; $serial->error_msg(1) or die; $serial->parity('none') or die; $serial->databits(8) or die; $serial->stopbits(1) or die; $serial->baudrate($config->{baudrate} || 19200) or die; $serial->handshake('none') or die; $serial->read_const_time(1000) or die; $serial->read_char_time(10) or die; $serial->write_settings or die; $serial; } my $END_DATA = pack 'C', 0xEF; sub send_message { my($self, $message) = @_; my $send = $message . $self->make_checksum($message) . $END_DATA; $self->{serial}->write( $send ); my($size, $recv) = $self->{serial}->read(7); if ($size) { my @recvs = unpack 'C*', $recv; return $recvs[2]; } return 0; } sub make_checksum { my($self, $message) = @_; my $sum = 0; for my $data (unpack 'C*', $message) { $sum += $data; } pack('CC', int($sum / 256), ($sum % 256)); } my $EMPTY_DATA = pack 'C', 0xFF; sub message { my($self, $mode, $str) = @_; unless (defined $str) { $str = $mode; $mode = 'force'; } carp "'$mode' is can't message command" unless $mode =~ /^(?:append|force|buffer)$/; my $objs; if (!ref($str)) { $objs = vfd_encode { STR $str }; } else { $objs = $str; } my $message = ''; for my $obj (@{ $objs }) { if ($obj->is_raw) { $message .= $obj; } else { $message .= $self->{encode}->encode($obj); } } use bytes; my @frames; if (bytes::length($message) > 256) { while (my $tmp = substr $message, 0, 256, '') { push @frames, $tmp; } } else { push @frames, $message; } my $ret = $self->$mode(scalar(@frames)) or return 0; croak 'send data error' if $ret eq 0xC3; croak 'time out error' if $ret eq 0xC3; my $i = 1; for my $msg (@frames) { $msg .= $EMPTY_DATA x (256 - bytes::length($msg)); $ret = $self->send_message( pack('C*', 0, $i++, 0) . $msg ); croak 'send data error' if $ret eq 0xC3; croak 'time out error' if $ret eq 0xC3; } $ret; } my %COMMANDS = ( append => 0x03, force => 0x04, buffer => 0x05, display => [ 0x10, 0x11 ], scroll => [ 0x12, 0x13 ], clear => 0x14, default => 0x15, switch => 0x17, rewind => 0x18, full => 0x29, reverse => [ 0x31, 0x30 ], ); sub command { my($self, $code, $frame_num) = @_; $frame_num ||= 0; $self->send_message( pack('C*', 0, 0, $code, $frame_num) ); } sub is_scroll { my $self = shift; ($self->command(0x1C) eq 0xC2); } my %TIMEOUT = ( 0.5 => 0x41, 1.0 => 0x42, 2.0 => 0x43, 3.0 => 0x44, 5.0 => 0x45, ); sub timeout { my($self, $time) = @_; my $code = $TIMEOUT{$time} or return 0; $self->command($code); } { for my $key (keys %COMMANDS) { no strict 'refs'; *{$key} = sub { my $self = shift; my $code = $COMMANDS{$key}; if (ref($code) eq 'ARRAY') { my $flag = shift; $code = $code->[((!defined $flag || $flag) ? 0 : 1)]; } $self->command($code, @_); }; } } 1; __END__ =head1 NAME Device::VFD::GP1022 - GP1022 VFD module controller =head1 SYNOPSIS use Device::VFD::GP1022; my $vfd = Device::VFD::GP1022->('/dev/ttyUSB0'); $vfd->message( 'messages for vfd' ); use to orignal serial module use Device::VFD::GP1022; use Device::SerialPort; my $serial = Device::SerialPort->new('/dev/ttyUSB0') or die; # more serial port configs my $vfd = Device::VFD::GP1022->( serial => $serial ); $vfd->message( 'messages for vfd' ); =head1 DESCRIPTION this module is controller for VFD module of GP1022. It works for Unix, Mac, and Windows. =head1 METHODS =over 4 =item new instance =item message 文字列を引数にあたえると、その文字列をVFDに表示します。 Lをuseする事で利用できるDSLを使用して、細かな表示制御を行えます。 特殊な呼び出しかたにより挙動を変更できます。 $vfd->message( append => 'for vfd' ); 表示中の文字列に続けて表示します。 $vfd->message( 'for vfd' ); $vfd->message( force => 'for vfd' ); 表示中の文字列を捨てて、即時表示します。 $vfd->message( buffer => 'for vfd' ); バッファーに文字列を蓄えます。別途 switch メソッドにてバッファを切り替えられます。 =item is_scroll 現在スクロール表示中なら真を返します。 =item display $vfd->display; $vfd->display(1); 表示開始する。 $vfd->display(0); 表示停止する。 =item scroll $vfd->scroll; $vfd->scroll(1); スクロール開始する。 $vfd->scroll(0); スクロール停止する。 =item switch switch to buffer =item rewind 表示内容を頭出しします。 =item full 画面領域全てを使ってメッセージを描画させます。 =item reverse 点滅時のバックグラウンドとフォアグラウンドを切り替えます。 $vfd->reverse; $vfd->reverse(1); 切り替える。 $vfd->reverse(0); デフォルトに戻す。 =item clear 表示データ全てをクリアします。 =item default 全ての状態をデフォルトに戻します。 =back =head1 AUTHOR Kazuhiro Osawa Eko@yappo.ne.jpE =head1 SEE ALSO L L =head1 LICENSE This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself. =cut