#include <avr/io.h>
#include <avr/interrupt.h>
#include <avr/sleep.h>
#include "hd44780.h"
#include <util/delay.h>
// 時計
#define RED 0x20 // 赤スイッチ
#define YELLOW 0x10 // 黄スイッチ
#define GREEN 0x08 // 緑スイッチ
#define MODE (RED | YELLOW) // 時計変更モード
#define SWON (RED | YELLOW | GREEN) // 全てのスイッチがON
// 温度計
#define LM61_ON PORTC |= 1 << PORTC2 // LM61電源ON
#define LM61_OFF PORTC &= ~(1 << PORTC2) // LM61電源OFF
#define ADC_ON ADCSRA |= (1 << ADEN) // adc許可
#define ADC_OFF ADCSRA &= ~(1 << ADEN) // adc停止
#define ADC_START ADCSRA |= 1 << ADSC // adcスタート
#define V_REF 2495 // レファレンス電圧
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); // 時計更新
void DoADC(void); // ADCを動作させ表示させる。 1秒毎
uint16_t ReadADCCel(void); // ADCからデータを読み出す。デジタルフィルタ付
void AdcDisp( uint16_t); // ADC値を変換し表示
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}}; // カレンダ閏年
uint8_t g_u8LcdStr[] = {'-', '-', '-', '-', '.', '-', 0xdf, 'C'}; // LCD表示データ格納バッファ
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;
DoADC(); // 1秒毎にADCを動作させ温度をLCDに表示する。
}
// 時計調整
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 = 0x06; // portc2:1=出力
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割り込み許可
// ADC
ADMUX = 0; // Vref = 外部レファレンス
ADCSRA = (1 << ADPS1) | (1 << ADPS0); // adcクロック = 125kHz
DIDR0 = (1 << ADC0D); // adc0デジタル入力バッファ禁止
//ADC_ON;
// スリープモードはアイドルに設定
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 DoADC(void) // ADCを動作させ表示させる。1秒毎
{
uint16_t result;
LM61_ON; // 温度センサ電源ON
ADC_ON; // ADCイネーブル
DELAY_US(6000); // wait 60ms レファレンス電圧、LM61、ADC安定待ち
ADC_START; // ADC動作開始
result = ReadADCCel(); // ADCからデータを読み出す。デジタルフィルタ付
ADC_OFF; // ADC禁止
LM61_OFF; // 温度センサ電源OFF
AdcDisp(result); // LCDにデータを表示
}
uint16_t ReadADCCel(void) // 指定されたチャンネルのADCからデータを読み出す。
{
static uint16_t adc_data1, adc_data2, adc_data3;
while((ADCSRA & (1 << ADSC)));
adc_data3 = adc_data2;
adc_data2 = adc_data1;
adc_data1 = ADCW;
return ((adc_data3 + adc_data2 + adc_data2 + adc_data1) / 4);
}
void AdcDisp( uint16_t result) // ADC値を変換し表示
{
int16_t i;
long sum;
sum = ((long)result * V_REF/ 1024) - 600;
// sumの正常範囲チェック
if ( (sum > 1800) || ( sum < -400))
{
for ( i = 0; i < 4; i++)
{
g_u8LcdStr[i] = '-';
}
g_u8LcdStr[5] = '-';
}
else
{
if ( sum < 0)
{
sum *= -1;
g_u8LcdStr[0] = '-';
}
else
{
g_u8LcdStr[0] = 0x20;
}
g_u8LcdStr[1] = 0x20;
g_u8LcdStr[2] = 0x20;
g_u8LcdStr[3] = 0x30;
g_u8LcdStr[5] = 0x30;
// binary -> BCDキャラクタ変換
for ( i = 5; i > 0; i--)
{
g_u8LcdStr[i] = (sum % 10) + 0x30;
sum /= 10;
if ( i == 5)
{
i--;
}
if ( sum <= 0)
{
break;
}
}
}
// LCDに表示
lcd_locate( 2, 0);
for ( i = 0; i < 8; i++)
{
lcd_putc(g_u8LcdStr[i]);
}
}
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++;
}