1- #include "Rtc.h"
1+ #include "RTC/ Rtc.h"
22#include "Io.h"
3+ #include "stdbool.h"
34
4- #define CMOS_ADDRESS 0x70
5- #define CMOS_DATA 0x71
6-
7- // CMOS Register numbers
8- #define CMOS_REG_SECONDS 0x00
9- #define CMOS_REG_MINUTES 0x02
10- #define CMOS_REG_HOURS 0x04
11- #define CMOS_REG_DAY 0x07
12- #define CMOS_REG_MONTH 0x08
13- #define CMOS_REG_YEAR 0x09
14- #define CMOS_REG_CENTURY 0x32 // Common on newer systems
15-
16- #define CMOS_REG_STATUS_A 0x0A
17- #define CMOS_REG_STATUS_B 0x0B
18-
19- static uint8_t cmos_read ( uint8_t reg ) {
20- // Select the register, making sure NMI is disabled
21- outb ( CMOS_ADDRESS , ( 1 << 7 ) | reg );
22- // Read the data
23- return inb (CMOS_DATA );
5+ #define RTC_CMOS_ADDRESS 0x70
6+ #define RTC_CMOS_DATA 0x71
7+
8+ #define RTC_SECONDS 0x00
9+ #define RTC_MINUTES 0x02
10+ #define RTC_HOURS 0x04
11+ #define RTC_DAY_OF_WEEK 0x06
12+ #define RTC_DAY_OF_MONTH 0x07
13+ #define RTC_MONTH 0x08
14+ #define RTC_YEAR 0x09
15+ #define RTC_CENTURY 0x32 // Most common century register
16+ #define RTC_STATUS_A 0x0A
17+ #define RTC_STATUS_B 0x0B
18+ #define RTC_STATUS_C 0x0C
19+
20+ typedef rtc_time_t RtcDateTime ;
21+
22+ static uint8_t Rtc_ReadRegister ( uint8_t reg ) {
23+ outb ( RTC_CMOS_ADDRESS , reg );
24+ return inb (RTC_CMOS_DATA );
2425}
2526
26- static int get_update_in_progress_flag () {
27- return cmos_read (CMOS_REG_STATUS_A ) & 0x80 ;
27+ static void Rtc_WriteRegister (uint8_t reg , uint8_t value ) {
28+ outb (RTC_CMOS_ADDRESS , reg );
29+ outb (RTC_CMOS_DATA , value );
2830}
2931
30- static uint8_t bcd_to_bin (uint8_t bcd ) {
31- return (bcd & 0x0F ) + ((bcd >> 4 ) * 10 );
32+ int Rtc_BcdToBinary (uint8_t bcd ) {
33+ return (bcd & 0x0F ) + ((bcd / 16 ) * 10 );
3234}
3335
34- void RtcReadTime (rtc_time_t * rtc_time ) {
35- rtc_time_t last_time ;
36- uint8_t status_b ;
37-
38- // The robust way to read the RTC is to read it twice and see if the
39- // values match. This ensures an update didn't happen in the middle of our read.
40- do {
41- // Wait until no update is in progress
42- while (get_update_in_progress_flag ()) {}
43-
44- rtc_time -> second = cmos_read (CMOS_REG_SECONDS );
45- rtc_time -> minute = cmos_read (CMOS_REG_MINUTES );
46- rtc_time -> hour = cmos_read (CMOS_REG_HOURS );
47- rtc_time -> day = cmos_read (CMOS_REG_DAY );
48- rtc_time -> month = cmos_read (CMOS_REG_MONTH );
49- rtc_time -> year = cmos_read (CMOS_REG_YEAR );
50- rtc_time -> century = cmos_read (CMOS_REG_CENTURY );
51- // Make a copy of the values we just read
52- last_time = * rtc_time ;
53-
54- // Wait again to ensure we are past the update
55- while (get_update_in_progress_flag ()){}
56-
57- // Read a second time
58- last_time .second = cmos_read (CMOS_REG_SECONDS );
59- last_time .minute = cmos_read (CMOS_REG_MINUTES );
60- last_time .hour = cmos_read (CMOS_REG_HOURS );
61- last_time .day = cmos_read (CMOS_REG_DAY );
62- last_time .month = cmos_read (CMOS_REG_MONTH );
63- last_time .year = cmos_read (CMOS_REG_YEAR );
64- #ifdef VF_CONFIG_RTC_CENTURY
65- last_time .century = cmos_read (CMOS_REG_CENTURY );
66- #endif
67-
68- } while ( (last_time .second != rtc_time -> second ) ||
69- (last_time .minute != rtc_time -> minute ) ||
70- (last_time .hour != rtc_time -> hour ) ||
71- (last_time .day != rtc_time -> day ) ||
72- (last_time .month != rtc_time -> month ) ||
73- (last_time .year != rtc_time -> year )
74- #ifdef VF_CONFIG_RTC_CENTURY
75- || (last_time .century != rtc_time -> century )
76- #endif
77- );
78-
79-
80- // Now that we have a stable read, convert from BCD if necessary
81- status_b = cmos_read (CMOS_REG_STATUS_B );
82-
83- if (!(status_b & 0x04 )) { // Bit 2 clear means BCD mode
84- rtc_time -> second = bcd_to_bin (rtc_time -> second );
85- rtc_time -> minute = bcd_to_bin (rtc_time -> minute );
86- // Handle 12/24 hour clock for the hour value
87- rtc_time -> hour = ((rtc_time -> hour & 0x7F ) + 12 * ((rtc_time -> hour & 0x80 ) != 0 )) % 24 ;
88- rtc_time -> day = bcd_to_bin (rtc_time -> day );
89- rtc_time -> month = bcd_to_bin (rtc_time -> month );
90- rtc_time -> year = bcd_to_bin (rtc_time -> year );
36+ uint8_t Rtc_BinaryToBcd (uint8_t binary ) {
37+ return ((binary / 10 ) << 4 ) | (binary % 10 );
38+ }
39+
40+ static bool Rtc_IsUpdating () {
41+ outb (RTC_CMOS_ADDRESS , 0x80 | RTC_STATUS_A ); // select reg A, NMI masked
42+ return (inb (RTC_CMOS_DATA ) & 0x80 ) != 0 ; // UIP bit
43+ }
44+
45+ void RtcReadTime (RtcDateTime * dateTime ) {
46+ uint8_t second , minute , hour , day , month , year , century ;
47+ uint8_t statusB ;
48+
49+ // Wait until the update in progress bit is 0
50+ while (Rtc_IsUpdating ());
51+
52+ // Read RTC registers
53+ second = Rtc_ReadRegister (RTC_SECONDS );
54+ minute = Rtc_ReadRegister (RTC_MINUTES );
55+ hour = Rtc_ReadRegister (RTC_HOURS );
56+ day = Rtc_ReadRegister (RTC_DAY_OF_MONTH );
57+ month = Rtc_ReadRegister (RTC_MONTH );
58+ year = Rtc_ReadRegister (RTC_YEAR );
59+ century = Rtc_ReadRegister (RTC_CENTURY ); // Read century byte
60+
61+ statusB = Rtc_ReadRegister (RTC_STATUS_B );
62+
63+ if (!(statusB & 0x04 )) { // If BCD mode
64+ dateTime -> second = Rtc_BcdToBinary (second );
65+ dateTime -> minute = Rtc_BcdToBinary (minute );
66+ dateTime -> hour = Rtc_BcdToBinary (hour );
67+ dateTime -> day = Rtc_BcdToBinary (day );
68+ dateTime -> month = Rtc_BcdToBinary (month );
69+ dateTime -> year = Rtc_BcdToBinary (year );
70+ dateTime -> century = Rtc_BcdToBinary (century );
71+ } else { // Binary mode
72+ dateTime -> second = second ;
73+ dateTime -> minute = minute ;
74+ dateTime -> hour = hour ;
75+ dateTime -> day = day ;
76+ dateTime -> month = month ;
77+ dateTime -> year = year ;
78+ dateTime -> century = century ;
9179 }
92- #ifdef VF_CONFIG_RTC_CENTURY
93- {
94- uint16_t cval = (uint16_t )rtc_time -> century ;
95- if (cval != 0 ) {
96- cval = bcd_to_bin ((uint8_t )cval );
97- rtc_time -> year += (uint16_t )(cval * 100 );
98- } else {
99- rtc_time -> year += 2000 ;
100- }
80+
81+ if (!(statusB & 0x02 ) && (dateTime -> hour & 0x80 )) { // 12-hour format and PM
82+ dateTime -> hour = ((dateTime -> hour & 0x7F ) + 12 ) % 24 ;
83+ }
84+
85+ // Calculate full year
86+ dateTime -> year += dateTime -> century * 100 ;
87+ }
88+
89+ void RtcSetTime (const RtcDateTime * dateTime ) {
90+ uint8_t statusB ;
91+ uint8_t second , minute , hour , day , month , year , century ;
92+
93+ // Read current status B to preserve settings
94+ statusB = Rtc_ReadRegister (RTC_STATUS_B );
95+
96+ // Disable NMI and updates while setting time
97+ Rtc_WriteRegister (RTC_CMOS_ADDRESS , RTC_STATUS_B | 0x80 ); // Disable NMI
98+ Rtc_WriteRegister (RTC_STATUS_B , statusB | 0x80 ); // Disable updates
99+
100+ // Convert to BCD if necessary
101+ if (!(statusB & 0x04 )) { // If BCD mode
102+ second = Rtc_BinaryToBcd (dateTime -> second );
103+ minute = Rtc_BinaryToBcd (dateTime -> minute );
104+ hour = Rtc_BinaryToBcd (dateTime -> hour );
105+ day = Rtc_BinaryToBcd (dateTime -> day );
106+ month = Rtc_BinaryToBcd (dateTime -> month );
107+ year = Rtc_BinaryToBcd (dateTime -> year % 100 );
108+ century = Rtc_BinaryToBcd (dateTime -> year / 100 );
109+ } else { // Binary mode
110+ second = dateTime -> second ;
111+ minute = dateTime -> minute ;
112+ hour = dateTime -> hour ;
113+ day = dateTime -> day ;
114+ month = dateTime -> month ;
115+ year = dateTime -> year % 100 ;
116+ century = dateTime -> year / 100 ;
101117 }
102- #else
103- rtc_time -> year += 2000 ;
104- #endif
118+
119+ // Write to RTC registers
120+ Rtc_WriteRegister (RTC_SECONDS , second );
121+ Rtc_WriteRegister (RTC_MINUTES , minute );
122+ Rtc_WriteRegister (RTC_HOURS , hour );
123+ Rtc_WriteRegister (RTC_DAY_OF_MONTH , day );
124+ Rtc_WriteRegister (RTC_MONTH , month );
125+ Rtc_WriteRegister (RTC_YEAR , year );
126+ Rtc_WriteRegister (RTC_CENTURY , century );
127+
128+ // Re-enable updates and NMI
129+ Rtc_WriteRegister (RTC_STATUS_B , statusB );
130+ Rtc_WriteRegister (RTC_CMOS_ADDRESS , RTC_STATUS_B ); // Re-enable NMI
105131}
0 commit comments