・マイコンプログラミングの入門といえば、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 0xff
volatile uint8_t STAGE = StStart; // Program STAGE definition
ISR(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
// Timer0
void 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 0x08
int 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 Makefile
PROJECT=7seg-tiny861-timer
SOURCES=$(PROJECT).c
CC=avr-gcc
OBJCOPY=avr-objcopy
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
(動作)
可変抵抗を回すと、その角度に応じて表示される数値が変化する。この数値がルーレットの数値変化の速度が速くなる。
タクトスイッチを押すと表示される数字が高速で変化する。
さらにタクトスイッチを押すと、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 0xff
volatile uint8_t STAGE = ST1; // Program STAGE definition
ISR(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
// Timer0
void 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 0x08
int 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 Makefile
PROJECT=7seg-tiny861-roulette
SOURCES=$(PROJECT).c
CC=avr-gcc
OBJCOPY=avr-objcopy
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