| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576 |
- #include "dac_njw1195.h"
- #include "driver/spi_master.h"
- #include "driver/gpio.h"
- #include "nvs_flash.h"
- #include "nvs.h"
- #include "esp_log.h"
- #include "freertos/FreeRTOS.h"
- #include "freertos/task.h"
- #include "freertos/semphr.h"
- #include "rom/ets_sys.h"
- #include "esp_timer.h"
- static const char TAG[] = "NJW1195AV";
- // GPIO Pin Definitions
- #define NJW_MOSI_GPIO 23
- #define NJW_CLK_GPIO 18
- #define NJW_LATCH_GPIO 33
- // NJW1195 寄存器地址 D3~D0 (ADR1=GND, ADR0=GND -> 芯片地址 0x0)
- // 16-bit 帧结构: [D15~D8 = DATA] [D7~D4 = 芯片地址 0x0] [D3~D0 = 寄存器地址]
- // ESP32 发送: Byte[0] = DATA(高字节), Byte[1] = (ChipAddr<<4)|RegAddr(低字节)
- #define NJW_REG_VOL_1A 0x0 // D3~D0=0000 主音箱左声道音量 (1A)
- #define NJW_REG_VOL_1B 0x1 // D3~D0=0001 主音箱右声道音量 (1B)
- #define NJW_REG_VOL_2A 0x2 // D3~D0=0010 低音炮左声道音量 (2A)
- #define NJW_REG_VOL_2B 0x3 // D3~D0=0011 低音炮右声道音量 (2B)
- #define NJW_REG_SEL_A 0x4 // D3~D0=0100 输入选择器 A (所有左声道)
- #define NJW_REG_SEL_B 0x5 // D3~D0=0101 输入选择器 B (所有右声道)
- // 芯片地址 D7~D4: ADR1=GND, ADR0=GND -> 0x0
- #define NJW1195_ADDR 0x0
- // 音量衰减数据 (D15~D8):
- // 0x40 = 0dB (最大安全音量)
- // 0xFF = 静音 (-95dB)
- #define NJW_VOL_MUTE 0xFF // 静音 (最大衰减)
- #define NJW_VOL_0DB 0x40 // 0dB 最大安全音量
- #define NJW_VOL_MAX_ATT 0xFF // -95dB 最大衰减值 (同静音)
- // 输入选择器数据 (D15~D8):
- // D15~D13: 1A 通道选择 (001=Input1/BT, 010=Input2/AUX)
- // D12~D10: 2A 通道选择 (001=Input1/BT, 010=Input2/AUX)
- // D9~D8: 固定为 0
- // BT(Input1): 001 001 00 -> 0x24
- // AUX(Input2): 010 010 00 -> 0x48
- // GND(Input3): 011 011 00 -> 0x6C
- #define NJW_SEL_BT 0x24 // 选通 Input 1 (蓝牙)
- #define NJW_SEL_AUX 0x48 // 选通 Input 2 (AUX)
- #define NJW_SEL_GND 0x6C // 选通 Input 3 (物理强制静音)
- // Volume range in dB for AirPlay
- #define VOLUME_MIN_DB (-30.0f)
- #define VOLUME_MAX_DB 0.0f
- static spi_device_handle_t s_spi_handle = NULL;
- static SemaphoreHandle_t s_njw_mutex = NULL;
- static uint8_t s_vol_main = 100;
- static uint8_t s_vol_bass = 100;
- static uint8_t s_prompt_vol = 50;
- static njw_source_t s_source = NJW_SOURCE_BT;
- static bool s_muted = false;
- bool s_in_panel_update = false;
- extern void panel_uart_report(uint8_t item, uint8_t value);
- static void njw1195_write_reg(uint8_t reg, uint8_t data) {
- if (s_spi_handle == NULL) {
- ESP_LOGE(TAG, "错误:SPI 未初始化");
- return;
- }
- // 16-bit 帧组包 (MSB First, 高字节先发):
- // Byte[0] = DATA (D15~D8): 音量衰减值 或 选通控制位
- // Byte[1] = (寄存器地址 D7~D4) | (芯片地址 D3~D0)
- uint8_t low_byte = (uint8_t)((reg & 0x0F) << 4) | (NJW1195_ADDR & 0x0F);
- uint8_t tx_data[2];
- tx_data[0] = data; // 高字节: DATA
- tx_data[1] = low_byte; // 低字节: 地址
- ESP_LOGI(TAG, "SPI TX: %02X %02X (DATA=%02X, REG=0x%X)", tx_data[0], tx_data[1], data, reg);
- spi_transaction_t t = {
- .flags = 0,
- .length = 16,
- .tx_buffer = tx_data,
- .rx_buffer = NULL,
- };
- // 时序: 空闲时 LATCH=HIGH
- // 拉低 LATCH -> 发送 16 bits -> 拉高 LATCH (芯片锁存数据) -> 维持高电平空闲
- gpio_set_level(NJW_LATCH_GPIO, 0); // 拉低:芯片进入接收状态
- ets_delay_us(2); // 建立时间,让芯片稳定
- esp_err_t err = spi_device_transmit(s_spi_handle, &t);
- if (err != ESP_OK) {
- ESP_LOGE(TAG, "SPI 发送失败: %s", esp_err_to_name(err));
- gpio_set_level(NJW_LATCH_GPIO, 1); // 恢复高电平空闲态
- return;
- }
- ets_delay_us(5); // 建立时间: 数据手册要求最小 4μs,此处给 5μs 余量
- gpio_set_level(NJW_LATCH_GPIO, 1); // 拉高:芯片锁存数据到寄存器
- ets_delay_us(5); // LATCH 保持脉宽 >= 1.6μs,此处给 5μs 余量
- // LATCH 保持高电平作为空闲态,等待下一次操作
- }
- static void save_settings_nvs(void) {
- nvs_handle_t nvs;
- if (nvs_open("njw", NVS_READWRITE, &nvs) == ESP_OK) {
- nvs_set_u8(nvs, "vol_main", s_vol_main);
- nvs_set_u8(nvs, "vol_bass", s_vol_bass);
- nvs_set_u8(nvs, "source", (uint8_t)s_source);
- nvs_commit(nvs);
- nvs_close(nvs);
- }
- }
- static esp_timer_handle_t s_nvs_timer = NULL;
- static void nvs_timer_callback(void *arg) {
- ESP_LOGI(TAG, "音量已保存到 NVS");
- save_settings_nvs();
- }
- static void trigger_save_settings_deferred(void) {
- if (s_nvs_timer == NULL) {
- esp_timer_create_args_t timer_args = {
- .callback = nvs_timer_callback,
- .arg = NULL,
- .name = "njw_nvs_save"
- };
- esp_timer_create(&timer_args, &s_nvs_timer);
- }
- if (s_nvs_timer != NULL) {
- esp_timer_stop(s_nvs_timer);
- // 延迟 3 秒保存 (3,000,000 微秒),以降低 Flash 磨损
- esp_timer_start_once(s_nvs_timer, 3000000);
- }
- }
- static void load_settings_nvs(void) {
- nvs_handle_t nvs;
- if (nvs_open("njw", NVS_READONLY, &nvs) == ESP_OK) {
- nvs_get_u8(nvs, "vol_main", &s_vol_main);
- nvs_get_u8(nvs, "vol_bass", &s_vol_bass);
- nvs_get_u8(nvs, "prompt_vol", &s_prompt_vol);
- uint8_t src_val = 0;
- if (nvs_get_u8(nvs, "source", &src_val) == ESP_OK) {
- s_source = (njw_source_t)src_val;
- }
- nvs_close(nvs);
- }
- ESP_LOGI(TAG, "从 NVS 加载设置成功 - 主音量: %d%%, 低音音量: %d%%, 输入源: %s",
- s_vol_main, s_vol_bass, (s_source == NJW_SOURCE_BT) ? "蓝牙" : "AUX");
- }
- // 将 0~100% 百分比转换为 NJW1195 音量衰减代码 (D15~D8)
- // 数据手册: 0x00 = +31.5dB (危险!), 0x40 = 0dB (最大安全值), 0xFF = 静音(-95dB)
- // 衰减值越大(数字越大),音量越小!方向与直觉相反!
- // 0% -> 0xFF (静音)
- // 100% -> 0x40 (0dB, 最大安全音量)
- static uint8_t pct_to_code(uint8_t pct) {
- if (pct == 0) {
- return NJW_VOL_MUTE; // 0xFF 静音
- }
- if (pct > 100) {
- pct = 100;
- }
- // 使用更柔和的符合人耳听觉的二次方曲线 (Audio Taper)
- // 最大衰减限制为 120 级 (约 -60dB)。
- // 使得 50% 音量对应约 -15dB,解决前 50% 太小、后 50% 太大的问题。
- uint32_t x = 100 - pct;
- uint32_t offset = (120 * x * x) / 10000;
- uint8_t code = 0x40 + (uint8_t)offset;
- return code;
- }
- // 主声道重映射 0~100% 到实际的物理输出比例
- static uint8_t pct_to_code_main(uint8_t pct) {
- if (pct == 0) return NJW_VOL_MUTE;
- if (pct > 100) pct = 100;
-
- // 限制最大音量,例如 (pct * 50) / 100;
- uint8_t physical_pct = (pct * 68) / 100;
- if (physical_pct == 0) physical_pct = 1; // 避免计算为0变静音
-
- return pct_to_code(physical_pct);
- }
- // 低音重映射 0~100% 到实际的物理输出比例
- static uint8_t pct_to_code_bass(uint8_t pct) {
- if (pct == 0) return NJW_VOL_MUTE;
- if (pct > 100) pct = 100;
- uint8_t physical_pct = (pct * 38) / 100;
- if (physical_pct == 0) physical_pct = 1; // 避免计算为0变静音
- return pct_to_code(physical_pct);
- }
- // ==========================================
- // 音量消抖 (Debounce) 定时器机制
- // ==========================================
- static esp_timer_handle_t s_vol_debounce_timer = NULL;
- static uint8_t s_target_vol_main = 0;
- static bool s_vol_update_pending = false;
- static bool s_target_vol_from_panel = false;
- static void vol_debounce_timer_cb(void *arg) {
- if (!s_vol_update_pending) return;
- s_vol_update_pending = false;
-
- uint8_t pct = s_target_vol_main;
- bool from_panel = s_target_vol_from_panel;
-
- if (s_vol_main == pct) {
- return; // 防抖阶段已经被其他机制覆盖,无变化则跳过
- }
-
- if (s_njw_mutex != NULL) {
- xSemaphoreTake(s_njw_mutex, portMAX_DELAY);
- }
- s_vol_main = pct;
- if (!s_muted) {
- uint8_t code = pct_to_code_main(s_vol_main);
- njw1195_write_reg(NJW_REG_VOL_2A, code);
- ets_delay_us(500); // 连续写入保护延时
- njw1195_write_reg(NJW_REG_VOL_2B, code);
- ESP_LOGI(TAG, "Vol: %d%% (Mapped to %d%%, Reg: %02X)", s_vol_main, (s_vol_main * 100) / 100, code);
- }
- trigger_save_settings_deferred();
- if (!from_panel) {
- panel_uart_report(0x0C, s_vol_main);
- }
- if (s_njw_mutex != NULL) {
- xSemaphoreGive(s_njw_mutex);
- }
- }
- void dac_njw1195_set_vol_main(uint8_t pct) {
- if (s_vol_main == pct && !s_vol_update_pending) {
- return; // Prevent flooding from rapid identical volume commands
- }
-
- if (s_vol_debounce_timer == NULL) {
- esp_timer_create_args_t args = {
- .callback = vol_debounce_timer_cb,
- .arg = NULL,
- .name = "vol_deb"
- };
- esp_timer_create(&args, &s_vol_debounce_timer);
- }
-
- s_target_vol_main = pct;
- s_vol_update_pending = true;
- s_target_vol_from_panel = s_in_panel_update;
-
- // 延迟 40ms 执行,如果在 40ms 内有新指令则重置计时器 (过滤快速滑动)
- esp_timer_stop(s_vol_debounce_timer);
- esp_timer_start_once(s_vol_debounce_timer, 40000);
- }
- void dac_njw1195_set_vol_bass(uint8_t pct) {
- if (s_njw_mutex == NULL) {
- s_njw_mutex = xSemaphoreCreateMutex();
- }
- if (s_njw_mutex != NULL) {
- xSemaphoreTake(s_njw_mutex, portMAX_DELAY);
- }
- s_vol_bass = pct;
- if (!s_muted) {
- uint8_t code = pct_to_code_bass(s_vol_bass);
- njw1195_write_reg(NJW_REG_VOL_1A, code);
- ets_delay_us(500); // 连续写入保护延时
- njw1195_write_reg(NJW_REG_VOL_1B, code);
- ESP_LOGI(TAG, "Bass: %d%% (Mapped to %d%%, Reg: %02X)", s_vol_bass, (s_vol_bass * 90) / 100, code);
- }
- trigger_save_settings_deferred();
-
- // 通知面板更新低音音量 (0x0D)
- if (!s_in_panel_update) {
- panel_uart_report(0x0D, s_vol_bass);
- }
-
- if (s_njw_mutex != NULL) {
- xSemaphoreGive(s_njw_mutex);
- }
- }
- void dac_njw1195_set_prompt_volume(uint8_t pct) {
- if (pct > 100) pct = 100;
- if (s_prompt_vol == pct) return;
- s_prompt_vol = pct;
- // Save directly to NVS
- nvs_handle_t nvs;
- if (nvs_open("njw", NVS_READWRITE, &nvs) == ESP_OK) {
- nvs_set_u8(nvs, "prompt_vol", s_prompt_vol);
- nvs_commit(nvs);
- nvs_close(nvs);
- }
- ESP_LOGI(TAG, "Prompt Volume set to: %d%%", s_prompt_vol);
- }
- uint8_t dac_njw1195_get_prompt_volume(void) {
- return s_prompt_vol;
- }
- void dac_njw1195_set_prompt_mode(bool active) {
- if (s_njw_mutex != NULL) {
- xSemaphoreTake(s_njw_mutex, portMAX_DELAY);
- }
- if (!s_muted) {
- if (active && s_source == NJW_SOURCE_AUX) {
- // Force source to BT (I2S) for prompt tone
- njw1195_write_reg(NJW_REG_SEL_A, NJW_SEL_BT);
- ets_delay_us(500);
- njw1195_write_reg(NJW_REG_SEL_B, NJW_SEL_BT);
- }
- uint8_t code = pct_to_code_main(active ? s_prompt_vol : s_vol_main);
- njw1195_write_reg(NJW_REG_VOL_2A, code);
- ets_delay_us(500);
- njw1195_write_reg(NJW_REG_VOL_2B, code);
- if (!active && s_source == NJW_SOURCE_AUX) {
- // Restore source to AUX after prompt tone
- njw1195_write_reg(NJW_REG_SEL_A, NJW_SEL_AUX);
- ets_delay_us(500);
- njw1195_write_reg(NJW_REG_SEL_B, NJW_SEL_AUX);
- }
- ESP_LOGI(TAG, "Prompt Mode %s, Vol: %d%% (Reg: %02X)", active ? "ON" : "OFF", active ? s_prompt_vol : s_vol_main, code);
- }
- if (s_njw_mutex != NULL) {
- xSemaphoreGive(s_njw_mutex);
- }
- }
- void dac_njw1195_set_source(njw_source_t src) {
- if (s_njw_mutex == NULL) {
- s_njw_mutex = xSemaphoreCreateMutex();
- }
- if (s_njw_mutex != NULL) {
- xSemaphoreTake(s_njw_mutex, portMAX_DELAY);
- }
- s_source = src;
- uint8_t code = (s_source == NJW_SOURCE_BT) ? NJW_SEL_BT : NJW_SEL_AUX;
- njw1195_write_reg(NJW_REG_SEL_A, code);
- ets_delay_us(500); // 连续写入保护延时
- njw1195_write_reg(NJW_REG_SEL_B, code);
- ESP_LOGI(TAG, "设置输入源: %s (寄存器代码: %02X)",
- (s_source == NJW_SOURCE_BT) ? "蓝牙" : "AUX", code);
- trigger_save_settings_deferred();
-
- // 通知面板更新输入源 (ITEM_SOURCE = 0x01, PROTO_VAL_ON = 0x01/BT, PROTO_VAL_OFF = 0x00/AUX)
- if (!s_in_panel_update) {
- panel_uart_report(0x01, (s_source == NJW_SOURCE_BT) ? 0x01 : 0x00);
- }
-
- if (s_njw_mutex != NULL) {
- xSemaphoreGive(s_njw_mutex);
- }
- }
- void dac_njw1195_set_mute(bool mute) {
- if (s_njw_mutex == NULL) {
- s_njw_mutex = xSemaphoreCreateMutex();
- }
- if (s_njw_mutex != NULL) {
- xSemaphoreTake(s_njw_mutex, portMAX_DELAY);
- }
- s_muted = mute;
- if (s_muted) {
- njw1195_write_reg(NJW_REG_VOL_1A, NJW_VOL_MUTE);
- ets_delay_us(500);
- njw1195_write_reg(NJW_REG_VOL_1B, NJW_VOL_MUTE);
- ets_delay_us(500);
- njw1195_write_reg(NJW_REG_VOL_2A, NJW_VOL_MUTE);
- ets_delay_us(500);
- njw1195_write_reg(NJW_REG_VOL_2B, NJW_VOL_MUTE);
- ESP_LOGI(TAG, "NJW1195AV 静音");
- } else {
- uint8_t main_code = pct_to_code_main(s_vol_main);
- uint8_t bass_code = pct_to_code_bass(s_vol_bass);
- // 主音量现已调换到通道 2A/2B
- njw1195_write_reg(NJW_REG_VOL_2A, main_code);
- ets_delay_us(500);
- njw1195_write_reg(NJW_REG_VOL_2B, main_code);
- ets_delay_us(500);
- // 低音音量现已调换到通道 1A/1B
- njw1195_write_reg(NJW_REG_VOL_1A, bass_code);
- ets_delay_us(500);
- njw1195_write_reg(NJW_REG_VOL_1B, bass_code);
- ESP_LOGI(TAG, "NJW Init (Vol: %d%%, Bass: %d%%)", s_vol_main, s_vol_bass);
- }
- if (s_njw_mutex != NULL) {
- xSemaphoreGive(s_njw_mutex);
- }
- }
- bool dac_njw1195_get_mute(void) {
- return s_muted;
- }
- uint8_t dac_njw1195_get_vol_main(void) {
- return s_vol_main;
- }
- uint8_t dac_njw1195_get_vol_bass(void) {
- return s_vol_bass;
- }
- njw_source_t dac_njw1195_get_source(void) {
- return s_source;
- }
- // ============================================================================
- // dac_ops_t Implementations
- // ============================================================================
- static esp_err_t njw1195_init(void *i2c_bus) {
- (void)i2c_bus; // NJW1195 uses SPI, not I2C
- if (s_njw_mutex == NULL) {
- s_njw_mutex = xSemaphoreCreateMutex();
- if (s_njw_mutex == NULL) {
- ESP_LOGE(TAG, "Failed to create mutex");
- return ESP_ERR_NO_MEM;
- }
- }
- // Configure LATCH Pin as GPIO Output
- gpio_config_t latch_conf = {
- .pin_bit_mask = (1ULL << NJW_LATCH_GPIO),
- .mode = GPIO_MODE_OUTPUT,
- .pull_up_en = GPIO_PULLUP_DISABLE,
- .pull_down_en = GPIO_PULLDOWN_DISABLE,
- .intr_type = GPIO_INTR_DISABLE,
- };
- ESP_ERROR_CHECK(gpio_config(&latch_conf));
- gpio_set_level(NJW_LATCH_GPIO, 1); // LATCH 空闲态为高电平
- // Initialize SPI Bus
- spi_bus_config_t buscfg = {
- .mosi_io_num = NJW_MOSI_GPIO,
- .miso_io_num = -1,
- .sclk_io_num = NJW_CLK_GPIO,
- .quadwp_io_num = -1,
- .quadhd_io_num = -1,
- .max_transfer_sz = 32,
- };
- // SPI2_HOST is commonly used
- esp_err_t err = spi_bus_initialize(SPI2_HOST, &buscfg, SPI_DMA_CH_AUTO);
- if (err != ESP_OK && err != ESP_ERR_INVALID_STATE) {
- ESP_LOGE(TAG, "Failed to initialize SPI bus: %s", esp_err_to_name(err));
- return err;
- }
- // Add device to SPI Bus
- // NJW1195A 时序: CLOCK 空闲高电平 (CPOL=1),数据在下降沿被采样 (CPHA=0)
- // 对应 SPI Mode 2
- spi_device_interface_config_t devcfg = {
- .clock_speed_hz = 200000, // 200kHz 时钟速度(保守,确保稳定)
- .mode = 2, // SPI Mode 2: CPOL=1, CPHA=0
- .spics_io_num = -1, // 软件控制 LATCH(不使用硬件 CS)
- .queue_size = 1,
- };
- err = spi_bus_add_device(SPI2_HOST, &devcfg, &s_spi_handle);
- if (err != ESP_OK) {
- ESP_LOGE(TAG, "Failed to add SPI device: %s", esp_err_to_name(err));
- return err;
- }
- // Load settings from NVS
- load_settings_nvs();
- // Initial sequence: Mute first, then restore values
- dac_njw1195_set_mute(true);
-
- // Apply source selection
- uint8_t src_code = (s_source == NJW_SOURCE_BT) ? NJW_SEL_BT : NJW_SEL_AUX;
- njw1195_write_reg(NJW_REG_SEL_A, src_code);
- njw1195_write_reg(NJW_REG_SEL_B, src_code);
- // Unmute with saved volume levels
- dac_njw1195_set_mute(false);
- ESP_LOGI(TAG, "NJW1195AV 初始化成功");
- // ================= 强制测试代码 =================
- /*
- ESP_LOGW(TAG, "执行强制暴力写入测试 (所有通道 0dB, Input 1)");
- // 主音箱 (1A, 1B)
- njw1195_write_reg(0x00, 0x40); // 1A 设为 0dB (0x40)
- ets_delay_us(500);
- njw1195_write_reg(0x01, 0x40); // 1B 设为 0dB (0x40)
- ets_delay_us(500);
- // 低音炮 (2A, 2B)
- njw1195_write_reg(0x02, 0x40); // 2A 设为 0dB (0x40)
- ets_delay_us(500);
- njw1195_write_reg(0x03, 0x40); // 2B 设为 0dB (0x40)
- ets_delay_us(500);
- // 强制选通蓝牙 (Input 1) -> 高中低位合并为 0x24
- njw1195_write_reg(0x04, 0x24); // 强制选通左声道 Input 1
- ets_delay_us(500);
- njw1195_write_reg(0x05, 0x24); // 强制选通右声道 Input 1
- */
- // ===============================================
- return ESP_OK;
- }
- static esp_err_t njw1195_deinit(void) {
- if (s_spi_handle) {
- spi_bus_remove_device(s_spi_handle);
- s_spi_handle = NULL;
- }
- if (s_njw_mutex != NULL) {
- vSemaphoreDelete(s_njw_mutex);
- s_njw_mutex = NULL;
- }
- return ESP_OK;
- }
- static void njw1195_set_volume(float volume_db) {
- // AirPlay volume is in dB scale (-30.0f to 0.0f)
- if (volume_db > VOLUME_MAX_DB) {
- volume_db = VOLUME_MAX_DB;
- }
- if (volume_db < VOLUME_MIN_DB) {
- volume_db = VOLUME_MIN_DB;
- }
- // Map -30..0 dB to 0..100%
- float pct_f = ((volume_db - VOLUME_MIN_DB) / (VOLUME_MAX_DB - VOLUME_MIN_DB)) * 100.0f;
- uint8_t pct = (uint8_t)(pct_f + 0.5f);
- ESP_LOGI(TAG, "AirPlay set volume DB: %.1f -> Pct: %d%%", volume_db, pct);
- dac_njw1195_set_vol_main(pct);
- }
- static void njw1195_set_power_mode(dac_power_mode_t mode) {
- switch (mode) {
- case DAC_POWER_ON:
- dac_njw1195_set_mute(false);
- break;
- case DAC_POWER_STANDBY:
- case DAC_POWER_OFF:
- dac_njw1195_set_mute(true);
- break;
- default:
- break;
- }
- }
- static void njw1195_on_i2s_started(void) {
- // No clock reconfiguration required for NJW1195 as it's not a codec
- dac_njw1195_set_mute(false);
- }
- static void njw1195_enable_speaker(bool enable) {
- dac_njw1195_set_mute(!enable);
- }
- static void njw1195_enable_line_out(bool enable) {
- (void)enable;
- // Main output is Out1, already controlled by speaker enable
- }
- const dac_ops_t dac_njw1195_ops = {
- .init = njw1195_init,
- .deinit = njw1195_deinit,
- .set_volume = njw1195_set_volume,
- .set_power_mode = njw1195_set_power_mode,
- .on_i2s_started = njw1195_on_i2s_started,
- .enable_speaker = njw1195_enable_speaker,
- .enable_line_out = njw1195_enable_line_out,
- };
|