![]() |
HowTo.AddAnLcdDisplayViaTheI2C HistoryHide minor edits - Show changes to markup July 04, 2008, at 07:12 AM
by --
Changed lines 816-817 from:
PORTB=0x00; to:
PORTB=SWITCH_MASK; Changed lines 917-918 from:
to:
Changed lines 919-923 from:
to:
Changed lines 922-926 from:
to:
Changed lines 925-927 from:
to:
Added line 971:
Check_ADC(); June 28, 2008, at 08:17 PM
by -- Removal of LCDs from wikiword
Changed lines 3-8 from:
I wanted to interface a generic HD44780 based LCD display to my NSLU2. There are many different displays of different dimensions available meeting this standard, and its relatively easy to programme. These LCDs? are relatively cheap, starting at a few dollars for a small device up to a few tens of dollars for the larger ones. When thinking about connecting any hardware peripheral to the NSLU2, the problem is there are no general purpose I/O pins available. I've seen projects using the USB interface to connect LCDs?, but only hints of projects using the I2C? bus. In this article, I'm going to detail how I implemented an I2C? LCD interface. Typical LCDs? don't come with I2C? interfaces built in. However cheap PIC microcontrollers can implement an I2C? bus for a couple of dollars. I chose to use a 16F88, an 18 pin DIP with two eight bit ports available. My design uses two of these 16 I/O pins for the I2C? interface, seven for the LCD interface and leaves seven for you to implement other cool stuff such as A/D inputs, keypad reading inputs or general purpose outputs. Using these other 16F88 I/O lines, I've added a battery voltage monitor and some general purpose switches accessible from the NSLU2 software through the I2C? bus. to:
I wanted to interface a generic HD44780 based LCD display to my NSLU2. There are many different displays of different dimensions available meeting this standard, and its relatively easy to programme. These LCDs are relatively cheap, starting at a few dollars for a small device up to a few tens of dollars for the larger ones. When thinking about connecting any hardware peripheral to the NSLU2, the problem is there are no general purpose I/O pins available. I've seen projects using the USB interface to connect LCDs, but only hints of projects using the I2C? bus. In this article, I'm going to detail how I implemented an I2C? LCD interface. Typical LCDs don't come with I2C? interfaces built in. However cheap PIC microcontrollers can implement an I2C? bus for a couple of dollars. I chose to use a 16F88, an 18 pin DIP with two eight bit ports available. My design uses two of these 16 I/O pins for the I2C? interface, seven for the LCD interface and leaves seven for you to implement other cool stuff such as A/D inputs, keypad reading inputs or general purpose outputs. Using these other 16F88 I/O lines, I've added a battery voltage monitor and some general purpose switches accessible from the NSLU2 software through the I2C? bus. June 27, 2008, at 07:18 AM
by --
Changed line 28 from:
to:
Changed line 31 from:
to:
Changed line 223 from:
to:
Changed lines 229-230 from:
printf(", %\02x", buf[n]);
to:
printf(", \%02x", buf[n]);
Changed lines 982-983 from:
Other tipsto:
June 27, 2008, at 07:09 AM
by --
Changed lines 78-79 from:
to:
Changed lines 173-176 from:
Secondly, here's an example of a programme to read a data structure off the I2C? bus. This is tailored to interpret the data structure returned by the PIC code below. to:
June 27, 2008, at 07:06 AM
by --
Changed line 107 from:
fprintf (stderr, "Usage: %s STRING [STRING] ...\n", argv[0]); to:
fprintf (stderr, "Usage: \s STRING [STRING] ...\n", argv[0]); Changed line 164 from:
printf("Write error! Returned d - %s\n", n, length, strerror(errno));
to:
printf("Write error! Returned \%d instead of \%d - \%s\n", n, length, strerror(errno));
Changed lines 218-224 from:
printf("d: Read error! Returned d instead of d\n", i, n, BUFSIZE);
printf("d: ADC1?=04x, ADC2?=04x", i, (buf[0]<<8 + buf[1]), (buf[2]<<8 + buf[3]));
printf(", SW1?(=c count=d)", (buf[4] & 0x80)?'1':'0', buf[4]&0x7f);
printf(", SW2?(=c count=d)", (buf[5] & 0x80)?'1':'0', buf[5]&0x7f);
printf(", SW3?(=c count=d)", (buf[6] & 0x80)?'1':'0', buf[6]&0x7f);
to:
printf("\%d: Read error! Returned \%d instead of \%d\n", i, n, BUFSIZE);
printf("d: ADC1?=\%04x, ADC2?=\%04x", i, (buf[0]<<8 + buf[1]), (buf[2]<<8 + buf[3]));
printf(", SW1?(=\%c count=\%d)", (buf[4] & 0x80)?'1':'0', buf[4]&0x7f);
printf(", SW2?(=\%c count=\%d)", (buf[5] & 0x80)?'1':'0', buf[5]&0x7f);
printf(", SW3?(=\%c count=\%d)", (buf[6] & 0x80)?'1':'0', buf[6]&0x7f);
Changed lines 226-227 from:
printf(", 02x", buf[n]);
to:
printf(", %\02x", buf[n]);
June 27, 2008, at 07:05 AM
by --
Changed lines 160-161 from:
printf ("Writing s\n", length, argv[i]) ;
to:
printf ("Writing \%d characters: \%s\n", length, argv[i]) ;
June 27, 2008, at 07:04 AM
by --
Changed lines 160-161 from:
printf ("Writing d characters: s\n", length, argv[i]) ;
to:
printf ("Writing s\n", length, argv[i]) ;
Changed line 164 from:
printf("Write error! Returned d instead of d - s\n", n, length, strerror(errno));
to:
printf("Write error! Returned d - %s\n", n, length, strerror(errno));
June 27, 2008, at 07:03 AM
by --
Changed line 107 from:
fprintf (stderr, "Usage: %37s STRING [STRING] ...\n", argv[0]); to:
fprintf (stderr, "Usage: %s STRING [STRING] ...\n", argv[0]); June 27, 2008, at 07:02 AM
by --
Changed line 107 from:
fprintf (stderr, "Usage: %s STRING [STRING] ...\n", argv[0]); to:
fprintf (stderr, "Usage: %37s STRING [STRING] ...\n", argv[0]); June 27, 2008, at 07:02 AM
by --
Changed line 107 from:
fprintf (stderr, "Usage: s STRING [STRING] ...\n", argv[0]); to:
fprintf (stderr, "Usage: %s STRING [STRING] ...\n", argv[0]); June 27, 2008, at 06:57 AM
by --
Changed line 107 from:
fprintf (stderr, "Usage: %s STRING [STRING] ...\n", argv[0]); to:
fprintf (stderr, "Usage: s STRING [STRING] ...\n", argv[0]); Changed lines 160-161 from:
printf ("Writing s\n", length, argv[i]) ;
to:
printf ("Writing d characters: s\n", length, argv[i]) ;
Changed line 164 from:
printf("Write error! Returned d - %s\n", n, length, strerror(errno));
to:
printf("Write error! Returned d instead of d - s\n", n, length, strerror(errno));
Changed lines 218-224 from:
printf("%d: Read error! Returned d\n", i, n, BUFSIZE);
printf("%d: ADC1?=%04x, ADC2?=%04x", i, (buf[0]<<8 + buf[1]), (buf[2]<<8 + buf[3]));
printf(", SW1?(=d)", (buf[4] & 0x80)?'1':'0', buf[4]&0x7f);
printf(", SW2?(=d)", (buf[5] & 0x80)?'1':'0', buf[5]&0x7f);
printf(", SW3?(=d)", (buf[6] & 0x80)?'1':'0', buf[6]&0x7f);
to:
printf("d: Read error! Returned d instead of d\n", i, n, BUFSIZE);
printf("d: ADC1?=04x, ADC2?=04x", i, (buf[0]<<8 + buf[1]), (buf[2]<<8 + buf[3]));
printf(", SW1?(=c count=d)", (buf[4] & 0x80)?'1':'0', buf[4]&0x7f);
printf(", SW2?(=c count=d)", (buf[5] & 0x80)?'1':'0', buf[5]&0x7f);
printf(", SW3?(=c count=d)", (buf[6] & 0x80)?'1':'0', buf[6]&0x7f);
Changed lines 226-227 from:
printf(", %02x", buf[n]);
to:
printf(", 02x", buf[n]);
June 27, 2008, at 06:55 AM
by -- update of software source code
Deleted line 82:
Changed line 92 from:
to:
Changed lines 99-170 from:
int fd;
int length ;
int n, i;
char *s ;
unsigned char buf[BUFSIZE+1];
if (argc < 2)
{
fprintf (stderr, "Usage: %s STRING [STRING] ...\n", argv[0]);
exit (1) ;
}
if ((fd = open("/dev/i2c-0", O_RDWR)) < 0)
{
printf("Error opening i2c port!\n");
exit (1);
}
if (ioctl(fd,I2C?_SLAVE,I2C?_ADDRESS) < 0)
{
printf("Error setting i2c address!\n");
exit (1);
}
if (ioctl(fd,I2C?_RETRIES,2) < 0)
{
printf("Error setting i2c retries!\n");
exit (1);
}
for (i=1 ; i < argc ; i++)
{
length = strlen (argv[i]) ;
if (!length)
continue ;
for (s=argv[i],length=0 ; *s && (length < BUFSIZE) ;)
{
if (*s == '\\')
{
switch (*++s)
{
case '0': buf[length++]=0x00 ; s++ ; break ;
case '1': buf[length++]=0x01 ; s++ ; break ;
case '2': buf[length++]=0x02 ; s++ ; break ;
case '3': buf[length++]=0x03 ; s++ ; break ;
case '4': buf[length++]=0x04 ; s++ ; break ;
case 'n': buf[length++]=0x0a ; s++ ; break ;
case 'r': buf[length++]=0x0d ; s++ ; break ;
default: ; break ; //Ignore the slash if following char is unknown
}
}
else
buf[length++]=*s++ ;
}
if ((i < argc) && (length < BUFSIZE))
buf[length++]=' ' ;
n = write(fd, buf, length);
printf ("Writing s\n", length, argv[i]) ;
if (n != length)
{
printf("Write error! Returned d - %s\n", n, length, strerror(errno));
exit (1);
}
}
close (fd);
return 0 ;
to:
int fd;
int length ;
int n, i;
char *s ;
unsigned char buf[BUFSIZE+1];
if (argc < 2)
{
fprintf (stderr, "Usage: %s STRING [STRING] ...\n", argv[0]);
exit (1) ;
}
if ((fd = open("/dev/i2c-0", O_RDWR)) < 0)
{
printf("Error opening i2c port!\n");
exit (1);
}
if (ioctl(fd,I2C?_SLAVE,I2C?_ADDRESS) < 0)
{
printf("Error setting i2c address!\n");
exit (1);
}
if (ioctl(fd,I2C?_RETRIES,2) < 0)
{
printf("Error setting i2c retries!\n");
exit (1);
}
for (i=1 ; i < argc ; i++)
{
if (strlen(argv[i]) == 0)
continue ;
length = 0 ;
if (i > 1))
buf[length++]=' ' ;
for (s=argv[i] ; *s && (length < BUFSIZE) ;)
{
if (*s == '\\')
{
switch (*++s)
{
case '0': buf[length++]=0x00 ; s++ ; break ;
case '1': buf[length++]=0x01 ; s++ ; break ;
case '2': buf[length++]=0x02 ; s++ ; break ;
case '3': buf[length++]=0x03 ; s++ ; break ;
case '4': buf[length++]=0x04 ; s++ ; break ;
case 'n': buf[length++]=0x0a ; s++ ; break ;
case 'r': buf[length++]=0x0d ; s++ ; break ;
default: ; break ; //Ignore the slash if following char is unknown
}
}
else
buf[length++]=*s++ ;
}
n = write(fd, buf, length);
printf ("Writing s\n", length, argv[i]) ;
if (n != length)
{
printf("Write error! Returned d - %s\n", n, length, strerror(errno));
exit (1);
}
}
close (fd);
return 0 ;
Added lines 173-234:
Secondly, here's an example of a programme to read a data structure off the I2C? bus. This is tailored to interpret the data structure returned by the PIC code below. #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <fcntl.h> #include <errno.h> #include <string.h> #include <sys/types.h> //include <linux/i2c-dev.h> #include "i2c-dev.h" #define I2C?_ADDRESS 0x32 #define BUFSIZE 10 int main (int argc, char **argv) { int fd; int n, i; unsigned char buf[BUFSIZE+1]; if ((fd = open("/dev/i2c-0", O_RDWR)) < 0) { printf("Error opening i2c port!\n"); exit (1); } if (ioctl(fd,I2C?_SLAVE,I2C?_ADDRESS) < 0) { printf("Error setting i2c address!\n"); exit (1); } if (ioctl(fd,I2C?_RETRIES,2) < 0) { printf("Error setting i2c retries!\n"); exit (1); } for (i=1 ; i < 8 ; i++) { sleep (1) ; n = read (fd, buf, BUFSIZE); if (n != BUFSIZE) printf("%d: Read error! Returned d\n", i, n, BUFSIZE); printf("%d: ADC1?=%04x, ADC2?=%04x", i, (buf[0]<<8 + buf[1]), (buf[2]<<8 + buf[3])); printf(", SW1?(=d)", (buf[4] & 0x80)?'1':'0', buf[4]&0x7f); printf(", SW2?(=d)", (buf[5] & 0x80)?'1':'0', buf[5]&0x7f); printf(", SW3?(=d)", (buf[6] & 0x80)?'1':'0', buf[6]&0x7f); for (n=7 ; n < BUFSIZE ; n++) printf(", %02x", buf[n]); printf ("\n"); } close (fd); return 0 ; } Added line 271:
$(INSTALL_BIN) $(PKG_BUILD_DIR)/read_i2c $(1)/bin/ Added line 273:
$(INSTALL_BIN) $(PKG_BUILD_DIR)/test_i2c $(1)/bin/ Changed line 284 from:
BINS = echo_lcd to:
BINS = echo_lcd read_i2c Changed lines 305-306 from:
to:
read_i2c: read_i2c.o Changed lines 325-328 from:
In order for these functions to work properly, you need to customise the C code with the dimensions of the LCD you are going to use. Use the You also need to choose an i2c address for the PIC and specify this in the to:
In order for these functions to work properly, you need to customise the C code with the dimensions of the LCD you are going to use. Use the Changed lines 331-335 from:
// 16F88 programme to drive a hitachi compatible LCD display from the // i2C bus // // There are plenty of spare inputs and outputs for additional functions ////////////////////////////////////////////////////////////////////// to:
// 16F88 programme to drive a hitachi compatible LCD display, read switches // and monitor some voltages..... Interface to NSLU2 or host micro via I2C? bus // Define the I2C? address for this device Changed line 336 from:
to:
Changed lines 340-349 from:
// This is the assignment of 16F88 pins I chose // PORT A bit 0 = D4 on LCD // PORT A bit 1 = D5 on LCD // PORT A bit 2 = D6 on LCD // PORT A bit 3 = D7 on LCD // PORT A bit 4 = NC // PORT A bit 5 = pulled high with 10k // PORT A bit 6 = NC // PORT A bit 7 = NC to:
////////////////////////////////////////////////////////////////////// // Copyright Stefan Keller-Tuberg (C) 2008 Added lines 343-365:
// This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // Visit <http://www.gnu.org/licenses/> to view the terms of the license. ////////////////////////////////////////////////////////////////////// // PIC 16F88 pin assignments as follows: // // PORT A bit 0 = D4 on LCD // PORT A bit 1 = D5 on LCD // PORT A bit 2 = D6 on LCD // PORT A bit 3 = Analogue Input - tied to 3.3V rail with 22k // PORT A bit 4 = Analogue Input - Battery Voltage via 1:2 voltage divider // PORT A bit 5 = pulled to VDD via 22k (This is normally reset input) // PORT A bit 6 = NC // PORT A bit 7 = D7 on LCD // Changed line 367 from:
to:
Changed lines 370-374 from:
to:
// PORT B bit 4 = CK on I2C? (I added my own 4k7 pullup to 3.3V - didn't work without) // PORT B bit 5 = Switch3 input. Pulled to VDD via 22k. Switched low. // PORT B bit 6 = Switch2 input. Pulled to VDD via 22k. Switched low. // PORT B bit 7 = Switch1 input. Pulled to VDD via 22k. Switched low. // // VDD connected to +5V USB port supply via a 1N4001 diode - to drop voltage so // I2C? inputs will work OK when pulled to only 3.3V ////////////////////////////////////////////////////////////////////// Added lines 379-380:
// Define PORTB bits used for LCD control Changed lines 384-387 from:
// counters for debugging purposes unsigned char crashed, crash_cause, info, last_i2c ; ////////////////////////////////////////////////////////////////////// to:
// Define PORTA bits used for the four LCD data bits #define LCD4? F0 #define LCD5? F1 #define LCD6? F2 #define LCD7? F7 // Define PORTA bits used for Analogue inputs #define ANALOGUE0? F3 #define ANALOGUE1? F4 // Define PORTB bits used for Digital inputs #define SW1? F5 #define SW2? F6 #define SW3? F7 #define SWITCH_MASK 0xe0 Changed lines 404-409 from:
TRISA.F0 = 0; //Set Bits 3-0 as OUTPUTS TRISA.F1 = 0; TRISA.F2 = 0; TRISA.F3 = 0; PORTA = c ; //Bits 3-0 = high order data nibble to:
TRISA.LCD4? = 0; TRISA.LCD5? = 0; TRISA.LCD6? = 0; TRISA.LCD7? = 0; PORTA.LCD4? = c.F0 ; PORTA.LCD5? = c.F1 ; PORTA.LCD6? = c.F2 ; PORTA.LCD7? = c.F3 ; Changed line 414 from:
to:
Changed line 419 from:
to:
Changed line 425 from:
to:
Changed lines 428-429 from:
to:
// Assumes Port A bits 7, 2-0 are the ONLY outputs (ie bits 6-3 are INPUTS) Changed lines 434-439 from:
TRISA.F0 = 1; //Set Bits 3-0 as INPUTS TRISA.F1 = 1; TRISA.F2 = 1; TRISA.F3 = 1; to:
Changed line 444 from:
to:
Changed line 447 from:
to:
Changed line 452 from:
busy = PORTA ; //Read the busy flag in bit 3 (bit 7 of LCD) to:
busy = PORTA ; //Read the busy flag in bit 7 (bit 7 of LCD) Changed lines 459-460 from:
} while (busy.F3); to:
Changed lines 463-464 from:
write_nibble_to_lcd (c & 0x0f, RS) ; to:
write_nibble_to_lcd (c, RS) ; Changed line 467 from:
to:
Changed line 474 from:
to:
Changed line 480 from:
to:
Changed line 482 from:
to:
Changed line 486 from:
to:
Changed line 491 from:
to:
Changed line 496 from:
to:
Changed line 499 from:
to:
Changed line 509 from:
to:
Changed lines 512-528 from:
to:
// The following is a bogus structure definition.... // the array is fed back to the NSLU2 when read off I2C? #define BATT_VOLT 0 // 2 byte ADC count #define REF_VOLT (BATT_VOLT+2) // 2 byte ADC count #define SWITCH1? (REF_VOLT+2) // 1 byte count of debounced presses #define SWITCH2? (SWITCH1?+1) // 1 byte count of debounced presses #define SWITCH3? (SWITCH2?+1) // 1 byte count of debounced presses #define SWITCHES (SWITCH3?+1) // 1 byte count current state #define STRUCT_LEN (SWITCHES+1) // Should be total of 8 unsigned char txbuf [STRUCT_LEN] ; unsigned char tx_index ; // counters for debugging purposes unsigned char crashed, crash_cause, info, last_i2c ; Changed line 533 from:
to:
Changed line 540 from:
to:
Changed line 549 from:
to:
Added line 555:
tx_index = 1 ; // byte to be transmitted Changed lines 557-558 from:
do to:
do Changed lines 560-561 from:
SSPCON.F7 = 0 ; // Clear the write collision flag
SSPBUF = 0xaa ; // Just return AA for the time being
to:
SSPCON.F7 = 0 ; // Clear the write collision flag
SSPBUF = txbuf[0] ; // Return first byte of array
Changed line 567 from:
to:
Changed line 571 from:
SSPBUF = 0x55 ; // Just return 55 for the time being to:
SSPBUF = txbuf[tx_index] ; // Return next byte of array Added lines 573-574:
tx_index++ ; // point to next byte in the array next time Changed line 587 from:
to:
Changed line 603 from:
to:
Changed line 605 from:
to:
Changed line 610 from:
to:
Changed line 619 from:
to:
Changed line 621 from:
to:
Changed line 624 from:
to:
Changed line 628 from:
to:
Changed line 643 from:
to:
Changed line 646 from:
to:
Changed line 649 from:
else if (c < 5) to:
else if (c <= LINES) Changed line 665 from:
to:
Changed line 668 from:
to:
Changed line 671 from:
to:
Changed line 677 from:
to:
Changed line 680 from:
enqueue_string (const unsigned char *s) to:
enqueue_char (unsigned char c) Changed lines 682-688 from:
while (*s) to:
PIE1?.F3 = 0; // Disable int F3=I2C? int rxbuf[rx_head++] = c ; rx_head &= BUFFER_MASK ; rx_len++ ; PIE1?.F3 = 1; // Enable int F3=I2C? int if (rx_len > BUFFER_LEN) Changed lines 690-700 from:
PIE1?.F3 = 0; // Disable int F3=I2C? int rxbuf[rx_head++] = *s++ ; rx_head &= BUFFER_MASK ; rx_len++ ; PIE1?.F3 = 1; // Enable int F3=I2C? int if (rx_len > BUFFER_LEN) { crashed = 4 ; //flag error return ; } to:
crashed = 4 ; //flag error
return ;
Changed line 694 from:
to:
Changed line 696 from:
enqueue_number (const unsigned char c) to:
enqueue_string (const unsigned char *s) Added lines 698-704:
while (*s)
enqueue_char (*s++) ;
}
void
enqueue_number (const unsigned char c)
{
Changed line 707 from:
to:
Changed line 709 from:
to:
Changed line 714 from:
to:
Changed line 718 from:
to:
Changed lines 723-728 from:
//============================================================================== #define MAGIC 0x3d unsigned char num_boots ; unsigned char magic ; // Will hold a magic number so we know a cold boot to:
Changed line 725 from:
main() to:
flush_display_buffer() Changed lines 727-757 from:
char i ; // Osc = bits 6,5,4 OSCCON=0x7c; // Internal osc: 0x5c=2MHz, 0x7c=8MHz while (OSCCON.F2 == 0) ; // Wait for osc to stabilise restart: OPTION_REG=0x03; //0x03=Prescaler=1:256 0x80=low..pullup enabled INTCON=0; //Disable interrupts PIR1?=0; //Clear all interrupt flags TRISA=0; //Set Port A to all outputs TRISB=0x12; //B4 and B1 are inputs (I2C?) PORTA=0x00; PORTB=0x00; WDTCON =0x17 ; // Enable watchdog with max timeout value = approx 2s ANSEL=0; //0 = PortA? bit is used for digital I/O ADCON1?=0x07 ; // Disable A/D SSPCON = 0x36 ; // F7=0 WCOL Write Collision Detect bit // F6=0 SSPOV Receive Overflow Indicator bit // F5=1 Synchronous Serial Port Enable // F4=1 When set at 0, I2C? clock pin held low to stretch clock // F3-0=0110: I2C? slave mode: 7 bits address SSPADD = I2C?_ADDRESS << 1 ; // 7 bit address in bits 7--1 SSPSTAT = 0 ; // Clear all status bits PIE1?.F3 = 1; // F6=ADC int, F3=I2C? int if (magic != MAGIC) to:
asm {
CLRWDT // clear the watchdog timer
}
if (rx_len)
Changed lines 733-735 from:
crash_cause=0 ; // used for debugging interrupt routine with print statements
num_boots = 0 ;
magic = MAGIC ;
to:
printchar (rxbuf[rx_tail++]) ; //THIS IS ONLY PLACE WE TOUCH rx_tail
rx_tail &= BUFFER_MASK ;
PIE1?.F3 = 0; // Disable int F3=I2C? int
rx_len-- ;
PIE1?.F3 = 1; // Enable int F3=I2C? int
Changed lines 739-754 from:
else
num_boots++ ;
crashed = 0 ; // We're not crashed at the moment!
rx_head = rx_tail = rx_len = 0 ;
INTCON.F6 = 1 ; // enable peripheral interrupts
INTCON.F7 = 1 ; // enable all unmasked interrupts
TXSTA = 0 ; // Turn off transmit from UART
RCSTA = 0 ;
CMCON = 0x07 ; // Disable comparators
Delay_ms (100); // Provide a little time for the LCD to initialise
asm {
CLRWDT // Clear the watchdog timer
to:
}
//==============================================================================
unsigned char debounce ;
void
Check_ADC()
{
unsigned char i ;
if (ADCON0?.F2 == 0) //zero means converstion finished
{
i = ADCON0? ;
i &= 0x38 ; // Only interested in the channel select bits
if (i == 0x18)
{
txbuf [REF_VOLT] = ADRESH ;
txbuf [REF_VOLT+1] = ADRESL ;
ADCON0?=0xa1 ;// convert voltage on A4 next
}
else
{
txbuf [BATT_VOLT] = ADRESH ;
txbuf [BATT_VOLT+1] = ADRESL ;
ADCON0?=0x99 ;// else convert voltage on A3 next
}
Delay_us (100); // Wait a bit for analogue input to settle
ADCON0?.F2 = 1; // Start the conversion
Changed lines 769-796 from:
write_nibble_to_lcd (0x03,0); // Function set 8 bit mode
Delay_ms (5); // Wait at least 4.1ms
write_nibble_to_lcd (0x03,0); // Function set 8 bit mode
Delay_ms (1); // Wait at least 100us
write_nibble_to_lcd (0x03,0); // Function set 8 bit mode
Delay_ms (1); // Wait at least 100us
write_nibble_to_lcd (0x02,0); // NOW FINALLY Function set 4 bit mode
Delay_ms (1); // Wait at least 100us
write_byte_to_lcd (0x28, 0); //And repeat, doing full 8 bits of function cmd
write_byte_to_lcd (0x0c, 0); //Display ON (04), Curson ON(02), Blinking(01)
write_byte_to_lcd (0x01, 0); //Clear display
write_byte_to_lcd (0x06, 0); //Entry mode Increment(02) Noshift(01)
write_byte_to_lcd (CLEAR_AND_FIRST_ROW, 0);
line = 1;
column = 0 ;
// Display the welcome message
enqueue_number (LINES) ;
enqueue_string ("x") ;
enqueue_number (COLUMNS) ;
enqueue_string (" lcd @addr ") ;
enqueue_number (I2C?_ADDRESS) ;
enqueue_string ("\nVersion 1");
// Give some indication if there's been a crash
if (num_boots)
to:
}
void
Long_Delay (unsigned char time)
{
unsigned char i ;
for (i=0 ; i < time ; i++)
Deleted lines 777-782:
enqueue_string ("\rBoot count: ");
enqueue_number (num_boots) ;
}
for (;;)
{
Changed line 779 from:
CLRWDT // clear the watchdog timer to:
CLRWDT // clear the watchdog timer Changed lines 781-828 from:
if (rx_len)
{
printchar (rxbuf[rx_tail++]) ;
rx_tail &= BUFFER_MASK ;
PIE1?.F3 = 0; // Disable int F3=I2C? int
rx_len-- ;
PIE1?.F3 = 1; // Enable int F3=I2C? int
}
if (crashed)
{
crash_cause = crashed ; // Remember why we crashed
write_byte_to_lcd (CLEAR_AND_FIRST_ROW, 0);
string_to_lcd ("Cause:") ;
hexbyte_to_lcd (crash_cause);
string_to_lcd (" last:") ;
hexbyte_to_lcd (last_i2c) ;
string_to_lcd (" info:") ;
hexbyte_to_lcd (info);
string_to_lcd (" boots:") ;
hexbyte_to_lcd (num_boots);
write_byte_to_lcd (SECOND_ROW, 0);
string_to_lcd ("Buf len:") ;
decbyte_to_lcd (rx_len);
write_byte_to_lcd (' ', 1);
for (i=0 ; i < 14 ; i++)
hexbyte_to_lcd (rxbuf[(rx_tail+i)&BUFFER_MASK]);
// The following kludge is to get around the non-reentrant version of C
// If there's an error inside the Interrupt, this flag will be set
// That lets us display something - and this loop will cause an infinte wait
// while (crashed) ; // Infinite loop if an error has been set in the ISR
// This will trip the watchdog timer some time later
// Delay for 15 seconds
for (i=0 ; i < 150 ; i++)
{
Delay_ms (100);
asm {
CLRWDT // clear the watchdog timer
}
}
goto restart ;
}
to:
Delay_ms (100); Changed lines 785-789 from:
Example code for the NSLU2to:
//==============================================================================
#define MAGIC 0x3d
unsigned char num_boots ;
unsigned char magic ; // Will hold a magic number so we know a cold boot
void
main()
{
char i, j ;
OSCCON=0x7c; // Internal osc: 0x4c=1MHz, 0x5c=2MHz, 0x6c=4MHz, 0x7c=8MHz
while (OSCCON.F2 == 0) ; // Wait for osc to stabilise
restart:
OPTION_REG=0x03; //0x03=Prescaler=1:256 0x80=low..pullup enabled
INTCON=0; //Disable interrupts
PIR1?=0; //Clear all interrupt flags
WDTCON =0x17 ;// Enable watchdog with max timeout value = approx 2s
TRISA=0; //Set Port A as outputs, except analogue inputs
TRISA.ANALOGUE0?=1 ;
TRISA.ANALOGUE1?=1 ;
TRISB=0x12 | SWITCH_MASK; //B4 and B1 are inputs (I2C?)
PORTA=0x00;
PORTB=0x00;
ANSEL=0; //Set Port A as outputs, except analogue inputs
ANSEL.ANALOGUE0?=1 ;
ANSEL.ANALOGUE1?=1 ;
ADCON1?=0xc0 ; // Bit7=1=right justify, 6=1=clokc/2, 5,4=00=Vref
ADCON0?=0x81 ; // Bit7,6=10 Osc/32, Bit5-3=Chan select, bit0=1 A-D enabled
SSPCON = 0x36 ; // F7=0 WCOL Write Collision Detect bit
// F6=0 SSPOV Receive Overflow Indicator bit
// F5=1 Synchronous Serial Port Enable
// F4=1 When set at 0, I2C? clock pin held low to stretch clock
// F3-0=0110: I2C? slave mode: 7 bits address
SSPADD = I2C?_ADDRESS << 1 ; // 7 bit address in bits 7--1
SSPSTAT = 0 ; // Clear all status bits
PIE1?.F3 = 1; // F6=ADC int, F3=I2C? int
if (magic != MAGIC)
{
crash_cause=0 ; // used for debugging interrupt routine with print statements
num_boots = 0 ;
magic = MAGIC ;
}
else
num_boots++ ;
crashed = 0 ; // We're not crashed at the moment!
rx_head = rx_tail = rx_len = 0 ;
INTCON.F6 = 1 ; // enable peripheral interrupts
INTCON.F7 = 1 ; // enable all unmasked interrupts
TXSTA = 0 ; // Turn off transmit from UART
RCSTA = 0 ;
CMCON = 0x07 ; // Disable comparators
Long_Delay (2) ; // Wait 200ms seconds
txbuf[SWITCHES] = PORTB & SWITCH_MASK ; // read the initial switch inputs
txbuf[SWITCH1?] = 0 ;
txbuf[SWITCH2?] = 0 ;
txbuf[SWITCH3?] = 0 ;
debounce=0 ; // Initialise debouncing algorithm OFF
write_nibble_to_lcd (0x03,0);// Function set 8 bit mode
Delay_ms (5); // Wait at least 4.1ms
write_nibble_to_lcd (0x03,0);// Function set 8 bit mode
Delay_ms (1); // Wait at least 100us
write_nibble_to_lcd (0x03,0);// Function set 8 bit mode
Delay_ms (1); // Wait at least 100us
write_nibble_to_lcd (0x02,0);// NOW FINALLY Function set 4 bit mode
Delay_ms (1); // Wait at least 100us
write_byte_to_lcd (0x28, 0); //And repeat, doing full 8 bits of function cmd
write_byte_to_lcd (0x0c, 0); //Display ON (04), Curson ON(02), Blinking(01)
write_byte_to_lcd (0x01, 0); //Clear display
write_byte_to_lcd (0x06, 0); //Entry mode Increment(02) Noshift(01)
write_byte_to_lcd (CLEAR_AND_FIRST_ROW, 0);
line = 1;
column = 0 ;
// Give some indication if there's been a crash
j=1 ;
enqueue_number (LINES) ;
enqueue_char ('x') ;
enqueue_number (COLUMNS) ;
enqueue_string (" lcd @addr ") ;
enqueue_number (I2C?_ADDRESS) ;
if (num_boots)
{
enqueue_string ("\nBoot count: ");
enqueue_number (num_boots) ;
j=2 ;
}
for (; j < LINES ; j++) // visual test for data line wiring errors...
{
enqueue_char ('\n');
for (i=0; i < COLUMNS ; i++)
enqueue_char (' '+i+(j*COLUMNS));
while (rx_len) // empty out what we've just queued
flush_display_buffer();
}
Long_Delay (50) ; // Wait 5 seconds
enqueue_char ('\0') ;
enqueue_string ("Booting Linux.......");
for (;;)
{
if (debounce) // are we currently debouncing?
{
debounce-- ;
if (debounce == 0)
{
i = PORTB & SWITCH_MASK ; // read the current switch inputs
if (txbuf[SWITCHES].SW1? != i.SW1?) // Switch 1 changed
{
txbuf[SWITCH1?]++ ;
txbuf[SWITCH1?].F7 = i.SW1? ;
}
if (txbuf[SWITCHES].SW2? != i.SW2?) // Switch 2 changed
{
txbuf[SWITCH2?]++ ;
txbuf[SWITCH2?].F7 = i.SW2? ;
}
if (txbuf[SWITCHES].SW3? != i.SW3?) // Switch 3 changed
{
txbuf[SWITCH3?]++ ;
txbuf[SWITCH3?].F7 = i.SW3? ;
}
txbuf[SWITCHES] = i ; // Remember state and END of debounce
}
}
else // we are NOT currently debouncing - so read the switches
{
i = PORTB & SWITCH_MASK ; // read the current switch inputs
if (txbuf[SWITCHES] != i) // need to debounce??
debounce-- ;
}
if (crashed)
{
crash_cause = crashed ; // Remember why we crashed
write_byte_to_lcd (CLEAR_AND_FIRST_ROW, 0);
string_to_lcd ("Cause:") ;
hexbyte_to_lcd (crash_cause);
string_to_lcd (" last:") ;
hexbyte_to_lcd (last_i2c) ;
string_to_lcd (" info:") ;
hexbyte_to_lcd (info);
string_to_lcd (" boots:") ;
hexbyte_to_lcd (num_boots);
write_byte_to_lcd (SECOND_ROW, 0);
string_to_lcd ("Buf len:") ;
decbyte_to_lcd (rx_len);
write_byte_to_lcd (' ', 1);
for (i=0 ; i < 14 ; i++)
hexbyte_to_lcd (rxbuf[(rx_tail+i)&BUFFER_MASK]);
// The following kludge is to get around the non-reentrant version of C
// If there's an error inside the Interrupt, this flag will be set
// That lets us display something - and this loop will cause an infinte wait
// while (crashed) ; // Infinite loop if an error has been set in the ISR
// This will trip the watchdog timer some time later
Long_Delay (150) ; // Delay for 15 seconds
goto restart ;
}
flush_display_buffer(); //Print any characters received on I2C?
}
}
Added lines 980-981:
I could not get the I2C? drivers on the NSLU2 to speak with anything (such as the PIC described above) on a cable longer than about 50mm (ie really short) unless I pulled the I2C? clock and data lines to 3.3V on my external board using a 4k7 resistors. I know that the NSLU2 has its own pull up resistors - but they appear to be insufficient when driving anything other than a trivially short I2C? cable. June 27, 2008, at 06:21 AM
by --
Changed lines 3-4 from:
I wanted to interface a generic HD44780? based LCD display to my NSLU2. There are many different displays of different dimensions available meeting this standard, and its relatively easy to programme. These LCDs? are relatively cheap, starting at a few dollars for a small device up to a few tens of dollars for the larger ones. to:
I wanted to interface a generic HD44780 based LCD display to my NSLU2. There are many different displays of different dimensions available meeting this standard, and its relatively easy to programme. These LCDs? are relatively cheap, starting at a few dollars for a small device up to a few tens of dollars for the larger ones. June 26, 2008, at 05:54 AM
by --
Deleted lines 0-1:
This is Work In Progress. I'll complete during June 2008How do you upload a photo to this wiki? I have a circuit diagram to uploadChanged lines 7-8 from:
Typical LCDs? don't come with I2C? interfaces built in. However cheap PIC microcontrollers can implement an I2C? bus for a couple of dollars. I chose to use a 16F88, an 18 pin DIP with two eight bit ports available. My design uses two of these 16 I/O pins for the I2C? interface, seven for the LCD interface and leaves seven for you to implement other cool stuff such as A/D inputs, keypad reading inputs or general purpose outputs. to:
Typical LCDs? don't come with I2C? interfaces built in. However cheap PIC microcontrollers can implement an I2C? bus for a couple of dollars. I chose to use a 16F88, an 18 pin DIP with two eight bit ports available. My design uses two of these 16 I/O pins for the I2C? interface, seven for the LCD interface and leaves seven for you to implement other cool stuff such as A/D inputs, keypad reading inputs or general purpose outputs. Using these other 16F88 I/O lines, I've added a battery voltage monitor and some general purpose switches accessible from the NSLU2 software through the I2C? bus. June 26, 2008, at 05:48 AM
by --
Added lines 19-40:
In the interim, without a circuit diagram, here are the 16F88 pin assignments: Pin 17: PORT A bit 0 = D4 on LCD Pin 18: PORT A bit 1 = D5 on LCD Pin 1: PORT A bit 2 = D6 on LCD Pin 2: PORT A bit 3 = Analogue Input - tied to 3.3V rail with 22k Pin 3: PORT A bit 4 = Analogue Input - Battery Voltage via 1:2 voltage divider Pin 4: PORT A bit 5 = pulled to VDD via 22k (This is normally reset input) Pin 15: PORT A bit 6 = NC Pin 16: PORT A bit 7 = D7 on LCD Pin 6: PORT B bit 0 = E on LCD Pin 7: PORT B bit 1 = SD on I2C? (I added my own 22k pullup to 3.3V) Pin 8: PORT B bit 2 = RW on LCD Pin 9: PORT B bit 3 = RS on LCD Pin 10: PORT B bit 4 = CK on I2C? (I added my own 22k pullup to 3.3V) Pin 11: PORT B bit 5 = Switch3 input. Pulled to VDD via 22k. Switched low. Pin 12: PORT B bit 6 = Switch2 input. Pulled to VDD via 22k. Switched low. Pin 13: PORT B bit 7 = Switch1 input. Pulled to VDD via 22k. Switched low. Pin 14: VDD connected to NSLU2 +5V USB supply via a 1N4001 diode Pin 5: VSS connected to NSLU2 0 Volt supply June 16, 2008, at 06:28 AM
by --
Changed lines 75-76 from:
to:
#define BUFSIZE 256 Changed lines 83-85 from:
to:
char *s ; unsigned char buf[BUFSIZE+1]; Changed line 110 from:
for (i=1 ; i < argc ;) to:
for (i=1 ; i < argc ; i++) Changed line 113 from:
to:
Changed lines 116-121 from:
n = write(fd, argv[i], length);
printf ("Writing s\n", length, argv[i]) ;
if (n != length)
to:
for (s=argv[i],length=0 ; *s && (length < BUFSIZE) ;) Changed lines 119-120 from:
printf("Write error! Returned d - %s\n", n, length, strerror(errno));
exit (1);
to:
if (*s == '\\')
{
switch (*++s)
{
case '0': buf[length++]=0x00 ; s++ ; break ;
case '1': buf[length++]=0x01 ; s++ ; break ;
case '2': buf[length++]=0x02 ; s++ ; break ;
case '3': buf[length++]=0x03 ; s++ ; break ;
case '4': buf[length++]=0x04 ; s++ ; break ;
case 'n': buf[length++]=0x0a ; s++ ; break ;
case 'r': buf[length++]=0x0d ; s++ ; break ;
default: ; break ; //Ignore the slash if following char is unknown
}
}
else
buf[length++]=*s++ ;
Added lines 136-148:
if ((i < argc) && (length < BUFSIZE))
buf[length++]=' ' ;
n = write(fd, buf, length);
printf ("Writing s\n", length, argv[i]) ;
if (n != length)
{
printf("Write error! Returned d - %s\n", n, length, strerror(errno));
exit (1);
}
}
Deleted lines 149-152:
if (++i < argc)
write(fd, " ", 1);
}
Changed lines 168-170 from:
SECTION:=utils
CATEGORY:=Base system
TITLE:=My i2c additions to the NSLU2
to:
SECTION:=utils
CATEGORY:=Base system
TITLE:=My i2c additions to the NSLU2
Changed lines 187-188 from:
$(INSTALL_DIR) $(1)/etc
$(INSTALL_DATA) ./files/* $(1)/etc
to:
$(INSTALL_DIR) $(1)/etc/rc.d
$(INSTALL_BIN) ./files/S* $(1)/etc/rc.d
Added lines 195-196:
Note the section starting with Changed lines 219-220 from:
install: $(INSTALL_BIN) $(BINS) $(DESTDIR)/sbin/ to:
install: $(INSTALL_BIN) $(BINS) $(DESTDIR)/bin/ June 15, 2008, at 09:09 AM
by --
Changed lines 15-16 from:
The schematic diagram is really simple. I socketed a 16F88 onto a small piece of matrix board, along with the resistors and diode. I've constructed several of these, some built directly onto the back of the LCD module itself, and others connected to the LCD (and NSLU2) using pluggable ribbon cables. to:
The schematic diagram is really simple. Just the 16F88, a diode, capacitor and a few resistors. I assembled them and some sockets onto a small piece of matrix board and now constructed several of them for my slugs. Changed lines 19-20 from:
Note that because the NSLU2 I2C? bus uses 3V3 levels and the 16F88 and LCD devices are 5V, you need to pull a trick to interface the two. Thankfully, the I2C? bus is open drain, and the 16F88 and LCD devices work well down to a little over 4V (at least that's what I've found on the ones I've built). So by picking 5V off the USB port on the NSLU2, dropping this by 0.6V through a diode, you can power the LCD and 16F88 at 4.4V. At this reduced voltage, the 16F88 will recognise the 3V3 I2C? signalling just fine. Nothing further needs to be done to translate levels. to:
Note that because the NSLU2 I2C? bus uses 3V3 (i.e. 3.3V) levels and the 16F88 and LCD devices are 5V, you need to pull a trick to interface the two. Thankfully, the I2C? bus is open drain, and the 16F88 and LCD devices work well down to a little over 4V (at least that's what I've found on the ones I've built). So by picking 5V off the USB port on the NSLU2, dropping this by 0V6 through a diode, you can power the LCD and 16F88 at 4V4. At this reduced voltage, the 16F88 will recognise the 3V3 I2C? signalling just fine. Nothing further needs to be done to translate levels. Changed lines 23-24 from:
As mentioned above, I chose the OpenWRT? distribution for my NSLU2. I had some trouble compiling a working kernel so I will make some notes here to get you going. I have still never been able to compile a working kernel with the svn source. to:
As mentioned above, I chose the OpenWRT? distribution for my NSLU2. I had some trouble compiling a working kernel so I will make some notes here to get you going. I have still never been able to compile a working kernel with the svn source. If somebody has a working kernel June 15, 2008, at 09:05 AM
by --
Changed lines 13-14 from:
There are other pages on this wiki that describe where the I2C? bus can be accessed on the NSLU2. I'm not going to repeat the information here. However there's no information yet on this wiki about implementing the I2C? interface in hardware. That's what I'm providing. to:
There are other pages on this wiki that describe where the I2C? bus can be accessed on the NSLU2. I'm not going to repeat the information here. However there's no information yet on this wiki about implementing the I2C? interface in hardware. That's what I'm providing. Changed lines 41-42 from:
to:
June 15, 2008, at 09:00 AM
by --
Changed lines 27-42 from:
to:
Changed lines 44-57 from:
to:
June 14, 2008, at 12:27 PM
by --
Changed lines 161-164 from:
$(MAKE) -C $(PKG_BUILD_DIR) LINUX="$(LINUX_DIR)" CC="$(TARGET_CC)" STAGING_DIR="$(STAGING_DIR)" to:
$(MAKE) -C $(PKG_BUILD_DIR) LINUX="$(LINUX_DIR)" CC="$(TARGET_CC)" STAGING_DIR="$(STAGING_DIR)" Changed lines 203-205 from:
Edit the dimensions of your LCD display into the to:
As written, the I2C? is configured into 7 bit addressing mode (the normal mode). The I2C? message consists of a leading address byte (containing the 7 bit address and 1 bit write command), followed by one or more ascii characters to display on the LCD. There are several special characters:
In order for these functions to work properly, you need to customise the C code with the dimensions of the LCD you are going to use. Use the You also need to choose an i2c address for the PIC and specify this in the When you power-on the 16F88, it will initialise the LCD and display the size of LCD compiled into it, and it will also display the I2C? address it is listening to. As I have a range of different LCD display sizes, this helped me realise when I'd connected the wrong PIC to the wrong LCD! Finally, I experimented with PIC clock speed to see if I could reduce power consumption by reducing the clock. The I2C? routines seem to work perfectly down to a clock speed of 2 MHz?. If you try to clock the PIC slower than this, characters start to be dropped because the I2C? interrupt routine isn't entered quickly enough or because of I2C? input buffer overflows (because the last character isn't emptied quickly enough). June 14, 2008, at 11:13 AM
by --
Added line 26:
Added line 28:
Added line 30:
Added line 32:
Added line 34:
Added line 36:
Added line 38:
Added line 40:
Changed lines 43-45 from:
to:
Now its time to add your LCD code and compile it into the build
Changed lines 49-56 from:
Software to access the I2C? on the NSLU2to:
Software to access the I2C? on the NSLU2 (echo_lcd.c)Changed lines 132-133 from:
to:
top-MakefileThis makefile goes in the top directory of your i2c application directory. Note that Makefiles require tabs and not space characters on the indented lines!!! include $(TOPDIR)/rules.mk PKG_NAME:=my-additions PKG_RELEASE:=1 PKG_BUILD_DIR:=$(BUILD_DIR)/$(PKG_NAME) include $(INCLUDE_DIR)/package.mk define Package/my-additions SECTION:=utils CATEGORY:=Base system TITLE:=My i2c additions to the NSLU2 endef define Package/my-additions/description This package contains all my i2c additions to the system endef define Build/Prepare mkdir -p $(PKG_BUILD_DIR) $(CP) ./src/* $(PKG_BUILD_DIR)/ endef define Build/Compile $(MAKE) -C $(PKG_BUILD_DIR) LINUX="$(LINUX_DIR)" CC="$(TARGET_CC)" STAGING_DIR="$(STAGING_DIR)" endef define Package/my-additions/install $(INSTALL_DIR) $(1)/etc $(INSTALL_DATA) ./files/* $(1)/etc $(INSTALL_DIR) $(1)/bin $(INSTALL_BIN) $(PKG_BUILD_DIR)/echo_lcd $(1)/bin/ endef $(eval $(call BuildPackage?,my-additions)) src-MakefileThis makefile goes in the src directory of your i2c application directory. Note that Makefiles require tabs and not space characters on the indented lines!!! BINS = echo_lcd DESTDIR = CFLAGS += -O2 prefix := /usr/local incdir := $(prefix)/include INSTALL := install INSTALL_BIN := $(INSTALL) -c -m 755 INSTALL_DATA := $(INSTALL) -c -m 644 INSTALL_DIR := $(INSTALL) -m 755 -d RM := rm -rf all: $(BINS) clean: $(RM) $(wildcard *.o *.so *.a $(BINS)) .depend install: $(INSTALL_BIN) $(BINS) $(DESTDIR)/sbin/ echo_lcd: echo_lcd.o June 14, 2008, at 11:00 AM
by --
Changed lines 26-27 from:
TREE="openwrt-kamikaze_7.09" to:
Software to access the I2C? on the NSLU2I found several example C programmes both on this wiki as well as generally on the web, but none of them worked without hacking on my OpenWRT? based NSLU2. In any case, all the examples seemed to be for interfacing I2C? EEPROMs?, and I wanted to be able to echo strings to the I2C? bus rather like you'd echo strings to an RS-232 port. Here's an example of a programme to echo strings to the I2C? bus, similar to #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <fcntl.h> #include <errno.h> #include <string.h> #include <sys/types.h> //include <linux/i2c-dev.h> #include "i2c-dev.h" Changed lines 61-64 from:
if [ -d ${TREE} ] ; then
echo "Wait while removing the old tree"
rm -rf ${TREE}
fi
to:
Deleted lines 62-88:
echo "Now wait while untaring the new tree"
tar zxf sources/${TREE}.tgz
ln -s packages/utils/i2c-tools/ ${TREE}/package/i2c-tools
ln -s ../../my-additions ${TREE}/package/my-additions Software to access the I2C? on the NSLU2I found several example C programmes both on this wiki as well as generally on the web, but none of them worked without hacking on my OpenWRT? based NSLU2. In any case, all the examples seemed to be for interfacing I2C? EEPROMs?, and I wanted to be able to echo strings to the I2C? bus rather like you'd echo strings to an RS-232 port. Here's an example of a programme to echo strings to the I2C? bus, similar to #include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <string.h>
#include <sys/types.h>
//include <linux/i2c-dev.h>
#include "i2c-dev.h"
#define I2C?_ADDRESS 0x32
June 14, 2008, at 10:34 AM
by --
Added lines 17-18:
<<INSERT PHOTO OF CIRCUIT DIAGRAM HERE - HOW DOES ONE UPLOAD A PHOTO??>> Added lines 21-42:
Compiling an image with your I2C? code embeddedAs mentioned above, I chose the OpenWRT? distribution for my NSLU2. I had some trouble compiling a working kernel so I will make some notes here to get you going. I have still never been able to compile a working kernel with the svn source.
TREE="openwrt-kamikaze_7.09"
if [ -d ${TREE} ] ; then
echo "Wait while removing the old tree"
rm -rf ${TREE}
fi
echo "Now wait while untaring the new tree"
tar zxf sources/${TREE}.tgz
ln -s packages/utils/i2c-tools/ ${TREE}/package/i2c-tools
ln -s ../../my-additions ${TREE}/package/my-additions June 14, 2008, at 10:24 AM
by -- how do I upload a png file of a circuit diagram?
Changed line 2 from:
to:
How do you upload a photo to this wiki? I have a circuit diagram to uploadJune 14, 2008, at 03:59 AM
by --
Changed lines 1-2 from:
This is Work In Progress. I'll complete over the next few daysto:
This is Work In Progress. I'll complete during June 2008June 14, 2008, at 03:59 AM
by --
Added lines 1-2:
This is Work In Progress. I'll complete over the next few daysJune 14, 2008, at 03:57 AM
by --
Changed lines 9-10 from:
Interfacing the I2c to the NSLU2to:
Changed lines 17-29 from:
Firmware for a 16F88 PICI've written assembler code for the 16F88 before and frankly, its a pain to debug. This time I decided that I'd have a crack at writing the LCD interface in C. There's several freely downloadable C compilers for the PIC available and I chose to use one called MikroC?, available at the mikroElektronika web site. Edit the dimensions of your LCD display into the // 16F88 programme to drive a hitachi compatible LCD display from the // i2C bus // // There are plenty of spare inputs and outputs for additional functions ////////////////////////////////////////////////////////////////////// to:
Software to access the I2C? on the NSLU2I found several example C programmes both on this wiki as well as generally on the web, but none of them worked without hacking on my OpenWRT? based NSLU2. In any case, all the examples seemed to be for interfacing I2C? EEPROMs?, and I wanted to be able to echo strings to the I2C? bus rather like you'd echo strings to an RS-232 port. Here's an example of a programme to echo strings to the I2C? bus, similar to #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <fcntl.h> #include <errno.h> #include <string.h> #include <sys/types.h> //include <linux/i2c-dev.h> #include "i2c-dev.h" Changed lines 35-37 from:
// Define the number of lines and columns on the LCD display #define LINES 4 #define COLUMNS 20 to:
int
main (int argc, char **argv)
{
int fd;
int length ;
int n, i;
Changed lines 42-59 from:
// This is the assignment of 16F88 pins I chose // PORT A bit 0 = D4 on LCD // PORT A bit 1 = D5 on LCD // PORT A bit 2 = D6 on LCD // PORT A bit 3 = D7 on LCD // PORT A bit 4 = NC // PORT A bit 5 = pulled high with 10k // PORT A bit 6 = NC // PORT A bit 7 = NC // // PORT B bit 0 = E on LCD // PORT B bit 1 = SD on I2C? // PORT B bit 2 = RW on LCD // PORT B bit 3 = RS on LCD // PORT B bit 4 = CK on I2C? // PORT B bit 5 = NC // PORT B bit 6 = NC // PORT B bit 7 = NC to:
if (argc < 2)
{
fprintf (stderr, "Usage: %s STRING [STRING] ...\n", argv[0]);
exit (1) ;
}
Changed lines 48-51 from:
// Fx is mikroC nomenclature for bit x #define RS_BIT F3 #define WR_BIT F2 #define E_BIT F0 to:
if ((fd = open("/dev/i2c-0", O_RDWR)) < 0)
{
printf("Error opening i2c port!\n");
exit (1);
}
Changed lines 54-64 from:
// counters for debugging purposes
unsigned char crashed, crash_cause, info, last_i2c ;
//////////////////////////////////////////////////////////////////////
void
write_nibble_to_lcd (const unsigned char c, const unsigned char RS)
{
TRISA.F0 = 0; //Set Bits 3-0 as OUTPUTS
TRISA.F1 = 0;
TRISA.F2 = 0;
TRISA.F3 = 0;
PORTA = c ; //Bits 3-0 = high order data nibble
to:
Changed lines 60-64 from:
PORTB.E_BIT = 0 ; to:
Changed lines 66-69 from:
if (RS)
PORTB.RS_BIT = 1 ;
else
PORTB.RS_BIT = 0 ;
to:
for (i=1 ; i < argc ;)
{
length = strlen (argv[i]) ;
Changed lines 70-74 from:
PORTB.WR_BIT = 0 ; Delay_us (1); PORTB.E_BIT = 1 ; //E High, WR low, RS set to selected value Delay_us (1); PORTB.E_BIT = 0 ; to:
if (!length)
continue ;
Changed lines 73-74 from:
// Exit with E Low, WR Low, RS set to selected value } to:
n = write(fd, argv[i], length); Changed lines 75-78 from:
void
write_byte_to_lcd (const unsigned char c, const unsigned char RS)
{
unsigned char busy ;
to:
printf ("Writing s\n", length, argv[i]) ;
Changed lines 77-80 from:
TRISA.F0 = 1; //Set Bits 3-0 as INPUTS TRISA.F1 = 1; TRISA.F2 = 1; TRISA.F3 = 1; to:
if (n != length)
{
printf("Write error! Returned d - %s\n", n, length, strerror(errno));
exit (1);
}
Changed lines 83-86 from:
PORTB.E_BIT = 0 ; PORTB.RS_BIT = 0 ; PORTB.WR_BIT = 1 ; Delay_us(1); to:
if (++i < argc)
write(fd, " ", 1);
}
Changed lines 87-88 from:
// Wait for LCD to become unbusy // BUSY FLAG polled on falling edge of E to:
close (fd); return 0 ; } Firmware for a 16F88 PICI've written assembler code for the 16F88 before and frankly, its a pain to debug. This time I decided that I'd have a crack at writing the LCD interface in C. There's several freely downloadable C compilers for the PIC available and I chose to use one called MikroC?, available at the mikroElektronika web site. Edit the dimensions of your LCD display into the // 16F88 programme to drive a hitachi compatible LCD display from the
// i2C bus
//
// There are plenty of spare inputs and outputs for additional functions
//////////////////////////////////////////////////////////////////////
#define I2C?_ADDRESS 0x32
Changed lines 107-118 from:
do // Need to set E high and low twice - two four bit reads
{
PORTB.E_BIT = 1; //WR High=Read, E High
Delay_us(1);
busy = PORTA ; //Read the busy flag in bit 3 (bit 7 of LCD)
PORTB.E_BIT = 0 ; //WR High, E Clocks low
Delay_us(1);
PORTB.E_BIT = 1; //WR High=Read, E High
Delay_us(1);
PORTB.E_BIT = 0 ; //WR High, E Clocks low
Delay_us(1);
} while (busy.F3);
to:
// Define the number of lines and columns on the LCD display #define LINES 4 #define COLUMNS 20 Changed lines 111-113 from:
// Now the LCD is ready, write byte in two 4 bit nibbles write_nibble_to_lcd (c >> 4, RS) ; write_nibble_to_lcd (c & 0x0f, RS) ; to:
// This is the assignment of 16F88 pins I chose // PORT A bit 0 = D4 on LCD // PORT A bit 1 = D5 on LCD // PORT A bit 2 = D6 on LCD // PORT A bit 3 = D7 on LCD // PORT A bit 4 = NC // PORT A bit 5 = pulled high with 10k // PORT A bit 6 = NC // PORT A bit 7 = NC // // PORT B bit 0 = E on LCD // PORT B bit 1 = SD on I2C? // PORT B bit 2 = RW on LCD // PORT B bit 3 = RS on LCD // PORT B bit 4 = CK on I2C? // PORT B bit 5 = NC // PORT B bit 6 = NC // PORT B bit 7 = NC Changed lines 130-131 from:
// Exit with E Low, WR Low, RS set to selected value } to:
// Fx is mikroC nomenclature for bit x #define RS_BIT F3 #define WR_BIT F2 #define E_BIT F0 Added lines 135-137:
// counters for debugging purposes unsigned char crashed, crash_cause, info, last_i2c ; ////////////////////////////////////////////////////////////////////// Changed line 139 from:
string_to_lcd (const unsigned char *s) to:
write_nibble_to_lcd (const unsigned char c, const unsigned char RS) Changed lines 141-143 from:
while (*s)
write_byte_to_lcd (*s++, 1) ;
}
to:
TRISA.F0 = 0; //Set Bits 3-0 as OUTPUTS TRISA.F1 = 0; TRISA.F2 = 0; TRISA.F3 = 0; PORTA = c ; //Bits 3-0 = high order data nibble Changed lines 147-151 from:
void
decbyte_to_lcd (unsigned char c)
{
char *s ;
unsigned char text[4] ;
to:
PORTB.E_BIT = 0 ; Changed lines 149-152 from:
to:
if (RS)
PORTB.RS_BIT = 1 ;
else
PORTB.RS_BIT = 0 ;
Changed lines 154-156 from:
for (s=text; *s ;)
write_byte_to_lcd (*s++, 1) ;
}
to:
PORTB.WR_BIT = 0 ; Delay_us (1); PORTB.E_BIT = 1 ; //E High, WR low, RS set to selected value Delay_us (1); PORTB.E_BIT = 0 ; Added lines 160-162:
// Exit with E Low, WR Low, RS set to selected value } Changed line 164 from:
hexnibble_to_lcd (unsigned char c) to:
write_byte_to_lcd (const unsigned char c, const unsigned char RS) Changed line 166 from:
c &= 0x0f ; to:
unsigned char busy ; Changed lines 168-171 from:
if (c > 9)
c += 'A' - 10 ;
else
c += '0' ;
to:
TRISA.F0 = 1; //Set Bits 3-0 as INPUTS TRISA.F1 = 1; TRISA.F2 = 1; TRISA.F3 = 1; Changed lines 173-174 from:
write_byte_to_lcd (c, 1) ; } to:
PORTB.E_BIT = 0 ; PORTB.RS_BIT = 0 ; PORTB.WR_BIT = 1 ; Delay_us(1); Changed lines 178-186 from:
void hexbyte_to_lcd (unsigned char c)
{
hexnibble_to_lcd (c >> 4) ;
hexnibble_to_lcd (c) ;
}
//==============================================================================
// BUFFER_POWER must be >= 2
#define BUFFER_LEN 64
#define BUFFER_MASK (BUFFER_LEN-1)
to:
// Wait for LCD to become unbusy // BUSY FLAG polled on falling edge of E Changed lines 181-189 from:
unsigned char rxbuf [BUFFER_LEN] ;
unsigned char rx_head, rx_tail, rx_len ;
void
interrupt ()
{
unsigned char stat;
if (PIR1?.F3 == 1) // is this an i2c interrupt
to:
do // Need to set E high and low twice - two four bit reads Changed lines 183-186 from:
SSPCON.F4 = 0 ; // Hold clock low - (ie stretch clock while we work)
stat = SSPSTAT & 0x2d ; // mask out bits we're not interestted in
// 5=(1=data,0=addr), 3=(1=start detected),
// 2=(1=read,0=write), 0=(1=sspbuf full)
to:
PORTB.E_BIT = 1; //WR High=Read, E High
Delay_us(1);
busy = PORTA ; //Read the busy flag in bit 3 (bit 7 of LCD)
PORTB.E_BIT = 0 ; //WR High, E Clocks low
Delay_us(1);
PORTB.E_BIT = 1; //WR High=Read, E High
Delay_us(1);
PORTB.E_BIT = 0 ; //WR High, E Clocks low
Delay_us(1);
} while (busy.F3);
Changed lines 194-201 from:
if (stat == 0x09) // I2C? Write: Byte received was an address { last_i2c = SSPBUF ; // Do a dummy read of the sspbuf } else if (stat == 0x29) // I2C? Write: Byte received was data { rxbuf[rx_head++] = SSPBUF ; rx_head &= BUFFER_MASK ; to:
// Now the LCD is ready, write byte in two 4 bit nibbles write_nibble_to_lcd (c >> 4, RS) ; write_nibble_to_lcd (c & 0x0f, RS) ; Changed lines 198-203 from:
if (rx_len++ >= BUFFER_LEN)
crashed = 1 ; //flag error
}
else if (stat == 0x0c) // I2C? Read: Byte received was an address
{
while (SSPSTAT.F0 == 1) ; // keep waiting while buffer full
to:
// Exit with E Low, WR Low, RS set to selected value } Changed lines 201-209 from:
do
{
SSPCON.F7 = 0 ; // Clear the write collision flag
SSPBUF = 0xaa ; // Just return AA for the time being
} while (SSPCON.F7 == 1) ; // Loop if there was a write collision
}
else if (stat == 0x2c) // I2C? Read: Byte to be transmitted will be data
{
while (SSPSTAT.F0 == 1) ; // keep waiting while buffer full
to:
void
string_to_lcd (const unsigned char *s)
{
while (*s)
write_byte_to_lcd (*s++, 1) ;
}
Changed lines 208-224 from:
do
{
SSPCON.F7 = 0 ; // Clear the write collision flag
SSPBUF = 0x55 ; // Just return 55 for the time being
} while (SSPCON.F7 == 1) ; // Loop if there was a write collision
}
else // NACK received 0x28 - or another condition - Do a reset
{
last_i2c = SSPBUF ; // Do a dummy read of the sspbuf
SSPCON.F5=0; // Temporarily disable
SSPCON = 0x36 ; // F7=0 WCOL Write Collision Detect bit
// F6=0 SSPOV Receive Overflow Indicator bit
// F5=1 Synchronous Serial Port Enable
// F4=1 When set at 0, I2C? clock pin held low to stretch clock
// F3-0=0110: I2C? slave mode: 7 bits address
SSPSTAT = 0 ; // Clear all status bits
}
to:
void
decbyte_to_lcd (unsigned char c)
{
char *s ;
unsigned char text[4] ;
Changed lines 214-221 from:
SSPCON.F4 = 1 ; // Release the clock (finish stretching it)
PIR1?.F3 = 0 ; // Clear the interrupt
}
else // unknown interrupt!!
{
crashed = 3;
info = PIR1? ;
}
to:
Deleted lines 218-223:
//============================================================================== #define CLEAR_AND_FIRST_ROW 0x01 #define FIRST_ROW 0x02 #define SECOND_ROW (0x80 | 0x40) #define THIRD_ROW (0x80 | 0x14) #define FOURTH_ROW (0x80 | 0x54) Deleted lines 219-220:
unsigned char line, column ; Changed line 221 from:
linefeed () to:
hexnibble_to_lcd (unsigned char c) Changed line 223 from:
unsigned char cmd, i ; to:
c &= 0x0f ; Changed lines 225-232 from:
// Blank the appropriate line.
switch (line)
{
case 1: cmd = FIRST_ROW ; break ;
case 2: cmd = SECOND_ROW ; break ;
case 3: cmd = THIRD_ROW ; break ;
default: cmd = FOURTH_ROW ; break ;
}
to:
if (c > 9)
c += 'A' - 10 ;
else
c += '0' ;
Changed lines 230-231 from:
write_byte_to_lcd (cmd, 0); // Go to start of line to:
write_byte_to_lcd (c, 1) ; } Changed lines 233-237 from:
for (i=0 ; i<COLUMNS ; i++) // Blank the entire line
write_byte_to_lcd (' ', 1);
write_byte_to_lcd (cmd, 0); // Return to start of line
column = 0 ;
to:
void hexbyte_to_lcd (unsigned char c)
{
hexnibble_to_lcd (c >> 4) ;
hexnibble_to_lcd (c) ;
Added lines 238-241:
//============================================================================== // BUFFER_POWER must be >= 2 #define BUFFER_LEN 64 #define BUFFER_MASK (BUFFER_LEN-1) Added lines 243-245:
unsigned char rxbuf [BUFFER_LEN] ; unsigned char rx_head, rx_tail, rx_len ; Changed line 247 from:
printchar (const unsigned char c) to:
interrupt () Changed lines 249-251 from:
if (c == 0) to:
Changed lines 253-261 from:
// Clear display entirely
write_byte_to_lcd (CLEAR_AND_FIRST_ROW, 0);
line = 1 ;
column = 0 ;
}
else if (c == '\n')
{
// linefeed and wrap around to line 0
line++ ;
to:
SSPCON.F4 = 0 ; // Hold clock low - (ie stretch clock while we work)
stat = SSPSTAT & 0x2d ; // mask out bits we're not interestted in
// 5=(1=data,0=addr), 3=(1=start detected),
// 2=(1=read,0=write), 0=(1=sspbuf full)
Changed lines 258-259 from:
if (line > LINES)
line = 1 ;
to:
if (stat == 0x09) // I2C? Write: Byte received was an address { last_i2c = SSPBUF ; // Do a dummy read of the sspbuf } else if (stat == 0x29) // I2C? Write: Byte received was data { rxbuf[rx_head++] = SSPBUF ; rx_head &= BUFFER_MASK ; Changed lines 267-281 from:
linefeed() ;
}
else if (c < 5)
{
// move to line 1..4
line = c ;
linefeed() ;
}
else if (c == '\r')
{
linefeed() ;
}
else
{
if (column >= COLUMNS)
to:
if (rx_len++ >= BUFFER_LEN)
crashed = 1 ; //flag error
}
else if (stat == 0x0c) // I2C? Read: Byte received was an address
Changed lines 272-273 from:
// need to force a linefeed
line++ ;
to:
while (SSPSTAT.F0 == 1) ; // keep waiting while buffer full Changed lines 274-277 from:
if (line > LINES)
line = 1 ;
linefeed() ;
to:
do
{
SSPCON.F7 = 0 ; // Clear the write collision flag
SSPBUF = 0xaa ; // Just return AA for the time being
} while (SSPCON.F7 == 1) ; // Loop if there was a write collision
Added lines 280-282:
else if (stat == 0x2c) // I2C? Read: Byte to be transmitted will be data
{
while (SSPSTAT.F0 == 1) ; // keep waiting while buffer full
Changed lines 284-288 from:
// Display the numbered character
write_byte_to_lcd (c, 1);
column++ ;
}
}
to:
do
{
SSPCON.F7 = 0 ; // Clear the write collision flag
SSPBUF = 0x55 ; // Just return 55 for the time being
} while (SSPCON.F7 == 1) ; // Loop if there was a write collision
}
else // NACK received 0x28 - or another condition - Do a reset
{
last_i2c = SSPBUF ; // Do a dummy read of the sspbuf
SSPCON.F5=0; // Temporarily disable
SSPCON = 0x36 ; // F7=0 WCOL Write Collision Detect bit
// F6=0 SSPOV Receive Overflow Indicator bit
// F5=1 Synchronous Serial Port Enable
// F4=1 When set at 0, I2C? clock pin held low to stretch clock
// F3-0=0110: I2C? slave mode: 7 bits address
SSPSTAT = 0 ; // Clear all status bits
}
Changed lines 302-306 from:
// Enqueue_string will print anything BUT NOT the \000 CLEAR command!!!!
void
enqueue_string (const unsigned char *s)
{
while (*s)
to:
SSPCON.F4 = 1 ; // Release the clock (finish stretching it)
PIR1?.F3 = 0 ; // Clear the interrupt
}
else // unknown interrupt!!
Changed lines 307-317 from:
PIE1?.F3 = 0; // Disable int F3=I2C? int rxbuf[rx_head++] = *s++ ; rx_head &= BUFFER_MASK ; rx_len++ ; PIE1?.F3 = 1; // Enable int F3=I2C? int if (rx_len > BUFFER_LEN) { crashed = 4 ; //flag error return ; } to:
Added lines 311-316:
//============================================================================== #define CLEAR_AND_FIRST_ROW 0x01 #define FIRST_ROW 0x02 #define SECOND_ROW (0x80 | 0x40) #define THIRD_ROW (0x80 | 0x14) #define FOURTH_ROW (0x80 | 0x54) Added lines 318-319:
unsigned char line, column ; Changed line 321 from:
enqueue_number (const unsigned char c) to:
linefeed () Changed lines 323-324 from:
unsigned char text[4] ; unsigned char *s ; to:
unsigned char cmd, i ; Changed lines 325-327 from:
to:
// Blank the appropriate line. switch (line) Changed lines 328-329 from:
if (*s == ' ')
continue ;
to:
case 1: cmd = FIRST_ROW ; break ;
case 2: cmd = SECOND_ROW ; break ;
case 3: cmd = THIRD_ROW ; break ;
default: cmd = FOURTH_ROW ; break ;
}
Changed lines 334-336 from:
rxbuf[rx_head++] = *s ;
rx_head &= BUFFER_MASK ;
rx_len++ ;
to:
write_byte_to_lcd (cmd, 0); // Go to start of line Changed lines 336-341 from:
if (rx_len > BUFFER_LEN)
crashed = 5 ; //flag error
}
}
//==============================================================================
#define MAGIC 0x3d
to:
for (i=0 ; i<COLUMNS ; i++) // Blank the entire line
write_byte_to_lcd (' ', 1);
Changed lines 339-340 from:
unsigned char num_boots ; unsigned char magic ; // Will hold a magic number so we know a cold boot to:
write_byte_to_lcd (cmd, 0); // Return to start of line column = 0 ; } Changed line 344 from:
main() to:
printchar (const unsigned char c) Changed lines 346-356 from:
char i ; to:
if (c == 0)
{
// Clear display entirely
write_byte_to_lcd (CLEAR_AND_FIRST_ROW, 0);
line = 1 ;
column = 0 ;
}
else if (c == '\n')
{
// linefeed and wrap around to line 0
line++ ;
Changed lines 358-360 from:
// Osc = bits 6,5,4 OSCCON=0x7c; // Internal osc: 0x5c=2MHz, 0x7c=8MHz while (OSCCON.F2 == 0) ; // Wait for osc to stabilise to:
if (line > LINES)
line = 1 ;
Changed lines 361-364 from:
restart:
OPTION_REG=0x03; //0x03=Prescaler=1:256 0x80=low..pullup enabled
INTCON=0; //Disable interrupts
PIR1?=0; //Clear all interrupt flags
to:
linefeed() ;
}
else if (c < 5)
{
// move to line 1..4
line = c ;
linefeed() ;
}
else if (c == '\r')
{
linefeed() ;
}
else
{
if (column >= COLUMNS)
{
// need to force a linefeed
line++ ;
Changed lines 380-383 from:
TRISA=0; //Set Port A to all outputs
TRISB=0x12; //B4 and B1 are inputs (I2C?)
PORTA=0x00;
PORTB=0x00;
to:
if (line > LINES)
line = 1 ;
Changed lines 383-384 from:
WDTCON =0x17 ; // Enable watchdog with max timeout value = approx 2s to:
linefeed() ;
}
Changed lines 386-387 from:
to:
// Display the numbered character
write_byte_to_lcd (c, 1);
column++ ;
}
}
Changed lines 392-401 from:
SSPCON = 0x36 ; // F7=0 WCOL Write Collision Detect bit
// F6=0 SSPOV Receive Overflow Indicator bit
// F5=1 Synchronous Serial Port Enable
// F4=1 When set at 0, I2C? clock pin held low to stretch clock
// F3-0=0110: I2C? slave mode: 7 bits address
SSPADD = I2C?_ADDRESS << 1 ; // 7 bit address in bits 7--1
SSPSTAT = 0 ; // Clear all status bits
PIE1?.F3 = 1; // F6=ADC int, F3=I2C? int
if (magic != MAGIC)
to:
// Enqueue_string will print anything BUT NOT the \000 CLEAR command!!!!
void
enqueue_string (const unsigned char *s)
{
while (*s)
Changed lines 398-403 from:
crash_cause=0 ; // used for debugging interrupt routine with print statements
num_boots = 0 ;
magic = MAGIC ;
}
else
num_boots++ ;
to:
PIE1?.F3 = 0; // Disable int F3=I2C? int rxbuf[rx_head++] = *s++ ; rx_head &= BUFFER_MASK ; rx_len++ ; PIE1?.F3 = 1; // Enable int F3=I2C? int Changed lines 404-405 from:
crashed = 0 ; // We're not crashed at the moment! rx_head = rx_tail = rx_len = 0 ; to:
if (rx_len > BUFFER_LEN)
{
crashed = 4 ; //flag error
return ;
}
}
}
Changed lines 412-413 from:
INTCON.F6 = 1 ; // enable peripheral interrupts INTCON.F7 = 1 ; // enable all unmasked interrupts to:
void
enqueue_number (const unsigned char c)
{
unsigned char text[4] ;
unsigned char *s ;
Changed lines 418-420 from:
TXSTA = 0 ; // Turn off transmit from UART RCSTA = 0 ; CMCON = 0x07 ; // Disable comparators to:
Changed lines 420-423 from:
Delay_ms (100); // Provide a little time for the LCD to initialise
asm {
CLRWDT // Clear the watchdog timer
}
to:
for (s=text ; *s ; s++)
{
if (*s == ' ')
continue ;
Changed lines 425-432 from:
write_nibble_to_lcd (0x03,0); // Function set 8 bit mode Delay_ms (5); // Wait at least 4.1ms write_nibble_to_lcd (0x03,0); // Function set 8 bit mode Delay_ms (1); // Wait at least 100us write_nibble_to_lcd (0x03,0); // Function set 8 bit mode Delay_ms (1); // Wait at least 100us write_nibble_to_lcd (0x02,0); // NOW FINALLY Function set 4 bit mode Delay_ms (1); // Wait at least 100us to:
rxbuf[rx_head++] = *s ;
rx_head &= BUFFER_MASK ;
rx_len++ ;
Changed lines 429-435 from:
write_byte_to_lcd (0x28, 0); //And repeat, doing full 8 bits of function cmd write_byte_to_lcd (0x0c, 0); //Display ON (04), Curson ON(02), Blinking(01) write_byte_to_lcd (0x01, 0); //Clear display write_byte_to_lcd (0x06, 0); //Entry mode Increment(02) Noshift(01) write_byte_to_lcd (CLEAR_AND_FIRST_ROW, 0); line = 1; column = 0 ; to:
if (rx_len > BUFFER_LEN)
crashed = 5 ; //flag error
}
}
//==============================================================================
#define MAGIC 0x3d
Changed lines 436-442 from:
// Display the welcome message
enqueue_number (LINES) ;
enqueue_string ("x") ;
enqueue_number (COLUMNS) ;
enqueue_string (" lcd @addr ") ;
enqueue_number (I2C?_ADDRESS) ;
enqueue_string ("\nVersion 1");
to:
unsigned char num_boots ; unsigned char magic ; // Will hold a magic number so we know a cold boot Changed lines 439-444 from:
// Give some indication if there's been a crash
if (num_boots)
{
enqueue_string ("\rBoot count: ");
enqueue_number (num_boots) ;
}
to:
void
main()
{
char i ;
Changed lines 444-448 from:
for (;;)
{
asm {
CLRWDT // clear the watchdog timer
}
to:
// Osc = bits 6,5,4 OSCCON=0x7c; // Internal osc: 0x5c=2MHz, 0x7c=8MHz while (OSCCON.F2 == 0) ; // Wait for osc to stabilise Changed lines 448-455 from:
if (rx_len)
{
printchar (rxbuf[rx_tail++]) ;
rx_tail &= BUFFER_MASK ;
PIE1?.F3 = 0; // Disable int F3=I2C? int
rx_len-- ;
PIE1?.F3 = 1; // Enable int F3=I2C? int
}
to:
restart:
OPTION_REG=0x03; //0x03=Prescaler=1:256 0x80=low..pullup enabled
INTCON=0; //Disable interrupts
PIR1?=0; //Clear all interrupt flags
Changed lines 453-464 from:
if (crashed)
{
crash_cause = crashed ; // Remember why we crashed
write_byte_to_lcd (CLEAR_AND_FIRST_ROW, 0);
string_to_lcd ("Cause:") ;
hexbyte_to_lcd (crash_cause);
string_to_lcd (" last:") ;
hexbyte_to_lcd (last_i2c) ;
string_to_lcd (" info:") ;
hexbyte_to_lcd (info);
string_to_lcd (" boots:") ;
hexbyte_to_lcd (num_boots);
to:
TRISA=0; //Set Port A to all outputs
TRISB=0x12; //B4 and B1 are inputs (I2C?)
PORTA=0x00;
PORTB=0x00;
Changed lines 458-461 from:
write_byte_to_lcd (SECOND_ROW, 0);
string_to_lcd ("Buf len:") ;
decbyte_to_lcd (rx_len);
write_byte_to_lcd (' ', 1);
to:
WDTCON =0x17 ; // Enable watchdog with max timeout value = approx 2s Changed lines 460-461 from:
for (i=0 ; i < 14 ; i++)
hexbyte_to_lcd (rxbuf[(rx_tail+i)&BUFFER_MASK]);
to:
Changed lines 463-467 from:
// The following kludge is to get around the non-reentrant version of C
// If there's an error inside the Interrupt, this flag will be set
// That lets us display something - and this loop will cause an infinte wait
// while (crashed) ; // Infinite loop if an error has been set in the ISR
// This will trip the watchdog timer some time later
to:
SSPCON = 0x36 ; // F7=0 WCOL Write Collision Detect bit
// F6=0 SSPOV Receive Overflow Indicator bit
// F5=1 Synchronous Serial Port Enable
// F4=1 When set at 0, I2C? clock pin held low to stretch clock
// F3-0=0110: I2C? slave mode: 7 bits address
SSPADD = I2C?_ADDRESS << 1 ; // 7 bit address in bits 7--1
SSPSTAT = 0 ; // Clear all status bits
PIE1?.F3 = 1; // F6=ADC int, F3=I2C? int
Changed lines 472-482 from:
// Delay for 15 seconds
for (i=0 ; i < 150 ; i++)
{
Delay_ms (100);
asm {
CLRWDT // clear the watchdog timer
}
}
goto restart ;
}
to:
if (magic != MAGIC)
{
crash_cause=0 ; // used for debugging interrupt routine with print statements
num_boots = 0 ;
magic = MAGIC ;
Added lines 478-581:
else
num_boots++ ;
crashed = 0 ; // We're not crashed at the moment!
rx_head = rx_tail = rx_len = 0 ;
INTCON.F6 = 1 ; // enable peripheral interrupts
INTCON.F7 = 1 ; // enable all unmasked interrupts
TXSTA = 0 ; // Turn off transmit from UART
RCSTA = 0 ;
CMCON = 0x07 ; // Disable comparators
Delay_ms (100); // Provide a little time for the LCD to initialise
asm {
CLRWDT // Clear the watchdog timer
}
write_nibble_to_lcd (0x03,0); // Function set 8 bit mode
Delay_ms (5); // Wait at least 4.1ms
write_nibble_to_lcd (0x03,0); // Function set 8 bit mode
Delay_ms (1); // Wait at least 100us
write_nibble_to_lcd (0x03,0); // Function set 8 bit mode
Delay_ms (1); // Wait at least 100us
write_nibble_to_lcd (0x02,0); // NOW FINALLY Function set 4 bit mode
Delay_ms (1); // Wait at least 100us
write_byte_to_lcd (0x28, 0); //And repeat, doing full 8 bits of function cmd
write_byte_to_lcd (0x0c, 0); //Display ON (04), Curson ON(02), Blinking(01)
write_byte_to_lcd (0x01, 0); //Clear display
write_byte_to_lcd (0x06, 0); //Entry mode Increment(02) Noshift(01)
write_byte_to_lcd (CLEAR_AND_FIRST_ROW, 0);
line = 1;
column = 0 ;
// Display the welcome message
enqueue_number (LINES) ;
enqueue_string ("x") ;
enqueue_number (COLUMNS) ;
enqueue_string (" lcd @addr ") ;
enqueue_number (I2C?_ADDRESS) ;
enqueue_string ("\nVersion 1");
// Give some indication if there's been a crash
if (num_boots)
{
enqueue_string ("\rBoot count: ");
enqueue_number (num_boots) ;
}
for (;;)
{
asm {
CLRWDT // clear the watchdog timer
}
if (rx_len)
{
printchar (rxbuf[rx_tail++]) ;
rx_tail &= BUFFER_MASK ;
PIE1?.F3 = 0; // Disable int F3=I2C? int
rx_len-- ;
PIE1?.F3 = 1; // Enable int F3=I2C? int
}
if (crashed)
{
crash_cause = crashed ; // Remember why we crashed
write_byte_to_lcd (CLEAR_AND_FIRST_ROW, 0);
string_to_lcd ("Cause:") ;
hexbyte_to_lcd (crash_cause);
string_to_lcd (" last:") ;
hexbyte_to_lcd (last_i2c) ;
string_to_lcd (" info:") ;
hexbyte_to_lcd (info);
string_to_lcd (" boots:") ;
hexbyte_to_lcd (num_boots);
write_byte_to_lcd (SECOND_ROW, 0);
string_to_lcd ("Buf len:") ;
decbyte_to_lcd (rx_len);
write_byte_to_lcd (' ', 1);
for (i=0 ; i < 14 ; i++)
hexbyte_to_lcd (rxbuf[(rx_tail+i)&BUFFER_MASK]);
// The following kludge is to get around the non-reentrant version of C
// If there's an error inside the Interrupt, this flag will be set
// That lets us display something - and this loop will cause an infinte wait
// while (crashed) ; // Infinite loop if an error has been set in the ISR
// This will trip the watchdog timer some time later
// Delay for 15 seconds
for (i=0 ; i < 150 ; i++)
{
Delay_ms (100);
asm {
CLRWDT // clear the watchdog timer
}
}
goto restart ;
}
}
June 14, 2008, at 03:52 AM
by --
Added lines 9-17:
Interfacing the I2c to the NSLU2There are other pages on this wiki that describe where the I2C? bus can be accessed on the NSLU2. I'm not going to repeat the information here. However there's no information yet on this wiki about implementing the I2C? interface in hardware. That's what I'm providing. The schematic diagram is really simple. I socketed a 16F88 onto a small piece of matrix board, along with the resistors and diode. I've constructed several of these, some built directly onto the back of the LCD module itself, and others connected to the LCD (and NSLU2) using pluggable ribbon cables. Note that because the NSLU2 I2C? bus uses 3V3 levels and the 16F88 and LCD devices are 5V, you need to pull a trick to interface the two. Thankfully, the I2C? bus is open drain, and the 16F88 and LCD devices work well down to a little over 4V (at least that's what I've found on the ones I've built). So by picking 5V off the USB port on the NSLU2, dropping this by 0.6V through a diode, you can power the LCD and 16F88 at 4.4V. At this reduced voltage, the 16F88 will recognise the 3V3 I2C? signalling just fine. Nothing further needs to be done to translate levels. Changed lines 22-23 from:
You need to specify the dimensions of your LCD display and the i2c address you'd like to allocate using #defines near the top of the code. to:
June 14, 2008, at 03:43 AM
by --
Changed lines 11-12 from:
I've written assembler code for the 16F88 before and frankly, its a pain to debug. This time I decided that I'd have a crack at writing the LCD interface in C. There's several freely downloadable C compilers for the PIC available and I chose to use one called MikroC?, available at the mikroElektronika web site. to:
I've written assembler code for the 16F88 before and frankly, its a pain to debug. This time I decided that I'd have a crack at writing the LCD interface in C. There's several freely downloadable C compilers for the PIC available and I chose to use one called MikroC?, available at the mikroElektronika web site. June 14, 2008, at 03:42 AM
by --
Changed lines 11-12 from:
I've written assembler code for the 16F88 before and frankly, its a pain to debug. This time I decided that I'd have a crack at writing the LCD interface in C. There's several freely downloadable C compilers for the PIC available and I chose to use one called MikroC?, available at [[http://www.mikroe.com/en/download]the mikroElektronika] web site. to:
I've written assembler code for the 16F88 before and frankly, its a pain to debug. This time I decided that I'd have a crack at writing the LCD interface in C. There's several freely downloadable C compilers for the PIC available and I chose to use one called MikroC?, available at the mikroElektronika web site. June 14, 2008, at 03:41 AM
by --
Changed lines 15-503 from:
this is a test
this is antother test
this is a third
// 16F88 programme to drive a hitachi compatible LCD display from the
// i2C bus
//
// There are plenty of spare inputs and outputs for additional functions
//////////////////////////////////////////////////////////////////////
// Define the number of lines and columns on the LCD display
#define LINES 4
#define COLUMNS 20
// This is the assignment of 16F88 pins I chose
// PORT A bit 0 = D4 on LCD
// PORT A bit 1 = D5 on LCD
// PORT A bit 2 = D6 on LCD
// PORT A bit 3 = D7 on LCD
// PORT A bit 4 = NC
// PORT A bit 5 = pulled high with 10k
// PORT A bit 6 = NC
// PORT A bit 7 = NC
//
// PORT B bit 0 = E on LCD
// PORT B bit 2 = RW on LCD
// PORT B bit 3 = RS on LCD
// PORT B bit 5 = NC
// PORT B bit 6 = NC
// PORT B bit 7 = NC
// Fx is mikroC nomenclature for bit x
#define RS_BIT F3
#define WR_BIT F2
#define E_BIT F0
// counters for debugging purposes
unsigned char crashed, crash_cause, info, last_i2c ;
//////////////////////////////////////////////////////////////////////
void
write_nibble_to_lcd (const unsigned char c, const unsigned char RS)
{
TRISA.F0 = 0; //Set Bits 3-0 as OUTPUTS
TRISA.F1 = 0;
TRISA.F2 = 0;
TRISA.F3 = 0;
PORTA = c ; //Bits 3-0 = high order data nibble
PORTB.E_BIT = 0 ;
if (RS)
PORTB.RS_BIT = 1 ;
else
PORTB.RS_BIT = 0 ;
PORTB.WR_BIT = 0 ;
Delay_us (1);
PORTB.E_BIT = 1 ; //E High, WR low, RS set to selected value
Delay_us (1);
PORTB.E_BIT = 0 ;
// Exit with E Low, WR Low, RS set to selected value
}
void
write_byte_to_lcd (const unsigned char c, const unsigned char RS)
{
unsigned char busy ;
TRISA.F0 = 1; //Set Bits 3-0 as INPUTS
TRISA.F1 = 1;
TRISA.F2 = 1;
TRISA.F3 = 1;
PORTB.E_BIT = 0 ;
PORTB.RS_BIT = 0 ;
PORTB.WR_BIT = 1 ;
Delay_us(1);
// Wait for LCD to become unbusy
// BUSY FLAG polled on falling edge of E
do // Need to set E high and low twice - two four bit reads
{
PORTB.E_BIT = 1; //WR High=Read, E High
Delay_us(1);
busy = PORTA ; //Read the busy flag in bit 3 (bit 7 of LCD)
PORTB.E_BIT = 0 ; //WR High, E Clocks low
Delay_us(1);
PORTB.E_BIT = 1; //WR High=Read, E High
Delay_us(1);
PORTB.E_BIT = 0 ; //WR High, E Clocks low
Delay_us(1);
} while (busy.F3);
// Now the LCD is ready, write byte in two 4 bit nibbles
write_nibble_to_lcd (c >> 4, RS) ;
write_nibble_to_lcd (c & 0x0f, RS) ;
// Exit with E Low, WR Low, RS set to selected value
}
void
string_to_lcd (const unsigned char *s)
{
while (*s)
write_byte_to_lcd (*s++, 1) ;
}
void
decbyte_to_lcd (unsigned char c)
{
char *s ;
unsigned char text[4] ;
for (s=text; *s ;)
write_byte_to_lcd (*s++, 1) ;
}
void
hexnibble_to_lcd (unsigned char c)
{
c &= 0x0f ;
if (c > 9)
c += 'A' - 10 ;
else
c += '0' ;
write_byte_to_lcd (c, 1) ;
}
void hexbyte_to_lcd (unsigned char c)
{
hexnibble_to_lcd (c >> 4) ;
hexnibble_to_lcd (c) ;
}
//==============================================================================
// BUFFER_POWER must be >= 2
#define BUFFER_LEN 64
#define BUFFER_MASK (BUFFER_LEN-1)
unsigned char rxbuf [BUFFER_LEN] ;
unsigned char rx_head, rx_tail, rx_len ;
void
interrupt ()
{
unsigned char stat;
{
SSPCON.F4 = 0 ; // Hold clock low - (ie stretch clock while we work)
stat = SSPSTAT & 0x2d ; // mask out bits we're not interestted in
// 5=(1=data,0=addr), 3=(1=start detected),
// 2=(1=read,0=write), 0=(1=sspbuf full)
{
last_i2c = SSPBUF ; // Do a dummy read of the sspbuf
}
{
rxbuf[rx_head++] = SSPBUF ;
rx_head &= BUFFER_MASK ;
if (rx_len++ >= BUFFER_LEN)
crashed = 1 ; //flag error
}
{
while (SSPSTAT.F0 == 1) ; // keep waiting while buffer full
do
{
SSPCON.F7 = 0 ; // Clear the write collision flag
SSPBUF = 0xaa ; // Just return AA for the time being
} while (SSPCON.F7 == 1) ; // Loop if there was a write collision
}
{
while (SSPSTAT.F0 == 1) ; // keep waiting while buffer full
do
{
SSPCON.F7 = 0 ; // Clear the write collision flag
SSPBUF = 0x55 ; // Just return 55 for the time being
} while (SSPCON.F7 == 1) ; // Loop if there was a write collision
}
else // NACK received 0x28 - or another condition - Do a reset
{
last_i2c = SSPBUF ; // Do a dummy read of the sspbuf
SSPCON.F5=0; // Temporarily disable
SSPCON = 0x36 ; // F7=0 WCOL Write Collision Detect bit
// F6=0 SSPOV Receive Overflow Indicator bit
// F5=1 Synchronous Serial Port Enable
SSPSTAT = 0 ; // Clear all status bits
}
SSPCON.F4 = 1 ; // Release the clock (finish stretching it)
}
else // unknown interrupt!!
{
crashed = 3;
}
}
//==============================================================================
#define CLEAR_AND_FIRST_ROW 0x01
#define FIRST_ROW 0x02
#define SECOND_ROW (0x80 | 0x40)
#define THIRD_ROW (0x80 | 0x14)
#define FOURTH_ROW (0x80 | 0x54)
unsigned char line, column ;
void
linefeed ()
{
unsigned char cmd, i ;
// Blank the appropriate line.
switch (line)
{
case 1: cmd = FIRST_ROW ; break ;
case 2: cmd = SECOND_ROW ; break ;
case 3: cmd = THIRD_ROW ; break ;
default: cmd = FOURTH_ROW ; break ;
}
write_byte_to_lcd (cmd, 0); // Go to start of line
for (i=0 ; i<COLUMNS ; i++) // Blank the entire line
write_byte_to_lcd (' ', 1);
write_byte_to_lcd (cmd, 0); // Return to start of line
column = 0 ;
}
void
printchar (const unsigned char c)
{
if (c == 0)
{
// Clear display entirely
write_byte_to_lcd (CLEAR_AND_FIRST_ROW, 0);
line = 1 ;
column = 0 ;
}
else if (c == '\n')
{
// linefeed and wrap around to line 0
line++ ;
if (line > LINES)
line = 1 ;
linefeed() ;
}
else if (c < 5)
{
// move to line 1..4
line = c ;
linefeed() ;
}
else if (c == '\r')
{
linefeed() ;
}
else
{
if (column >= COLUMNS)
{
// need to force a linefeed
line++ ;
if (line > LINES)
line = 1 ;
linefeed() ;
}
// Display the numbered character
write_byte_to_lcd (c, 1);
column++ ;
}
}
// Enqueue_string will print anything BUT NOT the \000 CLEAR command!!!!
void
enqueue_string (const unsigned char *s)
{
while (*s)
{
rxbuf[rx_head++] = *s++ ;
rx_head &= BUFFER_MASK ;
rx_len++ ;
if (rx_len > BUFFER_LEN)
{
crashed = 4 ; //flag error
return ;
}
}
}
void
enqueue_number (const unsigned char c)
{
unsigned char text[4] ;
unsigned char *s ;
for (s=text ; *s ; s++)
{
if (*s == ' ')
continue ;
rxbuf[rx_head++] = *s ;
rx_head &= BUFFER_MASK ;
rx_len++ ;
if (rx_len > BUFFER_LEN)
crashed = 5 ; //flag error
}
}
//==============================================================================
#define MAGIC 0x3d
unsigned char num_boots ;
unsigned char magic ; // Will hold a magic number so we know a cold boot
void
main()
{
char i ;
// Osc = bits 6,5,4
OSCCON=0x7c; // Internal osc: 0x5c=2MHz, 0x7c=8MHz
while (OSCCON.F2 == 0) ; // Wait for osc to stabilise
restart:
OPTION_REG=0x03; //0x03=Prescaler=1:256 0x80=low..pullup enabled
INTCON=0; //Disable interrupts
TRISA=0; //Set Port A to all outputs
PORTA=0x00;
PORTB=0x00;
WDTCON =0x17 ; // Enable watchdog with max timeout value = approx 2s
SSPCON = 0x36 ; // F7=0 WCOL Write Collision Detect bit
// F6=0 SSPOV Receive Overflow Indicator bit
// F5=1 Synchronous Serial Port Enable
SSPSTAT = 0 ; // Clear all status bits
if (magic != MAGIC)
{
crash_cause=0 ; // used for debugging interrupt routine with print statements
num_boots = 0 ;
magic = MAGIC ;
}
else
num_boots++ ;
crashed = 0 ; // We're not crashed at the moment!
rx_head = rx_tail = rx_len = 0 ;
INTCON.F6 = 1 ; // enable peripheral interrupts
INTCON.F7 = 1 ; // enable all unmasked interrupts
TXSTA = 0 ; // Turn off transmit from UART
RCSTA = 0 ;
CMCON = 0x07 ; // Disable comparators
Delay_ms (100); // Provide a little time for the LCD to initialise
asm {
CLRWDT // Clear the watchdog timer
}
write_nibble_to_lcd (0x03,0); // Function set 8 bit mode
Delay_ms (5); // Wait at least 4.1ms
write_nibble_to_lcd (0x03,0); // Function set 8 bit mode
Delay_ms (1); // Wait at least 100us
write_nibble_to_lcd (0x03,0); // Function set 8 bit mode
Delay_ms (1); // Wait at least 100us
write_nibble_to_lcd (0x02,0); // NOW FINALLY Function set 4 bit mode
Delay_ms (1); // Wait at least 100us
write_byte_to_lcd (0x28, 0); //And repeat, doing full 8 bits of function cmd
write_byte_to_lcd (0x0c, 0); //Display ON (04), Curson ON(02), Blinking(01)
write_byte_to_lcd (0x01, 0); //Clear display
write_byte_to_lcd (0x06, 0); //Entry mode Increment(02) Noshift(01)
write_byte_to_lcd (CLEAR_AND_FIRST_ROW, 0);
line = 1;
column = 0 ;
// Display the welcome message
enqueue_number (LINES) ;
enqueue_string ("x") ;
enqueue_number (COLUMNS) ;
enqueue_string (" lcd @addr ") ;
enqueue_string ("\nVersion 1");
// Give some indication if there's been a crash
if (num_boots)
{
enqueue_string ("\rBoot count: ");
enqueue_number (num_boots) ;
}
for (;;)
{
asm {
CLRWDT // clear the watchdog timer
}
if (rx_len)
{
printchar (rxbuf[rx_tail++]) ;
rx_tail &= BUFFER_MASK ;
rx_len-- ;
}
if (crashed)
{
crash_cause = crashed ; // Remember why we crashed
write_byte_to_lcd (CLEAR_AND_FIRST_ROW, 0);
string_to_lcd ("Cause:") ;
hexbyte_to_lcd (crash_cause);
string_to_lcd (" last:") ;
hexbyte_to_lcd (last_i2c) ;
string_to_lcd (" info:") ;
hexbyte_to_lcd (info);
string_to_lcd (" boots:") ;
hexbyte_to_lcd (num_boots);
write_byte_to_lcd (SECOND_ROW, 0);
string_to_lcd ("Buf len:") ;
decbyte_to_lcd (rx_len);
write_byte_to_lcd (' ', 1);
for (i=0 ; i < 14 ; i++)
hexbyte_to_lcd (rxbuf[(rx_tail+i)&BUFFER_MASK]);
// The following kludge is to get around the non-reentrant version of C
// If there's an error inside the Interrupt, this flag will be set
// That lets us display something - and this loop will cause an infinte wait
// while (crashed) ; // Infinite loop if an error has been set in the ISR
// This will trip the watchdog timer some time later
// Delay for 15 seconds
for (i=0 ; i < 150 ; i++)
{
Delay_ms (100);
asm {
CLRWDT // clear the watchdog timer
}
}
goto restart ;
}
}
}
to:
// 16F88 programme to drive a hitachi compatible LCD display from the // i2C bus // // There are plenty of spare inputs and outputs for additional functions ////////////////////////////////////////////////////////////////////// #define I2C?_ADDRESS 0x32 // Define the number of lines and columns on the LCD display #define LINES 4 #define COLUMNS 20 // This is the assignment of 16F88 pins I chose // PORT A bit 0 = D4 on LCD // PORT A bit 1 = D5 on LCD // PORT A bit 2 = D6 on LCD // PORT A bit 3 = D7 on LCD // PORT A bit 4 = NC // PORT A bit 5 = pulled high with 10k // PORT A bit 6 = NC // PORT A bit 7 = NC // // PORT B bit 0 = E on LCD // PORT B bit 1 = SD on I2C? // PORT B bit 2 = RW on LCD // PORT B bit 3 = RS on LCD // PORT B bit 4 = CK on I2C? // PORT B bit 5 = NC // PORT B bit 6 = NC // PORT B bit 7 = NC // Fx is mikroC nomenclature for bit x #define RS_BIT F3 #define WR_BIT F2 #define E_BIT F0 // counters for debugging purposes unsigned char crashed, crash_cause, info, last_i2c ; ////////////////////////////////////////////////////////////////////// void write_nibble_to_lcd (const unsigned char c, const unsigned char RS) { TRISA.F0 = 0; //Set Bits 3-0 as OUTPUTS TRISA.F1 = 0; TRISA.F2 = 0; TRISA.F3 = 0; PORTA = c ; //Bits 3-0 = high order data nibble PORTB.E_BIT = 0 ; if (RS) PORTB.RS_BIT = 1 ; else PORTB.RS_BIT = 0 ; PORTB.WR_BIT = 0 ; Delay_us (1); PORTB.E_BIT = 1 ; //E High, WR low, RS set to selected value Delay_us (1); PORTB.E_BIT = 0 ; // Exit with E Low, WR Low, RS set to selected value } void write_byte_to_lcd (const unsigned char c, const unsigned char RS) { unsigned char busy ; TRISA.F0 = 1; //Set Bits 3-0 as INPUTS TRISA.F1 = 1; TRISA.F2 = 1; TRISA.F3 = 1; PORTB.E_BIT = 0 ; PORTB.RS_BIT = 0 ; PORTB.WR_BIT = 1 ; Delay_us(1); // Wait for LCD to become unbusy // BUSY FLAG polled on falling edge of E do // Need to set E high and low twice - two four bit reads { PORTB.E_BIT = 1; //WR High=Read, E High Delay_us(1); busy = PORTA ; //Read the busy flag in bit 3 (bit 7 of LCD) PORTB.E_BIT = 0 ; //WR High, E Clocks low Delay_us(1); PORTB.E_BIT = 1; //WR High=Read, E High Delay_us(1); PORTB.E_BIT = 0 ; //WR High, E Clocks low Delay_us(1); } while (busy.F3); // Now the LCD is ready, write byte in two 4 bit nibbles write_nibble_to_lcd (c >> 4, RS) ; write_nibble_to_lcd (c & 0x0f, RS) ; // Exit with E Low, WR Low, RS set to selected value } void string_to_lcd (const unsigned char *s) { while (*s) write_byte_to_lcd (*s++, 1) ; } void decbyte_to_lcd (unsigned char c) { char *s ; unsigned char text[4] ; ByteToStr? (c, text); for (s=text; *s ;) write_byte_to_lcd (*s++, 1) ; } void hexnibble_to_lcd (unsigned char c) { c &= 0x0f ; if (c > 9) c += 'A' - 10 ; else c += '0' ; write_byte_to_lcd (c, 1) ; } void hexbyte_to_lcd (unsigned char c) { hexnibble_to_lcd (c >> 4) ; hexnibble_to_lcd (c) ; } //============================================================================== // BUFFER_POWER must be >= 2 #define BUFFER_LEN 64 #define BUFFER_MASK (BUFFER_LEN-1) unsigned char rxbuf [BUFFER_LEN] ; unsigned char rx_head, rx_tail, rx_len ; void interrupt () { unsigned char stat; if (PIR1?.F3 == 1) // is this an i2c interrupt { SSPCON.F4 = 0 ; // Hold clock low - (ie stretch clock while we work) stat = SSPSTAT & 0x2d ; // mask out bits we're not interestted in // 5=(1=data,0=addr), 3=(1=start detected), // 2=(1=read,0=write), 0=(1=sspbuf full) if (stat == 0x09) // I2C? Write: Byte received was an address { last_i2c = SSPBUF ; // Do a dummy read of the sspbuf } else if (stat == 0x29) // I2C? Write: Byte received was data { rxbuf[rx_head++] = SSPBUF ; rx_head &= BUFFER_MASK ; if (rx_len++ >= BUFFER_LEN) crashed = 1 ; //flag error } else if (stat == 0x0c) // I2C? Read: Byte received was an address { while (SSPSTAT.F0 == 1) ; // keep waiting while buffer full do { SSPCON.F7 = 0 ; // Clear the write collision flag SSPBUF = 0xaa ; // Just return AA for the time being } while (SSPCON.F7 == 1) ; // Loop if there was a write collision } else if (stat == 0x2c) // I2C? Read: Byte to be transmitted will be data { while (SSPSTAT.F0 == 1) ; // keep waiting while buffer full do { SSPCON.F7 = 0 ; // Clear the write collision flag SSPBUF = 0x55 ; // Just return 55 for the time being } while (SSPCON.F7 == 1) ; // Loop if there was a write collision } else // NACK received 0x28 - or another condition - Do a reset { last_i2c = SSPBUF ; // Do a dummy read of the sspbuf SSPCON.F5=0; // Temporarily disable SSPCON = 0x36 ; // F7=0 WCOL Write Collision Detect bit // F6=0 SSPOV Receive Overflow Indicator bit // F5=1 Synchronous Serial Port Enable // F4=1 When set at 0, I2C? clock pin held low to stretch clock // F3-0=0110: I2C? slave mode: 7 bits address SSPSTAT = 0 ; // Clear all status bits } SSPCON.F4 = 1 ; // Release the clock (finish stretching it) PIR1?.F3 = 0 ; // Clear the interrupt } else // unknown interrupt!! { crashed = 3; info = PIR1? ; } } //============================================================================== #define CLEAR_AND_FIRST_ROW 0x01 #define FIRST_ROW 0x02 #define SECOND_ROW (0x80 | 0x40) #define THIRD_ROW (0x80 | 0x14) #define FOURTH_ROW (0x80 | 0x54) unsigned char line, column ; void linefeed () { unsigned char cmd, i ; // Blank the appropriate line. switch (line) { case 1: cmd = FIRST_ROW ; break ; case 2: cmd = SECOND_ROW ; break ; case 3: cmd = THIRD_ROW ; break ; default: cmd = FOURTH_ROW ; break ; } write_byte_to_lcd (cmd, 0); // Go to start of line for (i=0 ; i<COLUMNS ; i++) // Blank the entire line write_byte_to_lcd (' ', 1); write_byte_to_lcd (cmd, 0); // Return to start of line column = 0 ; } void printchar (const unsigned char c) { if (c == 0) { // Clear display entirely write_byte_to_lcd (CLEAR_AND_FIRST_ROW, 0); line = 1 ; column = 0 ; } else if (c == '\n') { // linefeed and wrap around to line 0 line++ ; if (line > LINES) line = 1 ; linefeed() ; } else if (c < 5) { // move to line 1..4 line = c ; linefeed() ; } else if (c == '\r') { linefeed() ; } else { if (column >= COLUMNS) { // need to force a linefeed line++ ; if (line > LINES) line = 1 ; linefeed() ; } // Display the numbered character write_byte_to_lcd (c, 1); column++ ; } } // Enqueue_string will print anything BUT NOT the \000 CLEAR command!!!! void enqueue_string (const unsigned char *s) { while (*s) { PIE1?.F3 = 0; // Disable int F3=I2C? int rxbuf[rx_head++] = *s++ ; rx_head &= BUFFER_MASK ; rx_len++ ; PIE1?.F3 = 1; // Enable int F3=I2C? int if (rx_len > BUFFER_LEN) { crashed = 4 ; //flag error return ; } } } void enqueue_number (const unsigned char c) { unsigned char text[4] ; unsigned char *s ; ByteToStr? (c, text); for (s=text ; *s ; s++) { if (*s == ' ') continue ; rxbuf[rx_head++] = *s ; rx_head &= BUFFER_MASK ; rx_len++ ; if (rx_len > BUFFER_LEN) crashed = 5 ; //flag error } } //============================================================================== #define MAGIC 0x3d unsigned char num_boots ; unsigned char magic ; // Will hold a magic number so we know a cold boot void main() { char i ; // Osc = bits 6,5,4 OSCCON=0x7c; // Internal osc: 0x5c=2MHz, 0x7c=8MHz while (OSCCON.F2 == 0) ; // Wait for osc to stabilise restart: OPTION_REG=0x03; //0x03=Prescaler=1:256 0x80=low..pullup enabled INTCON=0; //Disable interrupts PIR1?=0; //Clear all interrupt flags TRISA=0; //Set Port A to all outputs TRISB=0x12; //B4 and B1 are inputs (I2C?) PORTA=0x00; PORTB=0x00; WDTCON =0x17 ; // Enable watchdog with max timeout value = approx 2s ANSEL=0; //0 = PortA? bit is used for digital I/O ADCON1?=0x07 ; // Disable A/D SSPCON = 0x36 ; // F7=0 WCOL Write Collision Detect bit // F6=0 SSPOV Receive Overflow Indicator bit // F5=1 Synchronous Serial Port Enable // F4=1 When set at 0, I2C? clock pin held low to stretch clock // F3-0=0110: I2C? slave mode: 7 bits address SSPADD = I2C?_ADDRESS << 1 ; // 7 bit address in bits 7--1 SSPSTAT = 0 ; // Clear all status bits PIE1?.F3 = 1; // F6=ADC int, F3=I2C? int if (magic != MAGIC) { crash_cause=0 ; // used for debugging interrupt routine with print statements num_boots = 0 ; magic = MAGIC ; } else num_boots++ ; crashed = 0 ; // We're not crashed at the moment! rx_head = rx_tail = rx_len = 0 ; INTCON.F6 = 1 ; // enable peripheral interrupts INTCON.F7 = 1 ; // enable all unmasked interrupts TXSTA = 0 ; // Turn off transmit from UART RCSTA = 0 ; CMCON = 0x07 ; // Disable comparators Delay_ms (100); // Provide a little time for the LCD to initialise asm { CLRWDT // Clear the watchdog timer } write_nibble_to_lcd (0x03,0); // Function set 8 bit mode Delay_ms (5); // Wait at least 4.1ms write_nibble_to_lcd (0x03,0); // Function set 8 bit mode Delay_ms (1); // Wait at least 100us write_nibble_to_lcd (0x03,0); // Function set 8 bit mode Delay_ms (1); // Wait at least 100us write_nibble_to_lcd (0x02,0); // NOW FINALLY Function set 4 bit mode Delay_ms (1); // Wait at least 100us write_byte_to_lcd (0x28, 0); //And repeat, doing full 8 bits of function cmd write_byte_to_lcd (0x0c, 0); //Display ON (04), Curson ON(02), Blinking(01) write_byte_to_lcd (0x01, 0); //Clear display write_byte_to_lcd (0x06, 0); //Entry mode Increment(02) Noshift(01) write_byte_to_lcd (CLEAR_AND_FIRST_ROW, 0); line = 1; column = 0 ; // Display the welcome message enqueue_number (LINES) ; enqueue_string ("x") ; enqueue_number (COLUMNS) ; enqueue_string (" lcd @addr ") ; enqueue_number (I2C?_ADDRESS) ; enqueue_string ("\nVersion 1"); // Give some indication if there's been a crash if (num_boots) { enqueue_string ("\rBoot count: "); enqueue_number (num_boots) ; } for (;;) { asm { CLRWDT // clear the watchdog timer } if (rx_len) { printchar (rxbuf[rx_tail++]) ; rx_tail &= BUFFER_MASK ; PIE1?.F3 = 0; // Disable int F3=I2C? int rx_len-- ; PIE1?.F3 = 1; // Enable int F3=I2C? int } if (crashed) { crash_cause = crashed ; // Remember why we crashed write_byte_to_lcd (CLEAR_AND_FIRST_ROW, 0); string_to_lcd ("Cause:") ; hexbyte_to_lcd (crash_cause); string_to_lcd (" last:") ; hexbyte_to_lcd (last_i2c) ; string_to_lcd (" info:") ; hexbyte_to_lcd (info); string_to_lcd (" boots:") ; hexbyte_to_lcd (num_boots); write_byte_to_lcd (SECOND_ROW, 0); string_to_lcd ("Buf len:") ; decbyte_to_lcd (rx_len); write_byte_to_lcd (' ', 1); for (i=0 ; i < 14 ; i++) hexbyte_to_lcd (rxbuf[(rx_tail+i)&BUFFER_MASK]); // The following kludge is to get around the non-reentrant version of C // If there's an error inside the Interrupt, this flag will be set // That lets us display something - and this loop will cause an infinte wait // while (crashed) ; // Infinite loop if an error has been set in the ISR // This will trip the watchdog timer some time later // Delay for 15 seconds for (i=0 ; i < 150 ; i++) { Delay_ms (100); asm { CLRWDT // clear the watchdog timer } } goto restart ; } } } June 14, 2008, at 03:39 AM
by --
Added lines 17-18:
this is a third June 14, 2008, at 03:39 AM
by --
Added lines 15-16:
this is a test this is antother test June 14, 2008, at 03:36 AM
by --
Changed lines 13-14 from:
You need to specify the dimensions of your LCD display and the i2c address you'd like to allocate using #defines near the top of the to:
You need to specify the dimensions of your LCD display and the i2c address you'd like to allocate using #defines near the top of the code. Changed lines 16-20 from:
// i2C bus
//
// There are plenty of spare inputs and outputs for additional functions
//////////////////////////////////////////////////////////////////////
to:
// i2C bus
//
// There are plenty of spare inputs and outputs for additional functions
//////////////////////////////////////////////////////////////////////
Changed lines 22-24 from:
// Define the number of lines and columns on the LCD display
#define LINES 4
#define COLUMNS 20
to:
// Define the number of lines and columns on the LCD display
#define LINES 4
#define COLUMNS 20
Changed lines 26-43 from:
// This is the assignment of 16F88 pins I chose
// PORT A bit 0 = D4 on LCD
// PORT A bit 1 = D5 on LCD
// PORT A bit 2 = D6 on LCD
// PORT A bit 3 = D7 on LCD
// PORT A bit 4 = NC
// PORT A bit 5 = pulled high with 10k
// PORT A bit 6 = NC
// PORT A bit 7 = NC
//
// PORT B bit 0 = E on LCD
// PORT B bit 2 = RW on LCD
// PORT B bit 3 = RS on LCD
// PORT B bit 5 = NC
// PORT B bit 6 = NC
// PORT B bit 7 = NC
to:
// This is the assignment of 16F88 pins I chose
// PORT A bit 0 = D4 on LCD
// PORT A bit 1 = D5 on LCD
// PORT A bit 2 = D6 on LCD
// PORT A bit 3 = D7 on LCD
// PORT A bit 4 = NC
// PORT A bit 5 = pulled high with 10k
// PORT A bit 6 = NC
// PORT A bit 7 = NC
//
// PORT B bit 0 = E on LCD
// PORT B bit 2 = RW on LCD
// PORT B bit 3 = RS on LCD
// PORT B bit 5 = NC
// PORT B bit 6 = NC
// PORT B bit 7 = NC
Changed lines 45-48 from:
// Fx is mikroC nomenclature for bit x
#define RS_BIT F3
#define WR_BIT F2
#define E_BIT F0
to:
// Fx is mikroC nomenclature for bit x
#define RS_BIT F3
#define WR_BIT F2
#define E_BIT F0
Changed lines 50-60 from:
// counters for debugging purposes
unsigned char crashed, crash_cause, info, last_i2c ;
//////////////////////////////////////////////////////////////////////
void
write_nibble_to_lcd (const unsigned char c, const unsigned char RS)
{
TRISA.F0 = 0; //Set Bits 3-0 as OUTPUTS
TRISA.F1 = 0;
TRISA.F2 = 0;
TRISA.F3 = 0;
PORTA = c ; //Bits 3-0 = high order data nibble
to:
// counters for debugging purposes
unsigned char crashed, crash_cause, info, last_i2c ;
//////////////////////////////////////////////////////////////////////
void
write_nibble_to_lcd (const unsigned char c, const unsigned char RS)
{
TRISA.F0 = 0; //Set Bits 3-0 as OUTPUTS
TRISA.F1 = 0;
TRISA.F2 = 0;
TRISA.F3 = 0;
PORTA = c ; //Bits 3-0 = high order data nibble
Changed line 62 from:
PORTB.E_BIT = 0 ;
to:
PORTB.E_BIT = 0 ;
Changed lines 64-67 from:
if (RS)
PORTB.RS_BIT = 1 ;
else
PORTB.RS_BIT = 0 ;
to:
if (RS)
PORTB.RS_BIT = 1 ;
else
PORTB.RS_BIT = 0 ;
Changed lines 69-73 from:
PORTB.WR_BIT = 0 ;
Delay_us (1);
PORTB.E_BIT = 1 ; //E High, WR low, RS set to selected value
Delay_us (1);
PORTB.E_BIT = 0 ;
to:
PORTB.WR_BIT = 0 ;
Delay_us (1);
PORTB.E_BIT = 1 ; //E High, WR low, RS set to selected value
Delay_us (1);
PORTB.E_BIT = 0 ;
Changed lines 75-76 from:
// Exit with E Low, WR Low, RS set to selected value
}
to:
// Exit with E Low, WR Low, RS set to selected value
}
Changed lines 78-81 from:
void
write_byte_to_lcd (const unsigned char c, const unsigned char RS)
{
unsigned char busy ;
to:
void
write_byte_to_lcd (const unsigned char c, const unsigned char RS)
{
unsigned char busy ;
Changed lines 83-86 from:
TRISA.F0 = 1; //Set Bits 3-0 as INPUTS
TRISA.F1 = 1;
TRISA.F2 = 1;
TRISA.F3 = 1;
to:
TRISA.F0 = 1; //Set Bits 3-0 as INPUTS
TRISA.F1 = 1;
TRISA.F2 = 1;
TRISA.F3 = 1;
Changed lines 88-91 from:
PORTB.E_BIT = 0 ;
PORTB.RS_BIT = 0 ;
PORTB.WR_BIT = 1 ;
Delay_us(1);
to:
PORTB.E_BIT = 0 ;
PORTB.RS_BIT = 0 ;
PORTB.WR_BIT = 1 ;
Delay_us(1);
Changed lines 93-94 from:
// Wait for LCD to become unbusy
// BUSY FLAG polled on falling edge of E
to:
// Wait for LCD to become unbusy
// BUSY FLAG polled on falling edge of E
Changed lines 96-107 from:
do // Need to set E high and low twice - two four bit reads
{
PORTB.E_BIT = 1; //WR High=Read, E High
Delay_us(1);
busy = PORTA ; //Read the busy flag in bit 3 (bit 7 of LCD)
PORTB.E_BIT = 0 ; //WR High, E Clocks low
Delay_us(1);
PORTB.E_BIT = 1; //WR High=Read, E High
Delay_us(1);
PORTB.E_BIT = 0 ; //WR High, E Clocks low
Delay_us(1);
} while (busy.F3);
to:
do // Need to set E high and low twice - two four bit reads
{
PORTB.E_BIT = 1; //WR High=Read, E High
Delay_us(1);
busy = PORTA ; //Read the busy flag in bit 3 (bit 7 of LCD)
PORTB.E_BIT = 0 ; //WR High, E Clocks low
Delay_us(1);
PORTB.E_BIT = 1; //WR High=Read, E High
Delay_us(1);
PORTB.E_BIT = 0 ; //WR High, E Clocks low
Delay_us(1);
} while (busy.F3);
Changed lines 109-111 from:
// Now the LCD is ready, write byte in two 4 bit nibbles
write_nibble_to_lcd (c >> 4, RS) ;
write_nibble_to_lcd (c & 0x0f, RS) ;
to:
// Now the LCD is ready, write byte in two 4 bit nibbles
write_nibble_to_lcd (c >> 4, RS) ;
write_nibble_to_lcd (c & 0x0f, RS) ;
Changed lines 113-114 from:
// Exit with E Low, WR Low, RS set to selected value
}
to:
// Exit with E Low, WR Low, RS set to selected value
}
Changed lines 116-121 from:
void
string_to_lcd (const unsigned char *s)
{
while (*s)
write_byte_to_lcd (*s++, 1) ;
}
to:
void
string_to_lcd (const unsigned char *s)
{
while (*s)
write_byte_to_lcd (*s++, 1) ;
}
Changed lines 123-127 from:
void
decbyte_to_lcd (unsigned char c)
{
char *s ;
unsigned char text[4] ;
to:
void
decbyte_to_lcd (unsigned char c)
{
char *s ;
unsigned char text[4] ;
Changed line 129 from:
to:
Changed lines 131-133 from:
for (s=text; *s ;)
write_byte_to_lcd (*s++, 1) ;
}
to:
for (s=text; *s ;)
write_byte_to_lcd (*s++, 1) ;
}
Changed lines 135-138 from:
void
hexnibble_to_lcd (unsigned char c)
{
c &= 0x0f ;
to:
void
hexnibble_to_lcd (unsigned char c)
{
c &= 0x0f ;
Changed lines 140-143 from:
if (c > 9)
c += 'A' - 10 ;
else
c += '0' ;
to:
if (c > 9)
c += 'A' - 10 ;
else
c += '0' ;
Changed lines 145-146 from:
write_byte_to_lcd (c, 1) ;
}
to:
write_byte_to_lcd (c, 1) ;
}
Changed lines 148-156 from:
void hexbyte_to_lcd (unsigned char c)
{
hexnibble_to_lcd (c >> 4) ;
hexnibble_to_lcd (c) ;
}
//==============================================================================
// BUFFER_POWER must be >= 2
#define BUFFER_LEN 64
#define BUFFER_MASK (BUFFER_LEN-1)
to:
void hexbyte_to_lcd (unsigned char c)
{
hexnibble_to_lcd (c >> 4) ;
hexnibble_to_lcd (c) ;
}
//==============================================================================
// BUFFER_POWER must be >= 2
#define BUFFER_LEN 64
#define BUFFER_MASK (BUFFER_LEN-1)
Changed lines 158-159 from:
unsigned char rxbuf [BUFFER_LEN] ;
unsigned char rx_head, rx_tail, rx_len ;
to:
unsigned char rxbuf [BUFFER_LEN] ;
unsigned char rx_head, rx_tail, rx_len ;
Changed lines 161-164 from:
void
interrupt ()
{
unsigned char stat;
to:
void
interrupt ()
{
unsigned char stat;
Changed lines 166-171 from:
{
SSPCON.F4 = 0 ; // Hold clock low - (ie stretch clock while we work)
stat = SSPSTAT & 0x2d ; // mask out bits we're not interestted in
// 5=(1=data,0=addr), 3=(1=start detected),
// 2=(1=read,0=write), 0=(1=sspbuf full)
to:
{
SSPCON.F4 = 0 ; // Hold clock low - (ie stretch clock while we work)
stat = SSPSTAT & 0x2d ; // mask out bits we're not interestted in
// 5=(1=data,0=addr), 3=(1=start detected),
// 2=(1=read,0=write), 0=(1=sspbuf full)
Changed lines 173-180 from:
{
last_i2c = SSPBUF ; // Do a dummy read of the sspbuf
}
{
rxbuf[rx_head++] = SSPBUF ;
rx_head &= BUFFER_MASK ;
to:
{
last_i2c = SSPBUF ; // Do a dummy read of the sspbuf
}
{
rxbuf[rx_head++] = SSPBUF ;
rx_head &= BUFFER_MASK ;
Changed lines 182-187 from:
if (rx_len++ >= BUFFER_LEN)
crashed = 1 ; //flag error
}
{
while (SSPSTAT.F0 == 1) ; // keep waiting while buffer full
to:
if (rx_len++ >= BUFFER_LEN)
crashed = 1 ; //flag error
}
{
while (SSPSTAT.F0 == 1) ; // keep waiting while buffer full
Changed lines 189-197 from:
do
{
SSPCON.F7 = 0 ; // Clear the write collision flag
SSPBUF = 0xaa ; // Just return AA for the time being
} while (SSPCON.F7 == 1) ; // Loop if there was a write collision
}
{
while (SSPSTAT.F0 == 1) ; // keep waiting while buffer full
to:
do
{
SSPCON.F7 = 0 ; // Clear the write collision flag
SSPBUF = 0xaa ; // Just return AA for the time being
} while (SSPCON.F7 == 1) ; // Loop if there was a write collision
}
{
while (SSPSTAT.F0 == 1) ; // keep waiting while buffer full
Changed lines 199-215 from:
do
{
SSPCON.F7 = 0 ; // Clear the write collision flag
SSPBUF = 0x55 ; // Just return 55 for the time being
} while (SSPCON.F7 == 1) ; // Loop if there was a write collision
}
else // NACK received 0x28 - or another condition - Do a reset
{
last_i2c = SSPBUF ; // Do a dummy read of the sspbuf
SSPCON.F5=0; // Temporarily disable
SSPCON = 0x36 ; // F7=0 WCOL Write Collision Detect bit
// F6=0 SSPOV Receive Overflow Indicator bit
// F5=1 Synchronous Serial Port Enable
SSPSTAT = 0 ; // Clear all status bits
}
to:
do
{
SSPCON.F7 = 0 ; // Clear the write collision flag
SSPBUF = 0x55 ; // Just return 55 for the time being
} while (SSPCON.F7 == 1) ; // Loop if there was a write collision
}
else // NACK received 0x28 - or another condition - Do a reset
{
last_i2c = SSPBUF ; // Do a dummy read of the sspbuf
SSPCON.F5=0; // Temporarily disable
SSPCON = 0x36 ; // F7=0 WCOL Write Collision Detect bit
// F6=0 SSPOV Receive Overflow Indicator bit
// F5=1 Synchronous Serial Port Enable
SSPSTAT = 0 ; // Clear all status bits
}
Changed lines 217-231 from:
SSPCON.F4 = 1 ; // Release the clock (finish stretching it)
}
else // unknown interrupt!!
{
crashed = 3;
}
}
//==============================================================================
#define CLEAR_AND_FIRST_ROW 0x01
#define FIRST_ROW 0x02
#define SECOND_ROW (0x80 | 0x40)
#define THIRD_ROW (0x80 | 0x14)
#define FOURTH_ROW (0x80 | 0x54)
to:
SSPCON.F4 = 1 ; // Release the clock (finish stretching it)
}
else // unknown interrupt!!
{
crashed = 3;
}
}
//==============================================================================
#define CLEAR_AND_FIRST_ROW 0x01
#define FIRST_ROW 0x02
#define SECOND_ROW (0x80 | 0x40)
#define THIRD_ROW (0x80 | 0x14)
#define FOURTH_ROW (0x80 | 0x54)
Changed line 233 from:
unsigned char line, column ;
to:
unsigned char line, column ;
Changed lines 235-238 from:
void
linefeed ()
{
unsigned char cmd, i ;
to:
void
linefeed ()
{
unsigned char cmd, i ;
Changed lines 240-247 from:
// Blank the appropriate line.
switch (line)
{
case 1: cmd = FIRST_ROW ; break ;
case 2: cmd = SECOND_ROW ; break ;
case 3: cmd = THIRD_ROW ; break ;
default: cmd = FOURTH_ROW ; break ;
}
to:
// Blank the appropriate line.
switch (line)
{
case 1: cmd = FIRST_ROW ; break ;
case 2: cmd = SECOND_ROW ; break ;
case 3: cmd = THIRD_ROW ; break ;
default: cmd = FOURTH_ROW ; break ;
}
Changed line 249 from:
write_byte_to_lcd (cmd, 0); // Go to start of line
to:
write_byte_to_lcd (cmd, 0); // Go to start of line
Changed lines 251-252 from:
for (i=0 ; i<COLUMNS ; i++) // Blank the entire line
write_byte_to_lcd (' ', 1);
to:
for (i=0 ; i<COLUMNS ; i++) // Blank the entire line
write_byte_to_lcd (' ', 1);
Changed lines 254-256 from:
write_byte_to_lcd (cmd, 0); // Return to start of line
column = 0 ;
}
to:
write_byte_to_lcd (cmd, 0); // Return to start of line
column = 0 ;
}
Changed lines 258-271 from:
void
printchar (const unsigned char c)
{
if (c == 0)
{
// Clear display entirely
write_byte_to_lcd (CLEAR_AND_FIRST_ROW, 0);
line = 1 ;
column = 0 ;
}
else if (c == '\n')
{
// linefeed and wrap around to line 0
line++ ;
to:
void
printchar (const unsigned char c)
{
if (c == 0)
{
// Clear display entirely
write_byte_to_lcd (CLEAR_AND_FIRST_ROW, 0);
line = 1 ;
column = 0 ;
}
else if (c == '\n')
{
// linefeed and wrap around to line 0
line++ ;
Changed lines 273-274 from:
if (line > LINES)
line = 1 ;
to:
if (line > LINES)
line = 1 ;
Changed lines 276-293 from:
linefeed() ;
}
else if (c < 5)
{
// move to line 1..4
line = c ;
linefeed() ;
}
else if (c == '\r')
{
linefeed() ;
}
else
{
if (column >= COLUMNS)
{
// need to force a linefeed
line++ ;
to:
linefeed() ;
}
else if (c < 5)
{
// move to line 1..4
line = c ;
linefeed() ;
}
else if (c == '\r')
{
linefeed() ;
}
else
{
if (column >= COLUMNS)
{
// need to force a linefeed
line++ ;
Changed lines 295-296 from:
if (line > LINES)
line = 1 ;
to:
if (line > LINES)
line = 1 ;
Changed lines 298-299 from:
linefeed() ;
}
to:
linefeed() ;
}
Changed lines 301-305 from:
// Display the numbered character
write_byte_to_lcd (c, 1);
column++ ;
}
}
to:
// Display the numbered character
write_byte_to_lcd (c, 1);
column++ ;
}
}
Changed lines 307-317 from:
// Enqueue_string will print anything BUT NOT the \000 CLEAR command!!!!
void
enqueue_string (const unsigned char *s)
{
while (*s)
{
rxbuf[rx_head++] = *s++ ;
rx_head &= BUFFER_MASK ;
rx_len++ ;
to:
// Enqueue_string will print anything BUT NOT the \000 CLEAR command!!!!
void
enqueue_string (const unsigned char *s)
{
while (*s)
{
rxbuf[rx_head++] = *s++ ;
rx_head &= BUFFER_MASK ;
rx_len++ ;
Changed lines 319-325 from:
if (rx_len > BUFFER_LEN)
{
crashed = 4 ; //flag error
return ;
}
}
}
to:
if (rx_len > BUFFER_LEN)
{
crashed = 4 ; //flag error
return ;
}
}
}
Changed lines 327-331 from:
void
enqueue_number (const unsigned char c)
{
unsigned char text[4] ;
unsigned char *s ;
to:
void
enqueue_number (const unsigned char c)
{
unsigned char text[4] ;
unsigned char *s ;
Changed line 333 from:
to:
Changed lines 335-338 from:
for (s=text ; *s ; s++)
{
if (*s == ' ')
continue ;
to:
for (s=text ; *s ; s++)
{
if (*s == ' ')
continue ;
Changed lines 340-342 from:
rxbuf[rx_head++] = *s ;
rx_head &= BUFFER_MASK ;
rx_len++ ;
to:
rxbuf[rx_head++] = *s ;
rx_head &= BUFFER_MASK ;
rx_len++ ;
Changed lines 344-349 from:
if (rx_len > BUFFER_LEN)
crashed = 5 ; //flag error
}
}
//==============================================================================
#define MAGIC 0x3d
to:
if (rx_len > BUFFER_LEN)
crashed = 5 ; //flag error
}
}
//==============================================================================
#define MAGIC 0x3d
Changed lines 351-352 from:
unsigned char num_boots ;
unsigned char magic ; // Will hold a magic number so we know a cold boot
to:
unsigned char num_boots ;
unsigned char magic ; // Will hold a magic number so we know a cold boot
Changed lines 354-357 from:
void
main()
{
char i ;
to:
void
main()
{
char i ;
Changed lines 359-361 from:
// Osc = bits 6,5,4
OSCCON=0x7c; // Internal osc: 0x5c=2MHz, 0x7c=8MHz
while (OSCCON.F2 == 0) ; // Wait for osc to stabilise
to:
// Osc = bits 6,5,4
OSCCON=0x7c; // Internal osc: 0x5c=2MHz, 0x7c=8MHz
while (OSCCON.F2 == 0) ; // Wait for osc to stabilise
Changed lines 363-366 from:
restart:
OPTION_REG=0x03; //0x03=Prescaler=1:256 0x80=low..pullup enabled
INTCON=0; //Disable interrupts
to:
restart:
OPTION_REG=0x03; //0x03=Prescaler=1:256 0x80=low..pullup enabled
INTCON=0; //Disable interrupts
Changed lines 368-371 from:
TRISA=0; //Set Port A to all outputs
PORTA=0x00;
PORTB=0x00;
to:
TRISA=0; //Set Port A to all outputs
PORTA=0x00;
PORTB=0x00;
Changed line 373 from:
WDTCON =0x17 ; // Enable watchdog with max timeout value = approx 2s
to:
WDTCON =0x17 ; // Enable watchdog with max timeout value = approx 2s
Changed lines 375-376 from:
to:
Changed lines 378-385 from:
SSPCON = 0x36 ; // F7=0 WCOL Write Collision Detect bit
// F6=0 SSPOV Receive Overflow Indicator bit
// F5=1 Synchronous Serial Port Enable
SSPSTAT = 0 ; // Clear all status bits
to:
SSPCON = 0x36 ; // F7=0 WCOL Write Collision Detect bit
// F6=0 SSPOV Receive Overflow Indicator bit
// F5=1 Synchronous Serial Port Enable
SSPSTAT = 0 ; // Clear all status bits
Changed lines 387-394 from:
if (magic != MAGIC)
{
crash_cause=0 ; // used for debugging interrupt routine with print statements
num_boots = 0 ;
magic = MAGIC ;
}
else
num_boots++ ;
to:
if (magic != MAGIC)
{
crash_cause=0 ; // used for debugging interrupt routine with print statements
num_boots = 0 ;
magic = MAGIC ;
}
else
num_boots++ ;
Changed lines 396-397 from:
crashed = 0 ; // We're not crashed at the moment!
rx_head = rx_tail = rx_len = 0 ;
to:
crashed = 0 ; // We're not crashed at the moment!
rx_head = rx_tail = rx_len = 0 ;
Changed lines 399-400 from:
INTCON.F6 = 1 ; // enable peripheral interrupts
INTCON.F7 = 1 ; // enable all unmasked interrupts
to:
INTCON.F6 = 1 ; // enable peripheral interrupts
INTCON.F7 = 1 ; // enable all unmasked interrupts
Changed lines 402-404 from:
TXSTA = 0 ; // Turn off transmit from UART
RCSTA = 0 ;
CMCON = 0x07 ; // Disable comparators
to:
TXSTA = 0 ; // Turn off transmit from UART
RCSTA = 0 ;
CMCON = 0x07 ; // Disable comparators
Changed lines 406-409 from:
Delay_ms (100); // Provide a little time for the LCD to initialise
asm {
CLRWDT // Clear the watchdog timer
}
to:
Delay_ms (100); // Provide a little time for the LCD to initialise
asm {
CLRWDT // Clear the watchdog timer
}
Changed lines 411-418 from:
write_nibble_to_lcd (0x03,0); // Function set 8 bit mode
Delay_ms (5); // Wait at least 4.1ms
write_nibble_to_lcd (0x03,0); // Function set 8 bit mode
Delay_ms (1); // Wait at least 100us
write_nibble_to_lcd (0x03,0); // Function set 8 bit mode
Delay_ms (1); // Wait at least 100us
write_nibble_to_lcd (0x02,0); // NOW FINALLY Function set 4 bit mode
Delay_ms (1); // Wait at least 100us
to:
write_nibble_to_lcd (0x03,0); // Function set 8 bit mode
Delay_ms (5); // Wait at least 4.1ms
write_nibble_to_lcd (0x03,0); // Function set 8 bit mode
Delay_ms (1); // Wait at least 100us
write_nibble_to_lcd (0x03,0); // Function set 8 bit mode
Delay_ms (1); // Wait at least 100us
write_nibble_to_lcd (0x02,0); // NOW FINALLY Function set 4 bit mode
Delay_ms (1); // Wait at least 100us
Changed lines 420-426 from:
write_byte_to_lcd (0x28, 0); //And repeat, doing full 8 bits of function cmd
write_byte_to_lcd (0x0c, 0); //Display ON (04), Curson ON(02), Blinking(01)
write_byte_to_lcd (0x01, 0); //Clear display
write_byte_to_lcd (0x06, 0); //Entry mode Increment(02) Noshift(01)
write_byte_to_lcd (CLEAR_AND_FIRST_ROW, 0);
line = 1;
column = 0 ;
to:
write_byte_to_lcd (0x28, 0); //And repeat, doing full 8 bits of function cmd
write_byte_to_lcd (0x0c, 0); //Display ON (04), Curson ON(02), Blinking(01)
write_byte_to_lcd (0x01, 0); //Clear display
write_byte_to_lcd (0x06, 0); //Entry mode Increment(02) Noshift(01)
write_byte_to_lcd (CLEAR_AND_FIRST_ROW, 0);
line = 1;
column = 0 ;
Changed lines 428-434 from:
// Display the welcome message
enqueue_number (LINES) ;
enqueue_string ("x") ;
enqueue_number (COLUMNS) ;
enqueue_string (" lcd @addr ") ;
enqueue_string ("\nVersion 1. SKT");
to:
// Display the welcome message
enqueue_number (LINES) ;
enqueue_string ("x") ;
enqueue_number (COLUMNS) ;
enqueue_string (" lcd @addr ") ;
enqueue_string ("\nVersion 1");
Changed lines 436-441 from:
// Give some indication if there's been a crash
if (num_boots)
{
enqueue_string ("\rBoot count: ");
enqueue_number (num_boots) ;
}
to:
// Give some indication if there's been a crash
if (num_boots)
{
enqueue_string ("\rBoot count: ");
enqueue_number (num_boots) ;
}
Changed lines 443-447 from:
for (;;)
{
asm {
CLRWDT // clear the watchdog timer
}
to:
for (;;)
{
asm {
CLRWDT // clear the watchdog timer
}
Changed lines 449-456 from:
if (rx_len)
{
printchar (rxbuf[rx_tail++]) ;
rx_tail &= BUFFER_MASK ;
rx_len-- ;
}
to:
if (rx_len)
{
printchar (rxbuf[rx_tail++]) ;
rx_tail &= BUFFER_MASK ;
rx_len-- ;
}
Changed lines 458-469 from:
if (crashed)
{
crash_cause = crashed ; // Remember why we crashed
write_byte_to_lcd (CLEAR_AND_FIRST_ROW, 0);
string_to_lcd ("Cause:") ;
hexbyte_to_lcd (crash_cause);
string_to_lcd (" last:") ;
hexbyte_to_lcd (last_i2c) ;
string_to_lcd (" info:") ;
hexbyte_to_lcd (info);
string_to_lcd (" boots:") ;
hexbyte_to_lcd (num_boots);
to:
if (crashed)
{
crash_cause = crashed ; // Remember why we crashed
write_byte_to_lcd (CLEAR_AND_FIRST_ROW, 0);
string_to_lcd ("Cause:") ;
hexbyte_to_lcd (crash_cause);
string_to_lcd (" last:") ;
hexbyte_to_lcd (last_i2c) ;
string_to_lcd (" info:") ;
hexbyte_to_lcd (info);
string_to_lcd (" boots:") ;
hexbyte_to_lcd (num_boots);
Changed lines 471-474 from:
write_byte_to_lcd (SECOND_ROW, 0);
string_to_lcd ("Buf len:") ;
decbyte_to_lcd (rx_len);
write_byte_to_lcd (' ', 1);
to:
write_byte_to_lcd (SECOND_ROW, 0);
string_to_lcd ("Buf len:") ;
decbyte_to_lcd (rx_len);
write_byte_to_lcd (' ', 1);
Changed lines 476-477 from:
for (i=0 ; i < 14 ; i++)
hexbyte_to_lcd (rxbuf[(rx_tail+i)&BUFFER_MASK]);
to:
for (i=0 ; i < 14 ; i++)
hexbyte_to_lcd (rxbuf[(rx_tail+i)&BUFFER_MASK]);
Changed lines 479-483 from:
// The following kludge is to get around the non-reentrant version of C
// If there's an error inside the Interrupt, this flag will be set
// That lets us display something - and this loop will cause an infinte wait
// while (crashed) ; // Infinite loop if an error has been set in the ISR
// This will trip the watchdog timer some time later
to:
// The following kludge is to get around the non-reentrant version of C
// If there's an error inside the Interrupt, this flag will be set
// That lets us display something - and this loop will cause an infinte wait
// while (crashed) ; // Infinite loop if an error has been set in the ISR
// This will trip the watchdog timer some time later
Changed lines 485-492 from:
// Delay for 15 seconds
for (i=0 ; i < 150 ; i++)
{
Delay_ms (100);
asm {
CLRWDT // clear the watchdog timer
}
}
to:
// Delay for 15 seconds
for (i=0 ; i < 150 ; i++)
{
Delay_ms (100);
asm {
CLRWDT // clear the watchdog timer
}
}
Changed lines 494-499 from:
goto restart ;
}
}
}
to:
goto restart ;
}
}
}
Changed line 503 from:
Other tipsto:
Other tipsJune 14, 2008, at 03:27 AM
by --
Changed line 15 from:
// 16F88 programme to drive a hitachi compatible LCD display from the
to:
// 16F88 programme to drive a hitachi compatible LCD display from the
June 14, 2008, at 03:26 AM
by --
Added lines 20-26:
// Define the number of lines and columns on the LCD display
#define LINES 4
#define COLUMNS 20
// This is the assignment of 16F88 pins I chose
Deleted lines 49-52:
// Define the number of lines and columns on the LCD display
#define LINES 4
#define COLUMNS 20
Deleted line 349:
Changed lines 360-368 from:
//000 = 31.25 kHz
//001 = 125 kHz
//010 = 250 kHz
//011 = 500 kHz
OSCCON=0x7c; // Internal osc: 0x2c=0.250MHz, 0x4c=1MHz, 0x5c=2MHz, 0x7c=8MHz
to:
OSCCON=0x7c; // Internal osc: 0x5c=2MHz, 0x7c=8MHz
June 14, 2008, at 03:22 AM
by --
Changed lines 15-505 from:
@@// 16F88 programme to drive a hitachi compatible LCD display from the
@@// i2C bus
// // There are plenty of spare inputs and outputs for additional functions ////////////////////////////////////////////////////////////////////// // PORT A bit 0 = D4 on LCD // PORT A bit 1 = D5 on LCD // PORT A bit 2 = D6 on LCD // PORT A bit 3 = D7 on LCD // PORT A bit 4 = NC // PORT A bit 5 = pulled high with 10k // PORT A bit 6 = NC // PORT A bit 7 = NC // // PORT B bit 0 = E on LCD // PORT B bit 1 = SD on I2C? // PORT B bit 2 = RW on LCD // PORT B bit 3 = RS on LCD // PORT B bit 4 = CK on I2C? // PORT B bit 5 = NC // PORT B bit 6 = NC // PORT B bit 7 = NC ] // Fx is mikroC nomenclature for bit x
// Define the number of lines and columns on the LCD display
// counters for debugging purposes unsigned char crashed, crash_cause, info, last_i2c ; ////////////////////////////////////////////////////////////////////// void write_nibble_to_lcd (const unsigned char c, const unsigned char RS) { TRISA.F0 = 0; //Set Bits 3-0 as OUTPUTS
TRISA.F1 = 0;
TRISA.F2 = 0;
TRISA.F3 = 0;
PORTA = c ; //Bits 3-0 = high order data nibble
PORTB.E_BIT = 0 ;
if (RS)
PORTB.RS_BIT = 1 ;
else
PORTB.RS_BIT = 0 ;
PORTB.WR_BIT = 0 ;
Delay_us (1);
PORTB.E_BIT = 1 ; //E High, WR low, RS set to selected value
Delay_us (1);
PORTB.E_BIT = 0 ;
// Exit with E Low, WR Low, RS set to selected value
} void write_byte_to_lcd (const unsigned char c, const unsigned char RS) { unsigned char busy ;
TRISA.F0 = 1; //Set Bits 3-0 as INPUTS
TRISA.F1 = 1;
TRISA.F2 = 1;
TRISA.F3 = 1;
PORTB.E_BIT = 0 ;
PORTB.RS_BIT = 0 ;
PORTB.WR_BIT = 1 ;
Delay_us(1);
// Wait for LCD to become unbusy
// BUSY FLAG polled on falling edge of E
do // Need to set E high and low twice - two four bit reads
{
PORTB.E_BIT = 1; //WR High=Read, E High
Delay_us(1);
busy = PORTA ; //Read the busy flag in bit 3 (bit 7 of LCD)
PORTB.E_BIT = 0 ; //WR High, E Clocks low
Delay_us(1);
PORTB.E_BIT = 1; //WR High=Read, E High
Delay_us(1);
PORTB.E_BIT = 0 ; //WR High, E Clocks low
Delay_us(1);
} while (busy.F3);
// Now the LCD is ready, write byte in two 4 bit nibbles
write_nibble_to_lcd (c >> 4, RS) ;
write_nibble_to_lcd (c & 0x0f, RS) ;
// Exit with E Low, WR Low, RS set to selected value
} void string_to_lcd (const unsigned char *s) { while (*s)
write_byte_to_lcd (*s++, 1) ;
} void decbyte_to_lcd (unsigned char c) { char *s ;
unsigned char text[4] ;
ByteToStr? (c, text);
for (s=text; *s ;)
write_byte_to_lcd (*s++, 1) ;
} void hexnibble_to_lcd (unsigned char c) { c &= 0x0f ;
if (c > 9)
c += 'A' - 10 ;
else
c += '0' ;
write_byte_to_lcd (c, 1) ;
} void hexbyte_to_lcd (unsigned char c) { hexnibble_to_lcd (c >> 4) ; hexnibble_to_lcd (c) ; } //============================================================================== // BUFFER_POWER must be >= 2
unsigned char rxbuf [BUFFER_LEN] ; unsigned char rx_head, rx_tail, rx_len ; void interrupt () { unsigned char stat; if (PIR1?.F3 == 1) // is this an i2c interrupt { SSPCON.F4 = 0 ; // Hold clock low - (ie stretch clock while we work) stat = SSPSTAT & 0x2d ; // mask out bits we're not interestted in // 5=(1=data,0=addr), 3=(1=start detected), // 2=(1=read,0=write), 0=(1=sspbuf full) if (stat == 0x09) // I2C? Write: Byte received was an address { last_i2c = SSPBUF ; // Do a dummy read of the sspbuf } else if (stat == 0x29) // I2C? Write: Byte received was data { rxbuf[rx_head++] = SSPBUF ; rx_head &= BUFFER_MASK ; if (rx_len++ >= BUFFER_LEN) crashed = 1 ; //flag error } else if (stat == 0x0c) // I2C? Read: Byte received was an address { while (SSPSTAT.F0 == 1) ; // keep waiting while buffer full do { SSPCON.F7 = 0 ; // Clear the write collision flag SSPBUF = 0xaa ; // Just return AA for the time being } while (SSPCON.F7 == 1) ; // Loop if there was a write collision } else if (stat == 0x2c) // I2C? Read: Byte to be transmitted will be data { while (SSPSTAT.F0 == 1) ; // keep waiting while buffer full do { SSPCON.F7 = 0 ; // Clear the write collision flag SSPBUF = 0x55 ; // Just return 55 for the time being } while (SSPCON.F7 == 1) ; // Loop if there was a write collision } else // NACK received 0x28 - or another condition - Do a reset { last_i2c = SSPBUF ; // Do a dummy read of the sspbuf SSPCON.F5=0; // Temporarily disable SSPCON = 0x36 ; // F7=0 WCOL Write Collision Detect bit // F6=0 SSPOV Receive Overflow Indicator bit // F5=1 Synchronous Serial Port Enable // F4=1 When set at 0, I2C? clock pin held low to stretch clock // F3-0=0110: I2C? slave mode: 7 bits address SSPSTAT = 0 ; // Clear all status bits } SSPCON.F4 = 1 ; // Release the clock (finish stretching it) PIR1?.F3 = 0 ; // Clear the interrupt } else // unknown interrupt!! { crashed = 3; info = PIR1? ; } } //==============================================================================
unsigned char line, column ; void linefeed () { unsigned char cmd, i ;
// Blank the appropriate line.
switch (line)
{
case 1: cmd = FIRST_ROW ; break ;
case 2: cmd = SECOND_ROW ; break ;
case 3: cmd = THIRD_ROW ; break ;
default: cmd = FOURTH_ROW ; break ;
}
write_byte_to_lcd (cmd, 0); // Go to start of line
for (i=0 ; i<COLUMNS ; i++) // Blank the entire line
write_byte_to_lcd (' ', 1);
write_byte_to_lcd (cmd, 0); // Return to start of line
column = 0 ;
} void printchar (const unsigned char c) { if (c == 0)
{
// Clear display entirely
write_byte_to_lcd (CLEAR_AND_FIRST_ROW, 0);
line = 1 ;
column = 0 ;
}
else if (c == '\n')
{
// linefeed and wrap around to line 0
line++ ;
if (line > LINES)
line = 1 ;
linefeed() ;
}
else if (c < 5)
{
// move to line 1..4
line = c ;
linefeed() ;
}
else if (c == '\r')
{
linefeed() ;
}
else
{
if (column >= COLUMNS)
{
// need to force a linefeed
line++ ;
if (line > LINES)
line = 1 ;
linefeed() ;
}
// Display the numbered character
write_byte_to_lcd (c, 1);
column++ ;
}
} // Enqueue_string will print anything BUT NOT the \000 CLEAR command!!!! void enqueue_string (const unsigned char *s) { while (*s)
{
PIE1?.F3 = 0; // Disable int F3=I2C? int
rxbuf[rx_head++] = *s++ ;
rx_head &= BUFFER_MASK ;
rx_len++ ;
PIE1?.F3 = 1; // Enable int F3=I2C? int
if (rx_len > BUFFER_LEN)
{
crashed = 4 ; //flag error
return ;
}
}
} void enqueue_number (const unsigned char c) { unsigned char text[4] ;
unsigned char *s ;
ByteToStr? (c, text);
for (s=text ; *s ; s++)
{
if (*s == ' ')
continue ;
rxbuf[rx_head++] = *s ;
rx_head &= BUFFER_MASK ;
rx_len++ ;
if (rx_len > BUFFER_LEN)
crashed = 5 ; //flag error
}
} //============================================================================== unsigned char num_boots ; unsigned char magic ; // Will hold a magic number so we know a cold boot void main() { char i ; // Osc = bits 6,5,4 //000 = 31.25 kHz //001 = 125 kHz //010 = 250 kHz //011 = 500 kHz //100 = 1 MHz? //101 = 2 MHz? //110 = 4 MHz? //111 = 8 MHz? OSCCON=0x7c; // Internal osc: 0x2c=0.250MHz, 0x4c=1MHz, 0x5c=2MHz, 0x7c=8MHz while (OSCCON.F2 == 0) ; // Wait for osc to stabilise restart: OPTION_REG=0x03; //0x03=Prescaler=1:256 0x80=low..pullup enabled INTCON=0; //Disable interrupts PIR1?=0; //Clear all interrupt flags TRISA=0; //Set Port A to all outputs TRISB=0x12; //B4 and B1 are inputs (I2C?) PORTA=0x00; PORTB=0x00; WDTCON =0x17 ; // Enable watchdog with max timeout value = approx 2s ANSEL=0; //0 = PortA? bit is used for digital I/O ADCON1?=0x07 ; // Disable A/D SSPCON = 0x36 ; // F7=0 WCOL Write Collision Detect bit // F6=0 SSPOV Receive Overflow Indicator bit // F5=1 Synchronous Serial Port Enable // F4=1 When set at 0, I2C? clock pin held low to stretch clock // F3-0=0110: I2C? slave mode: 7 bits address SSPADD = I2C?_ADDRESS << 1 ; // 7 bit address in bits 7--1 SSPSTAT = 0 ; // Clear all status bits PIE1?.F3 = 1; // F6=ADC int, F3=I2C? int if (magic != MAGIC) { crash_cause=0 ; // used for debugging interrupt routine with print statements num_boots = 0 ; magic = MAGIC ; } else num_boots++ ; crashed = 0 ; // We're not crashed at the moment! rx_head = rx_tail = rx_len = 0 ; INTCON.F6 = 1 ; // enable peripheral interrupts INTCON.F7 = 1 ; // enable all unmasked interrupts TXSTA = 0 ; // Turn off transmit from UART RCSTA = 0 ; CMCON = 0x07 ; // Disable comparators Delay_ms (100); // Provide a little time for the LCD to initialise asm { CLRWDT // Clear the watchdog timer } write_nibble_to_lcd (0x03,0); // Function set 8 bit mode Delay_ms (5); // Wait at least 4.1ms write_nibble_to_lcd (0x03,0); // Function set 8 bit mode Delay_ms (1); // Wait at least 100us write_nibble_to_lcd (0x03,0); // Function set 8 bit mode Delay_ms (1); // Wait at least 100us write_nibble_to_lcd (0x02,0); // NOW FINALLY Function set 4 bit mode Delay_ms (1); // Wait at least 100us write_byte_to_lcd (0x28, 0); //And repeat, doing full 8 bits of function cmd write_byte_to_lcd (0x0c, 0); //Display ON (04), Curson ON(02), Blinking(01) write_byte_to_lcd (0x01, 0); //Clear display write_byte_to_lcd (0x06, 0); //Entry mode Increment(02) Noshift(01) write_byte_to_lcd (CLEAR_AND_FIRST_ROW, 0); line = 1; column = 0 ; // Display the welcome message enqueue_number (LINES) ; enqueue_string ("x") ; enqueue_number (COLUMNS) ; enqueue_string (" lcd @addr ") ; enqueue_number (I2C?_ADDRESS) ; enqueue_string ("\nVersion 1. SKT"); // Give some indication if there's been a crash if (num_boots) { enqueue_string ("\rBoot count: "); enqueue_number (num_boots) ; } for (;;) { asm { CLRWDT // clear the watchdog timer } if (rx_len) { printchar (rxbuf[rx_tail++]) ; rx_tail &= BUFFER_MASK ; PIE1?.F3 = 0; // Disable int F3=I2C? int rx_len-- ; PIE1?.F3 = 1; // Enable int F3=I2C? int } if (crashed) { crash_cause = crashed ; // Remember why we crashed write_byte_to_lcd (CLEAR_AND_FIRST_ROW, 0); string_to_lcd ("Cause:") ; hexbyte_to_lcd (crash_cause); string_to_lcd (" last:") ; hexbyte_to_lcd (last_i2c) ; string_to_lcd (" info:") ; hexbyte_to_lcd (info); string_to_lcd (" boots:") ; hexbyte_to_lcd (num_boots); write_byte_to_lcd (SECOND_ROW, 0); string_to_lcd ("Buf len:") ; decbyte_to_lcd (rx_len); write_byte_to_lcd (' ', 1); for (i=0 ; i < 14 ; i++) hexbyte_to_lcd (rxbuf[(rx_tail+i)&BUFFER_MASK]); // The following kludge is to get around the non-reentrant version of C // If there's an error inside the Interrupt, this flag will be set // That lets us display something - and this loop will cause an infinte wait // while (crashed) ; // Infinite loop if an error has been set in the ISR // This will trip the watchdog timer some time later
// Delay for 15 seconds
for (i=0 ; i < 150 ; i++)
{
Delay_ms (100);
asm {
CLRWDT // clear the watchdog timer
}
}
goto restart ;
}
}
}] to:
// 16F88 programme to drive a hitachi compatible LCD display from the
// i2C bus
//
// There are plenty of spare inputs and outputs for additional functions
//////////////////////////////////////////////////////////////////////
// PORT A bit 0 = D4 on LCD
// PORT A bit 1 = D5 on LCD
// PORT A bit 2 = D6 on LCD
// PORT A bit 3 = D7 on LCD
// PORT A bit 4 = NC
// PORT A bit 5 = pulled high with 10k
// PORT A bit 6 = NC
// PORT A bit 7 = NC
//
// PORT B bit 0 = E on LCD
// PORT B bit 2 = RW on LCD
// PORT B bit 3 = RS on LCD
// PORT B bit 5 = NC
// PORT B bit 6 = NC
// PORT B bit 7 = NC
// Fx is mikroC nomenclature for bit x
#define RS_BIT F3
#define WR_BIT F2
#define E_BIT F0
// Define the number of lines and columns on the LCD display
#define LINES 4
#define COLUMNS 20
// counters for debugging purposes
unsigned char crashed, crash_cause, info, last_i2c ;
//////////////////////////////////////////////////////////////////////
void
write_nibble_to_lcd (const unsigned char c, const unsigned char RS)
{
TRISA.F0 = 0; //Set Bits 3-0 as OUTPUTS
TRISA.F1 = 0;
TRISA.F2 = 0;
TRISA.F3 = 0;
PORTA = c ; //Bits 3-0 = high order data nibble
PORTB.E_BIT = 0 ;
if (RS)
PORTB.RS_BIT = 1 ;
else
PORTB.RS_BIT = 0 ;
PORTB.WR_BIT = 0 ;
Delay_us (1);
PORTB.E_BIT = 1 ; //E High, WR low, RS set to selected value
Delay_us (1);
PORTB.E_BIT = 0 ;
// Exit with E Low, WR Low, RS set to selected value
}
void
write_byte_to_lcd (const unsigned char c, const unsigned char RS)
{
unsigned char busy ;
TRISA.F0 = 1; //Set Bits 3-0 as INPUTS
TRISA.F1 = 1;
TRISA.F2 = 1;
TRISA.F3 = 1;
PORTB.E_BIT = 0 ;
PORTB.RS_BIT = 0 ;
PORTB.WR_BIT = 1 ;
Delay_us(1);
// Wait for LCD to become unbusy
// BUSY FLAG polled on falling edge of E
do // Need to set E high and low twice - two four bit reads
{
PORTB.E_BIT = 1; //WR High=Read, E High
Delay_us(1);
busy = PORTA ; //Read the busy flag in bit 3 (bit 7 of LCD)
PORTB.E_BIT = 0 ; //WR High, E Clocks low
Delay_us(1);
PORTB.E_BIT = 1; //WR High=Read, E High
Delay_us(1);
PORTB.E_BIT = 0 ; //WR High, E Clocks low
Delay_us(1);
} while (busy.F3);
// Now the LCD is ready, write byte in two 4 bit nibbles
write_nibble_to_lcd (c >> 4, RS) ;
write_nibble_to_lcd (c & 0x0f, RS) ;
// Exit with E Low, WR Low, RS set to selected value
}
void
string_to_lcd (const unsigned char *s)
{
while (*s)
write_byte_to_lcd (*s++, 1) ;
}
void
decbyte_to_lcd (unsigned char c)
{
char *s ;
unsigned char text[4] ;
for (s=text; *s ;)
write_byte_to_lcd (*s++, 1) ;
}
void
hexnibble_to_lcd (unsigned char c)
{
c &= 0x0f ;
if (c > 9)
c += 'A' - 10 ;
else
c += '0' ;
write_byte_to_lcd (c, 1) ;
}
void hexbyte_to_lcd (unsigned char c)
{
hexnibble_to_lcd (c >> 4) ;
hexnibble_to_lcd (c) ;
}
//==============================================================================
// BUFFER_POWER must be >= 2
#define BUFFER_LEN 64
#define BUFFER_MASK (BUFFER_LEN-1)
unsigned char rxbuf [BUFFER_LEN] ;
unsigned char rx_head, rx_tail, rx_len ;
void
interrupt ()
{
unsigned char stat;
{
SSPCON.F4 = 0 ; // Hold clock low - (ie stretch clock while we work)
stat = SSPSTAT & 0x2d ; // mask out bits we're not interestted in
// 5=(1=data,0=addr), 3=(1=start detected),
// 2=(1=read,0=write), 0=(1=sspbuf full)
{
last_i2c = SSPBUF ; // Do a dummy read of the sspbuf
}
{
rxbuf[rx_head++] = SSPBUF ;
rx_head &= BUFFER_MASK ;
if (rx_len++ >= BUFFER_LEN)
crashed = 1 ; //flag error
}
{
while (SSPSTAT.F0 == 1) ; // keep waiting while buffer full
do
{
SSPCON.F7 = 0 ; // Clear the write collision flag
SSPBUF = 0xaa ; // Just return AA for the time being
} while (SSPCON.F7 == 1) ; // Loop if there was a write collision
}
{
while (SSPSTAT.F0 == 1) ; // keep waiting while buffer full
do
{
SSPCON.F7 = 0 ; // Clear the write collision flag
SSPBUF = 0x55 ; // Just return 55 for the time being
} while (SSPCON.F7 == 1) ; // Loop if there was a write collision
}
else // NACK received 0x28 - or another condition - Do a reset
{
last_i2c = SSPBUF ; // Do a dummy read of the sspbuf
SSPCON.F5=0; // Temporarily disable
SSPCON = 0x36 ; // F7=0 WCOL Write Collision Detect bit
// F6=0 SSPOV Receive Overflow Indicator bit
// F5=1 Synchronous Serial Port Enable
SSPSTAT = 0 ; // Clear all status bits
}
SSPCON.F4 = 1 ; // Release the clock (finish stretching it)
}
else // unknown interrupt!!
{
crashed = 3;
}
}
//==============================================================================
#define CLEAR_AND_FIRST_ROW 0x01
#define FIRST_ROW 0x02
#define SECOND_ROW (0x80 | 0x40)
#define THIRD_ROW (0x80 | 0x14)
#define FOURTH_ROW (0x80 | 0x54)
unsigned char line, column ;
void
linefeed ()
{
unsigned char cmd, i ;
// Blank the appropriate line.
switch (line)
{
case 1: cmd = FIRST_ROW ; break ;
case 2: cmd = SECOND_ROW ; break ;
case 3: cmd = THIRD_ROW ; break ;
default: cmd = FOURTH_ROW ; break ;
}
write_byte_to_lcd (cmd, 0); // Go to start of line
for (i=0 ; i<COLUMNS ; i++) // Blank the entire line
write_byte_to_lcd (' ', 1);
write_byte_to_lcd (cmd, 0); // Return to start of line
column = 0 ;
}
void
printchar (const unsigned char c)
{
if (c == 0)
{
// Clear display entirely
write_byte_to_lcd (CLEAR_AND_FIRST_ROW, 0);
line = 1 ;
column = 0 ;
}
else if (c == '\n')
{
// linefeed and wrap around to line 0
line++ ;
if (line > LINES)
line = 1 ;
linefeed() ;
}
else if (c < 5)
{
// move to line 1..4
line = c ;
linefeed() ;
}
else if (c == '\r')
{
linefeed() ;
}
else
{
if (column >= COLUMNS)
{
// need to force a linefeed
line++ ;
if (line > LINES)
line = 1 ;
linefeed() ;
}
// Display the numbered character
write_byte_to_lcd (c, 1);
column++ ;
}
}
// Enqueue_string will print anything BUT NOT the \000 CLEAR command!!!!
void
enqueue_string (const unsigned char *s)
{
while (*s)
{
rxbuf[rx_head++] = *s++ ;
rx_head &= BUFFER_MASK ;
rx_len++ ;
if (rx_len > BUFFER_LEN)
{
crashed = 4 ; //flag error
return ;
}
}
}
void
enqueue_number (const unsigned char c)
{
unsigned char text[4] ;
unsigned char *s ;
for (s=text ; *s ; s++)
{
if (*s == ' ')
continue ;
rxbuf[rx_head++] = *s ;
rx_head &= BUFFER_MASK ;
rx_len++ ;
if (rx_len > BUFFER_LEN)
crashed = 5 ; //flag error
}
}
//==============================================================================
#define MAGIC 0x3d
unsigned char num_boots ;
unsigned char magic ; // Will hold a magic number so we know a cold boot
void
main()
{
char i ;
// Osc = bits 6,5,4
//000 = 31.25 kHz
//001 = 125 kHz
//010 = 250 kHz
//011 = 500 kHz
OSCCON=0x7c; // Internal osc: 0x2c=0.250MHz, 0x4c=1MHz, 0x5c=2MHz, 0x7c=8MHz
while (OSCCON.F2 == 0) ; // Wait for osc to stabilise
restart:
OPTION_REG=0x03; //0x03=Prescaler=1:256 0x80=low..pullup enabled
INTCON=0; //Disable interrupts
TRISA=0; //Set Port A to all outputs
PORTA=0x00;
PORTB=0x00;
WDTCON =0x17 ; // Enable watchdog with max timeout value = approx 2s
SSPCON = 0x36 ; // F7=0 WCOL Write Collision Detect bit
// F6=0 SSPOV Receive Overflow Indicator bit
// F5=1 Synchronous Serial Port Enable
SSPSTAT = 0 ; // Clear all status bits
if (magic != MAGIC)
{
crash_cause=0 ; // used for debugging interrupt routine with print statements
num_boots = 0 ;
magic = MAGIC ;
}
else
num_boots++ ;
crashed = 0 ; // We're not crashed at the moment!
rx_head = rx_tail = rx_len = 0 ;
INTCON.F6 = 1 ; // enable peripheral interrupts
INTCON.F7 = 1 ; // enable all unmasked interrupts
TXSTA = 0 ; // Turn off transmit from UART
RCSTA = 0 ;
CMCON = 0x07 ; // Disable comparators
Delay_ms (100); // Provide a little time for the LCD to initialise
asm {
CLRWDT // Clear the watchdog timer
}
write_nibble_to_lcd (0x03,0); // Function set 8 bit mode
Delay_ms (5); // Wait at least 4.1ms
write_nibble_to_lcd (0x03,0); // Function set 8 bit mode
Delay_ms (1); // Wait at least 100us
write_nibble_to_lcd (0x03,0); // Function set 8 bit mode
Delay_ms (1); // Wait at least 100us
write_nibble_to_lcd (0x02,0); // NOW FINALLY Function set 4 bit mode
Delay_ms (1); // Wait at least 100us
write_byte_to_lcd (0x28, 0); //And repeat, doing full 8 bits of function cmd
write_byte_to_lcd (0x0c, 0); //Display ON (04), Curson ON(02), Blinking(01)
write_byte_to_lcd (0x01, 0); //Clear display
write_byte_to_lcd (0x06, 0); //Entry mode Increment(02) Noshift(01)
write_byte_to_lcd (CLEAR_AND_FIRST_ROW, 0);
line = 1;
column = 0 ;
// Display the welcome message
enqueue_number (LINES) ;
enqueue_string ("x") ;
enqueue_number (COLUMNS) ;
enqueue_string (" lcd @addr ") ;
enqueue_string ("\nVersion 1. SKT");
// Give some indication if there's been a crash
if (num_boots)
{
enqueue_string ("\rBoot count: ");
enqueue_number (num_boots) ;
}
for (;;)
{
asm {
CLRWDT // clear the watchdog timer
}
if (rx_len)
{
printchar (rxbuf[rx_tail++]) ;
rx_tail &= BUFFER_MASK ;
rx_len-- ;
}
if (crashed)
{
crash_cause = crashed ; // Remember why we crashed
write_byte_to_lcd (CLEAR_AND_FIRST_ROW, 0);
string_to_lcd ("Cause:") ;
hexbyte_to_lcd (crash_cause);
string_to_lcd (" last:") ;
hexbyte_to_lcd (last_i2c) ;
string_to_lcd (" info:") ;
hexbyte_to_lcd (info);
string_to_lcd (" boots:") ;
hexbyte_to_lcd (num_boots);
write_byte_to_lcd (SECOND_ROW, 0);
string_to_lcd ("Buf len:") ;
decbyte_to_lcd (rx_len);
write_byte_to_lcd (' ', 1);
for (i=0 ; i < 14 ; i++)
hexbyte_to_lcd (rxbuf[(rx_tail+i)&BUFFER_MASK]);
// The following kludge is to get around the non-reentrant version of C
// If there's an error inside the Interrupt, this flag will be set
// That lets us display something - and this loop will cause an infinte wait
// while (crashed) ; // Infinite loop if an error has been set in the ISR
// This will trip the watchdog timer some time later
// Delay for 15 seconds
for (i=0 ; i < 150 ; i++)
{
Delay_ms (100);
asm {
CLRWDT // clear the watchdog timer
}
}
goto restart ;
}
}
}
June 14, 2008, at 03:13 AM
by --
Changed lines 13-14 from:
You need to specify the dimensions of your LCD display and the i2c address you'd like to allocate using #defines near the top of the code. to:
You need to specify the dimensions of your LCD display and the i2c address you'd like to allocate using #defines near the top of the June 14, 2008, at 03:12 AM
by --
Changed lines 15-16 from:
// 16F88 programme to drive a hitachi compatible LCD display from the
// i2C bus
to:
@@// 16F88 programme to drive a hitachi compatible LCD display from the
@@// i2C bus
June 14, 2008, at 03:11 AM
by --
Changed lines 13-15 from:
[ // 16F88 programme to drive a hitachi compatible LCD display from the // i2C bus to:
You need to specify the dimensions of your LCD display and the i2c address you'd like to allocate using #defines near the top of the code. // 16F88 programme to drive a hitachi compatible LCD display from the
// i2C bus
June 14, 2008, at 03:09 AM
by --
Changed line 13 from:
[@ to:
[ Changed line 36 from:
to:
] June 14, 2008, at 03:08 AM
by --
Changed line 13 from:
[@echo off to:
[@ June 14, 2008, at 03:08 AM
by --
Changed lines 13-14 from:
[@echo off// 16F88 programme to drive a hitachi compatible LCD display from the to:
[@echo off // 16F88 programme to drive a hitachi compatible LCD display from the June 14, 2008, at 03:07 AM
by --
Changed lines 11-503 from:
to:
I've written assembler code for the 16F88 before and frankly, its a pain to debug. This time I decided that I'd have a crack at writing the LCD interface in C. There's several freely downloadable C compilers for the PIC available and I chose to use one called MikroC?, available at [[http://www.mikroe.com/en/download]the mikroElektronika] web site. [@echo off// 16F88 programme to drive a hitachi compatible LCD display from the // i2C bus // // There are plenty of spare inputs and outputs for additional functions ////////////////////////////////////////////////////////////////////// // PORT A bit 0 = D4 on LCD // PORT A bit 1 = D5 on LCD // PORT A bit 2 = D6 on LCD // PORT A bit 3 = D7 on LCD // PORT A bit 4 = NC // PORT A bit 5 = pulled high with 10k // PORT A bit 6 = NC // PORT A bit 7 = NC // // PORT B bit 0 = E on LCD // PORT B bit 1 = SD on I2C? // PORT B bit 2 = RW on LCD // PORT B bit 3 = RS on LCD // PORT B bit 4 = CK on I2C? // PORT B bit 5 = NC // PORT B bit 6 = NC // PORT B bit 7 = NC // Fx is mikroC nomenclature for bit x
// Define the number of lines and columns on the LCD display
// counters for debugging purposes unsigned char crashed, crash_cause, info, last_i2c ; ////////////////////////////////////////////////////////////////////// void write_nibble_to_lcd (const unsigned char c, const unsigned char RS) { TRISA.F0 = 0; //Set Bits 3-0 as OUTPUTS
TRISA.F1 = 0;
TRISA.F2 = 0;
TRISA.F3 = 0;
PORTA = c ; //Bits 3-0 = high order data nibble
PORTB.E_BIT = 0 ;
if (RS)
PORTB.RS_BIT = 1 ;
else
PORTB.RS_BIT = 0 ;
PORTB.WR_BIT = 0 ;
Delay_us (1);
PORTB.E_BIT = 1 ; //E High, WR low, RS set to selected value
Delay_us (1);
PORTB.E_BIT = 0 ;
// Exit with E Low, WR Low, RS set to selected value
} void write_byte_to_lcd (const unsigned char c, const unsigned char RS) { unsigned char busy ;
TRISA.F0 = 1; //Set Bits 3-0 as INPUTS
TRISA.F1 = 1;
TRISA.F2 = 1;
TRISA.F3 = 1;
PORTB.E_BIT = 0 ;
PORTB.RS_BIT = 0 ;
PORTB.WR_BIT = 1 ;
Delay_us(1);
// Wait for LCD to become unbusy
// BUSY FLAG polled on falling edge of E
do // Need to set E high and low twice - two four bit reads
{
PORTB.E_BIT = 1; //WR High=Read, E High
Delay_us(1);
busy = PORTA ; //Read the busy flag in bit 3 (bit 7 of LCD)
PORTB.E_BIT = 0 ; //WR High, E Clocks low
Delay_us(1);
PORTB.E_BIT = 1; //WR High=Read, E High
Delay_us(1);
PORTB.E_BIT = 0 ; //WR High, E Clocks low
Delay_us(1);
} while (busy.F3);
// Now the LCD is ready, write byte in two 4 bit nibbles
write_nibble_to_lcd (c >> 4, RS) ;
write_nibble_to_lcd (c & 0x0f, RS) ;
// Exit with E Low, WR Low, RS set to selected value
} void string_to_lcd (const unsigned char *s) { while (*s)
write_byte_to_lcd (*s++, 1) ;
} void decbyte_to_lcd (unsigned char c) { char *s ;
unsigned char text[4] ;
ByteToStr? (c, text);
for (s=text; *s ;)
write_byte_to_lcd (*s++, 1) ;
} void hexnibble_to_lcd (unsigned char c) { c &= 0x0f ;
if (c > 9)
c += 'A' - 10 ;
else
c += '0' ;
write_byte_to_lcd (c, 1) ;
} void hexbyte_to_lcd (unsigned char c) { hexnibble_to_lcd (c >> 4) ; hexnibble_to_lcd (c) ; } //============================================================================== // BUFFER_POWER must be >= 2
unsigned char rxbuf [BUFFER_LEN] ; unsigned char rx_head, rx_tail, rx_len ; void interrupt () { unsigned char stat; if (PIR1?.F3 == 1) // is this an i2c interrupt { SSPCON.F4 = 0 ; // Hold clock low - (ie stretch clock while we work) stat = SSPSTAT & 0x2d ; // mask out bits we're not interestted in // 5=(1=data,0=addr), 3=(1=start detected), // 2=(1=read,0=write), 0=(1=sspbuf full) if (stat == 0x09) // I2C? Write: Byte received was an address { last_i2c = SSPBUF ; // Do a dummy read of the sspbuf } else if (stat == 0x29) // I2C? Write: Byte received was data { rxbuf[rx_head++] = SSPBUF ; rx_head &= BUFFER_MASK ; if (rx_len++ >= BUFFER_LEN) crashed = 1 ; //flag error } else if (stat == 0x0c) // I2C? Read: Byte received was an address { while (SSPSTAT.F0 == 1) ; // keep waiting while buffer full do { SSPCON.F7 = 0 ; // Clear the write collision flag SSPBUF = 0xaa ; // Just return AA for the time being } while (SSPCON.F7 == 1) ; // Loop if there was a write collision } else if (stat == 0x2c) // I2C? Read: Byte to be transmitted will be data { while (SSPSTAT.F0 == 1) ; // keep waiting while buffer full do { SSPCON.F7 = 0 ; // Clear the write collision flag SSPBUF = 0x55 ; // Just return 55 for the time being } while (SSPCON.F7 == 1) ; // Loop if there was a write collision } else // NACK received 0x28 - or another condition - Do a reset { last_i2c = SSPBUF ; // Do a dummy read of the sspbuf SSPCON.F5=0; // Temporarily disable SSPCON = 0x36 ; // F7=0 WCOL Write Collision Detect bit // F6=0 SSPOV Receive Overflow Indicator bit // F5=1 Synchronous Serial Port Enable // F4=1 When set at 0, I2C? clock pin held low to stretch clock // F3-0=0110: I2C? slave mode: 7 bits address SSPSTAT = 0 ; // Clear all status bits } SSPCON.F4 = 1 ; // Release the clock (finish stretching it) PIR1?.F3 = 0 ; // Clear the interrupt } else // unknown interrupt!! { crashed = 3; info = PIR1? ; } } //==============================================================================
unsigned char line, column ; void linefeed () { unsigned char cmd, i ;
// Blank the appropriate line.
switch (line)
{
case 1: cmd = FIRST_ROW ; break ;
case 2: cmd = SECOND_ROW ; break ;
case 3: cmd = THIRD_ROW ; break ;
default: cmd = FOURTH_ROW ; break ;
}
write_byte_to_lcd (cmd, 0); // Go to start of line
for (i=0 ; i<COLUMNS ; i++) // Blank the entire line
write_byte_to_lcd (' ', 1);
write_byte_to_lcd (cmd, 0); // Return to start of line
column = 0 ;
} void printchar (const unsigned char c) { if (c == 0)
{
// Clear display entirely
write_byte_to_lcd (CLEAR_AND_FIRST_ROW, 0);
line = 1 ;
column = 0 ;
}
else if (c == '\n')
{
// linefeed and wrap around to line 0
line++ ;
if (line > LINES)
line = 1 ;
linefeed() ;
}
else if (c < 5)
{
// move to line 1..4
line = c ;
linefeed() ;
}
else if (c == '\r')
{
linefeed() ;
}
else
{
if (column >= COLUMNS)
{
// need to force a linefeed
line++ ;
if (line > LINES)
line = 1 ;
linefeed() ;
}
// Display the numbered character
write_byte_to_lcd (c, 1);
column++ ;
}
} // Enqueue_string will print anything BUT NOT the \000 CLEAR command!!!! void enqueue_string (const unsigned char *s) { while (*s)
{
PIE1?.F3 = 0; // Disable int F3=I2C? int
rxbuf[rx_head++] = *s++ ;
rx_head &= BUFFER_MASK ;
rx_len++ ;
PIE1?.F3 = 1; // Enable int F3=I2C? int
if (rx_len > BUFFER_LEN)
{
crashed = 4 ; //flag error
return ;
}
}
} void enqueue_number (const unsigned char c) { unsigned char text[4] ;
unsigned char *s ;
ByteToStr? (c, text);
for (s=text ; *s ; s++)
{
if (*s == ' ')
continue ;
rxbuf[rx_head++] = *s ;
rx_head &= BUFFER_MASK ;
rx_len++ ;
if (rx_len > BUFFER_LEN)
crashed = 5 ; //flag error
}
} //============================================================================== unsigned char num_boots ; unsigned char magic ; // Will hold a magic number so we know a cold boot void main() { char i ; // Osc = bits 6,5,4 //000 = 31.25 kHz //001 = 125 kHz //010 = 250 kHz //011 = 500 kHz //100 = 1 MHz? //101 = 2 MHz? //110 = 4 MHz? //111 = 8 MHz? OSCCON=0x7c; // Internal osc: 0x2c=0.250MHz, 0x4c=1MHz, 0x5c=2MHz, 0x7c=8MHz while (OSCCON.F2 == 0) ; // Wait for osc to stabilise restart: OPTION_REG=0x03; //0x03=Prescaler=1:256 0x80=low..pullup enabled INTCON=0; //Disable interrupts PIR1?=0; //Clear all interrupt flags TRISA=0; //Set Port A to all outputs TRISB=0x12; //B4 and B1 are inputs (I2C?) PORTA=0x00; PORTB=0x00; WDTCON =0x17 ; // Enable watchdog with max timeout value = approx 2s ANSEL=0; //0 = PortA? bit is used for digital I/O ADCON1?=0x07 ; // Disable A/D SSPCON = 0x36 ; // F7=0 WCOL Write Collision Detect bit // F6=0 SSPOV Receive Overflow Indicator bit // F5=1 Synchronous Serial Port Enable // F4=1 When set at 0, I2C? clock pin held low to stretch clock // F3-0=0110: I2C? slave mode: 7 bits address SSPADD = I2C?_ADDRESS << 1 ; // 7 bit address in bits 7--1 SSPSTAT = 0 ; // Clear all status bits PIE1?.F3 = 1; // F6=ADC int, F3=I2C? int if (magic != MAGIC) { crash_cause=0 ; // used for debugging interrupt routine with print statements num_boots = 0 ; magic = MAGIC ; } else num_boots++ ; crashed = 0 ; // We're not crashed at the moment! rx_head = rx_tail = rx_len = 0 ; INTCON.F6 = 1 ; // enable peripheral interrupts INTCON.F7 = 1 ; // enable all unmasked interrupts TXSTA = 0 ; // Turn off transmit from UART RCSTA = 0 ; CMCON = 0x07 ; // Disable comparators Delay_ms (100); // Provide a little time for the LCD to initialise asm { CLRWDT // Clear the watchdog timer } write_nibble_to_lcd (0x03,0); // Function set 8 bit mode Delay_ms (5); // Wait at least 4.1ms write_nibble_to_lcd (0x03,0); // Function set 8 bit mode Delay_ms (1); // Wait at least 100us write_nibble_to_lcd (0x03,0); // Function set 8 bit mode Delay_ms (1); // Wait at least 100us write_nibble_to_lcd (0x02,0); // NOW FINALLY Function set 4 bit mode Delay_ms (1); // Wait at least 100us write_byte_to_lcd (0x28, 0); //And repeat, doing full 8 bits of function cmd write_byte_to_lcd (0x0c, 0); //Display ON (04), Curson ON(02), Blinking(01) write_byte_to_lcd (0x01, 0); //Clear display write_byte_to_lcd (0x06, 0); //Entry mode Increment(02) Noshift(01) write_byte_to_lcd (CLEAR_AND_FIRST_ROW, 0); line = 1; column = 0 ; // Display the welcome message enqueue_number (LINES) ; enqueue_string ("x") ; enqueue_number (COLUMNS) ; enqueue_string (" lcd @addr ") ; enqueue_number (I2C?_ADDRESS) ; enqueue_string ("\nVersion 1. SKT"); // Give some indication if there's been a crash if (num_boots) { enqueue_string ("\rBoot count: "); enqueue_number (num_boots) ; } for (;;) { asm { CLRWDT // clear the watchdog timer } if (rx_len) { printchar (rxbuf[rx_tail++]) ; rx_tail &= BUFFER_MASK ; PIE1?.F3 = 0; // Disable int F3=I2C? int rx_len-- ; PIE1?.F3 = 1; // Enable int F3=I2C? int } if (crashed) { crash_cause = crashed ; // Remember why we crashed write_byte_to_lcd (CLEAR_AND_FIRST_ROW, 0); string_to_lcd ("Cause:") ; hexbyte_to_lcd (crash_cause); string_to_lcd (" last:") ; hexbyte_to_lcd (last_i2c) ; string_to_lcd (" info:") ; hexbyte_to_lcd (info); string_to_lcd (" boots:") ; hexbyte_to_lcd (num_boots); write_byte_to_lcd (SECOND_ROW, 0); string_to_lcd ("Buf len:") ; decbyte_to_lcd (rx_len); write_byte_to_lcd (' ', 1); for (i=0 ; i < 14 ; i++) hexbyte_to_lcd (rxbuf[(rx_tail+i)&BUFFER_MASK]); // The following kludge is to get around the non-reentrant version of C // If there's an error inside the Interrupt, this flag will be set // That lets us display something - and this loop will cause an infinte wait // while (crashed) ; // Infinite loop if an error has been set in the ISR // This will trip the watchdog timer some time later
// Delay for 15 seconds
for (i=0 ; i < 150 ; i++)
{
Delay_ms (100);
asm {
CLRWDT // clear the watchdog timer
}
}
goto restart ;
}
}
}] June 14, 2008, at 03:01 AM
by --
Added lines 1-15:
Interfacing to the LCD displayI wanted to interface a generic HD44780? based LCD display to my NSLU2. There are many different displays of different dimensions available meeting this standard, and its relatively easy to programme. These LCDs? are relatively cheap, starting at a few dollars for a small device up to a few tens of dollars for the larger ones. When thinking about connecting any hardware peripheral to the NSLU2, the problem is there are no general purpose I/O pins available. I've seen projects using the USB interface to connect LCDs?, but only hints of projects using the I2C? bus. In this article, I'm going to detail how I implemented an I2C? LCD interface. Typical LCDs? don't come with I2C? interfaces built in. However cheap PIC microcontrollers can implement an I2C? bus for a couple of dollars. I chose to use a 16F88, an 18 pin DIP with two eight bit ports available. My design uses two of these 16 I/O pins for the I2C? interface, seven for the LCD interface and leaves seven for you to implement other cool stuff such as A/D inputs, keypad reading inputs or general purpose outputs. Firmware for a 16F88 PICExample code for the NSLU2Other tips
view ·
edit ·
print ·
history ·
Last edited by stefan keller-tuberg.
Based on work by ByronT and stefan keller-tuberg. Originally by stefan keller-tuberg. Page last modified on July 04, 2008, at 07:12 AM
|