目次

Schumann resonance

 Schumann resonanceとは、地球の地表と電離層の間に
 存在している極極超長波で、周波数が7.83Hzです。
 1952年、ドイツの物理学者であるWinfried Otto Schumann博士
 が発見しました。

 Schumann resonanceの周波数で、電磁波を生成している場は
 リラックス効果がある空間になると言われています。

 PIC12F629を利用して、7.83Hzの電磁波を生成する回路を
 インダクタを利用して作ります。



 全体回路は、以下。




 10kHzを1277分周すると、7.83Hz前後になります。
 タイマー割込みで10kHzを生成して対応します。

 PIC12F629のタイマー0を利用し、オーバーフローで
 割込みを発生させます。

 タイマー0の入力クロックは1MHzなので、プリスケーラで
 2分周して500kHzにします。500kHzを50分周し、10kHzを
 生成します。その10kHzを1277分周して7.83Hzに。

 割り込みハンドラは、以下。
 イベントフラグをセットする場合の条件判定をしています。

#define CNTBEGIN 216
#define CNTMAX   1276

void interrupt(void)
{
  /* generate 7.83Hz */
  if ( INTCON.T0IF == ON ) {
    /* clear flag */
    INTCON.T0IF = OFF ;
    /* initialize */
    TMR0 = CNTBEGIN ;
    /* increment */
    timcnt++ ;
    /* judge */
    if ( timcnt == CNTMAX ) {
      /* clear */
      timcnt = 0 ;
      /* set flag */
      eflag = ON ;
    }
  }
}

 mainの中では、イベントフラグを受取り、カウンタを
 インクリメントして、カウンタ値に応じてLEDの点灯
 消灯を確定です。

    if ( eflag == ON ) {
      /* clear flag */
      eflag = OFF ;
      /* generate code */
      tmp = *(pat+idx) ;
      if ( GPIO.B5 == ON ) { tmp = *(pat+2-idx) ; }
      /* impress */
      GPIO = tmp & 0x07 ;
      /* update */
      idx++ ;
      /* judge */
      if ( idx == 3 ) { idx = 0 ; }
    }

 変数idxを利用して、点灯するLEDを変えていきます。
 雪の結晶の頂点にLEDがあるようにし、インダクタに
 電流を流します。

 タイミングチャートでみると、さざ波のように
 電流を流しています。



 3進カウンタを用意して、カウンタ値に対応する
 ビットパターンを出力して、さざ波を実現。

 カウンタ値に対するビットパターンは、配列に格納。

  *(pat+0) = ~0x01 ;
  *(pat+1) = ~0x02 ;
  *(pat+2) = ~0x04 ;

 回転の左右判断は、スイッチの状態で利用。
 回転方向は、ビットパターンを取出す順序を
 変えて対応します。

 まとめると、以下。

typedef unsigned char UBYTE ;
typedef unsigned int  UWORD ;

#define OFF 0
#define ON  OFF+1

#define CNTBEGIN 216
#define CNTMAX   1276

volatile UWORD timcnt ;
volatile UBYTE eflag ;
volatile UBYTE tmp ;
volatile UBYTE idx ;
volatile UBYTE pat[3] ;

/* function prototype */
void  init_usr(void);

/* interrupt handler */
void interrupt(void)
{
  /* generate 7.83Hz */
  if ( INTCON.T0IF == ON ) {
    /* clear flag */
    INTCON.T0IF = OFF ;
    /* initialize */
    TMR0 = CNTBEGIN ;
    /* increment */
    timcnt++ ;
    /* judge */
    if ( timcnt == CNTMAX ) {
      /* clear */
      timcnt = 0 ;
      /* set flag */
      eflag = ON ;
    }
  }
}

void main(void)
{
  /* initialize */
  init_usr() ;
  /* endless loop */
  while ( ON ) {
    /* event handling */
    if ( eflag == ON ) {
      /* clear flag */
      eflag = OFF ;
      /* generate code */
      tmp = *(pat+idx) ;
      if ( GPIO.B5 == ON ) { tmp = *(pat+2-idx) ; }
      /* impress */
      GPIO = tmp & 0x07 ;
      /* update */
      idx++ ;
      /* judge */
      if ( idx == 3 ) { idx = 0 ; }
    }
  }
}

/* define function body */
void init_usr(void)
{
  /* I/O state */
  GPIO = 0x00 ;
  /* I/O directions */
  TRISIO = 0x38 ; /* bit0,1,2 as output , others as input */
  /* disable compare module */
  CMCON = 0x07 ;
  /* pull-up */
  WPU = 0x30 ;
  /* initialize Timer 0 */
  {
    /*
       4MHz/4 = 1MHz -> 1MHz/2 = 500kHz prescaler = 1:2
    */
    OPTION_REG = 0x00 ;
    /* 256 - 50 = 216 */
    TMR0 = CNTBEGIN ;
    /* enable timer0 overflow interrupt */
    INTCON.T0IE = ON ;
  }
  /* enable general interrupt */
  INTCON.GIE = ON ;
  /* clear flag */
  eflag = OFF ;
  /* initialize variables */
  timcnt = 0 ;
  idx = 0 ;
  *(pat+0) = ~0x01 ;
  *(pat+1) = ~0x02 ;
  *(pat+2) = ~0x04 ;
}

 インダクタは、インクを使い切ったボールペンを
 利用して自作します。



 PIC12F629よりも安価なPIC12F1501でも、実現して
 みます。

 PIC12F1501は、I/Oへの書き込みをRead Modify Write
 ではなく、読込みと書込みに専用レジスタを用意した
 一般的なマイコンと同じ方式を採用しています。

 ソースリストは、以下。

typedef unsigned char  UBYTE ;
typedef unsigned short UWORD ;

#define OFF 0
#define ON  OFF+1

#define CNTBEGIN 6
#define CNTMAX   128

volatile UWORD timcnt ;
volatile UBYTE eflag ;
volatile UBYTE tmp ;
volatile UBYTE idx ;
volatile UBYTE pat[3] ;

/* function prototype */
void  init_usr(void);

/* interrupt handler */
void interrupt(void)
{
  /* generate 7.83Hz */
  if ( INTCON.T0IF == ON ) {
    /* clear flag */
    INTCON.T0IF = OFF ;
    /* initialize */
    TMR0 = CNTBEGIN ;
    /* increment */
    timcnt++ ;
    /* judge */
    if ( timcnt == CNTMAX ) {
      /* clear */
      timcnt = 0 ;
      /* set flag */
      eflag = ON ;
    }
  }
}

void main(void)
{
  /* initialize */
  init_usr() ;
  /* endless loop */
  while ( ON ) {
    /* event handling */
    if ( eflag == ON ) {
      /* clear flag */
      eflag = OFF ;
      /* generate code */
      tmp = *(pat+idx) ;
      if ( PORTA.B5 == ON ) { tmp = *(pat+2-idx) ; }
      /* impress */
      LATA = tmp & 0x07 ;
      /* update */
      idx++ ;
      /* judge */
      if ( idx == 3 ) { idx = 0 ; }
    }
  }
}

/* define function body */
void init_usr(void)
{
  /* select 4MHz */
  OSCCON = (0x0d << 3) | 0x03 ;
  /* disable A/D converter */
  ADCON0.ADON = OFF ;
  ADCON2      = 0 ;
  /* disable D/A converter */
  DACCON0.DACEN = OFF ;
  /* disable compare module */
  CM1CON0.C1ON = OFF ;
  CM1CON0.C1OE = OFF ;
  /* I/O state */
  LATA = 0 ;
  /* I/O directions */
  TRISA = 0xf8 ; /* bit0,1,2 as output , others as input */
  /* pull-up */
  WPUA = 0x30 ;
  /* initialize Timer 0 */
  {
    /*
       4MHz/4 = 1MHz -> 1MHz/2 = 250kHz prescaler = 1:4
    */
    OPTION_REG = 0x01 ;
    /* 256 - 6 = 250 */
    TMR0 = CNTBEGIN ;
    /* enable timer0 overflow interrupt */
    INTCON.TMR0IE = ON ;
  }
  /* enable general interrupt */
  INTCON.GIE = ON ;
  /* clear flag */
  eflag = OFF ;
  /* initialize variables */
  timcnt = 0 ;
  idx = 0 ;  
  *(pat+0) = ~0x01 ;
  *(pat+1) = ~0x02 ;
  *(pat+2) = ~0x04 ;
}

 PIC12F1501は、A/D変換器、D/A変換器、アナログコンパレータ
 タイマー0、1、2とモジュールがあり、さらに内蔵クロック
 がいろいろあるので、思う通りに動かすのに苦労しました。

 PICを使わないで、デジタルICだけで実現すると
 次のような大掛かりな回路になります。



 トランジスタを個別に半田づけするのは、しんどいので
 インバータバッファを利用してみました。



 回路図は、次のように単純になります。



 ファームウエアは、出力を正論理にします。

typedef unsigned char  UBYTE ;
typedef unsigned short UWORD ;

#define OFF 0
#define ON  OFF+1

#define CNTBEGIN 6
#define CNTMAX   128

volatile UWORD timcnt ;
volatile UBYTE eflag ;
volatile UBYTE tmp ;
volatile UBYTE idx ;
volatile UBYTE pat[3] ;

/* function prototype */
void  init_usr(void);

/* interrupt handler */
void interrupt(void)
{
  /* generate 7.83Hz */
  if ( INTCON.T0IF == ON ) {
    /* clear flag */
    INTCON.T0IF = OFF ;
    /* initialize */
    TMR0 = CNTBEGIN ;
    /* increment */
    timcnt++ ;
    /* judge */
    if ( timcnt == CNTMAX ) {
      /* clear */
      timcnt = 0 ;
      /* set flag */
      eflag = ON ;
    }
  }
}

void main(void)
{
  /* initialize */
  init_usr() ;
  /* endless loop */
  while ( ON ) {
    /* event handling */
    if ( eflag == ON ) {
      /* clear flag */
      eflag = OFF ;
      /* generate code */
      tmp = *(pat+idx) ;
      if ( PORTA.B5 == ON ) { tmp = *(pat+2-idx) ; }
      /* impress */
      LATA = tmp & 0x07 ;
      /* update */
      idx++ ;
      /* judge */
      if ( idx == 3 ) { idx = 0 ; }
    }
  }
}

/* define function body */
void init_usr(void)
{
  /* select 4MHz */
  OSCCON = (0x0d << 3) | 0x03 ;
  /* disable A/D converter */
  ADCON0.ADON = OFF ;
  ADCON2      = 0 ;
  /* disable D/A converter */
  DACCON0.DACEN = OFF ;
  /* disable compare module */
  CM1CON0.C1ON = OFF ;
  CM1CON0.C1OE = OFF ;
  /* I/O state */
  LATA = 0 ;
  /* I/O directions */
  TRISA = 0xf8 ; /* bit0,1,2 as output , others as input */
  /* pull-up */
  WPUA = 0x30 ;
  /* initialize Timer 0 */
  {
    /*
       4MHz/4 = 1MHz -> 1MHz/2 = 250kHz prescaler = 1:4
    */
    OPTION_REG = 0x01 ;
    /* 256 - 6 = 250 */
    TMR0 = CNTBEGIN ;
    /* enable timer0 overflow interrupt */
    INTCON.TMR0IE = ON ;
  }
  /* enable general interrupt */
  INTCON.GIE = ON ;
  /* clear flag */
  eflag = OFF ;
  /* initialize variables */
  timcnt = 0 ;
  idx = 0 ;  
  *(pat+0) = 0x01 ;
  *(pat+1) = 0x02 ;
  *(pat+2) = 0x04 ;
}

 LEDを、ダイソーで売られているプッシュライトに
 接続すると、次のようになります。



 プッシュライトは、3LEDが同時に点灯しますが
 LEDの実装基板の一部をカットし、独立点灯可能
 にしました。

 方向変更のスイッチをトグルではなく、プッシュしか
 手持ちがないので、スイッチを押すたびに右回り、左
 回りを指定できるようにしました。

typedef unsigned char  UBYTE ;
typedef unsigned short UWORD ;

typedef union {
  struct {
    unsigned B7:1;
    unsigned B6:1;
    unsigned B5:1;
    unsigned B4:1;
    unsigned B3:1;
    unsigned B2:1;
    unsigned B1:1;
    unsigned B0:1;
  } BIT ;
  unsigned char DR ;
} FLAGSP ;

volatile FLAGSP xflags ;

#define EFLAG xflags.BIT.B0
#define TFLAG xflags.BIT.B1

#define OFF 0
#define ON  OFF+1

#define CNTBEGIN 6
#define CNTMAX   128

#define MASK03 0x03

volatile UWORD timcnt ;
volatile UBYTE tmp ;
volatile UBYTE idx ;
volatile UBYTE pat[8] ;
volatile UBYTE rflag ;
volatile UBYTE state ;
volatile UBYTE sft ;

/* function prototype */
void  init_usr(void);

/* interrupt handler */
void interrupt(void)
{
  /* generate trigger */
  if ( INTCON.T0IF == ON ) {
    /* clear flag */
    INTCON.T0IF = OFF ;
    /* initialize */
    TMR0 = CNTBEGIN ;
    /* increment */
    timcnt++ ;
    /* shift register trigger */
    if ( timcnt & ON ) { TFLAG = ON ; }
    /* 7.83Hz trigger */
    if ( timcnt == CNTMAX ) {
      /* clear */
      timcnt = 0 ;
      /* set flag */
      EFLAG = ON ;
    }
  }
}

void main(void)
{
  /* initialize */
  init_usr() ;
  /* endless loop */
  while ( ON ) {
    /* direction */
    if ( TFLAG == ON ) {
      /* clear flag */
      TFLAG = OFF ;
      /* shift */
      sft <<= 1 ;
      /* mask */
      sft &= MASK03 ;
      /* set */
      if ( PORTA.B5 == OFF ) { sft |= ON ; }
      /* judge */
      if ( sft == ON ) {
        /* increment */
        rflag++ ;
        /* convert flag */
        rflag &= ON ;
      }
    }
    /* 7.83Hz handling */
    if ( EFLAG == ON ) {
      /* clear flag */
      EFLAG = OFF ;
      /* calculate pointer */
      idx = (rflag << 2) | state ;
      /* get LED pattern */
      tmp = *(pat+idx) ;
      /* impress */
      LATA = tmp & 0x07 ;
      /* update */
      state++ ;
      /* judge */
      if ( state == 3 ) { state = 0 ; }
    }
  }
}

/* define function body */
void init_usr(void)
{
  /* select 4MHz */
  OSCCON = (0x0d << 3) | MASK03 ;
  /* disable A/D converter */
  ADCON0.ADON = OFF ;
  ADCON2      = 0 ;
  /* disable D/A converter */
  DACCON0.DACEN = OFF ;
  /* disable compare module */
  CM1CON0.C1ON = OFF ;
  CM1CON0.C1OE = OFF ;
  /* I/O state */
  LATA = 0 ;
  /* I/O directions */
  TRISA = 0xf8 ; /* bit0,1,2 as output , others as input */
  /* pull-up */
  WPUA = 0x30 ;
  /* initialize Timer 0 */
  {
    /*
       4MHz/4 = 1MHz -> 1MHz/2 = 250kHz prescaler = 1:4
    */
    OPTION_REG = 0x01 ;
    /* 256 - 6 = 250 */
    TMR0 = CNTBEGIN ;
    /* enable timer0 overflow interrupt */
    INTCON.TMR0IE = ON ;
  }
  /* enable general interrupt */
  INTCON.GIE = ON ;
  /* clear flags */
  xflags.DR = 0 ;
  rflag = OFF ;
  /* initialize variables */
  timcnt = 0 ;
  state = 0 ;
  sft = 0 ;
  /* LED pattern */
  *(pat+0) = 0x01 ;
  *(pat+1) = 0x02 ;
  *(pat+2) = 0x04 ;
  *(pat+3) = 0x00 ;
  *(pat+4) = 0x01 ;
  *(pat+5) = 0x04 ;
  *(pat+6) = 0x02 ;
  *(pat+7) = 0x00 ;
}

 方向をrflagを利用して確定しています。

 フラグrflagを利用し、カウンタが0→1→2→0と
 変化するか4→5→6→4となるかを変えます。

 LEDの点灯パターンを配列に入れておき
 パターンが変化するようにしました。
 点灯パターンとカウンタ値の関係は、以下。

 0 001
 1 010
 2 100
 3 000
 4 001
 5 100
 6 010
 7 000

 方向を指定するrflagは、シフトレジスタを
 利用し、スイッチの論理値が1→0に変化
 するエッジと捉えて変更してます。

 シフトレジスタを使うため、タイマー割込みで
 100msの周期を生成しています。

 シフトレジスタを使うのは、チャタリングを除去
 するため。
 変数とフラグで3バイト程度必要となります。

 配列やフラグを利用しても、8バイト程度の
 増量なので、メモリの少ないPICでも問題に
 なりません。

 8ピンのPIC12F509用にアセンブリ言語コードを書いてみました。

    list      p=12F509     ; list directive to define processor
    #include <p12F509.inc> ; processor specific variable definitions

;+++++++++++++++++++++++++++++++++++++
; configrate
;   system clock intenal 4MHz
;   enable MCLR pin
;   disable Watch Dog Timer
;+++++++++++++++++++++++++++++++++++++

    __CONFIG   _MCLRE_ON & _CP_OFF & _WDT_OFF & _IntRC_OSC

;***** fixed values
SWBIT    EQU    5    ; switch bit
CNTMAX   EQU    0x70 ; = 128
CNTBEGIN EQU    0x06 ; = 256 - 6 = 250

;***** memory address
R0      EQU     0x07 ; 0x07
R1      EQU     R0+1 ; 0x08
R2      EQU     R0+2 ; 0x09
XSFT    EQU     R0+3 ; 0x0A
TIMCNT  EQU     R0+4 ; 0x0B
EFLAG   EQU     R0+5 ; 0x0C
TFLAG   EQU     R0+6 ; 0x0D
DIRS    EQU     R0+7 ; 0x0E
STATE   EQU     R0+8 ; 0x0F
PAT     EQU     0x10

;*******************************************************
    ORG     0x000
    MOVWF   OSCCAL
    goto    START

START:
    call       init_usr

    ; endless loop
main:
    ; check timer0 overflow
    call       tcheck
    ; check shift
    btfss      TFLAG,0
    goto       main0
    ; clear flag
    clrf       TFLAG
    ; shift
    rlf        XSFT,F
    movlw      3
    andwf      XSFT,F
    ; update LSB
    btfss      GPIO,SWBIT
    goto       main0
    bsf        XSFT,0
    ; check
    btfss      XSFT,0
    goto       main0
    ; update
    incf       DIRS,F
    ; reduce flag (multibit -> 1 bit)
    movlw      1
    andwf      DIRS,F
    ; calculate offset
    movf       DIRS,W
    movwf      R2
    rlf        R2,F    ; <<= 1
    rlf        R2,F    ; <<= 1
    movlw      4       ;
    andwf      R2,F

    ; check LED handling
main0:
    btfss      EFLAG,0
    goto       mainEnd

    ; LED handling
main1:
    ; clear flag
    clrf       EFLAG
    ; calculate pointer
    movlw      PAT
    addwf      R2,W
    addwf      STATE,W
    ; set poiner
    movwf      FSR
    ; get pattern
    movf       INDF,W
    ; impress
    movwf      GPIO
    ; update STATE
    incf       STATE,F
    ; range check
    movlw      3
    xorwf      STATE,W
    btfss      STATUS,Z
    goto       mainEnd
    ; clear state
    clrf       STATE

mainEnd:
    goto       main

;******************************************
; initialize
;******************************************
init_usr:
    ; set GPIO values
    clrw
    movwf      GPIO
    ; set GPIO direction 0,1,2 bits as output
    movlw      0x38
    tris       6
    ; initialize timer0
    movlw      0xc1
    option
    movlw      CNTBEGIN
    movwf      TMR0
    ; clear variables
    clrw
    movwf      R0
    movwf      R1
    movwf      R2
    movwf      TIMCNT
    movwf      EFLAG
    movwf      TFLAG 
    movwf      DIRS
    movwf      STATE 
	; set data
    movlw      PAT ; set pointer
    movwf      FSR 
    ; *(PAT+0) = 1
    movlw      1
    movwf      INDF
    incf       FSR,F
    ; *(PAT+1) = 2
    movlw      2
    movwf      INDF
    incf       FSR,F
    ; *(PAT+2) = 4
    movlw      4
    movwf      INDF
    incf       FSR,F
    ; *(PAT+3) = 0
    clrw
    movwf      INDF
    incf       FSR,F
    ; *(PAT+4) = 1
    movlw      1
    movwf      INDF
    incf       FSR,F
    ; *(PAT+5) = 4
    movlw      4
    movwf      INDF
    incf       FSR,F
    ; *(PAT+6) = 2
    movlw      2
    movwf      INDF
    incf       FSR,F
    ; *(PAT+7) = 0
    clrw
    movwf      INDF
    ;
    retlw      0

;******************************************
; check timer overflow
;******************************************
tcheck:
    ; set pointer
    movlw      STATUS
    movwf      FSR
    ; judge timer0 overflow
    btfsc      INDF,4
    goto       tcheckEnd
    ; initialize TMR0
    movlw      CNTBEGIN
    movwf      TMR0
    ; increment
    incf       TIMCNT,F
    ; judge 
    btfss      TIMCNT,0
    goto       tcheck0
    ; set shift handling event flag
    bsf        TFLAG,0
    ; judge
tcheck0:
    movf       TIMCNT,W
    XORLW      CNTMAX
    btfss      STATUS,Z
    goto       tcheckEnd
    ; clear
    movlw      0
    movwf      TIMCNT
    ; set flag
    bsf        EFLAG,0
    ; exit
tcheckEnd:
    retlw      0

    END

 PIC12F509では、タイマー割込みがないので、タイマーが
 オーバーフローしたか否かを常に監視し、割込みと等価
 な機構を用意して対応しました。


目次

inserted by FC2 system