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.