pulse_reader.c 8.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233
  1. #include "pulse_reader.h"
  2. #include <limits.h>
  3. #include <furi.h>
  4. #include <furi_hal.h>
  5. #include <furi_hal_gpio.h>
  6. #include <stm32wbxx_ll_dma.h>
  7. #include <stm32wbxx_ll_dmamux.h>
  8. #include <stm32wbxx_ll_tim.h>
  9. #include <stm32wbxx_ll_exti.h>
  10. struct PulseReader {
  11. uint32_t* timer_buffer;
  12. uint32_t* gpio_buffer;
  13. uint32_t size;
  14. uint32_t pos;
  15. uint32_t timer_value;
  16. uint32_t gpio_value;
  17. uint32_t gpio_mask;
  18. uint32_t unit_multiplier;
  19. uint32_t unit_divider;
  20. uint32_t bit_time;
  21. uint32_t dma_channel;
  22. const GpioPin* gpio;
  23. GpioPull pull;
  24. LL_DMA_InitTypeDef dma_config_timer;
  25. LL_DMA_InitTypeDef dma_config_gpio;
  26. };
  27. #define GPIO_PIN_MAP(pin, prefix) \
  28. (((pin) == (LL_GPIO_PIN_0)) ? prefix##0 : \
  29. ((pin) == (LL_GPIO_PIN_1)) ? prefix##1 : \
  30. ((pin) == (LL_GPIO_PIN_2)) ? prefix##2 : \
  31. ((pin) == (LL_GPIO_PIN_3)) ? prefix##3 : \
  32. ((pin) == (LL_GPIO_PIN_4)) ? prefix##4 : \
  33. ((pin) == (LL_GPIO_PIN_5)) ? prefix##5 : \
  34. ((pin) == (LL_GPIO_PIN_6)) ? prefix##6 : \
  35. ((pin) == (LL_GPIO_PIN_7)) ? prefix##7 : \
  36. ((pin) == (LL_GPIO_PIN_8)) ? prefix##8 : \
  37. ((pin) == (LL_GPIO_PIN_9)) ? prefix##9 : \
  38. ((pin) == (LL_GPIO_PIN_10)) ? prefix##10 : \
  39. ((pin) == (LL_GPIO_PIN_11)) ? prefix##11 : \
  40. ((pin) == (LL_GPIO_PIN_12)) ? prefix##12 : \
  41. ((pin) == (LL_GPIO_PIN_13)) ? prefix##13 : \
  42. ((pin) == (LL_GPIO_PIN_14)) ? prefix##14 : \
  43. prefix##15)
  44. #define GET_DMAMUX_EXTI_LINE(pin) GPIO_PIN_MAP(pin, LL_DMAMUX_REQ_GEN_EXTI_LINE)
  45. PulseReader* pulse_reader_alloc(const GpioPin* gpio, uint32_t size) {
  46. PulseReader* signal = malloc(sizeof(PulseReader));
  47. signal->timer_buffer = malloc(size * sizeof(uint32_t));
  48. signal->gpio_buffer = malloc(size * sizeof(uint32_t));
  49. signal->dma_channel = LL_DMA_CHANNEL_4;
  50. signal->gpio = gpio;
  51. signal->pull = GpioPullNo;
  52. signal->size = size;
  53. signal->timer_value = 0;
  54. signal->pos = 0;
  55. pulse_reader_set_timebase(signal, PulseReaderUnit64MHz);
  56. pulse_reader_set_bittime(signal, 1);
  57. signal->dma_config_timer.Direction = LL_DMA_DIRECTION_PERIPH_TO_MEMORY;
  58. signal->dma_config_timer.PeriphOrM2MSrcAddress = (uint32_t) & (TIM2->CNT);
  59. signal->dma_config_timer.PeriphOrM2MSrcIncMode = LL_DMA_PERIPH_NOINCREMENT;
  60. signal->dma_config_timer.PeriphOrM2MSrcDataSize = LL_DMA_PDATAALIGN_WORD;
  61. signal->dma_config_timer.MemoryOrM2MDstAddress = (uint32_t)signal->timer_buffer;
  62. signal->dma_config_timer.MemoryOrM2MDstIncMode = LL_DMA_MEMORY_INCREMENT;
  63. signal->dma_config_timer.MemoryOrM2MDstDataSize = LL_DMA_MDATAALIGN_WORD;
  64. signal->dma_config_timer.Mode = LL_DMA_MODE_CIRCULAR;
  65. signal->dma_config_timer.PeriphRequest =
  66. LL_DMAMUX_REQ_GENERATOR0; /* executes LL_DMA_SetPeriphRequest */
  67. signal->dma_config_timer.Priority = LL_DMA_PRIORITY_VERYHIGH;
  68. signal->dma_config_gpio.Direction = LL_DMA_DIRECTION_PERIPH_TO_MEMORY;
  69. signal->dma_config_gpio.PeriphOrM2MSrcIncMode = LL_DMA_PERIPH_NOINCREMENT;
  70. signal->dma_config_gpio.PeriphOrM2MSrcDataSize = LL_DMA_PDATAALIGN_WORD;
  71. signal->dma_config_gpio.MemoryOrM2MDstIncMode = LL_DMA_MEMORY_INCREMENT;
  72. signal->dma_config_gpio.MemoryOrM2MDstDataSize = LL_DMA_MDATAALIGN_WORD;
  73. signal->dma_config_gpio.Mode = LL_DMA_MODE_CIRCULAR;
  74. signal->dma_config_gpio.PeriphRequest =
  75. LL_DMAMUX_REQ_GENERATOR0; /* executes LL_DMA_SetPeriphRequest */
  76. signal->dma_config_gpio.Priority = LL_DMA_PRIORITY_VERYHIGH;
  77. return signal;
  78. }
  79. void pulse_reader_set_timebase(PulseReader* signal, PulseReaderUnit unit) {
  80. switch(unit) {
  81. case PulseReaderUnit64MHz:
  82. signal->unit_multiplier = 1;
  83. signal->unit_divider = 1;
  84. break;
  85. case PulseReaderUnitPicosecond:
  86. signal->unit_multiplier = 15625;
  87. signal->unit_divider = 1;
  88. break;
  89. case PulseReaderUnitNanosecond:
  90. signal->unit_multiplier = 15625;
  91. signal->unit_divider = 1000;
  92. break;
  93. case PulseReaderUnitMicrosecond:
  94. signal->unit_multiplier = 15625;
  95. signal->unit_divider = 1000000;
  96. break;
  97. }
  98. }
  99. void pulse_reader_set_bittime(PulseReader* signal, uint32_t bit_time) {
  100. signal->bit_time = bit_time;
  101. }
  102. void pulse_reader_set_pull(PulseReader* signal, GpioPull pull) {
  103. signal->pull = pull;
  104. }
  105. void pulse_reader_free(PulseReader* signal) {
  106. furi_assert(signal);
  107. free(signal->timer_buffer);
  108. free(signal->gpio_buffer);
  109. free(signal);
  110. }
  111. uint32_t pulse_reader_samples(PulseReader* signal) {
  112. uint32_t dma_pos = signal->size - (uint32_t)LL_DMA_GetDataLength(DMA1, signal->dma_channel);
  113. return ((signal->pos + signal->size) - dma_pos) % signal->size;
  114. }
  115. void pulse_reader_stop(PulseReader* signal) {
  116. LL_DMA_DisableChannel(DMA1, signal->dma_channel);
  117. LL_DMA_DisableChannel(DMA1, signal->dma_channel + 1);
  118. LL_DMAMUX_DisableRequestGen(NULL, LL_DMAMUX_REQ_GEN_0);
  119. LL_TIM_DisableCounter(TIM2);
  120. furi_hal_gpio_init_simple(signal->gpio, GpioModeAnalog);
  121. }
  122. void pulse_reader_start(PulseReader* signal) {
  123. /* configure DMA to read from a timer peripheral */
  124. signal->dma_config_timer.NbData = signal->size;
  125. signal->dma_config_gpio.PeriphOrM2MSrcAddress = (uint32_t) & (signal->gpio->port->IDR);
  126. signal->dma_config_gpio.MemoryOrM2MDstAddress = (uint32_t)signal->gpio_buffer;
  127. signal->dma_config_gpio.NbData = signal->size;
  128. /* start counter */
  129. LL_TIM_SetCounterMode(TIM2, LL_TIM_COUNTERMODE_UP);
  130. LL_TIM_SetClockDivision(TIM2, LL_TIM_CLOCKDIVISION_DIV1);
  131. LL_TIM_SetPrescaler(TIM2, 0);
  132. LL_TIM_SetAutoReload(TIM2, 0xFFFFFFFF);
  133. LL_TIM_SetCounter(TIM2, 0);
  134. LL_TIM_EnableCounter(TIM2);
  135. /* generator 0 gets fed by EXTI_LINEn */
  136. LL_DMAMUX_SetRequestSignalID(
  137. NULL, LL_DMAMUX_REQ_GEN_0, GET_DMAMUX_EXTI_LINE(signal->gpio->pin));
  138. /* trigger on rising edge of the interrupt */
  139. LL_DMAMUX_SetRequestGenPolarity(NULL, LL_DMAMUX_REQ_GEN_0, LL_DMAMUX_REQ_GEN_POL_RISING);
  140. /* now enable request generation again */
  141. LL_DMAMUX_EnableRequestGen(NULL, LL_DMAMUX_REQ_GEN_0);
  142. /* we need the EXTI to be configured as interrupt generating line, but no ISR registered */
  143. furi_hal_gpio_init_ex(
  144. signal->gpio, GpioModeInterruptRiseFall, signal->pull, GpioSpeedVeryHigh, GpioAltFnUnused);
  145. /* capture current timer */
  146. signal->pos = 0;
  147. signal->timer_value = TIM2->CNT;
  148. signal->gpio_mask = signal->gpio->pin;
  149. signal->gpio_value = signal->gpio->port->IDR & signal->gpio_mask;
  150. /* now set up DMA with these settings */
  151. LL_DMA_Init(DMA1, signal->dma_channel, &signal->dma_config_timer);
  152. LL_DMA_Init(DMA1, signal->dma_channel + 1, &signal->dma_config_gpio);
  153. LL_DMA_EnableChannel(DMA1, signal->dma_channel);
  154. LL_DMA_EnableChannel(DMA1, signal->dma_channel + 1);
  155. }
  156. uint32_t pulse_reader_receive(PulseReader* signal, int timeout_us) {
  157. uint32_t start_time = DWT->CYCCNT;
  158. uint32_t timeout_ticks = timeout_us * (F_TIM2 / 1000000);
  159. do {
  160. /* get the DMA's next write position by reading "remaining length" register */
  161. uint32_t dma_pos =
  162. signal->size - (uint32_t)LL_DMA_GetDataLength(DMA1, signal->dma_channel);
  163. /* the DMA has advanced in the ringbuffer */
  164. if(dma_pos != signal->pos) {
  165. uint32_t delta = signal->timer_buffer[signal->pos] - signal->timer_value;
  166. uint32_t last_gpio_value = signal->gpio_value;
  167. signal->gpio_value = signal->gpio_buffer[signal->pos];
  168. /* check if the GPIO really toggled. if not, we lost an edge :( */
  169. if(((last_gpio_value ^ signal->gpio_value) & signal->gpio_mask) != signal->gpio_mask) {
  170. signal->gpio_value ^= signal->gpio_mask;
  171. return PULSE_READER_LOST_EDGE;
  172. }
  173. signal->timer_value = signal->timer_buffer[signal->pos];
  174. signal->pos++;
  175. signal->pos %= signal->size;
  176. uint32_t delta_unit = 0;
  177. /* probably larger values, so choose a wider data type */
  178. if(signal->unit_divider > 1) {
  179. delta_unit =
  180. (uint32_t)((uint64_t)delta * (uint64_t)signal->unit_multiplier / signal->unit_divider);
  181. } else {
  182. delta_unit = delta * signal->unit_multiplier;
  183. }
  184. /* if to be scaled to bit times, save a few instructions. should be faster */
  185. if(signal->bit_time > 1) {
  186. return (delta_unit + signal->bit_time / 2) / signal->bit_time;
  187. }
  188. return delta_unit;
  189. }
  190. /* check for timeout */
  191. uint32_t elapsed = DWT->CYCCNT - start_time;
  192. if(elapsed > timeout_ticks) {
  193. return PULSE_READER_NO_EDGE;
  194. }
  195. } while(true);
  196. }