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.

376 lines
11KB

  1. // Copyright 2011 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. // Timer/counter
  19. #ifndef AVRLIBX_SYSTEM_TIMER_H_
  20. #define AVRLIBX_SYSTEM_TIMER_H_
  21. #include <avr/delay.h>
  22. #include "avrlibx/avrlibx.h"
  23. #include "avrlibx/io/gpio.h"
  24. #include "avrlibx/system/event_system.h"
  25. namespace avrlibx {
  26. enum TimerMode {
  27. TIMER_MODE_NORMAL = 0,
  28. TIMER_MODE_FREQUENCY_GENERATOR = 1,
  29. TIMER_MODE_SINGLE_PWM = 3,
  30. TIMER_MODE_DUAL_PWM_T = 5,
  31. TIMER_MODE_DUAL_PWM_TB = 6,
  32. TIMER_MODE_DUAL_PWM_B = 7
  33. };
  34. enum TimerPrescaler {
  35. TIMER_PRESCALER_OFF = 0,
  36. TIMER_PRESCALER_CLK = 1,
  37. TIMER_PRESCALER_CLK_2 = 2,
  38. TIMER_PRESCALER_CLK_4 = 3,
  39. TIMER_PRESCALER_CLK_8 = 4,
  40. TIMER_PRESCALER_CLK_64 = 5,
  41. TIMER_PRESCALER_CLK_256 = 6,
  42. TIMER_PRESCALER_CLK_1024 = 7,
  43. };
  44. enum TimerChannel {
  45. TIMER_CHANNEL_A,
  46. TIMER_CHANNEL_B,
  47. TIMER_CHANNEL_C,
  48. TIMER_CHANNEL_D
  49. };
  50. enum TimerEventAction {
  51. TIMER_EVENT_ACTION_NONE = TC_EVACT_OFF_gc,
  52. TIMER_EVENT_ACTION_CAPTURE = TC_EVACT_CAPT_gc,
  53. TIMER_EVENT_ACTION_UPDOWN = TC_EVACT_UPDOWN_gc,
  54. TIMER_EVENT_ACTION_QDEC = TC_EVACT_QDEC_gc,
  55. TIMER_EVENT_ACTION_RESTART = TC_EVACT_RESTART_gc,
  56. TIMER_EVENT_ACTION_FRQ = TC_EVACT_FRW_gc,
  57. TIMER_EVENT_ACTION_PW = TC_EVACT_PW_gc
  58. };
  59. template<typename Port, uint8_t index> struct TCWrapper { };
  60. #define WRAP_TIMER(port, index) \
  61. template<> \
  62. struct TCWrapper<Port ## port, index> { \
  63. static inline TC0_t& tc() { \
  64. return (TC0_t&)(TC ## port ## index); \
  65. } \
  66. static volatile inline uint16_t count() { \
  67. return TC ## port ## index ## _CNT; \
  68. } \
  69. static inline void set_count(uint16_t value) { \
  70. TC ## port ## index ## _CNT = value; \
  71. } \
  72. static inline uint16_t period() { \
  73. return TC ## port ## index ## _PERBUF; \
  74. } \
  75. static inline void set_period(uint16_t value) { \
  76. TC ## port ## index ## _PERBUF = value; \
  77. } \
  78. static inline uint8_t dma_tx_trigger() { \
  79. return DMA_CH_TRIGSRC_TC ## port ## index ## _OVF_gc; \
  80. } \
  81. static inline uint8_t overflow_event() { \
  82. return EVSYS_CHMUX_TC ## port ## index ## _OVF_gc; \
  83. } \
  84. template<uint8_t channel> \
  85. static inline void set_channel(uint16_t value) { \
  86. if (channel == TIMER_CHANNEL_A) { \
  87. TC ## port ## index ## _CCABUF = value; \
  88. } else if (channel == TIMER_CHANNEL_B) { \
  89. TC ## port ## index ## _CCBBUF = value; \
  90. } else if (channel == TIMER_CHANNEL_C && !index) { \
  91. TC ## port ## 0_CCCBUF = value; \
  92. } else if (channel == TIMER_CHANNEL_D && !index) { \
  93. TC ## port ## 0_CCDBUF = value; \
  94. } \
  95. } \
  96. template<uint8_t channel> \
  97. static inline uint16_t get_channel() { \
  98. if (channel == TIMER_CHANNEL_A) { \
  99. return TC ## port ## index ## _CCA; \
  100. } else if (channel == TIMER_CHANNEL_B) { \
  101. return TC ## port ## index ## _CCB; \
  102. } else if (channel == TIMER_CHANNEL_C && !index) { \
  103. return TC ## port ## 0_CCC; \
  104. } else if (channel == TIMER_CHANNEL_D && !index) { \
  105. return TC ## port ## 0_CCD; \
  106. } \
  107. } \
  108. };
  109. WRAP_TIMER(C, 0)
  110. WRAP_TIMER(C, 1)
  111. WRAP_TIMER(D, 0)
  112. WRAP_TIMER(D, 1)
  113. WRAP_TIMER(E, 0)
  114. #ifdef TCE1
  115. WRAP_TIMER(E, 1)
  116. #endif
  117. #ifdef TCF0
  118. WRAP_TIMER(F, 0)
  119. #endif
  120. template<typename Port, uint8_t index>
  121. class Timer {
  122. public:
  123. typedef TCWrapper<Port, index> TC;
  124. static inline void set_prescaler(TimerPrescaler prescaler) {
  125. TC::tc().CTRLA = prescaler;
  126. }
  127. static inline void set_mode(TimerMode mode) {
  128. // Preserve Compare/capture enable and set mode.
  129. TC::tc().CTRLB = (TC::tc().CTRLB & 0xf0) | mode;
  130. }
  131. static volatile inline uint16_t count() {
  132. return TC::count();
  133. }
  134. static inline void set_count(uint16_t value) {
  135. TC::set_count(value);
  136. }
  137. static inline uint16_t period() {
  138. return TC::period();
  139. }
  140. static inline void set_period(uint16_t value) {
  141. TC::set_period(value);
  142. }
  143. static inline void Restart() {
  144. TC::tc().CTRLFSET = 8;
  145. }
  146. static inline void Bind(uint8_t channel, TimerEventAction event_action) {
  147. TC::tc().CTRLD = event_action | 0x08 | channel;
  148. }
  149. static inline void EnableCC(uint8_t channel) {
  150. TC::tc().CTRLB |= (16 << channel);
  151. }
  152. static inline void StopCC(uint8_t channel) {
  153. TC::tc().CTRLB &= ~(16 << channel);
  154. }
  155. static inline void set_pwm_resolution(uint8_t resolution) {
  156. TC::set_period((1 << static_cast<uint16_t>(resolution) )- 1);
  157. }
  158. static inline void EnableOverflowInterrupt(uint8_t int_level) {
  159. TC::tc().INTCTRLA = (TC::tc().INTCTRLA & 0xfc) | int_level;
  160. }
  161. static inline void DisableOverflowInterrupt() {
  162. TC::tc().INTCTRLA = TC::tc().INTCTRLA & 0xfc;
  163. }
  164. static inline void EnableChannelInterrupt(
  165. uint8_t channel, uint8_t int_level) {
  166. uint8_t shift = channel << 2;
  167. uint8_t mask = (0x3) << shift;
  168. TC::tc().INTCTRLB = (TC::tc().INTCTRLB & ~mask) | (int_level << shift);
  169. }
  170. static inline void DisableChannelInterrupt(uint8_t channel) {
  171. uint8_t shift = channel << 2;
  172. uint8_t mask = (0x3) << shift;
  173. TC::tc().INTCTRLB = TC::tc().INTCTRLB & ~mask;
  174. }
  175. template<uint8_t channel>
  176. static inline void set_channel(uint16_t value) {
  177. TC::template set_channel<channel>(value);
  178. }
  179. template<uint8_t channel>
  180. static inline uint16_t get_channel() {
  181. return TC::template get_channel<channel>();
  182. }
  183. template<uint8_t channel>
  184. static inline void EnableChannelInterrupt(uint8_t int_level) {
  185. uint8_t shift = channel << 2;
  186. uint8_t mask = (0x3) << shift;
  187. TC::tc().INTCTRLB = (TC::tc().INTCTRLB & ~mask) | (int_level << shift);
  188. }
  189. template<uint8_t channel>
  190. static inline void DisableChannelInterrupt() {
  191. uint8_t shift = channel << 2;
  192. uint8_t mask = (0x3) << shift;
  193. TC::tc().INTCTRLB = TC::tc().INTCTRLB & ~mask;
  194. }
  195. static inline uint8_t dma_tx_trigger() {
  196. //
  197. // Horrible hack ahead!
  198. //
  199. // It looks like a timer overflow cannot be used as a DMA trigger. The
  200. // workaround for this is to use an Event as a proxy: set the event trigger
  201. // to be the timer overflow ; set the DMA trigger to be the event.
  202. // Here we use system event 0 for this purpose.
  203. EVSYS_CH0MUX = TC::overflow_event();
  204. return DMA_CH_TRIGSRC_EVSYS_CH0_gc;
  205. }
  206. };
  207. // To be tested (XMega-AU only)
  208. template<typename Port, uint8_t index>
  209. class DualTimer {
  210. public:
  211. typedef TCWrapper<Port, index> TC;
  212. static inline void set_prescaler(TimerPrescaler prescaler) {
  213. TC::tc().CTRLE = 0x2;
  214. TC::tc().CTRLA = prescaler;
  215. }
  216. static inline void EnabledInterrupts(uint8_t level_1, uint8_t level_2) {
  217. TC::tc().INTCTRLA = (level_1 << 2) | level_2;
  218. }
  219. static inline void set_periods(uint8_t period_1, uint8_t period_2) {
  220. TC::tc().HPER = period_1;
  221. TC::tc().LPER = period_2;
  222. }
  223. };
  224. template<typename Port, uint8_t pin>
  225. struct PWMPinToTimer { };
  226. #define BIND_TIMER_TO_PWM_PIN(port, index, Channel, pin) \
  227. template<> struct PWMPinToTimer<port, pin> { \
  228. typedef Timer<port, index> T; \
  229. enum { channel = Channel }; \
  230. }; \
  231. BIND_TIMER_TO_PWM_PIN(PortC, 0, TIMER_CHANNEL_A, 0);
  232. BIND_TIMER_TO_PWM_PIN(PortC, 0, TIMER_CHANNEL_B, 1);
  233. BIND_TIMER_TO_PWM_PIN(PortC, 0, TIMER_CHANNEL_C, 2);
  234. BIND_TIMER_TO_PWM_PIN(PortC, 0, TIMER_CHANNEL_D, 3);
  235. BIND_TIMER_TO_PWM_PIN(PortC, 1, TIMER_CHANNEL_A, 4);
  236. BIND_TIMER_TO_PWM_PIN(PortC, 1, TIMER_CHANNEL_B, 5);
  237. BIND_TIMER_TO_PWM_PIN(PortD, 0, TIMER_CHANNEL_A, 0);
  238. BIND_TIMER_TO_PWM_PIN(PortD, 0, TIMER_CHANNEL_B, 1);
  239. BIND_TIMER_TO_PWM_PIN(PortD, 0, TIMER_CHANNEL_C, 2);
  240. BIND_TIMER_TO_PWM_PIN(PortD, 0, TIMER_CHANNEL_D, 3);
  241. BIND_TIMER_TO_PWM_PIN(PortD, 1, TIMER_CHANNEL_A, 4);
  242. BIND_TIMER_TO_PWM_PIN(PortD, 1, TIMER_CHANNEL_B, 5);
  243. BIND_TIMER_TO_PWM_PIN(PortE, 0, TIMER_CHANNEL_A, 0);
  244. BIND_TIMER_TO_PWM_PIN(PortE, 0, TIMER_CHANNEL_B, 1);
  245. BIND_TIMER_TO_PWM_PIN(PortE, 0, TIMER_CHANNEL_C, 2);
  246. BIND_TIMER_TO_PWM_PIN(PortE, 0, TIMER_CHANNEL_D, 3);
  247. #ifdef TCE1
  248. BIND_TIMER_TO_PWM_PIN(PortE, 1, TIMER_CHANNEL_A, 4);
  249. BIND_TIMER_TO_PWM_PIN(PortE, 1, TIMER_CHANNEL_B, 5);
  250. #endif
  251. #ifdef TCF0
  252. BIND_TIMER_TO_PWM_PIN(PortF, 0, TIMER_CHANNEL_A, 0);
  253. BIND_TIMER_TO_PWM_PIN(PortF, 0, TIMER_CHANNEL_B, 1);
  254. BIND_TIMER_TO_PWM_PIN(PortF, 0, TIMER_CHANNEL_C, 2);
  255. BIND_TIMER_TO_PWM_PIN(PortF, 0, TIMER_CHANNEL_D, 3);
  256. #endif
  257. template<typename Port, uint8_t pin>
  258. class PWM {
  259. public:
  260. static inline void set_value(uint16_t value) {
  261. PWMPinToTimer<Port, pin>::T::template \
  262. set_channel<PWMPinToTimer<Port, pin>::channel>(value);
  263. }
  264. static inline uint16_t get_value() {
  265. return PWMPinToTimer<Port, pin>::T::template \
  266. get_channel<PWMPinToTimer<Port, pin>::channel>();
  267. }
  268. static inline void Init(uint8_t resolution) {
  269. typename PWMPinToTimer<Port, pin>::T timer;
  270. timer.set_prescaler(TIMER_PRESCALER_CLK);
  271. timer.set_mode(TIMER_MODE_SINGLE_PWM);
  272. timer.set_pwm_resolution(resolution);
  273. Start();
  274. }
  275. static inline void EnableInterrupt(uint8_t level) {
  276. PWMPinToTimer<Port, pin>::T::template \
  277. EnableChannelInterrupt<PWMPinToTimer<Port, pin>::channel>(level);
  278. }
  279. static inline void DisableInterrupt() {
  280. PWMPinToTimer<Port, pin>::T::template \
  281. DisableChannelInterrupt<PWMPinToTimer<Port, pin>::channel>();
  282. }
  283. static inline void Start() {
  284. Gpio<Port, pin>::set_direction(OUTPUT);
  285. PWMPinToTimer<Port, pin>::T::EnableCC(PWMPinToTimer<Port, pin>::channel);
  286. }
  287. static inline void Stop() {
  288. PWMPinToTimer<Port, pin>::T::StopCC(PWMPinToTimer<Port, pin>::channel);
  289. }
  290. static inline void Write(uint16_t value) { set_value(value); }
  291. };
  292. template<typename Port, uint8_t pin>
  293. class InputCapture {
  294. public:
  295. template<uint8_t event_channel>
  296. static inline void Init(bool frequency_measurement_mode) {
  297. Gpio<Port, pin> gpio;
  298. // Set the GPIO to input with rising edge detection, and disable pull-up.
  299. gpio.set_direction(INPUT);
  300. gpio.set_sense(SENSE_MODE_RISING);
  301. gpio.Low();
  302. // The selected event source is the GPIO state change.
  303. EventSystemChannel<event_channel>::set_source(gpio.event());
  304. // Bind this event to the channel capture.
  305. PWMPinToTimer<Port, pin>::T::Bind(
  306. event_channel,
  307. frequency_measurement_mode
  308. ? TIMER_EVENT_ACTION_FRQ
  309. : TIMER_EVENT_ACTION_CAPTURE);
  310. Start();
  311. }
  312. static inline void EnableInterrupt(uint8_t level) {
  313. PWMPinToTimer<Port, pin>::T::template \
  314. EnableChannelInterrupt<PWMPinToTimer<Port, pin>::channel>(level);
  315. }
  316. static inline void DisableInterrupt() {
  317. PWMPinToTimer<Port, pin>::T::template \
  318. DisableChannelInterrupt<PWMPinToTimer<Port, pin>::channel>();
  319. }
  320. static inline uint16_t get_value() {
  321. return PWMPinToTimer<Port, pin>::T::template \
  322. get_channel<PWMPinToTimer<Port, pin>::channel>();
  323. }
  324. static inline void Start() {
  325. PWMPinToTimer<Port, pin>::T::EnableCC(PWMPinToTimer<Port, pin>::channel);
  326. }
  327. static inline void Stop() {
  328. PWMPinToTimer<Port, pin>::T::StopCC(PWMPinToTimer<Port, pin>::channel);
  329. }
  330. };
  331. } // namespace avrlibx
  332. #endif // AVRLIBX_SYSTEM_TIMER_H_