8ピンマイコンPIC12F1822の全てのI/O ピンを使ったマイコンガジェット。
3つのLEDを光らせ、圧電ブザから音階(っぽく聞こえるもの)を鳴らせる。タクトスイッチと光センサで、接触・非接触インタフェースも使える。
PICマイコン PIC12F1822-I/P 1個
LED φ5mm VF1.9から2.9V 程度 3個
カーボン抵抗器 150Ω(茶緑茶金)3個
カーボン抵抗器100kΩ(茶黒黄金)1個
フォトトランジスタ NJL7502L 1個
タクトスイッチ2PIN 1個
積層セラミックコンデンサ 0.1uF (104) 1個
ブレッドボード EIC-801 1個
圧電サウンダ (13mm) Murata PKM13EPYH4
ジャンパ線(φ0.65mm単線)いくらか
/* * File: pwmled-adc-pwmbuz-12f1822.c * Author: KAIMU * * Created on 2019/11/03 *//* * Input * RA4/AN3 (PIN2) : photo transistor, bright:0, dark:1 * RA3 (PIN4) : Tact switch , ON:0, OFF:1 , weak pull-up * Output * CCP1 (RA2) [PIN5] : Piezo Speaker * RA0 [PIN7] : LED0 , active High * RA1 [PIN6] : LED1 , active High * RA5 [PIN2] : LED2 , active High */#include <xc.h>#include <stdint.h>////// ここから、プログラミングのためのデータや関数を定義する /////#pragma config FOSC = INTOSC // 内部クロック使用。#pragma config WDTE = OFF // ウォッチドッグタイマなし。#pragma config PWRTE = ON // 64ms後にプログラム開始。#pragma config MCLRE = OFF // 外部リセットなし。RA3は入力ピン。#pragma config CP = OFF // コード保護なし。#pragma config CPD = OFF // データ保護なし。#pragma config BOREN = ON // 電源電圧低下時のリセットあり。#pragma config CLKOUTEN = OFF // クロック出力不可。#pragma config IESO = OFF // 内部外部クロックなし。#pragma config FCMEN = OFF // FCMなし。// 動作設定その2// Configuration 2#pragma config WRT = OFF // 書き込み保護なし。#pragma config PLLEN = OFF // PLLなし。#pragma config STVREN = ON // スタックオーバー/アンダーフローでリセット。#pragma config BORV = LO // 電源電圧低下監視閾値はLowモード#pragma config LVP = OFF // 低電圧プログラムなし。// I/Oポートの定義 (LEDを接続するポートが変わった時はここを変更する)#define PORT_LED_0 RA0#define TRISA_LED_0 TRISA0#define PORT_LED_1 RA1#define TRISA_LED_1 TRISA1#define PORT_LED_2 RA5#define TRISA_LED_2 TRISA5// Tact Switch のI/Oポートの設定定義#define PORT_TACT_SW RA3#define TRISA_TACT_SW TRISA3#define WPU_TACT_SW WPUA3// A/D変換用I/Oポートの設定定義#define PORT_ADC ANSA4#define TRISA_ADC TRISA4#define PORT_NO_ADC 3#define led0on() (PORT_LED_0 = 1)#define led0off() (PORT_LED_0 = 0)#define led0() (PORT_LED_0 ^= 1)#define led1on() (PORT_LED_1 = 1)#define led1off() (PORT_LED_1 = 0)#define led1() (PORT_LED_1 ^= 1)#define led2on() (PORT_LED_2 = 1)#define led2off() (PORT_LED_2 = 0)#define led2() (PORT_LED_2 ^= 1)//#define isswon() ( PORT_TACT_SW == 0 )#define isswon() ( RA3 == 0 )void wait(volatile uint32_t t)// Delay{ while (t-- > 0) NOP();}void initIo(void) { //OSCCON = 0b01111010; // PLL disable, 16MHz internal clock // OSCCON = 0b01100010; // PLL disable, 8MHz internal clock OSCCON = 0b01101010; // PLL disable, 4MHz internal clock //OSCCON = 0b01011010; // PLL disable, 1MHz internal clock //OSCCON = 0b00111010; // PLL disable, 500kHz internal clock //INTEDG = 0; // Interrupt on falling edge of RB0/INT pin //INTE = 1; // External Interrupt Enable //GIE = 1; //Interrupt Enable ANSELA = 0b00000000; //ALL digital port CM1CON0 = 0x00; //Comparator disable TRISA = 0b00001000; //I/O Direction. RA0-2,4-5 : output, RA3 : Input //OPTION_REGbits.nWPUEN = 1; //// weak pull-up disable //WPUA = 0b00000000; // weak pull-up disable OPTION_REGbits.nWPUEN = 0; //// weak pull-up enable WPUA = 0b00001000; // weak pull-up 3 enable LATA = 0b00000000; // PORTA Latch Settings}// A/D変換関数void initAdc(void) { PORT_ADC = 1; // PORT_ADCで定義されたポートをアナログポートにする。 TRISA_ADC = 1; // A/D変換端子を入力モードにする。 ADCON0 = 0b00000001 | (PORT_NO_ADC << 2); //AD変換を有効化。 ADCON1 = 0b01000000; // 0:(ADFM=0)精度10bit, 100:Fosc/4 ,0:Reserved, 00:AVDD=VREF CM1CON0 = 0x00; //コンパレータを無効化。}uint16_t getAdc(void)// A/D変換した値を得る。// 戻り値: A/D 変換されたアナログ信号の大きさ(0?1023){ uint16_t adcvalue; //ADC wait(10); GO_nDONE = 1; while (GO_nDONE); adcvalue = ADRESH << 2 | ADRESL >> 6; return adcvalue;}int8_t isdark(void)// 光センサの出力から、「暗い」かどうか判断する。// 戻り値; 「暗い」と判断したとき -1// それ以外 0{ uint16_t photo; // 光センサの出力値 int8_t result; // 戻り値 uint16_t darkness_threshold = 900; //「暗い」と判断する閾値 photo = getAdc(); // 光センサの出力をA/D変換 if (photo > darkness_threshold) { //「暗い」とき result = -1; } else { // 「明るい」とき result = 0; } return result;}// PWM (CCP1) 制御関数 (ブザー用PWM)void initPwm(void) { //Timer2 settings for PWM standard mode PR2 = 0; // PWM Period = [(PR2) +1 ]*4*(1/FOSC)*(Timer2 Prescale value) T2CON = 0b00000010; //00 = Prescaler is 1, 01 = Prescaler is 4, // 10 = Prescaler is 16, 11 = Prescaler is 64 APFCON &= ~(1 << CCP1SEL); //CCP1 function is on RA2 //CCP1SEL = 1;//CCP1 function is on RA5 CCP1CON = 0b00001100; //PWM mode: P1A, P1C active-high; P1B, P1D active-high CCPR1L = 64; // PWM Duty Cycle (CCPR1L:CCP1CON<5:4>)*(1/FOSC)*(TMR2 Prescale Value) //Start PWM TMR2 = 0; TMR2ON = 1;}void stopPwm(void) { CCP1ASE = 1; TMR2ON = 0;}void startPwm(void) { CCP1ASE = 0; TMR2ON = 1;}void pwmPulseWidth(uint8_t value) { CCPR1L = value;}void pwmPulseFreq(uint8_t value) { PR2 = value;}// LED用PWM関連volatile int32_t PWMCounter = 0, PWMPeriod = 0, PWMCounterOn2Off = 0; // PWM Control Parametersvolatile int32_t dpwm = 1; // LEDの明るさをPWM制御するためのカウンタの増分void pwmledoff(void) { PWMCounter = 0, PWMPeriod = 0, PWMCounterOn2Off = 0; // PWMの設定(消灯) TMR1ON = 0; // Timer1 Interrupt Disable TMR1IE = 0; // Timer1 Interrupt Disable led0off(); //LED0 off}void pwmledon(int32_t period) { PWMCounter = 0; PWMPeriod = period; PWMCounterOn2Off = 1; // PWMの設定 TMR1ON = 1; // Timer1 Interrupt Enable TMR1IE = 1; // Timer1 Interrupt Enable}void setpwmledperiod(int32_t period) { PWMPeriod = period; // PWM周期の設定}void initTimer1(void) { // Initialize TIMER1 to control PWM-LED Flash TMR1CS1 = 0; TMR1CS0 = 0; // Internal clock (Fosc/4) // T1CKPS1 = 1; T1CKPS0 = 1;// prescaler 1:8 T1CKPS1 = 0; T1CKPS0 = 0; // prescaler 1:1 TMR1H = 0xff; TMR1L = 0x00; // Clear Timer1 counter TMR1ON = 1; // Enable Timer1 TMR1IE = 1; // Timer1 Intterupt Enable PEIE = 1; GIE = 1;}void intTimer1(void) { // Timer1割込みのとき実行される関数 // LED0をじわっと点灯 TMR1H = 0xff; TMR1L = 0x00; // Clear Timer1 counter if (PWMPeriod > 0) { if (PWMCounter < PWMCounterOn2Off) { led0on(); } else { led0off(); } PWMCounter++; if (PWMCounter >= PWMPeriod) { // PWMの1周期が完了したら、 PWMCounter = 0; PWMCounterOn2Off += dpwm; if (PWMCounterOn2Off >= PWMPeriod) { dpwm = -1; // 徐々に暗くするPWMに変更 } else if (PWMCounterOn2Off <= 0) { dpwm = 1; // 徐々に明るくするPWMに変更 } } } else { led0off(); }}// Timer0 制御関数volatile uint32_t TimerCounter; // Counter of Timer Interrupt#define T0UNIT 15UL // TimerCounter at 1 sec.void initTimer0(void)//Initialize Timer 0// count up every 256*256us = 65536us = 65.536ms (clock 4/4=1MHz, prescaler 1:256// if you want t[s] timer, TimerCount <= t / 0.065536{ TMR0IF = 0; // interrupt flag clear TimerCounter = 0; // Timer Interrupt Counter clear OPTION_REG &= 0b11000000; // Masking Timer0 bits OPTION_REG |= 0b00010111; // Timer0 Internal clock, Prescaler 1:256 // Timer0 Interrupt occured every 65.536ms TMR0IE = 1; // enable Timer0 interrupt GIE = 1; // enable interrupt}void waittimeout0(uint32_t timeLimit) { uint32_t timeLimitCount = timeLimit * T0UNIT; // 時間切れまでのカウント数 TimerCounter = 0; // タイマのリセット。計時開始。 timeLimitCount = T0UNIT * timeLimit; while (TimerCounter < timeLimitCount) { //制限時間になるまでの処理 }}void starttimer0() { TimerCounter = 0; // タイマのリセット。計時開始。}int8_t istimeout0(uint32_t timeLimit)//指定時間に達していなければ、0xffを返し、指定時間に達していたら0x0を返す。{ uint8_t retvalue; if (TimerCounter < T0UNIT * timeLimit) { retvalue = 0xff; } else { retvalue = 0x00; } return retvalue;}//割込処理void initExtInt(void) { // 外部割込み初期化 IOCAN3 = 1; //Interrupt-On-Change port-A Negative edge (RA3) IOCIE = 1; //Interrupt-On-Change Interrupt Enable GIE = 1; //Global Interrput Enable}void stopextint(void) { IOCAN3 = 0; //Interrupt-On-Change port-A Negative edge (RA3) disable}void startextint(void) { IOCAN3 = 1; //Interrupt-On-Change port-A Negative edge (RA3) enable}void buz(void) { pwmPulseFreq(0x50); pwmPulseWidth(0x20); startPwm(); wait(2000); stopPwm();}void startbuz(uint8_t freq) { pwmPulseFreq(freq); pwmPulseWidth(0x20); startPwm();}void stopbuz(void) { stopPwm();}void initialize(void) { initIo(); // I/O port初期化 initPwm(); // PWM (CCP1) 初期化 initTimer0(); // Timer0 初期化 initTimer1(); // Timer1 初期化 pwmledoff(); //LEDじわっと点灯の停止 while (isswon()); //タクトスイッチがOFFするまで待つ initExtInt(); // 外部割込み初期化 initAdc(); // A-D convertor 初期化 stopPwm(); // PWM (CCP1) 無効化}////// ここまで、プログラミングのためのデータや関数を定義する /////// 割込み処理関数void intRA3(void) { // RA3変化による外部割込み発生時に処理される buz(); // ブザを鳴らす}void __interrupt() intr(void)// Interrupt processing function{ if (INTF == 1) { INTF = 0; } if (TMR0IF == 1) { TMR0IF = 0; //Clear Timer0 Interrupt flag TimerCounter++; } if (TMR1IF) { TMR1IF = 0; // Clear Timer1 Interrupt Flag intTimer1(); // Timer1割込みの処理関数を実行 } if (IOCAF3 == 1) { // cleae external interrupt flag IOCAF3 = 0; if (isswon()) { // タクトスイッチがON(LOW)なら割込み処理 // IOCAN3 = 1(立下がりエッジ検出)のとき、立上りエッジを検出してしまうための対策 intRA3(); // RA3変化割込みの処理関数を実行 } }}// 割込み処理関数ここまで////// この後のmain(void) { から }までの間に、プログラムが書ける。 /////void main(void) { initialize(); // マイコン初期化 pwmledoff(); // LED PWM点灯を停止 led0off(); led1off(); led2off(); //LED0,1,2を消す startbuz(189); //E(ミ)の音 wait(5000); startbuz(212); //D(レ)の音 wait(5000); startbuz(232); //C(ド)の音 wait(5000); stopbuz(); //音を止める while (1) { //無限ループ。次の}までの間を無限回繰り返す。 if (isdark()) { // 暗いとき pwmledon(50); //LED0をじわっと光らせる starttimer0(); while (istimeout0(10)) { //10秒間実行 if (!isdark()) break; //明るくなったら抜ける } pwmledoff(); //LED0じわっと点灯終了 while (isdark()); // 暗い間待つ } else { // 明るいとき if (isswon()) { pwmledoff(); //LED0のじわっと点灯を停止 //スイッチONになったら led0on(); led1on(); led2on(); //LED0,1,2点灯 starttimer0(); // タイマ0計時開始 waittimeout0(5); //5秒待つ led0on(); led1on(); led2off(); // LED0,1点灯, LED2消灯 buz(); //ブザ鳴らす starttimer0(); // タイマ0計時開始 while (istimeout0(5)) { //5秒待つ if (isdark()) break; //暗くなったらおしまい } led0on(); led1off(); led2off(); // LED0点灯, LED1,D2消灯 buz(); //ブザ鳴らす starttimer0(); // タイマ0計時開始 while (istimeout0(5)) { //5秒待つ if (isdark()) break; //暗くなったらおしまい } led0off(); led1off(); led2off(); // LED0,1,D2消灯 for (int8_t i = 0; i < 5; i++) { //5回繰り返す startbuz(50); //終了音 wait(20000); stopbuz(); wait(10000); } } } }}////// この上の } より上にプログラムが書ける。 /////