landev:
Hello Arduino Community
i'm interested in learning how to do LOW LEVEL things.
For example in my Starter Kit is a LCD that can be connected with 4 or 8 Pins.
I realized that the libraries LiquidCristal.h print.h and so on does quite a lot of work for me and i would like to learn how to control an electronic component like the LCD without such a library and would like to ask if there are any tutorials or projects where i could learn these things.
(I tried to read these libraries on github but i realized that these libraries are already to big and to complex to start with)
With Kind Regards
Marc Landolt
Bit level code is a bit beyond (no pun intended) ordinary C/C++ books.
As far as the LCD is concerned, I can explain how it works and that may help you a bit.
The LCD acts sort of like a bare memory chip. You put data on the bus (i.e. a total of 8 ones or zeros), an address on the address bus, then "turn on" ("assert" is the proper word) the "WR(ite)" pin, then blip (assert, then de-assert the EN(able pin). This causes the bit pattern on the data bus to be "pushed" into the storage location you selected.
The Arduino AVR chips (as well as most others) are able to set each pin to one of 3 states... LOW/OFF/0 , HIGH/ON/1 and finally OPEN/FLOATING/? (this last one is for READING something outside).
Now, concerning the LCD, it needs to be powered up and initialized (i.e. told how many characters and rows it has, which port to use if it's a serial/parallel type and whether it's in 4 bit or 8 bit mode, etc...).
Let's assume it's setup for 8 bits and parallel data mode (makes the following easier to understand).
Right now, the LCD cursor (i.e. the electronic position of display memory inside the LCD is set to row 0, column 0). Now, let;s say we want to display a letter "A" at that location.
First we put
** **01000001** **
on the 8 bit data port (hex 0x41, decimal 65, ASCII A), then using another pin, set the WR (write) pin to LOW (0 volts, logic 0) then finally using another pin, bring the EN (enable) pin high for a few microseconds (the datasheet for the LCD will tell us what the proper value is) and this causes the bit pattern 01000001 to be placed in LCD display memory.
In turn, the LCD looks up the internal bit pattern to generate a picture of an "A" on the LCD and turns on those segments (you don't need to worry about that - the LCD controller chip does that itself). Also, the internal LCD controller automatically moves it's internal "cursor" to the next location. Repeating the sequence above would result in "AA" being displayed.
Now, say you didn't want "A" but instead "B". You than have to send a command to the LCD to move it's cursor back one place, then "print" a space there (to erase the A), then move the cursor back AGAIN (remember the LCD automatically moves the cursor up one each time you print), then finally put your replacement character "B" in place.
All of these detailed, low level commands are fairly complicated and there are quite a few of them:
[b]Display Clear
Cursor Home
Entry Mode Set
Display Control
Cursor or display shift
Function control
Brightness Control (VFD only)
CG Ram address setting
DD Ram address setting
Busy flag / Address read
Write data to CG or DD ram
Read data from CG or DD ram[/b]
And, each one of these takes parameters that define exactly what the command DOES, as well as certain delays required to alloy a certain command to complete. If you make a SINGLE MISTAKE in a command or parameter, the LCD can go crazy or just go blank (or print gibberish).
So, rather than writing in this low level, the programmer tediously uses the data sheet for all the nitty-gritty and writes functions with understandable, real world names like "SetCursor" or "ClearScreen" or "SetBrightness" (vfd only), etc...
Finally all of these functions or subroutines are collected together into a "class" which is given a name (in Arduino land, the class name is "LiquidCrystal").
After including the "LiquidCrystal" library into your sketch, all you need to do is make a local copy of the class, then use it... for example:
// example - not working code
#include <LiquidCrystal> // pull in the LCD driver code library
static LiquidCrystal LCD (pins, rw, en, etc); // make a copy called "LCD" and define pins
void setup (void)
{
LCD.begin(16,2); // call function "init" and tell it 16 chars, 2 rows
LCD.print("Hello therx"); // oops typo!
LCD.print("\be."); // backspace then change x to e and add a period
}
See? All the library does is insulate you from the tedious, error prone attempts at controlling the LCD bit by bit, byte by byte. You COULD simply have all the LCD functions in your own sketch and not even use LiquidCrystal, but why? And, what if you wanted to use TWO LCD displays at once? Using the library, all you need to do is define another copy of the class (with the appropriately different pin numbers) and, say, call it "LCD1".
Then, everything done to LCD.xxx goes to the first one and LCD1.xxx goes to the second. No need to re-write any code!
Lastly, mostly FYI, the Arduino boards have 8 bit PORTS where one 8 bit value can be written in one operation and all 8 pins will assume the appropriate 1 or 0 value. This would be ideal for the LCD to simply use one whole port for it's data.
BUT, each BIT of the LCD data port can be on any pin, which means the LCD most probably will not be connected to a whole port. So, the library, in addition to all it has to do, ALSO has to keep track of EACH pin used by a particular port bit, then build up the data a bit at a time, then finally "push" it all into the LCD (the EN pin).
If it was required that the LCD data port be all connected to one PORT, and if the driver didn't have to juggle bits, it would probably be 8 to 10 times faster (but so what? How fast can YOU read?) 
Hope this makes some sense, and feel free to ask anything else.