151 lines
5.1 KiB
C
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;
|
|
}
|