board.cpp 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235
  1. #include "board.h"
  2. #include <avr/interrupt.h>
  3. #include <avr/io.h>
  4. #include <avr/sleep.h>
  5. #include <avr/wdt.h>
  6. #include <string.h>
  7. #include <util/delay.h>
  8. #define LED PB5
  9. void Board::toggle_led() { PORTB ^= (1 << LED); }
  10. void Board::init_start() {
  11. // Initialize the application including WDT, PORTB.5
  12. // We will now disable the watchdog.
  13. // Service the watchdog just to be sure to avoid pending timeout.
  14. wdt_reset();
  15. // Clear WDRF in MCUSR.
  16. MCUSR &= ~(1U << WDRF);
  17. // Write logical one to WDCE and WDE.
  18. // Keep the old prescaler setting to prevent unintentional time-out.
  19. WDTCSR |= (1U << WDCE) | (1U << WDE);
  20. // Turn off the WDT.
  21. WDTCSR = 0x00;
  22. // We will now initialize PORTB.5 to be used as an LED driver port.
  23. // Set PORTB.5 value to low.
  24. PORTB &= ~(1U << PORTB5);
  25. /* initializing PB5 which is connected to port 13 of uno as output*/
  26. DDRB |= (1 << LED);
  27. }
  28. void Board::enable_timer() {
  29. // We will now initialize the TIMER0 clock and interrupt.
  30. // Clear the TIMER0 overflow flag.
  31. TIFR0 = static_cast<std::uint8_t>(1U << TOV0);
  32. // Enable the TIMER0 overflow interrupt.
  33. TIMSK0 = static_cast<std::uint8_t>(1U << TOIE0);
  34. // Set the TIMER0 clock source to f_osc/8 = 2MHz and begin counting.
  35. TCCR0B = static_cast<std::uint8_t>(1U << CS01);
  36. }
  37. void Board::disable_timer() {
  38. // erase TIMER0 clock source;
  39. TCCR0B = 0;
  40. // Clear the TIMER0 overflow flag.
  41. TIMSK0 = 0;
  42. // Enable the TIMER0 overflow interrupt.
  43. TIMSK0 = 0;
  44. }
  45. std::uint64_t gpt_system_tick = 0;
  46. rl::TimePoint Board::get_now() {
  47. // The entire system-tick is composed of the static
  48. // 64-bit variable mcal_gpt_system_tick and the 8-bit
  49. // timer register TCNT0. These are concatenated together
  50. // in software as a cohesive, consistent 64-bit tick.
  51. // This subroutine returns the concatenated 64-bit
  52. // mcal_gpt_system_tick/TCNT0 64-bit system tick.
  53. // A multiple read of the tick is used in order
  54. // to ensure data consistency.
  55. // Do the first read of the TIMER0 counter and the system tick.
  56. const auto tim0_cnt_1 = TCNT0;
  57. const auto sys_tick_1 = gpt_system_tick;
  58. // Do the second read of the TIMER0 counter.
  59. const auto tim0_cnt_2 = TCNT0;
  60. // Perform the consistency check to obtain the concatenated,
  61. // consistent, 64-bit system-tick.
  62. const auto consistent_microsecond_tick =
  63. ((tim0_cnt_2 >= tim0_cnt_1)
  64. ? static_cast<std::uint64_t>(
  65. sys_tick_1 | static_cast<std::uint8_t>(tim0_cnt_1 >> 1U))
  66. : static_cast<std::uint64_t>(
  67. gpt_system_tick |
  68. static_cast<std::uint8_t>(tim0_cnt_2 >> 1U)));
  69. return consistent_microsecond_tick;
  70. }
  71. void adjust_timer(const rotor_light::Duration &value) {
  72. gpt_system_tick += value;
  73. }
  74. static UARTRxCallback uart_rx;
  75. static UARTTxCallback uart_tx;
  76. void Board::enable_uart(UARTRxCallback rx, UARTTxCallback tx) {
  77. uart_rx = rx;
  78. uart_tx = tx;
  79. // USART
  80. #define BAUD 9600
  81. #include <util/setbaud.h>
  82. UBRR0H = UBRRH_VALUE;
  83. UBRR0L = UBRRL_VALUE;
  84. UCSR0B = (1 << RXEN0) // enable rx
  85. | (1 << TXEN0) // enable tx
  86. | (1 << RXCIE0) // enable rx interrupt
  87. ;
  88. /* Set frame format: 8data, 2stop bit */
  89. UCSR0C = (1 << USBS0) | (3 << UCSZ00);
  90. }
  91. void Board::send_uart(char value) {
  92. // enable empty data buffer interrupt
  93. UCSR0B |= (1 << UDRIE0);
  94. while (!(UCSR0A & (1 << UDRE0))) {
  95. // NO-OP
  96. }
  97. UDR0 = value;
  98. }
  99. void Board::enable_usart() {
  100. // USART
  101. #define BAUD 9600
  102. #include <util/setbaud.h>
  103. UBRR0H = UBRRH_VALUE;
  104. UBRR0L = UBRRL_VALUE;
  105. UCSR0B = (1 << RXEN0) | (1 << TXEN0);
  106. /* Set frame format: 8data, 2stop bit */
  107. UCSR0C = (1 << USBS0) | (3 << UCSZ00);
  108. }
  109. void Board::send_usart(const char *data) { send_usart(data, strlen(data)); }
  110. void Board::send_usart(const char *data, size_t count) {
  111. auto end = data + count;
  112. while (data != end) {
  113. while (!(UCSR0A & (1 << UDRE0)))
  114. ;
  115. UDR0 = static_cast<uint8_t>(*data++);
  116. }
  117. }
  118. void Board::sleep(const rl::TimePoint &until) {
  119. auto now = get_now();
  120. auto left = until - now;
  121. auto amount = rl::Duration{};
  122. auto sleep_constant = uint8_t{};
  123. cli();
  124. disable_timer();
  125. while (left > 0) {
  126. cli();
  127. if (left < 15) {
  128. break;
  129. } else if (left < 30) {
  130. amount = 15;
  131. sleep_constant = WDTO_15MS;
  132. } else if (left < 60) {
  133. amount = 30;
  134. sleep_constant = WDTO_30MS;
  135. } else if (left < 120) {
  136. amount = 60;
  137. sleep_constant = WDTO_60MS;
  138. } else if (left < 250) {
  139. amount = 120;
  140. sleep_constant = WDTO_120MS;
  141. } else if (left < 500) {
  142. amount = 250;
  143. sleep_constant = WDTO_250MS;
  144. } else if (left < 1000) {
  145. amount = 500;
  146. sleep_constant = WDTO_500MS;
  147. } else if (left < 2000) {
  148. amount = 1000;
  149. sleep_constant = WDTO_1S;
  150. } else if (left < 4000) {
  151. amount = 2000;
  152. sleep_constant = WDTO_2S;
  153. } else if (left < 8000) {
  154. amount = 4000;
  155. sleep_constant = WDTO_4S;
  156. } else {
  157. amount = 8000;
  158. sleep_constant = WDTO_8S;
  159. }
  160. TCNT0 = 0;
  161. adjust_timer(amount * 1000);
  162. wdt_reset();
  163. wdt_enable(sleep_constant);
  164. // enable watchdog interrupt
  165. WDTCSR |= (1 << WDIE);
  166. set_sleep_mode(SLEEP_MODE_PWR_DOWN);
  167. sleep_enable();
  168. sei();
  169. sleep_cpu();
  170. sleep_disable();
  171. sei();
  172. left -= amount;
  173. }
  174. enable_timer();
  175. sei();
  176. }
  177. void Board::delay() { _delay_ms(1000); }
  178. ISR(TIMER0_OVF_vect) {
  179. // Increment the 64-bit system tick with 0x80, representing 128 microseconds.
  180. gpt_system_tick += UINT8_C(0x80);
  181. }
  182. ISR(USART_RX_vect) {
  183. if (UCSR0A & (1 << RXC0)) {
  184. uart_rx(static_cast<char>(UDR0));
  185. }
  186. }
  187. ISR(USART_UDRE_vect) {
  188. // disable empty data buffer interrupt
  189. UCSR0B &= ~(1 << UDRIE0);
  190. uart_tx();
  191. }
  192. ISR(WDT_vect) {
  193. // WDIE & WDIF is cleared in hardware upon entering this ISR
  194. wdt_disable();
  195. }