赤外線射的ゲーム(ATtiny26L/861)
4つの的を赤外線光線銃で狙って撃つと、当たった的の数と種類に応じて得点が加算され、スコアLEDが点灯する。
赤外線光線銃の弾は4発。スコアLEDがたくさん点灯した人の勝ちだ。
赤外線は壁や天井、床で反射するため、なかなか狙いがつけにくい。
使用部品
的センサ基板(1枚当たりの数量。同じものを4枚作成する。)
赤外線リモコン受信モジュール PL-IRM0208 (38kHzで変調された信号を受信すると、出力がHigh→Lowに変化)
カーボン抵抗器 470Ω, 1/4W
LED Φ5mm
ユニバーサル基板 サンハヤトICB-90
分割ロングピンソケット 1x2 2個、1x3 1個
ブレッドボード用ジャンパワイヤ
基板固定用スペーサ、ネジ
的制御基板
AVRマイコン ATtiny26L (または ATtiny861 … ソース変更と再コンパイル必要)
ユニバーサル基板 PICOK200 94V-D ブレッドボード配線パターン
圧電サウンダ OKM13EPYH4000-A0 リード線ピッチ5mm, 直径12.6mm
カーボン抵抗器 10kΩ, 1/4W
積層セラミックコンデンサ 0.1uF
タクトスイッチ
スライドスイッチ
LED Φ5mm × 4個
ICソケット 20PIN
集合抵抗 330Ω 4個入
バッテリースナップ
電池ボックス 単3×3本用、バッテリースナップタイプ
分割ロングピンソケット 1x4 2個、2x4 1個
基板固定用スペーサ、ネジ
赤外線発信機
AVRマイコン ATtiny26L (または ATtiny861 … ソース変更と再コンパイル必要)
ユニバーサル基板 PICOK200 94V-D ブレッドボード配線パターン
圧電サウンダ OKM13EPYH4000-A0 リード線ピッチ5mm, 直径12.6mm
半固定抵抗器 1kΩ(つまみ付き)
カーボン抵抗器 10kΩ, 470Ω, 150Ω, いずれも1/4W
積層セラミックコンデンサ 0.1uF × 2個
タクトスイッチ
スライドスイッチ
LED Φ5mm × 5個
赤外線LED Φ5mm
ICソケット 20PIN
集合抵抗 330Ω 4個入
バッテリースナップ
電池ボックス 単3×3本用、バッテリースナップタイプ
分割ロングピンソケット 1x4 2個、2x4 1個
回路図
的制御基板
的用赤外線センサと表示用LED基板
赤外線発信機基板
赤外線信号の形式
発信機は、図のような変調をかけた赤外線信号を発信する。
ファームウェア
蛍光灯などでの誤動作を防ぐため、変調をかけた版を以下に示す。バイナリ等は添付ファイルir-shooting-tiny26(-tiny861).zipを参照。
なお、変調をかけていない単純な構成の版は添付ファイルir-shooting-no-modulation-tiny26(-tiny861).zipにある。
的制御基板用(AVR-GCCソース)
// TARGET : ATTINY26 / (ATTINY861)
//
// Input Port
// PA0,1,2,3 : PIN 20,19,18,17 : IR-remote control reciever : Active low
//
// Output Port
// PA4,5,6,7 : PIN 14,13,12,11 : score LED 1:ON, 0:OFF
// PB0,1,2,3 : PIN 1,2,3,4 : hit Target LED 1:OFF, 0:ON
// PB6 : PIN 9 : Buzzer
#define F_CPU 1000000UL // clock frequency 1MHz
#include <util/delay.h>
#include <avr/io.h>
#define buz() PORTB ^= (1<<PB6)
#define getIrSensor() ( PINA & 0b00001111 )
void wait(volatile long i)
{
while (i-- > 0);
}
void targetLed(uint8_t pattern)
// pattern : 1: LED ON, 0: LED OFF
{
PORTB = (~pattern) & 0b00001111;
}
void targetLedOn(uint8_t portNo) {
PORTB &= ~(1<<portNo);
}
void targetLedOff(uint8_t portNo) {
PORTB |= (1<<portNo);
}
void scoreLed(uint8_t pattern)
{
pattern <<= 4;
PORTA = pattern & 0b11110000 ;
}
uint8_t getIrSignal(uint8_t portNo)
// return value : Sensor #3210 -> 1:ON, 0:OFF
{
return !( PINA & ( 1<<portNo ) );
}
uint8_t getModulatedIrSignalBits(void)
// #senosr #3210
// ON : 1
// OFF : 0
{
uint8_t InBit, RetBit=0x00;
uint8_t BitPattern[3] = {0, 0, 0}; // Signal BIT pattern
volatile int i;
InBit = getIrSensor();
if ( InBit == 0b1111 ) {
RetBit = 0; // no sense
} else {
_delay_us(2000); // wait 2ms
InBit = getIrSensor();
if (InBit == 0b1111) {
RetBit = 0; // no valid signal
} else {
while ( InBit != 0b1111 ) {
InBit = getIrSensor();
} // wait for bit pattern 0b1111 (rising edge)
_delay_ms(1);
BitPattern[0] = getIrSensor();
_delay_ms(2);
BitPattern[1] = getIrSensor();
_delay_ms(2);
BitPattern[2] = getIrSensor();
}
for (i = 0; i < 4; i++) {
if ( ( BitPattern[0] & (1<<i) ) == 0 &&
( BitPattern[1] & (1<<i) ) == 0 &&
( BitPattern[2] & (1<<i) ) != 0 ) {
RetBit |= (1<<i); // detect signal on port #i
} else {
RetBit &= ~(1<<i);
}
}
}
return RetBit & 0b00001111;
}
void beep(int height, int period)
{
int i;
for (i = 0; i < period; i++) {
buz();
wait((int)height);
}
}
void initPort(void)
{
// Initialize I/O Port
PORTA = 0b00000000; // NO PORT Enable Interal Pull ups
DDRA = 0b11110000; // PA7654 Output mode , PA3210 Input Mode
DDRB = 0b01111111; //PB0-6 Output Mode
}
int main(void)
{
uint8_t irSignalBits;
uint8_t score;
uint8_t i;
initPort();
targetLed(0b0000); // All target LEDs are OFF
scoreLed(0b0000); // All score LED are OFF
while (getModulatedIrSignalBits()); // wait no signal
wait(10000); // wait few seconds
beep(5,200); // START !
score = 0;
while (1)
{
// sensor #0,#1,#2 : HIT +1 pts
// sensor #3 : HIT +2 pts
irSignalBits = getModulatedIrSignalBits();
for (i = 0; i < 4 ; i++ ) {
if ( irSignalBits & (1<<i) ) {
targetLedOn(i);
switch (i) {
case 0:
case 1:
case 2:
score ++;
beep(30, 50);
break;
case 3:
score += 2;
beep(2, 300);
break;
}
}
}
if (irSignalBits) {
wait(40000); //wait for a few second.
while ( getIrSensor() != 0b1111 ); // wait until no sense
}
while (irSignalBits) {
// wait until no signal
irSignalBits = getModulatedIrSignalBits();
};
targetLed(0b0000); // All target LEDs OFF
if (score > 15) {
score = 0;
}
scoreLed(score);
}
return 0;
}
的制御基板用(Makefile)
# AVR-GCC Makefile
PROJECT=tiny26irtarget
SOURCES=$(PROJECT).c
CC=avr-gcc
OBJCOPY=avr-objcopy
#for ATtiny26
MMCU=attiny26
TARGETDEV=t26
# for ATtiny861
#MMCU=attiny861
#TARGETDEV=t861
CFLAGS=-mmcu=$(MMCU) -Wall
$(PROJECT).hex: $(PROJECT).out
$(OBJCOPY) -j .text -O ihex $(PROJECT).out $(PROJECT).hex
$(PROJECT).out: $(SOURCES)
$(CC) $(CFLAGS) -I./ -o $(PROJECT).out $(SOURCES)
program: $(PROJECT).hex
sudo avrdude -p $(TARGETDEV) -P usb -c avrispmkII -e -U flash:w:$(PROJECT).hex
clean:
rm -f $(PROJECT).out
rm -f $(PROJECT).hex
赤外線発信機用(AVR-GCCソース)
// Ir-Shooting Game System
// Ir-Transmitter (Modulation mode)
//
// TARGET : ATTINY26 (ATTINY861)
//
// Input Port
// PA0 : PIN 11 : Tact switch pullup register enable 0:ON, 1:OFF
//
// Output Port
// PB1 : PIN 2 : Infarred LED : 1:ON, 0:OFF
// PB3 : PIN 4 : LED (Trigger ON) : 1:ON, 0:OFF
// PA7654 : PIN 14,13,12,11 : LED ( Energy Gage) 1:ON, 0:OFF
// PB6 : PIN 9 : Buzzer
#define F_CPU 1000000UL // clock frequency 1MHz
#include <util/delay.h>
#include <avr/io.h>
#define buz()
PORTB ^= (1<<PB6)
#define led_on() (PORTB |= (1<<PB3))
#define led_off() (PORTB &= ~(1<<PB3))
#define isSwitchOn() (!(PINA & (1<<PA0)))
void wait(volatile long i)
{
while (i-- > 0);
}
void beep(void)
{
int i;
for (i = 0; i < 300; i++) {
buz();
wait(2);
}
}
#define FREQ38KHZ 208 // (CK 1Mhz) value of OCR1A at 38kHz Frequency
void initTimer1CTC(void)
// Initialize Timer/Counter1 CTC (Clear Timer on Compare mode) mode
{
// Freq = 2 * PLL clock (64MHz/8) / N(1 + OCR1C) , N=2(1/2 Prescaling)
PLLCSR |= (1<<PLLE) | (1<<PCKE); //PCK Enable
TCCR1B = (1<<CTC1) | (0<<CS13) | (0<<CS12) | (1<<CS11) | (1<<CS10); // ATtiny26L
// TCCR1B = (0<<CS13) | (0<<CS12) | (1<<CS11) | (1<<CS10); // ATtiny861
OCR1C = FREQ38KHZ;
TIMSK |= (1<<OCIE1A);
}
void connect_OC1A(void)
{
TCCR1A = (0<<COM1A1) | (1<<COM1A0) | (0<<COM1B1) | (0<<COM1B0) | (0<<PWM1A);
}
void disconnect_OC1A(void)
{
TCCR1A = 0x00;
}
void showEnergyGage(uint8_t energy)
{
PORTA &= 0b00001111; // all Gage LED off
switch (energy) {
case 4 :
PORTA |= 0b11110000;
break;
case 3 :
PORTA |= 0b11100000;
break;
case 2 :
PORTA |= 0b11000000;
break;
case 1 :
PORTA |= 0b10000000;
break;
default :
PORTA |= 0;
}
}
void initPort(void)
{
// Initialize I/O Port
PORTA = 0b11110001; // PA0 Enable Interal Pull ups, PA0123 High (Energy Gage LED OFF)
DDRA = 0b11111110; // PA0 Input Mode
DDRB = 0b01111111; //PB6543210 Output Mode
}
void modulateOutput(void)
{
connect_OC1A();
_delay_ms(3);
disconnect_OC1A();
_delay_us(500);
connect_OC1A();
_delay_us(1500);
disconnect_OC1A();
_delay_us(500);
connect_OC1A();
_delay_us(1500);
disconnect_OC1A();
_delay_ms(5);
}
int main(void)
{
uint8_t energy;
int OutputCounter;
initPort();
initTimer1CTC();
disconnect_OC1A();
energy = 4;
showEnergyGage(energy);
while( isSwitchOn() );
while (1)
{
while (energy > 0) {
if ( isSwitchOn() ) {
led_on();
beep();
for (OutputCounter = 0; OutputCounter < 50; OutputCounter++) {
modulateOutput();
}
energy--;
showEnergyGage(energy);
while (isSwitchOn());
} else {
disconnect_OC1A();
led_off();
}
}
disconnect_OC1A();
led_off();
}
return 0;
}
赤外線発信機用(Makefile)
# AVR-GCC Makefile
PROJECT=ir-transmitter-tiny26
SOURCES=$(PROJECT).c
CC=avr-gcc
OBJCOPY=avr-objcopy
#for ATtiny26
MMCU=attiny26
TARGETDEV=t26
# for ATtiny861
#MMCU=attiny861
#TARGETDEV=t861
CFLAGS=-mmcu=$(MMCU) -Wall
$(PROJECT).hex: $(PROJECT).out
$(OBJCOPY) -j .text -O ihex $(PROJECT).out $(PROJECT).hex
$(PROJECT).out: $(SOURCES)
$(CC) $(CFLAGS) -I./ -o $(PROJECT).out $(SOURCES)
program: $(PROJECT).hex
sudo avrdude -p $(TARGETDEV) -P usb -c avrispmkII -e -U flash:w:$(PROJECT).hex
clean:
rm -f $(PROJECT).out
rm -f $(PROJECT).hex