fab Academy logo

Week 12 - Output Devices


Brief


The brief for this weeks assignment was to make a circuit with an output device and program it


LCD


For this weeks assignment I chose to make the LCD driver board. Neil did warn us that the LCD board was not easy to program and there is a lot of effort that goes into ensuring the timing and communication works correctly. However for my final project I think it would be useful to be able to program a LCD that could make up a display for a control panel on the brew box.

Milling the board


The LCD driver board is not a board I chose to re-design and customise. The point of this assignment for me is to get the circuit to work and spend as much time as possible on understanding the programming element.

That being the case I used the layout and c files from here, and milled the board.



I then stuffed the board and programmed it using Neil's c code.


I then had a look through the code to try and identify the sections. A common theme of programs is that 90% of the scripts sets up the communication, timing and defines variables and functions. WIth the last 10% making up the executable sequence. There is a lot pf the program below which I struggle to understand and I think in comparison to arduino examples this is quite complex stuff.

What I do understand about programming is that it isn't all about writing script from scratch. It is about finding bits of code that address certain issues and using that within a customised/modified program. This program is a key example of that notion. the highlighted text in red is the part that I want to change to modify the output of the LCD. The rest of the program is important to look at and question but at my current stage I am not able to dissect each section.


//
// hello.LCD.44.c
//
// LCD hello-world
//
// set lfuse to 0x7E for 20 MHz xtal
//
// Neil Gershenfeld
// 11/14/10
//
// (c) Massachusetts Institute of Technology 2010
// Permission granted for experimental and personal use;
// license for commercial sale available from MIT.
//

#include <avr/io.h>
#include <util/delay.h>
#include <avr/pgmspace.h>

#define output(directions,pin) (directions |= pin) // set port direction for output
#define set(port,pin) (port |= pin) // set port pin
#define clear(port,pin) (port &= (~pin)) // clear port pin
#define pin_test(pins,pin) (pins & pin) // test for port pin
#define bit_test(byte,bit) (byte & (1 << bit)) // test for bit set

#define LCD_port PORTA
#define LCD_direction DDRA
#define DB7 (1 << PA0)
#define DB6 (1 << PA1)
#define DB5 (1 << PA2)
#define DB4 (1 << PA3)
#define E (1 << PA4)
#define RS (1 << PA5)

#define long_delay() _delay_ms(1000) // delay before redraw
#define lcd_delay() _delay_ms(10) // delay between commands
#define strobe_delay() _delay_us(1) // delay for strobe

//
// lcd_putchar
//    put character in lcdbyte
//
void lcd_putchar(char lcdbyte) {
   //
   // set RS for data
   // 
   set(LCD_port, RS);
   //
   // output high nibble
   //
   if bit_test(lcdbyte, 7)
      set(LCD_port, DB7);
   else
      clear(LCD_port, DB7);
   if bit_test(lcdbyte, 6)
      set(LCD_port, DB6);
   else
      clear(LCD_port, DB6);
   if bit_test(lcdbyte, 5)
      set(LCD_port, DB5);
   else
      clear(LCD_port, DB5);
   if bit_test(lcdbyte, 4)
      set(LCD_port, DB4);
   else
      clear(LCD_port, DB4);
   //
   // strobe E
   //
   strobe_delay();
   set(LCD_port, E);
   strobe_delay();
   clear(LCD_port, E);
   //
   // wait
   //
   lcd_delay();
   //
   // output low nibble
   //
   if bit_test(lcdbyte, 3)
      set(LCD_port, DB7);
   else
      clear(LCD_port, DB7);
   if bit_test(lcdbyte, 2)
      set(LCD_port, DB6);
   else
      clear(LCD_port, DB6);
   if bit_test(lcdbyte, 1)
      set(LCD_port, DB5);
   else
      clear(LCD_port, DB5);
   if bit_test(lcdbyte, 0)
      set(LCD_port, DB4);
   else
      clear(LCD_port, DB4);
   //
   // strobe E
   //
   strobe_delay();
   set(LCD_port, E);
   strobe_delay();
   clear(LCD_port, E);
   //
   // wait and return
   //
   lcd_delay();
   }
//
// lcd_putcmd
//    put command in lcdbyte
//
void lcd_putcmd(char lcdbyte) {
   //
   // clear RS for command
   // 
   clear(LCD_port, RS);
   //
   // output command bits
   //
   PORTA = lcdbyte;
   //
   // strobe E
   //
   strobe_delay();
   set(LCD_port, E);
   strobe_delay();
   clear(LCD_port, E);
   //
   // wait and return
   //
   lcd_delay();
   }
//
// lcd_putstring
//    put a null-terminated string in flash
//
void lcd_putstring(PGM_P message) {
   static uint8_t index;
   static char chr;
   index = 0;
   while (1) {
      chr = pgm_read_byte(&(message[index]));
      if (chr == 0)
         return;
      lcd_putchar(chr);
      ++index;
      }
   }
//
// lcd_init
//    initialize the LCD
//
void lcd_init() {
   //
   // power-up delay
   //
   lcd_delay();
   //
   // initialization sequence
   //
   lcd_putcmd(DB5+DB4);
   lcd_putcmd(DB5+DB4);
   lcd_putcmd(DB5+DB4);
   //
   // 4-bit interface
   //
   lcd_putcmd(DB5);
   //
   // two lines, 5x7 font
   //
   lcd_putcmd(DB5);
   lcd_putcmd(DB7);
   //
   // display on
   //
   lcd_putcmd(0);
   lcd_putcmd(DB7+DB6+DB5);
   //
   // entry mode
   //
   lcd_putcmd(0);
   lcd_putcmd(DB6+DB5);
   }

int main(void) {
   //
   // main
   //
   // set clock divider to /1
   //
   CLKPR = (1 << CLKPCE);
   CLKPR = (0 << CLKPS3) | (0 << CLKPS2) | (0 << CLKPS1) | (0 << CLKPS0);
   //
   // initialize LCD pins
   //
   clear(LCD_port, DB7);
   output(LCD_direction, DB7);
   clear(LCD_port, DB6);
   output(LCD_direction, DB6);
   clear(LCD_port, DB5);
   output(LCD_direction, DB5);
   clear(LCD_port, DB4);
   output(LCD_direction, DB4);
   clear(LCD_port, E);
   output(LCD_direction, E);
   clear(LCD_port, RS);
   output(LCD_direction, RS);
   //
   // initialize LCD
   //
   lcd_init();
   //
   // main loop
   //
   while (1) {
      // 
      // go to zero position
      //
      lcd_putcmd(0);
      lcd_putcmd(DB5);
      //
      // print first line from flash
      //
      static const char line1[] PROGMEM = "Hello to";
      lcd_putstring((PGM_P) line1);
      //
      // move to second line
      //
      lcd_putcmd(DB7+DB6);
      lcd_putcmd(0);
      //
      // print second line from flash
      //
      static const char line2[] PROGMEM = "the world";
      lcd_putstring((PGM_P) line2);
      //
      // pause
      //
      long_delay();
      //
      // clear display
      //
      lcd_putcmd(0);
      lcd_putcmd(DB4);
      }
   }


   
  For the modification of the LCD I chose to write "will" on line 1 and "this work?" in line 2. Hey presto it worked. Further time needed to understand  the setup code used in this..