Engineering 15 Lab 3

PIC Microcontroller I

17 October 2005

Brian Park, Alex Benn, Aron Dobos

 

Figure 0. Proof of correct operation for Part 5.

Abstract

 

The PIC microcontroller was programmed in C to perform various functions including controlling an LCD and reading voltages.

 

Task 1

 

Code

 

#include "H:\e15\lab3\piclab1.h"

 

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_DISABLED);

   setup_timer_2(T2_DISABLED,0,1);

 

   output_high(pin_b0);

   delay_ms(250);

   output_low(pin_b0);

   delay_ms(250);

   output_high(pin_b0);

 

   while(1)

   {

      if (input(pin_a4)) {

         output_low(pin_b1);

      } else {

         output_high(pin_b1);

      }

   }

 

 

}

Description

 

The code first turns on LED D2, waits 250 milliseconds, turns it off, and then after another 250 millisecond delay turns it back on.  Then RB0 stays on for the rest of the program.  Inside the loop, the LED D3 is turned on if the input switch connected to RA4 is closed (i.e. pulled to ground).  When the switch is not pressed, it is open, and pulled high through a pullup resistor. 

 

The LED RB0 glows slightly because when no code is running RB0 left floating (HiZ).  There is a current path through R7, R19, and R21 through LED D2 to ground, and since the resistance is very high, the LED is barely lit.  When the switch S3 is depressed, RB0 is pulled to ground, and thus there is no voltage differential across the LED, turning it off.

Figure 1. PICDEM2 Board Connections

 

Task 2

 

Code

 

#include "H:\e15\lab3\piclab1.h"

 

 

void main() {

   int adcChan0;

  

   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_DISABLED);

   setup_timer_2(T2_DISABLED,0,1);

  

   setup_adc(ADC_CLOCK_DIV_32);

   setup_adc_ports( ALL_ANALOG );

   set_adc_channel(0);

     

  

   output_high(pin_b0);

   delay_ms(250);

   output_low(pin_b0);

   delay_ms(250);

   output_high(pin_b0);

 

   while(1) {

// read the A/D converter

      adcChan0 = read_adc();

 

// divide the result by 16 (shift right 4 times) and output to port B

      output_b(adcChan0>>4);

     }

}

 

Description

 

After toggling the led attached to RB0, the code enters an infinite loop that continuously reads the A/D converter, divides the result by 16, and outputs the result directly to port B, such that the upper four bits of the ADC result are shown in the lower four bits to which the LEDs RB3..0 are connected.

 

Proof

 

adcChan0

Binary (Port B Bold)

0

0000 0000

39

0010 0111

77

0100 1101

143

1000 1111

201

1100 1001

255

1111 1111

Table 1. A/D Converter value and result at Port B

 

Also see the attached movie for proof of the code’s correct operation.

 

Task 3

 

Memory location 0x06 is Port B data, and 0x86 is Port B HiZ enable (tristate).  When the tristate buffer is in high impedance (mem[0x86]=0x0C), the tri-state switch is open, and the value read at port B is unknown.  Since the tristate mask is 0x0C, the lower two bits are still passed but the upper two bits are high impedance state.  Therefore, the upper two LEDs RB3 and RB2 are off, and RB1 and RB0 show the value at location 0x06.  The memory map table in the reference manual confirms this.

 

0x06

0x86

Port B

0x00

0x00

0000

0x0F

0x00

1111

0x0A

0x00

1010

0x05

0x00

0101

0x00

0x0C

0000

0x0F

0x0C

0011

0x0A

0x0C

0010

0x05

0x0C

0001

Table 2. Direct hardware manipulation results

 

Task 4

 

Code

 

#include "\\data-software\Classes\Natural Sciences + Engineering\Engineering\Transfer\e15baah\lab3\piclab1.h"

 

/*

   LCD control lines A3..A1

      A1    A2    A3

      E     R/W   RS

 

   LCD data lines PIC::D3..D0 <==> LCD::DB7..DB4

*/

 

#define LCD_EN pin_a1

#define LCD_RW pin_a2

#define LCD_RS pin_a3

 

 

 

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_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()

{

   /* turn on LED RB0 to indicate LCD initialization */

   output_high(pin_b0);

 

 

      /* 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);

 

   /* turn off LED RB0 to indicate LCD ready */

   output_low(pin_b0);

}

 

#define  NDIGITS 5

 

void main()

{

   int i;

   int adcval = 0;

   int digits[NDIGITS];

   int32 dval;

 

   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_DISABLED);

   setup_timer_2(T2_DISABLED,0,1);

 

   setup_adc(ADC_CLOCK_DIV_32);

   setup_adc_ports( AN0 );

   set_adc_channel(0);

 

   lcd_init();

   lcd_clear();

   lcd_home();

  

   printf( lcd_putc, "Hello World!\n");

   printf( lcd_putc, "Hello to you too");

}

Description

 

The main() statement at the bottom of this block of code sets up the PIC, initializes the LCD, and then displays the text “Hello World!” on the LCD. It also handles newline characters in order to print text on the second line of the LCD.

 

Task 5

 

Code

 

#define  NDIGITS 5

 

void main()

{

   int i;

   int adcval = 0;

   int digits[NDIGITS];

   int32 dval;

 

   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_DISABLED);

   setup_timer_2(T2_DISABLED,0,1);

 

   setup_adc(ADC_CLOCK_DIV_32);

   setup_adc_ports( AN0 );

   set_adc_channel(0);

 

   lcd_init();

   lcd_clear();

   lcd_home();

  

   printf( lcd_putc, "VOLTMETER!\n");

   delay_ms(1000);

   printf( lcd_putc, "IS COOL!");

   delay_ms(1000);

  

   lcd_line2();

   printf(lcd_putc,"We deserve an A+");

  

   lcd_line1();

 

   while(1)

   {

      adcval = read_adc();

      lcd_home();

 

      dval = (((int32)adcval)*5000l)/255l;

 

 

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

      {

         digits[i] = (int)(dval%10l);

         dval = dval / 10;

      }

 

      printf( lcd_putc, "Volts=%d.%d%d V", digits[3], digits[2], digits[1]);

      delay_ms(10);

   }

}

Description

 

The main() statement at the bottom of this block of code sets up the PIC, initializes the LCD, and then displays the voltage from the potentiometer on the LCD. In order to convert the eight-bit value given by read_adc() to a number representative of the actual voltage, the code multiplies adcval by 5000/255, then selects the ones, tenths and hundredths digits from this value. Note that long integers are used (specified by an ‘l’ after each number) in order to avoid overflow and type casting problems.