Thursday, August 16, 2012

LaserJet 4 panel

I recently (huh 2009) got my hands on LJ4 control panel. As electonic engineer I decided to work out how it works and connect it to some microcontroller and make it work for yet-to-be-defined MY purpose :)

Hardware

It consists of keyboard (8 keys), 3 LEDs and nice looking a little bit retro alphanumeric VFD display (1 line by 16 characters ). Glance at the board reveals that it has voltage converter for VFD and 3 ICs. One is M66004FP - mitsubishi VFD controller, HC595 serial in-parallel out shift register and HC165 parallel in serial out shift register. Board is connected with the rest of the world with 10 wire ribbon cable. not saying more I've discovered pinout to be:
Pin#description(M66004/HC595/HC165)
1GND
2GND
3GND
4+5V
5+5V
6CSK/SRCLK/CLK
7SDATA/SER/-
8~CS/-/-
9-/RCLK/~LD
10-/-/QH
I've used TI's datasheets for HCxxx and pin names are taken from them, for M66004FP I've used original mitsubishi datasheet.

LEDs are connected to bits F, G, H (three MSB) of HC595 (active low - 0 in register makes led light up) and keys are connected to inputs of HC165 (active high - i.e. pressed key is loaded as hi to register).
(taken with shitty phone and after double re-compression, sorry for the quality)
VFD driving
The most easy task, first - grab datasheet, M66004FP is very easy to drive, and datasheet is concise and useful. Most (well, all except custom character definition) commands are one byte long and "use" empty space in ascii character map. In a nutshell to send byte to M66004FP you need to:
  1. pull ~CS low (this enables M66004FP) [pin 8]
  2. clock data using CSK [pin 6] and SDATA [pin 7] lines (bits are latched on rising CSK edge)
  3. pull ~CS high [pin 8]
That's it. One interesting note - VFD has special cursor/undeline segment under each digit and they can be driven independently (vide video).

Leds and keyboard

And now for something completely different - reading keyboard and driving LEDs. Reminder: LEDs are connected to 3 MSB of serial-to-parallel shift register HC595, keyboard is connected (8 keys - 8 bits) to parallel in-serial out shift register HC165. LEDs ar light up by 0 in register. Pressing a key forces hi (1) state on register input. And now the tricky part - both registers share clock signal (SRCLK/CLK), and load line (RCLK/~LD). Unfortunately according to me it's impossible to independently read keys and drive LEDS. Which means that in your program you'll need ugly global variable to store LED state.
Procedure is as follows:
  1. clock in 8 bits (they go to HC595) - 3 MSB define LED state
  2. drive RCLK/~LD high,low,high (which clocks data to HC595 outputs - setting LEDs and loads keyboard state to HC165)
  3. clock 8 bits of data from HC165 register, data is available on pin 10 of connector (QH)

Prototype

I used one of the most popular microcontrollers - PIC16F84 (or F628). And very nice programming language - similar to nothing but very well fitting 16F84 - JAL.
Pins:
A0 - serial input (uC input)
B0 - sclk (uC output)
B1 - sdata (uC output)
B2 - ~LD (uC output)
B3 - M66004FP CS (uC output)
It looks like this:


Source code:

--
-- name : hp_panel.jal
-- author : misio
-- date : 2009
-- purpose : poc driving hp lj panel
-- author : misio
-- public domain

-- target configuration: 16f84 with 4 Mhz Xtal
include 16f84_4

-- standard library
include jlib

-- two LEDs that are "on board"
var volatile bit led1 is pin_b7
var volatile bit led2 is pin_b6

-- panel connection
var volatile bit sclk is pin_b0
var volatile bit sdata is pin_b1
var volatile bit data_ld is pin_b2
var volatile bit m6600fp_cs is pin_b3
var volatile bit serin is pin_a0

var byte i

procedure clock_byte( byte in b ) is
 var bit x at b : 7
 for 8 loop
  delay_1us( 100 )
  sclk = off
  sdata = x
  delay_1us( 100 )
  sclk = on
  b = b << 1 

 end loop 
 sclk = off 
end procedure 

procedure send_m6600fp( byte in b ) is 
 data_ld = on 
 m6600fp_cs = off 
 clock_byte( b ) 
 delay_1us( 20 ) 
end procedure 

procedure write_m6600fp( byte in b ) is 
 send_m6600fp( b ) 
 m6600fp_cs = on 
end procedure 

procedure goto_m6600fp( byte in b ) is 
 send_m6600fp ( 0x_E0 | (b & 0x_0F) ) 
 m6600fp_cs = on 
end procedure 

procedure show_byte( byte in b ) is 
 var bit v at b : 0 
 goto_m6600fp(0) 
 write_m6600fp("[") 
 for 8 loop 
  if ( v == on ) then 
   write_m6600fp(0x_7F) 
 else 
   write_m6600fp(" ") 
 end 
 if b = b >> 1
   end loop
 write_m6600fp("]")
end procedure

procedure send_hc595( byte in b ) is
 m6600fp_cs = on
 data_ld = off
 clock_byte( b )
 data_ld = on
 delay_1us( 100 )
 data_ld = off
end procedure


function get_kbd_set_leds ( byte in m ) return byte is
var byte n
var byte b

m6600fp_cs = on
data_ld = off
clock_byte( m )
data_ld = on
delay_1us( 100 )
n = 7
b = 0
for 8 loop
 sclk = off
 delay_1us ( 100 )
 if serin == on then
  b = b | ( 1 << n ) 

 end if 
 sclk = on 
 delay_1us( 100 ) 
 n = n - 1 
end loop 
return b 
end function 

-- ------------------ -- MAIN PROGRAM -- ------------------ 
var byte led_status 

assembler 
 bsf 03, 5
 bcf 01, 7 
 bcf 03, 5 
end assembler
-- pin directions init 
 pin_b7_direction = output 
 pin_b6_direction = output 
 pin_b0_direction = output 
 pin_b1_direction = output 
 pin_b2_direction = output 
 pin_b3_direction = output 
 pin_a0_direction = input 

delay_1ms( 300 ) 
send_hc595( 0x_FF ) 
write_m6600fp( 0x_F3 ) -- test mode - all on 
write_m6600fp( 0x_07 ) -- 16 digits 
write_m6600fp( 0x_0F ) -- dim max 
write_m6600fp( 0x_F6 ) -- 128/fosc 
-- delay_1s( 1 ) 
-- write_m6600fp( 0x_F7 ) -- 128/fosc 
-- write_m6600fp( 0x_08 ) 
write_m6600fp( 0x_F1 ) -- test mode - normal 
write_m6600fp( 0x_80 ) -- cursor off @ 0 
write_m6600fp( 0x_11 ) -- cursor on @ 1 

write_m6600fp( "-" ) 
write_m6600fp( "=" ) 
write_m6600fp( " " ) 
write_m6600fp( "D" ) 
write_m6600fp( "u" ) 
write_m6600fp( "p" ) 
write_m6600fp( "a" ) 
write_m6600fp( " " ) 
write_m6600fp( "b" ) 
write_m6600fp( "l" ) 
write_m6600fp( "a" ) 
write_m6600fp( "d" ) 
write_m6600fp( "a" ) 
write_m6600fp( " " ) 
write_m6600fp( "=" ) 
write_m6600fp( "-" ) 

-- endless loop 
-- i = 0 
-- for 32 loop 

-- send_hc595( ! (1 << i) ) 
-- i = i + 1 
-- if i > 7 then
-- i = 0
-- end if
-- delay_1ms( 200 )
-- end loop
-- send_hc595 ( 0x_FF )


led_status = 1
forever loop
 i = get_kbd_set_leds ( led_status )
 if (i & 1) == 1 then
  while ( i & 1 ) == 1 loop
   i = get_kbd_set_leds ( led_status )
  end loop
  led_status = led_status << 1 

  if led_status == 0 then 
   led_status = 1 
  end if 
 end if 
-- i = read_hc165 
 show_byte( i ) 
 led1 = on delay_1ms( 50 ) 
 led1 = off
end loop -- final, never goes below 

forever loop 
 i = 0 
 for 8 loop 
  write_m6600fp( 0x_10 + i ) 
  write_m6600fp( 0x_10 + 15 - i ) 
  delay_1ms(100) 
  write_m6600fp( 0x80 + i ) 
  write_m6600fp( 0x80 + 15 - i ) 
  i = i + 1 
 end loop 
 i = i - 1 
 for 7 loop 
  write_m6600fp( 0x_10 + i ) 
  write_m6600fp( 0x_10 + 15 - i ) 
  delay_1ms(100) 
  write_m6600fp( 0x80 + i ) 
  write_m6600fp( 0x80 + 15 - i ) 
  i = i - 1 
 end loop 
-- delay_1s 
-- led1 = on 
-- delay_1s 
-- led2 = on 
-- delay_1s 
-- led1 = off 
-- delay_1s 
-- led2 = off 
end loop

No comments:

Post a Comment