目次
前
次
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では、タイマー割込みがないので、タイマーが
オーバーフローしたか否かを常に監視し、割込みと等価
な機構を用意して対応しました。
目次
前
次