pb_txen.c 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233
  1. /*
  2. * PiLC HAT firmware
  3. * PROFIBUS-DP PHY - TxEnable handler
  4. *
  5. * Copyright (c) 2016 Michael Buesch <m@bues.ch>
  6. *
  7. * This program is free software; you can redistribute it and/or modify
  8. * it under the terms of the GNU General Public License as published by
  9. * the Free Software Foundation; either version 2 of the License, or
  10. * (at your option) any later version.
  11. *
  12. * This program is distributed in the hope that it will be useful,
  13. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  14. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  15. * GNU General Public License for more details.
  16. *
  17. * You should have received a copy of the GNU General Public License along
  18. * with this program; if not, write to the Free Software Foundation, Inc.,
  19. * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
  20. */
  21. #include "pb_txen.h"
  22. #include <string.h>
  23. #include <avr/io.h>
  24. #include <avr/interrupt.h>
  25. #include <avr/wdt.h>
  26. /* Tx pin definitions (input) */
  27. #define PB_TXPORT PORTB
  28. #define PB_TXPIN PINB
  29. #define PB_TXDDR DDRB
  30. #define PB_TXBIT PB3
  31. #define PB_TXPOL -1
  32. /* Tx-enable pin definitions (output) */
  33. #define PB_TXENPORT PORTB
  34. #define PB_TXENDDR DDRB
  35. #define PB_TXENBIT PB4
  36. #define PB_TXENPOL 1
  37. struct pb_txen_state {
  38. bool txen; /* Current TxEn state. */
  39. bool debug; /* Debug mode enabled? */
  40. uint8_t ps; /* The active prescaler. */
  41. uint16_t timeout_us; /* TxEn timeout in microseconds. */
  42. };
  43. static struct pb_txen_state pb_txen;
  44. static inline bool pb_tx_get(void)
  45. {
  46. bool tx_state;
  47. tx_state = !!(PB_TXPIN & (1 << PB_TXBIT));
  48. if (PB_TXPOL < 0)
  49. tx_state = !tx_state;
  50. return tx_state;
  51. }
  52. static inline void pb_txen_set(bool enable)
  53. {
  54. if (enable) {
  55. /* Set to high. */
  56. PB_TXENDDR |= (1 << PB_TXENBIT);
  57. if (PB_TXENPOL < 0)
  58. PB_TXENPORT &= (uint8_t)~(1 << PB_TXENBIT);
  59. else
  60. PB_TXENPORT |= (1 << PB_TXENBIT);
  61. } else {
  62. /* Set to floating. */
  63. PB_TXENDDR &= (uint8_t)~(1 << PB_TXENBIT);
  64. PB_TXENPORT &= (uint8_t)~(1 << PB_TXENBIT);
  65. }
  66. }
  67. static inline void pb_txen_timer_config(uint8_t ps)
  68. {
  69. TCCR1 = (0 << CTC1) | (0 << PWM1A) |
  70. (0 << COM1A1) | (0 << COM1A0) |
  71. ps;
  72. }
  73. static inline void pb_txen_timer_start(void)
  74. {
  75. TCNT1 = 0;
  76. pb_txen_timer_config(pb_txen.ps);
  77. }
  78. static inline void pb_txen_timer_stop(void)
  79. {
  80. /* Disable prescaler */
  81. pb_txen_timer_config(0);
  82. }
  83. ISR(TIMER1_COMPA_vect)
  84. {
  85. pb_txen_set(false);
  86. pb_txen_timer_stop();
  87. pb_txen.txen = false;
  88. memory_barrier();
  89. }
  90. static void pb_txen_timer_init(void)
  91. {
  92. pb_txen_timer_stop();
  93. PLLCSR = 0;
  94. GTCCR &= (uint8_t)~((1 << PWM1B) | (1 << COM1B1) | (1 << COM1B0));
  95. TCNT1 = 0;
  96. OCR1A = 0;
  97. OCR1B = 0;
  98. OCR1C = 0;
  99. TIFR = (1 << OCF1A);
  100. TIMSK |= (1 << OCIE1A);
  101. }
  102. void pb_txen_set_timeout(uint16_t microseconds)
  103. {
  104. uint32_t mul, div, ocr;
  105. uint8_t ps_idx, ps;
  106. static const uint16_t clkdivs[] = {
  107. 1, 2, 4, 8, 16, 32, 64, 128, 256,
  108. 512, 1024, 2048, 4096, 8192, 16384,
  109. };
  110. static const uint8_t ps_tab[] = {
  111. ((0 << CS13) | (0 << CS12) | (0 << CS11) | (1 << CS10)),
  112. ((0 << CS13) | (0 << CS12) | (1 << CS11) | (0 << CS10)),
  113. ((0 << CS13) | (0 << CS12) | (1 << CS11) | (1 << CS10)),
  114. ((0 << CS13) | (1 << CS12) | (0 << CS11) | (0 << CS10)),
  115. ((0 << CS13) | (1 << CS12) | (0 << CS11) | (1 << CS10)),
  116. ((0 << CS13) | (1 << CS12) | (1 << CS11) | (0 << CS10)),
  117. ((0 << CS13) | (1 << CS12) | (1 << CS11) | (1 << CS10)),
  118. ((1 << CS13) | (0 << CS12) | (0 << CS11) | (0 << CS10)),
  119. ((1 << CS13) | (0 << CS12) | (0 << CS11) | (1 << CS10)),
  120. ((1 << CS13) | (0 << CS12) | (1 << CS11) | (0 << CS10)),
  121. ((1 << CS13) | (0 << CS12) | (1 << CS11) | (1 << CS10)),
  122. ((1 << CS13) | (1 << CS12) | (0 << CS11) | (0 << CS10)),
  123. ((1 << CS13) | (1 << CS12) | (0 << CS11) | (1 << CS10)),
  124. ((1 << CS13) | (1 << CS12) | (1 << CS11) | (0 << CS10)),
  125. ((1 << CS13) | (1 << CS12) | (1 << CS11) | (1 << CS10)),
  126. };
  127. build_assert(ARRAY_SIZE(clkdivs) == ARRAY_SIZE(ps_tab));
  128. mul = 1000000UL;
  129. div = F_CPU;
  130. while ((mul % 10 == 0) && (div % 10 == 0)) {
  131. mul /= 10;
  132. div /= 10;
  133. }
  134. for (ps_idx = 0; ps_idx < ARRAY_SIZE(clkdivs); ps_idx++) {
  135. ocr = udiv_round_up(mul * (uint32_t)microseconds,
  136. div * (uint32_t)clkdivs[ps_idx]);
  137. ps = ps_tab[ps_idx];
  138. if (ocr <= 0xFF)
  139. break;
  140. }
  141. pb_txen_set(false);
  142. pb_txen_timer_stop();
  143. pb_txen.txen = false;
  144. pb_txen.ps = ps;
  145. pb_txen.timeout_us = microseconds;
  146. OCR1A = (uint8_t)ocr;
  147. memory_barrier();
  148. }
  149. uint16_t pb_txen_get_timeout(void)
  150. {
  151. return pb_txen.timeout_us;
  152. }
  153. void pb_txen_set_debug(bool debug)
  154. {
  155. pb_txen.debug = debug;
  156. }
  157. bool pb_txen_get_debug(void)
  158. {
  159. return pb_txen.debug;
  160. }
  161. void pb_txen_init(void)
  162. {
  163. memset(&pb_txen, 0, sizeof(pb_txen));
  164. /* Initialize input */
  165. PB_TXPORT &= (uint8_t)~(1 << PB_TXBIT);
  166. PB_TXDDR &= (uint8_t)~(1 << PB_TXBIT);
  167. /* Initialize output */
  168. pb_txen_set(false);
  169. /* Wait for pin capacities */
  170. _delay_ms(20);
  171. pb_txen_timer_init();
  172. pb_txen_set_timeout(1000);
  173. }
  174. void pb_txen_work(void)
  175. {
  176. irq_enable();
  177. while (1) {
  178. wdt_reset();
  179. memory_barrier();
  180. if (pb_tx_get() || pb_txen.debug) {
  181. /* We are transmitting. Set TxEn. */
  182. pb_txen_set(true);
  183. memory_barrier();
  184. if (!pb_txen.txen) {
  185. /* We just started transmitting.
  186. * Start the TxEn timer. */
  187. pb_txen_timer_start();
  188. pb_txen.txen = true;
  189. }
  190. }
  191. }
  192. }