/*============================================================================= ; PIC12F1822 Elekey Touch Paddle DATE:2017/2/26 Ver:00.1 ; 2017/3/18 UPdate ; FILE:12F1822_Touch_Paddle_V00.1.c ; MPLAB X IDE v2.15 XC8 v1.32 ; ; タッチセンサにタッチしたときの値が大きい時と小さい時にタッチ有と判定する ; 現在の設定は 判定10ms しきい値 80% & 120% ; ; ---U--- ; VDD(+) |1 8| VSS(-) ; DOT_OUT /(RA5) |2 7| (RA0)/LED ; DASH_OUT /(RA4) |3 6| (RA1)/CPS1(DASH) ; SW-IN (RA3) |4 5| (RA2)/CPS2(DOT) ; ------- ; ; OSC : Internal OSC 8MHz ; ;=============================================================================*/ #include // ----------- CONFIG -------------------------------------------------------- #pragma config FOSC = INTOSC // INTOSC oscillator #pragma config WDTE = OFF // Watchdog Timer #pragma config PWRTE = OFF // Power-up Timer #pragma config MCLRE = OFF // MCLR Pin is digital input #pragma config CP = OFF // Flash Program Memory Code Protection #pragma config CPD = OFF // Data Memory Code Protection #pragma config BOREN = OFF // Brown-out Reset #pragma config CLKOUTEN = OFF // CLKOUT function #pragma config IESO = OFF // Internal/External Switchover mode #pragma config FCMEN = OFF // Fail-Safe Clock Monitor // CONFIG2 #pragma config WRT = OFF // Flash Memory Write protection #pragma config PLLEN = OFF // 4x PLL #pragma config STVREN = 0FF // Stack Overflow or Underflow will cause a Reset #pragma config BORV = LO // Brown-out Reset Voltage (Vbor), #pragma config LVP = OFF // High-voltage on MCLR/VPP // ----------- 変数定義 -------------------------------------------------------- #define _XTAL_FREQ 8000000 // Clock8MHz Delay_us() 定義用 unsigned int Cnt_def1 , Cnt_def2 ; // CPSカウント初期値を格納 unsigned int Cnt_now1 , Cnt_now2 ; // CPSカウントタッチした値を格納 unsigned char Slice_D , Slice_U ; // スライスレベルを格納 unsigned char Slice_DD = 80 ; // スライスレベルデフォルト値 80% unsigned char Slice_UD = 120 ; // スライスレベルデフォルト値 120% unsigned char PADLE_Mode = 0 ; // パドルモード設定 初期値0:エレキー unsigned char LED_Timer = 0 ; // モニタLED点滅用カウンタ unsigned int Cnt_sleep ; // SLEEP用カウンタWORK unsigned char w ; // WORK REG // ----------- 関数のプロトタイプ宣言 ------------------------------------------- void CPS_count() ; // 一定時間CPSのカウントをおこなう void MODE_Set() ; // モード変更・しきい値設定をおこなう unsigned char SW_Status() ; // SWの状態確認 //----------------------------------------------------------------------------- // MAIN //----------------------------------------------------------------------------- void main(void){ // ----------- レジスタ設定 ------------------------------------------------ OSCCON = 0b01110010 ; // INTOSC:8MHz ANSELA = 0b00000110 ; // RA1.RA2:CSM-INPUT TRISA = 0b00001110 ; // RA1.RA2:CPS RA3:SW-IN CPSCON0 = 0b00001100 ; // CPSRNG bit3-2:11 OSC High 18uA CPSCON1 = 0b00000001 ; // 初期値 CPS1 RA1をタッチセンサ入力とする T1CON = 0b11000001 ; // MR1CS:11 容量検知オシレータでタイマー1をカウント // TMR1ON:1 Timer Enable WPUA = 0b00001000 ; // RA3 Pull up OPTION_REG = 0b00000000 ; // WPU Enable Bit<7> //APFCON,RXDTSEL = 1 ; // RX-->RA5 // *指定をしてもTRISAでRA5を出力指定すれば // 出力が優先される //APFCON,TXCKSEL = 0 ; // TX-->RA0 // APFCONでRX・TXポートを変更する場合は[ANSEL]の指定不要で // [TRISA]は B'11111111'すべて入力でも動作する //TXSTA = 0b00100100 ; // TXEN=1 BRGH=1 //RCSTA = 0b10010000 ; // SPEN=1 CREN=1 // *SPEN=1にしないと送信だけでのときも送信できない //SPBRG = 51 ; // [BAUD RATES] BRGH=1 BRG16=0 8MHz 9600BPS PORTA = 0 ; // PORTA CLEAR PIR1,TMR1IF = 0 ; // TMR1オーバーフローBITクリア Cnt_sleep = 0 ; // SLEEPカウンタリセット IOCAN = 0b00001000 ; // RA3のSWを押すと立下りで割り込み発生 w = PORTA ; INTCON,IOCIF = 0 ; // IOC_INT FLAG CLEAR INTCON,IOCIE = 1 ; // IOC INT Enable //INTCON,GIE = 1 ; // INT Enable // ----------- ウォーミングアップ 必要ないと思うけど一応 --------------------- __delay_ms(500) ; // 0.5秒Wait CPSCON1 = 0b00000010 ; // CPS2 RA2のタッチセンサ入力セレクト TMR1H = 0 ; // タイマー初期化 TMR1L = 0 ; CPSCON0,CPSON = 1 ; // 容量検知モジュールを500ms間動作 __delay_ms(500) ; CPSCON0,CPSON = 0 ; // 容量検知モジュール停止 // 18uAの場合Timer1がオーバーフローしたらLEDを50msだけ点灯する if( PIR1,TMR1IF == 1){ RA0 = 1 ; } __delay_ms(50) ; RA0 = 0 ; PIR1,TMR1IF = 0 ; // TMR1オーバーフローBITクリアして500ms待つ __delay_ms(500) ; // ----------- 初期値設定 -------------------------------------------------- CPSCON1 = 0b00000001 ; // CPS1(RA1) 設定 CPS_count() ; Cnt_def1 = ((TMR1H*256) + TMR1L) ; __delay_ms(10) ; CPSCON1 = 0b00000010 ; // CPS2(RA2) 設定 CPS_count() ; Cnt_def2 = ((TMR1H*256) + TMR1L) ; __delay_ms(10) ; // ----------- 動作モード表示(モニタ)LED点灯 最初は点灯 ---------------------- RA0 = 1 ; // ----------- タッチの判定 ------------------------------------------------- Slice_D = Slice_DD ; // --- スライスレベル初期値設定 --- Slice_U = Slice_UD ; while(1) { // PADLE_Mode 0:エレキーモード 1:電鍵モード // ---- 電鍵モード時の処理 ------------------------------------------ if( PADLE_Mode == 1 ) { // -- 電鍵モード時はLEDを0.5秒間隔で点滅 LED_Timer++ ; if( LED_Timer >= 45 ){ // 1LOOP 11ms X 45 ≒ 500ms LED_Timer = 0 ; RA0^ = 1 ; } } else { RA0 = 1 ; // -- パドルモードの時はLEDは点灯 } // ---- CPS1のタッチセンサ判定 (DASH) ----------------------------------- CPSCON1 = 0b00000001 ; CPS_count() ; Cnt_now1 = (TMR1H*256)+TMR1L ; // ---- 初期値より現在の値が大きい時と小さい時にタッチ有 ------------- if ( Cnt_now1 < (Cnt_def1 / 100) * Slice_D ){ // 値が小さい時 if( PADLE_Mode == 1 ){ // 電鍵モードは出力RA5 RA5 = 1 ; Cnt_sleep = 0 ; } else{ RA4 = 1 ; Cnt_sleep = 0 ; } } else{ if ( Cnt_now1 > (Cnt_def1 / 100) * Slice_U ){ // 値が大きい時 if( PADLE_Mode == 1 ){ // 電鍵モードは出力RA5 RA5 = 1 ; Cnt_sleep = 0 ; } else{ RA4 = 1 ; Cnt_sleep = 0 ; } } else { // スライスレベル以下 if( PADLE_Mode == 1 ){ // 電鍵モードは出力RA5 RA5 = 0 ; Cnt_sleep++ ; } else{ RA4 = 0 ; Cnt_sleep++ ; } } } __delay_ms(1) ; // --- CPS2のタッチセンサ判定 (DOT) ------------------------------------- if( PADLE_Mode == 1 ) { // --- 電鍵モードはSkipする --- goto CPS2_Skip ; } CPSCON1 = 0b00000010 ; CPS_count() ; Cnt_now2 = (TMR1H*256)+TMR1L ; // ---- 初期値より現在の値が大きい時と小さい時にタッチ有 ----------- if ( Cnt_now2 < (Cnt_def2 / 100) * Slice_D ){ // 値が小さい時 RA5 = 1 ; Cnt_sleep = 0 ; } else{ if ( Cnt_now2 > (Cnt_def2 / 100) * Slice_U ){ // 値が大きい時 RA5 = 1 ; Cnt_sleep = 0 ; } else { RA5 = 0 ; Cnt_sleep++ ; } } __delay_ms(1) ; CPS2_Skip : ; // 電鍵モードはCPS2のタッチセンサ判定をSKIPする // ---- 5分間タッチがなければスリープする ---------------------------------------- if ( Cnt_sleep >= 27000 ){ // 計算値 5M x 60S / 11ms = 27272   __delay_ms(1000) ; Cnt_sleep = 0 ; INTCON,GIE = 0 ; // INT Desable CPSCON0 = 0b00000000 ; // CPS STOP T1CON = 0b00000000 ; // TMR1 STOP //TXSTA = 0b00000000 ; // TX RX Disavle //RCSTA = 0b00000000 ; // Disavleにしておかないと少しiが増える PORTA = 0b00000000 ; // デジタル出力LOWにしてSLEEPする ANSELA = 0b00000000 ; TRISA = 0b00001000 ; // RA3はINT用 WPUA = 0b00001000 ; // RA3 Pull up //OPTION_REG = 0b00000000 ; // WPU Enable Bit<7> IOCAN = 0b00001000 ; // RA3のSWを押すと立下りで割り込み発生 w = PORTA ; // PORTAを読む INTCON,IOCIF = 0 ; // INT FLAG CLEAR INTCON,IOCIE = 1 ; // INT Enable IOCAF = 0 ; // SWが押されると1になりクリアしておかないと // 2回目以降SLEEPしない *要注意* SLEEP() ; // ちょっとスリープするだね NOP() ; // ----- REGを元に戻しSLEEPカウンタリセット ------- ANSELA = 0b00000110 ; TRISA = 0b00001110 ; CPSCON0 = 0b00001100 ; CPSCON1 = 0b00000001 ; T1CON = 0b11000001 ; __delay_ms(500) ; IOCAF = 0 ; Cnt_sleep = 0 ; RA0 = 1 ; // オペレートモード点灯 } // ---- SWが押されたらモード・しきい値変更処理をおこなう-------------- if(SW_Status() == 0){ MODE_Set() ; } } // while(1)タッチセンサ検知のループの終わり } //------------------------------------------------------------------------------ // ここからサブルーチン //------------------------------------------------------------------------------ // ----------- CPS_count SET --------------------------------------------------- void CPS_count(){ // 10ms間容量検知モジュールのカウントを数える TMR1H = 0 ; // タイマー初期化 TMR1L = 0 ; CPSCON0,CPSON = 1 ; // 容量検知モジュール開始 __delay_ms(10) ; // 検出時間 = 10ms CPSCON0,CPSON = 0 ; } // ----------- スイッチの状態チェック ------------------------------------------- unsigned char SW_Status() { if( RA3 == 0 ){ // SWは50ms間隔で2回チェック __delay_ms(50) ; if( RA3 == 0 ){ return 0 ; // return 0 SW-ON } // return 1 SW-OFF } return 1 ; } // ----------- モード・しきい値変更処理をおこなう -------------------------------- void MODE_Set(){ unsigned char i , j , k ; // LOOP用カウンタWORK unsigned char Timer = 100 ; // 50msX60=3000ms 60+猶予40=100 unsigned char Timer1 ; // Timer1用カウンタワーク unsigned char SW_Cnt = 0 ; unsigned char SW_ON_Time = 0 ; // ---- まずモードLEDを消灯 ------------------------------------------------- RA0 = 0 ; // ---- SWの押されている時間を計測 ------------------------------------------ while(SW_Status() == 0){ SW_ON_Time++ ; // 1回 50ms 50ms X 40回 = 2秒 if( SW_ON_Time >= 40 ){ // 2秒以上押されたらLED点灯させる RA0 = 1 ; } if( SW_ON_Time >= 80 ){ // 4秒以上押されたらLED消灯させる RA0 = 0 ; } } // SWがOFFになった // ※いつまでも押し続けると「SW_ON_Time」はオーバフローする // 50msX256=12800ms オーバフローの処理はしていないがカウンタは // また0からカウントを始めるので特に影響はない // ---- 4秒以上の長押しだったらCPSの初期値を再設定する -------------------- if( SW_ON_Time >= 80 ){ // 50ms X 80回 // ----------- 初期値再設定 ------------------------------------- CPSCON1 = 0b00000001 ; // CPS1(RA1) 設定 CPS_count() ; Cnt_def1 = ((TMR1H*256) + TMR1L) ; __delay_ms(10) ; CPSCON1 = 0b00000010 ; // CPS2(RA2) 設定 CPS_count() ; Cnt_def2 = ((TMR1H*256) + TMR1L) ; __delay_ms(10) ; return ; // サブルーチンをぬける } // ---- 2秒以上の長押しだったら「エレキー」 ←→ 「電鍵」のモード変更 ---- if( SW_ON_Time >= 40 ){ // 2秒以上押されたら 50ms X 40 PADLE_Mode^ = 1 ; // モード変更ビットを反転 return ; // サブルーチンをぬける } // ---- SWが押されている時間が2秒以下ならLEDを消灯して0.5秒待つ --------------- RA0 = 0 ; __delay_ms(500) ; // ---- 3秒の間に2回のSWの押下があったらスライスレベル変更 -------------------- while(Timer >= 40){ // --- ここから3秒のカウント --- if(SW_Status() == 0){ // SW入力があったら Timer-- ; SW_Cnt++ ; while(SW_Status() == 0){ // SW OFFを50msごとに確認する __delay_ms(50) ; Timer-- ; } __delay_ms(100) ; // 次の入力まで100msあける Timer = Timer - 2 ; } __delay_ms(50) ; // SW入力ない時は50ms待ってから Timer-- ; // ---- 入力回数2回をしらべる --------------------------------------- if(SW_Cnt == 2){ // 2回入力あり Timer = 0 ; // --- 0.2秒間隔で2回点滅して合図する ---------------------------- for ( i=0 ; i<2 ; i++ ){ RA0 = 1 ; __delay_ms(200) ; RA0 = 0 ; __delay_ms(200) ; } __delay_ms(1500) ; // 1.5秒待つ // --- ここから3秒間隔でスライスレベルに合わせたLEDの点滅を5回 -------- j = 0 ; for ( i=0 ; i<5 ; i++ ){ // for ( k=j ; k<5 ; k++ ){ // 点滅回数5回→1回まで計5回 RA0 = 1 ; __delay_ms(150) ; RA0 = 0 ; __delay_ms(150) ; } j++ ; // --- 点滅の後SW入力があったら点滅の回数に合わせたスライスを設定 ---- Timer1 = 100 ; // 3秒タイマ (100-40) X 50ms while(Timer1 >= 40){ if(SW_Status() == 0){ // SW入力あったら while(SW_Status() == 0){ // SW OFFを確認する __delay_ms(50) ; Timer1-- ; } switch(j){ // 何回目の点滅で入力か case 5: // 点滅1回 Slice_D = 60 ; Slice_U = 140 ; break ; case 4: // 点滅2回 Slice_D = 70 ; Slice_U = 130 ; break ; case 3: // 点滅3回 Slice_D = 80 ; // デフォルト値 Slice_U = 120 ; break ; case 2: // 点滅4回 Slice_D = 85 ; Slice_U = 115 ; break ; case 1: // 点滅5回 Slice_D = 90 ; Slice_U = 110 ; break ; } //break は ここにぬける goto END_Mode_SET ; //return ; } // if(SW_Status()==0)の終わり __delay_ms(50) ; Timer1-- ; } // While(Timer1>=40)の終わり } // 最初のfor ここへぬける時はしきいち変更がなかった(SW入力なし) } // 入力回数2回 if の終わり } // 3秒間のSW入力whileの終わり RA0 = 1 ; // 2秒間SW入力がなかったので何もしないで戻る // タイマークリアしないとループから抜けない END_Mode_SET : ; RA0 = 1 ; }