Merge pull request #11494 from haslinghuis/dshot_gpio
[betaflight.git] / src / main / drivers / transponder_ir_io_hal.c
blobc971f2d763a2aac81724ea819eac4d86cbc60d3e
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 <stdbool.h>
22 #include <stdint.h>
23 #include <string.h>
25 #include "platform.h"
27 #ifdef USE_TRANSPONDER
29 #include "drivers/dma.h"
30 #include "drivers/dma_reqmap.h"
31 #include "drivers/io.h"
32 #include "drivers/nvic.h"
33 #include "drivers/rcc.h"
34 #include "drivers/timer.h"
35 #include "drivers/transponder_ir_arcitimer.h"
36 #include "drivers/transponder_ir_erlt.h"
37 #include "drivers/transponder_ir_ilap.h"
39 #include "transponder_ir.h"
41 volatile uint8_t transponderIrDataTransferInProgress = 0;
43 static IO_t transponderIO = IO_NONE;
44 static TIM_HandleTypeDef TimHandle;
45 static uint16_t timerChannel = 0;
46 static uint8_t output;
47 static uint8_t alternateFunction;
49 #if !(defined(STM32F7) || defined(STM32H7) || defined(STM32G4))
50 #error "Transponder (via HAL) not supported on this MCU."
51 #endif
53 #if defined(STM32H7)
54 DMA_RAM transponder_t transponder;
55 #elif defined(STM32G4)
56 DMA_RAM_W transponder_t transponder;
57 #else
58 transponder_t transponder;
59 #endif
60 bool transponderInitialised = false;
62 FAST_IRQ_HANDLER static void TRANSPONDER_DMA_IRQHandler(dmaChannelDescriptor_t* descriptor)
64 HAL_DMA_IRQHandler(TimHandle.hdma[descriptor->userParam]);
65 TIM_DMACmd(&TimHandle, timerChannel, DISABLE);
66 transponderIrDataTransferInProgress = 0;
69 void transponderIrHardwareInit(ioTag_t ioTag, transponder_t *transponder)
71 if (!ioTag) {
72 return;
75 const timerHardware_t *timerHardware = timerAllocate(ioTag, OWNER_TRANSPONDER, 0);
76 TIM_TypeDef *timer = timerHardware->tim;
77 timerChannel = timerHardware->channel;
78 output = timerHardware->output;
79 alternateFunction = timerHardware->alternateFunction;
81 #if defined(USE_DMA_SPEC)
82 const dmaChannelSpec_t *dmaSpec = dmaGetChannelSpecByTimer(timerHardware);
84 if (dmaSpec == NULL) {
85 return;
88 dmaResource_t *dmaRef = dmaSpec->ref;
89 uint32_t dmaChannel = dmaSpec->channel;
90 #else
91 dmaResource_t *dmaRef = timerHardware->dmaRef;
92 uint32_t dmaChannel = timerHardware->dmaChannel;
93 #endif
95 dmaIdentifier_e dmaIdentifier = dmaGetIdentifier(dmaRef);
96 if (dmaRef == NULL || !dmaAllocate(dmaIdentifier, OWNER_TRANSPONDER, 0)) {
97 return;
100 /* Time base configuration */
102 TimHandle.Instance = timer;
104 uint16_t prescaler = timerGetPrescalerByDesiredMhz(timer, transponder->timer_hz);
105 uint16_t period = timerGetPeriodByPrescaler(timer, prescaler, transponder->timer_carrier_hz);
107 transponder->bitToggleOne = period / 2;
109 TimHandle.Init.Prescaler = prescaler;
110 TimHandle.Init.Period = period; // 800kHz
111 TimHandle.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
112 TimHandle.Init.CounterMode = TIM_COUNTERMODE_UP;
113 if (HAL_TIM_PWM_Init(&TimHandle) != HAL_OK) {
114 /* Initialization Error */
115 return;
118 /* IO configuration */
120 static DMA_HandleTypeDef hdma_tim;
122 transponderIO = IOGetByTag(ioTag);
123 IOInit(transponderIO, OWNER_TRANSPONDER, 0);
124 IOConfigGPIOAF(transponderIO, IO_CONFIG(GPIO_MODE_AF_PP, GPIO_SPEED_FREQ_VERY_HIGH, GPIO_PULLDOWN), timerHardware->alternateFunction);
126 __DMA1_CLK_ENABLE();
127 __DMA2_CLK_ENABLE();
129 /* Set the parameters to be configured */
130 #if defined(STM32H7) || defined(STM32G4)
131 hdma_tim.Init.Request = dmaChannel;
132 #else
133 hdma_tim.Init.Channel = dmaChannel;
134 #endif
135 hdma_tim.Init.Direction = DMA_MEMORY_TO_PERIPH;
136 hdma_tim.Init.PeriphInc = DMA_PINC_DISABLE;
137 hdma_tim.Init.MemInc = DMA_MINC_ENABLE;
138 hdma_tim.Init.PeriphDataAlignment = DMA_PDATAALIGN_WORD;
139 hdma_tim.Init.MemDataAlignment = DMA_MDATAALIGN_WORD;
140 hdma_tim.Init.Mode = DMA_NORMAL;
141 hdma_tim.Init.Priority = DMA_PRIORITY_HIGH;
142 #if !defined(STM32G4)
143 hdma_tim.Init.FIFOMode = DMA_FIFOMODE_DISABLE;
144 hdma_tim.Init.FIFOThreshold = DMA_FIFO_THRESHOLD_FULL;
145 hdma_tim.Init.MemBurst = DMA_MBURST_SINGLE;
146 hdma_tim.Init.PeriphBurst = DMA_PBURST_SINGLE;
147 #endif
149 /* Set hdma_tim instance */
150 hdma_tim.Instance = (DMA_ARCH_TYPE *)dmaRef;
152 uint16_t dmaIndex = timerDmaIndex(timerChannel);
154 /* Link hdma_tim to hdma[x] (channelx) */
155 __HAL_LINKDMA(&TimHandle, hdma[dmaIndex], hdma_tim);
157 dmaEnable(dmaIdentifier);
158 dmaSetHandler(dmaIdentifier, TRANSPONDER_DMA_IRQHandler, NVIC_PRIO_TRANSPONDER_DMA, dmaIndex);
160 /* Initialize TIMx DMA handle */
161 if (HAL_DMA_Init(TimHandle.hdma[dmaIndex]) != HAL_OK) {
162 /* Initialization Error */
163 return;
167 RCC_ClockCmd(timerRCC(timer), ENABLE);
169 /* PWM1 Mode configuration: Channel1 */
170 TIM_OC_InitTypeDef TIM_OCInitStructure;
172 TIM_OCInitStructure.OCMode = TIM_OCMODE_PWM1;
173 TIM_OCInitStructure.OCIdleState = TIM_OCIDLESTATE_RESET;
174 TIM_OCInitStructure.OCPolarity = (timerHardware->output & TIMER_OUTPUT_INVERTED) ? TIM_OCPOLARITY_LOW : TIM_OCPOLARITY_HIGH;
175 TIM_OCInitStructure.OCNIdleState = TIM_OCNIDLESTATE_RESET;
176 TIM_OCInitStructure.OCNPolarity = (timerHardware->output & TIMER_OUTPUT_INVERTED) ? TIM_OCNPOLARITY_LOW : TIM_OCNPOLARITY_HIGH;
177 TIM_OCInitStructure.Pulse = 0;
178 TIM_OCInitStructure.OCFastMode = TIM_OCFAST_DISABLE;
179 if (HAL_TIM_PWM_ConfigChannel(&TimHandle, &TIM_OCInitStructure, timerChannel) != HAL_OK) {
180 /* Configuration Error */
181 return;
183 if (timerHardware->output & TIMER_OUTPUT_N_CHANNEL) {
184 if (HAL_TIMEx_PWMN_Start(&TimHandle, timerChannel) != HAL_OK) {
185 /* Starting PWM generation Error */
186 return;
188 } else {
189 if (HAL_TIM_PWM_Start(&TimHandle, timerChannel) != HAL_OK) {
190 /* Starting PWM generation Error */
191 return;
195 transponderInitialised = true;
198 bool transponderIrInit(const ioTag_t ioTag, const transponderProvider_e provider)
200 if (!ioTag) {
201 return false;
204 switch (provider) {
205 case TRANSPONDER_ARCITIMER:
206 transponderIrInitArcitimer(&transponder);
207 break;
208 case TRANSPONDER_ILAP:
209 transponderIrInitIlap(&transponder);
210 break;
211 case TRANSPONDER_ERLT:
212 transponderIrInitERLT(&transponder);
213 break;
214 default:
215 return false;
218 transponderIrHardwareInit(ioTag, &transponder);
220 return true;
223 bool isTransponderIrReady(void)
225 return !transponderIrDataTransferInProgress;
228 void transponderIrWaitForTransmitComplete(void)
230 #ifdef DEBUG
231 static uint32_t waitCounter = 0;
232 #endif
234 while (transponderIrDataTransferInProgress) {
235 #ifdef DEBUG
236 waitCounter++;
237 #endif
241 void transponderIrUpdateData(const uint8_t* transponderData)
243 transponderIrWaitForTransmitComplete();
244 transponder.vTable->updateTransponderDMABuffer(&transponder, transponderData);
247 void transponderIrDMAEnable(transponder_t *transponder)
249 if (!transponderInitialised) {
250 return;
253 if (DMA_SetCurrDataCounter(&TimHandle, timerChannel, transponder->transponderIrDMABuffer.ilap, transponder->dma_buffer_size) != HAL_OK) {
254 /* DMA set error */
255 transponderIrDataTransferInProgress = 0;
256 return;
259 /* Reset timer counter */
260 __HAL_TIM_SET_COUNTER(&TimHandle, 0);
261 /* Enable channel DMA requests */
262 TIM_DMACmd(&TimHandle, timerChannel, ENABLE);
265 void transponderIrDisable(void)
267 if (!transponderInitialised) {
268 return;
271 TIM_DMACmd(&TimHandle, timerChannel, DISABLE);
272 if (output & TIMER_OUTPUT_N_CHANNEL) {
273 HAL_TIMEx_PWMN_Stop(&TimHandle, timerChannel);
274 } else {
275 HAL_TIM_PWM_Stop(&TimHandle, timerChannel);
279 IOInit(transponderIO, OWNER_TRANSPONDER, 0);
281 #ifdef TRANSPONDER_INVERTED
282 IOHi(transponderIO);
283 #else
284 IOLo(transponderIO);
285 #endif
287 IOConfigGPIOAF(transponderIO, IO_CONFIG(GPIO_MODE_AF_PP, GPIO_SPEED_FREQ_VERY_HIGH, GPIO_PULLDOWN), alternateFunction);
290 void transponderIrTransmit(void)
292 transponderIrWaitForTransmitComplete();
294 transponderIrDataTransferInProgress = 1;
295 transponderIrDMAEnable(&transponder);
297 #endif