ブザーを叩くとLEDが点滅を始め、設定した時間が経過すると、メロディが流れる。
メロディは、MML(Music Macro Language)風の書き方 KMML ( Kaimu Music Macro Language )で入力できる。
マイコン PIC12F1822-I/P
発光ダイオードLED Φ5mm
カーボン抵抗器 150Ω 、10kΩ
圧電スピーカー(圧電サウンダ)(13mm)PKM13EPYH4
ミニブレッドボードBBー601
電池ボックス(単3型×2本用、バッテリスナップタイプ)
バッテリスナップ
開発環境:MPLAB XIDE, Cコンパイラ:XC8 Compiler
マイコンプログラマ: PICkit3
ブザーを叩くと、LEDが点滅を始める。
設定した時間(TIME_LIMIT)が経過すると、メロディが流れる。
メロディはKMML ( Kaimu Music Macro Language ) の文字列で指定できる。
コマンド
O* オクターブの指定(次に指定するまで保持) *:0,1,...
# 半音上がる(一音のみ)
b 半音下がる(一音のみ)
CDEFGAB ... 音階の指定
C* ... 指定した音階を*の長さだけ鳴らす *:1,2,4,8,@ (全、二分、四分、八分、16分)
R* ... 休符 *は1,2,4,8,@ (全、二分、四分、八分、16分)
/* * File: mml-timer-shock-12f1822.c * Author: kaimu * * Created on February 17, 2018, 8:50 AM *//* * Input * AN2 [PIN5] : Analog Input * Output * RA5 [PIN2] : LED (1...OFF, 0...ON) * CCP1 (RA2) [PIN5] : piezo buzzer */#include <xc.h>#include <stdint.h>// タイマーのアラーム・メロディ//static const char mml_melody[] = "O1R2C4D4E2D4C4R@C4D4E4D4C4D2R@"; // チャルメラ//static const char mml_melody[] = "O1D8C8 O0F4O1F4F4D8C8 O0F4O1F4F4D8C8 O0F4O1F4O0D4O1F4 O0C4O1E4E4D8C8 \ O0C4O1E4E4D8C8 O0C4O1E4E4D8C8 O0C4O1E4O0D4O1E4 O0F4O1F4F4R4"; //猫ふんじゃった//static const char mml_melody[] = "O0D8G8 G2B8A8G8 B2B8A8 G2E4 D2D8G8 \ G2B8A8G8 B2A8B8O1D1 D2O0B8O1D8 \ D2O0B8A8G8 B2B8A8 G2E4 D2D8G8 \ G2B8A8G8 B2A4 G2G2R4"; // アメイジング・グレイス//static const char mml_melody[] = "O0F8G8A8bB8 A8G8F8R8 A8bB8O1C8D8 C8O0bB8A8R8 \ F8R8F8R8 F8R8F8R8 F@F@G@G@A@A@bB@bB@ A8G8F8R8"; // カエルの合唱static const char mml_melody[] = "O1C4E4D4O0G2 O1C4D4E4C2 \ E4C4D4 O0G2 G4O1D4E4C4 R8"; // ウエストミンスターの鐘 Westminster Quarters// タイマーの動作音static const char mml_boot[] = "O0C8D8E8R8"; // 起動時static const char mml_timer_start[] = "R8O0A4O1A4R@"; // タイマーの開始時static const char mml_every_5sec[] = "O0A@R@"; // 5秒ごと// タイマーの設定時間[s]#define TIME_LIMIT 180 // LEDが点灯している時間(タイマの秒数)#define MELODY_COUNT 2 // 時間経過後に鳴らすメロディの繰り返し回数// LED点滅設定#define LED_BLINK_PERIOD_MIN 50 // LEDが点滅する周期の最小値(20-100)// I/Oポートの定義 (LEDを接続するポートが変わった時はここを変更する)#define PORT_LED_1 RA5#define TRISA_LED_1 TRISA5#define PORT_ADC ANSA2#define PORT_BUZ RA2#define TRISA_BUZ TRISA2#define TRISA_ADC TRISA2#define PORT_NO_ADC 0x02// PIC micro controller Configuration 1#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 // 低電圧プログラムなし。#define ledOn() (PORT_LED_1 = 0)#define ledOff() (PORT_LED_1 = 1)#define led() (PORT_LED_1 ^= 1)void setBuzOutputMode(void) { TRISA_BUZ = 0; // Buzzer Port OUTPUT mode PORT_ADC = 0; // ADC port as Digital port }void setBuzInputMode(void) { TRISA_BUZ = 1; // Buzzer Port INPUT mode PORT_ADC = 1; // ADC port as Analog port }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 = 0b00000001; //RA0 Analog port, others digital port CM1CON0 = 0x00; //Comparator disable TRISA = 0b00001001; //I/O Direction. RA1-2,4-5 : output, RA0,3 : Input LATA = 0b00000000; // PORTA Latch Settings}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;}// A/D変換関数// A/D conversion functionsvoid initAdc(void) { PORT_ADC = 1; // PORT_ADCで定義されたポートをアナログポートにする。Analog port (see PORT_ADC def.) setting TRISA_ADC = 1; // A/D変換端子を入力モードにする。 ADCON0 = 0b00000001 | (PORT_NO_ADC << 2); //AD変換を有効化。0:unimp, xxxxx:analog ch, 0:GOnDone, 1:ADC enable ADCON1 = 0b01000000; //0:(ADFM=0)Left justified (10bit res.), 100:Fosc/4 ,0:Reserved, 00:AVDD=VREF CM1CON0 = 0x00; //コンパレータを無効化。Comparator disable}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;}#define SHOCK_THRESHOLD 60 // 衝撃の閾値 (0 ~ 1023)#define SHOCKED 1#define NO_SHOCK 0uint8_t isShock(void)// Shock sensor detection// return value: SHOCKED ... shocked// NO_SHOCK ... no{ uint16_t adcvalue; uint8_t result; setBuzInputMode(); adcvalue = getAdc(); if (adcvalue > SHOCK_THRESHOLD) { result = SHOCKED; } else { result = NO_SHOCK; } return result;}void waitUntilBuz(void)//ブザーの振動がおさまるまで待つ{ uint8_t silentCounter; // ブザーが振動していない回数 for (silentCounter = 0; silentCounter < 5; silentCounter++) { if (isShock()) { silentCounter--; //ブザが振動していたら、振動していない回数を減ずる } }}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 = 0b11010111; // Timer0 Internal clock, Prescaler 1:256 // Timer0 Interrupt occured every 65.536ms TMR0IE = 1; // enable Timer0 interrupt GIE = 1; // enable interrupt}void initTimer1(void) { // Initialize TIMER1 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;}volatile int32_t PWMCounter = 0, PWMPeriod = 0, PWMCounterOn2Off = 0; // PWM Control Parametersvolatile int32_t dpwm = 1; // LEDの明るさをPWM制御するためのカウンタの増分void interrupt intr(void)// Interrupt processing function{ if (INTF == 1) { INTF = 0; } if (TMR0IF == 1) { TMR0IF = 0; //Clear TMR0 Interrupt flag TimerCounter++; } if (TMR1IF) { TMR1IF = 0; // Clear Timer0 Interrupt Flag TMR1H = 0xff; TMR1L = 0x00; // Clear Timer1 counter if (PWMPeriod > 0) { if (PWMCounter < PWMCounterOn2Off) { ledOn(); } else { ledOff(); } PWMCounter++; if (PWMCounter >= PWMPeriod) { // PWMの1周期が完了したら、 PWMCounter = 0; PWMCounterOn2Off += dpwm; if (PWMCounterOn2Off >= PWMPeriod) { dpwm = -1; // 徐々に暗くするPWMに変更 } else if (PWMCounterOn2Off <= 0) { dpwm = 1; // 徐々に明るくするPWMに変更 } } } else { ledOff(); } }}void pwmLedOff(void) { //PWMCounter = 0, PWMPeriod = 0, PWMCounterOn2Off=0; // PWMの設定(消灯) TMR1ON = 0; // Timer1 Interrupt Disable}void pwmLedOn(int32_t period) { PWMCounter = 0; PWMPeriod = period; PWMCounterOn2Off = 1; // PWMの設定 TMR1ON = 1; // Timer1 Interrupt Enable}void pwmLedSetPeriod(int32_t period) { PWMPeriod = period; // PWM周期の設定}void playFreq(uint8_t octIndex, uint8_t modIndex, uint8_t pitchIndex, uint8_t length)// 引数で指定されたオクターブ、調、音階、長さの音をPWMで出力する。// arguments modIndex :調:0,1, octIndex :オクターブ:0,1 , pitchIndex :音高:0~6,// length : 音の長さ#define N_OCT 2 // オクターブの種類#define N_MOD 3 // 調の種別#define N_PITCH 7 // 音階の種類{ // 周波数に対応したPWMのパラメータ // PITCH order -> ABCDEFG static const uint8_t freq[N_OCT][N_MOD][N_PITCH] = { 141, 126, 238, 212, 189, 178, 158, // ABCDEFG (oct. 4) 133, 126, 224, 200, 189, 168, 149, //#ABCDEFG (oct. 4) 149, 133, 238, 224, 200, 178, 168, //bABCDEFG (oct. 4) 70, 62, 118, 105, 94, 88, 79, // ABCDEFG (oct. 5) 112, 99, 94, 83, 74, 66, 62, //#ABCDEFG (oct.5)) 74, 66, 62, 112, 99, 94, 83 //bABCDEFG (oct.5) }; static const uint8_t maxLength = 16; // 音の最大の長さ setBuzOutputMode(); if (octIndex < N_OCT && modIndex < N_MOD && pitchIndex < N_PITCH) { PR2 = freq[octIndex][modIndex][pitchIndex]; wait((uint32_t) (maxLength / length) * 3000); // 音の長さだけ待つ } PR2 = 0; // Stop Sound}void playWait(uint8_t length) { const uint8_t maxLength = 16; // 音の最大の長さ PR2 = 0; wait((uint32_t) (maxLength / length) * 3000); // 音の長さだけ待つ}uint8_t getMmlLength(char mmlchar) { uint8_t length; if (mmlchar == '@') { length = 16; } else { length = (uint8_t) mmlchar - '0'; } return length;}void playMmlString(char *melodyString) { // play Music Macro Language // example "O1C4D8E@F2R8G4#D4R8" // C4 ... pitch name C , length 1/4 // pitch name : C, D, E, F, G, A, B // octave : O0, O1 // mode : #, b // rest : R // length 1, 2, 4, 8, @ (1/1 , 1/2, 1/4, 1/8, @ is 1/16) // MML文字列を再生 uint8_t index = 0; static volatile uint8_t octIndex = 0, modIndex = 0, pitchIndex = 0, length; while (*(melodyString + index) != '\0') { switch (*(melodyString + index)) { case 'O': index++; octIndex = *(melodyString + index) - '0'; break; case '#': modIndex = 1; index++; pitchIndex = *(melodyString + index) - 'A'; index++; length = getMmlLength(*(melodyString + index)); playFreq(octIndex, modIndex, pitchIndex, length); modIndex = 0; break; case 'b': modIndex = 2; index++; pitchIndex = *(melodyString + index) - 'A'; index++; length = getMmlLength(*(melodyString + index)); playFreq(octIndex, modIndex, pitchIndex, length); modIndex = 0; break; case 'R': index++; length = getMmlLength(*(melodyString + index)); playWait(length); break; case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': case 'G': modIndex = 0; pitchIndex = *(melodyString + index) - 'A'; index++; length = getMmlLength(*(melodyString + index)); playFreq(octIndex, modIndex, pitchIndex, length); default: playWait(0); } index++; }}void main(void) { int32_t pwmLedBlinkPeriod; // LEDのPWM制御の速度 uint32_t timeLimitCount; // 時間切れまでのカウント数 uint32_t i; //Initialization initIo(); initPwm(); initAdc(); initTimer0(); initTimer1(); ledOff(); pwmLedOff(); // PWMによるLED点滅を停止 playMmlString(mml_boot); waitUntilBuz(); // ブザーの振動がおさまるまで待つ while (1) { if (isShock()) { waitUntilBuz(); // ブザーの振動がおさまるまで待つ playMmlString(mml_timer_start); // 衝撃を検出したときの音 waitUntilBuz(); // ブザーの振動がおさまるまで待つ pwmLedBlinkPeriod = TIME_LIMIT; // 点滅間隔の初期化 pwmLedOn(pwmLedBlinkPeriod); // Start PWM (LED点灯) TimerCounter = 0; // タイマのリセット。計時開始。 timeLimitCount = T0UNIT * TIME_LIMIT; while (TimerCounter < timeLimitCount) { //制限時間になるまでの処理 if (TimerCounter % (T0UNIT * 5UL) == 0) { for (i = 0; i < (timeLimitCount - TimerCounter) / (T0UNIT * 60UL) + 1UL; i++) { // 残り分数+1の回数だけ音を5秒ごとにならす playMmlString(mml_every_5sec); } led(); pwmLedBlinkPeriod -= 5; // LED点滅速度を徐々に速くする if (pwmLedBlinkPeriod <= LED_BLINK_PERIOD_MIN) { pwmLedBlinkPeriod = LED_BLINK_PERIOD_MIN; } // LED点滅速度が速すぎるときは最速に設定 pwmLedSetPeriod(pwmLedBlinkPeriod); // LED点滅速度の設定 } } for (i = 0; i < MELODY_COUNT; i++) { playMmlString(mml_melody); //警報音を鳴らす。 } waitUntilBuz(); // ブザーの振動がおさまるまで待つ pwmLedOff(); // PWMの設定(消灯) ledOff(); } }}