#include <avr/io.h>
#include <avr/interrupt.h>
#include <avr/sleep.h>
#include "hd44780.h" // EZ-LCDのヘッダファイルをインクルードしておきます。
#define RED 0x20 // 赤スイッチ
#define YELLOW 0x10 // 黄スイッチ
#define GREEN 0x08 // 緑スイッチ
#define MODE (RED | YELLOW) // 時計変更モード
#define SWON (RED | YELLOW | GREEN) // 全てのスイッチがON
void InitHard(void); // ハードウェア初期化
void InitTime(void); // 時間初期化
void CalcDate(uint8_t, uint8_t); // 時計演算 0:加算、1:減算
void BlinkDisp(uint8_t, uint8_t); // 指定位置ブリンクする。、0:通常、1:前桁の文字を表示
void DateDisp(uint8_t, uint8_t, uint8_t); // 指定場所の文字を更新する。
void UpdateTime(void); // 時計更新
uint8_t g_u8BlinkFlg = 0; // 0:点灯、1:消灯
volatile uint8_t g_u8IntTimeFlg; // 時計割り込みフラグ
uint8_t g_u8CalenderBuf[6]; // 時計データ保存、データはBCDで保存。上位4bit:10の位、下位4bit:1の位
union switch_states // スイッチ状態フラグ
{
uint8_t BYTE;
struct
{
uint8_t mode :1; // 時計変更モード
uint8_t mode_keep :1; // 時計変更モード確定
uint8_t red :1; // 赤スイッチON
uint8_t red_keep :1; // 赤スイッチON->OFF
uint8_t yellow :1; // 黄スイッチON
uint8_t yellow_keep :1; // 黄スイッチON->OFF
uint8_t green :1; // 緑スイッチON
uint8_t green_keep :1; // 緑スイッチON->OFF
}BIT;
};
union switch_states g_uSwitchState;
uint8_t g_u8CalenderArray[2][12] = {{31, 28, 31, 30, 31, 30, 31, 31, 30,
31, 30, 31}, // カレンダ通常年
{ 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}}; // カレンダ閏年
int main(void)
{
uint8_t u8PortcState;
uint8_t u8LcdLocate; // 変更位置記憶 0:yy, 1:mm, 2:dd, 3:hhh, 4:mmm, 5:sss
uint8_t u8LocateChange = 0; // 表示位置変更フラグ 0:変更位置移動なし、1:移動あり
InitHard(); // ポート、LCD初期化
g_u8IntTimeFlg = 0; // 250ms毎にカウント
InitTime(); // 時計初期化および初期値表示
sei(); // 割り込み許可
while(1)
{
sleep_enable(); //sleep許可
sleep_cpu(); //sleep中
sleep_disable(); // sleep禁止
// 時計更新
if (g_u8IntTimeFlg > 3)
{
UpdateTime(); // 時計更新
g_u8IntTimeFlg = 0;
}
// 時計調整
u8PortcState = ~PINC & SWON;
if (g_uSwitchState.BIT.mode_keep == 0) // 時計変更モード確認
{
if ( (u8PortcState == MODE)) // 赤と黄色のスイッチを同時on。
{
g_uSwitchState.BIT.mode = 1;
}
else if ( (u8PortcState == 0) && (g_uSwitchState.BIT.mode == 1))
// 赤と黄スイッチがoff->on->offで時計変更モードに移行
{
g_uSwitchState.BIT.mode_keep = 1;
u8LcdLocate = 0;
BlinkDisp(u8LcdLocate, 0); // 年の位置ブリンク表示
}
}
else if (g_uSwitchState.BIT.mode_keep) // 時計変更モード、スイッチは赤->黄->緑の順で優先順位が低くなる。
{ // 赤と緑が押されだ場合は赤が優先される。
u8LocateChange = 0; //表示位置変更フラグクリア
if (u8PortcState == RED) // 赤がon
{
g_uSwitchState.BYTE = 0;
g_uSwitchState.BIT.mode_keep = 1;
g_uSwitchState.BIT.red = 1;
}
else if (u8PortcState == YELLOW) // 黄がon
{
g_uSwitchState.BYTE = 0;
g_uSwitchState.BIT.mode_keep = 1;
g_uSwitchState.BIT.yellow = 1;
}
else if (u8PortcState == GREEN) // 緑がon
{
g_uSwitchState.BYTE = 0;
g_uSwitchState.BIT.mode_keep = 1;
g_uSwitchState.BIT.green = 1;
}
else if ( u8PortcState == 0) // スイッチが全部off、スイッチがoff->on->offで各動作をする。
{
if (g_uSwitchState.BIT.red == 1) // 赤がonだった
{
g_uSwitchState.BIT.red = 0;
u8LcdLocate++;
u8LocateChange = 1;
}
if (g_uSwitchState.BIT.yellow == 1) // 黄がonだった
{
g_uSwitchState.BIT.yellow = 0;
CalcDate( u8LcdLocate, 0);
}
if (g_uSwitchState.BIT.green == 1) // 緑がonだった
{
g_uSwitchState.BIT.green = 0;
CalcDate(u8LcdLocate, 1);
}
}
BlinkDisp(u8LcdLocate, u8LocateChange); // ブリンク、カレンダ更新
}
} // while loop
}
void InitHard(void)
{
// port初期化
DDRB = 0xff; // portb7:0=出力
PORTB = 0;
DDRC = 0x07; // portc2:0=出力
PORTC = 0;
DDRD = 0xff; // portd7:0=出力
PORTD = 0;
// LCD初期化
lcd_init();
// 16bitタイマ設定
// CTC動作、1/8分周
OCR1A = 31686; // 31249 設定値
TCCR1A = (1 << COM1A0);
TCCR1B = 0x0a; // スタート
TIMSK1 = 1 << OCIE1A; // ctc割り込み許可
// スリープモードはアイドルに設定
set_sleep_mode(SLEEP_MODE_IDLE);
}
void InitTime(void)
{
g_u8CalenderBuf[0] = 0x12;
g_u8CalenderBuf[1] = 6;
g_u8CalenderBuf[2] = 1;
g_u8CalenderBuf[3] = 0;
g_u8CalenderBuf[4] = 0;
g_u8CalenderBuf[5] = 0;
lcd_locate( 0, 0); // 1行目:年月日表示
lcd_putc((g_u8CalenderBuf[0] >> 4) + 0x30); // 年
lcd_putc((g_u8CalenderBuf[0] & 0x0f) + 0x30);
lcd_putc('/');
lcd_putc((g_u8CalenderBuf[1] >> 4) + 0x30); // 月
lcd_putc((g_u8CalenderBuf[1] & 0x0f) + 0x30);
lcd_putc('/');
lcd_putc((g_u8CalenderBuf[2] >> 4) + 0x30); // 分
lcd_putc((g_u8CalenderBuf[2] & 0x0f) + 0x30);
lcd_locate( 1, 0); // 2行目:時分秒表示
lcd_putc((g_u8CalenderBuf[3] >> 4) + 0x30); // 時
lcd_putc((g_u8CalenderBuf[3] & 0x0f) + 0x30);
lcd_putc(':');
lcd_putc((g_u8CalenderBuf[4] >> 4) + 0x30); // 分
lcd_putc((g_u8CalenderBuf[4] & 0x0f) + 0x30);
lcd_putc(':');
lcd_putc((g_u8CalenderBuf[5] >> 4) + 0x30); // 秒
lcd_putc((g_u8CalenderBuf[5] & 0x0f) + 0x30);
}
void BlinkDisp(uint8_t dat, uint8_t flg) //指定位置から2文字分ブリンクする。
{
uint8_t n, nn, raw, col;
switch( dat)
{
case 0: // 年
lcd_locate(0, 0);
break;
case 1: // 月
lcd_locate(0, 3);
raw = 0;
col = 0;
break;
case 2: // 日
lcd_locate(0, 6);
raw = 0;
col = 3;
break;
case 3: // 時
lcd_locate(1, 0);
raw = 0;
col = 6;
break;
case 4: // 分
lcd_locate(1, 3);
raw = 1;
col = 0;
break;
case 5: // 秒
lcd_locate(1, 6);
raw = 1;
col = 3;
break;
default: // 時計修正モード終了
g_uSwitchState.BYTE = 0;
lcd_locate(1, 6);
lcd_putc((g_u8CalenderBuf[5] >> 4) + 0x30);
lcd_putc((g_u8CalenderBuf[5] & 0x0f) + 0x30);
return;
break;
}
if ( g_u8BlinkFlg == 0)
{
// 消灯
g_u8BlinkFlg = 1;
lcd_putc(' ');
lcd_putc(' ');
}
else
{
// 点灯
g_u8BlinkFlg = 0;
lcd_putc((g_u8CalenderBuf[dat] >> 4) + 0x30);
lcd_putc((g_u8CalenderBuf[dat] & 0x0f) + 0x30);
}
if (flg == 1)
{
lcd_locate(raw, col);
lcd_putc((g_u8CalenderBuf[dat - 1] >> 4) + 0x30);
lcd_putc((g_u8CalenderBuf[dat - 1] & 0x0f) + 0x30);
}
}
void CalcDate(uint8_t dat, uint8_t flg)
{
int sum, day;
switch ( dat)
{
case 0: // 年
sum = (g_u8CalenderBuf[dat] >> 4) * 10 + (g_u8CalenderBuf[dat] &
0x0f);
switch(flg)
{
case 0: // 加算
if ( ++sum > 99)
{
sum = 0;
}
break;
default: // 減算
if ( --sum < 0 )
{
sum = 99;
}
break;
}
g_u8CalenderBuf[dat] =((sum / 10) << 4) | (sum % 10);
break;
case 1: // 月
sum = (g_u8CalenderBuf[dat] >> 4) * 10 + (g_u8CalenderBuf[dat] &
0x0f);
switch(flg)
{
case 0: // 加算
if ( ++sum > 12)
{
sum = 1;
}
break;
default: // 減算
if ( --sum < 1 )
{
sum = 12;
}
break;
}
g_u8CalenderBuf[dat] =((sum / 10) << 4) | (sum % 10);
break;
case 2: // 日
sum = (g_u8CalenderBuf[1] >> 4) * 10 + (g_u8CalenderBuf[1] &
0x0f); // 月を抽出
if ((((g_u8CalenderBuf[0] >> 4) * 10 + (g_u8CalenderBuf[0] &
0x0f)) % 4) == 0) // うるう年なら
{
day = g_u8CalenderArray[1][sum - 1];
}
else
{
day = g_u8CalenderArray[0][sum -1];
}
sum = (g_u8CalenderBuf[dat] >> 4) * 10 + (g_u8CalenderBuf[dat] &
0x0f);
switch(flg)
{
case 0: // 加算
if ( ++sum > day)
{
sum = 1;
}
break;
default: // 減算
if ( --sum < 1 )
{
sum = day;
}
break;
}
g_u8CalenderBuf[dat] =((sum / 10) << 4) | (sum % 10);
break;
case 3: // 時
sum = (g_u8CalenderBuf[dat] >> 4) * 10 + (g_u8CalenderBuf[dat] &
0x0f);
switch(flg)
{
case 0: // 加算
if ( ++sum > 23)
{
sum = 1;
}
break;
default: // 減算
if ( --sum < 1 )
{
sum = 23;
}
break;
}
g_u8CalenderBuf[dat] =((sum / 10) << 4) | (sum % 10);
break;
default: // 分、秒
sum = (g_u8CalenderBuf[dat] >> 4) * 10 + (g_u8CalenderBuf[dat] &
0x0f);
switch(flg)
{
case 0: // 加算
if ( ++sum >59)
{
sum = 0;
}
break;
default: // 減算
if ( --sum < 1 )
{
sum = 59;
}
break;
}
g_u8CalenderBuf[dat] =((sum / 10) << 4) | (sum % 10);
break;
}
}
void UpdateTime(void) // 時計
{
uint8_t sum, day, flg = 0;
// 秒
sum = (g_u8CalenderBuf[5] >> 4) * 10 + (g_u8CalenderBuf[5] &
0x0f);
if ( ++sum > 59)
{
g_u8CalenderBuf[5] = 0;
flg = 1;
DateDisp(1, 6, g_u8CalenderBuf[5]);
}
else
{
g_u8CalenderBuf[5] =((sum / 10) << 4) | (sum % 10);
DateDisp(1, 6, g_u8CalenderBuf[5]);
return;
}
// 分
if ( flg == 1)
{
flg = 0;
sum = (g_u8CalenderBuf[4] >> 4) * 10 + (g_u8CalenderBuf[4] &
0x0f);
if( ++sum > 59)
{
g_u8CalenderBuf[4] = 0;
flg = 1;
DateDisp(1, 3, g_u8CalenderBuf[4]);
}
else
{
g_u8CalenderBuf[4] =((sum / 10) << 4) | (sum % 10);
DateDisp(1, 3, g_u8CalenderBuf[4]);
}
}
// 時
if ( flg == 1)
{
flg = 0;
sum = (g_u8CalenderBuf[3] >> 4) * 10 + (g_u8CalenderBuf[3] &
0x0f);
if( ++sum > 23)
{
g_u8CalenderBuf[3] = 0;
flg = 1;
DateDisp(1, 0, g_u8CalenderBuf[3]);
}
else
{
g_u8CalenderBuf[3] =((sum / 10) << 4) | (sum % 10);
DateDisp(1, 0, g_u8CalenderBuf[3]);
}
}
// 日
if ( flg == 1)
{
flg = 0;
sum = (g_u8CalenderBuf[1] >> 4) * 10 + (g_u8CalenderBuf[1] &
0x0f); // 月を抽出
if ((((g_u8CalenderBuf[0] >> 4) * 10 + (g_u8CalenderBuf[0] &
0x0f)) % 4) == 0) // うるう年なら
{
day = g_u8CalenderArray[1][sum - 1];
}
else
{
day = g_u8CalenderArray[0][sum -1];
}
sum = (g_u8CalenderBuf[2] >> 4) * 10 + (g_u8CalenderBuf[2] &
0x0f);
if( ++sum > day)
{
g_u8CalenderBuf[2] = 1;
DateDisp(0, 6, g_u8CalenderBuf[2]);
flg = 1;
}
else
{
g_u8CalenderBuf[2] =((sum / 10) << 4) | (sum % 10);
DateDisp(0, 6, g_u8CalenderBuf[2]);
}
}
// 月
if ( flg == 1)
{
flg = 0;
sum = (g_u8CalenderBuf[1] >> 4) * 10 + (g_u8CalenderBuf[1] &
0x0f);
if( ++sum > 12)
{
g_u8CalenderBuf[1] = 1;
DateDisp(0, 3, g_u8CalenderBuf[1]);
flg = 1;
}
else
{
g_u8CalenderBuf[1] =((sum / 10) << 4) | (sum % 10);
DateDisp(0, 3, g_u8CalenderBuf[1]);
}
}
// 年
if ( flg == 1)
{
flg = 0;
sum = (g_u8CalenderBuf[0] >> 4) * 10 + (g_u8CalenderBuf[0] &
0x0f);
if( ++sum > 99)
{
g_u8CalenderBuf[0] = 0;
DateDisp(0, 0, g_u8CalenderBuf[0]);
flg = 1;
}
else
{
g_u8CalenderBuf[0] =((sum / 10) << 4) | (sum % 10);
DateDisp(0, 0, g_u8CalenderBuf[0]);
}
}
}
void DateDisp(uint8_t raw, uint8_t col, uint8_t dat) // 指定場所の文字を更新する。
{
lcd_locate(raw, col);
lcd_putc((dat >> 4) + 0x30);
lcd_putc((dat & 0x0f) + 0x30);
}
SIGNAL(TIMER1_COMPA_vect) // 0.25秒ごとに割込発生
{
g_u8IntTimeFlg++;
}