TEA5767收音机模块,频率范围从76—108MHZ自动数字调谐。高灵敏度,高稳定性,低噪音,收音模块。
其内部集成了中频选频和解调网络,可以做到完全免调,锁相环调谐系统,软静音,立体声消噪(SNC),高电平切割 (HCC)能通过总线关断。

TEA5767通过6个可写寄存器(Register 0–5)完成所有功能配置,同时支持读取2个状态寄存器(Register 0–1)。
这些寄存器共同决定了频率设定、搜索方向、静音控制、立体声判别等关键行为。
TEA5767通过I2C协议与控制端通信,本文使用ESP32-S3做为控制端。
最基础的方案,所需器件如下图:

使用两个按钮来实现向前和向后调台,音频输出直接连有源小喇叭
代码如下:
/**
* 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全双工音频编解码模块,器件接线如下图:

代码如下:
/**
* 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寄存器信息请参照其数据手册。
在引入音频编解码模块后,我们可以实现录音、增加音效、处理音频数据等更多功能。
四位数码管是一种常见的LED显示器件,主要用于显示数字信息。
本节主要讲解Task类,FreeRTOS多任务的使用。
本节主要讲解Timer类,FreeRTOS定时器的使用。
本节主要讲解舵机驱动类和用按键控制舵机。
本节主要讲解执行器件类型和用按键控制继电器。
本小节主要讲解红外接收和遥控器件,以及遥控操作LED。
本小节讲解模拟量传感器使用,旋转电位器,DHT11温湿度传感器和实现自定义传感器类。
本小节讲解Sensor类及派生类、数字量传感器使用和传感器的推荐交互流程。
本小节讲解ESP32内置触摸引脚的用法,
本小节主要介绍按键信号转换、Button类及派生类、和Button交互推荐流程。
本小节主要介绍Ws2812灯珠的使用、对父类进行扩展实现自定义功能,和指针向下强制转换的使用。