用ESP32做一个FM收音机

本文介绍两种使用TEA5767收音机模块实现FM收音机的方案,感兴趣的朋友可在此基础上实现更丰富的功能。

TEA5767收音机模块,频率范围从76—108MHZ自动数字调谐。高灵敏度,高稳定性,低噪音,收音模块。

其内部集成了中频选频和解调网络,可以做到完全免调,锁相环调谐系统,软静音,立体声消噪(SNC),高电平切割 (HCC)能通过总线关断。

用ESP32做一个FM收音机

TEA5767通过6个可写寄存器(Register 0–5)完成所有功能配置,同时支持读取2个状态寄存器(Register 0–1)。

这些寄存器共同决定了频率设定、搜索方向、静音控制、立体声判别等关键行为。

TEA5767通过I2C协议与控制端通信,本文使用ESP32-S3做为控制端。


方案一

最基础的方案,所需器件如下图:

用ESP32做一个FM收音机

使用两个按钮来实现向前和向后调台,音频输出直接连有源小喇叭

代码如下:

/**
 * TEA5767 FM收音机模块示例程序一
 * author: Billy Zhang 
 */
#include <Wire.h>
#include <radio.h>  // https://github.com/mathertel/Radio
#include <TEA5767.h>
#include <OneButton.h>
#include <freertos/FreeRTOS.h>
#include <freertos/task.h>

#define I2C_SDA             6
#define I2C_SCL             7

#define BTN_PREV            9
#define BTN_NEXT            10

/**
 * 北京FM台频率
 * 北京文艺广播 FM87.6 
 * 北京新闻广播 FM94.5/AM828 
 * 北京音乐广播 FM97.4 
 * 京津冀之声 FM100.6 
 * 北京体育广播 FM102.5
 * 北京交通广播 FM103.9 
 * 北京城市广播副中心之声 FM107.3/AM1026 
 */
// 换成本地的FM台, 频率格式:87.6 MHz = 8760 
static const uint16_t kFMFreq[7] = { 8760, 9450, 9740, 10060, 10250, 10390, 10730 };
static const uint8_t kFMNum = 7;

TEA5767 radio;
OneButton btnPrev(BTN_PREV);
OneButton btnNext(BTN_NEXT);
QueueHandle_t queue;
uint8_t fm_index = 0;

/**
 * 初始化按钮
 * 按键btnPrev为向前调台,按键btnNex为向后调台
 * 向消息队列传递调台数据,队列只存储最后一次的按钮数据,避免频繁操作。
 */
void initButtons() {
    btnPrev.attachClick([](){
      uint8_t value=1;
      xQueueOverwrite(queue, &value);
    });

    btnNext.attachClick([](){
      uint8_t value=2;
      xQueueOverwrite(queue, &value);
    });

    xTaskCreate([](void* param) {
      while(1) {
        btnPrev.tick();
        btnNext.tick();
        delay(10);
      }
    }, "ButtonTick_Task", 4096, NULL, 1, NULL);
}

/**
 * 调台
 */
void changeFrequency(uint8_t index) {
    uint16_t freq = kFMFreq[index];
    radio.setBandFrequency(RADIO_BAND_FM, freq);
    Serial.printf("change frequency to %d", freq);
}

void setup() {
    Serial.begin(115200);

    queue = xQueueCreate(1, sizeof(uint8_t));

    Wire.begin(I2C_SDA, I2C_SCL);

    radio.init();
    radio.setVolume(2);
    changeFrequency(0);

    initButtons();
}

void loop() {
    uint8_t receive;
    if (xQueueReceive(queue, &receive, 0) == pdPASS) {
        // 接收按键数据进行调台
        if (receive == 1) {
            if (fm_index > 0) {
                fm_index--;
                changeFrequency(fm_index);
            }
        } else if (receive == 2) {
            if (fm_index < kFMNum-1) {
                fm_index++;
                changeFrequency(fm_index);
            }
        }
        delay(100);
    }
}

程序解读

TEA5767模块的使用比较简单,调用库封装好的方法即可,这里主要使用其setBandFrequency方法来调台。

本例中将本地已知FM频道频率存储在一个数组内,使用变量index保存频道索引值,通过按键来改变index的值,然后取得对应频率进行调台。

为避免重复按键或反复按键导致对TEA5767模块操作过于频繁,这里使用FreeRTOS的消息队列来做数据同步处理,消息队列只存储最后一次按键信息,未及时处理的按键信息将丢弃。


方案二

在方案一基础上增加了一个WM8978全双工音频编解码模块,器件接线如下图:

用ESP32做一个FM收音机

代码如下:

/**
 * TEA5767 FM收音机模块示例程序二
 * author: Billy Zhang 
 */
#include <Wire.h>
#include <OneButton.h>
#include <radio.h>  // https://github.com/mathertel/Radio
#include <TEA5767.h>
#include <WM8978.h> /* https://github.com/CelliesProjects/wm8978-esp32 */
#include <freertos/FreeRTOS.h>
#include <freertos/task.h>
#include <ESP_I2S.h>

#define I2C_SDA             6
#define I2C_SCL             7

#define BTN_PREV            9
#define BTN_NEXT            10

#define I2S_BCK             3
#define I2S_WS              5
#define I2S_DOUT            2
#define I2S_DIN             4
#define I2S_MCLK            8

/**
 * 北京FM台频率
 * 北京文艺广播 FM87.6 
 * 北京新闻广播 FM94.5/AM828 
 * 北京音乐广播 FM97.4 
 * 京津冀之声 FM100.6 
 * 北京体育广播 FM102.5
 * 北京交通广播 FM103.9 
 * 北京城市广播副中心之声 FM107.3/AM1026 
 */
// 换成本地的FM台, 频率格式:87.6 MHz = 8760 
static const uint16_t kFMFreq[7] = { 8760, 9450, 9740, 10060, 10250, 10390, 10730 };
static const uint8_t kFMNum = 7;

TEA5767 radio;
OneButton btnPrev(BTN_PREV);
OneButton btnNext(BTN_NEXT);
QueueHandle_t queue;
uint8_t fm_index = 0;

I2SClass i2s;
WM8978 wm8978;

/**
 * 初始化按钮
 * 按键btnPrev为向前调台,按键btnNex为向后调台
 * 向消息队列传递调台数据,队列只存储最后一次的按钮数据,避免频繁操作。
 */
void initButtons() {
    btnPrev.attachClick([](){
      uint8_t value=1;
      xQueueOverwrite(queue, &value);
    });

    btnNext.attachClick([](){
      uint8_t value=2;
      xQueueOverwrite(queue, &value);
    });

    xTaskCreate([](void* param) {
      while(1) {
        btnPrev.tick();
        btnNext.tick();
        delay(10);
      }
    }, "ButtonTick_Task", 4096, NULL, 1, NULL);
}

/**
 * 调台
 */
void changeFrequency(uint8_t index) {
    uint16_t freq = kFMFreq[index];
    radio.setBandFrequency(RADIO_BAND_FM, freq);
    Serial.printf("change frequency to %d", freq);
}

void setup() {
    Serial.begin(115200);

    queue = xQueueCreate(1, sizeof(uint8_t));

    Wire.begin(I2C_SDA, I2C_SCL);

    radio.init();
    radio.setVolume(2);
    changeFrequency(0);

    i2s.setPins(I2S_BCK, I2S_WS, I2S_DOUT, I2S_DIN, I2S_MCLK);

    // Initialize the I2S bus in standard mode
    if (!i2s.begin(I2S_MODE_STD, 44100, I2S_DATA_BIT_WIDTH_16BIT, I2S_SLOT_MODE_STEREO, I2S_STD_SLOT_BOTH)) {
      Serial.println("Failed to initialize I2S bus!");
      return;
    }

    /* Setup wm8978 I2C interface */
    if (!wm8978.begin()) {
      Serial.println("Error setting up dac. System halted");
      while (1) delay(100);
    }
    wm8978.setSPKvol(40); /* max 63 */
    wm8978.setHPvol(32, 32);
    wm8978.cfgInput(0, 1, 0); // 配置line输入

    initButtons();
}

void loop() {
    uint8_t receive;
    if (xQueueReceive(queue, &receive, 0) == pdPASS) {
        // 接收按键数据进行调台
        if (receive == 1) {
            if (fm_index > 0) {
                fm_index--;
                changeFrequency(fm_index);
            }
        } else if (receive == 2) {
            if (fm_index < kFMNum-1) {
                fm_index++;
                changeFrequency(fm_index);
            }
        }
        delay(100);
    }
}
程序解读

WM8978模块主要以I2S方式进行工作,因此需要配置相关寄存器值,本例配置为线路输入,更多WM8978寄存器信息请参照其数据手册。


在引入音频编解码模块后,我们可以实现录音、增加音效、处理音频数据等更多功能。

- 本文由用户 老张 发布,文中观点仅代表作者本人,不代表本站立场。
- 如需转载,请联系作者;如有侵权,请联系本站处理。

2025-12   阅读(152)   评论(0)
 标签: 创客 ESP32 收音机 音频 I2S

涨知识
勾股定理

勾股定理,是一个基本的几何定理,指直角三角形的两条直角边的平方和等于斜边的平方。中国古代称直角三角形为勾股形,并且直角边中较小者为勾,另一长直角边为股,斜边为弦,所以称这个定理为勾股定理,也有人称商高定理。

评论:
相关文章
【ESP32 C++教程】Unit1-3 ESP32 Arduino 开发框架

ESP32 Arduino Framework是专门针对ESP32开发板的Arduino应用开发框架,为用户开发IOT应用、HMI应用提供一致的开发体验。


ESPConnect:基于浏览器的ESP32管理器

ESPConnect是一个基于现代浏览器的管理器,在你需要快速验证、调试、管理文件、检查状态的时候,它能帮你省下大量打开和切换重型工具的时间。


GPIOViewer:让ESP32引脚状态一览无余!

GPIOViewer 是一个强大的 Arduino 库,专门为 ESP32 芯片设计,可以实时监控 ESP32 芯片上的所有 GPIO 引脚状态。它可以帮助你快速直观地了解每个引脚的当前状态,例如高电平、低电平、输入、输出、中断等等。


音频编解码器ES8311中文规格书

具有ADC和DAC的低功耗单声道音频编解码器ES8311。


一文搞懂 wav 文件格式

WAV 是微软公司开发的一种声音文件格式,它符合 RIFF(Resource Interchange File Format) 文件规范,用于保存 Windows 平台的音频信息资源,被 Windows 平台及其应用程序所广泛支持。


ESP32 I2S音频:初识I2S通信与配置基础

在音频处理领域,I2S是一种广泛使用的通信协议,它专门用于芯片之间的音频数据传输。ESP32 作为一款高性能的微控制器,不仅支持 I2S 通信,还提供了强大的硬件接口和灵活的软件库,使其成为音频项目开发的理想选择。


I2S:数字音频串行接口

I2S(Inter-IC Sound)协议是飞利浦公司为数字音频设备之间的音频数据传输而制定的一种总线标准,它主要用于连接数字音频编解码器、微控制器、数字信号处理器等设备,确保音频数据能够准确、高效地在不同芯片或模块间传输。


小鹏物联网自动浇花套件

小鹏物联网智能浇花系统是照顾植物的好帮手,支持自动控制和手动控制两种模式,可通过电脑端和手机端查看数据和控制浇水。


ESP32 Arduino 开发框架

Arduino开发环境下适用于ESP32芯片系列开发板的应用开发框架。


【ESP32 C++教程】Unit1-2 C++类基础知识

本小节主要介绍C++ 类相关的基础知识,包括类的定义、继承、多态,范围作用域等。