board.cpp 5.8 KB

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