Merge pull request #9742 from mikeller/fix_spi_transaction_support
[betaflight.git] / src / main / drivers / camera_control.c
blob490f2f1ed9c3817d2adc7b605e307e1f4678ee79
1 /*
2 * This file is part of Cleanflight and Betaflight.
4 * Cleanflight and Betaflight are free software. You can redistribute
5 * this software and/or modify this software under the terms of the
6 * GNU General Public License as published by the Free Software
7 * Foundation, either version 3 of the License, or (at your option)
8 * any later version.
10 * Cleanflight and Betaflight are distributed in the hope that they
11 * will be useful, but WITHOUT ANY WARRANTY; without even the implied
12 * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
13 * See the GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this software.
18 * If not, see <http://www.gnu.org/licenses/>.
21 #include "platform.h"
23 #ifdef USE_CAMERA_CONTROL
25 #include "camera_control.h"
26 #include "io.h"
27 #include "math.h"
28 #include "nvic.h"
29 #include "pwm_output.h"
30 #include "time.h"
31 #include "pg/pg_ids.h"
33 #define CAMERA_CONTROL_PWM_RESOLUTION 128
34 #define CAMERA_CONTROL_SOFT_PWM_RESOLUTION 448
36 #ifdef CURRENT_TARGET_CPU_VOLTAGE
37 #define ADC_VOLTAGE CURRENT_TARGET_CPU_VOLTAGE
38 #else
39 #define ADC_VOLTAGE 3.3f
40 #endif
42 #if !defined(STM32F411xE) && !defined(STM32F7) && !defined(STM32H7)
43 #define CAMERA_CONTROL_SOFTWARE_PWM_AVAILABLE
44 #include "build/atomic.h"
45 #endif
47 #define CAMERA_CONTROL_HARDWARE_PWM_AVAILABLE
48 #include "timer.h"
50 #ifdef USE_OSD
51 #include "osd/osd.h"
52 #endif
54 PG_REGISTER_WITH_RESET_FN(cameraControlConfig_t, cameraControlConfig, PG_CAMERA_CONTROL_CONFIG, 0);
56 void pgResetFn_cameraControlConfig(cameraControlConfig_t *cameraControlConfig)
58 cameraControlConfig->mode = CAMERA_CONTROL_MODE_HARDWARE_PWM;
59 cameraControlConfig->refVoltage = 330;
60 cameraControlConfig->keyDelayMs = 180;
61 cameraControlConfig->internalResistance = 470;
62 cameraControlConfig->ioTag = timerioTagGetByUsage(TIM_USE_CAMERA_CONTROL, 0);
63 cameraControlConfig->inverted = 0; // Output is inverted externally
64 cameraControlConfig->buttonResistanceValues[CAMERA_CONTROL_KEY_ENTER] = 450;
65 cameraControlConfig->buttonResistanceValues[CAMERA_CONTROL_KEY_LEFT] = 270;
66 cameraControlConfig->buttonResistanceValues[CAMERA_CONTROL_KEY_UP] = 150;
67 cameraControlConfig->buttonResistanceValues[CAMERA_CONTROL_KEY_RIGHT] = 68;
68 cameraControlConfig->buttonResistanceValues[CAMERA_CONTROL_KEY_DOWN] = 0;
71 static struct {
72 bool enabled;
73 IO_t io;
74 timerChannel_t channel;
75 uint32_t period;
76 uint8_t inverted;
77 } cameraControlRuntime;
79 static uint32_t endTimeMillis;
81 #ifdef CAMERA_CONTROL_SOFTWARE_PWM_AVAILABLE
82 static void cameraControlHi(void)
84 if (cameraControlRuntime.inverted) {
85 IOLo(cameraControlRuntime.io);
86 } else {
87 IOHi(cameraControlRuntime.io);
91 static void cameraControlLo(void)
93 if (cameraControlRuntime.inverted) {
94 IOHi(cameraControlRuntime.io);
95 } else {
96 IOLo(cameraControlRuntime.io);
100 void TIM6_DAC_IRQHandler(void)
102 cameraControlHi();
104 TIM6->SR = 0;
107 void TIM7_IRQHandler(void)
109 cameraControlLo();
111 TIM7->SR = 0;
113 #endif
115 void cameraControlInit(void)
117 if (cameraControlConfig()->ioTag == IO_TAG_NONE)
118 return;
120 cameraControlRuntime.inverted = cameraControlConfig()->inverted;
121 cameraControlRuntime.io = IOGetByTag(cameraControlConfig()->ioTag);
122 IOInit(cameraControlRuntime.io, OWNER_CAMERA_CONTROL, 0);
124 if (CAMERA_CONTROL_MODE_HARDWARE_PWM == cameraControlConfig()->mode) {
125 #ifdef CAMERA_CONTROL_HARDWARE_PWM_AVAILABLE
126 const timerHardware_t *timerHardware = timerAllocate(cameraControlConfig()->ioTag, OWNER_CAMERA_CONTROL, 0);
128 if (!timerHardware) {
129 return;
132 #ifdef STM32F1
133 IOConfigGPIO(cameraControlRuntime.io, IOCFG_AF_PP);
134 #else
135 IOConfigGPIOAF(cameraControlRuntime.io, IOCFG_AF_PP, timerHardware->alternateFunction);
136 #endif
138 pwmOutConfig(&cameraControlRuntime.channel, timerHardware, timerClock(TIM6), CAMERA_CONTROL_PWM_RESOLUTION, 0, cameraControlRuntime.inverted);
140 cameraControlRuntime.period = CAMERA_CONTROL_PWM_RESOLUTION;
141 *cameraControlRuntime.channel.ccr = cameraControlRuntime.period;
142 cameraControlRuntime.enabled = true;
143 #endif
144 } else if (CAMERA_CONTROL_MODE_SOFTWARE_PWM == cameraControlConfig()->mode) {
145 #ifdef CAMERA_CONTROL_SOFTWARE_PWM_AVAILABLE
147 IOConfigGPIO(cameraControlRuntime.io, IOCFG_OUT_PP);
148 cameraControlHi();
150 cameraControlRuntime.period = CAMERA_CONTROL_SOFT_PWM_RESOLUTION;
151 cameraControlRuntime.enabled = true;
153 NVIC_InitTypeDef nvicTIM6 = {
154 TIM6_DAC_IRQn, NVIC_PRIORITY_BASE(NVIC_PRIO_TIMER), NVIC_PRIORITY_SUB(NVIC_PRIO_TIMER), ENABLE
156 NVIC_Init(&nvicTIM6);
157 NVIC_InitTypeDef nvicTIM7 = {
158 TIM7_IRQn, NVIC_PRIORITY_BASE(NVIC_PRIO_TIMER), NVIC_PRIORITY_SUB(NVIC_PRIO_TIMER), ENABLE
160 NVIC_Init(&nvicTIM7);
162 RCC->APB1ENR |= RCC_APB1Periph_TIM6 | RCC_APB1Periph_TIM7;
163 TIM6->PSC = 0;
164 TIM7->PSC = 0;
165 #endif
166 } else if (CAMERA_CONTROL_MODE_DAC == cameraControlConfig()->mode) {
167 // @todo not yet implemented
171 void cameraControlProcess(uint32_t currentTimeUs)
173 if (endTimeMillis && currentTimeUs >= 1000 * endTimeMillis) {
174 if (CAMERA_CONTROL_MODE_HARDWARE_PWM == cameraControlConfig()->mode) {
175 *cameraControlRuntime.channel.ccr = cameraControlRuntime.period;
176 } else if (CAMERA_CONTROL_MODE_SOFTWARE_PWM == cameraControlConfig()->mode) {
180 endTimeMillis = 0;
184 static float calculateKeyPressVoltage(const cameraControlKey_e key)
186 const int buttonResistance = cameraControlConfig()->buttonResistanceValues[key] * 100;
187 return 1.0e-2f * cameraControlConfig()->refVoltage * buttonResistance / (100 * cameraControlConfig()->internalResistance + buttonResistance);
190 #if defined(CAMERA_CONTROL_HARDWARE_PWM_AVAILABLE) || defined(CAMERA_CONTROL_SOFTWARE_PWM_AVAILABLE)
191 static float calculatePWMDutyCycle(const cameraControlKey_e key)
193 const float voltage = calculateKeyPressVoltage(key);
195 return voltage / ADC_VOLTAGE;
197 #endif
199 void cameraControlKeyPress(cameraControlKey_e key, uint32_t holdDurationMs)
201 if (!cameraControlRuntime.enabled)
202 return;
204 if (key >= CAMERA_CONTROL_KEYS_COUNT)
205 return;
207 #if defined(CAMERA_CONTROL_HARDWARE_PWM_AVAILABLE) || defined(CAMERA_CONTROL_SOFTWARE_PWM_AVAILABLE)
208 const float dutyCycle = calculatePWMDutyCycle(key);
209 #else
210 (void) holdDurationMs;
211 #endif
213 #ifdef USE_OSD
214 // Force OSD timeout so we are alone on the display.
215 resumeRefreshAt = 0;
216 #endif
218 if (CAMERA_CONTROL_MODE_HARDWARE_PWM == cameraControlConfig()->mode) {
219 #ifdef CAMERA_CONTROL_HARDWARE_PWM_AVAILABLE
220 *cameraControlRuntime.channel.ccr = lrintf(dutyCycle * cameraControlRuntime.period);
221 endTimeMillis = millis() + cameraControlConfig()->keyDelayMs + holdDurationMs;
222 #endif
223 } else if (CAMERA_CONTROL_MODE_SOFTWARE_PWM == cameraControlConfig()->mode) {
224 #ifdef CAMERA_CONTROL_SOFTWARE_PWM_AVAILABLE
225 const uint32_t hiTime = lrintf(dutyCycle * cameraControlRuntime.period);
227 if (0 == hiTime) {
228 cameraControlLo();
229 delay(cameraControlConfig()->keyDelayMs + holdDurationMs);
230 cameraControlHi();
231 } else {
232 TIM6->CNT = hiTime;
233 TIM6->ARR = cameraControlRuntime.period;
235 TIM7->CNT = 0;
236 TIM7->ARR = cameraControlRuntime.period;
238 // Start two timers as simultaneously as possible
239 ATOMIC_BLOCK(NVIC_PRIO_TIMER) {
240 TIM6->CR1 = TIM_CR1_CEN;
241 TIM7->CR1 = TIM_CR1_CEN;
244 // Enable interrupt generation
245 TIM6->DIER = TIM_IT_Update;
246 TIM7->DIER = TIM_IT_Update;
248 const uint32_t endTime = millis() + cameraControlConfig()->keyDelayMs + holdDurationMs;
250 // Wait to give the camera a chance at registering the key press
251 while (millis() < endTime);
253 // Disable timers and interrupt generation
254 TIM6->CR1 &= ~TIM_CR1_CEN;
255 TIM7->CR1 &= ~TIM_CR1_CEN;
256 TIM6->DIER = 0;
257 TIM7->DIER = 0;
259 // Reset to idle state
260 IOHi(cameraControlRuntime.io);
262 #endif
263 } else if (CAMERA_CONTROL_MODE_DAC == cameraControlConfig()->mode) {
264 // @todo not yet implemented
268 #endif