SSブログ
アプリケーション ブログトップ
- | 次の10件

Maruduinoで時計を作る。リアルタイムクロック(RTC MRX-8564)の利用。 [アプリケーション]

組み立て編はこちらです。
http://maruduino.blog.so-net.ne.jp/2010-08-07

Img_2082.jpg組み立てが完了したので、簡単な?スケッチを記述してみました。ご参考になれば幸いです。
Wireライブラリを使って通信してみたのですが、あまり安定しない、読み出しがおかしい?などがあったので、ソフトウエアでI2C通信を実現しています。
Arduinoは勿論マスターモードとしています。

操作方法を書いておきます。

時刻設定モード:
SW1とSW3の同時押しで時刻設定モードに入ります。
LCDの上段には変更する日付または時刻が表示され、下段には変更位置を示すポインターが表示されています。
SW2で数字を増やし、SW3で数字を減らしますが、上限、下限まで達すると循環します。
数字が確定したらSW4で次の項目に移動します。最後の項目である秒の変更が完了してSW4を押すと、その時点で時刻設定が確定します。
※SW1を押すと入力を取りやめ、通常の時刻表示モードに戻ります。
日付や時刻の整合性はあまりチェックしていませんので、注意してください。

アラーム設定モード:
SW1とSW2の同時押しでアラーム設定モードに入ります。
時刻設定モードと同様上段には時刻、下段にはポインターが表示されます。
その他操作も時刻設定モードと同じです。
時刻の整合性はあまりチェックしていませんので、注意してください。
※アラームを鳴らしたくない時は24:00に設定してください。

アラーム鳴動解除:
アラーム鳴動中にSW1を押す事で鳴動を解除できます。

バッテリーバックアップがされているので、電源を落としても時刻の更新やアラーム時刻の保持は行われています。


こうやってプロトタイピングして、もっとコンパクトであったり、電池で駆動できるものを作って日々の生活に役立てるとか良いですよね。
実際、Arduinoではありませんが、別のOne Chipマイコンで作った自作の目覚まし時計に日々起こされていますヨ。

以下にスケッチを記述しておきます。
※関数time_adjust_mode、Alarm_set_modeにアトミックな処理を追加 2010/08/07 18:35
/***********************************************************************/
/* LCD clock                                                           */
/* I2C bus control RTC()RTC8564 use                                    */
/*                                               designed by hamayan   */
/*                                               Copyright(C) hamayan  */
/***********************************************************************/
#define  _SOFT_I2C_
  
#if defined( _SOFT_I2C_ )
#else
#include <Wire.h>
#endif

#include <string.h>
#include <LiquidCrystal.h>
#include <avr/io.h>

/*************************************************************************/
/* defines                                                               */
/*************************************************************************/
#define  DI13     13
#define  DI12     12
#define  DI11     11
#define  DI10     10
#define  DI9      9
#define  DI8      8
#define  DI7      7
#define  DI6      6
#define  DI5      5
#define  DI4      4
#define  DI3      3
#define  DI2      2
#define  DI1      1
#define  DI0      0
#define  AN0      14
#define  AN1      15
#define  AN2      16
#define  AN3      17
#define  AN4      18
#define  AN5      19

#define  SW1      AN3
#define  SW2      AN2
#define  SW3      AN1
#define  SW4      AN0

#define  SCL_BIT         0b00100000   /*PC5=SCL*/
#define  SDA_BIT         0b00010000   /*PC4=SDA*/
#define  SCL_IS_HIGH     PORTC |= SCL_BIT  /*about 120ns swittching*/
#define  SCL_IS_LOW      PORTC &= ~SCL_BIT  /*about 120ns swittching*/
#define  SDA_IS_HIGH     PORTC |= SDA_BIT  /*about 120ns swittching*/
#define  SDA_IS_LOW      PORTC &= ~SDA_BIT  /*about 120ns swittching*/
#define  SDA_OUTPUT      DDRC |= SDA_BIT
#define  SDA_INPUT       DDRC &= ~SDA_BIT
#define  SCL_OUTPUT      DDRC |= SCL_BIT

#define  ACK             0  /*ACK Bit*/
#define  NACK            1  /*NACK Bit*/

#define  RTC8564_CTRL1      0x00
#define  RTC8564_CTRL2      0x01
#define  RTC8564_SEC        0x02
#define  RTC8564_MIN        0x03
#define  RTC8564_HOUR       0x04
#define  RTC8564_DAY        0x05
#define  RTC8564_WEEK       0x06
#define  RTC8564_MONTH      0x07
#define  RTC8564_YEAR       0x08
#define  RTC8564_MIN_ALM    0x09
#define  RTC8564_HOUR_ALM   0x0A
#define  RTC8564_DAY_ALM    0x0B
#define  RTC8564_WEEK_ALM   0x0C
#define  RTC8564_CLK_FREQ   0x0D
#define  RTC8564_TIM_CTRL   0x0E
#define  RTC8564_TIMER      0x0F

#if defined( _SOFT_I2C_ )
#define  RTC_BASE_ADDR      (0xa2)
#else
#define  RTC_BASE_ADDR      (0xa2 >> 1)
#endif

/*************************************************************************/
/*  global parameter                                                     */
/*************************************************************************/
enum RTC8564Str {SECONDS,MINUTES,HOURS,DAYS,WEEKDAYS,MONTHS,YEARS};

/* 0123456789012345678 */
/* 2007/11/21 15:12:00 */
char date[ 20 ];  /**/
char alarm_set_time[] = "00:00";

unsigned long sectim;
unsigned long lasttim;
unsigned char switches[3];
boolean  clock_disp = false;

const char date_limit[][2] = /*offset = 2*/
{
  {'0','9'},  /*year 10*/
  {'0','9'},  /*year 1*/
  {'/','/'},  /**/
  {'0','1'},  /*month 10*/
  {'0','9'},  /*month 1*/
  {'/','/'},  /**/
  {'0','3'},  /*day 10*/
  {'0','9'},  /*day 1*/
};
const char time_limit[][2] = /*offset = 0*/
{
  {'0','2'},  /*hour 10*/
  {'0','9'},  /*hour 1*/
  {':',':'},  /**/
  {'0','5'},  /*monite 10*/
  {'0','9'},  /*monite 1*/
  {':',':'},  /**/
  {'0','5'},  /*second 10*/
  {'0','9'},  /*second 1*/
};

LiquidCrystal lcd(DI8, DI9, DI4, DI5, DI6, DI7);

/*************************************************************************/
/*  prototype                                                            */
/*************************************************************************/
static void clk100( void );
static void I2C_Terminate( void );
static void StartCondition( void );
static void StopCondition( void );
static int RcvAck( void );
static void ResAck( int ack );
static void BitWrite( int data );
static int BitRead( void );
static int ByteWrite( int data );
static int ByteRead( int ack );

static void date_print( const char *src, int x, int y );
static void time_print( const char *src, int x, int y );
static void ConvertFromRTC2DateTime( void );

void I2C_Init( void );
int I2C_Block_Write( int addr, const char *data, int size );
int I2C_Block_Read( int addr, char *data, int size );
int I2C_Write( int addr, unsigned char data );
int I2C_Read( int addr, unsigned char *data );

int InitRTC8564( int force );
unsigned char *DateStr2RTC8564Str( unsigned char *dest, const char *source );
char *RTC8564Str2DateStr( char *dest, const unsigned char source[] );
int I2CRTC8564_Write( unsigned char addr, char data );
int I2CRTC8564_Read( unsigned char addr, char *buff );
int I2CRTC8564_Blk_Read( unsigned char addr, char *buff, int size );
int I2CRTC8564_Blk_Write( unsigned char addr, const char *data, int size );
int RTC8564_CalenderSet( const char *date_time );

#define  CalenderRead(buf)  I2CRTC8564_Blk_Read(RTC8564_SEC, buf, 7)

/*************************************************************************/
/*  digital2 external int handler                                        */
/*************************************************************************/
static void di2_int_hdr( void )
{
  sectim++;
  ConvertFromRTC2DateTime();  /*update clock*/
  if( clock_disp == true )
  {
    date_print(date,0,0);
    time_print(date,2,1);
  }
}

/*************************************************************************/
/*  Switch down detection                                                */
/*************************************************************************/
static unsigned char detect_sw_down( unsigned char *sw )
{
  unsigned char temp = PINC & 0x0f;

  sw[0] = sw[1];
  sw[1] = sw[2];
  sw[2] = temp ^ 0x0f;

  return sw[0] & sw[1] & temp;
}

/*************************************************************************/
/*  RTC8564 data convert to date time strings.                           */
/*************************************************************************/
static void ConvertFromRTC2DateTime( void )
{
  char buf[10];

  CalenderRead( buf );
  RTC8564Str2DateStr( date, (const unsigned char *)buf );  /**/
}

/*************************************************************************/
/*  Date lcd print                                                       */
/*************************************************************************/
static void date_print( const char *src, int x, int y )
{
  char buf[16];

  memcpy( buf, src, 10 );
  buf[10] = '\0';
  lcd.setCursor(x,y);
  lcd.print( buf );
}

/*************************************************************************/
/*  Time lcd print                                                       */
/*************************************************************************/
static void time_print( const char *src, int x, int y )
{
  char buf[16];

  memcpy( buf, &src[11], 8 );
  buf[8] = '\0';
  lcd.setCursor(x,y);
  lcd.print( buf );
}

/*************************************************************************/
/*  Time adjust                                                          */
/*************************************************************************/
static void time_adjust_mode( void )
{
  int pos = 2;
  char copy_date[20];
  boolean loop = true;

  memcpy( copy_date, date, sizeof(copy_date) );
  lcd.clear();
  date_print(copy_date,0,0);
  lcd.setCursor(pos,1);
  lcd.print( '^' );

  while( loop )
  {
    delay( 100 );
    switch ( detect_sw_down( switches ) )
    {
      case 0b00000001:  /*confirm*/
        lcd.setCursor(pos,1);
        lcd.print( ' ' );
        if( ++pos == 10 )
        {
          loop = false;
          break;
        }
        if( pos == 4 || pos == 7 ) pos++;
        lcd.setCursor(pos,1);
        lcd.print( '^' );
        break;
      case 0b00000010:  /*down number*/
        if( --copy_date[pos] < date_limit[pos - 2][0] ) copy_date[pos] = date_limit[pos - 2][1];
        lcd.setCursor(pos,0);
        lcd.print( copy_date[pos] );
        break;
      case 0b00000100:  /*up number*/
        if( ++copy_date[pos] > date_limit[pos - 2][1] ) copy_date[pos] = date_limit[pos - 2][0];
        lcd.setCursor(pos,0);
        lcd.print( copy_date[pos] );
        break;
      case 0b00001000:  /*abort*/
        return;
      default:
        break;
    }
  }

  pos = 0;
  loop = true;
  lcd.clear();
  time_print(copy_date,0,0);
  lcd.setCursor(pos,1);
  lcd.print( '^' );

  while( loop )
  {
    delay( 100 );
    switch ( detect_sw_down( switches ) )
    {
      case 0b00000001:  /*confirm*/
        lcd.setCursor(pos,1);
        lcd.print( ' ' );
        if( ++pos == 8 )
        {
          loop = false;
          break;
        }
        if( pos == 2 || pos == 5 ) pos++;
        lcd.setCursor(pos,1);
        lcd.print( '^' );
        break;
      case 0b00000010:  /*down number*/
        if( --copy_date[pos + 11] < time_limit[pos][0] ) copy_date[pos + 11] = time_limit[pos][1];
        lcd.setCursor(pos,0);
        lcd.print( copy_date[pos + 11] );
        break;
      case 0b00000100:  /*up number*/
        if( ++copy_date[pos + 11] > time_limit[pos][1] ) copy_date[pos + 11] = time_limit[pos][0];
        lcd.setCursor(pos,0);
        lcd.print( copy_date[pos + 11] );
        break;
      case 0b00001000:  /*abort*/
        return;
      default:
        break;
    }
  }

  noInterrupts();  /*global interrupt disable*/
  EIMSK &= ~0x01;  /*int0 interrupt disable*/
  interrupts();    /*global interrupt enable*/
  memcpy( date, copy_date, sizeof(date) );
  RTC8564_CalenderSet( date );  /*update calender.*/
  EIMSK |= 0x01;   /*int0 interrupt enable*/
}

/*************************************************************************/
/*  Alarm set                                                            */
/*************************************************************************/
static void Alarm_set_mode( void )
{
  int pos = 0;
  unsigned char temp;
  boolean loop = true;

  lcd.clear();
  lcd.setCursor(0,0);
  lcd.print( alarm_set_time );
  lcd.setCursor(pos,1);
  lcd.print( '^' );

  while( loop )
  {
    delay( 100 );
    switch ( detect_sw_down( switches ) )
    {
      case 0b00000001:  /*confirm*/
        lcd.setCursor(pos,1);
        lcd.print( ' ' );
        if( ++pos == 5 )
        {
          loop = false;
          break;
        }
        if( pos == 2 ) pos++;
        lcd.setCursor(pos,1);
        lcd.print( '^' );
        break;
      case 0b00000010:  /*down number*/
        if( --alarm_set_time[pos] < time_limit[pos][0] ) alarm_set_time[pos] = time_limit[pos][1];
        lcd.setCursor(pos,0);
        lcd.print( alarm_set_time[pos] );
        break;
      case 0b00000100:  /*up number*/
        if( ++alarm_set_time[pos] > time_limit[pos][1] ) alarm_set_time[pos] = time_limit[pos][0];
        lcd.setCursor(pos,0);
        lcd.print( alarm_set_time[pos] );
        break;
      case 0b00001000:  /*abort*/
        return;
      default:
        break;
    }
  }

  temp = ((alarm_set_time[3] & 0x0f) << 4) + (alarm_set_time[4] & 0x0f);
  noInterrupts();  /*global interrupt disable*/
  EIMSK &= ~0x01;  /*int0 interrupt disable*/
  interrupts();    /*global interrupt enable*/
  I2CRTC8564_Write( RTC8564_MIN_ALM, temp );
  EIMSK |= 0x01;   /*int0 interrupt enable*/

  temp = ((alarm_set_time[0] & 0x0f) << 4) + (alarm_set_time[1] & 0x0f);
  noInterrupts();  /*global interrupt disable*/
  EIMSK &= ~0x01;  /*int0 interrupt disable*/
  interrupts();    /*global interrupt enable*/
  I2CRTC8564_Write( RTC8564_HOUR_ALM, temp );
  EIMSK |= 0x01;   /*int0 interrupt enable*/
}

/*************************************************************************/
/*  setup                                                                */
/*************************************************************************/
void setup()
{
  char buf[16];
  unsigned char temp;
//  static const char date_time[] = "2010/08/07 02:04:00";

  lcd.begin(16, 2);
  lcd.clear();
  lcd.println( "designed by hamayan." );

#if defined( _SOFT_I2C_ )
  I2C_Init();
#else  /*_SOFT_I2C_*/
  Wire.begin();  /*set i2c master device*/
  TWSR &= 0xfc;  /*baud rate control. prescaler=x1*/
  TWBR = 72;     /*baud rate control. 100khz*/
#endif  /*_SOFT_I2C_*/

  delay( 1000 );
  if( InitRTC8564( 0 ) < 0 )
  {
    lcd.clear();
    lcd.println( "rtc init error." );
    while(1) ;
  }

  I2CRTC8564_Read( RTC8564_MIN_ALM, (char *)&temp );
  if( !(temp & 0x80) )
  {
    alarm_set_time[3] = ((temp >> 4) & 0x07) + '0';
    alarm_set_time[4] = (temp & 0x0f) + '0';
    I2CRTC8564_Read( RTC8564_HOUR_ALM, (char *)&temp );
    alarm_set_time[0] = ((temp >> 4) & 0x03) + '0';
    alarm_set_time[1] = (temp & 0x0f) + '0';
  }

//  RTC8564_CalenderSet( date_time );  /*force setting calender.*/

  pinMode( SW1, INPUT );  /*set port at input mode*/
  pinMode( SW2, INPUT );  /*set port at input mode*/
  pinMode( SW3, INPUT );  /*set port at input mode*/
  pinMode( SW4, INPUT );  /*set port at input mode*/

  /*enable edge trigger interrupt*/
  attachInterrupt(0, di2_int_hdr, FALLING);
  DDRD &= ~0b00000100;  /*direction input*/
  PORTD |= 0b00000100;  /*enable pullup*/

  /*display alarm set time*/
  lcd.clear();
  lcd.setCursor(11,1);
  lcd.print( alarm_set_time );
  clock_disp = true;
  lasttim = millis() + 100;
}

/*************************************************************************/
/*  main loop                                                            */
/*************************************************************************/
void loop()
{
#define  ALARM_PERIOD  (60 * 5)
  unsigned char temp;
  static unsigned long oldsec;
  static int alarm_time;

  if( millis() > lasttim )
  {
    lasttim = millis() + 100;  /*next sample time update.*/
    switch ( detect_sw_down( switches ) )
    {
      case 0b00001010 :  /*time adjust mode*/
        clock_disp = false;
        time_adjust_mode();
        lcd.clear();
        lcd.setCursor(11,1);
        lcd.print( alarm_set_time );
        clock_disp = true;
        break;
      case 0b00001100 :  /*alarm set mode*/
        clock_disp = false;
        Alarm_set_mode();
        lcd.clear();
        lcd.setCursor(11,1);
        lcd.print( alarm_set_time );
        clock_disp = true;
        break;
      case 0b00001000 :  /*stop alarm*/
        alarm_time = 0;
        break;
      default :
        break;
    }
  }

  if( oldsec != sectim )
  {
    oldsec = sectim;

    if( date[18] == '0' &&
        date[17] == '0' &&
        alarm_set_time[4] == date[15] &&
        alarm_set_time[3] == date[14] &&
        alarm_set_time[1] == date[12] &&
        alarm_set_time[0] == date[11] )
      alarm_time = ALARM_PERIOD;  /*? minutes*/

    if( --alarm_time > 0 )
    {
      if( alarm_time % 3 ) tone( DI13, 1000, 800 );
    }
    else alarm_time = 0;
  }
}

/*************************************************************************/
/*  wait functions                                                       */
/*************************************************************************/
#define  wait1u()    delayMicroseconds(1)
#define  wait5u()    delayMicroseconds(5)

/*************************************************************************/
/*  100 clocks function                                                  */
/*************************************************************************/
static void clk100( void )
{
  int i;

  /*i2c lock release*/
  for( i = 0; i < 100; i++ )
  {
    SCL_IS_LOW;
    wait1u();
    SCL_IS_HIGH;
    wait1u();
  }
}

/*************************************************************************/
/*  initialize function                                                  */
/*************************************************************************/
void I2C_Init( void )
{
  int i;

  PORTC |= SDA_BIT | SCL_BIT;
  DDRC |= SDA_BIT | SCL_BIT;  /*pc4=sda,pc5=scl*/

  for( i = 0; i < 100; i++ )
  {
    SCL_IS_LOW;
    wait1u();
    SCL_IS_HIGH;
    wait1u();
  }
  SDA_IS_LOW;

  StopCondition();
  wait5u();
}

/*************************************************************************/
/*  I2C Terminate function                                               */
/*************************************************************************/
static void I2C_Terminate( void )
{
  SCL_OUTPUT;
  SDA_OUTPUT;

  SCL_IS_HIGH;  /*about 120ns*/
  SCL_IS_HIGH;  /*about 120ns*/
  SCL_IS_HIGH;  /*about 120ns*/
  SCL_IS_HIGH;  /*about 120ns*/
  SCL_IS_HIGH;  /*about 120ns*/

  SDA_IS_HIGH;
}

/*************************************************************************/
/*  start condition                                                      */
/*************************************************************************/
static void StartCondition( void )
{
  I2C_Terminate();
  wait1u();

  SDA_IS_LOW;  /*about 120ns*/
  SDA_IS_LOW;  /*about 120ns*/
  SDA_IS_LOW;  /*about 120ns*/
  SDA_IS_LOW;  /*about 120ns*/
  SDA_IS_LOW;  /*about 120ns*/

  SCL_IS_LOW;
}

/*************************************************************************/
/*  stop condition                                                       */
/*************************************************************************/
static void StopCondition( void )
{
  SCL_OUTPUT;
  SDA_OUTPUT;

  SCL_IS_HIGH;  /*about 120ns*/
  SCL_IS_HIGH;  /*about 120ns*/
  SCL_IS_HIGH;  /*about 120ns*/
  SCL_IS_HIGH;  /*about 120ns*/
  SCL_IS_HIGH;  /*about 120ns*/

  SDA_IS_HIGH;
}

/*************************************************************************/
/*  ACK confirm function                                                 */
/*************************************************************************/
static int RcvAck( void )
{
  int ret;

  SCL_IS_LOW;  /*about 120ns*/
  SDA_INPUT;
  wait1u();
  SCL_IS_HIGH;
  ret = (PINC & SDA_BIT) ? 1 : 0;
  SCL_IS_HIGH;  /*about 120ns*/
  SCL_IS_HIGH;  /*about 120ns*/
  SDA_OUTPUT;
  SCL_IS_LOW;

  return ret;
}

/*************************************************************************/
/*  ACK responce function                                                */
/*************************************************************************/
static void ResAck( int ack )
{
  SCL_IS_LOW;  /*about 120ns*/
  if(ack) SDA_IS_HIGH;
  else SDA_IS_LOW; 
  wait1u();
  SCL_IS_HIGH;  /*about 120ns*/
  SCL_IS_HIGH;  /*about 120ns*/
  SCL_IS_HIGH;  /*about 120ns*/
  SCL_IS_HIGH;  /*about 120ns*/
  SCL_IS_HIGH;  /*about 120ns*/
  SCL_IS_LOW;
  SDA_IS_LOW;
}

/*************************************************************************/
/*  Bit Write function                                                   */
/*************************************************************************/
static void BitWrite( int data )
{
  SCL_IS_LOW;  /*about 120ns*/
  if(data) SDA_IS_HIGH;
  else SDA_IS_LOW; 
  wait1u();
  SCL_IS_HIGH;  /*about 120ns*/
  SCL_IS_HIGH;  /*about 120ns*/
  SCL_IS_HIGH;  /*about 120ns*/
  SCL_IS_HIGH;  /*about 120ns*/
  SCL_IS_HIGH;  /*about 120ns*/
  SCL_IS_LOW;
  SDA_IS_LOW;
}

/*************************************************************************/
/*  Bit Read function                                                    */
/*************************************************************************/
static int BitRead( void )
{
  int ret;

  SCL_IS_LOW;  /*about 120ns*/
  wait1u();
  SCL_IS_HIGH;
  ret = (PINC & SDA_BIT) ? 1 : 0;
  SCL_IS_HIGH;  /*about 120ns*/
  SCL_IS_HIGH;  /*about 120ns*/
  SCL_IS_LOW;

  return ret;
}

/*************************************************************************/
/*  Byte Write function                                                  */
/*************************************************************************/
static int ByteWrite( int data )
{
  BitWrite( data & 0x80 );
  BitWrite( data & 0x40 );
  BitWrite( data & 0x20 );
  BitWrite( data & 0x10 );
  BitWrite( data & 0x08 );
  BitWrite( data & 0x04 );
  BitWrite( data & 0x02 );
  BitWrite( data & 0x01 );
//  SDA_IS_LOW;

  return ( RcvAck() == 0 ) ? 0 : (-1);
}

/*************************************************************************/
/*  Byte Read function                                                   */
/*************************************************************************/
static int ByteRead( int ack )
{
  int data = 0;

  SDA_INPUT;
  data |= ( BitRead() ) ? 0x80 : 0x00;
  data |= ( BitRead() ) ? 0x40 : 0x00;
  data |= ( BitRead() ) ? 0x20 : 0x00;
  data |= ( BitRead() ) ? 0x10 : 0x00;
  data |= ( BitRead() ) ? 0x08 : 0x00;
  data |= ( BitRead() ) ? 0x04 : 0x00;
  data |= ( BitRead() ) ? 0x02 : 0x00;
  data |= ( BitRead() ) ? 0x01 : 0x00;
  SDA_OUTPUT;
  ResAck( ack );

  return data;
}

/*************************************************************************/
/*  I2C Block Write function                                             */
/*************************************************************************/
int I2C_Block_Write( int addr, const char *data, int size )
{
  StartCondition();

  if( ByteWrite( addr ) == (-1) )
  {
    I2C_Terminate();
    return (-1);
  }

  for( ; size > 0; size-- )
  {
    if( ByteWrite( *data++ ) == (-1) )
    {
      I2C_Terminate();

      return (-1);
    }
  }

  StopCondition();

  return 0;
}

/*************************************************************************/
/*  I2C Block Read function                                              */
/*************************************************************************/
int I2C_Block_Read( int addr, char *data, int size )
{
  StartCondition();
  if( ByteWrite( addr | 0x01 ) == (-1) )
  {
    I2C_Terminate();
    return (-1);
  }

  for( ; size > 0; size-- ) *data++ = ByteRead( (size == 1) ? 1 : 0 );

  StopCondition();

  return 0;
}

/*************************************************************************/
/*  I2C Write function                                                   */
/*************************************************************************/
int I2C_Write( int addr, unsigned char data )
{
#if defined( _SOFT_I2C_ )
  StartCondition();

  if( ByteWrite( addr ) == (-1) )
  {
    I2C_Terminate();
    clk100();
    return (-1);
  }

  if( ByteWrite( data ) == (-1) )
  {
    I2C_Terminate();
    clk100();
    return (-1);
  }

  StopCondition();

  return 0;

#else  /*_SOFT_I2C_*/
  Wire.beginTransmission( addr );
  Wire.send( data );
  return Wire.endTransmission();
#endif  /*_SOFT_I2C_*/
}

/*************************************************************************/
/*  I2C Read function                                                    */
/*************************************************************************/
int I2C_Read( int addr, unsigned char *data )
{
#if defined( _SOFT_I2C_ )
  StartCondition();

  if( ByteWrite( addr | 0x01 ) == (-1) )
  {
    I2C_Terminate();
    clk100();
    return (-1);
  }

  *data = ByteRead( 1 );

  StopCondition();

  return 0;
#else  /*_SOFT_I2C_*/
  Wire.requestFrom( addr, 1 );
  while( Wire.available() == 0 ) ;
  *data = Wire.receive();
  return Wire.endTransmission();
#endif  /*_SOFT_I2C_*/
}

/*************************************************************************/
/* I2CRTC8564_Write                                                      */
/*************************************************************************/
int I2CRTC8564_Write( unsigned char addr, char data )
{
#if defined( _SOFT_I2C_ )
  StartCondition();

  if( ByteWrite( RTC_BASE_ADDR ) == (-1) )
  {
    I2C_Terminate();
    clk100();
    return (-1);
  }

  if( ByteWrite( addr ) == (-1) )
  {
    I2C_Terminate();
    clk100();
    return (-1);
  }

  if( ByteWrite( data ) == (-1) )
  {
    I2C_Terminate();
    clk100();
    return (-1);
  }

  StopCondition();

  return 0;
#else  /*_SOFT_I2C_*/
  Wire.beginTransmission( RTC_BASE_ADDR );
  Wire.send( addr );
  Wire.send( data );
  return Wire.endTransmission();
#endif  /*_SOFT_I2C_*/
}

/*************************************************************************/
/* I2CRTC8564_Read                                                       */
/*************************************************************************/
int I2CRTC8564_Read( unsigned char addr, char *buff )
{
#if defined( _SOFT_I2C_ )
  StartCondition();

  if( ByteWrite( RTC_BASE_ADDR ) == (-1) )
  {
    I2C_Terminate();
    clk100();
    return (-1);
  }

  if( ByteWrite( addr ) == (-1) )
  {
    I2C_Terminate();
    clk100();
    return (-1);
  }

  SDA_IS_HIGH;
  SCL_IS_HIGH;  /*about 120ns*/
  SCL_IS_HIGH;  /*about 120ns*/
  SCL_IS_HIGH;  /*about 120ns*/
  SCL_IS_HIGH;  /*about 120ns*/
  SCL_IS_HIGH;  /*about 120ns*/

  SDA_IS_LOW;  /*about 120ns*/
  SDA_IS_LOW;  /*about 120ns*/
  SDA_IS_LOW;  /*about 120ns*/
  SDA_IS_LOW;  /*about 120ns*/
  SDA_IS_LOW;  /*about 120ns*/

  if( ByteWrite( RTC_BASE_ADDR | 0x01 ) == (-1) )
  {
    I2C_Terminate();
    clk100();
    return (-1);
  }

  *buff = ByteRead( 1 );

  StopCondition();

  return 0;

#else  /*_SOFT_I2C_*/
  Wire.beginTransmission( RTC_BASE_ADDR );
  Wire.send( addr );

  Wire.requestFrom( RTC_BASE_ADDR, 1 );
  while( Wire.available() == 0 ) ;
  *buff = Wire.receive();
  return Wire.endTransmission();
#endif  /*_SOFT_I2C_*/
}

/*************************************************************************/
/* I2CRTC8564_Blk_Read                                                   */
/*************************************************************************/
int I2CRTC8564_Blk_Read( unsigned char addr, char *buff, int size )
{
#if defined( _SOFT_I2C_ )
  char *ptr = buff;
  char *limit = &buff[ size - 1 ];

  StartCondition();

  if( ByteWrite( RTC_BASE_ADDR ) == (-1) )
  {
    I2C_Terminate();
    clk100();
    return (-1);
  }

  if( ByteWrite( addr ) == (-1) )
  {
    I2C_Terminate();
    clk100();
    return (-1);
  }

  SDA_IS_HIGH;
  SCL_IS_HIGH;  /*about 120ns*/
  SCL_IS_HIGH;  /*about 120ns*/
  SCL_IS_HIGH;  /*about 120ns*/
  SCL_IS_HIGH;  /*about 120ns*/
  SCL_IS_HIGH;  /*about 120ns*/

  SDA_IS_LOW;  /*about 120ns*/
  SDA_IS_LOW;  /*about 120ns*/
  SDA_IS_LOW;  /*about 120ns*/
  SDA_IS_LOW;  /*about 120ns*/
  SDA_IS_LOW;  /*about 120ns*/

  if( ByteWrite( RTC_BASE_ADDR | 0x01 ) == (-1) )
  {
    I2C_Terminate();
    clk100();
    return (-1);
  }

  while( ptr < limit )
  {
    *ptr++ = ByteRead( 0 );
  }
  *ptr = ByteRead( 1 );

  StopCondition();

  return 0;

#else  /*_SOFT_I2C_*/
  Wire.beginTransmission( RTC_BASE_ADDR );
  Wire.send( addr );

  Wire.requestFrom( RTC_BASE_ADDR, size );
  while( Wire.available() < size ) ;
  for( ; size > 0; size-- )
  {
    *buff++ = Wire.receive();
  }
  return Wire.endTransmission();
#endif  /*_SOFT_I2C_*/
}

/*************************************************************************/
/* I2CRTC8564_Blk_Write                                                  */
/*************************************************************************/
int I2CRTC8564_Blk_Write( unsigned char addr, const char *data, int size )
{
#if defined( _SOFT_I2C_ )
  const char *ptr = data;
  const char *limit = &data[ size ];

  StartCondition();

  if( ByteWrite( RTC_BASE_ADDR ) == (-1) )
  {
    I2C_Terminate();
    clk100();
    return (-1);
  }

  if( ByteWrite( addr ) == (-1) )
  {
    I2C_Terminate();
    clk100();
    return (-1);
  }

  while( ptr < limit )
  {
    if( ByteWrite( *ptr++ ) == (-1) )
    {
      I2C_Terminate();
      clk100();
      return (-1);
    }
  }

  StopCondition();
  return 0;

#endif  /*_SOFT_I2C_*/
  return 0;
}

/*************************************************************************/
/* RTC-8564 initialize                                                   */
/*************************************************************************/
int InitRTC8564( int force )
{
  int i,ret;
  char temp,date_time[ 7 ],work[ 8 ];
  unsigned char rtc_adr;
  static const unsigned char RTC8564_Init_Code[] =
    {0x20, 0x11, 0x00,0x10,0x18,0x07,0x04,0x05, 0x10,0x80, 0x80,  0x80, 0x80,  0x00,0x81,63  };
   /*ctrl1,ctrl2,sec, min, hour,day, week,month,year,min_a,hour_a,day_a,week_a,freq,cont,timer*/

  /*RTC8564 confirmed VLbit*/
  if( I2CRTC8564_Read( RTC8564_SEC, &date_time[ SECONDS ] ) != 0 ) return (-1);

  if( date_time[ SECONDS ] & 0x80 || force )  /*VLbit was 1 then detected low voltage*/
  {
    for( rtc_adr = 0; rtc_adr <= sizeof( RTC8564_Init_Code ); rtc_adr++ )
    {
      if( I2CRTC8564_Write( rtc_adr, RTC8564_Init_Code[ rtc_adr ] ) != 0 ) return (-1);
    }
    if( I2CRTC8564_Write( RTC8564_CTRL1, 0x00 ) != 0 ) return (-1);  /*timer start*/

    for( i = 0; i < 100; i++ )
    {
      if( I2CRTC8564_Read( RTC8564_YEAR, &date_time[ YEARS ] ) != 0 ) return (-1);  /*RTC is not there.*/

      if( date_time[ YEARS ] == RTC8564_Init_Code[ RTC8564_YEAR ] )  /**/
      {
        break;
      }
      else
      {
        for( rtc_adr = 0; rtc_adr <= sizeof( RTC8564_Init_Code ); rtc_adr++ )
        {
          if( I2CRTC8564_Write( rtc_adr, RTC8564_Init_Code[ rtc_adr ] ) != 0 ) return (-1);
        }
        if( I2CRTC8564_Write( RTC8564_CTRL1, 0x00 ) != 0 ) return (-1);
      }
    }
    if( i != 100 ) ret = 1;  /**/
    else ret = (-2);  /*initialize error*/
  }
  else ret = 0;

  if( ret > (-1) )
  {
    if( I2CRTC8564_Blk_Read( RTC8564_SEC, work, 7 ) != 0 ) return (-1);
    RTC8564Str2DateStr( date, (const unsigned char *)work );  /**/
  }

  return ret;
}

/*************************************************************************/
/*   0123456789012345678                                                 */
/*   2007/11/21 15:12:00                                                 */
/* DateStr2RTC8564Str( (unsigned char *)work, "2007/12/31 23:59:00" );   */
/* I2CRTC8564_Blk_Write( RTC8564_SEC, (const char *)work, 7 );           */
/*************************************************************************/
unsigned char *DateStr2RTC8564Str( unsigned char *dest, const char *source )
{
  dest[ 0 ] = ((source[ 17 ] & 0x07) << 4) | (source[ 18 ] & 0x0f);  /*second*/
  dest[ 1 ] = ((source[ 14 ] & 0x07) << 4) | (source[ 15 ] & 0x0f);  /*minute*/
  dest[ 2 ] = ((source[ 11 ] & 0x03) << 4) | (source[ 12 ] & 0x0f);  /*hour*/
  dest[ 3 ] = ((source[ 8 ] & 0x03) << 4) | (source[ 9 ] & 0x0f);  /*day*/
  dest[ 4 ] = 0;  /*week*/
  dest[ 5 ] = ((source[ 5 ] & 0x01) << 4) | (source[ 6 ] & 0x0f);  /*month*/
  dest[ 6 ] = ((source[ 2 ] & 0x0f) << 4) | (source[ 3 ] & 0x0f);  /*year*/

  return dest;
}

/*************************************************************************/
/*   0123456789012345678                                                 */
/*   2007/11/21 15:12:00                                                 */
/*************************************************************************/
char *RTC8564Str2DateStr( char *dest, const unsigned char source[] )
{
  dest[ 0 ] = '2';
  dest[ 1 ] = '0';
  dest[ 2 ] = (source[YEARS] >> 4) + '0';
  dest[ 3 ] = (source[YEARS] & 0x0f) + '0';
  dest[ 4 ] = '/';
  dest[ 5 ] = ((source[MONTHS] >> 4) & 0x01) + '0';
  dest[ 6 ] = (source[MONTHS] & 0x0f) + '0';
  dest[ 7 ] = '/';
  dest[ 8 ] = ((source[DAYS] >> 4) & 0x03) + '0';
  dest[ 9 ] = (source[DAYS] & 0x0f) + '0';
  dest[ 10 ] = ' ';
  dest[ 11 ] = ((source[HOURS] >> 4) & 0x03) + '0';
  dest[ 12 ] = (source[HOURS] & 0x0f) + '0';
  dest[ 13 ] = ':';
  dest[ 14 ] = ((source[MINUTES] >> 4) & 0x07) + '0';
  dest[ 15 ] = (source[MINUTES] & 0x0f) + '0';
  dest[ 16 ] = ':';
  dest[ 17 ] = ((source[SECONDS] >> 4) & 0x07) + '0';
  dest[ 18 ] = (source[SECONDS] & 0x0f) + '0';
  dest[ 19 ] = '\0';

  return dest;
}

/*************************************************************************/
/* RTC-8564 Calender Set                                                 */
/*************************************************************************/
int RTC8564_CalenderSet( const char *date_time )
{
  int i;
  unsigned char rtc_adr,work[ 8 ];

  DateStr2RTC8564Str( work, date_time );

  /*timer stop*/
  if( I2CRTC8564_Write( RTC8564_CTRL1, 0x20 ) != 0 ) return (-1);

  /*date and time write*/
  for( rtc_adr = RTC8564_SEC, i = 0; rtc_adr <= RTC8564_YEAR; rtc_adr++ )
  {
    if( I2CRTC8564_Write( rtc_adr, work[ i++ ] ) != 0 ) return (-1);
  }

  /*timer start*/
  if( I2CRTC8564_Write( RTC8564_CTRL1, 0x00 ) != 0 ) return (-1);

  return 0;
}

/***********************************************************************/
/*                                               designed by hamayan   */
/*                                               Copyright(C) hamayan  */
/***********************************************************************/


Arduinoをはじめよう

Arduinoをはじめよう

  • 作者: Massimo Banzi
  • 出版社/メーカー: オライリージャパン
  • 発売日: 2009/03/27
  • メディア: 単行本(ソフトカバー)



ITRONプログラミング入門―H8マイコンとHOSで始める組み込み開発

ITRONプログラミング入門―H8マイコンとHOSで始める組み込み開発

  • 作者: 濱原 和明
  • 出版社/メーカー: オーム社
  • 発売日: 2005/04/25
  • メディア: 単行本



Maruduinoでラーメンタイマー、、、ただし6個いっぺんに作れます。 [アプリケーション]

※なんかイマイチ感がありありだったので、スケッチを修正しました。今度はいっぺんに6個までカップラーメンを作ることができます。

Img_2070.jpgいや、ほら、カップラーメンって全部3分と言うわけでなく、3分だったり、4分だったり、うどんは5分が多いかな。
逆に焼きそばは1分とか、もうまちまち。
一人なら問題無いけれど、多人数で一つのキッチンタイマーでは苦しすぎる。
で、5個6個いっぺんに計測可能なラーメンタイマーを作りましたヨ。

これでキッチンタイマーの奪い合いも無くなり、平和にラーメンをすすれますネ。

LCDの接続はスケッチをごらんいただくとして、スピーカー出力はDI13(DIGITAL13)をAUX1に接続します。2つのスイッチを使いますが、SW1はDI2、SW2はDI3に接続します。

※例の100円スピーカーを接続するのをお忘れなく。
※メロディ発生のスケッチはexamplesのdigitalのtoneMelodyから流用していますので、pitches.hをプロジェクトフォルダーにコピーして使ってください。
※色々と気になる点はありますが、まあ善しとしましょう。
※なんか気の抜けるネタばかりですいません。
/*
  LiquidCrystal Library - Hello World
 
 Demonstrates the use a 16x2 LCD display.  The LiquidCrystal
 library works with all LCD displays that are compatible with the 
 Hitachi HD44780 driver. There are many of them out there, and you
 can usually tell them by the 16-pin interface.
 
 This sketch prints "Hello World!" to the LCD
 and shows the time.
 
  The 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

 * 10K resistor:
 * ends to +5V and ground
 
 Library originally added 18 Apr 2008
 by David A. Mellis
 library modified 5 Jul 2009
 by Limor Fried (http://www.ladyada.net)
 example added 9 Jul 2009
 by Tom Igoe
 modified 25 July 2009
 by David A. Mellis
 
 
 http://www.arduino.cc/en/Tutorial/LiquidCrystal
 */

// include the library code:
#include <LiquidCrystal.h>
#include "pitches.h"

int int0_count,int1_count;
unsigned char store_sw_status[3];  /*スイッチ状態の記録*/
/*SW1はDI2に接続し、割り込み0、SW2はDI3に接続し、割り込み1で処理する。*/
unsigned long guard1_time,guard2_time;
int timers;
struct RECORD 
{
  int minutes;
  unsigned long end_time;
} rec[6];

const struct SCORE
{
  int note;
  int dura;
} score[] =
{
  {NOTE_G4,4},
  {NOTE_A4,4},
  {NOTE_B4,2},
  {NOTE_A4,4},
  {NOTE_G4,8},
  {0,8},
  {NOTE_G4,4},
  {NOTE_A4,4},
  {NOTE_B4,4},
  {NOTE_A4,4},
  {NOTE_G4,4},
  {NOTE_A4,1},
};

const struct POSITION
{
  int x;
  int y;
} pos[] =
{
  {0,0},
  {5,0},
  {10,0},
  {0,1},
  {5,1},
  {10,1},
};

// initialize the library with the numbers of the interface pins
LiquidCrystal lcd(8, 9, 4, 5, 6, 7);

void setup()
{
  lcd.begin(16, 2);
  for( int i = 0; i < sizeof(pos) / sizeof(pos[0]); i++ )
  {
    lcd.setCursor(pos[i].x,pos[i].y);
    lcd.print("0:00");
  }

  attachInterrupt(0, sw1_int_handler, RISING);
  attachInterrupt(1, sw2_int_handler, RISING);
}

void loop()
{
  int i;
  char buffer[16];
  static int count1,count2;

  for( i = 0; i < sizeof(pos) / sizeof(pos[0]); i++ )
  {
    /*表示位置の設定*/
    lcd.setCursor(pos[i].x,pos[i].y);

    if( rec[i].end_time != 0 )
    {
      if( rec[i].end_time <= millis() )
      {
        rec[i].minutes = 0;
        rec[i].end_time = 0;
        play_melody();
        lcd.print(millis2min(buffer,0));
      }
      else
        lcd.print(millis2min(buffer,rec[i].end_time - millis()));
    }
  }

  if( count1 != int0_count )  /*割り込みカウントと不一致が検出された時*/
  {
    count1 = int0_count;  /*更新*/

    /*レコードが空いているかの判断*/
    for( i = 0; i < sizeof(pos) / sizeof(pos[0]); i++ )
    {
      if( rec[ timers ].end_time == 0 ) break;
      if( ++timers >= sizeof(pos) / sizeof(pos[0]) ) timers = 0;
    }

    /*書き込み可能な場合*/
    if( i != sizeof(pos) / sizeof(pos[0]) )
    {
      rec[ timers ].minutes = rec[ timers ].minutes + 1;
      rec[ timers ].minutes %= 10;
      lcd.setCursor(pos[ timers ].x,pos[ timers ].y);
      lcd.print(millis2min(buffer,rec[ timers ].minutes * 60 * 1000UL));
    }
  }

  if( count2 != int1_count )  /*割り込みカウントと不一致が検出された時*/
  {
    count2 = int1_count;  /*更新*/

    /*レコードが空いているかの判断*/
    for( i = 0; i < sizeof(pos) / sizeof(pos[0]); i++ )
    {
      if( rec[ timers ].end_time == 0 ) break;
      if( ++timers >= sizeof(pos) / sizeof(pos[0]) ) timers = 0;
    }

    if( i != sizeof(pos) / sizeof(pos[0]) )
    {
      rec[ timers ].end_time = (rec[ timers ].minutes * 60 * 1000UL) + millis();
      if( ++timers >= sizeof(pos) / sizeof(pos[0]) ) timers = 0;  /*レコード番号の更新*/
    }
  }

  delay( 50 );  /*表示更新時間*/
}

char *millis2min( char *dest, unsigned long milli )
{
  int minute,second,temp;
  
  if( milli > 10 * 60 * 1000UL ) return 0;
  temp = (int)(milli / 1000UL);
  minute = temp / 60;
  second = temp % 60;

  dest[0] = (minute % 10) + '0';
  dest[1] = ':';
  dest[2] = (second / 10) + '0';
  dest[3] = (second % 10) + '0';
  dest[4] = '\0';

  return dest;
}

void play_melody()
{
  for (int thisNote = 0; thisNote < sizeof(score) / sizeof(score[0]); thisNote++)
  {
    int noteDuration = 1000/score[thisNote].dura;
    tone(13, score[thisNote].note,noteDuration);
    int pauseBetweenNotes = noteDuration * 1.30;
    delay(pauseBetweenNotes);
  }
}

void sw1_int_handler()
{
  if( millis() > guard1_time )  /*現在時刻がガードタイムを越した時のみ有効とする*/
  {
    int0_count++;  /*有効割り込み数*/
    guard1_time = millis() + 300UL;  /*不感帯時間の再設定*/
  }
}

void sw2_int_handler()
{
  if( millis() > guard2_time )  /*現在時刻がガードタイムを越した時のみ有効とする*/
  {
    int1_count++;  /*有効割り込み数*/
    guard2_time = millis() + 300UL;  /*不感帯時間の再設定*/
  }
}


ITRONプログラミング入門―H8マイコンとHOSで始める組み込み開発

ITRONプログラミング入門―H8マイコンとHOSで始める組み込み開発

  • 作者: 濱原 和明
  • 出版社/メーカー: オーム社
  • 発売日: 2005/04/25
  • メディア: 単行本



Prototyping Lab ―「作りながら考える」ためのArduino実践レシピ

Prototyping Lab ―「作りながら考える」ためのArduino実践レシピ

  • 作者: 小林 茂
  • 出版社/メーカー: オライリージャパン
  • 発売日: 2010/05/27
  • メディア: 大型本



Maruduino(Arduino)で簡易的なストップウオッチの製作 またかよ編 [アプリケーション]

Img_2043.jpgまたかよぅ、と言った感じですが、、、

今までの方式で最大の問題は、計測時間が正確ではなかった点です。
今までの時間の取得は50ms+内部処理時間周期でSW1の状態を監視していたので、ワーストケースでは50ms+LCD表示など内部処理に掛かる時間だけ実際のスイッチの変化から遅れて計測がスタート、ストップしていました。
これを解消するためには、スイッチの変化に対して即座に反応し、計測の開始時間や終了時間を記憶する必要があります。

そこで、Arduinoのライブラリの中のattachInterruptと言う割り込みハンドラの登録機能を利用します。
これは、スイッチの状態の変化が検出されると通常のloop処理以外の流れ(コンテキスト)にマイコンの処理を切り替え、そこで必要な処理を行おうというものです。
必要な処理はユーザーが自分で作成し、登録します。

つまりこのユーザーが登録した別のコンテキストの先頭で変化を検出した時間を記録しておき、その値を計測時間として利用します。
また、同時にチャタリング?対策もここで行ってしまいます。

※厳密に言えばこの方法でもスイッチの変化から割り込みハンドラが起動して時間が記録されるまでに数十とか数百μsくらいの遅延は発生しているのですが、10msの精度から見れば十分に許容範囲内です。

ただしattachInterruptで利用できる端子は限定されており、DIGITAL2またはDIGITAL3のみです。その為、Hello Worldで利用しているLCD端子の移動が必要です。
以下に掲載するスケッチのコメント部にも具体的な割付を書きましたが、DIGITAL2をLCDの制御線から移動し、代わりにSW1からの入力に利用しています。

と言う訳で、スケッチをここに掲載しておきます。
/*
  LiquidCrystal Library - Hello World
 
 Demonstrates the use a 16x2 LCD display.  The LiquidCrystal
 library works with all LCD displays that are compatible with the 
 Hitachi HD44780 driver. There are many of them out there, and you
 can usually tell them by the 16-pin interface.
 
 This sketch prints "Hello World!" to the LCD
 and shows the time.
 
  The 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

 * 10K resistor:
 * ends to +5V and ground
 * wiper to LCD VO pin (pin 3)
 
 Library originally added 18 Apr 2008
 by David A. Mellis
 library modified 5 Jul 2009
 by Limor Fried (http://www.ladyada.net)
 example added 9 Jul 2009
 by Tom Igoe
 modified 25 July 2009
 by David A. Mellis
 
 
 http://www.arduino.cc/en/Tutorial/LiquidCrystal
 */

// include the library code:
#include <LiquidCrystal.h>
int mode;  /*初期状態は0(計測スタート待ち)*/
  /*初期状態→計測中→停止中→初期状態に戻る  の状態遷移を行う*/
int int_count;
unsigned long passed_time,latest_time;
unsigned long int_time,guard_time;

// initialize the library with the numbers of the interface pins
LiquidCrystal lcd(8, 9, 4, 5, 6, 7);

void setup() {
  // set up the LCD's number of rows and columns: 
  lcd.begin(16, 2);
  // Print a message to the LCD.
  lcd.print("Play Stopwatch.");
  attachInterrupt(0, sw1_int_handler, RISING);
}

void loop() {
  static int count;

  if( count != int_count )  /*割り込みカウントと不一致が検出された時*/
  {
    count = int_count;  /*更新*/

    switch ( mode )
    {
      case 0 :  /*初期状態から計測中に遷移*/
        passed_time = int_time;  /*計測開始時刻*/
        mode = 1;
        break;
      case 1 :
        latest_time = int_time;  /*計測停止時刻*/
        mode = 2;
        break;
      default :
        lcd.setCursor(0, 1);
        lcd.print("                ");  /*表示クリア*/
        mode = 0;
        break;
    }
  }

  lcd.setCursor(0, 1);
  if( mode == 1 )
    lcd.print((double)(millis() - passed_time) / 1000.0);  /*0.1秒単位で出力*/
  else if( mode == 2 )
    lcd.print((double)(latest_time - passed_time) / 1000.0);  /*計測完了*/
  else
    lcd.print(0.0);  /*表示を0に戻す*/

  delay( 50 );  /*表示更新時間*/
}

void sw1_int_handler()
{
  if( millis() > guard_time )  /*現在時刻がガードタイムを越した時のみ有効とする*/
  {
    int_count++;  /*有効割り込み数*/
    int_time = millis();
    guard_time = int_time + 500UL;  /*不感帯時間の再設定*/
  }
}


Maruduino(Arduino)で簡易的なストップウオッチの製作2 [アプリケーション]

Img_2043.jpgさて、前回は簡易的なストップウオッチを作成しました。機能的にだけではなく論理で簡易的にもやっているので、幾つかの問題があります。
その一つは押下の検出にSW1の論理レベルを使っている事です。

もう一度スイッチの押下について考えてみます。
スイッチの情報を得る場合、2つの状態が考えられます。
1.今現在の状態の取得。つまり現在スイッチが押されているのか、それとも離されているのか?です。
2.スイッチの状態の変化の取得。つまり離されている状態から押された状態に変化したのか、それともその逆なのか?です。
実はこの2つの状態はよく組み合わせで使われます。スイッチを瞬間的に押された事を検出するなら2を、長押しを検出するなら1とかです。

前回のスケッチでは1の方法で検出をしていました。これはストップウオッチで1秒以上長押しされる事はあまり無い”だろう”と言う、あくまでもこちらの側の都合ですね。
で、今回は2の方法を実現してみます。

変化点を検出する為には、過去と現在の2つの状態が必要です。
スイッチが押された事を検出するなら、「過去は離されていたが、今見たら押されている!」と言う状態の検出です。

と言うわけで過去の状態を保存する変数と現在の状態を代入するbool型の変数の2つを用意してコーディングし直してみました。
この方法は、比較する論理レベルを逆にするとスイッチが離された事の検出に使えますし、bool型ではなく変数を整数型で複数ポートを一気に読み出し、代入を行うと、複数ポートの変化を論理だけで検出する事ができます。
※ITRON等で使う場合、変化点をXOR(排他的論理和)として検出し、その値をイベントフラグに代入すると、結構スマートな使い方が出来る。
/*
  LiquidCrystal Library - Hello World
 
 Demonstrates the use a 16x2 LCD display.  The LiquidCrystal
 library works with all LCD displays that are compatible with the 
 Hitachi HD44780 driver. There are many of them out there, and you
 can usually tell them by the 16-pin interface.
 
 This sketch prints "Hello World!" to the LCD
 and shows the time.
 
  The circuit:
 * LCD RS pin to digital pin 12
 * LCD Enable pin to digital pin 11
 * LCD D4 pin to digital pin 5
 * LCD D5 pin to digital pin 4
 * LCD D6 pin to digital pin 3
 * LCD D7 pin to digital pin 2
 * 10K resistor:
 * ends to +5V and ground
 * wiper to LCD VO pin (pin 3)
 
 Library originally added 18 Apr 2008
 by David A. Mellis
 library modified 5 Jul 2009
 by Limor Fried (http://www.ladyada.net)
 example added 9 Jul 2009
 by Tom Igoe
 modified 25 July 2009
 by David A. Mellis
 
 
 http://www.arduino.cc/en/Tutorial/LiquidCrystal
 */

// include the library code:
#include <LiquidCrystal.h>
const int sw1 = 13;  /*スイッチ1の入力*/
int mode;  /*初期状態は0(計測スタート待ち)*/
  /*初期状態→計測中→停止中→初期状態に戻る  の状態遷移を行う*/
int ignore_time;
unsigned long passed_time,latest_time;
boolean kako,ima;  /*sw1の状態を過去の時点と今の時点で保存する変数*/

// initialize the library with the numbers of the interface pins
LiquidCrystal lcd(12, 11, 5, 4, 3, 2);

void setup() {
  pinMode( sw1, INPUT );
  // set up the LCD's number of rows and columns: 
  lcd.begin(16, 2);
  // Print a message to the LCD.
  lcd.print("Play Stopwatch.");
  kako = (digitalRead( sw1 ) == HIGH) ? true : false;  /*初期化時に過去の値を保存して置く*/
}

void loop() {
  ima = (digitalRead( sw1 ) == HIGH) ? true : false;  /*今の状態を取得*/

  if( ignore_time == 0 && kako == false && ima == true )  /*つまり不感帯以外で過去に偽、今が真の時を探す*/
  {
    ignore_time = 20;  /*1秒の不感帯とする*/
    switch ( mode )
    {
      case 0 :  /*初期状態から計測中に遷移*/
        passed_time = millis();  /*計測開始時刻*/
        mode = 1;
        break;
      case 1 :
        latest_time =  millis();  /*計測停止時刻*/
        mode = 2;
        break;
      default :
        lcd.setCursor(0, 1);
        lcd.print("                ");  /*表示クリア*/
        mode = 0;
        break;
    }
  }
  kako = ima;  /*今の状態を過去に代入*/

  lcd.setCursor(0, 1);
  if( mode == 1 )
    lcd.print((double)(millis() - passed_time) / 1000.0);  /*0.1秒単位で出力*/
  else if( mode == 2 )
    lcd.print((double)(latest_time - passed_time) / 1000.0);  /*計測完了*/
  else
    lcd.print(0.0);  /*表示を0に戻す*/

  if( --ignore_time <= 0 ) ignore_time = 0;  /*スイッチを無視する時間の更新と上限*/

  delay( 50 );  /*表示更新時間*/
}


ITRONプログラミング入門―H8マイコンとHOSで始める組み込み開発

ITRONプログラミング入門―H8マイコンとHOSで始める組み込み開発

  • 作者: 濱原 和明
  • 出版社/メーカー: オーム社
  • 発売日: 2005/04/25
  • メディア: 単行本



Prototyping Lab ―「作りながら考える」ためのArduino実践レシピ

Prototyping Lab ―「作りながら考える」ためのArduino実践レシピ

  • 作者: 小林 茂
  • 出版社/メーカー: オライリージャパン
  • 発売日: 2010/05/27
  • メディア: 大型本



Maruduino(Arduino)で簡易的なストップウオッチの製作1 [アプリケーション]

Img_2043.jpg組み立て編でスイッチ、LED、LCDを実装しましたので、この辺で何か作ってみましょう。
わりと判り易いと思われるストップウオッチです、、、既にArduinoの作例として何処かに登録されている様な気がしないでもないけれどネ。

機能としては一応100分の1秒表示。スイッチは一つ、その一つのスイッチで
1.計測待ち
2.計測
3.計測停止
の3状態を1~3に、また1に戻ると言った簡単な状態遷移で行います。

肝心の時間の計測には起動時からの時間をms単位で返すmillisライブラリを使用します。簡単ですから。
スイッチの押下の検出は自分でやらねばなりません。

ところでスイッチの入力となるといつも問題なるのがチャタリングでしょう。切り替えた時に接点がバウンス(はね返り)してONとOFFの状態が連続して表れるとか。
ですが、このストップウオッチのスイッチ入力に巨大なレバー式スイッチなど使いませんので、現実的にはバウンスが問題になる訳ではありません。

使用しているタクトスイッチは、上から押し込む事で離れていた導電ゴムの接点が押しつぶされる様に接触しますので、どっちかと言えば接触するかしないかの際での人間の指の状態とか、人によって長く押したり、短く押したりと言った時間的不確実性の方が問題です。

やっぱり何等かの不確実性を解消しないといけないのは事実ですので、もう少しこのストップウオッチにおけるスイッチの役目を考えてみると、
1.まず押された事を検出できれば良い、、、つまり離された事まで考慮する必要が無い。
2.ONとOFFが繰り返される事は時間のフィルターを用いる事で解消できる。

と言う訳で今回のストップウオッチにおけるスイッチ押下による状態遷移の条件として、前回の変化から一定以上の時間が経過している事。押下のみ検出とします。

LiquidCrystalのHell Worldを改造して以下の様なスケッチを書いて見ました。
しかし100分の1秒の精度は実に怪しいですね。
※一桁間違えたので修正(笑)
/*
  LiquidCrystal Library - Hello World
 
 Demonstrates the use a 16x2 LCD display.  The LiquidCrystal
 library works with all LCD displays that are compatible with the 
 Hitachi HD44780 driver. There are many of them out there, and you
 can usually tell them by the 16-pin interface.
 
 This sketch prints "Hello World!" to the LCD
 and shows the time.
 
  The circuit:
 * LCD RS pin to digital pin 12
 * LCD Enable pin to digital pin 11
 * LCD D4 pin to digital pin 5
 * LCD D5 pin to digital pin 4
 * LCD D6 pin to digital pin 3
 * LCD D7 pin to digital pin 2
 * 10K resistor:
 * ends to +5V and ground
 * wiper to LCD VO pin (pin 3)
 
 Library originally added 18 Apr 2008
 by David A. Mellis
 library modified 5 Jul 2009
 by Limor Fried (http://www.ladyada.net)
 example added 9 Jul 2009
 by Tom Igoe
 modified 25 July 2009
 by David A. Mellis
 
 
 http://www.arduino.cc/en/Tutorial/LiquidCrystal
 */

// include the library code:
#include <LiquidCrystal.h>
const int sw1 = 13;  /*スイッチ1の入力*/
int mode;  /*初期状態は0(計測スタート待ち)*/
  /*初期状態→計測中→停止中→初期状態に戻る  の状態遷移を行う*/
int ignore_time;
unsigned long passed_time,latest_time;


// initialize the library with the numbers of the interface pins
LiquidCrystal lcd(12, 11, 5, 4, 3, 2);

void setup() {
  pinMode( sw1, INPUT );
  // set up the LCD's number of rows and columns: 
  lcd.begin(16, 2);
  // Print a message to the LCD.
  lcd.print("Play Stopwatch.");
}

void loop() {
  if( ignore_time == 0 && digitalRead( sw1 ) == HIGH )  /*スイッチが押されるとHIGHとなる*/
  {
    ignore_time = 20;  /*1秒の不感帯とする*/
    switch ( mode )
    {
      case 0 :  /*初期状態から計測中に遷移*/
        passed_time = millis();  /*計測開始時刻*/
        mode = 1;
        break;
      case 1 :
        latest_time =  millis();  /*計測停止時刻*/
        mode = 2;
        break;
      default :
        lcd.setCursor(0, 1);
        lcd.print("                ");  /*表示クリア*/
        mode = 0;
        break;
    }
  }

  lcd.setCursor(0, 1);
  if( mode == 1 )
    lcd.print((double)(millis() - passed_time) / 1000.0);  /*0.1秒単位で出力*/
  else if( mode == 2 )
    lcd.print((double)(latest_time - passed_time) / 1000.0);  /*計測完了*/
  else
    lcd.print(0.0);  /*表示を0に戻す*/

  if( --ignore_time <= 0 ) ignore_time = 0;  /*スイッチを無視する時間の更新と上限*/

  delay( 50 );  /*表示更新時間*/
}


ITRONプログラミング入門―H8マイコンとHOSで始める組み込み開発

ITRONプログラミング入門―H8マイコンとHOSで始める組み込み開発

  • 作者: 濱原 和明
  • 出版社/メーカー: オーム社
  • 発売日: 2005/04/25
  • メディア: 単行本



大人の科学マガジン Vol.27(8ビットマイコン) (Gakken Mook)

大人の科学マガジン Vol.27(8ビットマイコン) (Gakken Mook)

  • 作者: 大人の科学マガジン編集部
  • 出版社/メーカー: 学習研究社
  • 発売日: 2010/05/12
  • メディア: ムック



Prototyping Lab ―「作りながら考える」ためのArduino実践レシピ

Prototyping Lab ―「作りながら考える」ためのArduino実践レシピ

  • 作者: 小林 茂
  • 出版社/メーカー: オライリージャパン
  • 発売日: 2010/05/27
  • メディア: 大型本



超音波距離計の製作2 [アプリケーション]

Img_1996_s.jpg前回ブレッドボード上でプロトタイピングを行い、なんとか動かせる目処が付いたので、折角なので基板化してみました。回路は変更ありません。


Img_2000_s.jpg作成した基板の表側?です。スピーカー、マイク、LEDがこちらを見ています。


Img_1999_s.jpg裏側です。リード部品を所狭しと搭載しています。スピーカーと反対側にこれらの部品を実装したからなんとか収まった様なものです。


実際にブレッドボード上でプロトタイピングしたので大体は上手く動くのですが、基板化する事により発生した問題なんてのもあります。

ブレッドボード上にスピーカーを載せた時は、スピーカーの固定はゆるゆるです。あの穴にズボっと挿すだけですから。
ところが基板上に半田付けしてしまうとかなりかっちり固定される事となります。
超音波を発している時間は極短い時間ですし、周波数も40KHzと可聴領域の遥か上です。それなのに、なにやら音がポツッ!、ポツッ!と聞こえてきます。

そうです、どうも超音波を駆動した時、スピーカー自身がそのショックで振動してしまうようです。この振動が剛性のある基板を経由してマイク側に伝わるものですから、受信側は大変な事になってしまいます。
※耳にはっきりと聞き取れる位の音なので、可聴領域の周波数だとしても、その振動の継続時間がかなり長くなっていると思われ、これがゲート時間を超えています。

対策は色々あると思いますが、最も良いのはスピーカー側の固定をフローティングする事でしょう。但し機構的に難しいので妥協として出力レベルを押さえる事とします。

ADM3202の出力には最低300Ωの抵抗が入っています。これは短絡に対する保護だと思われますが、出力抵抗が300Ωだと考えれば、抵抗で分圧する事でスピーカーの入力を小さくできます。
今は240Ωの抵抗をスピーカーの2つの端子間に入れています。これだと2m弱位の測定距離となる様です。

まあ、まだまだカットアンドトライが必要みたいですが、一応完成と言う事で。
ultra_sonic_distance_02.png

Prototyping Lab ―「作りながら考える」ためのArduino実践レシピ

Prototyping Lab ―「作りながら考える」ためのArduino実践レシピ

  • 作者: 小林 茂
  • 出版社/メーカー: オライリージャパン
  • 発売日: 2010/05/27
  • メディア: 大型本



Making Things Talk ―Arduinoで作る「会話」するモノたち

Making Things Talk ―Arduinoで作る「会話」するモノたち

  • 作者: Tom Igoe
  • 出版社/メーカー: オライリージャパン
  • 発売日: 2008/11/17
  • メディア: 大型本



大人の科学マガジン Vol.27(8ビットマイコン) (Gakken Mook)

大人の科学マガジン Vol.27(8ビットマイコン) (Gakken Mook)

  • 作者: 大人の科学マガジン編集部
  • 出版社/メーカー: 学習研究社
  • 発売日: 2010/05/12
  • メディア: ムック



超音波距離計の製作1 [アプリケーション]

※勿論これは普通にArduinoでも実験可能です!。是非お試しを。
Img_1930_s.jpg超音波距離計は、40KHzの超音波領域の音をスピーカーから発し、対象物に当たって帰って来た音をマイクで拾い、往復に掛かった時間から対象物までの距離を計算すると言う、わりと簡単な原理で動いています。

秋月で発売されている超音波距離計は、タイマーICで40KHzを生成してスピーカーで出し、マイクで拾った音を増幅して簡単なバンドパスフィルターを通して一定のレベルとの比較出力を時間を換算しているロジックに入れて距離を出し、7セグメントLEDにて表示をしています。
なかなか良く考えられた回路だと思います。

翻ってArduinoでそれをやろうとした時、勿論特別にタイマーICを用意する必要も、時間を計るロジックICを用意する必要も無く、全てはArduinoのAVRマイコンの中にあります。

なので超音波距離計を作る残りの要素は極僅かですね。
超音波スピーカーをドライブする回路と、超音波スピーカー、超音波マイクに増幅回路、バンドパスフィルター、レベル比較器、表示器だけです。

この位の回路なら十分ブレッドボード上に構成可能です。それがこの記事の冒頭の写真となっています。
回路図はこれです。拡張子をpdfに戻してください。
また、スケッチは簡単ですが以下の内容で可能です。

※Maruduinoのプロトタイプ基板で作成している為、若干発売中の基板とはポートが異なります。
/***********************************************************************/
/* ultra sonic distance meter                                          */
/*                                               designed by hamayan   */
/*                                               Copyright(C) hamayan  */
/***********************************************************************/
#include <LiquidCrystal.h>
#include <avr/io.h>
#include <avr/interrupt.h>

/*************************************************************************/
/* defines                                                               */
/*************************************************************************/
#define  AREF     0
#define  NOT_GND  1
#define  DI13     2
#define  DI12     3
#define  DI11     4
#define  DI10     5
#define  DI9      6
#define  DI8      7
#define  DI7      8
#define  DI6      9
#define  DI5      10
#define  DI4      11
#define  DI3      12
#define  DI2      13
#define  DI1      
#define  DI0      
#define  AN0      14
#define  AN1      15
#define  AN2      16
#define  AN3      17
#define  AN4      18
#define  AN5      19
#define  PWM11    DI4
#define  PWM10    DI5
#define  PWM9     DI6
#define  PWM6     DI9
#define  PWM5     DI10
#define  PWM3     DI12

/*************************************************************************/
/*  global parameter                                                     */
/*************************************************************************/
#define  UltraSonicPin  PWM6
LiquidCrystal lcd(DI2, DI3, DI4, DI5, DI6, DI7);

/*************************************************************************/
/*  prototype                                                            */
/*************************************************************************/

/*************************************************************************/
/*  timer1 output compare match a int handler                            */
/*************************************************************************/
SIGNAL( TIMER1_COMPA_vect )
{
}

/*************************************************************************/
/*  setup                                                                */
/*************************************************************************/
void setup()
{
  lcd.begin(16,2);
  lcd.clear();
  lcd.print( "designed by" );
  lcd.setCursor(0,1);
  lcd.print( "  hamayan." );
  pinMode( DI8, INPUT );
  pinMode( DI10, OUTPUT );
  digitalWrite( DI10, LOW );
}

/*************************************************************************/
/*  main loop                                                            */
/*************************************************************************/
void loop()
{
  digitalWrite( DI10, HIGH );
  tone( UltraSonicPin, 40000 );
  unsigned long startMicro = micros();
//  delayMicroseconds( 500 / 8 );
  delayMicroseconds( 250 / 8 );
  noTone( UltraSonicPin );
//  delayMicroseconds( 500 / 8 );
  delayMicroseconds( 750 / 8 );
  digitalWrite( DI10, LOW );
  delayMicroseconds( 500 / 8 );
  unsigned long endMicro = startMicro;
  while( digitalRead( DI8 ) == LOW )
  {
    endMicro = micros();
    if( (endMicro - startMicro) > 20 * 1000UL ) break;
  }

  unsigned long distance;
  if( (endMicro - startMicro) < 20 * 1000UL )
  {
    distance = ((340 * (endMicro - startMicro)) / 10000UL) / 2;
  }
  else
  {
    distance = 999;
  }

  lcd.clear();
  lcd.print( distance );
  lcd.print( "cm" );

  delay( 500 );  
}


Prototyping Lab ―「作りながら考える」ためのArduino実践レシピ

Prototyping Lab ―「作りながら考える」ためのArduino実践レシピ

  • 作者: 小林 茂
  • 出版社/メーカー: オライリージャパン
  • 発売日: 2010/05/27
  • メディア: 大型本



Mad Science ―炎と煙と轟音の科学実験54

Mad Science ―炎と煙と轟音の科学実験54

  • 作者: Theodore Gray
  • 出版社/メーカー: オライリージャパン
  • 発売日: 2010/05/24
  • メディア: 大型本



- | 次の10件 アプリケーション ブログトップ

この広告は前回の更新から一定期間経過したブログに表示されています。更新すると自動で解除されます。