PIC16F690でI2C LCDを使ってみよう

PIC16F690はハード的にスレーブ用に設計されているようです。従ってにマスターになれないようなので、 ソフト的にマスターにするようにしました。たまたま、用いているポートはI2C用のピンを用いていますが、ピン番号を変更すれば他のピンでも動くはずです。(試してはいない)
I2C低電圧キャラクタ液晶モジュール(16x2行)を用います。
LCDの購入は http://strawberry-linux.com/catalog/items?code=27001です。

 配線などはこちらをご覧ください。

I2C

I2Cのプロトコルに沿った通信を行います。但し、簡易的に用いるので複数のデバイスには対応していません。対応させるにはTri-Stateをコントーロルすれば良いでしょう。

i2c.h
#ifndef __I2C
#define __I2C

#define ACK 0x00         // ACK Signal
#define NACK 0x01        // NACK Signal

void I2C_START();        // START Bit-Patten Generate
void I2C_STOP();         // END Bit-Pattern Generate
void SEND_ACK(char ack); // Send ACK or NACK Signal
char WAIT_ACK();         // Wait ACK or NACK Siganl
void WriteByte(char b);  // I2C Write Byte
char ReadByte(char ack); // I2C Read Byte
void Delay(int i);       // Delay Func.
#endif


i2c.c
#include "i2c.h"
// 簡易I2Cなので複数のデバイスは繋げない
// ブルアップ抵抗は要らない

// CPUのスピードによりDeleyを調整する
void Delay(int i)
{
 // for(; i != 0 ; i--) ;
}

#define HIGH 1
#define LOW  0

#define SDA PORTC.F4
#define SCL PORTC.F3


/*
 startのビットパターンを生成する
~~|________ SDA
~~~~|_____ SCL
*/
void I2C_START()
{
  SDA = HIGH; Delay(1); //SDA HIGH
  SCL = HIGH; Delay(1); //SCL HIGH
  SDA = LOW ; Delay(1); //SDA LOW
  SCL = LOW ; Delay(1); //SCL LOW
}

/*
ENDのビットパターンを生成する
______|~~~ SDA
___|~~~~~~ SCL
*/
void I2C_STOP()
{
  SCL = LOW ;Delay(1); // SCL LOW
  SDA = LOW ;Delay(1); // SDA LOW
  SCL = HIGH;Delay(1); // SCL HIGH;
  SDA = HIGH;Delay(1); // SDA HIGH;
}

void SEND_ACK(char ack)
{                
 // Send ACK(LOW) or NACK(HIGH) and Generate SCL clock.
  SCL = LOW ; Delay(1);
  if(ack == ACK) // If ACK?
    SDA = LOW ;
  else // If NACK?
    SDA = HIGH;
  Delay(2);
  SCL = HIGH; Delay(1); // SCL Clock
  SCL = LOW ; Delay(1);
}

char WAIT_ACK()
{
  char ack; // Wait for ACK or NACK
  TRISC |= 0x10;
  SCL = LOW ; Delay(1);
  SCL = HIGH; Delay(1);
  ack = SDA;
  SCL = LOW ;
  TRISC &= 0xEF;
  return ack;
}

// 1バイト送出する
void WriteByte(char b)
{
  char i =0;
  for( ; i < 8 ; i++)
  {
    SCL = LOW;
    if( b & 0x80)
      SDA = HIGH;
    else                   // If ‘0′-> ‘Low’ to SDA
      SDA = LOW;
    Delay(1);
    SCL = HIGH; Delay(1);
    b <<=1;
  }
  SCL = LOW ;Delay(1);
  while(WAIT_ACK());
}

// 1バイト読み込む
char ReadByte(char ack)
{
  char ret;
  char i = 0;
  ret = 0;
  SCL = LOW ; Delay(1); // SCL LOW
  TRISC |= 0x10;
  for(; i<8 ; i++)
  {
    SCL = HIGH; Delay(1); // SCL HIGH
    ret <<= 1;
    if(SDA) ret++;
    SCL = LOW ; Delay(1); // SCL LOW
  }
  TRISC &= 0xEF;
  SEND_ACK(ack);
  return ret;
}


I2C LCD



I2CLCD.h
#ifndef __LCD
#define __LCD
typedef unsigned char byte;

void LCDinit();
void home();
void clear();
void writeCh(unsigned char  ch);
void writeString(char*  ch);
void write(unsigned char control, unsigned char dt);
void writeF(int x, int column, char zeroPad);
void writeInt(int x);
void setIcon(int x);
void setCG(char mode, char* cgData);
#endif


I2CLCD.c
#define Wait 40
#include "I2CLCD.h"
#include "i2c.h"
/*
PIC16F690はハード的にマスターになれないようなので、
ソフト的にマスターにする。
I2C低電圧キャラクタ液晶モジュール(16x2行)を用いる。
http://strawberry-linux.com/catalog/items?code=27001
*/
byte chCount;
byte LCD_WRITE;
void write(unsigned char control, unsigned char dt);

// 初期化 コントラストなどは要チェック
void LCDinit()
{

  chCount = 0;
  Delay_ms(40);
  LCD_WRITE= 0x7c;
  write(0x0, 0x38);   //data8bit, numberOfLine=2
  Delay_us(Wait);
  write(0x0, 0x39);   //data8bit, numberOfLine=2
  Delay_us(Wait);
  write(0x0, 0x14);   //internal OSC frequency
  Delay_us(Wait);
  write(0x0, 0x7f);   // contrast
  Delay_us(Wait);
  write(0x0, 0x5F);   // power/icon/contrast control
  Delay_us(Wait);
  write(0x0, 0x6a);   // follower control
  Delay_ms(200);      // 200ms
  write(0x0, 0x0c);   // Display ON
  Delay_us(Wait);
  write(0x0, 0x01);   // clear disply
  Delay_us(2000);     // 2ms
  write(0x00, 0x06);  // entry mode set
  Delay_us(Wait);
}

// カーソルをホームに持って行く
void home()
{
  write(0x00,0x02); // return to home
  chCount=0;
  Delay_ms(2);
}

// 画面クリア
void clear()
{
  write(0x00,0x01); // clear display
  chCount=0;
  Delay_ms(2);
}

// 一文字表示
void writeCh(unsigned char  ch)
{
  int x;
  if(ch==0x0a){
    x= chCount %32;
    if(x<16) {
      write(0x00,0xc0);
      chCount=16;
    }
    else {
      write(0x00,0x01); // clear display
      chCount=0;
      Delay_ms(2);
    }
  }
  else
  {
    write(0x40,ch);
    x= ++chCount %32;
    if( x ==16) write(0x00,0xc0);
  }
}

// 文字列表示
void writeString(char*  ch)
{
  while(*ch){
    writeCh(*ch++);
  }
}

// データ送出
void write(unsigned char control, unsigned char dt)
{
  I2C_START();
  WriteByte(LCD_WRITE);
  WriteByte(control);
  WriteByte(dt);
  I2C_STOP();
}

// 簡易書式付き整数表示
void writeF(int x, int column, char zeroPad)
{
  char Buffer[10];
  char num[5];
  int i,j, n=0, signFlag=0;
  if(x<0) {     // マイナスの値の場合
    signFlag=1;
    x *= -1;
  }
  do {
    num[n++] = x%10;
    x /= 10;
  } while(x != 0);
  i = column-n;
  if(i>0) {
    for(i=0; i<10; i++) Buffer[i]=' ';
    if(zeroPad) for(i=0; i<column-n; i++) Buffer[i]='0';
    Buffer[9]=0;
    for(i=column-n, j=n-1; i<column; i++, j--) Buffer[i]=num[j]|'0';
    Buffer[i]=0;
    if(signFlag) Buffer[column-n-1]='-';
  }
  else {
    j=0;
    if(signFlag) Buffer[j++]='-';
    for(i=--n; i>=0; i--) Buffer[j++]=num[i]|'0';
    Buffer[j]=0;
  }
  writeString(Buffer);
}

// 整数表示
void writeInt(int x)
{
  char num[5];
  int n=0;
  if(x<0) {     // マイナスの値の場合
    writeCh('-');
    x *= -1;
  }
  do {
    num[n++] = x%10 +'0';
    x /= 10;
  } while(x != 0);
  while(--n >=0) writeCh(num[n]);
}

// アイコン表示
void setIcon(int x)
{
  write(0x00, 0x39);    // Function set
  write(0x00, 0x40 | 0x0f);   // flower icon
  if(x & 1) write(0x40, 0x10);
  else  write(0x40, 0x00);

  write(0x00, 0x40 | 0x0d);   // Battery icon
  write(0x40, 0);
  if(x & 0x0e) {
    write(0x00, 0x40 | 0x0d);   // Battery icon
    write(0x40, ((x & 0x0e)<<1) | 2);
  }

  write(0x00, 0x40 | 0x0b);   // no sound icon
  if(x & 0x10) write(0x40, 0x10);
  else write(0x40, 0);

  write(0x00, 0x40 | 0x09);   // Key icon
  if(x & 0x20) write(0x40, 0x10);
  else write(0x40, 0);

  write(0x00, 0x40 | 0x07);   // 矢印 icon
  write(0x40, 0);
  if(x & 0xC0) {
    write(0x00, 0x40 | 0x07);   // 矢印 icon
    write(0x40, (x & 0xC0)>>3);
  }

  write(0x00, 0x40 | 0x06);   // enter icon
  if(x & 0x100) write(0x40, 0x10);
  else write(0x40, 0);
  write(0x00, 0x40 | 0x04);   // ring icon
  if(x & 0x200) write(0x40, 0x10);
  else write(0x40, 0);
  write(0x00, 0x40 | 0x02);   // 電話 icon
  if(x & 0x400) write(0x40, 0x10);
  else write(0x40, 0);
  write(0x00, 0x40);          // antena icon
  if(x & 0x800) write(0x40, 0x10);
  else write(0x40, 0);
  write(0x00, 0x38);          // Function set
}

// グラフィック表示
void setCG(char mode, char* cgData)
{
  unsigned char i, j;
  write(0x00,0x38);     // Function set IS= 0
  write(0x00,0x40);     // CGRAM set address= 0
  if(mode == 0) {       // StringModeで呼ばれた時はデフォールトにセット
    for(i=0; i<8; i++) {
      for(j=0; j<8; j++) {
        if(j < (7-i))        write(0x40,0x00);
        else write(0x40,0x1f);     // 横棒をセット
      }
    }
  }
  else {
    for(i=0; i<64; i++ ) write(0x40, cgData[i]);
  }
}

メインプログラム



main.c
// LCD: Reset(1), SCL(2), SDA(3), Vss(4)、Vdd(5)
//      ResetはVddに繋いでおく
// LCD    :  16F690
// SDA(3) : PORTC.F4(6)
// SCL(2) : PORTC.F3(7)
// TxD(10)

#include "i2c.h"
#include "I2CLCD.h"

char buff[16];

void main() 
{
  float F;
  int i=0;
  OSCCON = 0x75;             // 内部OSC8MHzで動作させる
  ANSEL  = 0;                // アナログ入力をOFF
  ANSELH = 0;                // アナログ入力をOFF
  PORTC = 0xff;              // PORTCを0xffにする
  TRISC = 0xe7;              // PORTCを入力に設定する
  LCDinit();
  while(1) {
    clear();
    writeString("LCD test  ");
    writeInt(i++);           // 整数型を表示
    writeCh(0x0a);
    F = i*3.3/1024;
    FloatToStr(F, buff);     // float型を表示
    writeString(buff);
    Delay_ms(100);
  }
}