/******************************************************************************* * @file pulse_gen.c * @brief PWM Pulse Generator (Build-safe version) * @author CandyPops Co. / ??? ???? * @version V1.0.1 * @date 2025-10-13 ******************************************************************************/ #define SKIP_PWM // ? ?? ???? PWM ?? ??? #include "pulse_gen.h" #include "debug_print.h" #include "app_timer.h" #include "nrf_log.h" #ifdef SKIP_PWM /* ========================================================================== */ /* PWM ?? ???? Stub ?? */ /* ========================================================================== */ ret_code_t pulse_gen_init(void) { DBG_PRINTF("[PWM] skipped init\r\n"); return NRF_SUCCESS; } void pulse_gen_start(void) { DBG_PRINTF("[PWM] skipped start\r\n"); } void pulse_gen_stop(void) { DBG_PRINTF("[PWM] skipped stop\r\n"); } #else /* ==================================================================== */ /* ?? PWM ?? ?? (SKIP_PWM ?? ?? ? ???) */ /* ========================================================================== */ #include "nrfx_pwm.h" /* --- Configuration --- */ #define PULSE_PIN_1 28 #define PULSE_PIN_2 29 #define PULSE_PIN_3 30 #define PULSE_DELAY_MS 10 #define PWM_INSTANCE_ID 0 #define PWM_TOP_VALUE 8 #define SEQUENCE_LENGTH 6 static nrfx_pwm_t m_pwm = NRFX_PWM_INSTANCE(PWM_INSTANCE_ID); APP_TIMER_DEF(m_pulse_delay_timer_id); static volatile bool m_pulses_running = false; static nrf_pwm_values_individual_t m_pulse_seq_values[SEQUENCE_LENGTH]; static nrf_pwm_sequence_t const m_pulse_sequence = { .values.p_individual = m_pulse_seq_values, .length = NRF_PWM_VALUES_LENGTH(m_pulse_seq_values), .repeats = 0, .end_delay = 0 }; /* -------------------------------------------------------------------------- */ /* Local Handlers */ /* -------------------------------------------------------------------------- */ static void prepare_pulse_sequence(void) { m_pulse_seq_values[0].channel_0 = PWM_TOP_VALUE; m_pulse_seq_values[0].channel_1 = 0; m_pulse_seq_values[0].channel_2 = 0; m_pulse_seq_values[1].channel_0 = 0; m_pulse_seq_values[1].channel_1 = PWM_TOP_VALUE; m_pulse_seq_values[1].channel_2 = 0; m_pulse_seq_values[2].channel_0 = PWM_TOP_VALUE; m_pulse_seq_values[2].channel_1 = 0; m_pulse_seq_values[2].channel_2 = 0; m_pulse_seq_values[3].channel_0 = 0; m_pulse_seq_values[3].channel_1 = PWM_TOP_VALUE; m_pulse_seq_values[3].channel_2 = 0; m_pulse_seq_values[4].channel_0 = 0; m_pulse_seq_values[4].channel_1 = 0; m_pulse_seq_values[4].channel_2 = 0; m_pulse_seq_values[5].channel_0 = 0; m_pulse_seq_values[5].channel_1 = 0; m_pulse_seq_values[5].channel_2 = PWM_TOP_VALUE; } static void pulse_delay_timeout_handler(void * p_context) { if (m_pulses_running) { (void)nrfx_pwm_simple_playback(&m_pwm, &m_pulse_sequence, 1, NRFX_PWM_FLAG_STOP); (void)app_timer_start(m_pulse_delay_timer_id, APP_TIMER_TICKS(PULSE_DELAY_MS), NULL); } } /* -------------------------------------------------------------------------- */ /* Public Functions */ /* -------------------------------------------------------------------------- */ ret_code_t pulse_gen_init(void) { ret_code_t err_code; prepare_pulse_sequence(); nrfx_pwm_config_t const config = { .output_pins = { PULSE_PIN_1, PULSE_PIN_2, PULSE_PIN_3, NRFX_PWM_PIN_NOT_USED }, .irq_priority = NRFX_PWM_DEFAULT_CONFIG_IRQ_PRIORITY, .base_clock = NRF_PWM_CLK_16MHz, .count_mode = NRF_PWM_MODE_UP, .top_value = PWM_TOP_VALUE, .load_mode = NRF_PWM_LOAD_INDIVIDUAL, .step_mode = NRF_PWM_STEP_AUTO }; err_code = nrfx_pwm_init(&m_pwm, &config, NULL); if (err_code != NRF_SUCCESS) return err_code; err_code = app_timer_create(&m_pulse_delay_timer_id, APP_TIMER_MODE_SINGLE_SHOT, pulse_delay_timeout_handler); return err_code; } void pulse_gen_start(void) { if (m_pulses_running) return; m_pulses_running = true; pulse_delay_timeout_handler(NULL); } void pulse_gen_stop(void) { m_pulses_running = false; app_timer_stop(m_pulse_delay_timer_id); nrfx_pwm_stop(&m_pwm, true); } #endif /* SKIP_PWM */