マイコンで、好きな時間だけLEDが光ったあと警報音が鳴るタイマを作りましょう。
圧電スピーカをコツンと叩くと、LEDが点滅し、決まった時間だけ点滅すると、警報音が鳴ってLEDが消灯します。
マイコン PIC12F1822P
LED
カーボン抵抗器 150Ω, 10kΩ
圧電スピーカ
電池スナップ
電池ボックス(単3電池2本用)
ブレッドボード
/*
* File: bc17-06-shock-led-timer-12f1822.c
* Author: kaimu
*
* Created on November 4, 2017, 5:31 PM
*/
/*
* LED1 : PIN 2 (RA4) : 1-OFF, 0-ON
* Buzzer : PIN 6 (RA1)
* ANALOG PORT : PIN 6 (AN1)
*
*/
#include <xc.h> // XC8コンパイラ用のヘッダ(レジスタ名などのキーワードの定義を含む)
#include <stdint.h> // 整数型のビット数を明示して宣言するためのヘッダファイル
// 数値を変更すると機能が調整できる。
#define TIME_LIMIT 20 // LEDが点灯している時間(タイマの秒数)
#define BEEP_ALARM_COUNT 2 // 警報音の鳴る回数
// 上級者用
#define LED_BLINK_PERIOD_MIN 50 // LEDが点滅する周期の最小値(20-100)
#define BEEP_START_LENGTH 2000 // 起動音の長さ
#define BEEP_START_HEIGHT 5 // 起動音の高さ
#define BEEP_SHOCK_LENGTH 4000 // ブザーを叩いたときの音の長さ
#define BEEP_SHOCK_HEIGHT 2 // ブザーを叩いたときの音の高さ
#define BEEP_ALARM_LENGTH_1 4000 // 警報音(最初の音)の長さ
#define BEEP_ALARM_HEIGHT_1 8 // 警報音(最初の音)の高さ
#define BEEP_ALARM_LENGTH_2 2000 // 警報音(次の音)の長さ
#define BEEP_ALARM_HEIGHT_2 16 // 警報音(次の音)の高さ
#define SHOCK_THRESHOLD 50 // 衝撃の閾値 (0 ~ 1023)
// 調整用設定終了
// PICマイコンの動作設定その1
// 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 // 低電圧プログラムなし。
// I/Oポートの初期化
void initIo(void)
{
OSCCON = 0b01110010 ; // Internal clock 8MHz
OPTION_REG = 0b00000000 ; // Weak Pull up ON
ANSELA = 0b00000000 ; // ALL Digital PORT
TRISA = 0b00001000 ; // Input : RA3, Output: others
WPUA = 0b00001000 ; // Weak pull-up : RA3
PORTA = 0b00000000 ; // Initialize PORTA
}
// I/Oポートの定義 (LEDを接続するポートが変わった時はここを変更する)
#define PORT_LED RA4
#define TRISA_LED TRISA4
#define PORT_ADC ANSA1
#define PORT_BUZ RA1
#define TRISA_BUZ TRISA1
#define TRISA_ADC TRISA1
#define PORT_NO_ADC 1
// LED制御用関数
inline static void ledOn(void)
{ PORT_LED = 0;
}
inline static void ledOff(void)
{ PORT_LED = 1;
}
inline static void ledBlink(void)
{ PORT_LED ^= 1;
}
// ちょっと待つ処理を簡単に書くための関数
void wait(volatile uint32_t i)
{
while (i-- > 0);
}
// A/D変換関数
// A/D conversion functions
void initAdc(void)
{
PORT_ADC = 1; // PORT_ADCで定義されたポートをアナログポートにする。Analog port (see PORT_ADC def.) setting
TRISA_ADC = 1; // A/D変換端子を入力モードにする。RA1 as input
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;
}
uint16_t getShock(void)
{
uint16_t adcvalue;
TRISA_ADC = 1; // Buzzer Port INPUT mode
PORT_ADC = 1; // ADC port as Analog port
adcvalue = getAdc();
return adcvalue;
}
#define T0UNIT 31 // Timer0Counter about 1sec ( 1 / (1 / ((Clock 8MHz / 4) / prescaler 256) * 256)
void initTimer0(void)
{
TMR0 = 0x00; // Initialize TIMER0
TMR0CS = 0; //Internal instruction cycle clock (FOSC/4)
PSA = 0; //Prescaler is assigned to the Timer0 module
PS2 = 1; PS1=1; PS0 = 1; // Prescaler Rate Select bits set 1:256
TMR0IE = 1; // Timer0 Intterupt Enable
PEIE = 1;
GIE =1;
}
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 uint32_t T0Counter; // Timer0 overflow Counter
volatile int32_t PWMCounter = 0, PWMPeriod = 0, PWMCounterOn2Off = 0; // PWM Control Parameters
volatile int32_t dpwm = 1; // LEDの明るさをPWM制御するためのカウンタの増分
void interrupt isr(void)
// When Interrupt caught ...
{
if (TMR0IF) {
// Timer0 Interruption
TMR0IF = 0; // Clear Timer0 Interrupt Flag
TMR0 = 0; // Clear Timer1 counter
T0Counter++;
}
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 pwmOff(void) {
//PWMCounter = 0, PWMPeriod = 0, PWMCounterOn2Off=0; // PWMの設定(消灯)
TMR1ON = 0; // Timer1 Intterupt Disable
}
void pwmOn(int32_t period)
{
PWMCounter = 0; PWMPeriod = period; PWMCounterOn2Off= 1; // PWMの設定
TMR1ON = 1; // Timer1 Intterupt Enable
}
void pwmSetPeriod(int32_t period)
{
PWMPeriod = period; // PWM周期の設定
}
// ブザー制御用関数
inline static void buzToggle(void)
{ PORT_BUZ ^= 1;
}
void beepBuz(uint32_t length, uint32_t height)
// ブザを鳴らす
// 引数:
// length : 音の長さ
// height : 音の高さ(小さいほど高い音)
{
uint16_t i;
TRISA_BUZ = 0; // Buzzer Port OUTPUT mode
PORT_ADC = 0; // ADC port as Digital port
for (i = 0; i < length; i++) {
buzToggle();
wait(height);
}
}
void beepAlarm(void)
// 決められた時間が経過したときの警報音を鳴らす
{
uint8_t i;
for (i = 0; i < BEEP_ALARM_COUNT; i++) {
beepBuz(BEEP_ALARM_LENGTH_1, BEEP_ALARM_HEIGHT_1);
beepBuz(BEEP_ALARM_LENGTH_2, BEEP_ALARM_HEIGHT_2);
wait(2000);
}
}
void beepShort(uint8_t n)
//引数n回だけブザーを短く鳴らす
{
uint8_t i;
beepBuz(100, 16);
wait(10000);
for (i = 0; i < n - 1; i++) {
beepBuz(100, 4);
wait(10000);
}
}
void waitUntilBuz(void)
//ブザーの振動がおさまるまで待つ
{
uint8_t silentCounter; // ブザーが振動していない回数
uint16_t adc;
for (silentCounter = 0;silentCounter < 10;) {
adc = getShock();
if (adc > SHOCK_THRESHOLD) {
silentCounter++;
}
}
}
void main(void)
// メイン関数。この関数から動作開始
{
uint16_t adc; // A/D変換の値。A/D converted value
int32_t pwmLedBlinkPeriod; // LEDのPWM制御の速度
uint32_t timeLimitCount; // 時間切れまでのカウント数
initIo(); // I/Oポートの初期化
initAdc(); // A/D変換ポートの初期化
initTimer0(); // Timer0の初期化
initTimer1(); // Timer1の初期化
beepBuz(BEEP_START_LENGTH, BEEP_START_HEIGHT); // 起動音を鳴らす
pwmOff(); // PWMによるLED点滅を停止
waitUntilBuz(); // ブザーの振動がおさまるまで待つ
while (1) {
adc = getShock();
if (adc > SHOCK_THRESHOLD) {
beepBuz(BEEP_SHOCK_LENGTH, BEEP_SHOCK_HEIGHT); // 衝撃を検出したときの音
//pwmLedBlinkPeriod = LED_BLINK_PERIOD; // 点滅間隔の初期化
pwmLedBlinkPeriod = TIME_LIMIT; // 点滅間隔の初期化
pwmOn(pwmLedBlinkPeriod); // Start PWM (LED点灯)
T0Counter = 0;// タイマのリセット。計時開始。
timeLimitCount = T0UNIT * TIME_LIMIT;
while (T0Counter < timeLimitCount) {
//制限時間になるまでの処理
if (T0Counter % (T0UNIT * 5) == 0) {
beepShort((timeLimitCount - T0Counter) / (T0UNIT * 60) + 1);// 残り分数+1の回数だけ音を6秒ごとにならす
pwmLedBlinkPeriod -= 5; // LED点滅速度を徐々に速くする
if (pwmLedBlinkPeriod <= LED_BLINK_PERIOD_MIN) { pwmLedBlinkPeriod = LED_BLINK_PERIOD_MIN; } // LED点滅速度が速すぎるときは最速に設定
pwmSetPeriod( pwmLedBlinkPeriod ); // LED点滅速度の設定
}
}
pwmOff();// PWMの設定(消灯)
ledOn(); // LEDを点灯する。
beepAlarm(); //警報音を鳴らす。
ledOff(); // LEDを消灯する。
waitUntilBuz(); // ブザーの振動がおさまるまで待つ
}
}
return;
}