Files
ESP32_ZACUS/plip_voice/main/slic.c
T

151 lines
5.1 KiB
C

/*
* slic.c — K50835F / AG1171-class SLIC control (ESP-IDF port).
*
* Ported from hardware/projects/slic-phone/src/slic/Ks0835SlicController.cpp
* (Arduino) to bare ESP-IDF 5.x GPIO driver.
*
* Key differences from Arduino original:
* - Open-drain on PD achieved via GPIO_MODE_OUTPUT_OD + gpio_set_level(PD, 1)
* - Ring FR toggle runs as a FreeRTOS task instead of a cooperative tick()
* - hook_active_high is hard-coded TRUE (matches A252ConfigStore default)
*/
#include "slic.h"
#include "board_config.h"
#include "driver/gpio.h"
#include "esp_log.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#define TAG "slic"
/* Active-high hook polarity: HIGH on SHK = off-hook.
* Source: hardware/projects/slic-phone/src/config/A252ConfigStore.cpp:262
* cfg.hook_active_high = true; */
#define SLIC_SHK_OFFHOOK_LEVEL 1
static volatile bool s_ringing = false;
static TaskHandle_t s_ring_task = NULL;
/* ── FR toggle task (ring drive at ~25 Hz) ────────────────────────────────── */
static void slic_ring_task(void *arg)
{
(void)arg;
bool fr_state = false;
for (;;) {
if (s_ringing) {
fr_state = !fr_state;
gpio_set_level(PLIP_SLIC_FR, fr_state ? 1 : 0);
ESP_LOGV(TAG, "FR=%d", fr_state ? 1 : 0);
} else {
/* Suspended — keep FR low while idle */
gpio_set_level(PLIP_SLIC_FR, 0);
fr_state = false;
}
vTaskDelay(pdMS_TO_TICKS(20)); /* 20 ms → ~25 Hz */
}
}
/* ── Public API ──────────────────────────────────────────────────────────── */
esp_err_t slic_init(void)
{
/* RM: Ring Mode output, init LOW */
gpio_config_t rm_cfg = {
.pin_bit_mask = (1ULL << PLIP_SLIC_RM),
.mode = GPIO_MODE_OUTPUT,
.pull_up_en = GPIO_PULLUP_DISABLE,
.pull_down_en = GPIO_PULLDOWN_DISABLE,
.intr_type = GPIO_INTR_DISABLE,
};
esp_err_t ret = gpio_config(&rm_cfg);
if (ret != ESP_OK) return ret;
gpio_set_level(PLIP_SLIC_RM, 0);
/* FR: Forward/Reverse output, init LOW */
gpio_config_t fr_cfg = {
.pin_bit_mask = (1ULL << PLIP_SLIC_FR),
.mode = GPIO_MODE_OUTPUT,
.pull_up_en = GPIO_PULLUP_DISABLE,
.pull_down_en = GPIO_PULLDOWN_DISABLE,
.intr_type = GPIO_INTR_DISABLE,
};
ret = gpio_config(&fr_cfg);
if (ret != ESP_OK) return ret;
gpio_set_level(PLIP_SLIC_FR, 0);
/* SHK: Switch Hook input with pull-up (physical line has no external pull) */
gpio_config_t shk_cfg = {
.pin_bit_mask = (1ULL << PLIP_SLIC_SHK),
.mode = GPIO_MODE_INPUT,
.pull_up_en = GPIO_PULLUP_ENABLE,
.pull_down_en = GPIO_PULLDOWN_DISABLE,
.intr_type = GPIO_INTR_DISABLE,
};
ret = gpio_config(&shk_cfg);
if (ret != ESP_OK) return ret;
/* PD: Power Down — push-pull output HIGH = SLIC active (definite power-up).
* Previous config (open-drain HIGH) left the line floating when no pull-up
* is present on the bench, keeping the SLIC in power-down.
* Push-pull HIGH drives the PD pin firmly high regardless of external resistors. */
gpio_config_t pd_cfg = {
.pin_bit_mask = (1ULL << PLIP_SLIC_PD),
.mode = GPIO_MODE_OUTPUT, /* push-pull (pas OD) */
.pull_up_en = GPIO_PULLUP_DISABLE,
.pull_down_en = GPIO_PULLDOWN_DISABLE,
.intr_type = GPIO_INTR_DISABLE,
};
ret = gpio_config(&pd_cfg);
if (ret != ESP_OK) return ret;
ret = gpio_set_level(PLIP_SLIC_PD, 1); /* HIGH franc = SLIC actif */
if (ret != ESP_OK) return ret;
ESP_LOGI(TAG, "slic: PD GPIO%d push-pull HIGH (SLIC power-up, franc)", PLIP_SLIC_PD);
ESP_LOGI(TAG, "slic: pins RM=%d FR=%d SHK=%d PD=%d hook_active_high=1",
PLIP_SLIC_RM, PLIP_SLIC_FR, PLIP_SLIC_SHK, PLIP_SLIC_PD);
/* Spawn the FR-toggle task (runs indefinitely, toggles only when s_ringing=true) */
BaseType_t ok = xTaskCreatePinnedToCore(slic_ring_task, "slic_ring",
2048, NULL, 4, &s_ring_task, 1);
if (ok != pdPASS) {
ESP_LOGE(TAG, "Failed to create slic_ring task");
return ESP_ERR_NO_MEM;
}
int shk = gpio_get_level(PLIP_SLIC_SHK);
ESP_LOGI(TAG, "SLIC init OK — SHK level=%d (%s)",
shk, (shk == SLIC_SHK_OFFHOOK_LEVEL) ? "off-hook" : "on-hook");
return ESP_OK;
}
bool slic_is_offhook(void)
{
return gpio_get_level(PLIP_SLIC_SHK) == SLIC_SHK_OFFHOOK_LEVEL;
}
void slic_ring_start(void)
{
if (s_ringing) return;
ESP_LOGI(TAG, "ring start: RM=HIGH, FR toggling at 25 Hz");
gpio_set_level(PLIP_SLIC_RM, 1);
s_ringing = true;
}
void slic_ring_stop(void)
{
if (!s_ringing) return;
ESP_LOGI(TAG, "ring stop: RM=LOW, FR=LOW");
s_ringing = false;
gpio_set_level(PLIP_SLIC_RM, 0);
gpio_set_level(PLIP_SLIC_FR, 0);
}
bool slic_is_ringing(void)
{
return s_ringing;
}