・マイコンプログラミングの入門といえば、LEDピカピカ点滅だ。LED1個を自在に点滅させられるようになったら、次は7セグメントLEDを自在に光らせたい。配線も少々複雑になり、プログラムも関数を多用した高度なものになっていく。なにより、「数字」という人間が慣れ親しんだ表現ができるのが素晴らしい。
・20ピンの小さなAVRマイコンATtiny861を使って、7セグメントLEDを搭載したプラットフォームを作ってみた。ADコンバータのプログラムも試せるように、可変抵抗器と圧電サウンダもある。特に、圧電サウンダは音を鳴らす出力デバイスとしてだけでなく、振動を検出する入力デバイスとしても使えるので、おもしろい応用ができそうな予感がする。
・使用したAVRマイコンATtiny861は、プログラムメモリが8kバイト、RAMが512バイトある。OSを載せるのは無理かもしれないが、かなり複雑なファームウェアが書ける容量だ。デバッガが必要になるかどうかの分岐点に位置するものかもしれない。
・実行形式Hexファイルや回路図(Eagle形式)は、添付のZIPファイルを参照のこと。
マイコン ATMEL ATtiny861
7セグメントLED SL-1199(アノードコモン)
圧電サウンダ(他励振) Murata PKM13EPYH4000-A0
カーボン抵抗器 330Ω x 7個、10kΩ × 2個
可変抵抗器(半固定抵抗器) 50kΩ
積層セラミックコンデンサ 0.1uF x 2個
ピンヘッダ 2x3
電池ボックス 単三型2本用または(LEDが明るい方がお好みなら)3本用
タクトスイッチ
ユニバーサル基板 70mm x 45mm
スペーサ等
(動作)
可変抵抗を回すと、その角度に応じて7セグメントLEDに数値が表示される。
タクトスイッチを押すと、7セグメントLEDの数値が点滅する。
設定した時間が経過すると、7セグメントLEDの数値が短い周期で点滅するとともに、ブザが鳴る。
LEDの数値が点滅している間にタクトスイッチを押すと、スリープモードに移行する。
スリープモードにおいてタクトスイッチを押すと、最初の状態に戻る。
(ソースコード)7seg-tiny861-timer.c
// PA0 : 7seg-LED a 0:ON 1:OFF// PA1 : 7seg-LED b 0:ON 1:OFF// PA2 : 7seg-LED c 0:ON 1:OFF// PA3 : 7seg-LED d 0:ON 1:OFF// PA4 : 7seg-LED e 0:ON 1:OFF// PA5 : 7seg-LED f 0:ON 1:OFF// PA6 : 7seg-LED g 0:ON 1:OFF// -- a --// | |// f b// | |// -- g --// | |// e c// | |// -- d --// PB6: Switch 1:OFF 0:ON// PB4: Piezo Buz// PB5(ADC8) : Resister#include <avr/io.h>#include <avr/interrupt.h>#include <avr/sleep.h>void wait(volatile long i){while (i-- > 0);
}#define isSwOff() ( PINB & (1<<PB6) )#define buzOn() ( PORTB &= ~(1<<PB4) )#define buzOff() ( PORTB |= (1<<PB4) )#define ledA() PINA |= (1<<PA0)#define ledB() PINA |= (1<<PA1)#define ledC() PINA |= (1<<PA2)#define ledD() PINA |= (1<<PA3)#define ledE() PINA |= (1<<PA4)#define ledF() PINA |= (1<<PA5)#define ledG() PINA |= (1<<PA6)#define ledAoff() PORTA |= (1<<PA0)#define ledBoff() PORTA |= (1<<PA1)#define ledCoff() PORTA |= (1<<PA2)#define ledDoff() PORTA |= (1<<PA3)#define ledEoff() PORTA |= (1<<PA4)#define ledFoff() PORTA |= (1<<PA5)#define ledGoff() PORTA |= (1<<PA6)#define ledAon() PORTA &= ~(1<<PA0)#define ledBon() PORTA &= ~(1<<PA1)#define ledCon() PORTA &= ~(1<<PA2)#define ledDon() PORTA &= ~(1<<PA3)#define ledEon() PORTA &= ~(1<<PA4)#define ledFon() PORTA &= ~(1<<PA5)#define ledGon() PORTA &= ~(1<<PA6)void led7seg(uint8_t bitvalue)// bitvalue : 0b 0abcdefg 0:OFF, 1:ON{ if (bitvalue & 0b01000000) ledAon(); else ledAoff(); if (bitvalue & 0b00100000) ledBon(); else ledBoff(); if (bitvalue & 0b00010000) ledCon(); else ledCoff(); if (bitvalue & 0b00001000) ledDon(); else ledDoff(); if (bitvalue & 0b00000100) ledEon(); else ledEoff(); if (bitvalue & 0b00000010) ledFon(); else ledFoff(); if (bitvalue & 0b00000001) ledGon(); else ledGoff();}void ledNum(uint8_t value){ switch (value) { case 0: led7seg(0b1111110); //abcdef break; case 1: led7seg(0b0110000); //bc break; case 2: led7seg(0b1101101); //abdeg break; case 3: led7seg(0b1111001); //abcdg break; case 4: led7seg(0b0110011); //bcfg break; case 5: led7seg(0b1011011); //acdfg break; case 6: led7seg(0b1011111); //acdefg break; case 7: led7seg(0b1110000); //abc break; case 8: led7seg(0b1111111); //abcdefg break; case 9: led7seg(0b1110011); //abcfg break; case 10: //0xa led7seg(0b1111101); //abcdeg break; case 11: //0xb led7seg(0b0011111); //cdefg break; case 12: //0x0c led7seg(0b0001101); //deg break; case 13: //0x0d led7seg(0b0111101); //bcdeg break; case 14: //0x0e led7seg(0b1101111); //abdefg break; case 15: //0x0f led7seg(0b1000111); //aefg break; default: led7seg(0b0000000); }}void beep(int length, int pitch){ volatile unsigned int i, j; for ( i = 0; i < length; i++) { buzOn(); for (j = 0; j < pitch;j++); buzOff(); for (j = 0; j < pitch;j++); }}void initAdc(void){ // AD Conv. Enable & Clock ferquency and the input clock to the ADC) ADCSRA = ( (1<<ADEN) | (1<<ADPS2) | (1<<ADPS1) | (0<<ADPS0) );}uint8_t adc(uint8_t adcport){ uint8_t adcdata; // A/D Conv. data ADMUX = (1<<ADLAR) | adcport; // AD-Conv. 8-bit precision, Use ADC-PORTx ADCSRA |= (1<<ADIF); // Set ADC Interrupt flag ADCSRA |= (1<<ADSC); // Start AD Conv. while ( ( ADCSRA & (1<<ADIF) ) == 0); //Wait to complete adc adcdata = ADCH; // 8-bit precision return adcdata;}void initInt0(void){ GIMSK |= (1<<INT0); // INT0 interrupt enable MCUCR |= (1<<ISC01) | (0<<ISC00); // The falling edge ofINT0 or INT1}#define StStart 0x01#define StCount 0x02#define StStop 0x03#define STSleep 0xffvolatile uint8_t STAGE = StStart; // Program STAGE definitionISR(INT0_vect){ switch(STAGE) { case StStart: STAGE = StCount; break; case StCount: STAGE = StStop; break; case StStop: STAGE = STSleep; break; case STSleep: STAGE = StStart; break; default: STAGE = StStart; }}#define TC10min 36621L#define TC9min 32958L#define TC8min 29297L#define TC7min 25634L#define TC6min 21972L#define TC5min 18310L#define TC4min 14648L#define TC3min 10986L#define TC2min 7324L#define TC1min 3662L#define TC10sec 610L#define TC3sec 183L// Timer0void initTimer0(void){ TCCR0B |= (1<<CS01) | (1<<CS00); TCCR0B &= ~(1<<CS02);//Clock/64 TIMSK |= (1<<TOIE0); //Timer0 Overflow Interrupt Enable}volatile long TickCounter;ISR(TIMER0_OVF_vect){ TickCounter--;}#define ADCPortNo 0x08int main(void){ uint8_t adcdata; long period; // Initialize I/O Port DDRA = (1<<PA6) | (1<<PA5) | (1<<PA4) | (1<<PA3) | (1<<PA2) | (1<<PA1) | (1<<PA0); PORTB = (1<<PB6); DDRB = (1<<PB4) ; // Initailize Interrupt initInt0(); // Initialize AD-convertor initAdc(); //Initialize Timer0 initTimer0(); // LED initialize PORTA = 0b11111111; sei(); // Enable Interrupt STAGE = StStart; while (1) { switch (STAGE) { case StStart: adcdata = adc(ADCPortNo); period = adcdata>>4; ledNum(period); TickCounter = TC1min * period; break; case StCount: if (TickCounter > TC9min) { ledNum(10); } else if (TickCounter > TC8min) { ledNum(9); } else if (TickCounter > TC7min) { ledNum(8); } else if (TickCounter > TC6min) { ledNum(7); } else if (TickCounter > TC5min) { ledNum(6); } else if (TickCounter > TC4min) { ledNum(5); } else if (TickCounter > TC3min) { ledNum(4); } else if (TickCounter > TC2min) { ledNum(3); } else if (TickCounter > TC1min) { ledNum(2); } else if (TickCounter > 0) { ledNum(1); } else { STAGE = StStop; ledNum(0xff); } wait(5000); ledNum(-1); wait(5000); break; case StStop: ledNum(period); beep(500, 5); wait(5000); led7seg(0b0000000); wait(2500); break; case STSleep: led7seg(0b0000000); set_sleep_mode(SLEEP_MODE_IDLE); sleep_mode(); break; default: ledNum(0xe); } } return 0;}(Makefile)
# AVR-GCC MakefilePROJECT=7seg-tiny861-timerSOURCES=$(PROJECT).cCC=avr-gccOBJCOPY=avr-objcopyMMCU=attiny861TARGETDEV=t861CFLAGS=-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).hexsudo avrdude -p $(TARGETDEV) -P usb -c avrispmkII -e -U flash:w:$(PROJECT).hex
clean:rm -f $(PROJECT).out
rm -f $(PROJECT).hex
(動作)
可変抵抗を回すと、その角度に応じて表示される数値が変化する。この数値がルーレットの数値変化の速度が速くなる。
タクトスイッチを押すと表示される数字が高速で変化する。
さらにタクトスイッチを押すと、0から9までの数値のうちいずれかの値が点滅する。
さらにタクトスイッチを押すと、スリープモードに移行する。
スリープモード中にタクトスイッチを押すと、最初の状態に戻る。
(ソースコード AVR-GCC用) 7seg-tiny861-roulette.c
// PA0 : 7seg-LED a 0:ON 1:OFF// PA1 : 7seg-LED b 0:ON 1:OFF// PA2 : 7seg-LED c 0:ON 1:OFF// PA3 : 7seg-LED d 0:ON 1:OFF// PA4 : 7seg-LED e 0:ON 1:OFF// PA5 : 7seg-LED f 0:ON 1:OFF// PA6 : 7seg-LED g 0:ON 1:OFF// -- a --// | |// f b// | |// -- g --// | |// e c// | |// -- d --// PB6: Switch 1:OFF 0:ON// PB4: Piezo Buz// PB5(ADC8) : Resister#include <avr/io.h>#include <avr/interrupt.h>#include <avr/sleep.h>void wait(volatile long i){while (i-- > 0);
}#define isSwOff() ( PINB & (1<<PB6) )#define buzOn() ( PORTB &= ~(1<<PB4) )#define buzOff() ( PORTB |= (1<<PB4) )#define ledA() PINA |= (1<<PA0)#define ledB() PINA |= (1<<PA1)#define ledC() PINA |= (1<<PA2)#define ledD() PINA |= (1<<PA3)#define ledE() PINA |= (1<<PA4)#define ledF() PINA |= (1<<PA5)#define ledG() PINA |= (1<<PA6)#define ledAoff() PORTA |= (1<<PA0)#define ledBoff() PORTA |= (1<<PA1)#define ledCoff() PORTA |= (1<<PA2)#define ledDoff() PORTA |= (1<<PA3)#define ledEoff() PORTA |= (1<<PA4)#define ledFoff() PORTA |= (1<<PA5)#define ledGoff() PORTA |= (1<<PA6)#define ledAon() PORTA &= ~(1<<PA0)#define ledBon() PORTA &= ~(1<<PA1)#define ledCon() PORTA &= ~(1<<PA2)#define ledDon() PORTA &= ~(1<<PA3)#define ledEon() PORTA &= ~(1<<PA4)#define ledFon() PORTA &= ~(1<<PA5)#define ledGon() PORTA &= ~(1<<PA6)void led7seg(uint8_t bitvalue)// bitvalue : 0b 0abcdefg 0:OFF, 1:ON{ if (bitvalue & 0b01000000) ledAon(); else ledAoff(); if (bitvalue & 0b00100000) ledBon(); else ledBoff(); if (bitvalue & 0b00010000) ledCon(); else ledCoff(); if (bitvalue & 0b00001000) ledDon(); else ledDoff(); if (bitvalue & 0b00000100) ledEon(); else ledEoff(); if (bitvalue & 0b00000010) ledFon(); else ledFoff(); if (bitvalue & 0b00000001) ledGon(); else ledGoff();}void ledNum(uint8_t value){ switch (value) { case 0: led7seg(0b1111110); //abcdef break; case 1: led7seg(0b0110000); //bc break; case 2: led7seg(0b1101101); //abdeg break; case 3: led7seg(0b1111001); //abcdg break; case 4: led7seg(0b0110011); //bcfg break; case 5: led7seg(0b1011011); //acdfg break; case 6: led7seg(0b1011111); //acdefg break; case 7: led7seg(0b1110000); //abc break; case 8: led7seg(0b1111111); //abcdefg break; case 9: led7seg(0b1110011); //abcfg break; case 10: //0xa led7seg(0b1111101); //abcdeg break; case 11: //0xb led7seg(0b0011111); //cdefg break; case 12: //0x0c led7seg(0b0001101); //deg break; case 13: //0x0d led7seg(0b0111101); //bcdeg break; case 14: //0x0e led7seg(0b1101111); //abdefg break; case 15: //0x0f led7seg(0b1000111); //aefg break; default: led7seg(0b0000000); }}void beep(int length, int pitch){ volatile unsigned int i, j; for ( i = 0; i < length; i++) { buzOn(); for (j = 0; j < pitch;j++); buzOff(); for (j = 0; j < pitch;j++); }}void initAdc(void){ // AD Conv. Enable & Clock ferquency and the input clock to the ADC) ADCSRA = ( (1<<ADEN) | (1<<ADPS2) | (1<<ADPS1) | (0<<ADPS0) );}uint8_t adc(uint8_t adcport){ uint8_t adcdata; // A/D Conv. data ADMUX = (1<<ADLAR) | adcport; // AD-Conv. 8-bit precision, Use ADC-PORTx ADCSRA |= (1<<ADIF); // Set ADC Interrupt flag ADCSRA |= (1<<ADSC); // Start AD Conv. while ( ( ADCSRA & (1<<ADIF) ) == 0); //Wait to complete adc adcdata = ADCH; // 8-bit precision return adcdata;}void initInt0(void){ GIMSK |= (1<<INT0); // INT0 interrupt enable MCUCR |= (1<<ISC01) | (0<<ISC00); // The falling edge ofINT0 or INT1}#define ST1 0x01#define ST2 0x02#define ST3 0x03#define STSleep 0xffvolatile uint8_t STAGE = ST1; // Program STAGE definitionISR(INT0_vect){ switch(STAGE) { case ST1: STAGE = ST2; break; case ST2: STAGE = ST3; break; case ST3: STAGE = STSleep; break; case STSleep: STAGE = ST1; break; default: STAGE = ST1; }}#define TC10min 36621L#define TC9min 32958L#define TC8min 29297L#define TC7min 25634L#define TC6min 21972L#define TC5min 18310L#define TC4min 14648L#define TC3min 10986L#define TC2min 7324L#define TC1min 3662L#define TC10sec 610L#define TC3sec 183L// Timer0void initTimer0(void){ TCCR0B |= (1<<CS01) | (1<<CS00); TCCR0B &= ~(1<<CS02);//Clock/64 TIMSK |= (1<<TOIE0); //Timer0 Overflow Interrupt Enable}volatile long TICKCOUNTER = 0;ISR(TIMER0_OVF_vect){ TICKCOUNTER++;}#define ADCPortNo 0x08int main(void){ uint8_t adcdata; volatile uint8_t number = 0; long period; long i; // Initialize I/O Port DDRA = (1<<PA6) | (1<<PA5) | (1<<PA4) | (1<<PA3) | (1<<PA2) | (1<<PA1) | (1<<PA0); PORTB = (1<<PB6); DDRB = (1<<PB4) ; // Initailize Interrupt initInt0(); // Initialize AD-convertor initAdc(); //Initialize Timer0 initTimer0(); // LED initialize PORTA = 0b11111111; sei(); // Enable Interrupt STAGE = ST1; while (1) { switch (STAGE) { case ST1: adcdata = adc(ADCPortNo); period = adcdata>>4; ledNum(period); break; case ST2: number++; if (number >= 0x09) { number = 0x0; } ledNum(number); adcdata = adc(ADCPortNo); wait(period<<8); beep(50,5); break; case ST3: ledNum(number); for (i = 0; i < 5000; i++); ledNum(0xff); for (i = 0; i < 5000; i++); break; case STSleep: led7seg(0b0000000); set_sleep_mode(SLEEP_MODE_IDLE); sleep_mode(); break; default: ledNum(0xe); } } return 0;}(Makefile)
# AVR-GCC MakefilePROJECT=7seg-tiny861-rouletteSOURCES=$(PROJECT).cCC=avr-gccOBJCOPY=avr-objcopyMMCU=attiny861TARGETDEV=t861CFLAGS=-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).hexsudo avrdude -p $(TARGETDEV) -P usb -c avrispmkII -e -U flash:w:$(PROJECT).hex
clean:rm -f $(PROJECT).out
rm -f $(PROJECT).hex