advance/RTC
の編集
http://hrb.osask.jp/wiki/?advance/RTC
[
リロード
|
差分
|
単語検索
|
一覧
] [
編集
|
バックアップ
|
添付
]
-- 雛形とするページ --
A
Akkie
Athlon64X2
Clover
DAsoran
Falcon
FormatRule
FrontPage
Genesis
Help
I
InterWikiName
InterWikiSandBox
InterWikiテクニカル
Jormungand
K
Kebo
Kor_Lee_Hee_Rak
Leaf
Linux
Linux/wako_memo
MOIZ99
MW
MenuBar
OSC
PG_MANA
ReadersOS
RecentDeleted
SKYDASH
SandBox
Sero
Sigle
Source
Triangle_Ld.
Zxcvbnm
advance
advance/CPU
advance/FDC
advance/FPU
advance/NotHariMain
advance/QEMUVGA
advance/RTC
advance/blike
advance/cpu_reset
advance/driver
advance/driver/01
advance/driver/02
advance/families
advance/filesystem
advance/fwrite
advance/hddboot
advance/he86
advance/hints
advance/ipl
advance/kernel
advance/keycode
advance/osselect
advance/smaller1
advance/startup
advnace/smaller2
anzy
aotatsu
banbi-
bluedwarf
bo
bugs
challengers
cybozulabsyouth11
deskmanta
esb02b
faq
faq/advance
faq/asm
faq/c00-03
faq/c04-07
faq/c08-15
faq/c16-23
faq/c24-31
faq/make
faq/others
faq/qemu
guide
guide03
guide05
guide07
hikarupsp
imp_log/0000
imp_log/0001
imp_log/0002
imp_log/0003
impressions
index
k
killer_elf
kota
lea
lea/10_memory
lea/4_color
lea/idea
lea/terms
links
logs
logs/osa_hrb/comments0000
logs/osa_hrb/rumors0000
masa
members
message
mistakes
moge32
moppoi5168
notice
osdevjp
populars
prog_index
projects
q_and_a
q_and_a_2
qa_log/0000
qa_log/0001
qa_log/0002
qa_log/0003
qa_log/0004
qa_log/0005
qa_log/0006
qa_log/0007
qa_log/0008
qa_log/0009
quark
rankings
rule
sakamoto
sasaki
spc09
spcc_30min_os
tatsu
tools
tools/bim2hrb
tools/bin2obj
tools/cc1
tools/edimg
tools/gas2nask
tools/makefont
tools/nask
tools/obj2bim
tools/sjisconv
uchan
uho
updates
violations
wako
white
win64-bit
x
ytakano
ヘルプ
リックス
質問します
整形ルール
本は買ったぞ!持ってるぞ!
練習用ページ
* BIOSを使わないで時刻を取得するぞ! -(by [[K]], 2006.04.01) -発展課題のページ(全部読む前にここを読むのは混乱するのでおすすめじゃないです) *** やりかた(基本) -まず以下のような関数を考えます。これはCMOSメモリと呼ばれる領域を読み込むための関数です。CMOSメモリには主にBIOSの設定情報が記録されていますが、ここにRTC(リアルタイムクロック)の情報もあります。 int readcmos(unsigned char addr) { io_out8(0x70, addr); return io_in8(0x71); } -そしてCMOSメモリの以下の領域は、RTCになっています。RTC部分はRTC回路によって自動的に更新されています。 |addr|内容| |0x00|秒| |0x02|分| |0x04|時| |0x07|日| |0x08|月| |0x09|年の下2桁| |0x32|年の上2桁| -ここで注意があります。このRTCの情報は16進数で表示すると普通に見えるようになっています。・・・どういうことかというと、たとえば秒の値が0x12の場合、これは12秒を意味しているのであって、18秒を意味しているのではありません。このような表現方法をBCDといいます。以上のすべての内容はBCDで表現されていますので、取り扱いには注意してください。 --BCDの数値を表示するだけなら、sprintfで%02Xを使えばいいでしょう。 --BCDの数値の通常の数値に変換したいのなら、 y = (x >> 4) * 10 + (x & 0x0f); などとするといいでしょう。 *** まともに使うには -これだけでうまくいきそうに思えるかもしれませんが、実はダメです。プログラムが読み出し中にRTCの書き換えが起きることがときどきあって、それに遭遇すると、「12:34:59」の次が「12:34:00」になってしまったりします。こういう場合はもう一度読むと「12:35:00」という正しい値が読み出せます。 -この書き換え中の読み出しによる問題を避けるためには、「2度読み」というテクニックを使います。つまり上記7バイトを読んで変数に記録しておき、さらにもう一回上記7バイトを読んで先ほど読んだ値と同じかどうかを確認するのです。同じ値が読めればOKですが、値が違えばどちらかの読み出し結果が更新中のものだった可能性があるので、もう一度やり直します。 --2度読みの代わりに、1度しか読まずに前回取得の時刻と比較して、値が明らかにおかしければ読み込みなおすという方法もあります。これはかなり頭の良い方法といえますが、現在時刻の予測値が必要で、予測値があるのならそもそもRTCを読む必要があるのか少々疑問です(RTCはそれほど高速な回路ではないので、RTCを使わないで済むならそのほうが処理が高速になる)。まあOSが管理している内部時計の調節のためになら、2度読み以外の方法も使えるかもしれません。 -しかも問題はまだあります。機種によってはときどきとんでもない値が2度連続で出てきます。たとえば秒が0xffなどです。そんな場合はもう一度読み直すべきです。2度読みによって取得できた秒や分が0x59以下であるかどうかとか(秒に関しては0x60もごくまれにありえますが:うるう秒のときに)、1のくらいが9以下であるかどうかなど、いろいろテストしておかしかったら2度読みをやり直すようにプログラムするといいでしょう。 *** 適当に作ったプログラム例 #include <stdio.h> /* sprintf */ void readrtc(unsigned char *t) { char err; static unsigned char adr[7] = { 0x00, 0x02, 0x04, 0x07, 0x08, 0x09, 0x32 }; static unsigned char max[7] = { 0x60, 0x59, 0x23, 0x31, 0x12, 0x99, 0x99 }; int i; for (;;) { /* 読み込みが成功するまで繰り返す */ err = 0; for (i = 0; i < 7; i++) { io_out8(0x70, adr[i]); t[i] = io_in8(0x71); } for (i = 0; i < 7; i++) { io_out8(0x70, adr[i]); if (t[i] != io_in8(0x71) || (t[i] & 0x0f) > 9 || t[i] > max[i]) { err = 1; } } if (err == 0) { return; } } } void printtime(void) { unsigned char s[24], t[7]; readrtc(t); sprintf(s, "%02X%02X.%02X.%02X %02X:%02X:%02X\n", t[6], t[5], t[4], t[3], t[2], t[1], t[0]); .... } *** おまけ(時刻の取得だけじゃなくて設定もするぞ!) -時刻表示したり時刻を利用するようなOSになると、時刻が狂っていて困ることがあるかもしれません。そんなときに再起動してBIOSや他のOSを使って時刻を設定するのはちょっとかっこ悪いかもしれません。そんなときはどうせなら自作OSで時刻設定もしてしまいましょう! -まずはCMOSメモリに書き込む方法から。 void writecmos(unsigned char addr, unsigned char data) { io_out8(0x70, addr); io_out8(0x71, data); return; } -これを使って秒から書き込んでいきます。・・・なぜ秒から? 仮に 12:34:56 を設定しようとしているとしましょう。しかも実はRTCが03:59:59で、あとほんの一瞬で秒が増えそうだとします。そんなときに時から書いていると、時を書いたところでRTCの時刻更新が始まって、13:00:00になり、そこへ分と秒を書いて結局13:34:56というとんでもない時間になってしまうかもしれないからです。秒から書いた場合、56を書いた時点でこの先3秒は分や時が更新されることは無いわけで、ゆっくりと分や時を設定できるというわけです。 -しかし秒から書くにしても、たまにRTCとアクセスが衝突してうまく書き込めていなかった、なんていう場合があるので、書き込みの直後に上記の方法でRTCを読み出して設定値どおりかどうかを確認したほうがいいでしょう。 --どうせ確認するなら時から書いてもいいじゃないかと思うかもしれませんし、実はそのとおりなのですが(笑)、秒から書くほうが(設定が59秒でない限り)設定中に繰り上がり更新が発生する確率が減って(=設定が初回で成功する確率が上がって)、いいのではないかと思います。 --まあ突き詰めると、まず最初に秒を0にしてそれから時から設定していけば、1分以内に設定が終わる限り時刻更新に悩まされることはありません。でもCMOSへの書き込みを一度増やしてまでこの問題を深刻に考えてもしょうがない気がするので、やはり秒から設定&確認で十分だと思います。 * こめんと欄 -「適当に作ったプログラム例」で unsigned char max の要素の順番が逆なのと、配列の要素が7個しかないのに8までアクセスしようとしています。これだと無限ループ&配列外参照でまずいです。 -- 名無しさん SIZE(10){2006-04-04 (火) 14:40:46} -ご指摘ありがとうございます。直しました。 -- [[K]] SIZE(10){2006-04-04 (火) 16:20:48} -サンプルソースが欲しいです。 -- 名無しさん SIZE(10){2006-05-06 (土) 09:14:24} -上記以上のサンプルソースが必要だというのは、ちょっと疑問です。本当に本を最後まで読みましたか?このページは冒頭にあるように「読み終わった人」のためのものです。 -- [[K]] SIZE(10){2006-05-06 (土) 09:53:50} -既にお気づきかも知れませんが、「適当に作ったプログラム例」の呼び出し側関数(printtime)において、sprintfのフォーマット(時刻部分)に「X」が足りませんね。 -- ''rapper'' SIZE(10){2006-06-11 (日) 02:01:49} -おっとそのとおりでした。直しました。ご指摘ありがとうございました。 -- [[K]] SIZE(10){2006-06-11 (日) 08:47:46} -あの、Xの位置が違うと思います。 -- [[Clover]] SIZE(10){2006-06-11 (日) 13:03:55} -%02Xは16進2桁の数字を出力するためですよ。5とか1桁で%Xだと「5」になりますが、%02Xだと「05」になります。 -- 名無しさん SIZE(10){2006-06-11 (日) 14:38:12} -再度修正しておきました。 -- [[K]] SIZE(10){2006-06-11 (日) 18:12:11} #comment
タイムスタンプを変更しない
* BIOSを使わないで時刻を取得するぞ! -(by [[K]], 2006.04.01) -発展課題のページ(全部読む前にここを読むのは混乱するのでおすすめじゃないです) *** やりかた(基本) -まず以下のような関数を考えます。これはCMOSメモリと呼ばれる領域を読み込むための関数です。CMOSメモリには主にBIOSの設定情報が記録されていますが、ここにRTC(リアルタイムクロック)の情報もあります。 int readcmos(unsigned char addr) { io_out8(0x70, addr); return io_in8(0x71); } -そしてCMOSメモリの以下の領域は、RTCになっています。RTC部分はRTC回路によって自動的に更新されています。 |addr|内容| |0x00|秒| |0x02|分| |0x04|時| |0x07|日| |0x08|月| |0x09|年の下2桁| |0x32|年の上2桁| -ここで注意があります。このRTCの情報は16進数で表示すると普通に見えるようになっています。・・・どういうことかというと、たとえば秒の値が0x12の場合、これは12秒を意味しているのであって、18秒を意味しているのではありません。このような表現方法をBCDといいます。以上のすべての内容はBCDで表現されていますので、取り扱いには注意してください。 --BCDの数値を表示するだけなら、sprintfで%02Xを使えばいいでしょう。 --BCDの数値の通常の数値に変換したいのなら、 y = (x >> 4) * 10 + (x & 0x0f); などとするといいでしょう。 *** まともに使うには -これだけでうまくいきそうに思えるかもしれませんが、実はダメです。プログラムが読み出し中にRTCの書き換えが起きることがときどきあって、それに遭遇すると、「12:34:59」の次が「12:34:00」になってしまったりします。こういう場合はもう一度読むと「12:35:00」という正しい値が読み出せます。 -この書き換え中の読み出しによる問題を避けるためには、「2度読み」というテクニックを使います。つまり上記7バイトを読んで変数に記録しておき、さらにもう一回上記7バイトを読んで先ほど読んだ値と同じかどうかを確認するのです。同じ値が読めればOKですが、値が違えばどちらかの読み出し結果が更新中のものだった可能性があるので、もう一度やり直します。 --2度読みの代わりに、1度しか読まずに前回取得の時刻と比較して、値が明らかにおかしければ読み込みなおすという方法もあります。これはかなり頭の良い方法といえますが、現在時刻の予測値が必要で、予測値があるのならそもそもRTCを読む必要があるのか少々疑問です(RTCはそれほど高速な回路ではないので、RTCを使わないで済むならそのほうが処理が高速になる)。まあOSが管理している内部時計の調節のためになら、2度読み以外の方法も使えるかもしれません。 -しかも問題はまだあります。機種によってはときどきとんでもない値が2度連続で出てきます。たとえば秒が0xffなどです。そんな場合はもう一度読み直すべきです。2度読みによって取得できた秒や分が0x59以下であるかどうかとか(秒に関しては0x60もごくまれにありえますが:うるう秒のときに)、1のくらいが9以下であるかどうかなど、いろいろテストしておかしかったら2度読みをやり直すようにプログラムするといいでしょう。 *** 適当に作ったプログラム例 #include <stdio.h> /* sprintf */ void readrtc(unsigned char *t) { char err; static unsigned char adr[7] = { 0x00, 0x02, 0x04, 0x07, 0x08, 0x09, 0x32 }; static unsigned char max[7] = { 0x60, 0x59, 0x23, 0x31, 0x12, 0x99, 0x99 }; int i; for (;;) { /* 読み込みが成功するまで繰り返す */ err = 0; for (i = 0; i < 7; i++) { io_out8(0x70, adr[i]); t[i] = io_in8(0x71); } for (i = 0; i < 7; i++) { io_out8(0x70, adr[i]); if (t[i] != io_in8(0x71) || (t[i] & 0x0f) > 9 || t[i] > max[i]) { err = 1; } } if (err == 0) { return; } } } void printtime(void) { unsigned char s[24], t[7]; readrtc(t); sprintf(s, "%02X%02X.%02X.%02X %02X:%02X:%02X\n", t[6], t[5], t[4], t[3], t[2], t[1], t[0]); .... } *** おまけ(時刻の取得だけじゃなくて設定もするぞ!) -時刻表示したり時刻を利用するようなOSになると、時刻が狂っていて困ることがあるかもしれません。そんなときに再起動してBIOSや他のOSを使って時刻を設定するのはちょっとかっこ悪いかもしれません。そんなときはどうせなら自作OSで時刻設定もしてしまいましょう! -まずはCMOSメモリに書き込む方法から。 void writecmos(unsigned char addr, unsigned char data) { io_out8(0x70, addr); io_out8(0x71, data); return; } -これを使って秒から書き込んでいきます。・・・なぜ秒から? 仮に 12:34:56 を設定しようとしているとしましょう。しかも実はRTCが03:59:59で、あとほんの一瞬で秒が増えそうだとします。そんなときに時から書いていると、時を書いたところでRTCの時刻更新が始まって、13:00:00になり、そこへ分と秒を書いて結局13:34:56というとんでもない時間になってしまうかもしれないからです。秒から書いた場合、56を書いた時点でこの先3秒は分や時が更新されることは無いわけで、ゆっくりと分や時を設定できるというわけです。 -しかし秒から書くにしても、たまにRTCとアクセスが衝突してうまく書き込めていなかった、なんていう場合があるので、書き込みの直後に上記の方法でRTCを読み出して設定値どおりかどうかを確認したほうがいいでしょう。 --どうせ確認するなら時から書いてもいいじゃないかと思うかもしれませんし、実はそのとおりなのですが(笑)、秒から書くほうが(設定が59秒でない限り)設定中に繰り上がり更新が発生する確率が減って(=設定が初回で成功する確率が上がって)、いいのではないかと思います。 --まあ突き詰めると、まず最初に秒を0にしてそれから時から設定していけば、1分以内に設定が終わる限り時刻更新に悩まされることはありません。でもCMOSへの書き込みを一度増やしてまでこの問題を深刻に考えてもしょうがない気がするので、やはり秒から設定&確認で十分だと思います。 * こめんと欄 -「適当に作ったプログラム例」で unsigned char max の要素の順番が逆なのと、配列の要素が7個しかないのに8までアクセスしようとしています。これだと無限ループ&配列外参照でまずいです。 -- 名無しさん SIZE(10){2006-04-04 (火) 14:40:46} -ご指摘ありがとうございます。直しました。 -- [[K]] SIZE(10){2006-04-04 (火) 16:20:48} -サンプルソースが欲しいです。 -- 名無しさん SIZE(10){2006-05-06 (土) 09:14:24} -上記以上のサンプルソースが必要だというのは、ちょっと疑問です。本当に本を最後まで読みましたか?このページは冒頭にあるように「読み終わった人」のためのものです。 -- [[K]] SIZE(10){2006-05-06 (土) 09:53:50} -既にお気づきかも知れませんが、「適当に作ったプログラム例」の呼び出し側関数(printtime)において、sprintfのフォーマット(時刻部分)に「X」が足りませんね。 -- ''rapper'' SIZE(10){2006-06-11 (日) 02:01:49} -おっとそのとおりでした。直しました。ご指摘ありがとうございました。 -- [[K]] SIZE(10){2006-06-11 (日) 08:47:46} -あの、Xの位置が違うと思います。 -- [[Clover]] SIZE(10){2006-06-11 (日) 13:03:55} -%02Xは16進2桁の数字を出力するためですよ。5とか1桁で%Xだと「5」になりますが、%02Xだと「05」になります。 -- 名無しさん SIZE(10){2006-06-11 (日) 14:38:12} -再度修正しておきました。 -- [[K]] SIZE(10){2006-06-11 (日) 18:12:11} #comment
テキスト整形のルールを表示する