arhi
Member
I had a need for I2C lcd controller and was using some old software i2c library I made long time ago for 16F628 (16F84) but as 16F690 is even cheaper then 16F628 and have hardware ssp (not mssp so it cannot be master - but I might be wrong there) I decided to rewrite the code and use the hw i2c capable uC for the job ... as it might be useful to someone else, and I kinda do not have time to make a "site" with all the useful things I could share - this seams like a valid place - not to mention that I might get some useful comments
here is the 16F690 code (the I2C slave LCD controller) - written in PICC CCS C
the master code, looks like this (mikroC c):
here is the 16F690 code (the I2C slave LCD controller) - written in PICC CCS C
Code:
#include <16F690.h>
#device adc=8
#FUSES WDT //Watch Dog Timer
#FUSES HS //HS Oscilator
#FUSES NOPROTECT //Code not protected from reading
#FUSES NOBROWNOUT //No brownout reset
#FUSES NOMCLR //Master Clear pin used for I/O
#FUSES NOCPD //No EE protection
#FUSES NOPUT //No Power Up Timer
#FUSES NOIESO //Internal External Switch Over mode disabled
#FUSES NOFCMEN //Fail-safe clock monitor disabled
#use delay(clock=20000000)
#use i2c(Slave,Fast,sda=PIN_B7,scl=PIN_B6,restart_wdt,force_hw,address=0xAA)
//how big the buffer is .. ~78 is the largest I could make on 16f690
//33 is enough for lcd operations, everything else is pure bonus :)
#define ALCTD 75
BYTE incoming, state;
BYTE address;
BYTE buffer[ALCTD];
BYTE changed[ALCTD];
#int_SSP
void SSP_isr(void) {
state = i2c_isr_state();
if(state == 1){ //First received byte is address
incoming = i2c_read();
address = incoming;
if (address > ALCTD) address = 0;
}
if(state == 2){ //Second received byte is data
incoming = i2c_read();
buffer[address] = incoming;
changed[address] = 1;
}
if(state == 0x80){ //Master is asking for data
i2c_write (buffer[address]);
}
}
// PIC LCD
// C0 enable
// C1 rs
// C2 rw
// C3 NC
// C4 D4
// C5 D5
// C6 D6
// C7 D7
struct lcd_pin_map { // This structure is overlayed
BOOLEAN enable; // on to an I/O port to gain
BOOLEAN rs; // access to the LCD pins.
BOOLEAN rw; // The bits are allocated from
BOOLEAN unused; // low order up. ENABLE will
int data : 4; // be pin B0.
} lcd;
#locate lcd = getenv("sfr:PORTC") // This puts the entire structure over the port
#define set_tris_lcd(x) set_tris_c(x)
#define lcd_type 2 // 0=5x7, 1=5x10, 2=2 lines
#define lcd_line_two 0x40 // LCD RAM address for the second line
BYTE const LCD_INIT_STRING[4] = {0x20 | (lcd_type << 2), 0xc, 1, 6};
struct lcd_pin_map const LCD_WRITE = {0,0,0,0,0}; // For write mode all pins are out
struct lcd_pin_map const LCD_READ = {0,0,0,0,15}; // For read mode data pins are in
BYTE lcd_read_byte() {
BYTE low,high;
set_tris_lcd(LCD_READ);
lcd.rw = 1;
delay_cycles(1);
lcd.enable = 1;
delay_cycles(1);
high = lcd.data;
lcd.enable = 0;
delay_cycles(1);
lcd.enable = 1;
delay_us(1);
low = lcd.data;
lcd.enable = 0;
set_tris_lcd(LCD_WRITE);
return( (high<<4) | low);
}
void lcd_send_nibble( BYTE n ) {
lcd.data = n;
delay_cycles(1);
lcd.enable = 1;
delay_us(2);
lcd.enable = 0;
}
void lcd_send_byte( BYTE address, BYTE n ) {
lcd.rs = 0;
while ( bit_test(lcd_read_byte(),7) ) ;
lcd.rs = address;
delay_cycles(1);
lcd.rw = 0;
delay_cycles(1);
lcd.enable = 0;
lcd_send_nibble(n >> 4);
lcd_send_nibble(n & 0xf);
}
void lcd_init() {
BYTE i;
set_tris_lcd(LCD_WRITE);
lcd.rs = 0;
lcd.rw = 0;
lcd.enable = 0;
delay_ms(15);
for(i=1;i<=3;++i) {
lcd_send_nibble(3);
delay_ms(5);
}
lcd_send_nibble(2);
for(i=0;i<=3;++i)
lcd_send_byte(0,LCD_INIT_STRING[i]);
}
void main(){
BYTE i;
setup_adc_ports(NO_ANALOGS|VSS_VDD);
setup_adc(ADC_OFF);
setup_timer_0(RTCC_INTERNAL|RTCC_DIV_1);
setup_wdt(WDT_2304MS|WDT_DIV_16);
setup_timer_1(T1_DISABLED);
lcd_init();
enable_interrupts(INT_SSP);
enable_interrupts(GLOBAL);
//init buffers
for(i=0;i<32;i++){
changed[i]=0;
buffer[i]=0;
}
while (TRUE){
#asm
clrwdt
#endasm
//update 1st line
for(i=0;i<16;i++){
if (changed[i]){
changed[i]=0;
lcd_send_byte(0,0x80|i); //go to 1.row, i'th place
lcd_send_byte(1,buffer[i]);
}
}
//update 2nd line
for(i=16;i<32;i++){
if (changed[i]){
changed[i]=0;
lcd_send_byte(0,0x80|(i+lcd_line_two-16)); //go to 2.row, i'th place
lcd_send_byte(1,buffer[i]);
}
}
if (changed[32]){ // send controll code
changed[32] = 0;
lcd_send_byte(0,1); //clear screen
}
for(i=33;i<ALCTD;i++){
//do whatever .. controll the free oi pins on the uC...
}
}
}
the master code, looks like this (mikroC c):
Code:
void i2c_putc(unsigned char c){
unsigned char static position = 0;
if (c == '\f'){ // \f will clear screen
I2C_Start();
I2C_Wr(0xAA);
I2C_Wr(32);
I2C_Wr('x');
I2C_Stop();
position=0;
} else if (c=='\n'){ //move position to beginning of the second line
position=16;
} else {
I2C_Start();
I2C_Wr(0xAA);
I2C_Wr(position++);
I2C_Wr(c);
I2C_Stop();
if (position > 31) position=0;
}
}
void i2c_print(char *c){
unsigned char i;
i=0;
while (c[i]){
i2c_putc(c[i]);
i++;
}
}
char x[] = "\fHello World!\nsecond line";
void main(){
Delay_ms(200); //wait for the i2c slaves to come online
I2C_Init(100000); //"fast" i2c initialisation
i2c_print(x);
}
Last edited: