|
3 | 3 | *
|
4 | 4 | * The MIT License (MIT)
|
5 | 5 | *
|
6 |
| - * Copyright (c) 2016 Damien P. George |
| 6 | + * Copyright (c) 2018 Dan Halbert for Adafruit Industries |
7 | 7 | *
|
8 | 8 | * Permission is hereby granted, free of charge, to any person obtaining a copy
|
9 | 9 | * of this software and associated documentation files (the "Software"), to deal
|
|
29 | 29 | #include <stdint.h>
|
30 | 30 |
|
31 | 31 | #include "mpconfigport.h"
|
| 32 | +#include "nrf/pins.h" |
| 33 | +#include "nrf/timers.h" |
32 | 34 | #include "py/gc.h"
|
33 | 35 | #include "py/runtime.h"
|
34 |
| - |
35 | 36 | #include "shared-bindings/pulseio/PulseOut.h"
|
| 37 | +#include "shared-bindings/pulseio/PWMOut.h" |
| 38 | +#include "supervisor/shared/translate.h" |
36 | 39 |
|
37 |
| -//void pulse_finish(struct tc_module *const module) { |
38 |
| -// |
39 |
| -//} |
| 40 | +// A single timer is shared amongst all PulseOut objects under the assumption that |
| 41 | +// the code is single threaded. |
| 42 | +static uint8_t refcount = 0; |
40 | 43 |
|
41 |
| -void pulseout_reset() { |
| 44 | +static nrfx_timer_t *timer = NULL; |
| 45 | + |
| 46 | +static uint16_t *pulse_array = NULL; |
| 47 | +static volatile uint16_t pulse_array_index = 0; |
| 48 | +static uint16_t pulse_array_length; |
| 49 | + |
| 50 | +static void turn_on(pulseio_pulseout_obj_t *pulseout) { |
| 51 | + pulseout->pwmout->pwm->PSEL.OUT[0] = pulseout->pwmout->pin_number; |
| 52 | +} |
| 53 | + |
| 54 | +static void turn_off(pulseio_pulseout_obj_t *pulseout) { |
| 55 | + // Disconnect pin from PWM. |
| 56 | + pulseout->pwmout->pwm->PSEL.OUT[0] = 0xffffffff; |
| 57 | + // Make sure pin is low. |
| 58 | + nrf_gpio_pin_clear(pulseout->pwmout->pin_number); |
| 59 | +} |
| 60 | + |
| 61 | +static void start_timer(void) { |
| 62 | + nrfx_timer_clear(timer); |
| 63 | + // true enables interrupt. |
| 64 | + nrfx_timer_compare(timer, NRF_TIMER_CC_CHANNEL0, pulse_array[pulse_array_index], true); |
| 65 | + nrfx_timer_resume(timer); |
| 66 | +} |
| 67 | + |
| 68 | +static void pulseout_event_handler(nrf_timer_event_t event_type, void *p_context) { |
| 69 | + pulseio_pulseout_obj_t *pulseout = (pulseio_pulseout_obj_t*) p_context; |
| 70 | + if (event_type != NRF_TIMER_EVENT_COMPARE0) { |
| 71 | + // Spurious event. |
| 72 | + return; |
| 73 | + } |
| 74 | + nrfx_timer_pause(timer); |
| 75 | + |
| 76 | + pulse_array_index++; |
42 | 77 |
|
| 78 | + // No more pulses. Turn off output and don't restart. |
| 79 | + if (pulse_array_index >= pulse_array_length) { |
| 80 | + turn_off(pulseout); |
| 81 | + return; |
| 82 | + } |
| 83 | + |
| 84 | + // Alternate on and off, starting with on. |
| 85 | + if (pulse_array_index % 2 == 0) { |
| 86 | + turn_on(pulseout); |
| 87 | + } else { |
| 88 | + turn_off(pulseout); |
| 89 | + } |
| 90 | + |
| 91 | + // Count up to the next given value. |
| 92 | + start_timer(); |
43 | 93 | }
|
44 | 94 |
|
45 |
| -void common_hal_pulseio_pulseout_construct(pulseio_pulseout_obj_t* self, const pulseio_pwmout_obj_t* carrier) { |
46 |
| - mp_raise_NotImplementedError(NULL); |
| 95 | +void pulseout_reset() { |
| 96 | + if (timer != NULL) { |
| 97 | + nrf_peripherals_free_timer(timer); |
| 98 | + } |
| 99 | + refcount = 0; |
| 100 | +} |
| 101 | + |
| 102 | +void common_hal_pulseio_pulseout_construct(pulseio_pulseout_obj_t* self, |
| 103 | + const pulseio_pwmout_obj_t* carrier) { |
| 104 | + if (refcount == 0) { |
| 105 | + timer = nrf_peripherals_allocate_timer(); |
| 106 | + if (timer == NULL) { |
| 107 | + mp_raise_RuntimeError(translate("All timers in use")); |
| 108 | + } |
| 109 | + } |
| 110 | + refcount++; |
| 111 | + |
| 112 | + nrfx_timer_config_t timer_config = { |
| 113 | + // PulseOut durations are in microseconds, so this is convenient. |
| 114 | + .frequency = NRF_TIMER_FREQ_1MHz, |
| 115 | + .mode = NRF_TIMER_MODE_TIMER, |
| 116 | + .bit_width = NRF_TIMER_BIT_WIDTH_32, |
| 117 | + .interrupt_priority = NRFX_TIMER_DEFAULT_CONFIG_IRQ_PRIORITY, |
| 118 | + .p_context = self, |
| 119 | + }; |
| 120 | + |
| 121 | + self->pwmout = carrier; |
| 122 | + |
| 123 | + nrfx_timer_init(timer, &timer_config, &pulseout_event_handler); |
| 124 | + turn_off(self); |
47 | 125 | }
|
48 | 126 |
|
49 | 127 | bool common_hal_pulseio_pulseout_deinited(pulseio_pulseout_obj_t* self) {
|
50 |
| - return 1; |
| 128 | + return self->pwmout == NULL; |
51 | 129 | }
|
52 | 130 |
|
53 | 131 | void common_hal_pulseio_pulseout_deinit(pulseio_pulseout_obj_t* self) {
|
| 132 | + if (common_hal_pulseio_pulseout_deinited(self)) { |
| 133 | + return; |
| 134 | + } |
| 135 | + turn_on(self); |
| 136 | + self->pwmout = NULL; |
54 | 137 |
|
| 138 | + refcount--; |
| 139 | + if (refcount == 0) { |
| 140 | + nrf_peripherals_free_timer(timer); |
| 141 | + } |
55 | 142 | }
|
56 | 143 |
|
57 | 144 | void common_hal_pulseio_pulseout_send(pulseio_pulseout_obj_t* self, uint16_t* pulses, uint16_t length) {
|
| 145 | + pulse_array = pulses; |
| 146 | + pulse_array_index = 0; |
| 147 | + pulse_array_length = length; |
| 148 | + |
| 149 | + nrfx_timer_enable(timer); |
| 150 | + |
| 151 | + turn_on(self); |
| 152 | + // Count up to the next given value. |
| 153 | + start_timer(); |
| 154 | + |
| 155 | + while(pulse_array_index < length) { |
| 156 | + // Do other things while we wait. The interrupts will handle sending the |
| 157 | + // signal. |
| 158 | + #ifdef MICROPY_VM_HOOK_LOOP |
| 159 | + MICROPY_VM_HOOK_LOOP |
| 160 | + #endif |
| 161 | + } |
58 | 162 |
|
| 163 | + nrfx_timer_disable(timer); |
59 | 164 | }
|
0 commit comments