Skip to content

Commit

Permalink
Large commit, semiworking state, Driver C->OCaml
Browse files Browse the repository at this point in the history
This commit represents a lot of work which went into rewriting the driver in OCaml. Unfortunately I did not commit during this port but hopefully this won't cause any problems in the short and long run.
Summary/Observations:
- The directory is now divided into bin/ for the executable and lib/ for the library.
This has the advantage that one can now test the library interactively via "dune utop lib" (and then open Epd in utop). Furthermore this should smooth the way to creating an opam package which can then be used in multiple different programs (terminal, vnc server, graphics driver, ebookreader, etc.). The scope and feasability of these programs will be determined later, for now we are stuck with a terminal and use it for everything (via curses or other programs)
- The 5 layers of which the library consists have been ported with the exception of the lowest level.
The levels are, from lowest to highest:
* bcm: Interacting with the bus (Transmitting values)
* IT8951: Interacting with the IT8951 controller (Transmitting
		instructions)
* cmd: Stringing those commands together to achieve complex behaviour.
Here we finally have an effect on the display. This could perhaps be
merged with the IT8951 layer because they both represent interaction on
roughly the same level. Some of the features of cmd are simply single
commands and therefore the distinction is not as clear.
* interface: Another well chosen name. This layer implements even more
complex behaviour but is actually quite simple and can most likely be
absorbed in both the layers above and below. Currently it handles the
initialisation of the board, reading details about the IT8951 and the
screen, creating the initial buffer, transmitting it and displaying it.
Essentially it provides an abstract interface: display areas, draw
points and text (only points work currently). It does therefore not
provide any important functionality itself and handles things in a
probably too complex manner (which is a relic of the original driver).
* EPD: Essentially the same as interface. Should originally reimplement
the graphics library from the OCaml stdlib but as that is hardly useful
for anything serious this has been abandoned.
- Horrible performance:
The program is about 2 times slower than the C version which is
most likely the result of the interface layer and its handling of the
images. As of now I'm not all that certain exactly what must be
transmitted and as that is the limiting factor in regards to speed this
will be investigated soon. The original driver was built around having
multiple buffers which can be loaded onto the driver board. This most
likely eased the drawing of images (as can be seen in the original
		driver code). As this is no longer the focus of the driver and not
idiomatic for OCaml this could perhaps be changed to a single buffer
which contains everything drawn. Then the interface layer would only
provide a display area function which actually transmitted the buffer
and displayed it.
Another cause for this issue could be that utop does not compile the
code but interpret it and is therefore significantly slower. Should not
be as relevant on this IO bound task
Perhaps the distinction between transmitting and displaying is
worthwhile preserving but I personally doubt it as it would save, in the
worst case, a single transmission of the whole buffer at small but not
negligible overhead when writing any code. Most likely these issues will
resolve once the bus usage has been cut down to the minimum.
- Unclear and inconsistent naming:
Across the modules exist 3 load_image variations, additionally to
load_image_start/end, multiple display variants, multiple inits etc.
Exactly what each layer achieves is not obvious from the name alone.
- Port the lowest layer to WiringPi or create own library for the pins.
  • Loading branch information
Willenbrink committed Apr 7, 2020
1 parent b0ad335 commit 06179ad
Show file tree
Hide file tree
Showing 19 changed files with 500 additions and 240 deletions.
78 changes: 0 additions & 78 deletions EPD.ml

This file was deleted.

46 changes: 0 additions & 46 deletions IT8951.ml

This file was deleted.

11 changes: 0 additions & 11 deletions bcm2835.ml

This file was deleted.

File renamed without changes.
2 changes: 1 addition & 1 deletion dune → bin/dune
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
(executable
(name term)
(libraries WiringPi ctypes ctypes.foreign))
(libraries epd ctypes ctypes.foreign))

(env
(dev
Expand Down
File renamed without changes.
46 changes: 7 additions & 39 deletions term.ml → bin/term.ml
Original file line number Diff line number Diff line change
@@ -1,54 +1,20 @@
open Ctypes
open Foreign
open Epd

let nth_opt list i =
try
Some (List.nth list i)
with _ -> None

let ioctl = Dl.dlopen ~flags:[Dl.RTLD_LAZY] ~filename:(Unix.getcwd () ^ "/ioctl.so")
let ioctl = Dl.dlopen ~flags:[Dl.RTLD_LAZY] ~filename:(Unix.getcwd () ^ "/bin/ioctl.so")
let funer name params = foreign ~from:ioctl ~release_runtime_lock:false name params

let set_term_dim x y = funer "set_screen_dimensions" (int @-> int @-> returning void) x y
let get_term_dim () =
let () = funer "get_screen_dimensions" (void @-> returning void) () in
funer "getx" (void @-> returning int) (), funer "gety" (void @-> returning int) ()

module Matrix = struct
type 'a t = 'a array array

let create cols rows value : 'a t =
(* In docs: x then y but we want to access each row all at once -> first rows then cols
The getRow basically reads one value along the x dimension of the array , which corresponds to a column
but we want rows instead of columns*)
Array.make_matrix rows cols value
let set t c r v =
let rows = Array.length t in
let cols = Array.length t.(0) in
if r < 0 || c < 0 || r >= rows || c >= cols then Printf.printf "Accessing %i:%i with max %i:%i\n" r c rows cols
else
t.(r).(c) <- v
let get t c r = t.(r).(c)
let getRow t r = t.(r) |> Array.to_list
let getRows t =
Array.map (Array.to_list) t |> Array.to_list

let mapi f t =
Array.mapi (fun row_index row -> Array.mapi (fun col_index char -> f col_index row_index char) row) t

let iteri f t =
Array.iteri (fun row_index row -> Array.iteri (fun col_index char -> f col_index row_index char) row) t

let fold f_row f_col a b (t : 'a t) = (* Fold row by row, left to right*)
let fold_col = Array.fold_left (fun a char -> f_col a char) in
let fold_row = Array.fold_left (fun a row -> f_row a (fold_col b row)) in
fold_row a t

let diff t1 t2 =
let t_index = mapi (fun x y c -> (x,y,c)) t1 in
fold (@) (fun a (x,y,c) -> if get t2 x y = c then a else (x,y)::a) [] [] t_index
end

module type Draw =
sig
val f : (int * int) list -> ((int * int) * (int * int)) list
Expand Down Expand Up @@ -138,9 +104,11 @@ let read_vcs () =
text

let init () =
print_endline "Configuring virtual terminal";
set_term_dim 37 100;
EPD.init () |> ignore;
print_endline "Clearing buffer";
EPD.clear EPD.bg;
print_endline " Updating screen";
EPD.display_buffer_all EPD.White


Expand All @@ -155,7 +123,7 @@ let rec loop prevText =
List.iter (fun ((x1,y1),(x2,y2)) -> Printf.printf "%i-%i : %i-%i\t" y1 x1 y2 x2) dirty_areas;
if List.length dirty_areas <> 0 then print_endline "";
*)
List.iter (fun ((x1,y1),(x2,y2)) -> EPD.display_buffer ((x1*8,y1*8),(x2*8+8,y2*16+16)) EPD.Fast) dirty_areas
List.iter (fun ((x1,y1),(x2,y2)) -> EPD.display_buffer ((x1*8,y1*8,x2*8+8,y2*16+16)) EPD.Fast) dirty_areas
in

let chars_changed = Matrix.diff text prevText in
Expand All @@ -168,4 +136,4 @@ let main () =
init ();
loop (Matrix.create 100 37 (char_of_int 0))

let () = main ()
let () = ()
57 changes: 0 additions & 57 deletions cmd.ml

This file was deleted.

2 changes: 1 addition & 1 deletion dune-project
Original file line number Diff line number Diff line change
@@ -1 +1 @@
(lang dune 1.2)
(lang dune 2.4)
67 changes: 67 additions & 0 deletions lib/EPD.ml
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
open Ctypes
open Foreign
open Interface

let libIT = Dl.dlopen ~flags:[Dl.RTLD_LAZY] ~filename:("/opt/IT8951/eink-display/IT8951")

let funer name params = foreign ~from:libIT ~release_runtime_lock:false name params
let vv = void @-> returning void

type point = int * int
type area = point * point
type image = int carray
type mode = White | Unknown | Slow | Medium | Fast
let mode_to_int = function White -> 0 | Unknown -> 1 | Slow -> 2 | Medium -> 3 | Fast -> 4

let fg = 0x00
let bg = 0xFF

let check (x,y) = assert (x >= 0 && y >= 0 && x < width () && y < height ())

let free () = free_graphics

let plot (x,y) =
check (x,y);
plot fg (x,y)

let clear (c : int) = clear_color c

let get_screen () = (0, 0, width (), height ())

(* TODO unused
let load_image img ((x1,y1),(x2,y2) as a) =
check_area a;
load_image !img (x1,y1,x2, y2)
*)

let display area mode =
display area (mode_to_int mode)

let display_buffer area mode =
display_buffer area (mode_to_int mode)

let display_all mode = display (get_screen ()) mode

let display_buffer_all mode = display_buffer (get_screen ()) mode

let draw_char ?(fg=fg) ?(bg=bg) (x,y as p) c =
(*TODO check area instead of point <- calculate area of char beforehand*)
check p;
put_char p c fg bg

let draw_text ?(fg=fg) ?(bg=bg) (x,y as p) str =
(*TODO check area instead of point <- calculate area of char beforehand*)
check p;
put_text p str fg bg

let rgb r g b = (r+g+b)/3

let random int = Random.int int

let point () = random (width () -1), random (height () -1)

let char () = random 255 |> Char.chr

(* Returns true on failure TODO is this correct? *)
let () = init_graphics ()

Loading

0 comments on commit 06179ad

Please sign in to comment.