Saturday, January 5, 2013

Arduino Uno and DS1307 real time clock.


I must admit the Arduino stuff pulled me in recently and distracted me from my main project that I want to finish a.s.a.p. - the MKHBC-8-R1 home-brew computer. But I am only a nerdy human. I could not help myself.

Here I'd like share with hobbyists community a simple project that can be used as a base to learn Arduino programming - the electronic clock with RTC (Real Time Clock) module and LCD/keypad shield.


Hardware:

1) MINI Arduino I2C RTC DS1307 AT24C32 module.

        eBay item# 180646747674 by seller: e_goto
        Serial: SKU 00100-049

2) Keypad Shield 1602 LCD For Arduino MEGA 2560 1280 UNO R3 A005

       eBay item# 261039184894 by seller: womarts

The LCD/keypad shield you just put on top of the Arduino Uno compatible board. The RTC module is connected as outlined below:

RTC module -> Arduino Uno

SCL -> A5
SDA -> A4
VCC -> A3
GND -> A2

Note: the power to RTC (i2c_ds1307_at24c32) module is provided via Arduino's A2, A3 pins. The program code ensures proper logic levels/polarity on these pins so the RTC module gets the power through them.

Keypad functions:

SELECT - Switch between Clock Set/Clock Run modes. Select the date/time
             parameter to setup in the Clock Set mode.

LEFT - Immediately exit Clock Set mode and set the clock.

UP/DOWN - Increment/decrement selected date/time parameter.

The code:



/*
 * Date and time functions using a DS1307 RTC connected via I2C
 * and Wire lib
 * It is a simple clock application.
 * Author: Marek Karcz 2013. All rights reserved.
 * License: Freeware.
 * Disclaimer: Use at your own risk.
 * Hardware:
 *    1) MINI Arduino I2C RTC DS1307 AT24C32 module.
 *       eBay item# 180646747674 by seller: e_goto
 *       Serial: SKU 00100-049
 *    2) Keypad Shield 1602 LCD For Arduino MEGA 2560 1280 UNO 
 *       R3 A005
 *       eBay item# 261039184894 by seller: womarts
 */

/*
  The LCD circuit:
 * LCD RS pin to digital pin 8
 * LCD Enable pin to digital pin 9
 * LCD D4 pin to digital pin 4
 * LCD D5 pin to digital pin 5
 * LCD D6 pin to digital pin 6
 * LCD D7 pin to digital pin 7
 * LCD BL pin to digital pin 10
 * KEY pin to analog pin 0
 */

#include <Wire.h>
#include "RTClib.h"
#include <LiquidCrystal.h>

#define LOOP_DELAY  2000

LiquidCrystal lcd(8, 13, 9, 4, 5, 6, 7);
RTC_DS1307 RTC;
boolean bBlink = true;
const char *appVer = "MKHBC RTC 1.3   ";
const char *modTxt = "Set clock ...   ";

// Global variables for time setup/displaying purposes.
uint16_t set_Year;
uint8_t set_Month;
uint8_t set_Day;
uint8_t set_Hour;
uint8_t set_Minute;

/*
 * Keypad shield uses resistors array and single analog input.
 * The values in adc_key_val array help to determine which 
 * key on the shield was pressed by checking the analog input 
 * read value.
 */
const int adc_key_val[5] ={50, 200, 400, 600, 800 };
int adc_key_in;

// keypad keys definitions
enum KP
{
  KEY_RIGHT = 0,
  KEY_UP,
  KEY_DOWN,
  KEY_LEFT,
  KEY_SELECT,
  KEY_NUMKEYS, // mark the end of key definitions
  KEY_NONE     // definition of none of the keys pressed
};

// Finite Machine States
enum FMS
{
  RUN = 0,
  SETCLOCK,
  SETYEAR,
  SETMONTH,
  SETDAY,
  SETHOUR,
  SETMINUTE
} ClockState;

// Finite Machine State Transitions Table.
// Defines the flow of the application modes from one to another.
enum FMS StateMachine[] =
{
   /* RUN        -> */ SETCLOCK,
   /* SETCLOCK   -> */ SETYEAR,
   /* SETYEAR    -> */ SETMONTH,
   /* SETMONTH   -> */ SETDAY,
   /* SETDAY     -> */ SETHOUR,
   /* SETHOUR    -> */ SETMINUTE,
   /* SETMINUTE  -> */ RUN
};

enum KP key = KEY_NONE;

// Array of the numbers of month days.
const unsigned int month_days [] = 
{31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
const char *daysOfWeek[] = 
{ "Su", "Mo", "Tu", "We", "Th", "Fr", "Sa" };

int nDelay = 0; // controlling loops/key press latency

void setup () 
{
    nDelay = 0;
    key = KEY_NONE;
  // initialize LCD keypad module
    lcd.clear(); 
    lcd.begin(16, 2);
    lcd.setCursor(0,0); 
    lcd.print(appVer);       
  // power to i2c_ds1307_at24c32 module provided via A2, A3 pins
    pinMode(A3, OUTPUT); 
    digitalWrite(A3, HIGH);
    pinMode(A2, OUTPUT);
    digitalWrite(A2, LOW);
  // start communication, I2C and RTC
    Wire.begin();
    RTC.begin();
    
    ClockState = RUN;
    
    readKey();
 // if key SELECT is held at RESET/Start up procedure
    if (key == KEY_SELECT)
    {
        setClock(RTC.isrunning());
    }
    else if (key == KEY_LEFT) // if key LEFT is held at RESET
    {
 // following line sets the RTC to the date & time this sketch
 // was compiled
        RTC.adjust(DateTime(__DATE__, __TIME__));
    }
    else if (! RTC.isrunning()) // if battery was changed
    {
        setClock(false);
    }
 }

void loop () 
{
    readKey();
    if (key == KEY_SELECT)
    {
      ClockState = StateMachine[ClockState]; // switch mode
      delay(330);
    }
    
    switch (ClockState)
    {
       case RUN: 
          if (nDelay <= 0)  
          {
            DateTime now = RTC.now();

            dispTime(now.year(), 
                     now.month(), 
                     now.day(), 
                     now.hour(), 
                     now.minute(), 
                     now.dayOfWeek());
            bBlink = ((bBlink) ? false : true);
          }
          break;
          
       case SETCLOCK:

          nDelay = 0;
          lcd.setCursor(0,0); 
          lcd.print(modTxt);             
          setClock(RTC.isrunning());
          lcd.setCursor(0,0); 
          lcd.print(appVer);
          break;
          
       default: break;
   
    }
    if (nDelay <= 0)
      nDelay = LOOP_DELAY;
    else
      nDelay--;
}

/*
 * Functions to aid displaying the date/time.
 */
void dispYear(uint16_t yr)
{
  if (ClockState == SETYEAR)
  {
    if (bBlink)
      lcd.print(yr, DEC);
    else
      lcd.print("    ");
  }
  else
    lcd.print(yr, DEC);  
}

void dispMonth(uint8_t mo)
{
  if (ClockState == SETMONTH)
  {
    if (bBlink)
      lcd.print(mo, DEC);
    else
    {
      lcd.print(' ');
      if (mo >= 10)
        lcd.print(' ');
    }
  }
  else
    lcd.print(mo, DEC);  
}

void dispDay(uint8_t dy)
{
  if (ClockState == SETDAY)
  {
    if (bBlink)
      lcd.print(dy, DEC);
    else
    {
      lcd.print(' ');
      if (dy >= 10)
        lcd.print(' ');
    }
  }
  else
    lcd.print(dy, DEC);  
}

void dispHour(uint8_t hr)
{
  if (hr < 10)
  {
    if (ClockState == SETHOUR)
    {
      if (bBlink)
        lcd.print('0');
      else
        lcd.print(' ');
    }
    else
      lcd.print('0');
  }
  if (ClockState == SETHOUR)
  {
    if (bBlink)
      lcd.print(hr, DEC);
    else
    {
      lcd.print(' ');
      if (hr >= 10)
        lcd.print(' ');
    }
  }
  else
    lcd.print(hr, DEC);  
}

void dispMinute(uint8_t mn)
{
  if (mn < 10)
  {
    if (ClockState == SETMINUTE)
    {
      if (bBlink)
        lcd.print('0');
      else
        lcd.print(' ');
    }
    else
      lcd.print('0');
  }
  if (ClockState == SETMINUTE)
  {
    if (bBlink)
      lcd.print(mn, DEC);
    else
    {
      lcd.print(' ');
      if (mn >= 10)
        lcd.print(' ');
    }
  }
  else
    lcd.print(mn, DEC);  
}

void dispTime(uint16_t  yr,
              uint8_t   mo,
              uint8_t   dy,
              uint8_t   hr,
              uint8_t   mn,
              uint8_t   dow)
{
  lcd.setCursor(0,1);
  dispYear(yr);
  lcd.print('/');
  dispMonth(mo);
  lcd.print('/');
  dispDay(dy);
  lcd.print(' ');
  lcd.print(' ');    
  lcd.setCursor(11,1);
  dispHour(hr);
  if (ClockState == RUN)
  {
    if (bBlink)
       lcd.print(':');
    else
       lcd.print(' ');
  }
  else
    lcd.print(':');
  dispMinute(mn);
  lcd.setCursor (14, 0);
  lcd.print(daysOfWeek[dow]);
}

/*
 * Functions to aid setting date/time.
 */
void setYear(boolean incdec)
{
  if (incdec)
  {
    if (set_Year < 2100)
      set_Year++;  
  }
  else
  {
    if (set_Year > 2000)
      set_Year--;    
  }
}

void setMonth(boolean incdec)
{
  if (incdec)
  {
    if (set_Month < 12)
      set_Month++;  
  }
  else
  {
    if (set_Month > 1)
      set_Month--;    
  }
}

void setDay(boolean incdec)
{
  if (incdec)
  {
    if ((set_Month != 2 && set_Day < month_days[set_Month])
        || 
        (set_Month == 2 && set_Day < 28)
        ||
        (set_Month == 2 && set_Day == 28 && isLeapYear(set_Year))
       )
      set_Day++;   
  }
  else
  {
    if (set_Day > 1)
      set_Day--;     
  }
}

void setHour(boolean incdec)
{
  if (incdec)
  {
    if (set_Hour < 23)
      set_Hour++;
    else
      set_Hour = 0;  
  }
  else
  {
    if (set_Hour > 1)
      set_Hour--;
    else
      set_Hour = 23;    
  }
}

void setMinute(boolean incdec)
{
  if (incdec)
  {
    if (set_Minute < 59)
      set_Minute++;
    else
      set_Minute = 0;  
  }
  else
  {
    if (set_Minute > 1)
      set_Minute--;
    else
      set_Minute = 59;    
  }
}

void setDateTime(boolean incdec)
{
  if (incdec)
  {
    switch (ClockState)
    {
      case SETYEAR:
      
        setYear(true);    // increment year
        break;
       
      case SETMONTH:
     
        setMonth(true);   // increment month
        break;
       
      case SETDAY:
  
        setDay(true);     // increment day
        break;
        
      case SETHOUR:
      
        setHour(true);    // increment hour
        break;
        
      case SETMINUTE:
      
        setMinute(true);  // increment minute
        break;
        
        default: break;
       
    }    
  }
  else
  {
    switch (ClockState)
    {
      case SETYEAR:
      
        setYear(false);    // decrement year
        break;
       
      case SETMONTH:
     
        setMonth(false);   // decrement month
        break;
       
      case SETDAY:
  
        setDay(false);     // decrement day
        break;
        
      case SETHOUR:
      
        setHour(false);    // decrement hour
        break;
        
      case SETMINUTE:
      
        setMinute(false);  // decrement minute
        
        default: break;
       
    }            
  }
}

void setClock(boolean readrtc)
{
    DateTime now = DateTime(2013,1,1,0,0,0);
    
    if (readrtc)
      now = RTC.now();

    delay(500);   
    set_Year = now.year();
    set_Month = now.month();
    set_Day = now.day();
    set_Hour = now.hour();
    set_Minute = now.minute();
    
    ClockState = SETYEAR;
    
    while (ClockState >= SETCLOCK)
    {
      if (nDelay <= 0)
      {
        dispTime(set_Year, 
                 set_Month, 
                 set_Day, 
                 set_Hour, 
                 set_Minute, 
                 now.dayOfWeek());
        bBlink = ((bBlink) ? false : true);
      }

      readKey();
      if (key == KEY_UP || key == KEY_DOWN)
        bBlink = true;
      if (key == KEY_SELECT)
      {
        ClockState = StateMachine[ClockState];
        delay(330);
      }
      else 
      {
        if (nDelay <= 0)
        {
          if (key == KEY_UP)
          {
            setDateTime(true);  // increment
          }
          else if (key == KEY_DOWN)
          {
            setDateTime(false);  // decrement
          }
          else if (key == KEY_LEFT)
          {
            ClockState = RUN;    // exit set clock mode
          }
          nDelay = LOOP_DELAY;
        }
      }
      if (nDelay <= 0)
        nDelay = LOOP_DELAY;
      else
        nDelay--;
      //delay(330);
    }  
    
    RTC.adjust(DateTime(set_Year, 
                        set_Month, 
                        set_Day, 
                        set_Hour, 
                        set_Minute, 
                        0));
}

// Determine the leap year.
boolean isLeapYear(uint16_t yr)
{
  if ((yr%400)==0)
    return true;
  else if ((yr%100)==0)
    return false;
  else if ((yr%4)==0)
    return true;
   
  return false;
}

// Get key code from analog input.
unsigned int get_key(unsigned int input)
{
   unsigned int k;
   for (k = KEY_RIGHT; k < KEY_NUMKEYS; k++)
   {
      if (input < adc_key_val[k])
      {
         return k;
      }
   }
   if (k >= KEY_NUMKEYS) k = KEY_NONE; // No valid key pressed
      
   return k;
}

// Read analog input 0 to obtain key code in global variable: key
void readKey(void)
{
    key = KEY_NONE;
    adc_key_in = analogRead(0); // read the value from the sensor    
    // convert into key press  
    key = (enum KP) get_key(adc_key_in); 
}


Some pictures of the working application/hardware:




Thanks for looking.

Marek Karcz

2013/1/5

Wednesday, December 12, 2012

I2C keyboard - update.


My little TI99-4A matrix keyboard testing project has evolved into a cool I2C keyboard that may find use in any micro controller hobby application. I built a prototype circuit and wrote software running on Atmel AT89S52 micro that scans a full matrix keyboard of a retro computer and sends ANSI ASCII codes via I2C bus. Why settle for a keypad with just a few keys, when you can have a full ASCII terminal connected to your micro controller project?
Any matrix keyboard can be used after modification of the keyboard scanning algorithm and key codes table. Philosophy remains the same.

Device works in master mode. I tested it with Arduino Uno board since it has I2C port and can work in slave mode.

Wiring diagram 
(8051 controller only, connecting to Arduino is trivial, I'll explain in a minute):




I2C bus consists of 2 lines, SDA (data) and SCL (clock).
SDA line goes to Arduino's input pin A4, SCL to Arduino's pin A5.
I power the keyboard controller circuit from Arduino board, so there is a total of 4 lines going from keyboard controller to Arduino: SDA, SCL, VCC and GND.


Here is the 8051 code (SDCC):


/*
 * Project: TI99-4A I2C keyboard.
 * Module:  ti994a
 * Author:  Marek Karcz
 * Purpose: TI99-4A keyboard with micro connected to I2C.
 *          I2C input device. Works in MASTER mode.
 * 
 * Hardware:
 *    Atmel 8051 compatible microcontroller AT89S52.
 *    1602 LCD display
 *    TI99-4A matrix keyboard (ports P2, P3).
 *    I2C line drivers (NPN transistors, base and pull-up resistors, NAND gates 74LS00).
 *    NAND gates - incoming SDA, SCK from I2C bus to data/clock in pins.
 *    NPN transistors - outgoing SDA, SCL signals from 8051 data/clock out pins to I2C bus.
 *
 * Keyboard to port pin connections:
 *
 * TI99-4a pin#      Pn.b
 * --------------------------
 *          11       P3.7
 *          10       P3.6
 *           3       P3.5
 *           7       P3.4
 *           2       P3.3
 *           1       P3.2
 *           4       P3.1
 *           5       P3.0
 *
 *           6       P2.6
 *           8       P2.5
 *           9       P2.4
 *          15       P2.3
 *          14       P2.2
 *          13       P2.1
 *          12       P2.0
 *
 * =================================
 *
 *  I2C bus interface:
 *
 *  P1.3 - sda (data) out inverted
 *  P1.4 - sda (data) in  inverted
 *  P1.5 - scl (clock) out inverted
 *  P1.6 - scl (clock) in  inverted
 *
 *  ================================
 *
 *  TO DO:
 *
 *  1) Auto-repeat.
 *  2) Cover all CTRL ANSI codes.
 *  3) Receive characters from I2C bus and display on LCD.
 *     I am not sure if this is practical. Perhaps separate I2C display on I2C bus, independent
 *     from this microcontroller would work better.
 *  
 */


#include <at89x52.h>
#include <stdlib.h>
#include <string.h>

// keil -> sdcc
#define sbit __sbit
#define code __code
#define using __using
#define interrupt __interrupt
#define _nop_() __asm NOP __endasm

typedef unsigned char BYTE;
typedef unsigned int WORD;
typedef sbit BOOL ;


#define rs     P1_0 
#define rw     P1_1
#define ep     P1_2
#define KBP1   P3
#define KBP2   P2

// NOTE: I2C pins are inverted
#define SDA_OUT P1_3
#define SDA_IN  P1_4
#define SCL_OUT P1_5
#define SCL_IN  P1_6
#define I2C_HIGH 0
#define I2C_LOW  1

BYTE code dis1[] = {"I2C keyboard 1.0"};
BYTE code dis2[] = {"(C) Marek Karcz "};
BYTE code dis3[] = {"I2C line busy..."};
BYTE dispbuf[17];
BYTE keycode=0;
BYTE keysent=0;
BYTE row=0;
BYTE column=0;
BYTE code kbmatrix[8][7] =
{
{11, 43, 42, 41, 40, 22,  0},
{47, 31, 30, 29, 28, 32,  0},
{33, 20, 19, 18, 17, 21,  0},
{ 0,  9,  8,  7,  6, 10,  0},
{48,  2,  3,  4,  5,  1, 45},
{44, 24, 25, 26, 27, 23,  0},
{46, 13, 14, 15, 16, 12,  0},
{ 0, 36, 37, 38, 39, 35,  0}
};

BOOL bShiftOn = 0;
BOOL bCtrl = 0;
BOOL bFunc = 0;
BOOL bLock = 0;

void delay(BYTE ms);

void init_ports(void)
{
KBP1 = 0x00;
KBP2 = 0xFF;
P1 = 0xFF;
SDA_IN = 1;
SCL_IN = 1;
SDA_OUT = 0;
SCL_OUT = 0;
}

void init_dispbuf(void)
{
BYTE i=0;

for(i=0; i<16; i++)
{
dispbuf[i]=32;
}
dispbuf[i]=0;
}

void init_vars(void)
{
init_dispbuf();
keycode=0xFF;
row=0;
column=0;
}

void delay(BYTE ms)
{      
   BYTE i;
   while(ms--)
   {
      for(i = 0; i< 250; i++)
      {
         _nop_();
         _nop_();
         _nop_();
         _nop_();
      }
   }
}

/*
 * LCD 16x2 driver functions.
 */

BOOL lcd_bz()
{   
   BOOL result;
   rs = 0;
   rw = 1;
   ep = 1;
   _nop_();
   _nop_();
   _nop_();
   _nop_();
   result = (BOOL)(P0 & 0x80);
   ep = 0;
   return result; 
}

void lcd_wcmd(BYTE cmd)

   while(lcd_bz());
   rs = 0;
   rw = 0;
   ep = 0;
   _nop_();
   _nop_(); 
   P0 = cmd;
   _nop_();
   _nop_();
   _nop_();
   _nop_();
   ep = 1;
   _nop_();
   _nop_();
   _nop_();
   _nop_();
   ep = 0;  
}

void lcd_pos(BYTE pos)
{
   lcd_wcmd(pos | 0x80);
}

void lcd_wdat(BYTE dat) 

    while(lcd_bz());
    rs = 1;
    rw = 0;
    ep = 0;
    P0 = dat;
    _nop_();
    _nop_();
    _nop_();
    _nop_();
    ep = 1;
    _nop_();
    _nop_();
    _nop_();
    _nop_();
    ep = 0; 
}

void lcd_init()
{
   lcd_wcmd(0x38); 
   delay(1);
   lcd_wcmd(0x0c); 
   delay(1);
   lcd_wcmd(0x06);
   delay(1);
   lcd_wcmd(0x01);
   delay(1);
}

void lcd_clear(BYTE pos)
{
   BYTE i=0;
   lcd_pos(pos);
   for(i=0; i<16; i++)
   {
      lcd_wdat(32);
   }
}

void lcd_text(BYTE pos, BYTE txt[])
{
   BYTE i=0;
   lcd_clear(pos);
   lcd_pos(pos);
   i=0;
   while((int)txt[i] != (int)'\0')
   {
      lcd_wdat(txt[i]); 
      i++;
   }
}

/*
 * TI99-4A keyboard driver functions.
 */

/* Scans keyboard.
 * Returns 0 if no key pressed.
 * Returns row# (1-8) if key pressed.
 * Sets global flags indicating special/control key pressed.
 * Column is calculated from the read key code.
 */
BYTE read_kb(void)
{
BYTE i=0;
BYTE j=0;
BYTE ret=0;
BYTE keyscan=0;
BYTE pattern=0x01;
BYTE col=0;

for (i=0,pattern=0x01; pattern!=0; pattern<<=1,i++)
{
  KBP2 = 0xFF;
  KBP1 = ~pattern;
  keyscan = KBP2;
  if(keyscan!=0xFF)
  {
 init_dispbuf();
 for(j=1,col=0; j!=0; j<<=1,col++)
 {
    if (~keyscan & j)
{
if (kbmatrix[i][col] == 44) bShiftOn = 1;
else if (kbmatrix[i][col] == 45) { bShiftOn = 1; bLock = 1; }
else if (kbmatrix[i][col] == 46) bCtrl = 1;
else if (kbmatrix[i][col] == 48) bFunc = 1;
else { row = i; ret = i+1; column = col; keycode = kbmatrix[row][column]; }
}
 }
  }
  delay(2);
}

// key released, reset flags
if (ret == 0)
       keysent = bShiftOn = bLock = bCtrl = bFunc = 0;

return ret;
}

/* 
 * I2C bus communication driver functions.
 *
 * NOTE: in/out data and clk signals inverted
 * 
 */

/*Send start condition*/
void I2C_Start(void)        /*Initial conditions*/
{
        SDA_OUT = I2C_HIGH;
        SCL_OUT = I2C_HIGH;
        _nop_ ();
        _nop_ ();
        _nop_ ();
        _nop_ ();
        SDA_OUT = I2C_LOW;
        _nop_ ();
        _nop_ ();
        _nop_ ();
        _nop_ ();
SCL_OUT = I2C_LOW;
}

void I2C_Stop(void) /*Stop condition*/
{
SCL_OUT = I2C_LOW;
        SDA_OUT = I2C_LOW;
        _nop_ ();
        _nop_ ();
        _nop_ ();
        _nop_ ();
        SCL_OUT = I2C_HIGH;
        _nop_ ();
        _nop_ ();
        _nop_ ();
        _nop_ ();
        SDA_OUT = I2C_HIGH;
        _nop_ ();
        _nop_ ();
        _nop_ ();
        _nop_ ();
}

BOOL I2C_Ack(void)  /* Receive acknowledge bit */
{
        SDA_IN = 1;
        _nop_ ();
        _nop_ ();
        _nop_ ();
        _nop_ ();
        SCL_OUT = I2C_HIGH;
        _nop_ ();
        _nop_ ();
        _nop_ ();
        _nop_ ();
        SCL_OUT = I2C_LOW;

return (SDA_IN == I2C_LOW);
}

void  I2C_NoAck(void)       /* Send acknowledge bit */
{
        SDA_OUT = I2C_LOW;
        _nop_ ();
        _nop_ ();
        _nop_ ();
        _nop_ ();
        SCL_OUT = I2C_HIGH;
        _nop_ ();
        _nop_ ();
        _nop_ ();
        _nop_ ();
        SCL_OUT = I2C_LOW;
}

void I2C_Send(BYTE d)   /*Subroutine to send data, d is required to send data*/
{
        BYTE BitCounter=8;       /*Median control*/
        BYTE temp;     /*Intermediate variable control*/
        do
        {
           temp=d;
           SCL_OUT = I2C_LOW;
           _nop_ ();
           _nop_ ();
           _nop_ ();
           _nop_ ();
           if((temp&0x80)==0x80)/* If the highest bit is 1*/
                SDA_OUT = I2C_HIGH;
           else
                SDA_OUT = I2C_LOW;
           _nop_ ();
           _nop_ ();
           _nop_ ();
           _nop_ ();
           SCL_OUT = I2C_HIGH;
           temp=d<<1;        /*RLC*/
           d=temp;
           BitCounter--;
         }while(BitCounter);
         SCL_OUT = I2C_LOW;
}

BYTE I2C_Read(void) /*Read one byte of data, and returns the byte value*/
{
        BYTE temp=0;
        BYTE temp1=0;
        BYTE BitCounter=8;
        SDA_IN = 1;
        do{
          SCL_OUT = I2C_LOW;
          _nop_ ();
          _nop_ ();
          _nop_ ();
          _nop_ ();
          SCL_OUT = I2C_HIGH;
          _nop_ ();
          _nop_ ();
          _nop_ ();
          _nop_ ();
          if(SDA_IN == I2C_HIGH)       /*If Sda=1;*/
                temp=temp|0x01;  /*The lowest temp 1*/
          else
                temp=temp&0xfe;  /*Otherwise, the lowest temp clear 0*/
          if(BitCounter-1)
          {   temp1=temp<<1;
              temp=temp1;
          }
          BitCounter--;
        }while(BitCounter);
        return(temp);
}

BOOL I2C_LineBusy(void)
{
return (SDA_IN != I2C_HIGH || SCL_IN != I2C_HIGH);
}

void lcdmsg(BYTE msg[], BYTE iter, BYTE dl)
{
BYTE n;

lcd_text(0, msg);
for(n=0; n<iter; n++)
{
       delay(dl);
}
    lcd_text(0, dis1);
}

void I2C_SendKey(BYTE kc)
{
if (!I2C_LineBusy())
{
  if (kc != keysent) // debounce
  {
     I2C_Start();
     I2C_Send(4<<1); // slave receiver address 
                     // shift left for 7 bit addr. + LSB as a R/W bit
     // LSB = 0 - WRITE
     I2C_Ack();
     I2C_Send(kc);
     I2C_Ack();
     I2C_Stop();
          keysent = kc;
  }
}
else
{
  lcdmsg(dis3, 8, 255); // Line busy message to LCD
  lcd_text(0, dis1);
}
}

/*
 * Convert keyboard scan code to ANSI ASCII code.
 * (more or less :-) )
 */
BYTE convKeyCode2Char(BYTE kc)
{
   BYTE ch = 0;

   switch (kc)
   {
      case  1: ch = ((bShiftOn) ? '!' : '1'); break; 
      case  2: ch = ((bShiftOn) ? '@' : '2'); break; 
      case  3: ch = ((bShiftOn) ? '#' : '3'); break; 
      case  4: ch = ((bShiftOn) ? '$' : '4'); break; 
      case  5: ch = ((bShiftOn) ? '%' : '5'); break; 
      case  6: ch = ((bShiftOn) ? '^' : '6'); break; 
      case  7: ch = ((bShiftOn) ? '&' : '7'); break; 
      case  8: ch = ((bShiftOn) ? '*' : '8'); break; 
      case  9: ch = ((bShiftOn) ? '(' : '9'); break; 
      case 10: ch = ((bShiftOn) ? ')' : '0'); break; 
      case 11: ch = ((bShiftOn) ? '+' : '='); break; 
      case 12: if (bCtrl) ch = 17; /* CTRL-Q */ else ch = ((bShiftOn) ? 'Q' : 'q'); break; 
      case 13: if (bFunc) ch = '~'; else ch = ((bShiftOn) ? 'W' : 'w'); break; 
      case 14: if (bFunc) ch = 128; /* up arrow */ else ch = ((bShiftOn) ? 'E' : 'e'); break; 
      case 15: if (bFunc) ch = '['; else ch = ((bShiftOn) ? 'R' : 'r'); break; 
      case 16: if (bFunc) ch = ']'; else ch = ((bShiftOn) ? 'T' : 't'); break; 
      case 17: ch = ((bShiftOn) ? 'Y' : 'y'); break; 
      case 18: if (bFunc) ch = '_'; else ch = ((bShiftOn) ? 'U' : 'u'); break; 
      case 19: if (bFunc) ch = '?'; else ch = ((bShiftOn) ? 'I' : 'i'); break; 
      case 20: if (bFunc) ch = '\''; else ch = ((bShiftOn) ? 'O' : 'o'); break; 
      case 21: if (bFunc) ch = '"'; else ch = ((bShiftOn) ? 'P' : 'p'); break; 
      case 22: ch = ((bShiftOn) ? '-' : '/'); break; 
      case 23: if (bFunc) ch = '|'; else ch = ((bShiftOn) ? 'A' : 'a'); break; 
      case 24: if (bCtrl) ch = 19; /* CTRL-S */
  else {if (bFunc) ch = 129; /* left arrow */ else ch = ((bShiftOn) ? 'S' : 's');}
  break; 
      case 25: if (bFunc) ch = 130; /* right arrow */ else ch = ((bShiftOn) ? 'D' : 'd'); break; 
      case 26: if (bFunc) ch = '{'; else ch = ((bShiftOn) ? 'F' : 'f'); break; 
      case 27: if (bFunc) ch = '}'; else ch = ((bShiftOn) ? 'G' : 'g'); break; 
      case 28: if (bCtrl) ch = 8; /* CTRL-H or BACKSPACE */ else ch = ((bShiftOn) ? 'H' : 'h'); break; 
      case 29: ch = ((bShiftOn) ? 'J' : 'j'); break; 
      case 30: ch = ((bShiftOn) ? 'K' : 'k'); break; 
      case 31: ch = ((bShiftOn) ? 'L' : 'l'); break; 
      case 32: ch = ((bShiftOn) ? ':' : ';'); break; 
      case 33: ch = '\n'; break;
      case 35: if (bCtrl) ch = 26; /* CTRL-Z */
  else {if (bFunc) ch = '\\'; else ch = ((bShiftOn) ? 'Z' : 'z');}
  break; 
      case 36: if (bFunc) ch = 131; /* down arrow */ else ch = ((bShiftOn) ? 'X' : 'x'); break; 
      case 37: if (bCtrl) ch = 3; /* CTRL-C */
  else { if (bFunc) ch = '`'; else ch = ((bShiftOn) ? 'C' : 'c');} 
  break; 
      case 38: ch = ((bShiftOn) ? 'V' : 'v'); break; 
      case 39: ch = ((bShiftOn) ? 'B' : 'b'); break; 
      case 40: ch = ((bShiftOn) ? 'N' : 'n'); break; 
      case 41: ch = ((bShiftOn) ? 'M' : 'm'); break; 
      case 42: ch = ((bShiftOn) ? '<' : ','); break; 
      case 43: ch = ((bShiftOn) ? '>' : '.'); break; 
      case 44: ch = 0; break;
 case 45: ch = 0; break;
 case 46: ch = 0; break;
 case 47: ch = ' '; break;
 case 48: ch = 0; break;

      default: ch = 0; break;
   }

   bShiftOn = 0; bLock = 0; bFunc = 0; bCtrl = 0;

   return ch;
}

/*
 * -------------- MAIN LOOP ---------------------
 */
main()
{
   BYTE kcodehex[5];
   BYTE kcodedec[10];
   BYTE rowbuf[3];
   BYTE row=0;
   BYTE ch=0;

   init_vars();
   init_ports();
   lcd_init();
   delay(10);
   // wait for I2C bus to become available
   while (I2C_LineBusy())
   {
 lcdmsg(dis3, 4, 255); // Line busy message to LCD
   }
   lcd_text(0,dis1);
   lcd_text(0x40,dis2);
   delay(255);
   delay(255);
   while(1)
   {
      if((row=read_kb())!=0)
 {
_uitoa(keycode,kcodehex,16);
_uitoa(keycode,kcodedec,10);
_uitoa(row,rowbuf,10);
strcpy(dispbuf,kcodehex);
strcat(dispbuf,":");
strcat(dispbuf,kcodedec);
strcat(dispbuf,":");
strcat(dispbuf,rowbuf);
    lcd_text(0x40,dispbuf);
ch = convKeyCode2Char(keycode);
if (ch != 0)
   I2C_SendKey(ch);
 }
 else
 {
    lcd_clear(0x40);
 }
   }
}

and here is the Arduino code:


// Wire Slave Receiver
// by Nicholas Zambetti <http://www.zambetti.com>

// Demonstrates use of the Wire library
// Receives data as an I2C/TWI slave device
// Refer to the "Wire Master Writer" example for use with this

// Created 29 March 2006

// This example code is in the public domain.


#include <Wire.h>


boolean bShiftPressed = false;

void setup()
{
  Wire.begin(4);                // join i2c bus with address #4
  Wire.onReceive(receiveEvent); // register event
  Serial.begin(9600);           // start serial for output
  Serial.println("I2C Slave Receiver");
}

void loop()
{
  delay(100);
}

char getCharFromKeyCode(int c)
{
   char ch = 0;
   
   switch (c)
   {
      case 128: break;
      case 129: break;
      case 130: break;
      case 131: break;
      
      default: ch = c; break;
   }
   
   return ch;
}

// function that executes whenever data is received from master
// this function is registered as an event, see setup()
void receiveEvent(int howMany)
{
  char ch;
  int c;

  while(1 < Wire.available()) // loop through all but the last
  {
    c = Wire.read(); // receive byte as an integer
    ch = getCharFromKeyCode(c);
    if (ch)
       Serial.print(ch);         // print the character
  }
  c = Wire.read();    // receive byte as an integer
  ch = getCharFromKeyCode(c);
  if (ch)
     Serial.print(ch);         // print the character
}


Some pictures of the prototype and a screenshot of the Arduino's serial console on the receiving end:






Thank you for looking.

Marek Karcz
12/13/2012

Reset And Panic Buttons, Buttons Debouncing

Reset And Panic Buttons, Buttons Debouncing The monitor program in my home brew computer has a nice debug feature. The NMI (Non-Maskab...