アトメル社のAVRマイコンをつかった電子工作および無線を使った電子工作を紹介します。





ホーム > 工作室トップ > ログ保存付遠隔表示温度計

ログ保存付遠隔表示温度計

今までに製作してきた遠隔表示温度計の温度データをSDカードに定期的に保存します。
SDカードへの保存はChanNさんのFatfsを使用します。SDカードの制御について知識がなくともマイコンにあった設定をするだけで、SDカードが使用できるすばらしいソフトです。
SDカードへの保存以外は前作の「無線で遠隔温度計計をつくる」と同じです。(入出力ポートは見直しを行っています。)
センサ側基板は変更ありません。

1.仕様

1.表示器
項目 内容
機能 1行目:年月日を表示します。
2行目:時分秒を表示します。
3行目:温度1を表示します。(表示器上の温度を表示します。以前製作した温度計と同様です。)
4行目:温度2を表示します。(温度センサからの温度を表示します。)
温度1は以前製作した温度計と同様1秒毎に更新します。
温度2は温度センサからデータがきたときに更新します。
年月日時分秒の調整ができます。(以前製作した温度計と同様です。)
表示器と温度センサはXBeeで通信します。(温度センサから表示器へ一方向の通信です。)
表示器はコーディネーターに設定します。
温度センサはエンドデバイスに設定します。
電源 3.3Vで動作させます。
LCD 形名:SC2004CS
表示:20文字 X 4行
データ:4ビットモードで使用します。
3.3V動作用
マイコン ATmega328
温度センサ 温度センサはLM61CIZを使用します。
ADコンバータの基準電源はシャントレギュレータTL451を使用します。
 LCD制御 CHANさんのEZ-LCDを使用します。
XBee XB24-Z7CIT-004
SDへの保存 ChanNさんのFatfsを使用します。
SDカードはマイクロSDカードを使用します。

2.温度センサ
項目 内容
機能 10秒毎に温度計測データを無線で送信します。
送信のみとします。
電源 電池2本(3V)で動作させます。
温度センサ 温度センサはLM61CIZを使用します。
XBee XB24-Z7CIT-004

2.回路

表示器部回路図

前述した「無線で遠隔温度計をつくる」とほぼ同様で違う所を以下に示します。
  1. SDカードのインターフェース回路。インターフェース回路はAVRマイコンのSPIを使用します。
  2. LCDの入出力ポートをポートBからポートDに変更。AVRマイコンのSPIを使用するためです。
  3. マイクロSDカード用コネクタcON1を追加。秋月電子さんより購入したものでSDカード検出スイッチ付きです。
  4. マイクロSDカード非挿入表示LED1を追加。
表示器側部品表
名称 形名 メーカ 購入先 数量
LCD SC2004CS(3.3V用) 秋月電子 1
マイコン ATmega328 アトメル 秋月電子 1
抵抗 10kΩ 1/4W 秋月電子 7
抵抗 1kΩ 1/4W 秋月電子 1
半固定抵抗 10kΩ 秋月電子 1
押しボタンスイッチ 赤色タクトスイッチ 秋月電子 1
押しボタンスイッチ 黄色タクトスイッチ 秋月電子 1
押しボタンスイッチ 緑色タクトスイッチ 秋月電子 1
温度センサ LM61CIZ ナショセミ 秋月電子 1
シャントレギュレータ TL431ACZ 秋月電子 1
XBee XB24-Z7CIT-004 スイッチサイエンス 1
マイクロSDカードスロット 秋月電子 1
赤色LED φ3 秋月電子 1

表示基部組立図

4.フローチャート

メインフロー

メインフロー

  1. 初期設定ではポート設定、タイマー初期化、LCDの初期化、SDカード初期化(Fatfs)、時計初期化、UART初期化、スリープモード設定を行い割込を許可します。
  2. SDカードが挿入されているか確認を行います。SDカードが挿入されている場合はファイルをオープンします。今回は、固定ファイル名でアペンドモードで書込を行います。ファイルがいっぱいになった場合はファイルポインタを先頭に戻して書込を行います。
  3. SDカードへの保存は5分毎に行います。テキストで保存し、[現在日時(月日時分の8byte):本体温度(+***.*):遠隔温度(+***.*),]で順に保存します。データの区切りは’:’で行い、時間毎のデータの区切りは’,’で行います。
  4. スリープの解除は1秒毎の時計割込およびXBeeからの受信割込です。
  5. 遠隔温度はXBeeからのデータです。データが受信されると温度を更新して表示します。
  6. 1秒毎に時計を更新します。これど同時に表示器部の温度温度を更新します。

5.プログラムの実行

表示器部のLCD表示部

プログラム実行後のLCD表示部を示します。
SD保存データ例
[月日時分];[表示器温度];[遠隔温度],で1レコードデータ
   
09242205; 27.6; 25.8,09242210; 27.6; 25.8,09242215; 27.6; 25.8,
09242220; 27.6; 25.8,09242225; 27.6; 25.8,09242230; 27.6; 26.0,
09242235; 27.6; 26.0,09242240; 27.6; 26.0,09242245; 27.6; 26.0,
09242250; 27.6; 26.0,09242255; 27.4; 26.0,09242300; 27.4; 26.0,
09242305; 27.4; 26.0,09242310; 27.4; 25.8,09242315; 27.1; 25.8,
09242320; 27.1; 25.8,09242325; 27.1; 25.8,09242330; 27.1; 25.8,
09242335; 27.1; 25.7,09242340; 27.1; 25.7,09242345; 27.1; 25.7,
09242350; 26.9; 25.7,09242355; 26.9; 25.7,09250000; 26.9; 25.7,
09250005; 26.9; 25.6,09250010; 26.9; 25.6,09250015; 26.9; 25.6,
09250020; 26.9; 25.6,09250025; 26.9; 25.6,09250030; 26.9; 25.6,
09250035; 26.7; 25.6,09250040; 26.7; 25.5,09250045; 26.9; 25.5,

6.プログラム

FatfsのATmega328P用設定として、以下を変更します。

mmc.c


/*-----------------------------------------------------------------------*/
/* MMCv3/SDv1/SDv2 (in SPI mode) control module (C)ChaN, 2010 */
/*-----------------------------------------------------------------------*/
/* Only rcvr_spi(), xmit_spi(), disk_timerproc() and some macros */
/* are platform dependent. */
/*-----------------------------------------------------------------------*/


#include <avr/io.h>
#include "diskio.h"


/*--------------------------------------------------------------------------

Module Private Functions

---------------------------------------------------------------------------*/

/* Port Controls (Platform dependent) */
//#define CS_LOW() PORTB &= ~1 /* MMC CS = L */
#define CS_LOW() PORTB &= ~_BV(2) /*12.09.09 ATmega328用に変更 */
//#define CS_HIGH() PORTB |= 1 /* MMC CS = H */
#define CS_HIGH() PORTB |= _BV(2) /* 12.09.09 ATmega328用に変更 */

//#define SOCKWP (PINB & 0x20) /* Write protected. yes:true, no:false, default:false 12.09.09 ATmega328用に変更*/
#define SOCKWP 0 /* :検出しない 常にプロテクトしていない*/
//#define SOCKINS (!(PINB & 0x10)) /* Card detected. yes:true, no:false, default:true 12.09.09 ATmega328用に変更 */
#define SOCKINS (!(PINB & 0x01)) // カードが挿入されていると'0'
//#define FCLK_SLOW() SPCR = 0x52 /* Set slow clock (100k-400k) */ 12.09.16 250kHzに設定
#define FCLK_SLOW() SPCR = 0x50
#define FCLK_FAST() SPCR = 0x50 /* Set fast clock (depends on the CSD) */

    ・
    ・
    ・
    ・

/*-----------------------------------------------------------------------*/
/* Power Control (Platform dependent) */
/*-----------------------------------------------------------------------*/
/* When the target system does not support socket power control, there */
/* is nothing to do in these functions and chk_power always returns 1. */

static
int power_status(void) /* Socket power state: 0=off, 1=on */
{
//return (PORTE & 0x80) ? 0 : 1; //12.09.05 電源は常時ON
return 1;
}


static
void power_on (void)
{
// PORTE &= ~0x80; /* Socket power on 12.09.09 常に電源ON */
//for (Timer1 = 2; Timer1; ); /* Wait for 20ms 12.09.09 */
//PORTB = 0b10110101; /* Enable drivers 12.09.09 ATmega328用に変更*/
PORTB = (PORTB & 0x02) | 0b00000100;
//DDRB = 0b11000111; // 12.09.09 ATmega328用に変更
DDRB = 0b11101110; /* pb7:未使用
pb6:未使用
pb5:SCK
pb4:MISO
pb3:MOSI
pb2:CS
pb1:クロック出力(SPIとは無関係
pb0:カード挿入? */


//SPCR = 0x52; /* Enable SPI function in mode 0 */
SPCR = 0x50; //12.09.09 main clock = 1MHzでSPIは250kHzで動作させる。
//SPSR = 0x01; /* SPI 2x mode */
SPSR = 0; // 12.09.09 AT328用に変更
}


static
void power_off (void)
{
SPCR = 0; /* Disable SPI function */
//DDRB = 0b11000000; /* Disable drivers 12.09.09 ATmega328用に変更*/
//PORTB = 0b10110000;

//PORTE |= 0x80; /* Socket power off 12.09.09 ATmega328用に変更*/
Stat |= STA_NOINIT;
}

rtc.c


リアルタイムクロックを実装していないので固定値を返すようにします。

/*--------------------------------------------------------------------------*/
/* RTC controls */

#include <avr/io.h>
#include <string.h>
#include "rtc.h"



//12.09.10 RTCは固定時刻
//#define SCL_LOW() DDRE |= 0x04 /* SCL = LOW */
//#define SCL_HIGH() DDRE &= 0xFB /* SCL = High-Z */
//#define SCL_VAL ((PINE & 0x04) ? 1 : 0) /* SCL input level */
//#define SDA_LOW() DDRE |= 0x08 /* SDA = LOW */
//#define SDA_HIGH() DDRE &= 0xF7 /* SDA = High-Z */
//#define SDA_VAL ((PINE & 0x08) ? 1 : 0) /* SDA input level */



/*-------------------------------------------------*/
/* I2C bus protocol */


static
void iic_delay (void)
{
// 12.09.10 RTCは固定時刻
return;
/*int n;

for (n = 4; n; n--) PINB; */
}


/* Generate start condition on the IIC bus */
static
void iic_start (void)
{
// 12.09.10 RTCは固定時刻
return;
/*
SDA_HIGH();
iic_delay();
SCL_HIGH();
iic_delay();
SDA_LOW();
iic_delay();
SCL_LOW();
iic_delay(); */
}


/* Generate stop condition on the IIC bus */
static
void iic_stop (void)
{
// 12.09.10 RTCは固定時刻
return;
/*
SDA_LOW();
iic_delay();
SCL_HIGH();
iic_delay();
SDA_HIGH();
iic_delay(); */
}


/* Send a byte to the IIC bus */
static
int iic_send (BYTE dat)
{
// 12.09.10 RTCは固定時刻
return 1;

//BYTE b = 0x80;
//int ack;
//
//
//do {
//if (dat & b) { /* SDA = Z/L */
//SDA_HIGH();
//} else {
//SDA_LOW();
//}
//iic_delay();
//SCL_HIGH();
//iic_delay();
//SCL_LOW();
//iic_delay();
//} while (b >>= 1);
//SDA_HIGH();
//iic_delay();
//SCL_HIGH();
//ack = SDA_VAL ? 0 : 1; /* Sample ACK */
//iic_delay();
//SCL_LOW();
//iic_delay();
//return ack;
//}
//
//
///* Receive a byte from the IIC bus */
//static
//BYTE iic_rcvr (int ack)
//{
//UINT d = 1;
//
//
//do {
//d <<= 1;
//SCL_HIGH();
//if (SDA_VAL) d++;
//iic_delay();
//SCL_LOW();
//iic_delay();
//} while (d < 0x100);
//if (ack) { /* SDA = ACK */
//SDA_LOW();
//} else {
//SDA_HIGH();
//}
//iic_delay();
//SCL_HIGH();
//iic_delay();
//SCL_LOW();
//SDA_HIGH();
//iic_delay();
//
//return (BYTE)d;
}



/*-------------------------------------------------*/
/* I2C block read/write controls */


int iic_read (
BYTE dev, /* Device address */
UINT adr, /* Read start address */
UINT cnt, /* Read byte count */
void* buff /* Read data buffer */
)
{
// 12.09.10 RTCは固定時刻
return 1;
//BYTE *rbuff = buff;
//int n;
//
//
//if (!cnt) return 0;
//
//n = 10;
//do { /* Select device */
//iic_start();
//} while (!iic_send(dev) && --n);
//if (n) {
//if (iic_send((BYTE)adr)) { /* Set start address */
//iic_start(); /* Reselect device in read mode */
//if (iic_send(dev | 1)) {
//do { /* Receive data */
//cnt--;
//*rbuff++ = iic_rcvr(cnt ? 1 : 0);
//} while (cnt);
//}
//}
//}
//
//iic_stop(); /* Deselect device */
//
//return cnt ? 0 : 1;
}



int iic_write (
BYTE dev, /* Device address */
UINT adr, /* Write start address */
UINT cnt, /* Write byte count */
const void* buff /* Data to be written */
)
{
// 12.09.10 RTCは固定時刻
return 1;
//const BYTE *wbuff = buff;
//int n;
//
//
//if (!cnt) return 0;
//
//n = 10;
//do { /* Select device */
//iic_start();
//} while (!iic_send(dev) && --n);
//if (n) {
//if (iic_send((BYTE)adr)) { /* Set start address */
//do { /* Send data */
//if (!iic_send(*wbuff++)) break;
//} while (--cnt);
//}
//}
//
//iic_stop(); /* Deselect device */
//
//return cnt ? 0 : 1;
}



/*-------------------------------------------------*/
/* RTC functions */


int rtc_gettime (RTC *rtc)
{
// 12.09.10 RTCは固定時刻

//BYTE buf[8];

//if (!iic_read(0xD0, 0, 7, buf)) return 0;
//
//rtc->sec = (buf[0] & 0x0F) + ((buf[0] >> 4) & 7) * 10;
//rtc->min = (buf[1] & 0x0F) + (buf[1] >> 4) * 10;
//rtc->hour = (buf[2] & 0x0F) + ((buf[2] >> 4) & 3) * 10;
//rtc->wday = (buf[2] & 0x07);
//rtc->mday = (buf[4] & 0x0F) + ((buf[4] >> 4) & 3) * 10;
//rtc->month = (buf[5] & 0x0F) + ((buf[5] >> 4) & 1) * 10;
//rtc->year = 2000 + (buf[6] & 0x0F) + (buf[6] >> 4) * 10;

rtc->sec = 0;
rtc->min = 0;
rtc->hour = 0;
rtc->wday = 1;
rtc->mday = 1;
rtc->month = 1;
rtc->year = 2012;
return 1;
}




int rtc_settime (const RTC *rtc)
{
// 12.09.10 RTCは固定時刻
//BYTE buf[8];
//
//
//buf[0] = rtc->sec / 10 * 16 + rtc->sec % 10;
//buf[1] = rtc->min / 10 * 16 + rtc->min % 10;
//buf[2] = rtc->hour / 10 * 16 + rtc->hour % 10;
//buf[3] = rtc->wday & 7;
//buf[4] = rtc->mday / 10 * 16 + rtc->mday % 10;
//buf[5] = rtc->month / 10 * 16 + rtc->month % 10;
//buf[6] = (rtc->year - 2000) / 10 * 16 + (rtc->year - 2000) % 10;
//return iic_write(0xD0, 0, 7, buf);
return 1;
}




int rtc_init (void)
{
// 12.09.10 RTCは固定時刻
//BYTE buf[8]; /* RTC R/W buffer */
//UINT adr;
//
//
///* Read RTC registers */
//if (!iic_read(0xD0, 0, 8, buf)) return 0; /* IIC error */
//
//if (buf[7] & 0x20) { /* When data has been volatiled, set default time */
///* Clear nv-ram. Reg[8..63] */
//memset(buf, 0, 8);
//for (adr = 8; adr < 64; adr += 8)
//iic_write(0x0D, adr, 8, buf);
///* Reset time to Jan 1, '08. Reg[0..7] */
//buf[4] = 1; buf[5] = 1; buf[6] = 8;
//iic_write(0x0D, 0, 8, buf);
//}
return 1;
}

メインプログラム

/*
* main.c
*
* Created: 2012/09/23
* Author: YoshioNakamura
* 温度計測結果を無線で送り表示する。
* LCDの制御はChaNさんの "EZ-LCD - Generic control module for HD44780" LCDCを使用
* XBeeは「CQ出版超お手軽無線モジュールXBee」から引用
* SDカードへの書込はChaNさんのFatfsを使用
*/

#define F_CPU 1000000UL // 1MHzで動作

#include <avr/io.h>
#include <avr/interrupt.h>
#include <avr/sleep.h>
#include "hd44780.h"
#include <util/delay.h>

// ChaNさんのFatfs
#include <avr/pgmspace.h>
#include <string.h>
#include "xitoa.h"
#include "ff.h"
#include "diskio.h"
#include "rtc.h"


FUSES = {0xAF, 0xC3, 0xFF}; /* Fuse values: Low, High, Ext */
/* This is the fuse settings for this project. The fuse data will be output into the
hex file with program code. However some flash programmers may not support this
sort of hex files. If it is the case, use these values to program the fuse bits.
*/


DWORD AccSize; /* Work register for fs command */
WORD AccFiles, AccDirs;
FILINFO Finfo;
#if _USE_LFN
char Lfname[_MAX_LFN+1];
#endif

FATFS Fatfs[_VOLUMES]; /* File system object for each logical drive */
BYTE Buff[128]; /* Working buffer */

volatile WORD Timer; /* 100Hz increment timer */

#if _MULTI_PARTITION != 0
const PARTITION Drives[] = { {0,0}, {0,1} };
#endif


/*---------------------------------------------------------*/
/* User Provided Timer Function for FatFs module */
/*---------------------------------------------------------*/
/* This is a real time clock service to be called from */
/* FatFs module. Any valid time must be returned even if */
/* the system does not support a real time clock. */
/* This is not required in read-only configuration. */


DWORD get_fattime (void)
{
RTC rtc;


/* Get local time */
rtc_gettime(&rtc);

/* Pack date and time into a DWORD variable */
return ((DWORD)(rtc.year - 1980) << 25)
| ((DWORD)rtc.month << 21)
| ((DWORD)rtc.mday << 16)
| ((DWORD)rtc.hour << 11)
| ((DWORD)rtc.min << 5)
| ((DWORD)rtc.sec >> 1);
}
// Fatfs ここまで

// 時計
#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 2480 // レファレンス電圧
// 通信
#define RXBUF_SIZE 30 // 受信データ数
#define XBEE_REF 1200 // XBeeの温度データのレファレンス電圧
#define SAVE_NUM 23 // sdに保存するデータ数

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値を変換し表示
void XBeeTempDisp(void); // XBeeの温度データを変換して表示する。
void MakeSaveData(void); // XBeeの温度データおよび、時間をSDに保存

uint8_t g_u8BlinkFlg = 0; // 0:点灯、1:消灯
volatile uint8_t g_u8IntTimeFlg; // 時計割り込みフラグ

uint8_t g_u8CalenderBuf[6]; // 時計データ保存、データはBCDで保存。上位4bit:10の位、下位4bit:1の位
// sd にセーブするデータ23byte 月    日    時 分 温度1              温度2
uint8_t g_u8SDSaveData[] = {'0', '9', '2', '0', '0', '0', '0', '0', ';', '-', '-', '-', '-', '-', '-', ';', '-', '-', '-', '-', '-', '-', ','};

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[] = {0xb5, 0xdd, 0xc4, 0xde, '1', ':', '-', '-', '-', '-', '.', '-', 0xdf, 'C'};
// LCD表示データ格納バッファ


// 通信
uint8_t g_u8Count; // 受信データ数
uint8_t g_u8RxBuff[RXBUF_SIZE]; // 受信データ保存バッファ
union int_flg
{
uint8_t BYTE;
struct
{
uint8_t :6; // 余り
uint8_t time :1; // 時計割込
uint8_t rx7E :1; // 受信割込0x7e受信でON
}BIT;
};
union int_flg g_uIntFlg;

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:移動あり
uint8_t sum;

// Fatfs用
BYTE res; // Fatfsのリザルトコードを保存
UINT s2; // f_writeの書き込まれバイト数
FIL file; /* File object */
char loop_flg = 0; // 0:sd無し,1:mount = ok, 2:open = ok, 3:sdエラー(満杯等)
// Fatfs用ここまで

InitHard(); // ポート、LCD初期化
g_u8IntTimeFlg = 0; // 250ms毎にカウント
InitTime(); // 時計初期化および初期値表示

sei(); // 割り込み許可

while(1)
{
if( (PINB & 0x01) == 0) // SDカードが挿入されているか?
{
if ( loop_flg == 0) // SDカードが挿入されるとマウントする。
{
res = f_mount( 0, &Fatfs[0]); // マウント
_delay_ms(500); // 500msウエイト
loop_flg = 1; // マウントしたことを記憶
}
if ( loop_flg == 1) // マウントされていたらファイルをオープンする。
{
// 書込モード+既存のファイルを開くファイルがないときは新たにファイルをつくる
if ( f_open(&file, "120924a.txt", FA_WRITE | FA_OPEN_ALWAYS) == FR_OK)
// ファイルがオープンできたら
{
loop_flg = 2; // ファイルのオープンを記憶
PORTB &= ~(1 << PORTB7); // LEDを消灯
res = f_lseek(&file, f_size( &file)); // ファイルポインタをファイルの最後に移動
}
else // ファイルがオープンできないときはLEDを消灯しない
{
PORTB |= 1 << PORTB7;
loop_flg = 1;
}
}
}
else // sdカードが挿入されていないときはLEDを消灯しない
{
PORTB |= 1 << PORTB7;
loop_flg = 0;
}
sleep_enable(); //sleep許可
sleep_cpu(); //sleep中
sleep_disable(); // sleep禁止

if( (PINB & 0x01) != 0) // スリープから復帰したらsdカード挿入のチェックしSDカードが挿入されていないときはLEDを点灯させる。
{
PORTB |= 1 << PORTB7;
loop_flg = 0;
}

// 通信
if ( (g_uIntFlg.BIT.rx7E == 1) && (g_u8Count > 23))
{
if ( g_u8Count == 24) // 受信完了データ取得
{
g_uIntFlg.BIT.rx7E = 0;
XBeeTempDisp();

}
else // データエラー
{
g_uIntFlg.BIT.rx7E = 0;
g_u8Count = 0;
}
}
// 時計更新
if ( g_uIntFlg.BIT.time == 1)
{
g_uIntFlg.BIT.time = 0;
g_u8IntTimeFlg++;

if (g_u8IntTimeFlg > 3) // 一秒経過で時計更新および温度更新
{
UpdateTime(); // 時計更新
g_u8IntTimeFlg = 0;
DoADC(); // 1秒毎にADCを動作させ温度をLCDに表示する。

// sdカードに保存
if ( loop_flg == 2)
{
sum = g_u8CalenderBuf[4] & 0x0f;
if ( ((sum == 0) || (sum == 5)) && (g_u8CalenderBuf[5] == 0x30)) // 5分毎に温度計測データをSDに保存する。
{
MakeSaveData(); // 温度データは各計測時にg_u8SaveDataに保存し、ここでは保存時刻を保存する。
res = f_write( &file, g_u8SDSaveData, SAVE_NUM, &s2);
if ( s2 < SAVE_NUM) // SDカードがフルになったら一番古いデータから上書きする。
{
res = f_lseek( &file, 0);
}
f_sync( &file);
}
}

}

// 時計調整
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)
{
uint8_t i;

// port初期化
DDRB = 0b11000010;
PORTB = 0b10111101;
DDRC = 0x06; // portc2:1=出力
PORTC = 0;
DDRD = 0xfe; // portd7:1=出力
PORTD = 0;

// LCD初期化
lcd_init();

// 時計初期値
//InitTime();

// 温度1初期値
lcd_locate( 2, 0);
g_u8LcdStr[4] = '1';
for ( i = 0; i < 14; i++)
{
lcd_putc(g_u8LcdStr[i]);
}
// 温度2初期値
lcd_locate( 3, 0);
g_u8LcdStr[4] = '2';
for ( i = 0; i < 14; i++)
{
lcd_putc(g_u8LcdStr[i]);
}

// 16bitタイマ設定
// CTC動作、1/8分周 周期は250ms
OCR1A = 31686; // 31249 設定値
TCCR1A = (1 << COM1A0);
TCCR1B = 0x0a; // スタート
TIMSK1 = 1 << OCIE1A; // ctc割り込み許可

// timer2設定
// main clock = 1MHz
// 32分周で100Hzをつくる。
OCR2A = 155;
TCCR2A = 0x02; // CTC動作
TCCR2B = 0x03; // 32分周
TIMSK2 = 0x02; // OCRAと比較一致で割込

// ADC
ADMUX = 0; // Vref = 外部レファレンス
ADCSRA = (1 << ADPS1) | (1 << ADPS0); // adcクロック = 125kHz
DIDR0 = (1 << ADC0D); // adc0デジタル入力バッファ禁止
//UART 設定
// baurate 9600
// data 8
// parity non
// stop bit 1
UCSR0A = 1 << U2X0;
UCSR0C = (1 << UCSZ01) | (1 << UCSZ00);
UBRR0L = 12;
UCSR0B = (1 << RXCIE0) | (1 << RXEN0);

rtc_init(); // Initialize RTC
// スリープモードはアイドルに設定
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の正常範囲チェック
g_u8LcdStr[4] = '1';

if ( (sum > 1800) || ( sum < -400))
{
for ( i = 6; i < 10; i++)
{
g_u8LcdStr[i] = '-';
}
g_u8LcdStr[11] = '-';
}
else
{
if ( sum < 0)
{
sum *= -1;
g_u8LcdStr[6] = '-';
}
else
{
g_u8LcdStr[6] = 0x20;
}
g_u8LcdStr[7] = 0x20;
g_u8LcdStr[8] = 0x20;
g_u8LcdStr[9] = 0x30;
g_u8LcdStr[11] = 0x30;

// binary -> BCDキャラクタ変換
for ( i = 11; i > 5; i--)
{
g_u8LcdStr[i] = (sum % 10) + 0x30;
sum /= 10;
if ( i == 11)
{
i--;
}
if ( sum <= 0)
{
break;
}
}
}

for ( i = 6; i < 12; i++) // 温度1を取り出す。
{
g_u8SDSaveData[i + 3] = g_u8LcdStr[i];
}
g_u8SDSaveData[15] = ';'; // データの区切り
// LCDに表示
lcd_locate( 2, 0);
for ( i = 0; i < 14; i++)
{
lcd_putc(g_u8LcdStr[i]);
}
}


void XBeeTempDisp(void) // XBeeの温度データを変換して表示する。
{
int16_t i;
long sum;

g_u8LcdStr[4] = '2';
sum =(g_u8RxBuff[22] << 8) | g_u8RxBuff[23];
sum = (sum * XBEE_REF/ 1024) - 600;


if ( (sum > 1800) || ( sum < -400))
{
for ( i = 6; i < 10; i++)
{
g_u8LcdStr[i] = '-';

}
g_u8LcdStr[11] = '-';
}
else
{
if ( sum < 0)
{
sum *= -1;
g_u8LcdStr[6] = '-';
}
else
{
g_u8LcdStr[6] = 0x20;
}
g_u8LcdStr[7] = 0x20;
g_u8LcdStr[8] = 0x20;
g_u8LcdStr[9] = 0x30;
g_u8LcdStr[11] = 0x30;

// binary -> BCDキャラクタ変換
for ( i = 11; i > 5; i--)
{
g_u8LcdStr[i] = (sum % 10) + 0x30;
sum /= 10;
if ( i == 11)
{
i--;
}
if ( sum <= 0)
{
break;
}
}
}
for ( i = 6; i < 12; i++) // 温度2を取り出す。
{
g_u8SDSaveData[i + 10] = g_u8LcdStr[i];
}
g_u8SDSaveData[22] = ','; // データの区切り
// LCDに表示
lcd_locate( 3, 0);
for ( i = 0; i < 14; i++)
{
lcd_putc(g_u8LcdStr[i]);
}
}


void BlinkDisp(uint8_t dat, uint8_t flg) //指定位置から2文字分ブリンクする。
{
uint8_t 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);
}

void MakeSaveData(void) // 記録時間を時間をg_u8SDSaveDataに記憶する。
{
char i;

for ( i = 1; i < 5; i++) // 月日時分変換
{
g_u8SDSaveData[i * 2 - 2] = (g_u8CalenderBuf[i] >> 4) + 0x30;
g_u8SDSaveData[i * 2 - 1] = (g_u8CalenderBuf[i] & 0x0f) + 0x30;
}
g_u8SDSaveData[8] = ';'; // 日付と温度の区切り
}

SIGNAL(TIMER1_COMPA_vect) // 0.25秒ごとに割込発生
{
g_uIntFlg.BIT.time = 1;
}

SIGNAL(USART_RX_vect) // 受信割込、データ受信完了はメインループでg_u8Count= 24で判断する。
{
if ( UDR0 == 0x7e) // 0x7eで受信開始
{
g_uIntFlg.BIT.rx7E = 1;
g_u8Count = 0;
}
if ( g_uIntFlg.BIT.rx7E == 1) // UDR0から受信バッファにコピー
{
g_u8RxBuff[g_u8Count++] = UDR0;
}
}

// 12.09.23 Fatfsで使用
/*---------------------------------------------------------*/
/* 100Hz timer interrupt generated by OC2 */
/*---------------------------------------------------------*/

SIGNAL(TIMER2_COMPA_vect)
{
Timer++; /* Performance counter for this module */
disk_timerproc(); /* Drive timer procedure of low level disk I/O module */
}