You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

189 lines
6.0KB

  1. // Copyright 2009 Olivier Gillet.
  2. //
  3. // Author: Olivier Gillet (ol.gillet@gmail.com)
  4. //
  5. // This program is free software: you can redistribute it and/or modify
  6. // it under the terms of the GNU General Public License as published by
  7. // the Free Software Foundation, either version 3 of the License, or
  8. // (at your option) any later version.
  9. // This program is distributed in the hope that it will be useful,
  10. // but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. // GNU General Public License for more details.
  13. // You should have received a copy of the GNU General Public License
  14. // along with this program. If not, see <http://www.gnu.org/licenses/>.
  15. //
  16. // -----------------------------------------------------------------------------
  17. //
  18. // BufferedSoftwareSerialOutput, the name says it all:
  19. // - It is software serial.
  20. // - Contrary to the other Software Serial implementations out there which use
  21. // fine-tuned busy loop to delay between bits, this implementation bangs bits
  22. // in a timer interrupt ; while writes are done in an asynchronous, non-blocking
  23. // way to a buffer.
  24. //
  25. // Caveats:
  26. // - The timer rate must be a multiple of the baud rate otherwise this will
  27. // miserably FAIL.
  28. // - Tested only for 2400 (main timer at 31250 Hz) and 4800 (main timer at
  29. // 62500 Hz) baud per seconds.
  30. // - Please check the timing tolerance of your device's UART!
  31. //
  32. // SoftwareSerialOutput is a Vanilla blocking implementation, taken from the
  33. // Arduino libs ; Copyright (c) 2006 David A. Mellis.
  34. #ifndef AVRLIB_SOFTWARE_SERIAL_H_
  35. #define AVRLIB_SOFTWARE_SERIAL_H_
  36. #include "avrlib/avrlib.h"
  37. #include "avrlib/gpio.h"
  38. #include "avrlib/ring_buffer.h"
  39. namespace avrlib {
  40. enum SoftwareSerialState {
  41. START_BIT = 0,
  42. END_BIT = 9,
  43. NEXT_BYTE = 10
  44. };
  45. // Parameters:
  46. // TxPin: digital pin used for transmission.
  47. // timer_rate: frequency (Hz) of the timer which will drive the output.
  48. // baud_rate: target baud rate. must be a divisor of timer_rate.
  49. // buffer_size: prefered buffer size.
  50. template<typename TxPin, uint16_t timer_rate, uint16_t baud_rate,
  51. uint8_t buffer_size_>
  52. class BufferedSoftwareSerialOutput {
  53. typedef BufferedSoftwareSerialOutput<TxPin, timer_rate,
  54. baud_rate, buffer_size_> Me;
  55. typedef RingBuffer<Me> OutputBuffer;
  56. public:
  57. typedef uint8_t Value;
  58. enum {
  59. prescaler_reset_value = timer_rate / baud_rate,
  60. buffer_size = buffer_size_,
  61. data_size = 8
  62. };
  63. static void Init() {
  64. prescaler_counter_ = prescaler_reset_value;
  65. tx_state_ = NEXT_BYTE;
  66. TxPin::set_mode(DIGITAL_OUTPUT);
  67. TxPin::High();
  68. }
  69. static inline void Write(Value v) {
  70. OutputBuffer::Write(v);
  71. }
  72. static inline uint8_t writable() { return OutputBuffer::writable(); }
  73. static inline uint8_t NonBlockingWrite(Value v ) {
  74. return OutputBuffer::NonBlockingWrite(v);
  75. }
  76. static inline void Overwrite(Value v) { OutputBuffer::Overwrite(v); }
  77. static inline void Tick() {
  78. --prescaler_counter_;
  79. if (prescaler_counter_ > 0) {
  80. return;
  81. }
  82. prescaler_counter_ = prescaler_reset_value;
  83. // Check in which stage of the transmission we are.
  84. if (tx_state_ == NEXT_BYTE) {
  85. // Attempt to start the transmission of the next byte in the buffer, (if
  86. // there is any).
  87. int16_t next_byte = OutputBuffer::NonBlockingRead();
  88. if (next_byte >= 0) {
  89. tx_byte_ = next_byte;
  90. tx_state_ = START_BIT;
  91. return;
  92. } else {
  93. return;
  94. }
  95. } else if (tx_state_ == START_BIT) {
  96. TxPin::Low();
  97. tx_symbol_mask_ = 1;
  98. } else if (tx_state_ == END_BIT) {
  99. TxPin::High();
  100. } else {
  101. TxPin::set_value(tx_byte_ & tx_symbol_mask_);
  102. tx_symbol_mask_ <<= 1;
  103. }
  104. ++tx_state_;
  105. }
  106. private:
  107. static uint8_t prescaler_counter_;
  108. // Mask shifted every symbol, used to extract bits from the current character
  109. // to transmit.
  110. static uint8_t tx_symbol_mask_;
  111. static uint8_t tx_state_;
  112. static uint8_t tx_byte_;
  113. DISALLOW_COPY_AND_ASSIGN(BufferedSoftwareSerialOutput);
  114. };
  115. template<typename TxPin, uint16_t timer_rate, uint16_t baud_rate,
  116. uint8_t buffer_size_>
  117. uint8_t BufferedSoftwareSerialOutput<TxPin, timer_rate, baud_rate,
  118. buffer_size_>::prescaler_counter_;
  119. template<typename TxPin, uint16_t timer_rate, uint16_t baud_rate,
  120. uint8_t buffer_size_>
  121. uint8_t BufferedSoftwareSerialOutput<TxPin, timer_rate, baud_rate,
  122. buffer_size_>::tx_symbol_mask_;
  123. template<typename TxPin, uint16_t timer_rate, uint16_t baud_rate,
  124. uint8_t buffer_size_>
  125. uint8_t BufferedSoftwareSerialOutput<TxPin, timer_rate, baud_rate,
  126. buffer_size_>::tx_state_;
  127. template<typename TxPin, uint16_t timer_rate, uint16_t baud_rate,
  128. uint8_t buffer_size_>
  129. uint8_t BufferedSoftwareSerialOutput<TxPin, timer_rate, baud_rate,
  130. buffer_size_>::tx_byte_;
  131. // Parameters:
  132. // TxPin: digital pin used for transmission.
  133. // baud_rate: target baud rate. must be a divisor of timer_rate.
  134. // Following code from NewSoftSerial, Copyright (c) 2006 David A. Mellis.
  135. template<typename TxPin, uint16_t baud_rate>
  136. struct SoftwareSerialOutput {
  137. static void Init() {
  138. TxPin::set_mode(DIGITAL_OUTPUT);
  139. }
  140. static void Write(uint8_t tx_byte) {
  141. uint8_t oldSREG = SREG;
  142. cli();
  143. uint16_t delay = (F_CPU / baud_rate) / 7;
  144. uint16_t tx_delay = delay - 5;
  145. TxPin::Low();
  146. TunedDelay(delay);
  147. for (uint8_t mask = 1; mask; mask <<= 1) {
  148. TxPin::set_value(tx_byte & mask);
  149. TunedDelay(tx_delay);
  150. }
  151. TxPin::High();
  152. SREG = oldSREG;
  153. TunedDelay(delay);
  154. }
  155. static inline void TunedDelay(uint16_t delay) {
  156. uint8_t tmp = 0;
  157. asm volatile(
  158. "sbiw %0, 0x01" "\n\t"
  159. "ldi %1, 0xff" "\n\t"
  160. "cpi %A0, 0xff" "\n\t"
  161. "cpc %B0, %1" "\n\t"
  162. "brne .-10" "\n\t"
  163. : "+r" (delay), "+a" (tmp)
  164. : "0" (delay)
  165. );
  166. }
  167. };
  168. } // namespace avrlib
  169. #endif // AVRLIB_SOFTWARE_SERIAL_H_