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)
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/>.
23 #ifdef USE_CAMERA_CONTROL
25 #include "camera_control.h"
29 #include "pwm_output.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
39 #define ADC_VOLTAGE 3.3f
42 #if !defined(STM32F411xE) && !defined(STM32F7) && !defined(STM32H7) && !defined(STM32G4)
43 #define CAMERA_CONTROL_SOFTWARE_PWM_AVAILABLE
44 #include "build/atomic.h"
47 #define CAMERA_CONTROL_HARDWARE_PWM_AVAILABLE
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;
74 timerChannel_t channel
;
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
);
87 IOHi(cameraControlRuntime
.io
);
91 static void cameraControlLo(void)
93 if (cameraControlRuntime
.inverted
) {
94 IOHi(cameraControlRuntime
.io
);
96 IOLo(cameraControlRuntime
.io
);
100 void TIM6_DAC_IRQHandler(void)
107 void TIM7_IRQHandler(void)
115 void cameraControlInit(void)
117 if (cameraControlConfig()->ioTag
== IO_TAG_NONE
)
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
) {
133 IOConfigGPIO(cameraControlRuntime
.io
, IOCFG_AF_PP
);
135 IOConfigGPIOAF(cameraControlRuntime
.io
, IOCFG_AF_PP
, timerHardware
->alternateFunction
);
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;
144 } else if (CAMERA_CONTROL_MODE_SOFTWARE_PWM
== cameraControlConfig()->mode
) {
145 #ifdef CAMERA_CONTROL_SOFTWARE_PWM_AVAILABLE
147 IOConfigGPIO(cameraControlRuntime
.io
, IOCFG_OUT_PP
);
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
;
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
) {
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
;
199 void cameraControlKeyPress(cameraControlKey_e key
, uint32_t holdDurationMs
)
201 if (!cameraControlRuntime
.enabled
)
204 if (key
>= CAMERA_CONTROL_KEYS_COUNT
)
207 #if defined(CAMERA_CONTROL_HARDWARE_PWM_AVAILABLE) || defined(CAMERA_CONTROL_SOFTWARE_PWM_AVAILABLE)
208 const float dutyCycle
= calculatePWMDutyCycle(key
);
210 (void) holdDurationMs
;
214 // Force OSD timeout so we are alone on the display.
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
;
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
);
229 delay(cameraControlConfig()->keyDelayMs
+ holdDurationMs
);
233 TIM6
->ARR
= cameraControlRuntime
.period
;
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
;
259 // Reset to idle state
260 IOHi(cameraControlRuntime
.io
);
263 } else if (CAMERA_CONTROL_MODE_DAC
== cameraControlConfig()->mode
) {
264 // @todo not yet implemented