(1) キッチンタイマー
30秒刻みで、31分30秒まで設定可能。
時間は、半固定抵抗で設定し、スイッチ0を押すことで起動する。
(2) 汎用カウンタ
スイッチを押すとカウントアップする手動カウンタ
(3) ストップウォッチ
秒単位で計測し、59分まで計測可能
初期画面から、スイッチ0を押すとキッチンタイマーの時間設定に移行する。
機能は、スイッチ1を押すことで
汎用カウンタ→ストップウォッチ→キッチンタイマー
の順で切りかわる。
AVRマイコン ATmega168P
ICソケット 28PIN, 狭幅タイプ
I2C接続薄型16文字×2行液晶 AQM1602XA-RN-GBW
ピンソケット 4ピン
カーボン皮膜抵抗器 1/4W, 470Ω, 1kΩ, 10kΩ×3個
半固定抵抗器 100kΩ ツマミ付
LED φ5mm
積層セラミックコンデンサ 0.1uF 3個
タクトスイッチ 2個
スライドスイッチ
圧電サウンダ PKM13EPYH4000-A0
ユニバーサル基板 Sunhayato ICB-93SG
バッテリースナップ
電池ボックス バッテリースナップタイプ 単3型3本用
スペーサ類
ニッケル水素電池(単3型)
(写真の回路とは一部異なります)
スイッチ1を押すと、モードが切り替わる。
スイッチ0を押すと、モード内での機能を切り替える。
I2Cや液晶ディスプレイの基本制御ライブラリは別ファイルで、下記のコードから構成される。
OS X (Mac) やLinuxの開発環境を使用する場合のMakefileの例は、ソース中にある。
//// 3mode-timer-lcd-atmega168.c//// Created by kaimunantai on 2015/11/16.// modified on 2015/11/21 : i2c master , define LCD parameters // modified on 2015/11/22 : add Timer, sw2// modified on 2015/11/23 : 3-mode add universal counter, stop-watch/* * Target : ATmega168P * OUTPUT : * LED : PD0 (PIN 2) * Buzzer : PB1 (PIN15) * LCD (AQM1602XA-RN-GBW) : I2C (TWI) SCL(PIN27) , SDA (PIN28) * INPUT : * tact switch sw0 : PD2 / INT0 (PIN 4) * tact switch sw1 : PD3 / INT1 (PIN 5) * VR controler : ADC1 (PIN24) * *//* # AVR-GCC Makefile PROJECT=i2c-timer-lcd-led-buz-sw-adc-atmega168 SOURCES=$(PROJECT).c twimaster.c i2c_lcd.c adc.c CC=avr-gcc OBJCOPY=avr-objcopy MMCU=atmega168 TARGETDEV=m168p CFLAGS=-g -O2 -mmcu=$(MMCU) -Wall $(PROJECT).hex: $(PROJECT).out$(OBJCOPY) -j .text -j .data -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
* */#define F_CPU 1000000UL#include <avr/io.h>#include <avr/interrupt.h>#include <util/delay.h>#include <util/twi.h>#include <string.h>#include "i2cmaster.h"#include "i2c_lcd.h"#include "adc.h"/* * i2c-master interface for avrgcc ((C) 2015 Peter Fleury, GNU General Public License Version 3) *http://homepage.hispeed.ch/peterfleury/avr-software.html#libs * *//* * I/O Control MACROs */#define led() ( PORTD ^= (1<<PD0) )#define ledOn() ( PORTD |= (1<<PD0) )#define ledOff() ( PORTD &= ~(1<<PD0) )#define isSw0On() ( !( PIND & (1<<PD2) ) )#define isSw0Off() ( PIND & (1<<PD2) )#define isSw1On() ( !( PIND & (1<<PD3) ) )#define isSw1Off() ( PIND & (1<<PD3) )#define buz() ( PORTB ^= (1<<PB1) )/* * stage definition */enum { STAGE_START, STAGE_TIMER_READY, STAGE_TIMER_SET, STAGE_TIMER_COUNTDOWN, STAGE_ALARM, STAGE_END, STAGE_COUNTER_RESET, STAGE_COUNTER_UP, STAGE_STOPWATCH_RESET, STAGE_STOPWATCH_UP, STAGE_STOPWATCH_STOP};static volatile uint8_t STAGE = STAGE_START;
/* * Mode definition */enum { MODE_TIMER, MODE_COUNTER, MODE_STOPWATCH};static volatile uint8_t MODE = MODE_TIMER;
static inline void wait(volatile unsigned long i)
{ while (i-- > 0);}static inline void beep(unsigned int height, unsigned int length)
{ volatile unsigned int i, j;
for (i = 0; i < length; i++) { buz(); for (j = 0; j < height; j++); }}/* * initialize */static inline void io_init(void)
{ PORTD = (1<<PD2) | (1<<PD3); // weak pull up resistor enagle on PD2,3 DDRD = 0b11110011; // PD2,3 as Input, others Output DDRB = 0b11111111; // PORTB All Output}static inline void int0_init(void)
{ EICRA |= (1<<ISC01) | (1<<ISC11); // INT0 Falling Edge, INT1 Falling edge EIMSK |= (1<<INT0) | (1<<INT1); // Enable Interrupt 0, 1}static inline void timer0_init(void)
// Initialize TIMER0{ // CTC mode , TOP is OCR0A TCCR0A = (1 << WGM01) | (0 << WGM00) ; // I/O clock prescaler N = 8 TCCR0B = (0 << WGM02) | (0 << CS02) | (1 << CS01) | (0 << CS00); // Timer0 on OCR0A compare match interrupt enable TIMSK0 |= ( 1<< OCIE0A ); // set TOP value . Interrupt every 1 ms OCR0A = 125; // OCR0A = interrupt period [s] * f_clkio [Hz] / I/O clock prescaler N}/* * Interrupt */volatile unsigned int T0count; // Timer0 interrupt counter
#define TIMER0_LIMIT 65535#define TIMER0_10SEC 10000#define TIMER0_1SEC 1000#define TIMER0_500MSEC 500ISR(TIMER0_COMPA_vect)// Timer0 Timer counter Compair match Interrupt{ if ( T0count > TIMER0_LIMIT ) { T0count = 0; } else { T0count++; } }volatile unsigned int UNIVERSAL_COUNTER = 0;
ISR(INT0_vect)// INT0 Interrupt{ switch ( MODE ) { case MODE_TIMER : switch ( STAGE ) { case STAGE_START : STAGE = STAGE_TIMER_SET; break; case STAGE_TIMER_SET : STAGE = STAGE_TIMER_READY; break; case STAGE_TIMER_READY : STAGE = STAGE_TIMER_COUNTDOWN; break; case STAGE_TIMER_COUNTDOWN : STAGE = STAGE_END; break; case STAGE_ALARM : STAGE = STAGE_TIMER_SET; break; default : STAGE = STAGE_START; } break; case MODE_COUNTER : switch (STAGE) { case STAGE_START : STAGE = STAGE_COUNTER_RESET; break; case STAGE_COUNTER_RESET : UNIVERSAL_COUNTER++; STAGE = STAGE_COUNTER_UP; break; case STAGE_COUNTER_UP : UNIVERSAL_COUNTER++; break; default : STAGE = STAGE_START; } break; case MODE_STOPWATCH : switch ( STAGE ) { case STAGE_STOPWATCH_RESET : STAGE = STAGE_STOPWATCH_UP; break; case STAGE_STOPWATCH_UP : STAGE = STAGE_STOPWATCH_STOP; break; case STAGE_STOPWATCH_STOP : STAGE = STAGE_STOPWATCH_RESET; break; default : STAGE = STAGE_STOPWATCH_RESET; } break; default: STAGE = STAGE_START; } beep(100, 20); while(isSw0On());}ISR(INT1_vect)// INT1 Interrupt{ switch ( MODE ) { case MODE_TIMER : MODE = MODE_COUNTER; STAGE = STAGE_COUNTER_RESET; break; case MODE_COUNTER : MODE = MODE_STOPWATCH; STAGE = STAGE_STOPWATCH_RESET; break; case MODE_STOPWATCH : MODE = MODE_TIMER; STAGE = STAGE_TIMER_SET; } beep(10, 200); while(isSw1On());}/* * main */int main(void){ unsigned int adata = 0; // data from ADC
int minutes = 0, seconds = 0; // minutes , seconds for timer io_init(); int0_init(); timer0_init(); adc_init(); i2c_lcd_init(); ledOn(); while(isSw0On() && isSw1On() ); // wait until switch released ledOff(); sei(); // Enable Interrupt i2c_lcd_setContrast(20); i2c_lcd_clearDisplay(); while (1) { switch ( STAGE ) { case STAGE_START : i2c_lcd_putStringRC(1, 1, "< LCD-MEGA168 >"); i2c_lcd_putStringRC(2, 1, " by KeinSoft"); break; case STAGE_TIMER_SET : i2c_lcd_putStringRC(1, 1, "< TIMER M168 >"); i2c_lcd_putStringRC(2, 1,"SET TIME>"); adata = adc_get_data(1); // get Analog signal from ADC1 minutes = adata >> 5 ; seconds = (((adata & 0x1f)>>4) % 2) * 30; i2c_lcd_putNumberRC(2, 10, (unsigned)minutes, 2); i2c_lcd_putStringRC(2, 12,":"); i2c_lcd_putNumberRC(2, 13, (unsigned)seconds, 2); i2c_lcd_putStringRC(2, 15," "); T0count = 0; break; case STAGE_TIMER_READY : i2c_lcd_putStringRC(2, 1, "**START* "); STAGE = STAGE_TIMER_COUNTDOWN; T0count = 0; break; case STAGE_TIMER_COUNTDOWN : if (T0count >= TIMER0_1SEC) { led(); seconds--; if (minutes <= 0 && seconds <= 0) { STAGE = STAGE_ALARM; ledOff(); i2c_lcd_putStringRC(2, 1, "!!! ALARM !!!! "); } else { if ( seconds <= 0) { seconds = 59; minutes--; } i2c_lcd_putNumberRC(2, 10, (unsigned)minutes, 2); i2c_lcd_putStringRC(2, 12, ":"); i2c_lcd_putNumberRC(2, 13, (unsigned)seconds, 2); } T0count = 0; } break; case STAGE_ALARM : led(); beep(5,100); wait(5000); break; case STAGE_COUNTER_RESET : UNIVERSAL_COUNTER = 0; i2c_lcd_putStringRC(1, 1, "< COUNTER M168 >"); i2c_lcd_putStringRC(2, 1, "COUNTUP:"); i2c_lcd_putNumberRC(2, 9, UNIVERSAL_COUNTER, 8); break; case STAGE_COUNTER_UP : i2c_lcd_putNumberRC(2, 9, UNIVERSAL_COUNTER, 8); break; case STAGE_STOPWATCH_RESET : i2c_lcd_putStringRC(1, 1, "< STOP WATCH >"); i2c_lcd_putStringRC(2, 1, " TIME >"); minutes = 0; seconds = 0; i2c_lcd_putNumberRC(2, 10, (unsigned)minutes, 2); i2c_lcd_putStringRC(2, 12,":"); i2c_lcd_putNumberRC(2, 13, (unsigned)seconds, 2); i2c_lcd_putStringRC(2, 15," "); T0count = 0; break; case STAGE_STOPWATCH_UP: i2c_lcd_putStringRC(2, 1, " START >"); if (T0count >= TIMER0_1SEC) { seconds++; if (seconds >= 60) { seconds = 0; minutes++; if ( minutes >= 60 ) { minutes = 0; } i2c_lcd_putNumberRC(2, 10, (unsigned)minutes, 2); } i2c_lcd_putNumberRC(2, 13, (unsigned)seconds, 2); i2c_lcd_putStringRC(2, 15," "); T0count = 0; } break; case STAGE_STOPWATCH_STOP : i2c_lcd_putStringRC(2, 1, " TIME :"); break; case STAGE_END : i2c_lcd_putStringRC(1, 1, "< LCD-TIMER168 >"); i2c_lcd_putStringRC(2, 1, " BYE-BYE "); break; default : ledOff(); } } return 0;
}ATmega168P のTWI機能によるI2Cのインターフェース制御コード。
Peter Fleury氏のサイト(http://homepage.hispeed.ch/peterfleury/avr-software.html#libs)のコード(GPL3ライセンスによるオープンソース)をそのまま利用させていただいた。ありがとうございます。
使用しているファイルは、次の二つ。
i2cmaster.h
twimaster.c
参考にさせていただいたのは、over80氏のコード。
(http://d.hatena.ne.jp/over80/20141013/1413174967#seemore)
秋月のデータシートと合わせて読むと、制御の手順がよくわかる素晴らしいコード。ありがとうございます。
元のコードの可読性を高め(低くなっているかも)、命名ルールをi2c masterに合わせ、機能を追加したものとなっている。
ヘッダファイル i2c_lcd.h
//// i2c_lcd.h// //// Created by kaimunantai on 2015/11/21.////#ifndef i2c_lcd_h#define i2c_lcd_h#include <stdio.h>/* * I2C LCD Commands, etc */#define I2C_LCD_ADDRESS 0x7c // Akizuki AQM1602XA-RN-GBW ( Controller : ST7032i )
#define LCD_CMD_CONTINUOUS 0x00 // Co:0, RS:0 continuous command#define LCD_CMD_ONLYONE 0x80 // Co:1, RS:0 only One command#define LCD_CMD_FUNCTION 0x38 // Normal mode command#define LCD_CMD_FUNCTION_EXT 0x39 // Extended mode command
#define LCD_CMD_OSCFREQ 0x14 // Internal OSC frequency
#define LCD_CMD_CONTRAST0 0b01110100 // Contrast set ( 0b0111 | C3 C2 C1 C0 )
#define LCD_CMD_NOICON_3V_CONTRAST1 0b01010101//0b0101 | ICON OFF(0) | Booster Output ON(1) | Contrast set ( C5 C4 )#define LCD_CMD_FOLLOWER_CONTROL 0x6c#define LCD_CMD_CLEAR_DISPLAY 0x01#define LCD_CMD_DISPLAY_ON 0x0c#define LCD_CMD_DDRAM_ADDRESS 0x80 // set DDRAM Address Instruction
#define LCD_CMD_WRITE_RAM 0x40 // Write Data into internal RAM
#define LCD_DDRAM_R1C1 0x00 // DDRAM Row 1, Col 1#define LCD_DDRAM_R2C1 0x40 // DDRAM Row 2, Col 1uint8_t i2c_lcd_init(void);uint8_t i2c_lcd_sendControls(uint8_t *data, uint8_t num);uint8_t i2c_lcd_sendString(uint8_t addr, char *data, uint8_t num);uint8_t i2c_lcd_clearDisplay(void);uint8_t i2c_lcd_setContrast(uint8_t contrast);uint8_t i2c_lcd_putStringRC(uint8_t row, uint8_t col, char *string);uint8_t i2c_lcd_putNumberRC(uint8_t row, uint8_t col, unsigned int value, int digit);
#endif /* i2c_lcd_h */モジュール本体 i2c
//// i2c_lcd.c// //// Created by kaimunantai on 2015/11/21.////#define F_CPU 1000000UL#include <avr/io.h>#include <avr/interrupt.h>#include <util/delay.h>#include <util/twi.h>#include <string.h>#include "i2cmaster.h"#include "i2c_lcd.h"/* I2C_LCD : ST7032i(I2C charactor LCD) */// Refer to// http://d.hatena.ne.jp/over80/20141013/1413174967// STARTuint8_t i2c_lcd_init(void){ uint8_t init_data1[] = { LCD_CMD_FUNCTION, LCD_CMD_FUNCTION_EXT, LCD_CMD_OSCFREQ, LCD_CMD_CONTRAST0, LCD_CMD_NOICON_3V_CONTRAST1, LCD_CMD_FOLLOWER_CONTROL }; uint8_t init_data2[] = { LCD_CMD_FUNCTION, LCD_CMD_DISPLAY_ON, LCD_CMD_CLEAR_DISPLAY }; uint8_t st; _delay_ms(40); i2c_init(); st = i2c_lcd_sendControls(init_data1, sizeof(init_data1)); if (st != 0) { return st; } _delay_ms(200); st = i2c_lcd_sendControls(init_data2, sizeof(init_data2)); if (st != 0) { return st; } _delay_ms(2); return 0;
}uint8_t i2c_lcd_sendControls(uint8_t *data, uint8_t num){ uint8_t i; i2c_start_wait(I2C_LCD_ADDRESS | I2C_WRITE); i2c_write(LCD_CMD_CONTINUOUS); // Co:0(continuous) RS:0 if (TW_STATUS != TW_MT_DATA_ACK) { return -1; } for (i = 0; i < num; i++) { i2c_write(*(data + i)); if (TW_STATUS != TW_MT_DATA_ACK) { return -1; } _delay_us(27); } i2c_stop(); return 0;
}uint8_t i2c_lcd_sendString(uint8_t addr, char *data, uint8_t num){ uint8_t i; i2c_start_wait(I2C_LCD_ADDRESS | I2C_WRITE); i2c_write(LCD_CMD_ONLYONE); // Co:1(one shot) RS:0 if (TW_STATUS != TW_MT_DATA_ACK) { return -1; } i2c_write(LCD_CMD_DDRAM_ADDRESS | addr); // Set DDRAM address if (TW_STATUS != TW_MT_DATA_ACK) { return -1; } i2c_write(LCD_CMD_WRITE_RAM); //Co:0 RS:1 (Write data to RAM) if (TW_STATUS != TW_MT_DATA_ACK) { return -1; } for (i = 0; i < num; i++) { i2c_write( *(data + i) ); if (TW_STATUS != TW_MT_DATA_ACK) { return -1; } _delay_us(27); } i2c_stop(); return 0;
}// Refer to// http://d.hatena.ne.jp/over80/20141013/1413174967// ENDuint8_t i2c_lcd_clearDisplay(void)/* * Clear LCD Screen */{ uint8_t st; uint8_t command[] = { LCD_CMD_FUNCTION, LCD_CMD_CLEAR_DISPLAY }; st = i2c_lcd_sendControls(command, sizeof(command)); if (st != 0) { return st; } _delay_ms(2); return 0;
}uint8_t i2c_lcd_setContrast(uint8_t contrast)/* * set LCD Contrast * * arguments * contrast : contrust value 0-63 * */{ uint8_t st; uint8_t contrast_command[] = { LCD_CMD_FUNCTION, LCD_CMD_FUNCTION_EXT, 0x70, 0x55, LCD_CMD_FUNCTION}; contrast_command[2] = 0x70 | (contrast & 0b00001111); contrast_command[3] = 0x50 | 0b0100 | ((contrast & 0b00110000) >> 4); st = i2c_lcd_sendControls(contrast_command, sizeof(contrast_command)); if (st != 0) { return st; } _delay_ms(2); return 0;
}uint8_t i2c_lcd_putStringRC(uint8_t row, uint8_t col, char *string)/* * ARGUMENTS * * row : String start position , row of LCD * col : String start position , column of LCD * * col ->->-> * 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 * row | 1 * ^ 2 * * string : pointer of string data */{ uint8_t address; uint8_t st; address = LCD_DDRAM_R1C1 + ( row - 1 ) * LCD_DDRAM_R2C1 + col - 1; st = i2c_lcd_sendString(address, string, strlen(string)); if (st != 0 ) return st; return 0;
}uint8_t i2c_lcd_putNumberRC(uint8_t row, uint8_t col, unsigned int value, int digit)
/* * ARGUMENTS * * row : String start position , row of LCD * col : String start position , column of LCD * * col ->->-> * 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 * row | 1 * ^ 2 * * value : output value * digit : number of digit */{ char string[17]; // buffer of strings uint8_t st; int i; if (digit >= 17) { // Over flow return -1; } string[digit] = 0x00; for (i = digit - 1 ; i >= 0; i--) { string[i] = '0' + value % 10; value /= 10; } st = i2c_lcd_putStringRC(row, col, string); if (st != 0 ) return st; return 0;
}