IRDA: Use DMA for async TX (#608)
This commit is contained in:
		
							parent
							
								
									9c38efd4ef
								
							
						
					
					
						commit
						ba399abb5d
					
				| @ -10,6 +10,7 @@ | |||||||
| #include <string> | #include <string> | ||||||
| #include <m-string.h> | #include <m-string.h> | ||||||
| #include <irda_transmit.h> | #include <irda_transmit.h> | ||||||
|  | #include <sys/types.h> | ||||||
| 
 | 
 | ||||||
| static void signal_received_callback(void* context, IrdaWorkerSignal* received_signal) { | static void signal_received_callback(void* context, IrdaWorkerSignal* received_signal) { | ||||||
|     furi_assert(received_signal); |     furi_assert(received_signal); | ||||||
| @ -47,7 +48,7 @@ static void signal_received_callback(void* context, IrdaWorkerSignal* received_s | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static void irda_cli_start_ir_rx(Cli* cli, string_t args, void* context) { | static void irda_cli_start_ir_rx(Cli* cli, string_t args, void* context) { | ||||||
|     if(api_hal_irda_rx_irq_is_busy()) { |     if(api_hal_irda_is_busy()) { | ||||||
|         printf("IRDA is busy. Exit."); |         printf("IRDA is busy. Exit."); | ||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
| @ -105,7 +106,7 @@ static bool parse_signal_raw( | |||||||
|     uint32_t* timings, |     uint32_t* timings, | ||||||
|     uint32_t* timings_cnt, |     uint32_t* timings_cnt, | ||||||
|     float* duty_cycle, |     float* duty_cycle, | ||||||
|     float* frequency) { |     uint32_t* frequency) { | ||||||
|     char frequency_str[10]; |     char frequency_str[10]; | ||||||
|     char duty_cycle_str[10]; |     char duty_cycle_str[10]; | ||||||
|     int parsed = sscanf(str, "RAW F:%9s DC:%9s", frequency_str, duty_cycle_str); |     int parsed = sscanf(str, "RAW F:%9s DC:%9s", frequency_str, duty_cycle_str); | ||||||
| @ -141,14 +142,14 @@ static bool parse_signal_raw( | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static void irda_cli_start_ir_tx(Cli* cli, string_t args, void* context) { | static void irda_cli_start_ir_tx(Cli* cli, string_t args, void* context) { | ||||||
|     if(api_hal_irda_rx_irq_is_busy()) { |     if(api_hal_irda_is_busy()) { | ||||||
|         printf("IRDA is busy. Exit."); |         printf("IRDA is busy. Exit."); | ||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     IrdaMessage message; |     IrdaMessage message; | ||||||
|     const char* str = string_get_cstr(args); |     const char* str = string_get_cstr(args); | ||||||
|     float frequency; |     uint32_t frequency; | ||||||
|     float duty_cycle; |     float duty_cycle; | ||||||
|     uint32_t* timings = (uint32_t*)furi_alloc(sizeof(uint32_t) * 1000); |     uint32_t* timings = (uint32_t*)furi_alloc(sizeof(uint32_t) * 1000); | ||||||
|     uint32_t timings_cnt = 1000; |     uint32_t timings_cnt = 1000; | ||||||
| @ -156,7 +157,7 @@ static void irda_cli_start_ir_tx(Cli* cli, string_t args, void* context) { | |||||||
|     if(parse_message(str, &message)) { |     if(parse_message(str, &message)) { | ||||||
|         irda_send(&message, 1); |         irda_send(&message, 1); | ||||||
|     } else if(parse_signal_raw(str, timings, &timings_cnt, &duty_cycle, &frequency)) { |     } else if(parse_signal_raw(str, timings, &timings_cnt, &duty_cycle, &frequency)) { | ||||||
|         irda_send_raw_ext(timings, timings_cnt, true, duty_cycle, frequency); |         irda_send_raw_ext(timings, timings_cnt, true, frequency, duty_cycle); | ||||||
|     } else { |     } else { | ||||||
|         printf("Wrong arguments.\r\n"); |         printf("Wrong arguments.\r\n"); | ||||||
|         irda_cli_print_usage(); |         irda_cli_print_usage(); | ||||||
|  | |||||||
| @ -1,11 +1,21 @@ | |||||||
| #pragma once | #pragma once | ||||||
| #include <stdint.h> | #include <stdint.h> | ||||||
| #include <stdbool.h> | #include <stdbool.h> | ||||||
|  | #include <stddef.h> | ||||||
| 
 | 
 | ||||||
| #ifdef __cplusplus | #ifdef __cplusplus | ||||||
| extern "C" { | extern "C" { | ||||||
| #endif | #endif | ||||||
| 
 | 
 | ||||||
|  | typedef enum { | ||||||
|  |     ApiHalIrdaTxGetDataStateError,      /* An error occured during transmission */ | ||||||
|  |     ApiHalIrdaTxGetDataStateOk,         /* New data obtained */ | ||||||
|  |     ApiHalIrdaTxGetDataStateDone,       /* New data obtained, and this is end of package */ | ||||||
|  |     ApiHalIrdaTxGetDataStateLastDone,   /* New data obtained, and this is end of package and no more data available */ | ||||||
|  | } ApiHalIrdaTxGetDataState; | ||||||
|  | 
 | ||||||
|  | typedef ApiHalIrdaTxGetDataState (*ApiHalIrdaTxGetDataCallback) (void* context, uint32_t* duration, bool* level); | ||||||
|  | 
 | ||||||
| /**
 | /**
 | ||||||
|  * Signature of callback function for receiving continuous IRDA rx signal. |  * Signature of callback function for receiving continuous IRDA rx signal. | ||||||
|  * |  * | ||||||
| @ -13,26 +23,26 @@ extern "C" { | |||||||
|  * @param   level[in] - level of input IRDA rx signal |  * @param   level[in] - level of input IRDA rx signal | ||||||
|  * @param   duration[in] - duration of continuous rx signal level in us |  * @param   duration[in] - duration of continuous rx signal level in us | ||||||
|  */ |  */ | ||||||
| typedef void (*ApiHalIrdaCaptureCallback)(void* ctx, bool level, uint32_t duration); | typedef void (*ApiHalIrdaRxCaptureCallback)(void* ctx, bool level, uint32_t duration); | ||||||
| 
 | 
 | ||||||
| /**
 | /**
 | ||||||
|  * Signature of callback function for reaching silence timeout on IRDA port. |  * Signature of callback function for reaching silence timeout on IRDA port. | ||||||
|  * |  * | ||||||
|  * @param   ctx[in] - context to pass to callback |  * @param   ctx[in] - context to pass to callback | ||||||
|  */ |  */ | ||||||
| typedef void (*ApiHalIrdaTimeoutCallback)(void* ctx); | typedef void (*ApiHalIrdaRxTimeoutCallback)(void* ctx); | ||||||
| 
 | 
 | ||||||
| /**
 | /**
 | ||||||
|  * Initialize IRDA RX timer to receive interrupts. |  * Initialize IRDA RX timer to receive interrupts. | ||||||
|  * It provides interrupts for every RX-signal edge changing |  * It provides interrupts for every RX-signal edge changing | ||||||
|  * with its duration. |  * with its duration. | ||||||
|  */ |  */ | ||||||
| void api_hal_irda_rx_irq_init(void); | void api_hal_irda_async_rx_start(void); | ||||||
| 
 | 
 | ||||||
| /**
 | /**
 | ||||||
|  * Deinitialize IRDA RX interrupt. |  * Deinitialize IRDA RX interrupt. | ||||||
|  */ |  */ | ||||||
| void api_hal_irda_rx_irq_deinit(void); | void api_hal_irda_async_rx_stop(void); | ||||||
| 
 | 
 | ||||||
| /** Setup api hal for receiving silence timeout.
 | /** Setup api hal for receiving silence timeout.
 | ||||||
|  * Should be used with 'api_hal_irda_timeout_irq_set_callback()'. |  * Should be used with 'api_hal_irda_timeout_irq_set_callback()'. | ||||||
| @ -40,7 +50,7 @@ void api_hal_irda_rx_irq_deinit(void); | |||||||
|  * @param[in]   timeout_ms - time to wait for silence on IRDA port |  * @param[in]   timeout_ms - time to wait for silence on IRDA port | ||||||
|  *                           before generating IRQ. |  *                           before generating IRQ. | ||||||
|  */ |  */ | ||||||
| void api_hal_irda_rx_timeout_irq_init(uint32_t timeout_ms); | void api_hal_irda_async_rx_set_timeout(uint32_t timeout_ms); | ||||||
| 
 | 
 | ||||||
| /**
 | /**
 | ||||||
|  * Setup callback for previously initialized IRDA RX interrupt. |  * Setup callback for previously initialized IRDA RX interrupt. | ||||||
| @ -48,7 +58,7 @@ void api_hal_irda_rx_timeout_irq_init(uint32_t timeout_ms); | |||||||
|  * @param[in]   callback - callback to call when RX signal edge changing occurs |  * @param[in]   callback - callback to call when RX signal edge changing occurs | ||||||
|  * @param[in]   ctx - context for callback |  * @param[in]   ctx - context for callback | ||||||
|  */ |  */ | ||||||
| void api_hal_irda_rx_irq_set_callback(ApiHalIrdaCaptureCallback callback, void *ctx); | void api_hal_irda_async_rx_set_capture_isr_callback(ApiHalIrdaRxCaptureCallback callback, void *ctx); | ||||||
| 
 | 
 | ||||||
| /**
 | /**
 | ||||||
|  * Setup callback for reaching silence timeout on IRDA port. |  * Setup callback for reaching silence timeout on IRDA port. | ||||||
| @ -57,27 +67,53 @@ void api_hal_irda_rx_irq_set_callback(ApiHalIrdaCaptureCallback callback, void * | |||||||
|  * @param[in]   callback - callback for silence timeout |  * @param[in]   callback - callback for silence timeout | ||||||
|  * @param[in]   ctx - context to pass to callback |  * @param[in]   ctx - context to pass to callback | ||||||
|  */ |  */ | ||||||
| void api_hal_irda_rx_timeout_irq_set_callback(ApiHalIrdaTimeoutCallback callback, void *ctx); | void api_hal_irda_async_rx_set_timeout_isr_callback(ApiHalIrdaRxTimeoutCallback callback, void *ctx); | ||||||
| 
 |  | ||||||
| /**
 |  | ||||||
|  * Start generating IRDA TX PWM. Provides PWM initialization on |  | ||||||
|  * defined frequency. |  | ||||||
|  * |  | ||||||
|  * @param[in]   duty_cycle - duty cycle |  | ||||||
|  * @param[in]   freq - PWM frequency to generate |  | ||||||
|  */ |  | ||||||
| void api_hal_irda_pwm_set(float duty_cycle, float freq); |  | ||||||
| 
 |  | ||||||
| /**
 |  | ||||||
|  * Stop generating IRDA PWM signal. |  | ||||||
|  */ |  | ||||||
| void api_hal_irda_pwm_stop(); |  | ||||||
| 
 | 
 | ||||||
| /**
 | /**
 | ||||||
|  * Check if IRDA is in use now. |  * Check if IRDA is in use now. | ||||||
|  * @return  false - IRDA is busy, true otherwise. |  * @return  true - IRDA is busy, false otherwise. | ||||||
|  */ |  */ | ||||||
| bool api_hal_irda_rx_irq_is_busy(void); | bool api_hal_irda_is_busy(void); | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Set callback providing new data. This function has to be called | ||||||
|  |  * before api_hal_irda_async_tx_start(). | ||||||
|  |  * | ||||||
|  |  * @param[in]   callback - function to provide new data | ||||||
|  |  * @param[in]   context - context for callback | ||||||
|  |  */ | ||||||
|  | void api_hal_irda_async_tx_set_data_isr_callback(ApiHalIrdaTxGetDataCallback callback, void* context); | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Start IR asynchronous transmission. It can be stopped by 2 reasons: | ||||||
|  |  * 1) implicit call for api_hal_irda_async_tx_stop() | ||||||
|  |  * 2) callback can provide ApiHalIrdaTxGetDataStateLastDone response | ||||||
|  |  *      which means no more data available for transmission. | ||||||
|  |  * | ||||||
|  |  * Any func (api_hal_irda_async_tx_stop() or | ||||||
|  |  * api_hal_irda_async_tx_wait_termination()) has to be called to wait | ||||||
|  |  * end of transmission and free resources. | ||||||
|  |  * | ||||||
|  |  * @param[in]   freq - frequency for PWM | ||||||
|  |  * @param[in]   duty_cycle - duty cycle for PWM | ||||||
|  |  * @return      true if transmission successfully started, false otherwise. | ||||||
|  |  *              If start failed no need to free resources. | ||||||
|  |  */ | ||||||
|  | bool api_hal_irda_async_tx_start(uint32_t freq, float duty_cycle); | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Stop IR asynchronous transmission and free resources. | ||||||
|  |  * Transmission will stop as soon as transmission reaches end of | ||||||
|  |  * package (ApiHalIrdaTxGetDataStateDone or ApiHalIrdaTxGetDataStateLastDone). | ||||||
|  |  */ | ||||||
|  | void api_hal_irda_async_tx_stop(void); | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Wait for end of IR asynchronous transmission and free resources. | ||||||
|  |  * Transmission will stop as soon as transmission reaches end of | ||||||
|  |  * transmission (ApiHalIrdaTxGetDataStateLastDone). | ||||||
|  |  */ | ||||||
|  | void api_hal_irda_async_tx_wait_termination(void); | ||||||
| 
 | 
 | ||||||
| #ifdef __cplusplus | #ifdef __cplusplus | ||||||
| } | } | ||||||
|  | |||||||
| @ -32,11 +32,6 @@ void COMP_IRQHandler(void) { | |||||||
|     HAL_COMP_IRQHandler(&hcomp1); |     HAL_COMP_IRQHandler(&hcomp1); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void TIM1_UP_TIM16_IRQHandler(void) { |  | ||||||
|     HAL_TIM_IRQHandler(&htim1); |  | ||||||
|     HAL_TIM_IRQHandler(&htim16); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void TIM1_TRG_COM_TIM17_IRQHandler(void) { | void TIM1_TRG_COM_TIM17_IRQHandler(void) { | ||||||
|     HAL_TIM_IRQHandler(&htim1); |     HAL_TIM_IRQHandler(&htim1); | ||||||
| } | } | ||||||
|  | |||||||
| @ -5,6 +5,7 @@ | |||||||
| #include <stm32wbxx_ll_tim.h> | #include <stm32wbxx_ll_tim.h> | ||||||
| 
 | 
 | ||||||
| volatile ApiHalInterruptISR api_hal_tim_tim2_isr = NULL; | volatile ApiHalInterruptISR api_hal_tim_tim2_isr = NULL; | ||||||
|  | volatile ApiHalInterruptISR api_hal_tim_tim1_isr = NULL; | ||||||
| 
 | 
 | ||||||
| #define API_HAL_INTERRUPT_DMA_COUNT 2 | #define API_HAL_INTERRUPT_DMA_COUNT 2 | ||||||
| #define API_HAL_INTERRUPT_DMA_CHANNELS_COUNT 8 | #define API_HAL_INTERRUPT_DMA_CHANNELS_COUNT 8 | ||||||
| @ -32,6 +33,13 @@ void api_hal_interrupt_set_timer_isr(TIM_TypeDef* timer, ApiHalInterruptISR isr) | |||||||
|             furi_assert(api_hal_tim_tim2_isr != NULL); |             furi_assert(api_hal_tim_tim2_isr != NULL); | ||||||
|         } |         } | ||||||
|         api_hal_tim_tim2_isr = isr; |         api_hal_tim_tim2_isr = isr; | ||||||
|  |     } else if (timer == TIM1) { | ||||||
|  |         if (isr) { | ||||||
|  |             furi_assert(api_hal_tim_tim1_isr == NULL); | ||||||
|  |         } else { | ||||||
|  |             furi_assert(api_hal_tim_tim1_isr != NULL); | ||||||
|  |         } | ||||||
|  |         api_hal_tim_tim1_isr = isr; | ||||||
|     } else { |     } else { | ||||||
|         furi_check(0); |         furi_check(0); | ||||||
|     } |     } | ||||||
| @ -43,7 +51,7 @@ void api_hal_interrupt_set_dma_channel_isr(DMA_TypeDef* dma, uint32_t channel, A | |||||||
|     furi_check(channel < API_HAL_INTERRUPT_DMA_CHANNELS_COUNT); |     furi_check(channel < API_HAL_INTERRUPT_DMA_CHANNELS_COUNT); | ||||||
|     if (dma == DMA1) { |     if (dma == DMA1) { | ||||||
|         api_hal_dma_channel_isr[0][channel] = isr; |         api_hal_dma_channel_isr[0][channel] = isr; | ||||||
|     } else if (dma == DMA1) { |     } else if (dma == DMA2) { | ||||||
|         api_hal_dma_channel_isr[1][channel] = isr; |         api_hal_dma_channel_isr[1][channel] = isr; | ||||||
|     } else { |     } else { | ||||||
|         furi_check(0); |         furi_check(0); | ||||||
| @ -73,6 +81,15 @@ void TIM2_IRQHandler(void) { | |||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | /* Timer 1 Update */ | ||||||
|  | void TIM1_UP_TIM16_IRQHandler(void) { | ||||||
|  |     if (api_hal_tim_tim1_isr) { | ||||||
|  |         api_hal_tim_tim1_isr(); | ||||||
|  |     } else { | ||||||
|  |         HAL_TIM_IRQHandler(&htim1); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
| /* DMA 1 */ | /* DMA 1 */ | ||||||
| void DMA1_Channel1_IRQHandler(void) { | void DMA1_Channel1_IRQHandler(void) { | ||||||
|     if (api_hal_dma_channel_isr[0][0]) api_hal_dma_channel_isr[0][0](); |     if (api_hal_dma_channel_isr[0][0]) api_hal_dma_channel_isr[0][0](); | ||||||
|  | |||||||
| @ -1,4 +1,8 @@ | |||||||
| #include "api-hal-irda.h" | #include "api-hal-irda.h" | ||||||
|  | #include "api-hal-delay.h" | ||||||
|  | #include "furi/check.h" | ||||||
|  | #include "stm32wbxx_ll_dma.h" | ||||||
|  | #include "sys/_stdint.h" | ||||||
| #include <cmsis_os2.h> | #include <cmsis_os2.h> | ||||||
| #include <api-hal-interrupt.h> | #include <api-hal-interrupt.h> | ||||||
| #include <api-hal-resources.h> | #include <api-hal-resources.h> | ||||||
| @ -9,81 +13,115 @@ | |||||||
| 
 | 
 | ||||||
| #include <stdio.h> | #include <stdio.h> | ||||||
| #include <furi.h> | #include <furi.h> | ||||||
|  | #include <math.h> | ||||||
| #include <main.h> | #include <main.h> | ||||||
| #include <api-hal-pwm.h> | #include <api-hal-pwm.h> | ||||||
| 
 | 
 | ||||||
| static struct{ | #define IRDA_TIM_TX_DMA_BUFFER_SIZE         200 | ||||||
|     ApiHalIrdaCaptureCallback capture_callback; | #define IRDA_POLARITY_SHIFT                 1 | ||||||
|  | 
 | ||||||
|  | #define IRDA_TX_CCMR_HIGH    (TIM_CCMR2_OC3PE | LL_TIM_OCMODE_PWM2)              /* Mark time - enable PWM2 mode */ | ||||||
|  | #define IRDA_TX_CCMR_LOW     (TIM_CCMR2_OC3PE | LL_TIM_OCMODE_FORCED_INACTIVE)   /* Space time - force low */ | ||||||
|  | 
 | ||||||
|  | typedef struct{ | ||||||
|  |     ApiHalIrdaRxCaptureCallback capture_callback; | ||||||
|     void *capture_context; |     void *capture_context; | ||||||
|     ApiHalIrdaTimeoutCallback timeout_callback; |     ApiHalIrdaRxTimeoutCallback timeout_callback; | ||||||
|     void *timeout_context; |     void *timeout_context; | ||||||
| } timer_irda; | } IrdaTimRx; | ||||||
| 
 | 
 | ||||||
| typedef enum{ | typedef struct{ | ||||||
|     TimerIRQSourceCCI1, |     uint8_t* polarity; | ||||||
|     TimerIRQSourceCCI2, |     uint16_t* data; | ||||||
| } TimerIRQSource; |     size_t size; | ||||||
|  |     bool packet_end; | ||||||
|  |     bool last_packet_end; | ||||||
|  | } IrdaTxBuf; | ||||||
| 
 | 
 | ||||||
| static void api_hal_irda_handle_timeout(void) { | typedef struct { | ||||||
|     /* Timers CNT register starts to counting from 0 to ARR, but it is
 |     float cycle_duration; | ||||||
|      * reseted when Channel 1 catches interrupt. It is not reseted by |     ApiHalIrdaTxGetDataCallback data_callback; | ||||||
|      * channel 2, though, so we have to distract it's values (see TimerIRQSourceCCI1 ISR). |     void* data_context; | ||||||
|      * This can cause false timeout: when time is over, but we started |     IrdaTxBuf buffer[2]; | ||||||
|      * receiving new signal few microseconds ago, because CNT register |     osSemaphoreId_t stop_semaphore; | ||||||
|      * is reseted once per period, not per sample. */ | } IrdaTimTx; | ||||||
|     if (LL_GPIO_IsInputPinSet(gpio_irda_rx.port, gpio_irda_rx.pin) == 0) |  | ||||||
|         return; |  | ||||||
| 
 | 
 | ||||||
|     if (timer_irda.timeout_callback) | typedef enum { | ||||||
|         timer_irda.timeout_callback(timer_irda.timeout_context); |     IrdaStateIdle,                  /** Api Hal Irda is ready to start RX or TX */ | ||||||
| } |     IrdaStateAsyncRx,               /** Async RX started */ | ||||||
|  |     IrdaStateAsyncTx,               /** Async TX started, DMA and timer is on */ | ||||||
|  |     IrdaStateAsyncTxStopReq,        /** Async TX started, async stop request received */ | ||||||
|  |     IrdaStateAsyncTxStopInProgress, /** Async TX started, stop request is processed and we wait for last data to be sent */ | ||||||
|  |     IrdaStateAsyncTxStopped,        /** Async TX complete, cleanup needed */ | ||||||
|  |     IrdaStateMAX, | ||||||
|  | } IrdaState; | ||||||
| 
 | 
 | ||||||
| /* High pin level is a Space state of IRDA signal. Invert level for further processing. */ | static volatile IrdaState api_hal_irda_state = IrdaStateIdle; | ||||||
| static void api_hal_irda_handle_capture(TimerIRQSource source) { | static IrdaTimTx irda_tim_tx; | ||||||
|     uint32_t duration = 0; | static IrdaTimRx irda_tim_rx; | ||||||
|     bool level = 0; |  | ||||||
| 
 | 
 | ||||||
|     switch (source) { | static bool api_hal_irda_tx_fill_buffer(uint8_t buf_num, uint8_t polarity_shift); | ||||||
|     case TimerIRQSourceCCI1: | static void api_hal_irda_async_tx_free_resources(void); | ||||||
|         duration = LL_TIM_IC_GetCaptureCH1(TIM2) - LL_TIM_IC_GetCaptureCH2(TIM2); | static void api_hal_irda_tx_dma_set_polarity(uint8_t buf_num, uint8_t polarity_shift); | ||||||
|         level = 1; | static void api_hal_irda_tx_dma_set_buffer(uint8_t buf_num); | ||||||
|         break; | static void api_hal_irda_tx_fill_buffer_last(uint8_t buf_num); | ||||||
|     case TimerIRQSourceCCI2: | static uint8_t api_hal_irda_get_current_dma_tx_buffer(void); | ||||||
|         duration = LL_TIM_IC_GetCaptureCH2(TIM2); | static void api_hal_irda_tx_dma_polarity_isr(); | ||||||
|         level = 0; | static void api_hal_irda_tx_dma_isr(); | ||||||
|         break; |  | ||||||
|     default: |  | ||||||
|         furi_check(0); |  | ||||||
|     } |  | ||||||
| 
 | 
 | ||||||
|     if (timer_irda.capture_callback) | static void api_hal_irda_tim_rx_isr() { | ||||||
|         timer_irda.capture_callback(timer_irda.capture_context, level, duration); |  | ||||||
| } |  | ||||||
| 
 | 
 | ||||||
| static void api_hal_irda_isr() { |     /* Timeout */ | ||||||
|     if(LL_TIM_IsActiveFlag_CC3(TIM2)) { |     if(LL_TIM_IsActiveFlag_CC3(TIM2)) { | ||||||
|         LL_TIM_ClearFlag_CC3(TIM2); |         LL_TIM_ClearFlag_CC3(TIM2); | ||||||
|         api_hal_irda_handle_timeout(); |         furi_assert(api_hal_irda_state == IrdaStateAsyncRx); | ||||||
|     } |  | ||||||
|     if(LL_TIM_IsActiveFlag_CC1(TIM2)) { |  | ||||||
|         LL_TIM_ClearFlag_CC1(TIM2); |  | ||||||
| 
 | 
 | ||||||
|         if(READ_BIT(TIM2->CCMR1, TIM_CCMR1_CC1S)) { |         /* Timers CNT register starts to counting from 0 to ARR, but it is
 | ||||||
|             // input capture
 |          * reseted when Channel 1 catches interrupt. It is not reseted by | ||||||
|             api_hal_irda_handle_capture(TimerIRQSourceCCI1); |          * channel 2, though, so we have to distract it's values (see TimerIRQSourceCCI1 ISR). | ||||||
|  |          * This can cause false timeout: when time is over, but we started | ||||||
|  |          * receiving new signal few microseconds ago, because CNT register | ||||||
|  |          * is reseted once per period, not per sample. */ | ||||||
|  |         if (LL_GPIO_IsInputPinSet(gpio_irda_rx.port, gpio_irda_rx.pin) != 0) { | ||||||
|  |             if (irda_tim_rx.timeout_callback) | ||||||
|  |                 irda_tim_rx.timeout_callback(irda_tim_rx.timeout_context); | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  | 
 | ||||||
|  |     /* Rising Edge */ | ||||||
|  |     if(LL_TIM_IsActiveFlag_CC1(TIM2)) { | ||||||
|  |         LL_TIM_ClearFlag_CC1(TIM2); | ||||||
|  |         furi_assert(api_hal_irda_state == IrdaStateAsyncRx); | ||||||
|  | 
 | ||||||
|  |         if(READ_BIT(TIM2->CCMR1, TIM_CCMR1_CC1S)) { | ||||||
|  |             /* Low pin level is a Mark state of IRDA signal. Invert level for further processing. */ | ||||||
|  |             uint32_t duration = LL_TIM_IC_GetCaptureCH1(TIM2) - LL_TIM_IC_GetCaptureCH2(TIM2); | ||||||
|  |             if (irda_tim_rx.capture_callback) | ||||||
|  |                 irda_tim_rx.capture_callback(irda_tim_rx.capture_context, 1, duration); | ||||||
|  |         } else { | ||||||
|  |             furi_assert(0); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /* Falling Edge */ | ||||||
|     if(LL_TIM_IsActiveFlag_CC2(TIM2)) { |     if(LL_TIM_IsActiveFlag_CC2(TIM2)) { | ||||||
|         LL_TIM_ClearFlag_CC2(TIM2); |         LL_TIM_ClearFlag_CC2(TIM2); | ||||||
|  |         furi_assert(api_hal_irda_state == IrdaStateAsyncRx); | ||||||
| 
 | 
 | ||||||
|         if(READ_BIT(TIM2->CCMR1, TIM_CCMR1_CC2S)) { |         if(READ_BIT(TIM2->CCMR1, TIM_CCMR1_CC2S)) { | ||||||
|             // input capture
 |             /* High pin level is a Space state of IRDA signal. Invert level for further processing. */ | ||||||
|             api_hal_irda_handle_capture(TimerIRQSourceCCI2); |             uint32_t duration = LL_TIM_IC_GetCaptureCH2(TIM2); | ||||||
|  |             if (irda_tim_rx.capture_callback) | ||||||
|  |                 irda_tim_rx.capture_callback(irda_tim_rx.capture_context, 0, duration); | ||||||
|  |         } else { | ||||||
|  |             furi_assert(0); | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void api_hal_irda_rx_irq_init(void) { | void api_hal_irda_async_rx_start(void) { | ||||||
|  |     furi_assert(api_hal_irda_state == IrdaStateIdle); | ||||||
|  | 
 | ||||||
|     LL_APB1_GRP1_EnableClock(LL_APB1_GRP1_PERIPH_TIM2); |     LL_APB1_GRP1_EnableClock(LL_APB1_GRP1_PERIPH_TIM2); | ||||||
|     LL_AHB2_GRP1_EnableClock(LL_AHB2_GRP1_PERIPH_GPIOA); |     LL_AHB2_GRP1_EnableClock(LL_AHB2_GRP1_PERIPH_GPIOA); | ||||||
| 
 | 
 | ||||||
| @ -114,50 +152,433 @@ void api_hal_irda_rx_irq_init(void) { | |||||||
|     LL_TIM_IC_SetActiveInput(TIM2, LL_TIM_CHANNEL_CH2, LL_TIM_ACTIVEINPUT_INDIRECTTI); |     LL_TIM_IC_SetActiveInput(TIM2, LL_TIM_CHANNEL_CH2, LL_TIM_ACTIVEINPUT_INDIRECTTI); | ||||||
|     LL_TIM_IC_SetPrescaler(TIM2, LL_TIM_CHANNEL_CH2, LL_TIM_ICPSC_DIV1); |     LL_TIM_IC_SetPrescaler(TIM2, LL_TIM_CHANNEL_CH2, LL_TIM_ICPSC_DIV1); | ||||||
| 
 | 
 | ||||||
|  |     api_hal_interrupt_set_timer_isr(TIM2, api_hal_irda_tim_rx_isr); | ||||||
|  |     api_hal_irda_state = IrdaStateAsyncRx; | ||||||
|  | 
 | ||||||
|     LL_TIM_EnableIT_CC1(TIM2); |     LL_TIM_EnableIT_CC1(TIM2); | ||||||
|     LL_TIM_EnableIT_CC2(TIM2); |     LL_TIM_EnableIT_CC2(TIM2); | ||||||
|     LL_TIM_CC_EnableChannel(TIM2, LL_TIM_CHANNEL_CH1); |     LL_TIM_CC_EnableChannel(TIM2, LL_TIM_CHANNEL_CH1); | ||||||
|     LL_TIM_CC_EnableChannel(TIM2, LL_TIM_CHANNEL_CH2); |     LL_TIM_CC_EnableChannel(TIM2, LL_TIM_CHANNEL_CH2); | ||||||
| 
 | 
 | ||||||
|     api_hal_interrupt_set_timer_isr(TIM2, api_hal_irda_isr); |  | ||||||
| 
 |  | ||||||
|     LL_TIM_SetCounter(TIM2, 0); |     LL_TIM_SetCounter(TIM2, 0); | ||||||
|     LL_TIM_EnableCounter(TIM2); |     LL_TIM_EnableCounter(TIM2); | ||||||
| 
 | 
 | ||||||
|     NVIC_SetPriority(TIM2_IRQn, NVIC_EncodePriority(NVIC_GetPriorityGrouping(),5, 0)); |     NVIC_SetPriority(TIM2_IRQn, NVIC_EncodePriority(NVIC_GetPriorityGrouping(), 5, 0)); | ||||||
|     NVIC_EnableIRQ(TIM2_IRQn); |     NVIC_EnableIRQ(TIM2_IRQn); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void api_hal_irda_rx_irq_deinit(void) { | void api_hal_irda_async_rx_stop(void) { | ||||||
|  |     furi_assert(api_hal_irda_state == IrdaStateAsyncRx); | ||||||
|     LL_TIM_DeInit(TIM2); |     LL_TIM_DeInit(TIM2); | ||||||
|     api_hal_interrupt_set_timer_isr(TIM2, NULL); |     api_hal_interrupt_set_timer_isr(TIM2, NULL); | ||||||
|  |     LL_APB1_GRP1_DisableClock(LL_APB1_GRP1_PERIPH_TIM2); | ||||||
|  |     api_hal_irda_state = IrdaStateIdle; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void api_hal_irda_rx_timeout_irq_init(uint32_t timeout_ms) { | void api_hal_irda_async_rx_set_timeout(uint32_t timeout_ms) { | ||||||
|     LL_TIM_OC_SetCompareCH3(TIM2, timeout_ms * 1000); |     LL_TIM_OC_SetCompareCH3(TIM2, timeout_ms * 1000); | ||||||
|     LL_TIM_OC_SetMode(TIM2, LL_TIM_CHANNEL_CH3, LL_TIM_OCMODE_ACTIVE); |     LL_TIM_OC_SetMode(TIM2, LL_TIM_CHANNEL_CH3, LL_TIM_OCMODE_ACTIVE); | ||||||
|     LL_TIM_CC_EnableChannel(TIM2, LL_TIM_CHANNEL_CH3); |     LL_TIM_CC_EnableChannel(TIM2, LL_TIM_CHANNEL_CH3); | ||||||
|     LL_TIM_EnableIT_CC3(TIM2); |     LL_TIM_EnableIT_CC3(TIM2); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| bool api_hal_irda_rx_irq_is_busy(void) { | bool api_hal_irda_is_busy(void) { | ||||||
|     return (LL_TIM_IsEnabledIT_CC1(TIM2) || LL_TIM_IsEnabledIT_CC2(TIM2)); |     return api_hal_irda_state != IrdaStateIdle; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void api_hal_irda_rx_irq_set_callback(ApiHalIrdaCaptureCallback callback, void *ctx) { | void api_hal_irda_async_rx_set_capture_isr_callback(ApiHalIrdaRxCaptureCallback callback, void *ctx) { | ||||||
|     timer_irda.capture_callback = callback; |     irda_tim_rx.capture_callback = callback; | ||||||
|     timer_irda.capture_context = ctx; |     irda_tim_rx.capture_context = ctx; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void api_hal_irda_rx_timeout_irq_set_callback(ApiHalIrdaTimeoutCallback callback, void *ctx) { | void api_hal_irda_async_rx_set_timeout_isr_callback(ApiHalIrdaRxTimeoutCallback callback, void *ctx) { | ||||||
|     timer_irda.timeout_callback = callback; |     irda_tim_rx.timeout_callback = callback; | ||||||
|     timer_irda.timeout_context = ctx; |     irda_tim_rx.timeout_context = ctx; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void api_hal_irda_pwm_set(float value, float freq) { | static void api_hal_irda_tx_dma_terminate(void) { | ||||||
|     hal_pwmn_set(value, freq, &IRDA_TX_TIM, IRDA_TX_CH); |     LL_DMA_DisableIT_TC(DMA1, LL_DMA_CHANNEL_1); | ||||||
|  |     LL_DMA_DisableIT_HT(DMA1, LL_DMA_CHANNEL_2); | ||||||
|  |     LL_DMA_DisableIT_TC(DMA1, LL_DMA_CHANNEL_2); | ||||||
|  | 
 | ||||||
|  |     furi_assert(api_hal_irda_state == IrdaStateAsyncTxStopInProgress); | ||||||
|  | 
 | ||||||
|  |     LL_DMA_DisableIT_TC(DMA1, LL_DMA_CHANNEL_1); | ||||||
|  |     LL_DMA_DisableChannel(DMA1, LL_DMA_CHANNEL_2); | ||||||
|  |     LL_DMA_DisableChannel(DMA1, LL_DMA_CHANNEL_1); | ||||||
|  |     LL_TIM_DisableCounter(TIM1); | ||||||
|  |     osStatus_t status = osSemaphoreRelease(irda_tim_tx.stop_semaphore); | ||||||
|  |     furi_check(status == osOK); | ||||||
|  |     api_hal_irda_state = IrdaStateAsyncTxStopped; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void api_hal_irda_pwm_stop() { | static uint8_t api_hal_irda_get_current_dma_tx_buffer(void) { | ||||||
|     hal_pwmn_stop(&IRDA_TX_TIM, IRDA_TX_CH); |     uint8_t buf_num = 0; | ||||||
|  |     uint32_t buffer_adr = LL_DMA_GetMemoryAddress(DMA1, LL_DMA_CHANNEL_2); | ||||||
|  |     if (buffer_adr == (uint32_t) irda_tim_tx.buffer[0].data) { | ||||||
|  |         buf_num = 0; | ||||||
|  |     } else if (buffer_adr == (uint32_t) irda_tim_tx.buffer[1].data) { | ||||||
|  |         buf_num = 1; | ||||||
|  |     } else { | ||||||
|  |         furi_assert(0); | ||||||
|  |     } | ||||||
|  |     return buf_num; | ||||||
| } | } | ||||||
|  | 
 | ||||||
|  | static void api_hal_irda_tx_dma_polarity_isr() { | ||||||
|  |     if (LL_DMA_IsActiveFlag_TE1(DMA1)) { | ||||||
|  |         LL_DMA_ClearFlag_TE1(DMA1); | ||||||
|  |         furi_check(0); | ||||||
|  |     } | ||||||
|  |     if (LL_DMA_IsActiveFlag_TC1(DMA1) && LL_DMA_IsEnabledIT_TC(DMA1, LL_DMA_CHANNEL_1)) { | ||||||
|  |         LL_DMA_ClearFlag_TC1(DMA1); | ||||||
|  | 
 | ||||||
|  |         furi_check((api_hal_irda_state == IrdaStateAsyncTx) | ||||||
|  |                     || (api_hal_irda_state == IrdaStateAsyncTxStopReq) | ||||||
|  |                     || (api_hal_irda_state == IrdaStateAsyncTxStopInProgress)); | ||||||
|  |         /* actually TC2 is processed and buffer is next buffer */ | ||||||
|  |         uint8_t next_buf_num = api_hal_irda_get_current_dma_tx_buffer(); | ||||||
|  |         api_hal_irda_tx_dma_set_polarity(next_buf_num, 0); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void api_hal_irda_tx_dma_isr() { | ||||||
|  |     if (LL_DMA_IsActiveFlag_TE2(DMA1)) { | ||||||
|  |         LL_DMA_ClearFlag_TE2(DMA1); | ||||||
|  |         furi_check(0); | ||||||
|  |     } | ||||||
|  |     if (LL_DMA_IsActiveFlag_HT2(DMA1) && LL_DMA_IsEnabledIT_HT(DMA1, LL_DMA_CHANNEL_2)) { | ||||||
|  |         LL_DMA_ClearFlag_HT2(DMA1); | ||||||
|  |         uint8_t buf_num = api_hal_irda_get_current_dma_tx_buffer(); | ||||||
|  |         uint8_t next_buf_num = !buf_num; | ||||||
|  |         if (irda_tim_tx.buffer[buf_num].last_packet_end) { | ||||||
|  |             LL_DMA_DisableIT_HT(DMA1, LL_DMA_CHANNEL_2); | ||||||
|  |         } else if (!irda_tim_tx.buffer[buf_num].packet_end || (api_hal_irda_state == IrdaStateAsyncTx)) { | ||||||
|  |             bool result = api_hal_irda_tx_fill_buffer(next_buf_num, 0); | ||||||
|  |             if (irda_tim_tx.buffer[next_buf_num].last_packet_end) { | ||||||
|  |                 LL_DMA_DisableIT_HT(DMA1, LL_DMA_CHANNEL_2); | ||||||
|  |             } | ||||||
|  |             if (!result) { | ||||||
|  |                 furi_assert(0); | ||||||
|  |                 api_hal_irda_state = IrdaStateAsyncTxStopReq; | ||||||
|  |             } | ||||||
|  |         } else if (api_hal_irda_state == IrdaStateAsyncTxStopReq) { | ||||||
|  |             /* fallthrough */ | ||||||
|  |         } else { | ||||||
|  |             furi_check(0); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |     if (LL_DMA_IsActiveFlag_TC2(DMA1) && LL_DMA_IsEnabledIT_TC(DMA1, LL_DMA_CHANNEL_2)) { | ||||||
|  |         LL_DMA_ClearFlag_TC2(DMA1); | ||||||
|  |         furi_check((api_hal_irda_state == IrdaStateAsyncTxStopInProgress) | ||||||
|  |                     || (api_hal_irda_state == IrdaStateAsyncTxStopReq) | ||||||
|  |                     || (api_hal_irda_state == IrdaStateAsyncTx)); | ||||||
|  | 
 | ||||||
|  |         uint8_t buf_num = api_hal_irda_get_current_dma_tx_buffer(); | ||||||
|  |         uint8_t next_buf_num = !buf_num; | ||||||
|  |         if (api_hal_irda_state == IrdaStateAsyncTxStopInProgress) { | ||||||
|  |             api_hal_irda_tx_dma_terminate(); | ||||||
|  |         } else if (irda_tim_tx.buffer[buf_num].last_packet_end | ||||||
|  |            || (irda_tim_tx.buffer[buf_num].packet_end && (api_hal_irda_state == IrdaStateAsyncTxStopReq))) { | ||||||
|  |             api_hal_irda_state = IrdaStateAsyncTxStopInProgress; | ||||||
|  |             api_hal_irda_tx_fill_buffer_last(next_buf_num); | ||||||
|  |             api_hal_irda_tx_dma_set_buffer(next_buf_num); | ||||||
|  |         } else { | ||||||
|  |             /* if it's not end of the packet - continue receiving */ | ||||||
|  |             api_hal_irda_tx_dma_set_buffer(next_buf_num); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void api_hal_irda_configure_tim_pwm_tx(uint32_t freq, float duty_cycle) | ||||||
|  | { | ||||||
|  |     LL_APB2_GRP1_EnableClock(LL_APB2_GRP1_PERIPH_TIM1); | ||||||
|  | /*    LL_DBGMCU_APB2_GRP1_FreezePeriph(LL_DBGMCU_APB2_GRP1_TIM1_STOP); */ | ||||||
|  | 
 | ||||||
|  |     LL_TIM_DisableCounter(TIM1); | ||||||
|  |     LL_TIM_SetRepetitionCounter(TIM1, 0); | ||||||
|  |     LL_TIM_SetCounter(TIM1, 0); | ||||||
|  |     LL_TIM_SetPrescaler(TIM1, 0); | ||||||
|  |     LL_TIM_SetCounterMode(TIM1, LL_TIM_COUNTERMODE_UP); | ||||||
|  |     LL_TIM_EnableARRPreload(TIM1); | ||||||
|  |     LL_TIM_SetAutoReload(TIM1, __LL_TIM_CALC_ARR(SystemCoreClock, LL_TIM_GetPrescaler(TIM1), freq)); | ||||||
|  |     LL_TIM_OC_SetCompareCH3(TIM1, ( (LL_TIM_GetAutoReload(TIM1) + 1 ) * (1 - duty_cycle))); | ||||||
|  |     LL_TIM_OC_EnablePreload(TIM1, LL_TIM_CHANNEL_CH3); | ||||||
|  |     /* LL_TIM_OCMODE_PWM2 set by DMA */ | ||||||
|  |     LL_TIM_OC_SetMode(TIM1, LL_TIM_CHANNEL_CH3, LL_TIM_OCMODE_FORCED_INACTIVE); | ||||||
|  |     LL_TIM_OC_SetPolarity(TIM1, LL_TIM_CHANNEL_CH3N, LL_TIM_OCPOLARITY_HIGH); | ||||||
|  |     LL_TIM_OC_DisableFast(TIM1, LL_TIM_CHANNEL_CH3); | ||||||
|  |     LL_TIM_CC_EnableChannel(TIM1, LL_TIM_CHANNEL_CH3N); | ||||||
|  |     LL_TIM_DisableIT_CC3(TIM1); | ||||||
|  |     LL_TIM_DisableMasterSlaveMode(TIM1); | ||||||
|  |     LL_TIM_EnableAllOutputs(TIM1); | ||||||
|  |     LL_TIM_DisableIT_UPDATE(TIM1); | ||||||
|  |     LL_TIM_EnableDMAReq_UPDATE(TIM1); | ||||||
|  | 
 | ||||||
|  |     NVIC_SetPriority(TIM1_UP_TIM16_IRQn, NVIC_EncodePriority(NVIC_GetPriorityGrouping(), 5, 0)); | ||||||
|  |     NVIC_EnableIRQ(TIM1_UP_TIM16_IRQn); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void api_hal_irda_configure_tim_cmgr2_dma_tx(void) { | ||||||
|  |     LL_C2_AHB1_GRP1_EnableClock(LL_C2_AHB1_GRP1_PERIPH_DMA1); | ||||||
|  | 
 | ||||||
|  |     LL_DMA_InitTypeDef dma_config = {0}; | ||||||
|  |     dma_config.PeriphOrM2MSrcAddress = (uint32_t)&(TIM1->CCMR2); | ||||||
|  |     dma_config.MemoryOrM2MDstAddress = (uint32_t) NULL; | ||||||
|  |     dma_config.Direction = LL_DMA_DIRECTION_MEMORY_TO_PERIPH; | ||||||
|  |     dma_config.Mode = LL_DMA_MODE_NORMAL; | ||||||
|  |     dma_config.PeriphOrM2MSrcIncMode = LL_DMA_PERIPH_NOINCREMENT; | ||||||
|  |     dma_config.MemoryOrM2MDstIncMode = LL_DMA_MEMORY_INCREMENT; | ||||||
|  |     /* fill word to have other bits set to 0 */ | ||||||
|  |     dma_config.PeriphOrM2MSrcDataSize = LL_DMA_PDATAALIGN_WORD; | ||||||
|  |     dma_config.MemoryOrM2MDstDataSize = LL_DMA_MDATAALIGN_BYTE; | ||||||
|  |     dma_config.NbData = 0; | ||||||
|  |     dma_config.PeriphRequest = LL_DMAMUX_REQ_TIM1_UP; | ||||||
|  |     dma_config.Priority = LL_DMA_PRIORITY_VERYHIGH; | ||||||
|  |     LL_DMA_Init(DMA1, LL_DMA_CHANNEL_1, &dma_config); | ||||||
|  |     api_hal_interrupt_set_dma_channel_isr(DMA1, LL_DMA_CHANNEL_1, api_hal_irda_tx_dma_polarity_isr); | ||||||
|  |     LL_DMA_ClearFlag_TE1(DMA1); | ||||||
|  |     LL_DMA_ClearFlag_TC1(DMA1); | ||||||
|  |     LL_DMA_EnableIT_TE(DMA1, LL_DMA_CHANNEL_1); | ||||||
|  |     LL_DMA_EnableIT_TC(DMA1, LL_DMA_CHANNEL_1); | ||||||
|  | 
 | ||||||
|  |     NVIC_SetPriority(DMA1_Channel1_IRQn, NVIC_EncodePriority(NVIC_GetPriorityGrouping(), 4, 0)); | ||||||
|  |     NVIC_EnableIRQ(DMA1_Channel1_IRQn); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void api_hal_irda_configure_tim_rcr_dma_tx(void) { | ||||||
|  |     LL_C2_AHB1_GRP1_EnableClock(LL_C2_AHB1_GRP1_PERIPH_DMA1); | ||||||
|  | 
 | ||||||
|  |     LL_DMA_InitTypeDef dma_config = {0}; | ||||||
|  |     dma_config.PeriphOrM2MSrcAddress = (uint32_t)&(TIM1->RCR); | ||||||
|  |     dma_config.MemoryOrM2MDstAddress = (uint32_t) NULL; | ||||||
|  |     dma_config.Direction = LL_DMA_DIRECTION_MEMORY_TO_PERIPH; | ||||||
|  |     dma_config.Mode = LL_DMA_MODE_NORMAL; | ||||||
|  |     dma_config.PeriphOrM2MSrcIncMode = LL_DMA_PERIPH_NOINCREMENT; | ||||||
|  |     dma_config.MemoryOrM2MDstIncMode = LL_DMA_MEMORY_INCREMENT; | ||||||
|  |     dma_config.PeriphOrM2MSrcDataSize = LL_DMA_PDATAALIGN_HALFWORD; | ||||||
|  |     dma_config.MemoryOrM2MDstDataSize = LL_DMA_MDATAALIGN_HALFWORD; | ||||||
|  |     dma_config.NbData = 0; | ||||||
|  |     dma_config.PeriphRequest = LL_DMAMUX_REQ_TIM1_UP; | ||||||
|  |     dma_config.Priority = LL_DMA_PRIORITY_MEDIUM; | ||||||
|  |     LL_DMA_Init(DMA1, LL_DMA_CHANNEL_2, &dma_config); | ||||||
|  |     api_hal_interrupt_set_dma_channel_isr(DMA1, LL_DMA_CHANNEL_2, api_hal_irda_tx_dma_isr); | ||||||
|  |     LL_DMA_ClearFlag_TC2(DMA1); | ||||||
|  |     LL_DMA_ClearFlag_HT2(DMA1); | ||||||
|  |     LL_DMA_ClearFlag_TE2(DMA1); | ||||||
|  |     LL_DMA_EnableIT_TC(DMA1, LL_DMA_CHANNEL_2); | ||||||
|  |     LL_DMA_EnableIT_HT(DMA1, LL_DMA_CHANNEL_2); | ||||||
|  |     LL_DMA_EnableIT_TE(DMA1, LL_DMA_CHANNEL_2); | ||||||
|  | 
 | ||||||
|  |     NVIC_SetPriority(DMA1_Channel2_IRQn, NVIC_EncodePriority(NVIC_GetPriorityGrouping(), 5, 0)); | ||||||
|  |     NVIC_EnableIRQ(DMA1_Channel2_IRQn); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void api_hal_irda_tx_fill_buffer_last(uint8_t buf_num) { | ||||||
|  |     furi_assert(buf_num < 2); | ||||||
|  |     furi_assert(api_hal_irda_state != IrdaStateAsyncRx); | ||||||
|  |     furi_assert(api_hal_irda_state < IrdaStateMAX); | ||||||
|  |     furi_assert(irda_tim_tx.data_callback); | ||||||
|  |     IrdaTxBuf* buffer = &irda_tim_tx.buffer[buf_num]; | ||||||
|  |     furi_assert(buffer->data != NULL); | ||||||
|  |     furi_assert(buffer->polarity != NULL); | ||||||
|  | 
 | ||||||
|  |     irda_tim_tx.buffer[buf_num].data[0] = 0;       // 1 pulse
 | ||||||
|  |     irda_tim_tx.buffer[buf_num].polarity[0] = IRDA_TX_CCMR_LOW; | ||||||
|  |     irda_tim_tx.buffer[buf_num].data[1] = 0;       // 1 pulse
 | ||||||
|  |     irda_tim_tx.buffer[buf_num].polarity[1] = IRDA_TX_CCMR_LOW; | ||||||
|  |     irda_tim_tx.buffer[buf_num].size = 2; | ||||||
|  |     irda_tim_tx.buffer[buf_num].last_packet_end = true; | ||||||
|  |     irda_tim_tx.buffer[buf_num].packet_end = true; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static bool api_hal_irda_tx_fill_buffer(uint8_t buf_num, uint8_t polarity_shift) { | ||||||
|  |     furi_assert(buf_num < 2); | ||||||
|  |     furi_assert(api_hal_irda_state != IrdaStateAsyncRx); | ||||||
|  |     furi_assert(api_hal_irda_state < IrdaStateMAX); | ||||||
|  |     furi_assert(irda_tim_tx.data_callback); | ||||||
|  |     IrdaTxBuf* buffer = &irda_tim_tx.buffer[buf_num]; | ||||||
|  |     furi_assert(buffer->data != NULL); | ||||||
|  |     furi_assert(buffer->polarity != NULL); | ||||||
|  | 
 | ||||||
|  |     ApiHalIrdaTxGetDataState status = ApiHalIrdaTxGetDataStateOk; | ||||||
|  |     uint32_t duration = 0; | ||||||
|  |     bool level = 0; | ||||||
|  |     size_t *size = &buffer->size; | ||||||
|  |     size_t polarity_counter = 0; | ||||||
|  |     while (polarity_shift--) { | ||||||
|  |         buffer->polarity[polarity_counter++] = IRDA_TX_CCMR_LOW; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     for (*size = 0; (*size < IRDA_TIM_TX_DMA_BUFFER_SIZE) && (status == ApiHalIrdaTxGetDataStateOk); ++(*size), ++polarity_counter) { | ||||||
|  |         status = irda_tim_tx.data_callback(irda_tim_tx.data_context, &duration, &level); | ||||||
|  |         if (status == ApiHalIrdaTxGetDataStateError) { | ||||||
|  |             furi_assert(0); | ||||||
|  |             break; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         uint32_t num_of_impulses = roundf(duration / irda_tim_tx.cycle_duration); | ||||||
|  | 
 | ||||||
|  |         if ((buffer->data[*size] + num_of_impulses - 1) > 0xFFFF) { | ||||||
|  |             furi_assert(0); | ||||||
|  |             status = ApiHalIrdaTxGetDataStateError; | ||||||
|  |             break; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         buffer->polarity[polarity_counter] = level ? IRDA_TX_CCMR_HIGH : IRDA_TX_CCMR_LOW; | ||||||
|  |         buffer->data[*size] = num_of_impulses - 1; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     buffer->last_packet_end = (status == ApiHalIrdaTxGetDataStateLastDone); | ||||||
|  |     buffer->packet_end = buffer->last_packet_end || (status == ApiHalIrdaTxGetDataStateDone); | ||||||
|  | 
 | ||||||
|  |     return status != ApiHalIrdaTxGetDataStateError; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void api_hal_irda_tx_dma_set_polarity(uint8_t buf_num, uint8_t polarity_shift) { | ||||||
|  |     furi_assert(buf_num < 2); | ||||||
|  |     furi_assert(api_hal_irda_state < IrdaStateMAX); | ||||||
|  |     IrdaTxBuf* buffer = &irda_tim_tx.buffer[buf_num]; | ||||||
|  |     furi_assert(buffer->polarity != NULL); | ||||||
|  | 
 | ||||||
|  |     __disable_irq(); | ||||||
|  |     bool channel_enabled = LL_DMA_IsEnabledChannel(DMA1, LL_DMA_CHANNEL_1); | ||||||
|  |     if (channel_enabled) { | ||||||
|  |         LL_DMA_DisableChannel(DMA1, LL_DMA_CHANNEL_1); | ||||||
|  |     } | ||||||
|  |     LL_DMA_SetMemoryAddress(DMA1, LL_DMA_CHANNEL_1, (uint32_t) buffer->polarity); | ||||||
|  |     LL_DMA_SetDataLength(DMA1, LL_DMA_CHANNEL_1, buffer->size + polarity_shift); | ||||||
|  |     if (channel_enabled) { | ||||||
|  |         LL_DMA_EnableChannel(DMA1, LL_DMA_CHANNEL_1); | ||||||
|  |     } | ||||||
|  |     __enable_irq(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void api_hal_irda_tx_dma_set_buffer(uint8_t buf_num) { | ||||||
|  |     furi_assert(buf_num < 2); | ||||||
|  |     furi_assert(api_hal_irda_state < IrdaStateMAX); | ||||||
|  |     IrdaTxBuf* buffer = &irda_tim_tx.buffer[buf_num]; | ||||||
|  |     furi_assert(buffer->data != NULL); | ||||||
|  | 
 | ||||||
|  |     /* non-circular mode requires disabled channel before setup */ | ||||||
|  |     __disable_irq(); | ||||||
|  |     bool channel_enabled = LL_DMA_IsEnabledChannel(DMA1, LL_DMA_CHANNEL_2); | ||||||
|  |     if (channel_enabled) { | ||||||
|  |         LL_DMA_DisableChannel(DMA1, LL_DMA_CHANNEL_2); | ||||||
|  |     } | ||||||
|  |     LL_DMA_SetMemoryAddress(DMA1, LL_DMA_CHANNEL_2, (uint32_t)buffer->data); | ||||||
|  |     LL_DMA_SetDataLength(DMA1, LL_DMA_CHANNEL_2, buffer->size); | ||||||
|  |     if (channel_enabled) { | ||||||
|  |         LL_DMA_EnableChannel(DMA1, LL_DMA_CHANNEL_2); | ||||||
|  |     } | ||||||
|  |     __enable_irq(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void api_hal_irda_async_tx_free_resources(void) { | ||||||
|  |     furi_assert((api_hal_irda_state == IrdaStateIdle) || (api_hal_irda_state == IrdaStateAsyncTxStopped)); | ||||||
|  |     osStatus_t status; | ||||||
|  | 
 | ||||||
|  |     hal_gpio_init_ex(&gpio_irda_tx, GpioModeOutputOpenDrain, GpioPullDown, GpioSpeedLow, 0); | ||||||
|  |     api_hal_interrupt_set_dma_channel_isr(DMA1, LL_DMA_CHANNEL_1, NULL); | ||||||
|  |     api_hal_interrupt_set_dma_channel_isr(DMA1, LL_DMA_CHANNEL_2, NULL); | ||||||
|  |     LL_TIM_DeInit(TIM1); | ||||||
|  |     LL_APB2_GRP1_DisableClock(LL_APB2_GRP1_PERIPH_TIM1); | ||||||
|  |     LL_C2_AHB1_GRP1_DisableClock(LL_C2_AHB1_GRP1_PERIPH_DMA1); | ||||||
|  | 
 | ||||||
|  |     status = osSemaphoreDelete(irda_tim_tx.stop_semaphore); | ||||||
|  |     furi_check(status == osOK); | ||||||
|  |     free(irda_tim_tx.buffer[0].data); | ||||||
|  |     free(irda_tim_tx.buffer[1].data); | ||||||
|  |     free(irda_tim_tx.buffer[0].polarity); | ||||||
|  |     free(irda_tim_tx.buffer[1].polarity); | ||||||
|  | 
 | ||||||
|  |     irda_tim_tx.buffer[0].data = NULL; | ||||||
|  |     irda_tim_tx.buffer[1].data = NULL; | ||||||
|  |     irda_tim_tx.buffer[0].polarity = NULL; | ||||||
|  |     irda_tim_tx.buffer[1].polarity = NULL; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool api_hal_irda_async_tx_start(uint32_t freq, float duty_cycle) { | ||||||
|  |     if ((duty_cycle > 1) || (duty_cycle < 0) || (freq > 40000) || (freq < 10000) || (irda_tim_tx.data_callback == NULL)) { | ||||||
|  |         furi_assert(0); | ||||||
|  |         return false; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     furi_assert(api_hal_irda_state == IrdaStateIdle); | ||||||
|  |     furi_assert(irda_tim_tx.buffer[0].data == NULL); | ||||||
|  |     furi_assert(irda_tim_tx.buffer[1].data == NULL); | ||||||
|  |     furi_assert(irda_tim_tx.buffer[0].polarity == NULL); | ||||||
|  |     furi_assert(irda_tim_tx.buffer[1].polarity == NULL); | ||||||
|  | 
 | ||||||
|  |     size_t alloc_size_data = IRDA_TIM_TX_DMA_BUFFER_SIZE * sizeof(uint16_t); | ||||||
|  |     irda_tim_tx.buffer[0].data = furi_alloc(alloc_size_data); | ||||||
|  |     irda_tim_tx.buffer[1].data = furi_alloc(alloc_size_data); | ||||||
|  | 
 | ||||||
|  |     size_t alloc_size_polarity = (IRDA_TIM_TX_DMA_BUFFER_SIZE + IRDA_POLARITY_SHIFT) * sizeof(uint8_t); | ||||||
|  |     irda_tim_tx.buffer[0].polarity = furi_alloc(alloc_size_polarity); | ||||||
|  |     irda_tim_tx.buffer[1].polarity = furi_alloc(alloc_size_polarity); | ||||||
|  | 
 | ||||||
|  |     irda_tim_tx.stop_semaphore = osSemaphoreNew(1, 0, NULL); | ||||||
|  |     irda_tim_tx.cycle_duration = 1000000.0 / freq; | ||||||
|  | 
 | ||||||
|  |     bool result = api_hal_irda_tx_fill_buffer(0, IRDA_POLARITY_SHIFT); | ||||||
|  | 
 | ||||||
|  |     if (result) { | ||||||
|  |         api_hal_irda_configure_tim_pwm_tx(freq, duty_cycle); | ||||||
|  |         api_hal_irda_configure_tim_cmgr2_dma_tx(); | ||||||
|  |         api_hal_irda_configure_tim_rcr_dma_tx(); | ||||||
|  |         api_hal_irda_tx_dma_set_polarity(0, IRDA_POLARITY_SHIFT); | ||||||
|  |         api_hal_irda_tx_dma_set_buffer(0); | ||||||
|  | 
 | ||||||
|  |         api_hal_irda_state = IrdaStateAsyncTx; | ||||||
|  | 
 | ||||||
|  |         LL_TIM_ClearFlag_UPDATE(TIM1); | ||||||
|  |         LL_DMA_EnableChannel(DMA1, LL_DMA_CHANNEL_1); | ||||||
|  |         LL_DMA_EnableChannel(DMA1, LL_DMA_CHANNEL_2); | ||||||
|  |         delay_us(5); | ||||||
|  |         LL_TIM_GenerateEvent_UPDATE(TIM1);  /* DMA -> TIMx_RCR */ | ||||||
|  |         delay_us(5); | ||||||
|  |         LL_GPIO_ResetOutputPin(gpio_irda_tx.port, gpio_irda_tx.pin);    /* when disable it prevents false pulse */ | ||||||
|  |         hal_gpio_init_ex(&gpio_irda_tx, GpioModeAltFunctionPushPull, GpioPullUp, GpioSpeedHigh, GpioAltFn1TIM1); | ||||||
|  | 
 | ||||||
|  |         __disable_irq(); | ||||||
|  |         LL_TIM_GenerateEvent_UPDATE(TIM1);  /* TIMx_RCR -> Repetition counter */ | ||||||
|  |         LL_TIM_EnableCounter(TIM1); | ||||||
|  |         __enable_irq(); | ||||||
|  | 
 | ||||||
|  |     } else { | ||||||
|  |         api_hal_irda_async_tx_free_resources(); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     return result; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void api_hal_irda_async_tx_wait_termination(void) { | ||||||
|  |     furi_assert(api_hal_irda_state >= IrdaStateAsyncTx); | ||||||
|  |     furi_assert(api_hal_irda_state < IrdaStateMAX); | ||||||
|  | 
 | ||||||
|  |     osStatus_t status; | ||||||
|  |     status = osSemaphoreAcquire(irda_tim_tx.stop_semaphore, osWaitForever); | ||||||
|  |     furi_check(status == osOK); | ||||||
|  |     api_hal_irda_async_tx_free_resources(); | ||||||
|  |     api_hal_irda_state = IrdaStateIdle; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void api_hal_irda_async_tx_stop(void) { | ||||||
|  |     furi_assert(api_hal_irda_state >= IrdaStateAsyncTx); | ||||||
|  |     furi_assert(api_hal_irda_state < IrdaStateMAX); | ||||||
|  | 
 | ||||||
|  |     __disable_irq(); | ||||||
|  |     if (api_hal_irda_state == IrdaStateAsyncTx) | ||||||
|  |         api_hal_irda_state = IrdaStateAsyncTxStopReq; | ||||||
|  |     __enable_irq(); | ||||||
|  | 
 | ||||||
|  |     api_hal_irda_async_tx_wait_termination(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void api_hal_irda_async_tx_set_data_isr_callback(ApiHalIrdaTxGetDataCallback callback, void* context) { | ||||||
|  |     furi_assert(api_hal_irda_state == IrdaStateIdle); | ||||||
|  |     irda_tim_tx.data_callback = callback; | ||||||
|  |     irda_tim_tx.data_context = context; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | |||||||
| @ -46,7 +46,7 @@ IrdaDecoderHandler* irda_alloc_decoder(void); | |||||||
| /**
 | /**
 | ||||||
|  * Provide to decoder next timing. |  * Provide to decoder next timing. | ||||||
|  * |  * | ||||||
|  * \param[in]   handler     - handler to IRDA decoders. Should be aquired with \c irda_alloc_decoder(). |  * \param[in]   handler     - handler to IRDA decoders. Should be acquired with \c irda_alloc_decoder(). | ||||||
|  * \param[in]   level       - high(true) or low(false) level of input signal to analyze. |  * \param[in]   level       - high(true) or low(false) level of input signal to analyze. | ||||||
|  *                          it should alternate every call, otherwise it is an error case, |  *                          it should alternate every call, otherwise it is an error case, | ||||||
|  *                          and decoder resets its state and start decoding from the start. |  *                          and decoder resets its state and start decoding from the start. | ||||||
| @ -58,14 +58,14 @@ const IrdaMessage* irda_decode(IrdaDecoderHandler* handler, bool level, uint32_t | |||||||
| /**
 | /**
 | ||||||
|  * Deinitialize decoder and free allocated memory. |  * Deinitialize decoder and free allocated memory. | ||||||
|  * |  * | ||||||
|  * \param[in]   handler     - handler to IRDA decoders. Should be aquired with \c irda_alloc_decoder(). |  * \param[in]   handler     - handler to IRDA decoders. Should be acquired with \c irda_alloc_decoder(). | ||||||
|  */ |  */ | ||||||
| void irda_free_decoder(IrdaDecoderHandler* handler); | void irda_free_decoder(IrdaDecoderHandler* handler); | ||||||
| 
 | 
 | ||||||
| /**
 | /**
 | ||||||
|  * Reset IRDA decoder. |  * Reset IRDA decoder. | ||||||
|  * |  * | ||||||
|  * \param[in]   handler     - handler to IRDA decoders. Should be aquired with \c irda_alloc_decoder(). |  * \param[in]   handler     - handler to IRDA decoders. Should be acquired with \c irda_alloc_decoder(). | ||||||
|  */ |  */ | ||||||
| void irda_reset_decoder(IrdaDecoderHandler* handler); | void irda_reset_decoder(IrdaDecoderHandler* handler); | ||||||
| 
 | 
 | ||||||
| @ -119,7 +119,7 @@ IrdaEncoderHandler* irda_alloc_encoder(void); | |||||||
| /**
 | /**
 | ||||||
|  * Free encoder handler previously allocated with \c irda_alloc_encoder(). |  * Free encoder handler previously allocated with \c irda_alloc_encoder(). | ||||||
|  * |  * | ||||||
|  * \param[in]   handler     - handler to IRDA encoder. Should be aquired with \c irda_alloc_encoder(). |  * \param[in]   handler     - handler to IRDA encoder. Should be acquired with \c irda_alloc_encoder(). | ||||||
|  */ |  */ | ||||||
| void irda_free_encoder(IrdaEncoderHandler* handler); | void irda_free_encoder(IrdaEncoderHandler* handler); | ||||||
| 
 | 
 | ||||||
| @ -132,7 +132,7 @@ void irda_free_encoder(IrdaEncoderHandler* handler); | |||||||
|  *  4) when \c irda_encode() returns IrdaStatusDone, it means new message is fully encoded. |  *  4) when \c irda_encode() returns IrdaStatusDone, it means new message is fully encoded. | ||||||
|  *  5) to encode additional timings, just continue calling \c irda_encode(). |  *  5) to encode additional timings, just continue calling \c irda_encode(). | ||||||
|  * |  * | ||||||
|  * \param[in]   handler     - handler to IRDA encoder. Should be aquired with \c irda_alloc_encoder(). |  * \param[in]   handler     - handler to IRDA encoder. Should be acquired with \c irda_alloc_encoder(). | ||||||
|  * \param[out]  duration    - encoded timing. |  * \param[out]  duration    - encoded timing. | ||||||
|  * \param[out]  level       - encoded level. |  * \param[out]  level       - encoded level. | ||||||
|  * |  * | ||||||
| @ -145,7 +145,7 @@ IrdaStatus irda_encode(IrdaEncoderHandler* handler, uint32_t* duration, bool* le | |||||||
|  * IrdaStatusDone in \c irda_encode(), encoder will encode repeat messages |  * IrdaStatusDone in \c irda_encode(), encoder will encode repeat messages | ||||||
|  * till the end of time. |  * till the end of time. | ||||||
|  * |  * | ||||||
|  * \param[in]   handler     - handler to IRDA encoder. Should be aquired with \c irda_alloc_encoder(). |  * \param[in]   handler     - handler to IRDA encoder. Should be acquired with \c irda_alloc_encoder(). | ||||||
|  * \param[in]   message     - message to encode. |  * \param[in]   message     - message to encode. | ||||||
|  */ |  */ | ||||||
| void irda_reset_encoder(IrdaEncoderHandler* handler, const IrdaMessage* message); | void irda_reset_encoder(IrdaEncoderHandler* handler, const IrdaMessage* message); | ||||||
|  | |||||||
| @ -6,72 +6,96 @@ | |||||||
| #include <api-hal-irda.h> | #include <api-hal-irda.h> | ||||||
| #include <api-hal-delay.h> | #include <api-hal-delay.h> | ||||||
| 
 | 
 | ||||||
| #define IRDA_SET_TX_COMMON(d, l)        irda_set_tx((d), (l), IRDA_COMMON_DUTY_CYCLE, IRDA_COMMON_CARRIER_FREQUENCY) | static uint32_t irda_tx_number_of_transmissions = 0; | ||||||
|  | static uint32_t irda_tx_raw_timings_index = 0; | ||||||
|  | static uint32_t irda_tx_raw_timings_number = 0; | ||||||
|  | static uint32_t irda_tx_raw_start_from_mark = 0; | ||||||
|  | static bool irda_tx_raw_add_silence = false; | ||||||
| 
 | 
 | ||||||
| static void irda_set_tx(uint32_t duration, bool level, float duty_cycle, float frequency) { | ApiHalIrdaTxGetDataState irda_get_raw_data_callback (void* context, uint32_t* duration, bool* level) { | ||||||
|     if (level) { |     furi_assert(duration); | ||||||
|         api_hal_irda_pwm_set(duty_cycle, frequency); |     furi_assert(level); | ||||||
|         delay_us(duration); |     furi_assert(context); | ||||||
|  | 
 | ||||||
|  |     ApiHalIrdaTxGetDataState state = ApiHalIrdaTxGetDataStateOk; | ||||||
|  |     const uint32_t* timings = context; | ||||||
|  | 
 | ||||||
|  |     if (irda_tx_raw_add_silence && (irda_tx_raw_timings_index == 0)) { | ||||||
|  |         irda_tx_raw_add_silence = false; | ||||||
|  |         *level = false; | ||||||
|  |         *duration = 180000;     // 180 ms delay between raw packets
 | ||||||
|     } else { |     } else { | ||||||
|         api_hal_irda_pwm_stop(); |         *level = irda_tx_raw_start_from_mark ^ (irda_tx_raw_timings_index % 2); | ||||||
|         delay_us(duration); |         *duration = timings[irda_tx_raw_timings_index++]; | ||||||
|     } |     } | ||||||
|  | 
 | ||||||
|  |     if (irda_tx_raw_timings_number == irda_tx_raw_timings_index) { | ||||||
|  |         state = ApiHalIrdaTxGetDataStateLastDone; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     return state; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void irda_send_raw_ext(const uint32_t timings[], uint32_t timings_cnt, bool start_from_mark, float duty_cycle, float frequency) { | void irda_send_raw_ext(const uint32_t timings[], uint32_t timings_cnt, bool start_from_mark, uint32_t frequency, float duty_cycle) { | ||||||
|     __disable_irq(); |     furi_assert(timings); | ||||||
|     for (uint32_t i = 0; i < timings_cnt; ++i) { |     furi_assert(timings_cnt > 1); | ||||||
|         irda_set_tx(timings[i], (i % 2) ^ start_from_mark, duty_cycle, frequency); | 
 | ||||||
|     } |     irda_tx_raw_start_from_mark = start_from_mark; | ||||||
|     IRDA_SET_TX_COMMON(0, false); |     irda_tx_raw_timings_index = 0; | ||||||
|     __enable_irq(); |     irda_tx_raw_timings_number = timings_cnt; | ||||||
|  |     irda_tx_raw_add_silence = start_from_mark; | ||||||
|  |     api_hal_irda_async_tx_set_data_isr_callback(irda_get_raw_data_callback, (void*) timings); | ||||||
|  |     api_hal_irda_async_tx_start(frequency, duty_cycle); | ||||||
|  |     api_hal_irda_async_tx_wait_termination(); | ||||||
|  | 
 | ||||||
|  |     furi_assert(!api_hal_irda_is_busy()); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void irda_send_raw(const uint32_t timings[], uint32_t timings_cnt, bool start_from_mark) { | void irda_send_raw(const uint32_t timings[], uint32_t timings_cnt, bool start_from_mark) { | ||||||
|     __disable_irq(); |     irda_send_raw_ext(timings, timings_cnt, start_from_mark, IRDA_COMMON_CARRIER_FREQUENCY, IRDA_COMMON_DUTY_CYCLE); | ||||||
|     for (uint32_t i = 0; i < timings_cnt; ++i) { | } | ||||||
|         IRDA_SET_TX_COMMON(timings[i], (i % 2) ^ start_from_mark); | 
 | ||||||
|  | ApiHalIrdaTxGetDataState irda_get_data_callback (void* context, uint32_t* duration, bool* level) { | ||||||
|  |     ApiHalIrdaTxGetDataState state = ApiHalIrdaTxGetDataStateError; | ||||||
|  |     IrdaEncoderHandler* handler = context; | ||||||
|  |     IrdaStatus status = IrdaStatusError; | ||||||
|  | 
 | ||||||
|  |     if (irda_tx_number_of_transmissions > 0) { | ||||||
|  |         status = irda_encode(handler, duration, level); | ||||||
|     } |     } | ||||||
|     IRDA_SET_TX_COMMON(0, false); | 
 | ||||||
|     __enable_irq(); |     if (status == IrdaStatusError) { | ||||||
|  |         state = ApiHalIrdaTxGetDataStateError; | ||||||
|  |     } else if (status == IrdaStatusOk) { | ||||||
|  |         state = ApiHalIrdaTxGetDataStateOk; | ||||||
|  |     } else if (status == IrdaStatusDone) { | ||||||
|  |         state = ApiHalIrdaTxGetDataStateDone; | ||||||
|  |         if (--irda_tx_number_of_transmissions == 0) { | ||||||
|  |             state = ApiHalIrdaTxGetDataStateLastDone; | ||||||
|  |         } | ||||||
|  |     } else { | ||||||
|  |         furi_assert(0); | ||||||
|  |         state = ApiHalIrdaTxGetDataStateError; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     return state; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void irda_send(const IrdaMessage* message, int times) { | void irda_send(const IrdaMessage* message, int times) { | ||||||
|     furi_assert(message); |     furi_assert(message); | ||||||
|  |     furi_assert(times); | ||||||
|     furi_assert(irda_is_protocol_valid(message->protocol)); |     furi_assert(irda_is_protocol_valid(message->protocol)); | ||||||
| 
 | 
 | ||||||
|     IrdaStatus status; |  | ||||||
|     uint32_t duration = 0; |  | ||||||
|     bool level = false; |  | ||||||
|     IrdaEncoderHandler* handler = irda_alloc_encoder(); |     IrdaEncoderHandler* handler = irda_alloc_encoder(); | ||||||
|     irda_reset_encoder(handler, message); |     irda_reset_encoder(handler, message); | ||||||
|  |     irda_tx_number_of_transmissions = times; | ||||||
| 
 | 
 | ||||||
|     /* Hotfix: first timings is space timing, so make delay instead of locking
 |     api_hal_irda_async_tx_set_data_isr_callback(irda_get_data_callback, handler); | ||||||
|      * whole system for that long. Replace when async timing lib will be ready. |     api_hal_irda_async_tx_start(IRDA_COMMON_CARRIER_FREQUENCY, IRDA_COMMON_DUTY_CYCLE); | ||||||
|      * This timing doesn't have to be precise. |     api_hal_irda_async_tx_wait_termination(); | ||||||
|      */ |  | ||||||
|     status = irda_encode(handler, &duration, &level); |  | ||||||
|     furi_assert(status != IrdaStatusError); |  | ||||||
|     furi_assert(level == false); |  | ||||||
|     delay_us(duration); |  | ||||||
| 
 |  | ||||||
|     __disable_irq(); |  | ||||||
| 
 |  | ||||||
|     while (times) { |  | ||||||
|         status = irda_encode(handler, &duration, &level); |  | ||||||
|         if (status != IrdaStatusError) { |  | ||||||
|             IRDA_SET_TX_COMMON(duration, level); |  | ||||||
|         } else { |  | ||||||
|             furi_assert(0); |  | ||||||
|             break; |  | ||||||
|         } |  | ||||||
|         if (status == IrdaStatusDone) |  | ||||||
|             --times; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     IRDA_SET_TX_COMMON(0, false); |  | ||||||
|     __enable_irq(); |  | ||||||
| 
 | 
 | ||||||
|     irda_free_encoder(handler); |     irda_free_encoder(handler); | ||||||
|  | 
 | ||||||
|  |     furi_assert(!api_hal_irda_is_busy()); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -1,5 +1,6 @@ | |||||||
| #include <api-hal-irda.h> | #include <api-hal-irda.h> | ||||||
| #include <irda.h> | #include <irda.h> | ||||||
|  | #include <stdint.h> | ||||||
| 
 | 
 | ||||||
| #ifdef __cplusplus | #ifdef __cplusplus | ||||||
| extern "C" { | extern "C" { | ||||||
| @ -33,7 +34,7 @@ void irda_send_raw(const uint32_t timings[], uint32_t timings_cnt, bool start_fr | |||||||
|  * \param[in]   duty_cycle - duty cycle to generate on PWM |  * \param[in]   duty_cycle - duty cycle to generate on PWM | ||||||
|  * \param[in]   frequency - frequency to generate on PWM |  * \param[in]   frequency - frequency to generate on PWM | ||||||
|  */ |  */ | ||||||
| void irda_send_raw_ext(const uint32_t timings[], uint32_t timings_cnt, bool start_from_mark, float duty_cycle, float frequency); | void irda_send_raw_ext(const uint32_t timings[], uint32_t timings_cnt, bool start_from_mark, uint32_t frequency, float duty_cycle); | ||||||
| 
 | 
 | ||||||
| #ifdef __cplusplus | #ifdef __cplusplus | ||||||
| } | } | ||||||
|  | |||||||
| @ -190,19 +190,19 @@ void irda_worker_start(IrdaWorker* instance) { | |||||||
|     furi_thread_start(instance->thread); |     furi_thread_start(instance->thread); | ||||||
| 
 | 
 | ||||||
|     instance->worker_handle = furi_thread_get_thread_id(instance->thread); |     instance->worker_handle = furi_thread_get_thread_id(instance->thread); | ||||||
|     api_hal_irda_rx_irq_init(); |     api_hal_irda_async_rx_start(); | ||||||
|     api_hal_irda_rx_timeout_irq_init(IRDA_WORKER_RX_TIMEOUT); |     api_hal_irda_async_rx_set_timeout(IRDA_WORKER_RX_TIMEOUT); | ||||||
|     api_hal_irda_rx_irq_set_callback(irda_worker_rx_callback, instance); |     api_hal_irda_async_rx_set_capture_isr_callback(irda_worker_rx_callback, instance); | ||||||
|     api_hal_irda_rx_timeout_irq_set_callback(irda_worker_rx_timeout_callback, instance); |     api_hal_irda_async_rx_set_timeout_isr_callback(irda_worker_rx_timeout_callback, instance); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void irda_worker_stop(IrdaWorker* instance) { | void irda_worker_stop(IrdaWorker* instance) { | ||||||
|     furi_assert(instance); |     furi_assert(instance); | ||||||
|     furi_assert(instance->worker_handle); |     furi_assert(instance->worker_handle); | ||||||
| 
 | 
 | ||||||
|     api_hal_irda_rx_timeout_irq_set_callback(NULL, NULL); |     api_hal_irda_async_rx_set_timeout_isr_callback(NULL, NULL); | ||||||
|     api_hal_irda_rx_irq_set_callback(NULL, NULL); |     api_hal_irda_async_rx_set_capture_isr_callback(NULL, NULL); | ||||||
|     api_hal_irda_rx_irq_deinit(); |     api_hal_irda_async_rx_stop(); | ||||||
| 
 | 
 | ||||||
|     xTaskNotify(instance->worker_handle, IRDA_WORKER_EXIT, eSetBits); |     xTaskNotify(instance->worker_handle, IRDA_WORKER_EXIT, eSetBits); | ||||||
| 
 | 
 | ||||||
|  | |||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user
	 Albert Kharisov
						Albert Kharisov