#!/usr/bin/env perl use strict; use GD::Image; use IO::Socket::INET; use Data::Dumper; #use IO::Socket::Timeout; # # $SIG{INT} = \&quit; my $prompt = '> '; sub my_readline_worker($) { my $sock = shift; my $ret = ""; my $d = ""; while (1) { return $ret if $sock->read( $d, 1 ) != 1; next if $d eq "\n"; $ret .= $d; return $ret if $d eq "\r"; return $ret if $ret =~ /> $/; } } sub my_readline($) { my $sock = shift; my $ret = my_readline_worker($sock); #print $ret."\n"; return $ret; } sub wait_for_prompt($) { my $ocd = shift; 1 while ( my_readline($ocd) ne $prompt ); } sub open_ocd($) { my $addr = shift; my $sock = IO::Socket::INET->new($addr); wait_for_prompt($sock); $sock->printf( "reset halt\n"); wait_for_prompt($sock); return $sock; } sub write_reg($$$) { my ( $ocd, $r, $v ) = @_; $ocd->printf( "mww 0x%08x 0x%08x\n", $r, $v ); wait_for_prompt($ocd); } sub read_reg($$) { my ( $ocd, $r ) = @_; my $ret; $ocd->printf( "mdw 0x%08x\n", $r ); $ret = my_readline($ocd); $ret = my_readline($ocd); wait_for_prompt($ocd); $ret =~ s/[\r\n\s]//g; if ( $ret =~ /0x[0-9A-Fa-f]+:([0-9a-fA-F]+)/ ) { return hex($1); } return undef; } sub dir_set($$) { my ( $ocd, $v ) = @_; write_reg( $ocd, 0x50000518, $v ); } sub dir_clr($$) { my ( $ocd, $v ) = @_; write_reg( $ocd, 0x5000051c, $v ); } sub io_set($$) { my ( $ocd, $v ) = @_; write_reg( $ocd, 0x50000508, $v ); } sub io_clr($$) { my ( $ocd, $v ) = @_; write_reg( $ocd, 0x5000050c, $v ); } sub io_get($) { my $ocd = shift; return read_reg( $ocd, 0x50000510 ); } my $SDABIT = 1 << 23; my $SCLBIT = 1 << 24; sub i2c_set($$$) { my ( $ocd, $scl, $sda ) = @_; my $clr = 0; my $set = 0; if ($scl) { $clr |= $SCLBIT; } else { $set |= $SCLBIT; } if ($sda) { $clr |= $SDABIT; } else { $set |= $SDABIT; } dir_set( $ocd, $set ); dir_clr( $ocd, $clr ); } sub i2c_get($) { my $ocd = shift; return ( io_get($ocd) & $SDABIT ) ? 1 : 0; } sub i2c_start($) { my $ocd = shift; i2c_set( $ocd, 1, 1 ); i2c_set( $ocd, 1, 0 ); i2c_set( $ocd, 0, 0 ); print "S"; } sub i2c_stop($) { my $ocd = shift; i2c_set( $ocd, 0, 0 ); i2c_set( $ocd, 1, 0 ); i2c_set( $ocd, 1, 1 ); print "T\n"; } sub i2c_sendbyte($$) { my ( $ocd, $byte ) = @_; for ( my $c = 0x80 ; $c ; $c >>= 1 ) { my $v = ( $c & $byte ) ? 1 : 0; print $v; i2c_set( $ocd, 0, $v ); i2c_set( $ocd, 1, $v ); i2c_set( $ocd, 0, $v ); } } sub i2c_startaddr($$) { my ( $ocd, $addr ) = @_; my $ret; i2c_start($ocd); i2c_sendbyte( $ocd, $addr ); print " "; i2c_set( $ocd, 0, 1 ); i2c_set( $ocd, 1, 1 ); $ret = i2c_get($ocd); i2c_set( $ocd, 0, 1 ); print $ret; return $ret; } sub i2c_ping($$) { my ( $ocd, $addr ) = @_; my $ret; $ret = i2c_startaddr( $ocd, $addr ); i2c_stop($ocd); return $ret; } my $SDADDR = 0x78; sub sd_write($$$) { my ( $ocd, $dnc, $v ) = @_; my $ack1; my $ack2; i2c_startaddr( $ocd, $SDADDR ); i2c_sendbyte( $ocd, 0x80 | ( $dnc ? 0x40 : 0x00 ) ); i2c_set( $ocd, 0, 1 ); i2c_set( $ocd, 1, 1 ); $ack1 = i2c_get($ocd); i2c_set( $ocd, 0, 1 ); i2c_sendbyte( $ocd, $v ); i2c_set( $ocd, 0, 1 ); i2c_set( $ocd, 1, 1 ); $ack2 = i2c_get($ocd); i2c_set( $ocd, 0, 1 ); i2c_stop($ocd); return $ack1 | $ack2; } sub sd_cmd($$) { my ( $ocd, $c ) = @_; return sd_write( $ocd, 0, $c ); } sub sd_dat($$) { my ( $ocd, $d ) = @_; return sd_write( $ocd, 1, $d ); } sub sd_address_mode($$) { my ( $ocd, $m ) = @_; sd_cmd( $ocd, 0x20 ); sd_cmd( $ocd, $m ); } sub sd_cols($$$) { my ( $ocd, $s, $e ) = @_; sd_cmd( $ocd, 0x21 ); sd_cmd( $ocd, $s ); sd_cmd( $ocd, $e ); } sub sd_rows($$$) { my ( $ocd, $s, $e ) = @_; sd_cmd( $ocd, 0x22 ); sd_cmd( $ocd, $s ); sd_cmd( $ocd, $e ); } sub sd_init($) { my $ocd = shift; # off sd_cmd( $ocd, 0xae ); # set clock freq sd_cmd( $ocd, 0xd5 ); sd_cmd( $ocd, 0xa0 ); #set multiplex ratio sd_cmd( $ocd, 0xa8 ); sd_cmd( $ocd, 0x1f ); #set display offset sd_cmd( $ocd, 0xd3 ); sd_cmd( $ocd, 0x00 ); #set display start sd_cmd( $ocd, 0x40 ); #set charge pump using DC/DC sd_cmd( $ocd, 0x8d ); sd_cmd( $ocd, 0x14 ); #set segment remap sd_cmd( $ocd, 0xa1 ); #set com scan direction sd_cmd( $ocd, 0xc8 ); # set com pins hardware config. sd_cmd( $ocd, 0xda ); sd_cmd( $ocd, 0x02 ); #set contrast sd_cmd( $ocd, 0x81 ); sd_cmd( $ocd, 0x8f ); #set precharge period using DC/DC sd_cmd( $ocd, 0xd9 ); sd_cmd( $ocd, 0xf1 ); #set deselect voltage sd_cmd( $ocd, 0xdb ); sd_cmd( $ocd, 0x40 ); #set display on/off sd_cmd( $ocd, 0xa4 ); #set display on sd_cmd( $ocd, 0xaf ); } sub sd_display($$) { my ( $ocd, $fn ) = @_; my $ack; my $file=GD::Image->new($fn); my $img=GD::Image->new(128,32,1); $img->copy($file,0,0,0,0,128,32); sd_cols($ocd,0,127); sd_rows($ocd,0,63); for (my $y=0;$y<32;$y+=8) { print "y=$y\n"; i2c_startaddr( $ocd, $SDADDR ); i2c_sendbyte( $ocd, 0x40 ); i2c_set( $ocd, 0, 1 ); i2c_set( $ocd, 1, 1 ); $ack = i2c_get($ocd); i2c_set( $ocd, 0, 1 ); print " "; for (my $x=0;$x<128;++$x) { my $v=0; my $c=0x1; for (my $i=0;$i<8;++$i) { my $w=$img->getPixel($x,$i+$y); # print "r=$r,g=$g,b=$b\n"; $v|=$c if $w>8388607; $c<<=1; } i2c_sendbyte( $ocd, $v ); i2c_set( $ocd, 0, 1 ); i2c_set( $ocd, 1, 1 ); $ack = i2c_get($ocd); i2c_set( $ocd, 0, 1 ); print " "; } i2c_stop($ocd); } } my $ocd = open_ocd('127.0.0.1:4444'); my $LED = 1 << 22; my $SCREEN_PWR = 1 << 25; my $SCREEN_BRIGHT = 1 << 30; sub quit { io_clr( $ocd, $SCREEN_PWR ); io_clr( $ocd, $SCREEN_BRIGHT ); io_set( $ocd, $LED ); kill 9, $$; } dir_set( $ocd, $LED | $SCREEN_PWR | $SCREEN_BRIGHT ); io_clr( $ocd, $SCLBIT | $SDABIT ); io_clr( $ocd, $LED ); i2c_start($ocd); i2c_stop($ocd); #for (my $a=0;$a<0x100;$a++) { #printf "a=0x%02x ",$a; #i2c_ping($ocd,$a); #} # #Power down screen io_clr( $ocd, $SCREEN_PWR | $SCREEN_BRIGHT ); #power up screen io_set( $ocd, $SCREEN_PWR | $SCREEN_BRIGHT ); sd_init($ocd); sd_address_mode( $ocd, 0x0 ); sd_display($ocd,"fish.png"); while (1) { sd_cmd( $ocd, 0xa7 ); sleep(1); sd_cmd( $ocd, 0xa6 ); sleep(1); }