######################################################################################### # Package HiPi::Interface::EPaper::TypeB # Description : Control Monochrome Epaper Displays # Copyright : Copyright (c) 2018 Mark Dootson # License : This is free software; you can redistribute it and/or modify it under # the same terms as the Perl 5 programming language system itself. ######################################################################################### package HiPi::Interface::EPaper::TypeB; ######################################################################################### use strict; use warnings; use parent qw( HiPi::Interface::EPaper ); use HiPi qw( :rpi :spi :epaper ); use Carp; __PACKAGE__->create_accessors( qw( power_setting_bytes booster_soft_start_bytes panel_setting_bytes vcom_and_data_byte pll_control_bytes tcon_resolution_bytes vcm_dc_setting_bytes vcom_and_data_shutdown ) ); our $VERSION ='0.81'; use constant { PANEL_SETTING => 0x00, POWER_SETTING => 0x01, POWER_OFF => 0x02, POWER_OFF_SEQUENCE_SETTING => 0x03, POWER_ON => 0x04, POWER_ON_MEASURE => 0x05, BOOSTER_SOFT_START => 0x06, DEEP_SLEEP => 0x07, DATA_START_TRANSMISSION_1 => 0x10, DATA_STOP => 0x11, DISPLAY_REFRESH => 0x12, DATA_START_TRANSMISSION_2 => 0x13, PLL_CONTROL => 0x30, TEMPERATURE_SENSOR_COMMAND => 0x40, TEMPERATURE_SENSOR_CALIBRATION => 0x41, TEMPERATURE_SENSOR_WRITE => 0x42, TEMPERATURE_SENSOR_READ => 0x43, VCOM_AND_DATA_INTERVAL_SETTING => 0x50, LOW_POWER_DETECTION => 0x51, TCON_SETTING => 0x60, TCON_RESOLUTION => 0x61, SOURCE_AND_GATE_START_SETTING => 0x62, GET_STATUS => 0x71, AUTO_MEASURE_VCOM => 0x80, VCOM_VALUE => 0x81, VCM_DC_SETTING_REGISTER => 0x82, PROGRAM_MODE => 0xA0, ACTIVE_PROGRAM => 0xA1, READ_OTP_DATA => 0xA2, HIPI_BORDER_DDXCDI_MASK => 0x3F, HIPI_BORDER_BLACK => 0x00, HIPI_BORDER_WHITE => 0x40, HIPI_BORDER_COLOUR => 0x80, HIPI_BORDER_FLOAT => 0xC0, }; sub _create { my( $class, %params ) = @_; # Default pins for WaveShare 'HAT' modules # reset_pin => RPI_PIN_11, # 17 # dc_pin => RPI_PIN_22, # 25 # busy_pin => RPI_PIN_18, # 24 $params{reset_pin} //= RPI_PIN_11; $params{dc_pin} //= RPI_PIN_22; $params{busy_pin} //= RPI_PIN_18; my $self = $class->SUPER::_create( %params ); return $self; } sub set_lut_bw { my $self = shift; my $lut_commands = { lut_vcom0 => 0x20, lut_w => 0x21, lut_b => 0x22, lut_g1 => 0x23, lut_g2 => 0x24, }; for my $lut( qw( lut_vcom0 lut_w lut_b lut_g1 lut_g2 ) ) { $self->_send_command_if_data( $lut_commands->{$lut}, $self->$lut ); } return; } sub set_lut_colour { my $self = shift; my $lut_commands = { lut_vcom1 => 0x25, lut_red0 => 0x26, lut_red1 => 0x27, }; for my $lut( qw( lut_vcom1 lut_red0 lut_red1 ) ) { $self->_send_command_if_data( $lut_commands->{$lut}, $self->$lut ); } return; } sub display_sleep { my $self = shift; return if $self->_in_deep_sleep(); $self->wait_for_idle(); $self->send_command(VCOM_AND_DATA_INTERVAL_SETTING, $self->vcom_and_data_shutdown); $self->send_command(VCM_DC_SETTING_REGISTER, 0x00); # to solve Vcom drop $self->send_command(POWER_SETTING, 0x02, 0x00, 0x00, 0x00); # power setting - gate switch to external $self->wait_for_idle(); $self->send_command(POWER_OFF); $self->_in_deep_sleep(1); return; } sub display_wake { my $self = shift; $self->display_reset; return; } sub display_reset { my $self = shift; $self->reset(); $self->_in_deep_sleep(0); $self->_send_command_if_data( POWER_SETTING, $self->power_setting_bytes ); $self->_send_command_if_data( BOOSTER_SOFT_START, $self->booster_soft_start_bytes ); $self->send_command(POWER_ON); $self->wait_for_idle(); $self->_send_command_if_data( PANEL_SETTING, $self->panel_setting_bytes ); $self->send_command( VCOM_AND_DATA_INTERVAL_SETTING, $self->get_vcom_and_data ); $self->_send_command_if_data( PLL_CONTROL, $self->pll_control_bytes ); $self->_send_command_if_data( TCON_RESOLUTION, $self->tcon_resolution_bytes ); $self->_send_command_if_data( VCM_DC_SETTING_REGISTER, $self->vcm_dc_setting_bytes ); $self->set_lut_bw; $self->set_lut_colour; $self->wait_for_idle(); return; } sub display_update { my $self = shift; $self->wait_for_idle(); # Frame 1 if( $self->frame_1_type != EPD_FRAME_TYPE_UNUSED ) { if( $self->frame_1_bpp == EPD_FRAME_BPP_2 ) { $self->_do_update_2_bpp( DATA_START_TRANSMISSION_1, $self->context->buffers->[0], $self->context->buffer_bytes ); } elsif( $self->frame_1_bpp == EPD_FRAME_BPP_1 ) { $self->_do_update_1_bpp( DATA_START_TRANSMISSION_1, $self->context->buffers->[0], $self->context->buffer_bytes ); } } # Frame 2 if( $self->frame_2_type != EPD_FRAME_TYPE_UNUSED ) { if( $self->frame_2_bpp == EPD_FRAME_BPP_2 ) { $self->_do_update_2_bpp( DATA_START_TRANSMISSION_2, $self->context->buffers->[1], $self->context->buffer_bytes ); } elsif( $self->frame_2_bpp == EPD_FRAME_BPP_1 ) { $self->_do_update_1_bpp( DATA_START_TRANSMISSION_2, $self->context->buffers->[1], $self->context->buffer_bytes ); } } $self->send_command(DISPLAY_REFRESH); $self->wait_for_idle(); return; } sub _do_update_2_bpp { my($self, $transmission, $buffer, $bufferbytes) = @_; $self->send_command( $transmission ); $self->delay(2); my $chunksize = $self->spi_chunksize; my @bytes = (); for (my $i = 0; $i < $bufferbytes; $i++) { my $temp = 0x00; for (my $bit = 0; $bit < 4; $bit++) { if (($buffer->[$i] & (0x80 >> $bit)) != 0) { $temp |= 0xC0 >> ($bit * 2); } } push @bytes, $temp; if( @bytes == $chunksize) { $self->send_data(@bytes); @bytes = (); } $temp = 0x00; for (my $bit = 4; $bit < 8; $bit++) { if (($buffer->[$i] & (0x80 >> $bit)) != 0) { $temp |= 0xC0 >> (($bit - 4) * 2); } } push @bytes, $temp; if( @bytes == $chunksize) { $self->send_data(@bytes); @bytes = (); } } $self->send_data( @bytes ) if @bytes; $self->delay(2); return; } sub _do_update_1_bpp { my($self, $transmission, $buffer, $bufferbytes) = @_; $self->send_command( $transmission ); $self->delay(2); my $chunksize = $self->spi_chunksize; my $byteloops = int($bufferbytes / $chunksize); my $lastbytes = $bufferbytes % $chunksize; my $index; for ($index = 0; $index < $byteloops; $index++) { my $start = $index * $chunksize; my $end = $start + $chunksize - 1; $self->send_data( @$buffer[$start..$end] ); } if( $lastbytes ) { my $start = $index * $chunksize; my $end = $start + $lastbytes - 1; $self->send_data( @$buffer[$start..$end] ); } $self->delay(2); return; } sub get_vcom_and_data { my($self) = @_; my $vcom = $self->vcom_and_data_byte & HIPI_BORDER_DDXCDI_MASK; my $control = $self->border_control; my $border = HIPI_BORDER_FLOAT; if( $control == EPD_BORDER_BLACK ) { $border = HIPI_BORDER_BLACK; } elsif( $control == EPD_BORDER_COLOUR ) { if( $self->is_tri_colour ) { $border = HIPI_BORDER_COLOUR; } else { $border = HIPI_BORDER_BLACK; } } elsif( $control == EPD_BORDER_WHITE ) { $border = HIPI_BORDER_WHITE; } my $vcdata = $border | $vcom; # warn sprintf('RETURNING VCOM DATA 0x%X', $vcdata); return $vcdata; } 1; __END__