RTCサンプル
をテンプレートにして作成
[
トップ
] [
新規
|
一覧
|
単語検索
|
最終更新
|
ヘルプ
]
開始行:
#analog
#norelated
#contents
* RTCサンプル [#o86fce4c]
RTCモジュールとI2Cで通信して時計を作る。~
I2Cは前回よくわかってないって書いたけど、前よりはちょっと...
USARTは、2本の線を使って、1本が送信、1本が受信の役割をす...
複数の機器は、それぞれがマスターにもスレーブにもなれて、...
AVRと[[秋月のRTCモジュール:http://akizukidenshi.com/catal...
** 回路図 [#s17638b1]
前回書いたように、UART(USART)はRXD(PD0)とTXD(PD1)をクロス...
SCLとSDAは、RTCモジュールのジャンパをショートすれば、内部...
プルアップ抵抗値の決め方とかも、理由があるんだろうけど、...
#ref(RtcTest.png,left,nowrap,RTCサンプル)
** ソースコード [#lfaf1c0f]
今回、RTC-8564NBモジュールを使った時計表示を行うために、L...
LCDライブラリは、カーソル表示とカーソルブリンクのON/OFFを...
USARTライブラリは、関数ポインタを登録することで、受信完了...
&ref(LcdLib.h,left,nowrap,LCDライブラリヘッダ);&ref(LcdLi...
今回は長くなるので、分割しながら載せていきます。~
プログラムサイズは、sprintf使用で4.8kbくらいです。~
*** 定義部分ソースコード [#b78da7d4]
#include <stdio.h>
#include <avr/interrupt.h>
#include "LcdLib.h"
#include "UsartLib.h"
#include "util.h"
#define RTC_ADDR_WRITE 0xA2 // RTC書き込みアドレス
#define RTC_ADDR_READ 0xA3 // RTC読み出しアドレス
#define I2C_BPS 2 // ボーレート設定値
// TWIモード列挙型定義
typedef enum _tagTWIMode{
TWI_MODE_SEND, // 送信モード
TWI_MODE_RECEIVE, // 受信モード
} TWIMode;
// TWIアクノリッジモード列挙型定義
typedef enum _tagTWIACKMode{
TWI_ACKMODE_ACK, // ACK送信モード
TWI_ACKMODE_NACK, // NACK送信モード
} TWIACKMode;
// USART受信バッファ
#define USART_BUFF_SIZE 16
char usart_buff[USART_BUFF_SIZE];
uint8_t usart_buff_cnt;
まずは、RTCの書き込みと読み出しをするためのI2Cスレーブア...
これは、RTCの[[アプリケーションマニュアル:http://www.epso...
次は、RTCとI2C通信をするための、ビットレートの設定。~
RTC-8564NBモジュールは、最大で400kHzでの通信に対応してい...
しかし、AVR側のTWI設定は、最大でもCPUクロックの1/16でしか...
現在AVRは1MHz駆動で動かしているので、最大で62.5kHzでの通...
なので、ここはきりのいい50kHzで通信することにし、AVRのTWI...
TWBRレジスタ値 = ( 1000000(CPU周波数) / 50000(SCLクロッ...
というわけで、設定する値は2。~
次はTWIモード定義。~
TWIには、マスター動作の送信/受信と、スレーブ動作の送信/受...
今回はマスター動作しかしないので、とりあえず送信/受信を切...
TWIアクノリッジモードは、データ受信時に、相手に返す応答の...
応答でACKを返すと、「正しく受け取ったので次を送ってくださ...
まだこのRTCモジュールしか知らないので、他のI2C通信機器も...
USART受信バッファは、PCから入力されたデータを一旦保存して...
ここにデータをためておいて、Enter入力時にバッファ内のデー...
*** I2C(TWI)部分ソースコード [#e1945366]
void I2c_init( void )
{
TWSR &= ~0x03; // BPSの前置分周は等倍にする
TWBR = I2C_BPS; // CPUクロックが1MHzなのに対して、50KH...
TWCR = 0b00000100; // ピンをTWI(SCL/SDA)として使用する
}
初期化部分。~
プリスケーラの等倍設定と、ビットレート指定と、PC4とPC5をS...
int8_t I2c_start( void )
{
TWCR = 0b10100100; // スタート設定
while( !( TWCR & 0b10000000 )){
; // 送信開始OKになるまで待つ
}
if( 0x08 == ( TWSR & 0xF8 )){
return 0;
} else if( 0x10 == ( TWSR & 0xF8 )){
return 1;
}
return -1;
}
マスターとしての、通信開始部分です。~
スレーブモードだとスタートはいらないのかな?~
とりあえずはまだマスター動作しかよくわかっていないので、...
基本的に、今のAVRとRTCが1対1の作りで、通信に失敗するよう...
失敗したことがないので、この作りが正しいのかどうかはよく...
int8_t I2c_setaddr( uint8_t addr, TWIMode mode )
{
TWDR = addr;
TWCR = 0b10000100; // 送信設定
while( !( TWCR & 0b10000000 )){
; // 相手の応答まで待つ
}
if( TWI_MODE_SEND == mode ){
if( 0x18 == ( TWSR & 0xF8 )){
return 0;
} else if( 0x20 == ( TWSR & 0xF8 )){
return 1;
}
} else {
if( 0x40 == ( TWSR & 0xF8 )){
return 0;
} else if( 0x48 == ( TWSR & 0xF8 )){
return 1;
}
}
return -1;
}
通信相手を指定するための、スレーブアドレス送信処理です。~
この処理については、送信モードと受信モードで処理後のステ...
int8_t I2c_snddata( uint8_t data )
{
TWDR = data;
TWCR = 0b10000100; // 送信設定
while( !( TWCR & 0b10000000 )){
; // 相手の応答まで待つ
}
if( 0x28 == ( TWSR & 0xF8 )){
return 0;
} else if( 0x30 == ( TWSR & 0xF8 )){
return 1;
}
return -1;
}
データの送信処理です。~
int8_t I2c_rcvdata( uint8_t *data, TWIACKMode mode )
{
if( TWI_ACKMODE_ACK == mode ){
TWCR = 0b11000100;
} else {
TWCR = 0b10000100;
}
while( !( TWCR & 0b10000000 )){
; // データを受信するまで待つ
}
if( 0x50 == ( TWSR & 0xF8 )){
*data = TWDR;
return 0;
} else if( 0x58 == ( TWSR & 0xF8 )){
*data = TWDR;
return 1;
}
return -1;
}
データの受信処理です。~
受信後に相手に返すアクノリッジモードを引数で指定するよう...
void I2c_stop( void )
{
TWCR = 0b10010100; // ストップ設定
}
I2C通信の停止処理です。~
値の設定だけ。~
*** RTC部分ソースコード [#u40a33a4]
uint8_t Rtc_setdata( uint8_t start, uint8_t *data, uint8...
{
uint8_t i = 0; // ループ変数
if( 16 < start + num || NULL == data ) return -1;
while( 1 ){
// 書き込みアドレス設定
if( 0 > I2c_start() ){
continue;
}
if( 0 != I2c_setaddr( RTC_ADDR_WRITE, TWI_MODE_SEND )){
continue;
}
if( 0 != I2c_snddata( start )){
continue;
}
// データ書き込み
for( i = 0; i < num; i++ ){
if( 0 > I2c_snddata( data[i] )) break;
}
I2c_stop();
break;
}
return 0;
}
RTCにデータを送信する部分です。~
RTCにデータを送ると、RTCでは内部のアドレスを自動的にイン...
なので、引数には、スタートアドレスとデータの先頭ポインタ...
このあたりの、書き込み読み出しの手順も[[アプリケーション...
uint8_t Rtc_getdata( uint8_t start, uint8_t *data, uint8...
{
uint8_t i = 0; // ループ変数
if( 16 < start + num || NULL == data ) return -1;
while( 1 ){
// 読み出しアドレス設定
if( 0 > I2c_start() ){
continue;
}
if( 0 != I2c_setaddr( RTC_ADDR_WRITE, TWI_MODE_SEND )){
continue;
}
if( 0 != I2c_snddata( start )){
continue;
}
// データ読み出し
if( 0 > I2c_start() ){
continue;
}
if( 0 != I2c_setaddr( RTC_ADDR_READ, TWI_MODE_RECEIVE ...
continue;
}
for( i = 0; i < num - 1; i++ ){
if( 0 > I2c_rcvdata( &data[i], TWI_ACKMODE_ACK )) bre...
}
if( num - 1 != i ){
continue;
}
if( 0 > I2c_rcvdata( &data[i], TWI_ACKMODE_NACK )){
continue;
}
I2c_stop();
break;
}
return 0;
}
RTCからデータを受信する部分です。~
送信と同じく、RTCでは内部のアドレスを自動的にインクリメン...
スタートしてから、書き込みモードでアドレス指定して、読み...
最後のデータを受信する時には、もうこれ以上データを送らな...
void Rtc_allread( uint8_t *data )
{
Rtc_getdata( 0, data, 16 );
}
全データ(16個のRTCのレジスタ)を読み出すためのラッパー関数...
void Rtc_allwrite( uint8_t *data )
{
Rtc_setdata( 0, data, 16 );
}
全データ(16個のRTCのレジスタ)を書き込むためのラッパー関数...
void Rtc_init( uint8_t *data )
{
Rtc_allread( data ); // 全データ読み込み
// 初期データ設定
data[0] = 0;
data[1] = 0;
data[2] = data[2] & ~0x80;
data[3] = data[3] & ~0x80;
data[4] = data[4] & ~0xC0;
data[5] = data[5] & ~0xC0;
data[6] = data[6] & ~0xF8;
data[7] = data[7] & ~0xE0;
// data[8] = data[8];
data[9] = data[9] & 0x80;
data[10] = ( data[10] & ~0xC0 ) | 0x80;
data[11] = ( data[11] & ~0xC0 ) | 0x80;
data[12] = ( data[12] & ~0xF8 ) | 0x80;
data[13] = 0;
data[14] = 0;
data[15] = 0;
Rtc_allwrite( data ); // 全データ書き込み
}
RTCの初期化関数です。~
全データをバッファに読み込み、不要なビットを落として、設...
ここでいう初期化と言うのは、有効な日時を設定するという意...
uint8_t Rtc_settime( char* time )
{
uint8_t i = 0; // ループ変数
uint8_t data[7]; // 設定データ
uint16_t year = 0; // 年(数字格納用)
uint16_t month = 0; // 月(数字格納用)
uint16_t day = 0; // 日(数字格納用)
uint8_t uitmp = 0; // 数字格納用
// 入力チェック
for( i = 0; i < 14; i++ ){
if( '0' > time[i] || '9' < time[i] ){
return -1; // 数字以外のデータが混ざっているので失敗
}
}
// 現在データ取得
if( 0 > Rtc_getdata( 2, data, 7 )){
return -1;
}
// 年のチェック
year = ( time[0] - '0' ) * 1000 + ( time[1] - '0' ) * 1...
data[6] = (( time[2] - '0' ) << 4 ) | ( time[3] - '0' );
// 月のチェック
month = ( time[4] - '0' ) * 10 + ( time[5] - '0' );
if( 12 < month ){
return -1;
}
data[5] &= ~0x7f;
data[5] |= (( time[4] - '0' ) << 4 ) | ( time[5] - '0' );
// 日のチェック
day = ( time[6] - '0' ) * 10 + ( time[7] - '0' );
if( 31 < day ){
return -1;
}
data[3] = (( time[6] - '0' ) << 4 ) | ( time[7] - '0' );
// 曜日の計算(ツェラーの公式)
data[4] = ( year + ( year >> 2 ) - year / 100 + year / ...
// 時のチェック
uitmp = ( time[8] - '0' ) * 10 + ( time[9] - '0' );
if( 24 < uitmp ){
return -1;
}
data[2] = (( time[8] - '0' ) << 4 ) | ( time[9] - '0' );
// 分のチェック
uitmp = ( time[10] - '0' ) * 10 + ( time[11] - '0' );
if( 59 < uitmp ){
return -1;
}
data[1] = (( time[10] - '0' ) << 4 ) | ( time[11] - '0'...
// 秒のチェック
uitmp = ( time[12] - '0' ) * 10 + ( time[13] - '0' );
if( 59 < uitmp ){
return -1;
}
data[0] &= ~0x7f;
data[0] |= (( time[12] - '0' ) << 4 ) | ( time[13] - '0...
// 現在時刻設定
if( 0 > Rtc_setdata( 2, data, 7 )){
return -1;
}
return 0;
}
RTCに入力されたデータを設定する関数です。~
一度読み出しを行っているのは、設定する箇所以外のビットは...
(秒レジスタのVLビットとか、月レジスタの世紀ビットとか、っ...
ふと気づいたけど、本当は最後のRTCにデータを設定する前に、...
*** main動作部分ソースコード [#m38fad0d]
void usart_buff_init( void )
{
uint8_t i = 0; // ループ変数
usart_buff_cnt = 0;
for( i = 0; i < USART_BUFF_SIZE; i++ ){
usart_buff[i] = 0;
}
}
PCからの入力を保持しておくグローバルバッファを初期化しま...
void usart_rcv_callback( char data )
{
if( 0x2e == data ){
// 入力を間違った時のリセット「.」
usart_buff_init();
Usart_sndstr( usart_buff );
return;
}
if( 0x0d == data ){
// ターミネータの処理
Rtc_settime( usart_buff );
usart_buff_init();
Usart_sndstr( usart_buff );
return;
}
usart_buff[usart_buff_cnt] = data;
usart_buff_cnt++;
if( USART_BUFF_SIZE <= usart_buff_cnt ){
// オーバーフロー時はターミネータが来るまで最終バッフ...
usart_buff_cnt = USART_BUFF_SIZE - 1;
}
Usart_sndstr( usart_buff );
}
PCからの入力があった時、割り込みで呼ばれる関数です。~
USARTの受信完了割り込みで呼ばれます。~
Usart_init関数の引数にこの関数のポインタを指定することで...
PCから「.」(0x2e)が入力された時には、グローバルバッファを...
void print_time( uint8_t *data )
{
uint8_t sec = data[2];
uint8_t min = data[3];
uint8_t hour = data[4];
uint8_t day = data[5];
uint8_t week = data[6];
uint8_t month = data[7];
uint8_t year = data[8];
char date[17];
char weekstr[7][4] = {
"Sun",
"Mon",
"Tue",
"Wed",
"Thr",
"Fri",
"Sat",
};
sec = (( sec >> 4 ) & 0x07 ) * 10 + ( sec & 0x0F );
min = (( min >> 4 ) & 0x07 ) * 10 + ( min & 0x0F );
hour = (( hour >> 4 ) & 0x03 ) * 10 + ( hour & 0x0F );
day = (( day >> 4 ) & 0x03 ) * 10 + ( day & 0x0F );
week = week & 0x07;
month = (( month >> 4 ) & 0x01 ) * 10 + ( month & 0x0F );
year = (( year >> 4 ) & 0x0F ) * 10 + ( year & 0x0F );
sprintf( date, "%02d/%02d/%02d %s", year, month, day, w...
Lcd_setpos( 0, 0 );
Lcd_setstr( date );
sprintf( date, "%02d:%02d:%02d", hour, min, sec );
Lcd_setpos( 1, 0 );
Lcd_setstr( date );
}
BCD形式の入力を、10進数に変換して、sprintfで書式化してか...
上段に年月日と曜日、下段に時分秒の表示です。~
int main( void )
{
uint8_t data[16]; // 受信データ格納用
// uint8_t i = 0; // ループ変数
// char str[5] = " "; // 16進コード格納用
usart_buff_init(); // 受信バッファ初期化
Lcd_init( 0 ); // LCD初期化
Usart_init( usart_rcv_callback, NULL ); // シリアル通信...
I2c_init(); // I2C通信初期化
Rtc_init( data ); // RTCモジュール初期化
sei(); // 割り込み許可
while( 1 ){
Rtc_allread( data ); // 全データ読み込み
print_time( data );
/* Lcd_setpos( 0, 0 );
for( i = 0; i < 8; i++ ){
sprintf( str, "%02X", data[i] );
Lcd_setstr( str );
}
Lcd_setpos( 1, 0 );
for( i = 8; i < 16; i++ ){
sprintf( str, "%02X", data[i] );
Lcd_setstr( str );
}*/
delay_ms( 100 );
}
return 0;
}
main関数ですが、タイマー割り込み等を使用しているわけでは...
せっかくだから、RTCからクロック出力をもらって、その割り込...
バッテリーバックアップ等も含めて、更新契機は今後の課題で...
** 基本動作 [#k26822dd]
まず、電源を入れてPCとUSB通信ができるようにします。~
この辺は、前回の[[USBシリアル通信サンプル]]を参照のこと。~
繋がったら、PCから設定したい日時を、以下のように打ち込ん...
20100418123456
この例では、2010/04/18の、12:34:56に設定されるわけです。~
1回キーを押す度に、その時点のバッファの内容がエコーバック...
間違ったキーを入力してしまった場合、「.」(ピリオド)の入力...
設定した時刻は、LCDに二段に分かれて表示されます。~
上段は、日付と曜日、下段は時刻。~
曜日は、PCからの入力時に、[[ツェラーの公式:http://www.goo...
PCから入力した西暦の1000の位と100の位は、この曜日計算にし...
RTCモジュールは、西暦の下2桁しか気にしないので、RTC内にも...
なので、表示時にも下2桁しか表示出来ない仕様となります。~
終了行:
#analog
#norelated
#contents
* RTCサンプル [#o86fce4c]
RTCモジュールとI2Cで通信して時計を作る。~
I2Cは前回よくわかってないって書いたけど、前よりはちょっと...
USARTは、2本の線を使って、1本が送信、1本が受信の役割をす...
複数の機器は、それぞれがマスターにもスレーブにもなれて、...
AVRと[[秋月のRTCモジュール:http://akizukidenshi.com/catal...
** 回路図 [#s17638b1]
前回書いたように、UART(USART)はRXD(PD0)とTXD(PD1)をクロス...
SCLとSDAは、RTCモジュールのジャンパをショートすれば、内部...
プルアップ抵抗値の決め方とかも、理由があるんだろうけど、...
#ref(RtcTest.png,left,nowrap,RTCサンプル)
** ソースコード [#lfaf1c0f]
今回、RTC-8564NBモジュールを使った時計表示を行うために、L...
LCDライブラリは、カーソル表示とカーソルブリンクのON/OFFを...
USARTライブラリは、関数ポインタを登録することで、受信完了...
&ref(LcdLib.h,left,nowrap,LCDライブラリヘッダ);&ref(LcdLi...
今回は長くなるので、分割しながら載せていきます。~
プログラムサイズは、sprintf使用で4.8kbくらいです。~
*** 定義部分ソースコード [#b78da7d4]
#include <stdio.h>
#include <avr/interrupt.h>
#include "LcdLib.h"
#include "UsartLib.h"
#include "util.h"
#define RTC_ADDR_WRITE 0xA2 // RTC書き込みアドレス
#define RTC_ADDR_READ 0xA3 // RTC読み出しアドレス
#define I2C_BPS 2 // ボーレート設定値
// TWIモード列挙型定義
typedef enum _tagTWIMode{
TWI_MODE_SEND, // 送信モード
TWI_MODE_RECEIVE, // 受信モード
} TWIMode;
// TWIアクノリッジモード列挙型定義
typedef enum _tagTWIACKMode{
TWI_ACKMODE_ACK, // ACK送信モード
TWI_ACKMODE_NACK, // NACK送信モード
} TWIACKMode;
// USART受信バッファ
#define USART_BUFF_SIZE 16
char usart_buff[USART_BUFF_SIZE];
uint8_t usart_buff_cnt;
まずは、RTCの書き込みと読み出しをするためのI2Cスレーブア...
これは、RTCの[[アプリケーションマニュアル:http://www.epso...
次は、RTCとI2C通信をするための、ビットレートの設定。~
RTC-8564NBモジュールは、最大で400kHzでの通信に対応してい...
しかし、AVR側のTWI設定は、最大でもCPUクロックの1/16でしか...
現在AVRは1MHz駆動で動かしているので、最大で62.5kHzでの通...
なので、ここはきりのいい50kHzで通信することにし、AVRのTWI...
TWBRレジスタ値 = ( 1000000(CPU周波数) / 50000(SCLクロッ...
というわけで、設定する値は2。~
次はTWIモード定義。~
TWIには、マスター動作の送信/受信と、スレーブ動作の送信/受...
今回はマスター動作しかしないので、とりあえず送信/受信を切...
TWIアクノリッジモードは、データ受信時に、相手に返す応答の...
応答でACKを返すと、「正しく受け取ったので次を送ってくださ...
まだこのRTCモジュールしか知らないので、他のI2C通信機器も...
USART受信バッファは、PCから入力されたデータを一旦保存して...
ここにデータをためておいて、Enter入力時にバッファ内のデー...
*** I2C(TWI)部分ソースコード [#e1945366]
void I2c_init( void )
{
TWSR &= ~0x03; // BPSの前置分周は等倍にする
TWBR = I2C_BPS; // CPUクロックが1MHzなのに対して、50KH...
TWCR = 0b00000100; // ピンをTWI(SCL/SDA)として使用する
}
初期化部分。~
プリスケーラの等倍設定と、ビットレート指定と、PC4とPC5をS...
int8_t I2c_start( void )
{
TWCR = 0b10100100; // スタート設定
while( !( TWCR & 0b10000000 )){
; // 送信開始OKになるまで待つ
}
if( 0x08 == ( TWSR & 0xF8 )){
return 0;
} else if( 0x10 == ( TWSR & 0xF8 )){
return 1;
}
return -1;
}
マスターとしての、通信開始部分です。~
スレーブモードだとスタートはいらないのかな?~
とりあえずはまだマスター動作しかよくわかっていないので、...
基本的に、今のAVRとRTCが1対1の作りで、通信に失敗するよう...
失敗したことがないので、この作りが正しいのかどうかはよく...
int8_t I2c_setaddr( uint8_t addr, TWIMode mode )
{
TWDR = addr;
TWCR = 0b10000100; // 送信設定
while( !( TWCR & 0b10000000 )){
; // 相手の応答まで待つ
}
if( TWI_MODE_SEND == mode ){
if( 0x18 == ( TWSR & 0xF8 )){
return 0;
} else if( 0x20 == ( TWSR & 0xF8 )){
return 1;
}
} else {
if( 0x40 == ( TWSR & 0xF8 )){
return 0;
} else if( 0x48 == ( TWSR & 0xF8 )){
return 1;
}
}
return -1;
}
通信相手を指定するための、スレーブアドレス送信処理です。~
この処理については、送信モードと受信モードで処理後のステ...
int8_t I2c_snddata( uint8_t data )
{
TWDR = data;
TWCR = 0b10000100; // 送信設定
while( !( TWCR & 0b10000000 )){
; // 相手の応答まで待つ
}
if( 0x28 == ( TWSR & 0xF8 )){
return 0;
} else if( 0x30 == ( TWSR & 0xF8 )){
return 1;
}
return -1;
}
データの送信処理です。~
int8_t I2c_rcvdata( uint8_t *data, TWIACKMode mode )
{
if( TWI_ACKMODE_ACK == mode ){
TWCR = 0b11000100;
} else {
TWCR = 0b10000100;
}
while( !( TWCR & 0b10000000 )){
; // データを受信するまで待つ
}
if( 0x50 == ( TWSR & 0xF8 )){
*data = TWDR;
return 0;
} else if( 0x58 == ( TWSR & 0xF8 )){
*data = TWDR;
return 1;
}
return -1;
}
データの受信処理です。~
受信後に相手に返すアクノリッジモードを引数で指定するよう...
void I2c_stop( void )
{
TWCR = 0b10010100; // ストップ設定
}
I2C通信の停止処理です。~
値の設定だけ。~
*** RTC部分ソースコード [#u40a33a4]
uint8_t Rtc_setdata( uint8_t start, uint8_t *data, uint8...
{
uint8_t i = 0; // ループ変数
if( 16 < start + num || NULL == data ) return -1;
while( 1 ){
// 書き込みアドレス設定
if( 0 > I2c_start() ){
continue;
}
if( 0 != I2c_setaddr( RTC_ADDR_WRITE, TWI_MODE_SEND )){
continue;
}
if( 0 != I2c_snddata( start )){
continue;
}
// データ書き込み
for( i = 0; i < num; i++ ){
if( 0 > I2c_snddata( data[i] )) break;
}
I2c_stop();
break;
}
return 0;
}
RTCにデータを送信する部分です。~
RTCにデータを送ると、RTCでは内部のアドレスを自動的にイン...
なので、引数には、スタートアドレスとデータの先頭ポインタ...
このあたりの、書き込み読み出しの手順も[[アプリケーション...
uint8_t Rtc_getdata( uint8_t start, uint8_t *data, uint8...
{
uint8_t i = 0; // ループ変数
if( 16 < start + num || NULL == data ) return -1;
while( 1 ){
// 読み出しアドレス設定
if( 0 > I2c_start() ){
continue;
}
if( 0 != I2c_setaddr( RTC_ADDR_WRITE, TWI_MODE_SEND )){
continue;
}
if( 0 != I2c_snddata( start )){
continue;
}
// データ読み出し
if( 0 > I2c_start() ){
continue;
}
if( 0 != I2c_setaddr( RTC_ADDR_READ, TWI_MODE_RECEIVE ...
continue;
}
for( i = 0; i < num - 1; i++ ){
if( 0 > I2c_rcvdata( &data[i], TWI_ACKMODE_ACK )) bre...
}
if( num - 1 != i ){
continue;
}
if( 0 > I2c_rcvdata( &data[i], TWI_ACKMODE_NACK )){
continue;
}
I2c_stop();
break;
}
return 0;
}
RTCからデータを受信する部分です。~
送信と同じく、RTCでは内部のアドレスを自動的にインクリメン...
スタートしてから、書き込みモードでアドレス指定して、読み...
最後のデータを受信する時には、もうこれ以上データを送らな...
void Rtc_allread( uint8_t *data )
{
Rtc_getdata( 0, data, 16 );
}
全データ(16個のRTCのレジスタ)を読み出すためのラッパー関数...
void Rtc_allwrite( uint8_t *data )
{
Rtc_setdata( 0, data, 16 );
}
全データ(16個のRTCのレジスタ)を書き込むためのラッパー関数...
void Rtc_init( uint8_t *data )
{
Rtc_allread( data ); // 全データ読み込み
// 初期データ設定
data[0] = 0;
data[1] = 0;
data[2] = data[2] & ~0x80;
data[3] = data[3] & ~0x80;
data[4] = data[4] & ~0xC0;
data[5] = data[5] & ~0xC0;
data[6] = data[6] & ~0xF8;
data[7] = data[7] & ~0xE0;
// data[8] = data[8];
data[9] = data[9] & 0x80;
data[10] = ( data[10] & ~0xC0 ) | 0x80;
data[11] = ( data[11] & ~0xC0 ) | 0x80;
data[12] = ( data[12] & ~0xF8 ) | 0x80;
data[13] = 0;
data[14] = 0;
data[15] = 0;
Rtc_allwrite( data ); // 全データ書き込み
}
RTCの初期化関数です。~
全データをバッファに読み込み、不要なビットを落として、設...
ここでいう初期化と言うのは、有効な日時を設定するという意...
uint8_t Rtc_settime( char* time )
{
uint8_t i = 0; // ループ変数
uint8_t data[7]; // 設定データ
uint16_t year = 0; // 年(数字格納用)
uint16_t month = 0; // 月(数字格納用)
uint16_t day = 0; // 日(数字格納用)
uint8_t uitmp = 0; // 数字格納用
// 入力チェック
for( i = 0; i < 14; i++ ){
if( '0' > time[i] || '9' < time[i] ){
return -1; // 数字以外のデータが混ざっているので失敗
}
}
// 現在データ取得
if( 0 > Rtc_getdata( 2, data, 7 )){
return -1;
}
// 年のチェック
year = ( time[0] - '0' ) * 1000 + ( time[1] - '0' ) * 1...
data[6] = (( time[2] - '0' ) << 4 ) | ( time[3] - '0' );
// 月のチェック
month = ( time[4] - '0' ) * 10 + ( time[5] - '0' );
if( 12 < month ){
return -1;
}
data[5] &= ~0x7f;
data[5] |= (( time[4] - '0' ) << 4 ) | ( time[5] - '0' );
// 日のチェック
day = ( time[6] - '0' ) * 10 + ( time[7] - '0' );
if( 31 < day ){
return -1;
}
data[3] = (( time[6] - '0' ) << 4 ) | ( time[7] - '0' );
// 曜日の計算(ツェラーの公式)
data[4] = ( year + ( year >> 2 ) - year / 100 + year / ...
// 時のチェック
uitmp = ( time[8] - '0' ) * 10 + ( time[9] - '0' );
if( 24 < uitmp ){
return -1;
}
data[2] = (( time[8] - '0' ) << 4 ) | ( time[9] - '0' );
// 分のチェック
uitmp = ( time[10] - '0' ) * 10 + ( time[11] - '0' );
if( 59 < uitmp ){
return -1;
}
data[1] = (( time[10] - '0' ) << 4 ) | ( time[11] - '0'...
// 秒のチェック
uitmp = ( time[12] - '0' ) * 10 + ( time[13] - '0' );
if( 59 < uitmp ){
return -1;
}
data[0] &= ~0x7f;
data[0] |= (( time[12] - '0' ) << 4 ) | ( time[13] - '0...
// 現在時刻設定
if( 0 > Rtc_setdata( 2, data, 7 )){
return -1;
}
return 0;
}
RTCに入力されたデータを設定する関数です。~
一度読み出しを行っているのは、設定する箇所以外のビットは...
(秒レジスタのVLビットとか、月レジスタの世紀ビットとか、っ...
ふと気づいたけど、本当は最後のRTCにデータを設定する前に、...
*** main動作部分ソースコード [#m38fad0d]
void usart_buff_init( void )
{
uint8_t i = 0; // ループ変数
usart_buff_cnt = 0;
for( i = 0; i < USART_BUFF_SIZE; i++ ){
usart_buff[i] = 0;
}
}
PCからの入力を保持しておくグローバルバッファを初期化しま...
void usart_rcv_callback( char data )
{
if( 0x2e == data ){
// 入力を間違った時のリセット「.」
usart_buff_init();
Usart_sndstr( usart_buff );
return;
}
if( 0x0d == data ){
// ターミネータの処理
Rtc_settime( usart_buff );
usart_buff_init();
Usart_sndstr( usart_buff );
return;
}
usart_buff[usart_buff_cnt] = data;
usart_buff_cnt++;
if( USART_BUFF_SIZE <= usart_buff_cnt ){
// オーバーフロー時はターミネータが来るまで最終バッフ...
usart_buff_cnt = USART_BUFF_SIZE - 1;
}
Usart_sndstr( usart_buff );
}
PCからの入力があった時、割り込みで呼ばれる関数です。~
USARTの受信完了割り込みで呼ばれます。~
Usart_init関数の引数にこの関数のポインタを指定することで...
PCから「.」(0x2e)が入力された時には、グローバルバッファを...
void print_time( uint8_t *data )
{
uint8_t sec = data[2];
uint8_t min = data[3];
uint8_t hour = data[4];
uint8_t day = data[5];
uint8_t week = data[6];
uint8_t month = data[7];
uint8_t year = data[8];
char date[17];
char weekstr[7][4] = {
"Sun",
"Mon",
"Tue",
"Wed",
"Thr",
"Fri",
"Sat",
};
sec = (( sec >> 4 ) & 0x07 ) * 10 + ( sec & 0x0F );
min = (( min >> 4 ) & 0x07 ) * 10 + ( min & 0x0F );
hour = (( hour >> 4 ) & 0x03 ) * 10 + ( hour & 0x0F );
day = (( day >> 4 ) & 0x03 ) * 10 + ( day & 0x0F );
week = week & 0x07;
month = (( month >> 4 ) & 0x01 ) * 10 + ( month & 0x0F );
year = (( year >> 4 ) & 0x0F ) * 10 + ( year & 0x0F );
sprintf( date, "%02d/%02d/%02d %s", year, month, day, w...
Lcd_setpos( 0, 0 );
Lcd_setstr( date );
sprintf( date, "%02d:%02d:%02d", hour, min, sec );
Lcd_setpos( 1, 0 );
Lcd_setstr( date );
}
BCD形式の入力を、10進数に変換して、sprintfで書式化してか...
上段に年月日と曜日、下段に時分秒の表示です。~
int main( void )
{
uint8_t data[16]; // 受信データ格納用
// uint8_t i = 0; // ループ変数
// char str[5] = " "; // 16進コード格納用
usart_buff_init(); // 受信バッファ初期化
Lcd_init( 0 ); // LCD初期化
Usart_init( usart_rcv_callback, NULL ); // シリアル通信...
I2c_init(); // I2C通信初期化
Rtc_init( data ); // RTCモジュール初期化
sei(); // 割り込み許可
while( 1 ){
Rtc_allread( data ); // 全データ読み込み
print_time( data );
/* Lcd_setpos( 0, 0 );
for( i = 0; i < 8; i++ ){
sprintf( str, "%02X", data[i] );
Lcd_setstr( str );
}
Lcd_setpos( 1, 0 );
for( i = 8; i < 16; i++ ){
sprintf( str, "%02X", data[i] );
Lcd_setstr( str );
}*/
delay_ms( 100 );
}
return 0;
}
main関数ですが、タイマー割り込み等を使用しているわけでは...
せっかくだから、RTCからクロック出力をもらって、その割り込...
バッテリーバックアップ等も含めて、更新契機は今後の課題で...
** 基本動作 [#k26822dd]
まず、電源を入れてPCとUSB通信ができるようにします。~
この辺は、前回の[[USBシリアル通信サンプル]]を参照のこと。~
繋がったら、PCから設定したい日時を、以下のように打ち込ん...
20100418123456
この例では、2010/04/18の、12:34:56に設定されるわけです。~
1回キーを押す度に、その時点のバッファの内容がエコーバック...
間違ったキーを入力してしまった場合、「.」(ピリオド)の入力...
設定した時刻は、LCDに二段に分かれて表示されます。~
上段は、日付と曜日、下段は時刻。~
曜日は、PCからの入力時に、[[ツェラーの公式:http://www.goo...
PCから入力した西暦の1000の位と100の位は、この曜日計算にし...
RTCモジュールは、西暦の下2桁しか気にしないので、RTC内にも...
なので、表示時にも下2桁しか表示出来ない仕様となります。~
ページ名: