Ретро FM-радио на модуле TEA5767 и Arduino Nano: пошаговое руководство
Компоненты и материалы
| Компонент | Описание | Кол-во |
|---|---|---|
| Arduino Nano R3 | Управление | 1 |
| TEA5767 FM-модуль | Приемник FM | 1 |
| Аналоговые стрелочные индикаторы (из старого радио) | Уровень сигнала и частота | 2 |
| Adafruit LCD 16x2 (белый на синем) | Цифровая шкала и инфо | 1 |
| Стереопотенциометр 10к лог | Регулятор громкости/баланса | 1 |
| D-класс аудио усилитель 2×3Вт | Усилитель звука | 1 |
| Динамик 3Вт, 4 Ом | Низко-середние частоты | 2 |
| Динамик 0.25Вт, 8 Ом | Высокие частоты | 2 |
| Ротационный энкодер с кнопкой | Настройка частоты | 1 |
| Тактильные кнопки 12 мм | 4 кнопки памяти станций | 4 |
| Конденсатор 1 мкФ | Разделение полос динамиков | 2 |
| Светодиоды | Индикация Stereo, Auto/Manual | 3 |
Приложения и сервисы
- Arduino IDE
О проекте
База — код Rodolfo Broco Manin (RodLophus на GitHub) с красивой аналоговой шкалой на LCD 16x2. Добавлено:
- Два аналоговых стрелочных прибора: уровень сигнала и частота.
- Светодиоды «Stereo» и «Auto/Manual search».
- 4 кнопки памяти с заранее заданными частотами — винтажный вид.
Аудиовыходы TEA5767 → стереопотенциометр → D-класс усилитель 2×3Вт → двухполосные динамики (ВЧ отделен 1мкФ конденсатором). Стрелки от старого радио, полное отклонение настраивается подстроечными резисторами. Питание: 2 литий-ионных элемента в последовательно через стабилизатор 7805 (стабильные 5В).

Библиотека TEA5767: GitHub. Схема и код ниже.
Известные нюансы: стрелка частоты обновляется дискретно (каждые 1 МГц) — особенность библиотеки. При слабых станциях частая переключение стерео/моно — в следующем обновлении добавлю переключатель Stereo/Mono и Mute.
Всё собрано в корпусе с телескопической антенной — готовый FM-стереоприемник в ретро-стиле.
Код
FM Radio (Arduino)
/* Simple radio tuner using Arduino Leonardo and the Philips TEA5767 module (Arduino Uno version)
Notes: - TEA5767 max 5V. - Signal level updates only on station change.
Encoder debouncing: pin >---+---/\/\/\/----> Encoder pin | 100Ohms | ----- 47nF cap | --- GND
Connections:
Encoder (with push): SW->D2, A->D3, B->D4
LCD: D7->D8, D6->D9, D5->D10, D4->D11, RS->D13, RW->GND, E->D12, VO->2k2 to GND
TEA5767: 1->SDA, 2->SCL, 3->GND, 5->5V, 6->GND, 7->Audio R, 8->Audio L, 10->Antenna
*/
#include <LiquidCrystal.h>
#include <Wire.h>
#include <TEA5767.h>
#define ENCODER_SW 2
#define ENCODER_A 3
#define ENCODER_B 4
#define button_freq1 A0
#define button_freq2 A1
#define button_freq3 A2
#define button_freq4 A3
#define SCALE_CLEAR 5
#define STEREO_CHAR_S 6
#define STEREO_CHAR_T 7
#define ST_AUTO 0
#define ST_STEREO 1
#define ST_GO_UP 2
#define ST_GO_DOWN 3
#define ST_SEARCH 4
const int LED = 0;
int analogsignal=0, analogscale=0, stereoled=0;
TEA5767 Radio;
float frequency = 88;
byte status = 0;
LiquidCrystal lcd(13, 12, 11, 10, 9, 8);
void updateScale() {
int lcdBase = (frequency - 88) * 4;
if(lcdBase > 79) lcdBase = 79;
int lcdMajor = lcdBase / 5;
int lcdMinor = lcdBase % 5;
if(lcdMajor > 0) { lcd.setCursor(lcdMajor - 1, 0); lcd.write(SCALE_CLEAR); }
else lcd.setCursor(lcdMajor, 0);
lcd.write(lcdMinor);
if(lcdMajor < 15) lcd.write(SCALE_CLEAR);
}
void isrEncoder() {
delay(50);
if(digitalRead(ENCODER_B) == HIGH) bitWrite(status, ST_GO_UP, 1);
else bitWrite(status, ST_GO_DOWN, 1);
}
void isrSwitch() {
delay(50);
if(bitRead(status, ST_AUTO)) bitWrite(status, ST_AUTO, 0);
else bitWrite(status, ST_AUTO, 1);
}
void setup() {
Serial.end();
pinMode(button_freq1, INPUT); digitalWrite(button_freq1,LOW);
pinMode(button_freq2, INPUT); digitalWrite(button_freq2,LOW);
pinMode(button_freq3, INPUT); digitalWrite(button_freq3,LOW);
pinMode(button_freq4, INPUT); digitalWrite(button_freq4,LOW);
pinMode(LED, OUTPUT); pinMode(6,OUTPUT); pinMode(7,OUTPUT); pinMode(5,OUTPUT);
byte needleChar[8];
byte stereoChar1[8] = { 0b01111, 0b11000, 0b11011, 0b11101, 0b11110, 0b11000, 0b01111, 0b00000 };
lcd.createChar(STEREO_CHAR_S, stereoChar1);
byte stereoChar2[8] = { 0b11110, 0b00011, 0b10111, 0b10111, 0b10111, 0b10111, 0b11110, 0b00000 };
lcd.createChar(STEREO_CHAR_T, stereoChar2);
byte scaleChar[8] = { 0b00000, 0b00000, 0b00000, 0b00000, 0b00000, 0b00100, 0b10101, 0b10101 };
lcd.createChar(SCALE_CLEAR, scaleChar);
for(int j = 0; j < 5; j++) {
for(int i = 0; i < 8; i++) needleChar[i] = scaleChar[i] | (0b10000 >> j);
lcd.createChar(j, needleChar);
}
lcd.begin(16, 2); lcd.clear();
for(int i = 0; i < 16; i++) lcd.write(SCALE_CLEAR);
pinMode(ENCODER_SW, INPUT); digitalWrite(ENCODER_SW, HIGH);
pinMode(ENCODER_A, INPUT); digitalWrite(ENCODER_A, HIGH);
pinMode(ENCODER_B, INPUT); digitalWrite(ENCODER_B, HIGH);
attachInterrupt(0, isrSwitch, RISING);
attachInterrupt(1, isrEncoder, RISING);
Wire.begin(); Radio.init(); Radio.set_frequency(frequency);
}
void loop() {
unsigned char buf[5];
int stereo, signalLevel, searchDirection, i;
i=0;
lcd.setCursor(12, 1); lcd.write(bitRead(status, ST_AUTO) ? 'A' : 'M');
if(bitRead(status, ST_AUTO)) digitalWrite(LED, LOW); else digitalWrite(LED, HIGH);
if (Radio.read_status(buf) == 1) {
frequency = floor(Radio.frequency_available(buf) / 100000 + .5) / 10;
stereo = Radio.stereo(buf);
signalLevel = (Radio.signal_level(buf) * 100) / 15;
analogsignal = map(signalLevel, 0, 100, 0, 255);
analogscale = map(frequency, 88, 114, 0, 255);
stereoled = map(stereo, 0.7, 1, 0, 255);
analogWrite(5, analogsignal); // Signal meter
analogWrite(6, analogscale); // Frequency meter
analogWrite(7, stereoled); // Stereo LED
updateScale();
lcd.setCursor(0, 1); lcd.write(183);
if(signalLevel < 100) lcd.write(' ');
lcd.print(signalLevel); lcd.write('%');
lcd.setCursor(6, 1);
if(frequency < 100) lcd.write(' ');
lcd.print(frequency, 1);
lcd.setCursor(14, 1);
if(stereo){ lcd.write(STEREO_CHAR_S); lcd.write(STEREO_CHAR_T); }
else lcd.print(" ");
}
if(bitRead(status, ST_SEARCH)) {
if(Radio.process_search(buf, searchDirection) == 1) bitWrite(status, ST_SEARCH, 0);
}
// Кнопки памяти (4 преднастроенные частоты)
if(digitalRead(button_freq2)==HIGH){ if(frequency > 94.8) { frequency=94.8; Radio.set_frequency(frequency); bitWrite(status, ST_GO_DOWN, 0); } else { frequency=94.8; Radio.set_frequency(frequency); bitWrite(status, ST_GO_UP, 0); } precrtajScale(); }
if(digitalRead(button_freq1)==HIGH){ if(frequency > 92) { frequency=92; Radio.set_frequency(frequency); bitWrite(status, ST_GO_DOWN, 0); } else { frequency=92; Radio.set_frequency(frequency); bitWrite(status, ST_GO_UP, 0); } precrtajScale(); }
if(digitalRead(button_freq3)==HIGH){ if(frequency > 97) { frequency=97; Radio.set_frequency(frequency); bitWrite(status, ST_GO_DOWN, 0); } else { frequency=97; Radio.set_frequency(frequency); bitWrite(status, ST_GO_UP, 0); } precrtajScale(); }
if(digitalRead(button_freq4)==HIGH){ if(frequency > 101.2) { frequency=101.2; Radio.set_frequency(frequency); bitWrite(status, ST_GO_DOWN, 0); } else { frequency=101.2; Radio.set_frequency(frequency); bitWrite(status, ST_GO_UP, 0); } precrtajScale(); }
// Энкодер: ручное/авто поиск
if(bitRead(status, ST_GO_UP)) {
if(bitRead(status, ST_AUTO) && !bitRead(status, ST_SEARCH)) {
bitWrite(status, ST_SEARCH, 1);
searchDirection = TEA5767_SEARCH_DIR_UP;
Radio.search_up(buf); delay(50);
} else {
if(frequency < 108) { frequency += 0.1; Radio.set_frequency(frequency); }
}
bitWrite(status, ST_GO_UP, 0);
}
if(bitRead(status, ST_GO_DOWN)) {
if(bitRead(status, ST_AUTO) && !bitRead(status, ST_SEARCH)) {
bitWrite(status, ST_SEARCH, 1);
searchDirection = TEA5767_SEARCH_DIR_DOWN;
Radio.search_down(buf); delay(50);
} else {
if(frequency > 88) { frequency -= 0.1; Radio.set_frequency(frequency); }
}
bitWrite(status, ST_GO_DOWN, 0);
}
}
void precrtajScale() {
int i; lcd.clear();
for(i = 0; i < 16; i++) lcd.write(SCALE_CLEAR);
}
Схемы
Производственный процесс
- Bluetooth Mesh: выбор между модулем и дискретным чипом
- Сборка DIY умного термостата типа NEST на Node-RED и Raspberry Pi
- Построение надёжного Temperature & Sensor API на Raspberry Pi с модулем GY‑91
- Создание бюджетного детектора плача ребенка на Raspberry Pi — DIY-руководство
- Ретро-часы на индикаторах Numitron с Arduino: просто, надёжно, экономно
- Импорт модулей в Python: практическое руководство с примерами
- Высокоскоростная фотография капель воды на Arduino Nano — DIY-триггер для вспышки
- Сборка DIY VR-скейтборда на Arduino, Google Cardboard и Bluetooth
- Водонепроницаемая FM-радио для душа на Arduino Nano, RDA5807, Nokia LCD и PAM8403
- Подключение Arduino Mega к GPS-модулю NEO‑6M: пошаговое руководство