PlatformIO package of the Teensy core framework compatible with GCC 10 & C++20
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.

361 line
8.5KB

  1. /* An Alternative Software Serial Library
  2. * http://www.pjrc.com/teensy/td_libs_AltSoftSerial.html
  3. * Copyright (c) 2014 PJRC.COM, LLC, Paul Stoffregen, paul@pjrc.com
  4. *
  5. * Permission is hereby granted, free of charge, to any person obtaining a copy
  6. * of this software and associated documentation files (the "Software"), to deal
  7. * in the Software without restriction, including without limitation the rights
  8. * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  9. * copies of the Software, and to permit persons to whom the Software is
  10. * furnished to do so, subject to the following conditions:
  11. *
  12. * The above copyright notice and this permission notice shall be included in
  13. * all copies or substantial portions of the Software.
  14. *
  15. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  16. * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  17. * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  18. * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  19. * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  20. * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  21. * THE SOFTWARE.
  22. */
  23. // Revisions are now tracked on GitHub
  24. // https://github.com/PaulStoffregen/AltSoftSerial
  25. //
  26. // Version 1.2: Support Teensy 3.x
  27. //
  28. // Version 1.1: Improve performance in receiver code
  29. //
  30. // Version 1.0: Initial Release
  31. #include "AltSoftSerial.h"
  32. #include "config/AltSoftSerial_Boards.h"
  33. #include "config/AltSoftSerial_Timers.h"
  34. /****************************************/
  35. /** Initialization **/
  36. /****************************************/
  37. static uint16_t ticks_per_bit=0;
  38. bool AltSoftSerial::timing_error=false;
  39. static uint8_t rx_state;
  40. static uint8_t rx_byte;
  41. static uint8_t rx_bit = 0;
  42. static uint16_t rx_target;
  43. static uint16_t rx_stop_ticks=0;
  44. static volatile uint8_t rx_buffer_head;
  45. static volatile uint8_t rx_buffer_tail;
  46. #define RX_BUFFER_SIZE 80
  47. static volatile uint8_t rx_buffer[RX_BUFFER_SIZE];
  48. static volatile uint8_t tx_state=0;
  49. static uint8_t tx_byte;
  50. static uint8_t tx_bit;
  51. static volatile uint8_t tx_buffer_head;
  52. static volatile uint8_t tx_buffer_tail;
  53. #define TX_BUFFER_SIZE 68
  54. static volatile uint8_t tx_buffer[TX_BUFFER_SIZE];
  55. #ifndef INPUT_PULLUP
  56. #define INPUT_PULLUP INPUT
  57. #endif
  58. #define MAX_COUNTS_PER_BIT 6241 // 65536 / 10.5
  59. void AltSoftSerial::init(uint32_t cycles_per_bit)
  60. {
  61. //Serial.printf("cycles_per_bit = %d\n", cycles_per_bit);
  62. if (cycles_per_bit < MAX_COUNTS_PER_BIT) {
  63. CONFIG_TIMER_NOPRESCALE();
  64. } else {
  65. cycles_per_bit /= 8;
  66. //Serial.printf("cycles_per_bit/8 = %d\n", cycles_per_bit);
  67. if (cycles_per_bit < MAX_COUNTS_PER_BIT) {
  68. CONFIG_TIMER_PRESCALE_8();
  69. } else {
  70. #if defined(CONFIG_TIMER_PRESCALE_256)
  71. cycles_per_bit /= 32;
  72. //Serial.printf("cycles_per_bit/256 = %d\n", cycles_per_bit);
  73. if (cycles_per_bit < MAX_COUNTS_PER_BIT) {
  74. CONFIG_TIMER_PRESCALE_256();
  75. } else {
  76. return; // baud rate too low for AltSoftSerial
  77. }
  78. #elif defined(CONFIG_TIMER_PRESCALE_128)
  79. cycles_per_bit /= 16;
  80. //Serial.printf("cycles_per_bit/128 = %d\n", cycles_per_bit);
  81. if (cycles_per_bit < MAX_COUNTS_PER_BIT) {
  82. CONFIG_TIMER_PRESCALE_128();
  83. } else {
  84. return; // baud rate too low for AltSoftSerial
  85. }
  86. #else
  87. return; // baud rate too low for AltSoftSerial
  88. #endif
  89. }
  90. }
  91. ticks_per_bit = cycles_per_bit;
  92. rx_stop_ticks = cycles_per_bit * 37 / 4;
  93. pinMode(INPUT_CAPTURE_PIN, INPUT_PULLUP);
  94. digitalWrite(OUTPUT_COMPARE_A_PIN, HIGH);
  95. pinMode(OUTPUT_COMPARE_A_PIN, OUTPUT);
  96. rx_state = 0;
  97. rx_buffer_head = 0;
  98. rx_buffer_tail = 0;
  99. tx_state = 0;
  100. tx_buffer_head = 0;
  101. tx_buffer_tail = 0;
  102. ENABLE_INT_INPUT_CAPTURE();
  103. }
  104. void AltSoftSerial::end(void)
  105. {
  106. DISABLE_INT_COMPARE_B();
  107. DISABLE_INT_INPUT_CAPTURE();
  108. flushInput();
  109. flushOutput();
  110. DISABLE_INT_COMPARE_A();
  111. // TODO: restore timer to original settings?
  112. }
  113. /****************************************/
  114. /** Transmission **/
  115. /****************************************/
  116. void AltSoftSerial::writeByte(uint8_t b)
  117. {
  118. uint8_t intr_state, head;
  119. head = tx_buffer_head + 1;
  120. if (head >= TX_BUFFER_SIZE) head = 0;
  121. while (tx_buffer_tail == head) ; // wait until space in buffer
  122. intr_state = SREG;
  123. cli();
  124. if (tx_state) {
  125. tx_buffer[head] = b;
  126. tx_buffer_head = head;
  127. } else {
  128. tx_state = 1;
  129. tx_byte = b;
  130. tx_bit = 0;
  131. ENABLE_INT_COMPARE_A();
  132. CONFIG_MATCH_CLEAR();
  133. SET_COMPARE_A(GET_TIMER_COUNT() + 16);
  134. }
  135. SREG = intr_state;
  136. }
  137. ISR(COMPARE_A_INTERRUPT)
  138. {
  139. uint8_t state, byte, bit, head, tail;
  140. uint16_t target;
  141. state = tx_state;
  142. byte = tx_byte;
  143. target = GET_COMPARE_A();
  144. while (state < 10) {
  145. target += ticks_per_bit;
  146. if (state < 9)
  147. bit = byte & 1;
  148. else
  149. bit = 1; // stopbit
  150. byte >>= 1;
  151. state++;
  152. if (bit != tx_bit) {
  153. if (bit) {
  154. CONFIG_MATCH_SET();
  155. } else {
  156. CONFIG_MATCH_CLEAR();
  157. }
  158. SET_COMPARE_A(target);
  159. tx_bit = bit;
  160. tx_byte = byte;
  161. tx_state = state;
  162. // TODO: how to detect timing_error?
  163. return;
  164. }
  165. }
  166. head = tx_buffer_head;
  167. tail = tx_buffer_tail;
  168. if (head == tail) {
  169. if (state == 10) {
  170. // Wait for final stop bit to finish
  171. tx_state = 11;
  172. SET_COMPARE_A(target + ticks_per_bit);
  173. } else {
  174. tx_state = 0;
  175. CONFIG_MATCH_NORMAL();
  176. DISABLE_INT_COMPARE_A();
  177. }
  178. } else {
  179. if (++tail >= TX_BUFFER_SIZE) tail = 0;
  180. tx_buffer_tail = tail;
  181. tx_byte = tx_buffer[tail];
  182. tx_bit = 0;
  183. CONFIG_MATCH_CLEAR();
  184. if (state == 10)
  185. SET_COMPARE_A(target + ticks_per_bit);
  186. else
  187. SET_COMPARE_A(GET_TIMER_COUNT() + 16);
  188. tx_state = 1;
  189. // TODO: how to detect timing_error?
  190. }
  191. }
  192. void AltSoftSerial::flushOutput(void)
  193. {
  194. while (tx_state) /* wait */ ;
  195. }
  196. /****************************************/
  197. /** Reception **/
  198. /****************************************/
  199. ISR(CAPTURE_INTERRUPT)
  200. {
  201. uint8_t state, bit, head;
  202. uint16_t capture, target;
  203. uint16_t offset, offset_overflow;
  204. capture = GET_INPUT_CAPTURE();
  205. bit = rx_bit;
  206. if (bit) {
  207. CONFIG_CAPTURE_FALLING_EDGE();
  208. rx_bit = 0;
  209. } else {
  210. CONFIG_CAPTURE_RISING_EDGE();
  211. rx_bit = 0x80;
  212. }
  213. state = rx_state;
  214. if (state == 0) {
  215. if (!bit) {
  216. uint16_t end = capture + rx_stop_ticks;
  217. SET_COMPARE_B(end);
  218. ENABLE_INT_COMPARE_B();
  219. rx_target = capture + ticks_per_bit + ticks_per_bit/2;
  220. rx_state = 1;
  221. }
  222. } else {
  223. target = rx_target;
  224. offset_overflow = 65535 - ticks_per_bit;
  225. while (1) {
  226. offset = capture - target;
  227. if (offset > offset_overflow) break;
  228. rx_byte = (rx_byte >> 1) | rx_bit;
  229. target += ticks_per_bit;
  230. state++;
  231. if (state >= 9) {
  232. DISABLE_INT_COMPARE_B();
  233. head = rx_buffer_head + 1;
  234. if (head >= RX_BUFFER_SIZE) head = 0;
  235. if (head != rx_buffer_tail) {
  236. rx_buffer[head] = rx_byte;
  237. rx_buffer_head = head;
  238. }
  239. CONFIG_CAPTURE_FALLING_EDGE();
  240. rx_bit = 0;
  241. rx_state = 0;
  242. return;
  243. }
  244. }
  245. rx_target = target;
  246. rx_state = state;
  247. }
  248. //if (GET_TIMER_COUNT() - capture > ticks_per_bit) AltSoftSerial::timing_error = true;
  249. }
  250. ISR(COMPARE_B_INTERRUPT)
  251. {
  252. uint8_t head, state, bit;
  253. DISABLE_INT_COMPARE_B();
  254. CONFIG_CAPTURE_FALLING_EDGE();
  255. state = rx_state;
  256. bit = rx_bit ^ 0x80;
  257. while (state < 9) {
  258. rx_byte = (rx_byte >> 1) | bit;
  259. state++;
  260. }
  261. head = rx_buffer_head + 1;
  262. if (head >= RX_BUFFER_SIZE) head = 0;
  263. if (head != rx_buffer_tail) {
  264. rx_buffer[head] = rx_byte;
  265. rx_buffer_head = head;
  266. }
  267. rx_state = 0;
  268. CONFIG_CAPTURE_FALLING_EDGE();
  269. rx_bit = 0;
  270. }
  271. int AltSoftSerial::read(void)
  272. {
  273. uint8_t head, tail, out;
  274. head = rx_buffer_head;
  275. tail = rx_buffer_tail;
  276. if (head == tail) return -1;
  277. if (++tail >= RX_BUFFER_SIZE) tail = 0;
  278. out = rx_buffer[tail];
  279. rx_buffer_tail = tail;
  280. return out;
  281. }
  282. int AltSoftSerial::peek(void)
  283. {
  284. uint8_t head, tail;
  285. head = rx_buffer_head;
  286. tail = rx_buffer_tail;
  287. if (head == tail) return -1;
  288. if (++tail >= RX_BUFFER_SIZE) tail = 0;
  289. return rx_buffer[tail];
  290. }
  291. int AltSoftSerial::available(void)
  292. {
  293. uint8_t head, tail;
  294. head = rx_buffer_head;
  295. tail = rx_buffer_tail;
  296. if (head >= tail) return head - tail;
  297. return RX_BUFFER_SIZE + head - tail;
  298. }
  299. int AltSoftSerial::availableForWrite(void)
  300. {
  301. uint8_t head, tail;
  302. head = tx_buffer_head;
  303. tail = tx_buffer_tail;
  304. if (tail > head) return tail - head;
  305. return TX_BUFFER_SIZE + tail - head;
  306. };
  307. void AltSoftSerial::flushInput(void)
  308. {
  309. rx_buffer_head = rx_buffer_tail;
  310. }
  311. #ifdef ALTSS_USE_FTM0
  312. void ftm0_isr(void)
  313. {
  314. uint32_t flags = FTM0_STATUS;
  315. FTM0_STATUS = 0;
  316. if (flags & (1<<0) && (FTM0_C0SC & 0x40)) altss_compare_b_interrupt();
  317. if (flags & (1<<5)) altss_capture_interrupt();
  318. if (flags & (1<<6) && (FTM0_C6SC & 0x40)) altss_compare_a_interrupt();
  319. }
  320. #endif