nixie.rs 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689
  1. use core::{cell::RefCell, ops::DerefMut};
  2. use cortex_m::interrupt::{free, Mutex};
  3. use stm32l4xx_hal::{
  4. prelude::{
  5. _embedded_hal_blocking_i2c_Read, _embedded_hal_blocking_i2c_Write,
  6. _embedded_hal_blocking_i2c_WriteRead,
  7. },
  8. timer::Event,
  9. };
  10. use crate::{ds3231, pca9685};
  11. pub const DS3231_ADDR: u8 = 0x68;
  12. pub const TUSB322_ADDR: u8 = 0x47;
  13. pub const PCA9685_ADDR_1: u8 = 0x41;
  14. pub const PCA9685_ADDR_2: u8 = 0x42;
  15. pub const PCA9685_ADDR_3: u8 = 0x43;
  16. pub const PCA9685_ALL_CALL: u8 = 0x70; // Default enabled
  17. pub const PCA9685_SUB_CALL_1: u8 = 0x71; // Default disabled
  18. pub const PCA9685_SUB_CALL_2: u8 = 0x72; // Default disabled
  19. pub const PCA9685_SUB_CALL_3: u8 = 0x73; // Default disabled
  20. pub const REFRESH_RATE_HZ: u32 = 1000;
  21. const DIGIT_FADE_DURATION_US: u32 = 1_000_000;
  22. const DIGIT_RNG_FADE_DURATION_US: u32 = 200_000;
  23. const DIGIT_RNG_FADE_ITERATIONS: usize = 20;
  24. const DIGIT_RNG_REFRESH_INTERVAL: usize = 60;
  25. const DIGIT_RNG_REFRESH_VARIANCE: usize = 30;
  26. const DOT_MIN_BRIGHTNESS: u32 = 256;
  27. const DOT_MAX_BRIGHTNESS: u32 = 640;
  28. const DOT_FADE_DURATION_US: u32 = 1_000_000;
  29. const DIGIT_MAX_BRIGHTNESS: u32 = 4096;
  30. const DIGIT_MIN_BRIGHTNESS: u32 = 0;
  31. const NUM_TUBES: usize = 4;
  32. const NUM_DIGITS: usize = 10;
  33. const MAP_DOT_ADDR: u8 = PCA9685_ADDR_2;
  34. const MAP_DOT_PIN: u8 = 15;
  35. const MAP_ADDR: usize = 0;
  36. const MAP_PIN: usize = 1;
  37. struct DigitToPin {
  38. address: u8,
  39. pin: usize,
  40. }
  41. struct PwmDriver {
  42. digit: [DigitToPin; 10],
  43. }
  44. struct PwmOutputMap {
  45. driver: [PwmDriver; 4],
  46. dot_address: u8,
  47. dot_pin: usize,
  48. }
  49. static TUBE_MAPPING: PwmOutputMap = {
  50. PwmOutputMap {
  51. driver: [
  52. PwmDriver {
  53. digit: [
  54. DigitToPin {
  55. address: PCA9685_ADDR_1,
  56. pin: 8,
  57. }, // Tube 0 Digit 0
  58. DigitToPin {
  59. address: PCA9685_ADDR_1,
  60. pin: 9,
  61. }, // Tube 0 Digit 1
  62. DigitToPin {
  63. address: PCA9685_ADDR_1,
  64. pin: 10,
  65. }, // Tube 0 Digit 2
  66. DigitToPin {
  67. address: PCA9685_ADDR_1,
  68. pin: 12,
  69. }, // Tube 0 Digit 3
  70. DigitToPin {
  71. address: PCA9685_ADDR_1,
  72. pin: 15,
  73. }, // Tube 0 Digit 4
  74. DigitToPin {
  75. address: PCA9685_ADDR_1,
  76. pin: 14,
  77. }, // Tube 0 Digit 5
  78. DigitToPin {
  79. address: PCA9685_ADDR_1,
  80. pin: 11,
  81. }, // Tube 0 Digit 6
  82. DigitToPin {
  83. address: PCA9685_ADDR_1,
  84. pin: 0,
  85. }, // Tube 0 Digit 7
  86. DigitToPin {
  87. address: PCA9685_ADDR_1,
  88. pin: 1,
  89. }, // Tube 0 Digit 8
  90. DigitToPin {
  91. address: PCA9685_ADDR_1,
  92. pin: 13,
  93. }, // Tube 0 Digit 9
  94. ],
  95. },
  96. PwmDriver {
  97. digit: [
  98. DigitToPin {
  99. address: PCA9685_ADDR_1,
  100. pin: 5,
  101. }, // Tube 1 Digit 0
  102. DigitToPin {
  103. address: PCA9685_ADDR_1,
  104. pin: 6,
  105. }, // Tube 1 Digit 1
  106. DigitToPin {
  107. address: PCA9685_ADDR_1,
  108. pin: 7,
  109. }, // Tube 1 Digit 2
  110. DigitToPin {
  111. address: PCA9685_ADDR_1,
  112. pin: 2,
  113. }, // Tube 1 Digit 3
  114. DigitToPin {
  115. address: PCA9685_ADDR_2,
  116. pin: 4,
  117. }, // Tube 1 Digit 4
  118. DigitToPin {
  119. address: PCA9685_ADDR_2,
  120. pin: 1,
  121. }, // Tube 1 Digit 5
  122. DigitToPin {
  123. address: PCA9685_ADDR_1,
  124. pin: 4,
  125. }, // Tube 1 Digit 6
  126. DigitToPin {
  127. address: PCA9685_ADDR_2,
  128. pin: 2,
  129. }, // Tube 1 Digit 7
  130. DigitToPin {
  131. address: PCA9685_ADDR_2,
  132. pin: 3,
  133. }, // Tube 1 Digit 8
  134. DigitToPin {
  135. address: PCA9685_ADDR_1,
  136. pin: 3,
  137. }, // Tube 1 Digit 9
  138. ],
  139. },
  140. PwmDriver {
  141. digit: [
  142. DigitToPin {
  143. address: PCA9685_ADDR_3,
  144. pin: 8,
  145. }, // Tube 2 Digit 0
  146. DigitToPin {
  147. address: PCA9685_ADDR_3,
  148. pin: 9,
  149. }, // Tube 2 Digit 1
  150. DigitToPin {
  151. address: PCA9685_ADDR_3,
  152. pin: 10,
  153. }, // Tube 2 Digit 2
  154. DigitToPin {
  155. address: PCA9685_ADDR_3,
  156. pin: 12,
  157. }, // Tube 2 Digit 3
  158. DigitToPin {
  159. address: PCA9685_ADDR_2,
  160. pin: 12,
  161. }, // Tube 2 Digit 4
  162. DigitToPin {
  163. address: PCA9685_ADDR_2,
  164. pin: 13,
  165. }, // Tube 2 Digit 5
  166. DigitToPin {
  167. address: PCA9685_ADDR_3,
  168. pin: 11,
  169. }, // Tube 2 Digit 6
  170. DigitToPin {
  171. address: PCA9685_ADDR_2,
  172. pin: 14,
  173. }, // Tube 2 Digit 7
  174. DigitToPin {
  175. address: PCA9685_ADDR_2,
  176. pin: 11,
  177. }, // Tube 2 Digit 8
  178. DigitToPin {
  179. address: PCA9685_ADDR_3,
  180. pin: 13,
  181. }, // Tube 2 Digit 9
  182. ],
  183. },
  184. PwmDriver {
  185. digit: [
  186. DigitToPin {
  187. address: PCA9685_ADDR_3,
  188. pin: 5,
  189. }, // Tube 3 Digit 0
  190. DigitToPin {
  191. address: PCA9685_ADDR_3,
  192. pin: 6,
  193. }, // Tube 3 Digit 1
  194. DigitToPin {
  195. address: PCA9685_ADDR_3,
  196. pin: 7,
  197. }, // Tube 3 Digit 2
  198. DigitToPin {
  199. address: PCA9685_ADDR_3,
  200. pin: 2,
  201. }, // Tube 3 Digit 3
  202. DigitToPin {
  203. address: PCA9685_ADDR_3,
  204. pin: 14,
  205. }, // Tube 3 Digit 4
  206. DigitToPin {
  207. address: PCA9685_ADDR_3,
  208. pin: 15,
  209. }, // Tube 3 Digit 5
  210. DigitToPin {
  211. address: PCA9685_ADDR_3,
  212. pin: 4,
  213. }, // Tube 3 Digit 6
  214. DigitToPin {
  215. address: PCA9685_ADDR_3,
  216. pin: 1,
  217. }, // Tube 3 Digit 7
  218. DigitToPin {
  219. address: PCA9685_ADDR_3,
  220. pin: 0,
  221. }, // Tube 3 Digit 8
  222. DigitToPin {
  223. address: PCA9685_ADDR_3,
  224. pin: 3,
  225. }, // Tube 3 Digit 9
  226. ],
  227. },
  228. ],
  229. dot_address: PCA9685_ADDR_2,
  230. dot_pin: 15,
  231. }
  232. };
  233. #[derive(Debug, PartialEq)]
  234. enum State {
  235. Idle,
  236. Incrementing,
  237. Decrementing,
  238. }
  239. struct Digit {
  240. state: State,
  241. value: u32,
  242. pwm_start: u32,
  243. pwm_end: u32,
  244. updated: bool,
  245. }
  246. impl Digit {
  247. const fn default() -> Self {
  248. Self {
  249. state: State::Idle,
  250. value: 0,
  251. pwm_start: 0,
  252. pwm_end: 0,
  253. updated: false,
  254. }
  255. }
  256. }
  257. struct Tube {
  258. digits: [Digit; NUM_DIGITS],
  259. last_active_digit: Option<usize>,
  260. refresh_last_digit: Option<usize>,
  261. refresh_active: bool,
  262. }
  263. impl Tube {
  264. const fn default() -> Self {
  265. const DIGIT_INIT: Digit = Digit::default();
  266. Self {
  267. digits: [DIGIT_INIT; 10],
  268. last_active_digit: None,
  269. refresh_last_digit: None,
  270. refresh_active: false,
  271. }
  272. }
  273. }
  274. static CLOCK: Mutex<RefCell<Clock>> = Mutex::new(RefCell::new(Clock::default()));
  275. struct Clock {
  276. tubes: [Tube; NUM_TUBES],
  277. dot: Digit,
  278. fade_duration: u32,
  279. minute: Option<u32>,
  280. hour: Option<u32>,
  281. }
  282. impl Clock {
  283. const fn default() -> Self {
  284. const TUBE_INIT: Tube = Tube::default();
  285. Self {
  286. tubes: [TUBE_INIT; NUM_TUBES],
  287. dot: Digit::default(),
  288. fade_duration: 0,
  289. minute: None,
  290. hour: None,
  291. }
  292. }
  293. pub fn rtc_tick(&mut self, second: u32, minute: u32, hour: u32) {
  294. match self.hour {
  295. Some(prev_hour) if prev_hour / 10 == hour / 10 => {
  296. if hour / 10 == 0 {
  297. self.fade_in_out_digit(0, None, DIGIT_FADE_DURATION_US, false);
  298. }
  299. }
  300. _ => {
  301. self.fade_in_out_digit(
  302. 0,
  303. Some((hour / 10) as usize),
  304. DIGIT_FADE_DURATION_US,
  305. false,
  306. );
  307. }
  308. }
  309. match self.hour {
  310. Some(prev_hour) if prev_hour % 10 == hour % 10 => {}
  311. _ => {
  312. self.fade_in_out_digit(
  313. 1,
  314. Some((hour % 10) as usize),
  315. DIGIT_FADE_DURATION_US,
  316. false,
  317. );
  318. }
  319. }
  320. match self.minute {
  321. Some(prev_minute) if prev_minute / 10 == minute / 10 => {}
  322. _ => {
  323. self.fade_in_out_digit(
  324. 2,
  325. Some((minute / 10) as usize),
  326. DIGIT_FADE_DURATION_US,
  327. false,
  328. );
  329. }
  330. }
  331. match self.minute {
  332. Some(prev_minute) if prev_minute % 10 == minute % 10 => {}
  333. _ => {
  334. self.fade_in_out_digit(
  335. 3,
  336. Some((minute % 10) as usize),
  337. DIGIT_FADE_DURATION_US,
  338. false,
  339. );
  340. }
  341. }
  342. #[cfg(test)]
  343. println!(
  344. "RTC tick: {}{}:{}{}",
  345. hour / 10,
  346. hour % 10,
  347. minute / 10,
  348. minute % 10
  349. );
  350. self.dot.state = match second % 2 {
  351. 0 => State::Incrementing,
  352. 1 => State::Decrementing,
  353. _ => State::Idle,
  354. };
  355. #[cfg(test)]
  356. println!("RTC tick: dot state is {:?}", self.dot.state);
  357. self.hour = Some(hour);
  358. self.minute = Some(minute);
  359. #[cfg(not(test))]
  360. free(|cs| {
  361. let mut timer_ref = super::REFRESH_TIMER.borrow(cs).borrow_mut();
  362. if let Some(ref mut timer) = timer_ref.deref_mut() {
  363. timer.listen(Event::TimeOut);
  364. }
  365. })
  366. }
  367. pub fn refresh_tick(&mut self) -> bool {
  368. let mut pending_refresh: bool = false;
  369. let mut update_fn = |digit: &mut Digit, min: u32, max: u32, steps: u32| {
  370. match digit.state {
  371. State::Incrementing => {
  372. if digit.value >= max {
  373. digit.value = max;
  374. digit.state = State::Idle;
  375. } else {
  376. digit.value = digit.value.saturating_add(steps).clamp(min, max);
  377. digit.updated = true;
  378. pending_refresh = true;
  379. }
  380. }
  381. State::Decrementing => {
  382. if digit.value <= min {
  383. digit.value = min;
  384. digit.state = State::Idle;
  385. } else {
  386. digit.value = digit.value.saturating_sub(steps).clamp(min, max);
  387. digit.updated = true;
  388. pending_refresh = true;
  389. }
  390. }
  391. State::Idle => (),
  392. };
  393. };
  394. let ticks = self.fade_duration / REFRESH_RATE_HZ;
  395. let steps = ((DIGIT_MAX_BRIGHTNESS - DIGIT_MIN_BRIGHTNESS) + ticks - 1) / ticks;
  396. #[cfg(not(test))]
  397. self.tubes.iter_mut().for_each(|tube| {
  398. tube.digits.iter_mut().for_each(|digit| {
  399. update_fn(digit, DIGIT_MIN_BRIGHTNESS, DIGIT_MAX_BRIGHTNESS, steps);
  400. });
  401. });
  402. #[cfg(test)]
  403. for (t, tube) in self.tubes.iter_mut().enumerate() {
  404. for (d, digit) in tube.digits.iter_mut().enumerate() {
  405. update_fn(digit, DIGIT_MIN_BRIGHTNESS, DIGIT_MAX_BRIGHTNESS, steps);
  406. if digit.updated {
  407. println!(
  408. "Refresh tick: updated tube {} digit {} to value {}",
  409. t, d, digit.value
  410. );
  411. }
  412. }
  413. }
  414. // Handle dot
  415. let steps = ((DOT_MAX_BRIGHTNESS - DOT_MIN_BRIGHTNESS) + ticks - 1) / ticks;
  416. update_fn(&mut self.dot, DOT_MIN_BRIGHTNESS, DOT_MAX_BRIGHTNESS, steps);
  417. #[cfg(test)]
  418. if self.dot.updated {
  419. println!("Refresh tick: updated dot to value {}", self.dot.value);
  420. }
  421. if pending_refresh {
  422. self.distribute_pwm();
  423. }
  424. pending_refresh
  425. }
  426. pub fn rng_tick<T>(&mut self, _i2c: &mut T)
  427. where
  428. T: _embedded_hal_blocking_i2c_WriteRead
  429. + _embedded_hal_blocking_i2c_Read
  430. + _embedded_hal_blocking_i2c_Write,
  431. {
  432. }
  433. pub fn write_i2c<T>(&mut self, i2c: &mut T)
  434. where
  435. T: _embedded_hal_blocking_i2c_WriteRead
  436. + _embedded_hal_blocking_i2c_Read
  437. + _embedded_hal_blocking_i2c_Write,
  438. {
  439. for (t, tube) in self.tubes.iter().enumerate() {
  440. for (d, digit) in tube.digits.iter().enumerate() {
  441. if digit.updated {
  442. pca9685::set_digit(
  443. i2c,
  444. TUBE_MAPPING.driver[t].digit[d].address,
  445. TUBE_MAPPING.driver[t].digit[d].pin,
  446. digit.pwm_start,
  447. digit.pwm_end,
  448. );
  449. }
  450. }
  451. }
  452. if self.dot.updated {
  453. pca9685::set_digit(
  454. i2c,
  455. TUBE_MAPPING.dot_address,
  456. TUBE_MAPPING.dot_pin,
  457. self.dot.pwm_start,
  458. self.dot.pwm_end,
  459. );
  460. }
  461. }
  462. fn fade_in_out_digit(
  463. &mut self,
  464. tube: usize,
  465. digit: Option<usize>,
  466. fade_duration: u32,
  467. refresh_cmd: bool,
  468. ) {
  469. // If the tube is in the middle of a refresh sequence and a call comes
  470. // in to update the tube digit (for time), override the last value of
  471. // the refresh sequence with the new digit.
  472. if self.tubes[tube].refresh_active && !refresh_cmd {
  473. self.tubes[tube].refresh_last_digit = digit;
  474. }
  475. // Dont update if actively refreshing tube unless RngUpdate is set
  476. if (!self.tubes[tube].refresh_active && !refresh_cmd) || refresh_cmd {
  477. // Fade out all digits
  478. for digit in 0..NUM_DIGITS {
  479. if self.tubes[tube].digits[digit].value != DIGIT_MIN_BRIGHTNESS {
  480. self.tubes[tube].digits[digit].state = State::Decrementing;
  481. }
  482. }
  483. // Fade in the specified digit
  484. if let Some(digit) = digit {
  485. if self.tubes[tube].digits[digit].value != DIGIT_MAX_BRIGHTNESS {
  486. self.tubes[tube].digits[digit].state = State::Incrementing;
  487. }
  488. }
  489. self.tubes[tube].last_active_digit = digit;
  490. self.fade_duration = fade_duration;
  491. }
  492. }
  493. // In the event that there are multiple PWM outputs at less than 100% duty cycle,
  494. // stagger the start time of each PWM to reduce the switch on surge current. If the
  495. // duty cycle is greater than 100%, distribute the PWM outputs as much as possible
  496. // to keep the current consumption at a minimum.
  497. fn distribute_pwm(&mut self) {
  498. let mut last_pwm: u32 = 0;
  499. let mut incrementing: bool = true;
  500. // Closure to avoid duplicate code
  501. let mut update_digit = |digit: &mut Digit| {
  502. if digit.value == DIGIT_MIN_BRIGHTNESS {
  503. digit.pwm_start = 0;
  504. digit.pwm_end = 0;
  505. } else if digit.value == DIGIT_MAX_BRIGHTNESS {
  506. digit.pwm_start = 0;
  507. digit.pwm_end = DIGIT_MAX_BRIGHTNESS;
  508. } else {
  509. if incrementing {
  510. if last_pwm + digit.value > DIGIT_MAX_BRIGHTNESS {
  511. digit.pwm_start = DIGIT_MAX_BRIGHTNESS - digit.value;
  512. digit.pwm_end = DIGIT_MAX_BRIGHTNESS;
  513. last_pwm = digit.pwm_start;
  514. incrementing = false;
  515. } else {
  516. digit.pwm_start = last_pwm;
  517. digit.pwm_end = digit.pwm_start + digit.value;
  518. last_pwm = digit.pwm_end;
  519. }
  520. } else {
  521. if last_pwm - DIGIT_MIN_BRIGHTNESS < digit.value {
  522. digit.pwm_start = DIGIT_MIN_BRIGHTNESS;
  523. digit.pwm_end = digit.pwm_start + digit.value;
  524. last_pwm = digit.pwm_end;
  525. incrementing = true;
  526. } else {
  527. digit.pwm_end = last_pwm;
  528. digit.pwm_start = digit.pwm_end - digit.value;
  529. last_pwm = digit.pwm_start;
  530. }
  531. }
  532. digit.updated = true;
  533. }
  534. };
  535. #[cfg(not(test))]
  536. self.tubes.iter_mut().for_each(|tube| {
  537. tube.digits.iter_mut().for_each(|digit| {
  538. update_digit(digit);
  539. });
  540. });
  541. #[cfg(test)]
  542. for (t, tube) in self.tubes.iter_mut().enumerate() {
  543. for (d, digit) in tube.digits.iter_mut().enumerate() {
  544. update_digit(digit);
  545. if digit.updated {
  546. println!(
  547. "Distribute PWM: tube {} digit {} start {} end {}",
  548. t, d, digit.pwm_start, digit.pwm_end
  549. );
  550. }
  551. }
  552. }
  553. update_digit(&mut self.dot);
  554. #[cfg(test)]
  555. println!(
  556. "Distribute PWM: dot start {} end {}",
  557. self.dot.pwm_start, self.dot.pwm_end
  558. );
  559. }
  560. }
  561. pub fn rtc_interrupt<T>(i2c: &mut T)
  562. where
  563. T: _embedded_hal_blocking_i2c_WriteRead
  564. + _embedded_hal_blocking_i2c_Read
  565. + _embedded_hal_blocking_i2c_Write,
  566. {
  567. let (second, minute, hour) = ds3231::get_time(DS3231_ADDR, i2c);
  568. let (weekday, day, month, _, _) = ds3231::get_date(DS3231_ADDR, i2c);
  569. let hour = if ds3231::in_dst(weekday, day, month, hour) {
  570. (hour + 1) % 12
  571. } else {
  572. hour % 12
  573. };
  574. let hour = if hour == 0 { 12 } else { hour };
  575. free(|cs| {
  576. let mut clock_ref = CLOCK.borrow(cs).borrow_mut();
  577. let clock = clock_ref.deref_mut();
  578. clock.rtc_tick(second, minute, hour);
  579. });
  580. }
  581. pub fn refresh_interrupt<T>(i2c: &mut T)
  582. where
  583. T: _embedded_hal_blocking_i2c_WriteRead
  584. + _embedded_hal_blocking_i2c_Read
  585. + _embedded_hal_blocking_i2c_Write,
  586. {
  587. free(|cs| {
  588. let mut clock_ref = CLOCK.borrow(cs).borrow_mut();
  589. let clock = clock_ref.deref_mut();
  590. let refresh = clock.refresh_tick();
  591. if refresh {
  592. clock.write_i2c(i2c);
  593. free(|cs| {
  594. let mut timer_ref = super::REFRESH_TIMER.borrow(cs).borrow_mut();
  595. if let Some(ref mut timer) = timer_ref.deref_mut() {
  596. timer.clear_interrupt(Event::TimeOut);
  597. }
  598. })
  599. } else {
  600. free(|cs| {
  601. let mut timer_ref = super::REFRESH_TIMER.borrow(cs).borrow_mut();
  602. if let Some(ref mut timer) = timer_ref.deref_mut() {
  603. timer.unlisten(Event::TimeOut);
  604. }
  605. })
  606. }
  607. });
  608. }
  609. #[cfg(test)]
  610. mod test {
  611. use super::*;
  612. use std::println;
  613. #[test]
  614. fn pwm_calc_test() {
  615. let mut clock: Clock = Clock::default();
  616. clock.rtc_tick(10, 23, 12);
  617. for tick in 0..1005 {
  618. println!("\nRefresh tick: {}", tick);
  619. if !clock.refresh_tick() {
  620. println!("Refresh halted");
  621. break;
  622. }
  623. }
  624. }
  625. }