Friday 24 June 2016

Snakes and displays part II: Writing text on the screen

Last time we learned how to talk with the I/O expander chip via I2C protocol. Now we'll see whether we can figure out how to get some text on the screen.

First we have to know how the data bits sent to the I2C chip are mapped to the pins of the LCD controller. Let's take another look at the schematic:


So, when a data byte is sent to the expander, it sets the pins P7...0 accordingly. Apparently the high bits 7...4 are mapped one-to-one to the 4 highest bits of the display's data bus. Bit 3 is controls the backlight of the screen through an NPN transistor as we concluded in the previous post. 

Bit 2 is connected to CS which is supposed to stand for Chip Select, I guess. In the datasheet the pin is called E for Enable and is used to indicate the chip that we want to read/write data to it. Bit 1 controls the RW (Read/Write) pin, indicating the direction of data, and bit 0 is RS (Register Select) which tells whether we want to access the settings of the display or the data (text) memory.

Okay but what about the low half DB3...0? According to the datasheet there are two interface modes. In the 8-bit mode the instructions and data are transferred using all the DB pins. However, it also has the 4-bit mode where the byte going through DB is split into two 4-bit parts that are consecutively pushed through DB7...4. So, an instruction consisting of RS, RW, and DB7...0 bit is converted to two bytes and sent through the I2C like this:


Pulling this all together, a hasty (or stupid, can't decide which one I am) person might think that one can control the screen by setting the E pin HIGH and sending the bytes over the I2C. Well, it turned out you can't.

I partly blame the schematic where the pin was misleadingly labelled as CS. To my best knowledge, the Chip Select pin usually works so that it makes the chip to listen incoming signals. Thus I thought it is simply enough to set it HIGH. If one thinks about this a bit further before implementing it (and wasting a couple of hours), they would have figured out why it can't work.

If my first though was the case, the chip couldn't differentiate between the proper input signal and, say, the transition between the aforementioned two bytes. There has to be a way to tell the chip that "The input is now settled. Go get it, boy!". Like with a clock pulse or something like that. The proper use of E pin is actually described in the datasheet, if I were to read it more carefully:

A plateful of timing spaghetti, s'il vous plâit!
So, in order to write on the LCD controller, I first have to set the RS and RW bits, then wait awhile, and send a pulse on the E pin that on the descending edge records the state of the data bus. Then using the write_bytes function from the previous post, the Python code to send an instruction looks like this:
 def write_instruction(cmd):  
     db74 = [cmd[2],cmd[3],cmd[4],cmd[5]]  
     db30 = [cmd[6],cmd[7],cmd[8],cmd[9]]  
     rwrs = [cmd[1],cmd[0]]  
   
     write_bytes([db74+[1,0]+rwrs, db74+[1,1]+rwrs, db74+[1,0]+rwrs])  
     write_bytes([db30+[1,0]+rwrs, db30+[1,1]+rwrs, db30+[1,0]+rwrs])
Both parts of the command are sent three times to the chip toggling the E bit. I didn't add any additional delays to the code, since controlling the GPIO with RPi.GPIO is rather slow (according to this the max frequency is 70 kHz = 14 microseconds per pulse) and the datasheet says the waiting times should be order of 10 nanoseconds.

However, the write_instruction function can't be used immediately after the power up, as the display will be in the 8-bit mode. The 4-bit mode has to be set with a separate command that in principle is 8-bit but it doesn't care the state of the unconnected pins. In addition, the datasheet describes a boot up sequence that has to be executed before the display can listen to other instructions. The function to initialize the display is
 def init_display():  
 
     #Init command  
     write_bytes([[0,0,1,1,1,0,0,0], [0,0,1,1,1,1,0,0], [0,0,1,1,1,0,0,0]])  
   
     #5 ms delay (required) 
     time.sleep(0.005)  
  
     #Init command repeated twice more according to datasheet 
     write_bytes([[0,0,1,1,1,0,0,0], [0,0,1,1,1,1,0,0], [0,0,1,1,1,0,0,0]])  
     write_bytes([[0,0,1,1,1,0,0,0], [0,0,1,1,1,1,0,0], [0,0,1,1,1,0,0,0]])  
   
     #Set 4-bit interface  
     write_bytes([[0,0,1,0,1,0,0,0], [0,0,1,0,1,1,0,0], [0,0,1,0,1,0,0,0]])  
   
With these we are finally all set! Let's test it out by writing some text on the screen. Sending an instruction 10XXXXXXXX writes a single character on the screen and increments/decrements the cursor position by 1. X:s stand for the 8-bit memory address of the character that are tabulated in the datasheet. For example A is 0100 0001 and & is 0010 0110 (these actually follow the ASCII coding, which will be useful later on). It is also possible to define own character graphics but I haven't yet tried it out. Here's my test code:
 #Init  
 init_display()  
 
 #Settings (part of the boot up sequence in the datasheet) 
 write_instruction([0,0,0,0,1,0,0,1,0,0]) #Function set (4-bit, 1-line, 5x10 dots)  
 write_instruction([0,0,0,0,0,0,1,0,0,0]) #Display Off, cursor off, blinking of cursor off  
 write_instruction([0,0,0,0,0,0,0,0,0,1]) #Clear display  
 write_instruction([0,0,0,0,0,0,0,1,1,0]) #Entry mode (left to right, don't shift display)  
   
 #set on display  
 write_instruction([0,0,0,0,0,0,1,1,0,0]) #Display on  
 
 #write text on the screen  
 write_instruction([1,0,0,0,1,1,1,0,0,0]) #8  
 write_instruction([1,0,0,0,1,0,1,1,0,1]) #-  
 write_instruction([1,0,0,1,0,0,0,0,1,0]) #B  
 write_instruction([1,0,0,1,0,0,1,0,0,1]) #I  
 write_instruction([1,0,0,1,0,1,0,1,0,0]) #T  
 write_instruction([1,0,0,0,1,0,0,0,0,0]) #  
 write_instruction([1,0,0,1,0,0,0,0,1,1]) #C  
 write_instruction([1,0,0,1,0,0,1,1,1,1]) #O  
 write_instruction([1,0,0,1,0,0,1,1,1,1]) #O  
 write_instruction([1,0,0,1,0,0,1,0,1,1]) #K  
 write_instruction([1,0,0,1,0,0,0,0,1,0]) #B  
 write_instruction([1,0,0,1,0,0,1,1,1,1]) #O  
 write_instruction([1,0,0,1,0,0,1,1,1,1]) #O  
 write_instruction([1,0,0,1,0,0,1,0,1,1]) #K  
   
 #Clear pins  
 GPIO.cleanup()  
   
and the magnificently unsurprising result:


Splendid. I also tried using the screen in the two-line mode but the contrast went really poor and turning the contrast screw didn't do a thing. I guess the screen requires more power in the two-line mode than the GPIO can provide. At least not through the 3.3V pin. With 5V volts the contrast was way better but then the I2C communication didn't work anymore (Rasp's IO pins are 3.3V in the HIGH state). I found out later that it is not necessarily a good idea to put 5V to GPIO pins anyway, so I advice not to mix the voltages and risk getting the smoke out of the GPIO bus.

References:

LCD 1602 Datasheet

Saturday 18 June 2016

Snakes and displays part I: Communication with I2C

Technology grows old so fast nowadays so it's really hard to keep up. My first "game console" was outdated even before it got out for consumers to enjoy! Maybe my timing was just poor. I couldn't get my system ready to be presented in this year's E3 Expo letting all those small garage companies like Nintendo, Sony, and Microsoft steal my market share.

For the next model I surely need to spice up specs too. What is the funniest thing one can make with three LEDs? Traffic lights probably, which gives away the biggest shortcoming of my console. But what would be the best choice for graphics? 3D's popular but I believe it is nothing more but a passing fad. Like who ever talks about how they remember particulary nice polygons or shaders from their favorite games? No one.

No, the best games are all about good gameplay, fascinating storylines or memorable characters like Super Mario, Link and Sonic. And what style of graphics has more characters than ASCII graphics? That's why I ordered this 16 x 2 character display LCD 1602 from DealExtreme for less than 5 euros.

Front side of the LCD with power and data wires attached.
The display consists of two clearly separable parts: the screen and its controlling electronics, and the I2C I/O expander circuit. The last one is quite handy, as it allows to move data from to the microcontroller to the display using only two wires instead of a half fuckton of pins you can see right below the screen. With 5 pin I/O of ATtiny85 (not counting the RESET pin), that's more than welcome.

Back side of the circuit. The I2C expander is the black circuit.
Before I can use this for anything, I must first learn how to use it. The manufacturer provides the C++ library to be used with Arduino development board. I read somewhere that it might work somehow with avr-gcc as well but I decided not to use it anyway. It will be more educative to figure out from scratch how to control the display.

To ease out my task, I'll begin programming with Raspberry Pi and Python instead of AVR and C. Compared to C language, Python is much more flexible and faster to write, making it ideal for prototyping. It is an interpreted language, meaning that it doesn't require compiling but on the downside isn't generally as fast to execute. However, there are powerful and efficient libraries for pretty much everything that makes Python useful for computationally demanding applications as well. At work I use Python for measurement control, data analysis and simulations, for example. And the best part is that it is free and also very easy to learn (you can find Python tutorials for example at Codecademy).

I decided to use RPi.GPIO module to control GPIO pins. On the display, there are four pins to be connected to the Pi. I connected VCC to pin 1, GND to pin 6 and the I2C lines SCL and SDA to pins 3 and 5 respectively. There are two different numbering schemes for the Pi's GPIO pins that RPi.GPIO supports. I decided to use the physical numbering i.e. how the pins are arranged on the board, as I find it easier to use.

Physical numbering of Raspberry Pi GPIO pins [source]
The connector on the screen was a bit bent during the delivery but the screen seemed to work ok as the backlight turned on when the screen was connected.

The display lights up immediately when connected to the Pi. 
Next I'd like to write some text on the display which took me two nights to figure out how. The first night was spend on understanding how to communicate through I2C protocol. I2C or IIC (Inter-Integrated Circuit) is a two-wire serial communication protocol invented by Philips Semiconductors. There are many sources out there where I2C is described in detail (e.g. this nice tutorial), so I'll just cover here the parts I need.

In the idle state both SDA (serial data) and SCL (serial clock) lines are in HIGH state. The communication starts when the master device (Raspberry Pi) sets the SDA line down while SCL is kept HIGH. This tells the slave (I2C chip of the display) to get ready to receive data. Normally SDA shouldn't change when SCL is HIGH.

As I2C supports multiple devices on the same bus, we need a way to tell which device we want to communicate with. Therefore the first byte send after the start condition contains a 7-bit address that matches the correct device. The 8th bit is a read/write bit indicating whether the master wants to send to or receive data from the slave (HIGH for read, LOW for write). The master sets the state of the SDA line according to each bit and sends the clock pulse on SCL when the bit is ready to be read.

After the 8th bit, the master gives the control of the SDA line to the slave. If there is a slave device with the correct address, it sets the SDA line LOW, indicating that it is ready to receive or send data. If not, then SDA should stay HIGH and the master decides how to continue. This as called as the acknowledge bit (ACK). The master reads the ACK bit and sends the 9th clock pulse on SCL.
If the ACK was LOW then it is time to read or write some data. The procedure for writing is pretty much the same as with the previous step; the master sets the SDA line and informs the slave with SCL pulses. After 8 bits the control of SDA is given to the slave and the ACK bit is read. Reading the data is the same, except now the slave controls the SDA line and the master sets the ACK bit. In both cases SCL is controlled by the master, however.

This can be repeated for arbitrary number of bytes. When the last byte is written/read, the master sets the SDA line LOW. Then it sets the SCL to the HIGH state and sets the SDA line HIGH as well. Change of SDA from LOW to HIGH when SCL is HIGH tells the slave that the communication is over.

And here is how I implemented it in Python:
 import RPi.GPIO as GPIO  
   
 #Pin numbering mode  
 GPIO.setmode(GPIO.BOARD)  
   
 #pins  
 SCL = 3  
 SDA = 5  
   
 #set pins to output  
 GPIO.setup(SCL,GPIO.OUT)  
 GPIO.setup(SDA,GPIO.OUT)  
   
 #set the bus to ready state  
 GPIO.output(SDA,True)  
 GPIO.output(SCL,True)  
   
 #chip address  
 ADDRESS = [0,1,0,0,1,1,1]  
   
 def write_bytes(array_of_bytes):  
  #add read/write bit to address and add the result to array   
  array_of_bytes = [ADDRESS+[0]] + array_of_bytes  
   
  #start condition (bus should be ready)  
  GPIO.output(SDA,False)   
   
  #write bytes  
  GPIO.output(SCL,False) #Clock LOW
   
  for byte in array_of_bytes:  
   for bit in byte:  
    GPIO.output(SDA,bit)  #Set data line     
    GPIO.output(SCL,True) #Clock HIGH  
    GPIO.output(SCL,False) #Clock LOW  
      
   if not byte is array_of_bytes[-1]:   
    #if the byte is not the last one -> acknowledge  
   
    GPIO.setup(SDA,GPIO.IN) #Give SDA contol to slave     
    GPIO.output(SCL,True)  #Clock HIGH
   
    print 'ACK: ', GPIO.input(SDA) #read the ACK bit   
   
    GPIO.output(SCL,False)  #Clock LOW  
   
    GPIO.setup(SDA,GPIO.OUT) #Regain the control of SDA  
    GPIO.output(SDA,True)  
   
   else:  
    #stop condition  
    GPIO.output(SDA,False)  
    GPIO.output(SCL,True)   
    GPIO.output(SDA,True)  
   

ADDRESS = 0100111 comes partly from the datasheet of PCF8574 which converts the serial data sent through I2C to parallel pin states and vice versa. The beginning 0100 is fixed but the last three bits are set through A2...0 pins of the chip. Luckily the manufacturer was kind enough to provide the circuit diagram of the display and the expander chip. Here's its essential part: 

We see all address pins are connected to VCC, so the ending of the address is 111. Now we have everything to test the code out.

Before getting into the display itself, I first wanted to try something simple. From the datasheets and the diagram above, we can see that the pin P3 of the I2C chip controls the backlight LED of the display through a transistor. So writing something like 00000000 to the chip would turn it off and 00001000 back on. Using my I2C code it looks like this:
 write_bytes([[0,0,0,0,0,0,0,0]])  #turn backlight OFF
 time.sleep(1)                     #1 sec delay, remember to import time
 write_bytes([[0,0,0,0,1,0,0,0]])  #turn backlight ON
   
 GPIO.cleanup() #Clears the pin setup on exit 

And the light turns off and on again! I guess I would qualify as an IT support person. I didn't took video this time but I'm sure you can imagine how it looks like.

So the I2C communication is up and running. In the next post I'll take this step further and actually write something on the screen.

---

Ps. Now, when I'm writing these things down, they seem and feel straightforward but it's very misleading. There are some tedious stuff that are necessary to go through, like gathering information from datasheets and internet, but typically most of the time goes to hunting down errors and mistakes. Usually stupid ones.

Like here I first thought that I don't have to read the ACK bit, because I'll notice it anyway whether the screen works or not. I'll save a bit of effort just neglecting it. As a result I spend all night (until 4 am) trying to fix my code in vain. The next morning I started to think that maybe giving the control to the slave is actually important and I got it working in less than 10 minutes.

Lesson learned: Respect the protocol.


References:

LCD 1602 datasheet

PCF8574 datasheet

I2C on Wikipedia: https://en.wikipedia.org/wiki/I²C

Python logo https://commons.wikimedia.org/wiki/File:Python_logo_and_wordmark.svg

Tuesday 14 June 2016

The Torment of Alfred McSilvernuts: The "Game"

We now know how to control outputs and read input signals. Together with timers and interrupts that's all we need to build a simple controller and program something to control with it. No, let's top it up a notch and make a whole console! Let's start off with the connection diagram:

Circuit diagram of my very first "console".
Isn't it glorious? Powered by a single ATtiny85, it has a two button "controller" for one player and a mind-blowing three pixel monochrome LED "display". Yeah, those quotation marks are there for a reason.


So, my plan is to use the PB2..0 pins as output ports to light up some red LEDs and the PB3 and PB4 pins to read the button state. Clear as the fifth beer of the evening after passing through the system, no? Only noteworthy thing is that I have connected the buttons straight to the ground as I'm making my job easier by utilizing the internal pull-up resistors of the chip.

The circuit assembled on two breadboards.

Our "console" needs also a "game". With three LEDs and two buttons one doesn't make a new Megaman but how about this:  

A successful business man, Alfred McSilvernuts, woke up to evil laughter.

"Mwahaha, thy time was short but the list of thy sins is out of this world. However, as a nice guy that I am, I give thee one last chance to repent."

"NEVER,", yelled McSilvernuts "for my heart is black as my ass is wide! Those poor bastards deserve all the shit I managed to sell them and all their hard-earned money belongs rightfully to my bottomless bank account!"

"Very well. For now on, thou shall spend thy eternity trapped in this one-dimensional doom. No matter which direction thou travel, no matter how far and for how long, thou shall find thee circling around, always ending up in this very place thou now stand on."

Then there was a loud, low roar, like the heaven itself had emptied its bowels after a near-fatal combination of hard liquor and questionable fast food. The roar faded, ending up in complete silence and emptiness. Except for Alfred McSilvernuts and his prison.

Shitty backstory but so will be the "game" itself; the player moves Alfred (i.e. a lit LED) around his prison (a row of LEDs). There's no goal, no way to lose, no way to win. The quotation marks have found their place. However, "The Torment of Alfred McSilvernuts" is an important step as it will contain all the core principles (except for sound) that I'll be using in the future games and devices as well.

Let's start off with the topic of last post: timers and interrupts. To make games run smoothly, I want run them at a constant frame rate. To do this I'll use the Timer/Counter0 Overflow interrupt to tell the processor how often it should execute the frame worth of game logic, update the screen, read the controller input, and so on.

First, we'll add our preamble to the source code:
 #define F_CPU 1000000UL  
 #define L_BTN 0b10000  
 #define R_BTN 0b01000  
   
 #include <avr/interrupt.h>  
 #include <avr/io.h>  

In addition to the lines present in the last example, I defined two constants corresponding to the pins PB4 and PB3 to which the buttons are connected. Not necessary but makes the code more readable.
 volatile uint8_t player_position = 0;  
 volatile uint8_t old_state = 0;  

Here I initialise two variables. One holding the position of the player character and the other holding the state of the pins during the previous frame.
 void init_timer(){  
   
   //Init counter  
   TCNT0 = 0;  
     
   //set the clock, prescaler 64  
   TCCR0B |= (1 << CS01) | (1 << CS00);  
   
   //enable overflow interrupt  
   TIMSK |= 1 << TOIE0;  
   
 }  

For readability, I put the initialisation code of the Timer/Counter0 to a separate funtion. With 1 MHz clock signal and prescaler of 64, I'll get for the overflow frequency 1MHz/256/64 = 61.035...Hz, which is quite nice a frame rate. And now for the interesting part:
 ISR(TIM0_OVF_vect){  
   //Read the pin states  
   uint8_t pin_state = PINB;  
   
   //Move left if L_BTN down and the button state has changed  
   if (L_BTN & ~pin_state & old_state)  
     if (player_position == 0)  
       player_position = 2;  
     else  
       player_position--;  
   
   //Move left if R_BTN down and the button state has changed  
   if (R_BTN & ~pin_state & old_state)  
     if (player_position == 2)  
       player_position = 0;  
     else  
       player_position++;  
   
   //replace the old pin state with the current one  
   old_state = pin_state;  
     
   //Clear "screen"  
   PORTB &= ~0b00111;  
   
   //Put "player" on the "screen"  
   PORTB |= (1 << player_position);  
   
 }  

The Interrupt Service Routine for the Timer0 Overflow is the heart of the program. Called every frame, it contains all the game logic and input/output code. In the beginning, we define a new local variable pin_state and assign the current state of the pins to it. The pins are accessed through PINB register and it contains the states of both the input and output pins.

Next comes the game logic; we check whether a button is pressed and move the player character accordingly. When a button is pressed, it connects the corresponding input pin to the ground or LOW signal. When not, the pin is set HIGH (why? keep reading..). To get the state of the left button, we take bitwise AND (&) between L_BTN and pin_state. This is true ONLY if the bit in pin_state corresponding to one defined in L_BTN is HIGH. But wait! That's not what we want. We want to know whether it is LOW. This is done simply taking the bitwise inverse (LOWs to HIGH and vice versa) of pin_state with the NOT (~) operator.

In addition, we take the bitwise AND with the old_state variable. This ensures that the expression is true only when the old_state of the pin was HIGH (i.e. the button was not pressed down) during the previous frame. Now the if clause reacts only when the button is pressed down and doesn't care whether the player holds it down or not.

If the following conditions apply, then the program moves the player. Since my high-tech display has three LEDs, the player has three possible positions marked with numbers 0, 1, and 2. When the left button is pressed, the program tries to move the player character left by subtracting one from the position. If the player is far left (position is 0), it jumps over the edge to the far right.

The same procedure is repeated for the right button and after that the old_state is replaced with the current one to be passed to the next frame. Then the display is cleared by setting all the output pins to zero. The NOT operator flips 0b00111, where I marked the output pins with ones, to 0b11000. Now when I take the bitwise AND with PORTB, it leaves the first two bits as they are and sets the last three to LOW.

The last step is to place the player on screen. This is done by shifting 1 or 0b001 the number of bits indicated by player_position and taking bitwise OR with PORTB to set the output and after the frame is completed and the program moves to wait for the next interrupt.

And here's the result! Bon Appétit!




Tuesday 7 June 2016

Pull up my input

Erm, change of plans. Last time I referred to my plant watering machine but a couple of complications turned up. First, I still haven't gotten all the parts I need. Sometimes you can save a good deal of money by ordering components from China but as a downside it might take a while.

The second problem is with the power supply design. In brief, I need two different voltages: +5 volts for the controlling circuit (microcontroller and relays) and +12 volts for the solenoid valve that dispenses water from the reservoir to the plant. I have an AC adapter which produces +12 volts. At first I planned to use a couple of resistors as a voltage divider to drop it down to 5V but I soon realised it would be a stupid idea. My circuit doesn't require much energy but the voltage divider as a power supply would increase the power consumption manyfold - such a waste! For the sake of sanity (and my electricity bill), I now plan to use a voltage regulator instead. Unfortunately I don't have any which means that I'll have to visit the electronic parts store again..


It seems that my future in robotic agriculture has to wait. I did get some buttons via mail though (and a free disposable razor too!). Thus it's a good moment to go through how to use an AVR's I/O pins to read input.  

In C, the direction of the pins is once again set through the Data Direction Register DDRB. As a reminder, setting a bit to HIGH sets the corresponding pin as output and LOW as input. Say, if I want to use PB4 and PB3 as inputs and PB2..0 as outputs, I'll set DDRB to

 DDRB = 0b00111;  

..aand, that's it! To read the state of the pins into the variable x, for example, I simply read PINB register:

 x = PINB;  

Now x contains the states of all the pins as they were at the moment of reading. As PINB gives out both input and output pin states, we still need to pick out the right pin. To check the state of the pin PBn, one can do it for example followingly:

 x & (1 << n)  

1 << n shifts 1 to left n positions. For example if I want to read the state of PB3,
1 << 3 = 0b01000. Bitwise AND with x then results to 0 if the corresponding state is LOW and to a non-zero value if it is HIGH.

In principle this is sufficient to read the input but there's one more very beneficial trick, especially to be used with buttons. With PB4 and PB3 as inputs, I also set

 PORTB = 0b11000;  

This sets the output pins off and the input pins...on? In some sense, yes. Setting PORTB bits of input pins controls whether so called internal pull-up resistors are used or not.

The pull-up resistor raises the pin voltage when the switch is opened.

A pull-up resistor has a large resistance and it is connected between the input pin and the HIGH voltage. If the pin is disconnected, a tiny current goes through this resistor and sets the state of the pin HIGH. However, if pin is connected to the ground, the large resistance eats away all the voltage allowing the pin to go LOW.

Unless the pin is connected to a logic LOW or HIGH at all times, it is necessary to use pull-up resistors. Otherwise some electrons might be left to hang around on the pin when they shouldn't be. This potentially makes the state of the pin go nuts, which can mess up with the behaviour of the circuit.

AVR are quite handy that they have internal pull-ups, otherwise one would have to connect extra resistors to the pins to take care of those pesky, free-loading charge carriers. These internal pull-ups make using buttons on AVR very simple; all you have to do is to connect a button between ground and the pin. Then set the corresponding PORTB bit to one to enable the pull-up and your microcontroller is all ready to read some button states!