Engineering 15 Lab 4

Alex al-Benn, Brian van der Park, Aroné Dobois

9 February 1972

 

Figure 1. Our Source of Inspiration

 

Task 1

 

Description

 

This code causes the LED on RB1 to change state once per second.  It configures the timer to use the external oscillator, use a prescaler of 4:1.  The timer is initialized with a count value of 57344, so that in 8192 counts it will overflow.  In the ISR, the timer register is reset to this value, resulting in oscillations every 1 second.  In the ISR, the led attached to RB1 is toggled.

 

Code

 

#INT_TIMER1 // interrupt triggered when timer overflows

void timer1_isr() {

   output_toggle(pin_b1);

   set_timer1(57344);      // 65536 - 32768 / 4 = 57344; overflow once every 32768 / 4=8192 counts

}

 

 

void main() {

 

   setup_adc_ports(NO_ANALOGS);

   setup_adc(ADC_OFF);

   setup_psp(PSP_DISABLED);

   setup_spi(FALSE);

   setup_timer_0(RTCC_INTERNAL|RTCC_DIV_1);

  

   setup_timer_1(T1_EXTERNAL | T1_EXTERNAL_SYNC | T1_DIV_BY_4 | T1_CLK_OUT);  //turn on timer1

   setup_timer_2(T2_DISABLED,0,1);

  

   set_timer1(57344);   //initialize timer1

  

      enable_interrupts(INT_TIMER1);      // turn on timer1 interrupt

      enable_interrupts(GLOBAL);    // enable interrupts globally.

  

   while (1) { }

}

 

Task 2

 

Description

 

Timer 1 is configured to be driven by the external 32768Hz oscillator, and with a prescaler of 4:1.  Since we want the colons to blink each second, they must be toggled every 0.5 second.  As a result, the timer register is initialized to 65536-4096=61440, so that the timer fires twice as rapidly as in part 1.  In the ISR, the colon flag is toggled, and every other time the ISR is run, the number of seconds is incremented.

 

In the main() loop the number of minutes is incremented when seconds reaches 60, and the number of hours is also appropriately incremented or reset.  Finally, the time is formatted by a printf statement and displayed on the LCD using the code developed in the previous lab.

 

Code

 

#include "C:\Documents and Settings\abenn1\My Documents\Lab 4\lab 4.h"

 

// insert LCD code here (see task 3/4/Extension for code)

 

int hours;

int minutes;

int seconds;

short colonBlink;

 

#INT_TIMER1 // interrupt triggered when timer overflows

void timer1_isr() {

   if (colonBlink == 0)

      seconds++;

   colonBlink = !colonBlink;

   set_timer1(61440);      // 32768 / 4 = 8192; overflow once every 4096 counts

}

 

void main() {

   int ch;

   setup_adc_ports(NO_ANALOGS);

   setup_adc(ADC_OFF);

   setup_psp(PSP_DISABLED);

   setup_spi(FALSE);

   setup_timer_0(RTCC_INTERNAL|RTCC_DIV_1);

   setup_timer_1(T1_EXTERNAL | T1_EXTERNAL_SYNC | T1_DIV_BY_4 | T1_CLK_OUT);  //turn on timer1

   setup_timer_2(T2_DISABLED,0,1);

  

   set_timer1(61440);   //initialize timer1

  

   hours = 12;

   minutes = 59;

   seconds = 55;

   colonBlink = 1;

  

   lcd_init();

   lcd_clear();

  

   enable_interrupts(INT_TIMER1);   // turn on timer1 interrupt

   enable_interrupts(GLOBAL); // enable interrupts globally.

  

   while (1) {

      if (seconds == 60) {

         seconds = 0;

         minutes++;

         if (minutes == 60) {

            minutes = 0;

            hours++;

            if (hours == 13) {

               hours = 1;

            }

         }

      }

     

      lcd_line2();

     

      ch = colonBlink==1 ? ':' : ' ';

      printf(lcd_putc, "%2d%C%02d%C%02d", hours, ch, minutes, ch, seconds);

      delay_ms(50);

     

   }

}

 

 

Tasks 3, 4, Extension

 

Description

 

For Task 3, we developed an algorithm to determine which of the sixteen keys on a keypad are pressed when the user presses a key. When a key is pressed, one of the upper four bits on port B changes state, triggering an interrupt. The ISR scans these top four bits to determine which column the keypress occurred in; then it switches the lower four bits on port B to inputs and reads those in, to determine the row of the keypress. The key pressed is determined from this row-column combination. The key is decoded by a table lookup, and the associated character is output to the display.

 

Task 4 consisted of combining Tasks 2 and 3, so the codebases were simply merged to create the code below, with the exception of a few LCD formatting calls, which were changed in order to be sure that the output is displayed in the correct locations.

 

For the Extension, we introduced a small state machine to enable the user to set the time. The user enters hour-set mode by pressing C, and is prompted for input; upon pressing C again, the hour is set and minute-set mode is entered with an appropriate prompt. Pressing C a third time sets the minutes, resets the seconds to 0, and exits the state machine. A few extra utility functions for storing character input and state data were added as well as code to decode character input to a numerical value.

 

Code

 

#include "\\data-software\Classes\Natural Sciences + Engineering\Engineering\Transfer\e15baah\Lab4\lab 4 part3.h"

#include <stdlib.h>

//Use fast_io so port B pins don't change direction when we access the

//entire byte at once.  (i.e., output_b(32) doesn't make all pins outputs)

#use fast_io(B)

 

#define LCD_EN pin_a1

#define LCD_RW pin_a2

#define LCD_RS pin_a3

 

#define LINE1 1

#define LINE2 2

 

void write4( int data)

{

   output_low( LCD_RW );

   output_d( data );

 

   delay_us(2);

   output_high( LCD_EN );

   delay_us(3);

   output_low( LCD_EN );

   delay_us(2);

}

 

void write8(int data)

{

      write4( (data & 0xF0) >> 4 );

      write4( data & 0x0F );

}

 

void writecmd8(int cmd)

{

      output_low( LCD_RS );

      write8(cmd);

   delay_ms(10);

}

 

void writedata8(int data)

{

      output_high( LCD_RS );

      write8(data);

   delay_ms(10);

}

 

 

void lcd_clear()

{

   writecmd8(0x01);

   delay_ms(15);

}

 

void lcd_home()

{

   writecmd8(0x02);

   delay_ms(15);

}

 

 

void lcd_ddram_addr(int addr)

{

   /* lower 7 bits are data address */

   writecmd8(addr | 0x80);

}

 

void lcd_moveto(int line, int c)

{

   if (line==2)

      c += 0x40;

   lcd_ddram_addr(c);

}

 

void lcd_line1()

{

   lcd_ddram_addr(0x00);

}

 

void lcd_line2()

{

   lcd_ddram_addr(0x40);

}

 

void lcd_putc(int ch)

{

   if (ch == '\n')

   {

      lcd_line2();

   }

   else

   {

      writedata8(ch);

   }

}

 

 

void lcd_init()

{

      /* clear all port data */

   output_low( LCD_EN );

   output_low( LCD_RW );

   output_low( LCD_RS );

 

   output_d( 0 );

 

 

   /* 4-bit LCD initialization sequence */

   delay_ms(16); /* wait more than 15ms */

 

   write4( 0x03 );

   delay_ms(5); /* wait more than 4.1ms */

 

   write4( 0x03 );

   delay_ms(1); /* wait more than 100us */

 

 

   write4( 0x03 );

   delay_ms(5);

   write4( 0x02 );

   delay_ms(5);

 

 

 /* hardware should be initialized at this point */

 

 /* configure the lcd properties */

 

   writecmd8( 0x01 ); /* clear display */

   writecmd8( 0x28 ); /* function set 4 bit IF N=1(2line) F=0(5x7) */

   writecmd8( 0x06 ); /* entry mode set  I/D=1 S=0 */

   writecmd8( 0x0C ); /* display on, C=0 B=0 */

 

   lcd_ddram_addr(0x00);

}

 

int key_press_event, ctr, key_code;

#INT_RB

void key_isr() {

   int i,scan;

 

   // debounce: waits for the input to stabilize before

   // rechecking whether a key was actually pressed

   delay_ms(1);

   if (input_b() == 0xF0)

      return;

 

   // keep a count of the interrupts

   ctr++;

 

   // read the upper four bits of port B

   key_code = input_b() & 0xF0;

 

   //scan through setting B0 through B3 set high

   scan = 1;

   for(i = 0; i < 4; i++)

   {

      output_b(scan);

      if (key_code != (input_b() & 0xF0)) {

         break;

      }

      scan = scan << 1;

   }

   output_b(0x00);

 

   // ‘or’ together the lower four bits with the upper four bits

   key_code |= (~scan & 0xF);

 

   // signal to the main loop that

   // a key was pressed

   key_press_event = 1;

}

 

 

// experimentally determined key map table

static int keypad_map[16] = { 0x77, 0x7b, 0x7d, 0x7e, 0xb7, 0xbb, 0xbd, 0xbe, 0xd7, 0xdb, 0xdd, 0xde, 0xe7, 0xeb, 0xed, 0xee};

static char ascii_map[16] = { '1',  '2',  '3',  'A',  '4',  '5',  '6',  'B',  '7',  '8',  '9',  'C',  '*',  '0',  '#',  'D' };

 

 

// translation utility

char getascii(int keycode)

{

   int i;

   for (i=0;i<16;i++)

      if (keycode==keypad_map[i])

         return ascii_map[i];

 

   return '\0';  // if the key was not found, return NULL character

}

 

 

// **********  CLOCK code from part 2 ****************** //

 

int hours;

int minutes;

int seconds;

short colonBlink;

 

#INT_TIMER1 // interrupt triggered when timer overflows

void timer1_isr() {

   if (colonBlink == 0)

      seconds++;

   colonBlink = !colonBlink;

   set_timer1(61440);      // 32768 / 4 = 8192; overflow once every 4096 counts

}

 

int set_state;

char buf[16];

int bufct;

 

// ******  EXTENSION Code for setting the time ****** //

 

enum {SET_HOURS, SET_MINUTES, SET_NONE};

 

// clear the input buffer

void bufclear()

{

   bufct = 0;

}

 

// add a character to the input buffer

// if we are in a SET_HOURS or SET_MINUTES state

void bufcharin(int ch)

{

   if (set_state == SET_NONE)

      bufct = 0;

   else if (bufct < 15)

      buf[bufct++] = ch;  

}

 

// returns a numerical value of the

// string in the buffer, clamped to the

// min, max parameters

int getbufval(int min, int max)

{

   int val;

  

   if (bufct < 16)

      buf[bufct] = '\0';

   else

      return 0;

     

   val = atoi(buf);

  

   if (val < min)

      val = min;

    

   if (val > max)

      val = max;

  

   return val;

}

 

 

void main()

{

   int ch;

   int evtctr=0;

   setup_adc_ports(NO_ANALOGS);

   setup_adc(ADC_OFF);

   setup_psp(PSP_DISABLED);

   setup_spi(FALSE);

 

      /* SETUP THE TIMERS */

   setup_timer_0(RTCC_INTERNAL|RTCC_DIV_1);

   setup_timer_2(T2_DISABLED,0,1);

   setup_timer_1(T1_EXTERNAL | T1_EXTERNAL_SYNC | T1_DIV_BY_4 | T1_CLK_OUT);  //turn on timer1

 

   set_timer1(61440);   //initialize timer1

 

      /* INITIALIZE THE CLOCK */

   hours = 12;

   minutes = 59;

   seconds = 55;

   colonBlink = 1;

 

      /* INTIALIZE THE LCD */

   lcd_init();

   lcd_clear();

   lcd_home();

  

      /* INITIALIZE THE STATE VARIABLES (key event, clock set)

   key_code = 0;

   key_press_event=0;

   ctr = 0;

  

   set_state = SET_NONE;

   bufct = 0;

  

      /* welcome message */

   printf(lcd_putc,"What time is it?");

   delay_ms(1000);

   lcd_clear();

 

      /* configure the port B  */

   SET_TRIS_B(0xF0); // B7,B6,B5,B4 are inputs - B3,B2,B1,B0 are outputs

   port_b_pullups(0xF0); // B7,B6,B5,B4 have pullups.  B3,B2,B1,B0 do not

   output_b(0x00); // Set B3, B2, B1, B0 to zero.

 

      /* enable interrupts, obviously */

   enable_interrupts(INT_TIMER1);   // turn on timer1 interrupt

   enable_interrupts(INT_RB); // turn on B port interrupt

   enable_interrupts(GLOBAL); // enable interrupts globally.

 

   lcd_home();

   while(1) {

 

      if (key_press_event)

      {

         evtctr++;

         key_press_event=0;

 

         // clear the screen when ‘A’ is pressed

         if (getascii(key_code)=='A') {

            lcd_clear();

            evtctr = 0;

         }

         else if (getascii(key_code)=='C')

         {

            // state machine implementing time set feature

            switch(set_state)

            {

            case SET_HOURS:

               hours = getbufval(1, 12);

               bufclear();

               lcd_clear();

               lcd_line2();

               printf(lcd_putc, ">MIN, then C: ");

               set_state = SET_MINUTES;

               evtctr = 0;

               break;

              

            case SET_MINUTES:

               minutes = getbufval(0, 59);

               seconds = 0;

               bufclear();

               lcd_clear();

               lcd_line2();

               printf(lcd_putc, "Time set.");

               set_state = SET_NONE;

               evtctr = 0;

               break;

              

            case SET_NONE:

               evtctr = 0;

               lcd_clear();

               lcd_line2();

               printf(lcd_putc, ">HR, then C: ");

               set_state = SET_HOURS;

               bufclear();

               break;

            }          

         } else {

            // read and translate the character

            ch = getascii(key_code);

            bufcharin(ch);  // add it to the input buffer

           

            // set the LCD cursor position

            if (set_state == SET_NONE)

               lcd_moveto(LINE2, evtctr);

            else

               lcd_moveto(LINE2, 13+evtctr);

 

            lcd_putc(ch);

         }

            // scroll back to beginning of line        

         if (evtctr == 15)

            evtctr=0;

      }

 

 

      // clock update code from task 2

      if (seconds == 60)

      {

         seconds = 0;

         minutes++;

         if (minutes == 60) {

            minutes = 0;

            hours++;

            if (hours == 13) {

               hours = 1;

            }

         }

      }

 

      // print the time

      lcd_moveto(LINE1, 0);

      ch = colonBlink==1 ? ':' : ' ';

      printf(lcd_putc, "%2d%C%02d%C%02d", hours, ch, minutes, ch, seconds);

      delay_ms(10);

   }

}