пятница, 8 ноября 2019 г.

Радио на RDA5807M. Часть 1

RDA5807M Arduino

Микросхема RDA5807M - это FM радиоприемник нового поколения с поддержкой RDS/RBDS и цифровым управлением по I2C. Микросхема выполнена по CMOS технологии, что определяет ее минимальное энергопотребление. RDA5807M уже содержит все необходимые узлы и требует лишь небольшого числа внешних компонентов. А мощный аудиопроцессор обеспечивает оптимальное качество звука при различных условиях приема. Все это делает RDA5807M удачным выбором для носимых, портативных устройств.



В интернет магазинах распространен модуль RRD-102v2, на котором распаяны RDA5807M, кварцевый резонатор и пара компонентов обвязки. В данной статье я опишу как подключить этот модуль к Ардуино и что нужно знать для создания радиоприемника на его основе.

Характеристики RDA5807M

Сразу даю ссылку на даташит: RDA5807M_datasheet_v1.1, наиболее полную информацию о характеристиках RDA5807M вы можете найти в нем. Я перечислю некоторые из них:
  • Напряжение питания 2.7 - 3.3В
  • Потребляемый ток (при напряжении питания 3В):
    • в рабочем режиме - не более 20мА
    • в режиме сна - не более 15мкА
  • Диапазон принимаемых частот 50 - 115МГц
  • Выбираемый шаг изменения частоты: 200кГц, 100кГц, 50кГц, 25кГц
  • Выбираемый источник тактового сигнала: внешний или внутренний генератор (для внутреннего генератора требуется резонатор 32.768кГц)
  • Поддержка RDS/RBDS
  • Управление по шине I2C
  • Возможность прямого подключения нагрузки от 32Ом

Диапазон напряжения питания не самый удобный, например, от лития без стабилизации запитать не получится. Зато наушники можно подключать прямо к выводам микросхемы, дополнительное усиление не требуется. Также можно отметить небольшой потребляемый ток микросхемы, что позволяет запитывать ее от цифрового вывода микроконтроллера (по крайней мере AVR) в тех случаях, когда требуется отключение питания радио в целях энергосбережения.

Распиновка и подключение к Ардуино


RRD-102v2 (RDA5807M) pinout
Распиновка RRD-102v2

RRD-102v2 (RDA5807M) подключение к Arduino
Подключение RRD-102v2 (RDA5807M) к Ардуино

Выводы SDA и SCL модуля подключаются к одноименным выводам Ардуино. Для платы Uno это пины A4 и A5 соответственно. Их уровни превышают напряжение питания RDA5807M, но это не критично, микросхема отлично работает без преобразователя уровней. Питание берем с вывода 3v3.

Интерфейс управления

Здесь я хочу обратить внимание на имеющуюся в технической документации неточность (даташит на эту микросхему вообще очень мутный): в ней говорится, что I2C адрес микросхемы 0x10h, что внутренние адреса ее регистров не видны и что чтение и запись выполняются последовательно, начиная с фиксированного стартового адреса (0x0Ah для чтения, 0x02h для записи). После каждой операции чтения/записи происходит инкремент внутреннего счетчика и очередная операция будет выполняться уже для следующего регистра. Так до тех пор, пока внутренний счетчик не дойдет до верхней границы 0x3Ah, после этого он вернется к своему начальному значению.

На самом деле RDA5807M отзывается на три I2C адреса, в чем легко убедиться, воспользовавшись I2C сканером:

I2C адреса микросхемы RDA5807M
I2C адреса RDA5807M

Адрес 0x10h используется для последовательного обращения к регистрам, как было описано выше.
Адрес 0x11h позволяет обращаться к произвольным регистрам.
Адрес 0x60h позволяет работать с RDA5807M в режиме совместимости с TEA5767.

Упоминание адреса 0x11h можно найти в документе RDA5807P_ProgManual_1.0. Хоть он и предназначен для другой микросхемы, но практически всё применимо и для RDA5807M. Ниже приведен фрагмент из данного документа, описывающий формат I2C обмена при использовании адреса 0x11h:

Формат обмена RDA5807M I2C 0x11h
Формат обмена с RDA5807M по I2C адресу 0x11h

Как можно видеть, при записи в режиме произвольного доступа первым передается адрес интересующего регистра (REGISTER ADDRESS), затем старший и младший байты данных. Для чтения содержимого регистра из RDA5807M микроконтроллер сначала передает его адрес, затем считывает старший и младший байты. Чуть позже я приведу пример чтения/записи регистров, а пока разберемся с их назначением.

Регистры RDA5807M

Управление работой RDA5807M заключается в обращении к его регистрам: изменяя одни регистры, мы производим необходимые нам настройки; из других можно читать различную информацию (флаги, данные RDS и т.д.). Регистры 16-разрядные, их адреса и назначение приведены в даташите. Описание весьма скудное, поэтому я решил сам "пощупать" каждый регистр, чтобы понять какой бит за что отвечает. Для этого была написана следующая программа:

Программа для PC RDA5807M
Управление работой RDA5807M с компьютера

Данная программа читает значения регистров RDA5807M, отображает в удобном виде и позволяет изменять их, щелкая мышью по элементам управления. Ардуино при этом выступает в роли посредника между программой на компьютере и RDA5807M, для этого в нее должен быть загружен соответствующий скетч (вы найдёте его в программе по кнопке "Скетч для Ардуино"). Очень рекомендую попробовать данную программу, чтобы разобраться с назначением регистров. Скачать ее можно здесь. И, чтобы совсем не осталось вопросов по управлению RDA5807M, привожу описание регистров на понятном языке.

Регистр Биты Имя Назначение Значение
по умолчанию
00h 15:0 CHIP_ID Chip ID - Идентификатор микросхемы. Есть у меня основания полагать, что значение ChipID состоит именно из двух байт, а не одного, как это указано в даташите.  0x5804
02h 15 DHIZ Audio Output High-Z Disable. Управляет состоянием аудио выводов: 0 - выводы находятся в высокоимпедансном состоянии; 1 - переводит выводы в рабочий режим.  0
14 DMUTE  Mute Disable - отключение режима mute, который по умолчанию включен (значение 0). Для отключения mute в этот бит следует записать 1.  0
13 MONO Принудительное моно, включается записью в данный бит значения 1   0
12 BASS Bass Boost - усиление басов. Для включения данной опции необходимо записать 1  0
11 RCLK_NON_
CALIBRATE_
MODE
Если я правильно понял, этот бит отключает температурную компенсацию тактового генератора, в результате чего RDA5807M не сможет работать в заявленном температурном диапазоне (-20..70C) и сможет поддерживать колебания температуры только на +/- 20C от точки настройки.  0
10 RCLK_DIRECT_
INPUT_MODE 
Бит RCLK Direct Input Mode следует установить в 1, если используется внешний тактовый сигнал  0
9 SEEKUP Seek Up - направление поиска радиостанций: 0 - к нижней границе диапазона; 1 - вверх.  0
8 SEEK Запись 1 в этот бит запускает процесс поиска радиостанции. Поиск ведется в направлении, заданном битом SEEKUP, до нахождения радиостанции или до прохождения всего диапазона частот, после чего данный бит сбрасывается и устанавливается бит STC.  0
7 SKMODE Seek Mode. Определяет поведение при достижении границы диапазона во время поиска радиостанций: 0 - продолжить поиск с другой границы; 1 - прекратить поиск 0
6:4 CLK_MODE Позволяет выбрать частоту внешнего тактового сигнала. Возможны следующие значения:
000 = 32.768кГц
001 = 12МГц
101 = 24МГц
010 = 13МГц
110 = 26МГц
011 = 19.2МГц
111 = 38.4МГц
При указании неверного значения ничего страшного не произойдет, просто FM приемник не сможет настраиваться на частоту и выполнять поиск радиостанций.
000
3 RDS_EN RDS/RBDS Enable. Запись 1 в этот бит включает прием RDS/RBDS сообщений. 0
2 NEW_METHOD New Demodulation Method Enable - установка этого бита задействует новый метод демодуляции, способный улучшить чувствительность приемника 0
1 SOFT_RESET  Программный сброс RDA5807M. Установка бита в 1 приведет к сбросу всех внутренних регистров к значениям по умолчанию. Сброс выполняется автоматически при включении питания микросхемы, нет необходимости сбрасывать устройство дополнительно. 0
0 ENABLE  Power Up Enable - разрешение работы. Установка в 1 переводит приемник в рабочий режим; 0 - спящий режим - отключает питание внутренних узлов, состояние регистров при этом сохраняется, после возвращения в рабочий режим необходимо выполнить TUNE для настройки на радиостанцию. 0
03h 15:6 CHAN Channel Select - выбор канала. Частота радиостанции устанавливается не явно, а путем изменения значения CHAN, которое при умножении на SPACE и прибавления нижней границы диапазона дает итоговую частоту. Для записи CHAN необходимо также установить бит TUNE, в противном случае CHAN не изменится.  0x00
5 DIRECT_MODE Режим прямого управления, который используется только при тестировании - это описание из даташита, не уверен, что данный бит имеет отношение к RDA5807M.
4 TUNE Запись в этот бит значения 1 запускает процесс настройки. По окончании настройки устанавливается бит STC, бит TUNE при этом сбрасывается.
3:2 BAND Выбор полосы. Возможно одно из четырех значений:
00 - 87..108МГц
01 - 76..91МГц
10 - 76..108МГц
11 - 65..76МГц или 50..65МГц (определяется битом 65M_50M MODE регистра 07h)
00 
1-0 SPACE  Задает шаг изменения частоты. Возможны следующие значения:
00 - 100кГц
01 - 200кГц
10 - 50кГц
11 - 25кГц
В большинстве стран частоты радиостанций разнесены на 200кГц или 100кГц. Поэтому, скорее всего, вам не придется менять значение по умолчанию. Установив шаг 50кГц или 25кГц, вы лишь замедлите процесс поиска радиостанций.
 00
 04h 15:12 RSVD Биты зарезервированы   0000
11 DE De-emphases. Определяет значение постоянной времени для  частотных предыскажений:
0 - 75мкс
1 - 50мкс
Предыскажения подразумевают усиление высоких частот сигнала при передаче (pre-emphasis) с целью уменьшения воздействия на них помех и последующее восстановление исходного сигнала в приемнике (ослабление высоких частот - de-emphases). В большинстве стран (в т.ч. в Европе и РФ) используется значение 50. В Северной и Южной Америке, в Южной Корее - 75.
 0
10 RSVD Зарезервирован 1
9 SOFTMUTE_EN Soft Mute Enable - приглушение звука, может быть использовано для минимизации шумов в условиях слабого приема. Функция включается установкой бита в 1. 0
8 AFCD Automatic Frequency Control Disable - отключение автоматической подстройки частоты.
0 - AFC работает
1 - AFC выключено. 
 0
05h 15 INT_MODE Режим генерации прерывания при завершении поиска/настройки. Данный бит определен в даташите, но не имеет отношения к RDA5807M. Актуален для микросхем с дополнительными выводами GPIO, например, RDA5807P.  1
14:12 RSVD Биты зарезервированы 000 
11:8 SEEKTH Seek Threshold. Данные биты задают порог отношения сигнал/шум при выполнении поиска радиостанций.   1000
7:6 LNA_PORT_SEL  Low Noise Amplifier Port Selection - эти биты пропущены в даташите RDA5807M. При этом они весьма важны, поскольку определяют источник сигнала для приемника (смотрите блок LNA на функциональной схеме приемника в даташите):
00 - нет входа
01 - LNAN (земля)
10 - LNAP (вход FMIN)
11 - оба источника
10 
5:4 RSVD Биты зарезервированы 00 
3:0 VOLUME Регулировка громкости 1011
06h 15 RSVD Зарезервирован 0
14:13 OPEN_MODE Данные биты указаны в даташите, но они неактуальны для RDA5807M. В других микросхемах серии установка этих битов в 11 разрешает изменение остальных битов регистра, отвечающих за настройку I2S (Audio Data Interface). 00
07h 15 RSVD Зарезервирован 0
14:10 TH_SOFTBLEND Soft Blend Thershold - настройка уровня шумоподавления. 10000
9 65M_50M MODE Данный бит определяет используемый диапазон частот, когда биты BAND содержат значение 11:
0 - 50..76МГц
1 - 65..76МГц
1
8 RSVD Зарезервирован 0
7:2 SEEK_TH_OLD Seek Threshold Old - по аналогии с SEEKTH данные биты определяют порог при поиске радиостанций, но актуальны только при SEEK_MODE (биты 14:12 регистра 0x20h) = 001 - "старый" метод поиска. 000000
1 SOFTBLEND_EN Soft Blend Enable. Данный бит разрешает шумоподавление, уровень которого задан битами TH_SOFTBLEND. Помогает здорово очистить сигнал от помех. 1
0 FREQ_MODE Режим задания частоты. Когда данный бит сброшен в 0, результирующая частота определяется как BAND + CHAN * STEP. При FREQ_MODE = 1 частота определяется как BAND + содержимое регистра 08h. 0
08h 15:0 FREQ_DIRECT Определяет частоту при FREQ_MODE = 1. Результирующая частота является суммой значения данного регистра (в килогерцах) и нижней границы диапазона. Например, при BAND = 00 (87..108МГц) и FREQ_DIRECT = 20300 итоговая частота будет 107,3МГц.
Описание регистра FREQ_DIRECTотсутствует в даташите, вероятно, потому, что такой способ изменения частоты не является штатным.
0x0h
0Ah 15 RDSR RDS Ready - флаг готовности данных RDS/RBDS (1 - данные готовы) 0
14 STC Seek/Tune Complete - флаг завершения поиска/настройки на заданную частоту (1 - операция завершена).  0
13 SF Seek Fail - флаг, сигнализирующий о неуспешном выполнении поиска, когда не удалось найти сигнал с RSSI большим порога SEEKTH 0
12 RDSS RDS Synchronization - индикатор синхронизации RDS.
0 - RDS декодер не синхронизирован;
1 - RDS декодер синхронизирован.
В даташите указано, что данный флаг обновляется только в verbose (подробном) режиме работы RDS (в противопоставление стандартному, менее информативному режиму), однако не уточняется, какой бит отвечает за выбор режима. Судя по тому, что данный флаг обновляется, как и биты BLERA .. BLERD, RDA5807M по умолчанию работает в подробном режиме RDS.
0
11 BLK_E Данный флаг сообщает о получении E блока.0
10 ST Stereo Indicator.
0 - моно;
1 - стерео.
1
9:0 READCHAN Read Channel. Эти биты содержат значение CHAN, доступны только для чтения. В режиме последовательного доступа к регистрам RDA5807M стартовый адрес для чтения - 0Ah, таким образом нет возможности прочитать значение CHAN регистра 03h. Этим и обусловлено наличие битов READCHAN. 0x0h
0Bh 15:9 RSSI Received Signal Strength Indicator - показатель уровня принимаемого сигнала. 0
8 FM_TRUE Данный флаг сигнализирует о наличии передачи на текущей частоте. То есть приемник настроен на радиостанцию. 0
7 FM_READY Насколько я могу судить, данный флаг идентичен флагу STC 0
6:5 RSVD Биты зарезервированы 00
4 ABCD_E Действующий в США стандарт RBDS помимо блоков A, B, C и D, предусмотренных стандартом RDS,  предполагает использование еще одного блока - E. Бит ABCD_E помогает идентифицировать содержимое регистров 0Ch..0Fh как  ABCD или E:
0 - A, B, C, D
1 - E
0
3:2 BLERA Block Errors Level Of A - уровень ошибок в блоке A (RDS) или E (RBDS, когда ABCD_E флаг установлен в 1):
00 - нет ошибок;
01 - 1..2 ошибки требуют коррекции;
10 - 3..5 ошибок  требуют коррекции;
11 - более 6 ошибок - блок некорректный и не должен использоваться.
1:0 BLERB Block Errors Level Of B - уровень ошибок в блоке B (RDS) или E (RBDS, когда ABCD_E флаг установлен в 1). Значения битов аналогичны BLERA.
0Ch 15:0 RDSA Блок A (в режиме RDS) или E (в режиме RBDS при ABCD_E = 1). 0x5803h
0Dh 15:0 RDSB Блок B (в режиме RDS) или E (в режиме RBDS при ABCD_E = 1). 0x5804h
0Eh 15:0 RDSC Блок C (в режиме RDS) или E (в режиме RBDS при ABCD_E = 1). 0x5808h
0Fh 15:0 RDSD Блок D (в режиме RDS) или E (в режиме RBDS при ABCD_E = 1). 0x5804h
10h 15:14 BLERC Block Errors Level Of C - уровень ошибок в блоке C (RDS) или E (RBDS, когда ABCD_E флаг установлен в 1). Значения битов аналогичны BLERA.

13:12 BLERD Block Errors Level Of D - уровень ошибок в блоке D (RDS) или E (RBDS, когда ABCD_E флаг установлен в 1). Значения битов аналогичны BLERA.

Это не все регистры RDA5807M, по старшим адресам доступны другие. Возможно, среди них есть еще что-то интересное. И если вам о них известно, пишите, добавлю их в список.

Программирование RDA5807M

Давайте начнем с простенького скетча. Если вы попробуете управлять RDA5807M из моей программы, то обнаружите, что для того чтобы заставить его работать достаточно установить несколько битов: ENABLE, DHIZ, DMUTE, SEEK. Установка последнего запустит поиск радиостанции. Эти же действия можно выполнить программно при помощи следующего скетча:
#include <Wire.h>

void setup() {
  Wire.begin();
  setRegister(0x02, 0xC101); // set ENABLE, DHIZ, DMUTE, SEEK
}

void loop() {
}

void setRegister(uint8_t reg, const uint16_t value) {
  Wire.beginTransmission(0x11);
  Wire.write(reg);
  Wire.write(highByte(value));
  Wire.write(lowByte(value));
  Wire.endTransmission(true);
}

Подключите RDA5807M к Ардуино по приведенной ранее схеме и залейте в нее скетч. Приемник выполнит поиск и настроится на первую найденную радиостанцию. Бит Tune при этом сбрасывается. Нажатие кнопки Reset на Ардуино и повторное выполнение функции setup будут снова устанавливать этот бит, инициируя поиск следующей станции. Работает? Двигаемся дальше.

В примере скетча выше мы записали в регистр 02h заранее определенное значение. На деле такое требуется редко, разве что для инициализации некоторых регистров. В основном же значения регистров формируются в процессе работы программы при изменении отдельных битов. В таких случаях удобно использовать константы, содержащие номера этих битов или маски для их установки. Ниже приведен пример такого скетча. Он позволяет настроиться на конкретную радиостанцию, установить громкость и получить RSSI.

#include <Wire.h>

#define RDA5807M_RANDOM_ACCESS_ADDRESS 0x11
// регистры
#define RDA5807M_REG_CONFIG 0x02
#define RDA5807M_REG_TUNING 0x03
#define RDA5807M_REG_VOLUME 0x05
#define RDA5807M_REG_RSSI   0x0B
// флаги
#define RDA5807M_FLG_DHIZ 0x8000
#define RDA5807M_FLG_DMUTE 0x4000
#define RDA5807M_FLG_BASS 0x1000
#define RDA5807M_FLG_ENABLE word(0x0001)
#define RDA5807M_FLG_TUNE word(0x0010)
//маски
#define RDA5807M_CHAN_MASK 0xFFC0
#define RDA5807M_CHAN_SHIFT 6
#define RDA5807M_VOLUME_MASK word(0x000F)
#define RDA5807M_VOLUME_SHIFT 0
#define RDA5807M_RSSI_MASK 0xFE00
#define RDA5807M_RSSI_SHIFT 9

uint8_t volume = 1; // 0..15
uint16_t freq = 1073; // 107.3FM
uint16_t reg02h, reg03h, reg05h, reg0Bh;

void setup() {
  Serial.begin(9600);
  Wire.begin();
  // Регистр 02h - включение, настройки
  reg02h = RDA5807M_FLG_ENABLE | RDA5807M_FLG_DHIZ | RDA5807M_FLG_DMUTE;
  setRegister(RDA5807M_REG_CONFIG, reg02h);
  
  // А потом решили еще усилить басы:
  reg02h |= RDA5807M_FLG_BASS;
  setRegister(RDA5807M_REG_CONFIG, reg02h);
  
  // Регистр 03h - выбор радиостанции
  // После сброса в регистре 03h значение по умолчанию - 0
  // Таким образом BAND = 00 (87..108МГц), STEP = 00 (100кГц). Оставим их как есть
  reg03h = (freq - 870) << RDA5807M_CHAN_SHIFT; // chan = (freq - band) / space
  setRegister(RDA5807M_REG_TUNING, reg03h | RDA5807M_FLG_TUNE);
  
  // Регистр 05h. Установим громкость, остальные биты не изменяем
  reg05h = getRegister(RDA5807M_REG_VOLUME); // Считываем текущее значение
  reg05h &= ~RDA5807M_VOLUME_MASK; // Сбрасываем биты VOLUME
  reg05h |= volume << RDA5807M_VOLUME_SHIFT; // Устанавливаем новую громкость
  setRegister(RDA5807M_REG_VOLUME, reg05h);
}

void loop() {
  // Регистр 0Bh - статус
  reg0Bh = getRegister(RDA5807M_REG_RSSI);
  uint8_t RSSI = (reg0Bh & RDA5807M_RSSI_MASK) >> RDA5807M_RSSI_SHIFT;
  Serial.print("RSSI = ");
  Serial.print(RSSI);
  Serial.println(" (0-min, 127-max)");
  delay(500);
}

void setRegister(uint8_t reg, const uint16_t value) {
  Wire.beginTransmission(0x11);
  Wire.write(reg);
  Wire.write(highByte(value));
  Wire.write(lowByte(value));
  Wire.endTransmission(true);
}

uint16_t getRegister(uint8_t reg) {
  uint16_t result;
  Wire.beginTransmission(RDA5807M_RANDOM_ACCESS_ADDRESS);
  Wire.write(reg);
  Wire.endTransmission(false);
  Wire.requestFrom(0x11, 2, true);
  result = (uint16_t)Wire.read() << 8;
  result |= Wire.read();
  return result;
}

В этом примере значения регистров получаются установкой отдельных разрядов. Для этого используются определенные в начале скетча флаги и маски. Я описал несколько из них для примера, остальные добавляются по аналогии.

Чтобы настроить RDA5807M на интересующую частоту необходимо установить значения BAND и SPACE и затем изменять только значение CHAN. Итоговая частота определяется по формуле:
F = BAND + CHAN * SPACE.

В скетче используются определенные по умолчанию BAND и SPACE (87..108МГц  и 100кГц соответственно). По ним можно определить значение, которое должно быть записано в биты CHAN для получения интересующей частоты. Не забывайте при записи CHAN устанавливать также бит TUNE.

Для изменения громкости значение регистра 05h считывается из RDA5807M в переменную. Затем осуществляется сброс битов VOLUME. И уже после этого можно устанавливать новое значение громкости и записывать результат в регистр.

Для получения RSSI выполняются обратные действия: в считанном из регистра 0Bh значении сбрасываются все биты, кроме содержащих RSSI. Затем результат сдвигается вправо, чтобы младший бит RSSI оказался в младшем разряде переменной. Так мы получим нужное нам значение.

Теперь, когда описаны основные приемы управления RDA5807M, можно приступить к программированию. Нужно лишь определиться с функционалом и интерфейсом.

Добавим LCD дисплей и энкодер

Да, я люблю использовать в своих проектах LCD2004 с I2C интерфейсом и энкодер вращения. Это уже привычные для меня элементы создания пользовательского интерфейса. Используя их, я могу сосредоточиться на текущей задаче, а не заморачиваться с изобретением велосипеда. Поэтому сейчас я добавил их в схему:

Радио на Arduino и RDA5807M с дисплеем и энкодером
Схема радио с дисплеем и энкодером


Макет радио RDA5807M с дисплеем и энкодером
Макет радио с дисплеем и энкодером

Итак, моя текущая задача - это создание радио с базовым функционалом и индикацией. О законченном проекте речь пока не идет. Для меня это скорее знакомство с данным модулем. Поэтому в предлагаемом ниже скетче нет фишек вроде сохранения списка радиостанций в EEPROM. Нет и работы с RDS - использованию этой технологии в RDA5807M я посвящу следующую публикацию. В последствии я планирую сделать радио в оригинальном корпусе с OLED дисплеем. А пока можете оценить результат данного этапа, скетч доступен по ссылке. Для работы требуется библиотека LiquidCrystal_I2C_Menu, скачайте и установите ее.

В скетче реализовано:
  • Поиск радиостанции вверх/вниз и отображение частоты
  • Регулировка громкости
  • Ввод значения частоты энкодером
  • Выбор поведения при повороте энкодера: регулировка громкости или поиск радиостанции
  • Установка ряда параметров, отвечающих за звук и шумоподавление
  • Сохранение настроек в EEPROM и чтение их при включении радио

Вход в меню осуществляется при нажатии на кнопку энкодера. Кстати, в скетче присутствует код для управления RDA5807M с компьютера из упомянутой ранее программы. Это удобно для отслеживания содержимого регистров в процессе работы радио. Чтобы отключить эту возможность достаточно закомментировать первую строку скетча.

На этом пока всё. Продолжение будет в следующей публикации с обзором RDS.

77 комментариев:

  1. Случайно зашел, великолепно. Пожалуй прочитаю все что есть ) Спасибо автору!

    ОтветитьУдалить
  2. Спасибо большое, все доступно и понятно.

    ОтветитьУдалить
  3. Заинтересовало, надо поэксперементировать. Спасибо Владимир.

    ОтветитьУдалить
  4. Спасибо очень интересно, занимаюсь созданием своего радио, данная публикация очень помогла.
    Хотелось-бы узнать о автоматической настройке и записи найденных радиостанций в каналы, чтобы потом переключать только каналы. Я так понимаю нужно использовать регистр SEEK? Как правильно его использовать?

    ОтветитьУдалить
    Ответы
    1. Верно, SEEK. Только это не регистр, а бит регистра 02H. Если вам нужна процедура нахождения всех радиостанций, то ее алгоритм может быть следующий:
      1. Устанавливаете CHAN = 0, SEEKUP = 1 (поиск вверх), SKMODE = 1 (прекратить поиск при достижении границы).
      2. В цикле пока SF != 1 (т.е. пока не дошли до верхней границы) устанавливаете бит SEEK для поиска очередной станции и периодически читаете регистр 0AH в ожидании STC = 1.
      3. Когда бит STC изменится с 0 на 1 (при условии что SF = 0), сохраняете текущее значение CHAN. В массиве или в EEPROM - как вам удобнее. И возвращаетесь к пункту 2 для поиска следующей станции.
      4. При прохождении всего диапазона частот будут установлены флаги STC и SF. Последний является условием для выхода из предложенного цикла.

      По окончании процедуры получите массив значений CHAN и их количество.

      Удалить
  5. ..спасибо, что в регистрах навели порядок, собирался тожсамое, а тут бац! и ваша статья попалась, и отдельная благодарность за скан чипа на доп.отклики, мнеб ивголову непришло, карочи, like-like �� very-very

    ОтветитьУдалить
    Ответы
    1. Благодарю за положительный отзыв о публикации. Сегодня выложил продолжение про RDS

      Удалить
  6. Владимир, если вас не затруднит, помогите со скетчем для автоматического поиска. Бьюсь уже месяц, хоть и не каждый день. Или посмотрите мой кусок:
    if (enc1.isRight ()) { //поворот энкодера вправо
    reg02h |= RDA5807M_FLG_SEEKUP | RDA5807M_FLG_SKMODE; //устанавливаем флаги SEEKUP (направление поиска) и SKMODE (прекратить поиск при достижении диаппазона)
    setRegister(RDA5807M_REG_CONFIG, reg02h); //записываем в регистр
    reg03h &= ~RDA5807M_CHAN_MASK; //сбрасываем значение CHAN
    setRegister(RDA5807M_REG_TUNING, reg03h); //записываем в регистр
    while (sf != 1) { //начало цикла (пока не дошли до конца диаппазона)
    reg02h |= RDA5807M_FLG_SEEK; //устанавливаем флаг SEEK (начало поиска)
    setRegister(RDA5807M_REG_CONFIG, reg02h); //записываем в регистр
    reg0Ah = getRegister(RDA5807M_REG_STATUS1); //читаем регистр 0AH
    bool stc = (reg0Ah & RDA5807M_FLAG_STC); //читаем флаг STC (найдена радиостанции)
    // Serial.println ("STC " + String (stc));
    reg0Ah = getRegister(RDA5807M_REG_STATUS1); //читаем регистр 0AH
    sf = (reg0Ah & RDA5807M_FLAG_SF); //читаем флаг SF (успешность поиска)
    //Serial.println ("SF " + String (sf));
    if (stc == true && sf == false) { //если найдена радиостанция и не дошли до конца диаппазона
    reg0Ah = getRegister (RDA5807M_REG_STATUS1); //читаем регистр 0AH
    readChan = (reg0Ah & RDA5807M_READCHAN_MASK); //читаем значение readChan (частота)
    channel[i] = readChan; //присваиваем элементу массива channel с индексом i значение readChan
    Serial.println ("READCHAN " + String (readChan));
    Serial.println ("CHANNEL " + String (channel[i]) + String (i));
    i++; //увеличиваем индекс i на 1
    break;
    }
    }
    }
    }
    Где-то я что-то путаю.

    ОтветитьУдалить
    Ответы
    1. Добрый день!

      Строка, где вы сбрасываете CHAN - новое его значение не запишется без установки флага TUNE. Но ошибка скетча не в этом, скорее в операторе break, который прерывает цикл поиска радиостанций после первого же нахождения.

      Флаги, которые сбрасываются приемником автоматически (TUNE, SEEK), я бы советовал записывать не в переменные, а непосредственно в регистры. Чтобы потом, например, при включении/выключении усиления басов не записать в регистр 02h флаг SEEK повторно. Иначе придется считывать содержимое регистра перед каждым его изменением.

      Для автоматического поиска попробуйте такой алгоритм:

      - Установка начальных значений регистров (CHAN, SEEKUP, SKMODE и т.д.)
      - Бесконечный цикл
      --- Установка флага SEEK в регистре 02h
      --- Цикл, пока в 0ah не установится STC (ждем завершения поиска). Внутри цикла можно добавить delay, чтобы не дергать приемник всё это время
      --- Считываем флаг SF. Если установлен, то выходим из цикла
      --- Считываем и сохраняем значение CHAN
      - Возврат к началу цикла

      Кода, реализующего такой алгоритм, у меня нет. Но он совсем простой, проблем c реализацией возникнуть не должно.

      И еще, внутри цикла вы несколько раз читали регистр 0ah - это лишнее. Достаточно 1 раза, для приведенного выше алгоритма - внутри вложенного цикла. Считанное значение потом используется и для проверки SF, и для получения READCHAN.

      Удалить
    2. Этот комментарий был удален автором.

      Удалить
    3. Шумодав не гасит шумы,а тупо снижает громкость-печалька.Хотел для переговорного приемник и жук с кварцем на передачу.Не судьба.

      Удалить
    4. Добрый день!
      Вы случаем не путаете шумоподавление (SOFTBLEND) с SOFTMUTE_EN?
      TH_SOFTBLEND и SOFTBLEND_EN на самом деле гасят шумы. Я это не из даташита взял, а проверил сам при плохом приеме. Работает.

      Удалить
    5. Думал-помучаю,отпишусь.Но то делал отцу приемник(по болгарски где),то приболел-начинаю отходить.Вроде не путал,сейчас доигрался-тишина вообще.По одному параметру меняю-слушаю.Исправно с др скетчами.Мне не ПРИГЛУШАТЬ надо а шумоподавление!полное.Буду играться еще.Ну и срисовал несколько схем ШП.Мне 5-6 фраз за смену на 0.5 км-эфир не засру. Если можно-проверте ШП между станциями.

      Удалить
  7. Спасибо Владимир, буду пробовать.

    ОтветитьУдалить
  8. господа, очень понравился проект, собрал. все супер с одним лишь отличием - у автора 4х строчный дисплей и скетч под него. ткните пальцем как подправить его под двухстрочный? был бы благодарен!!!

    ОтветитьУдалить
  9. и еще пытаюсь найти в коде как сделать более плавной регулировку громкости и что бы минимальный порог был потише. это возможно или ограничивается возможностями модуля rda5807?

    ОтветитьУдалить
    Ответы
    1. Чтобы сделать тише, нужно подобрать резисторный делитель. Сам так планирую поступить, для наушников максимальная громкость слишком высока. А вот регулировку сделать плавнее в этом модуле не получится.

      Удалить
    2. Владимир, спасибо. Я подключил еще усилитель на pam8403 вроде самая минимальная громкость не очень громкая)) а по поводу адаптации под двухстрочный дисплей сможете ответить?

      спасибо!

      Удалить
    3. При использовании двухстрочного что-то не умещается? Можете сделать фотки, чтобы я понял где править? По идее при объявлении
      LiquidCrystal_I2C_Menu lcd(0x27, 16, 2);
      все функции должны подстроиться под указанный размер дисплея. Но я это не тестировал, у меня нет lcd1602, только четырехстрочный

      Удалить
    4. да, не помещались пункты меню например которые ниже идут. и чтобы их выбрать нужно было сначала прокрутить чтоб они появились на экране а потом обратно крутить энкодер чтоб его выбрать и войти в него. таким образом "back" на экране вообще никогда не появлялось))

      исправил, спасибо, все теперь помещается. я просто абсолютный нуб в ардуино поэтому задаю такие вопросы. еще осталась проблема с энкодером. по идее вращение вправо - увеличение (громкости) а влево - уменьшение. сейчас наоборот. влево увеличение вправо уменьшение. поможет ли замена в коде eRight на eLeft и наоборот?

      Удалить
    5. От энкодера к Ардуино идут 2 провода (CLK и DT, если энкодер как у меня). Просто поменяйте их местами, подключая к Ардуино

      Удалить
    6. Тогда изменится направление перемещения по строкам меню.

      Удалить
  10. Добрый день. Спасибо большое за полный и понятный разбор управления регистрами! Есть еще один вопрос. Магазины закрыты, али долго, нет энкодера в общем. можно как нибудь заменить его на кнопки + и -? заранее спасибо.

    ОтветитьУдалить
    Ответы
    1. Добрый день!
      Чтобы переделать на управление кнопками, надо изменить функцию getEncoderState в файле LiquidCrystal_I2C_Menu.cpp. Сейчас она опрашивает одну кнопку и выводы энкодера A и B. Сделайте вместо опроса A, B опрос еще двух кнопок по аналогии с первой. Для них надо будет завести переменные для хранения предыдущих значений. Номер пина можно хранить в имеющихся _pinA и _pinB или переименовать их в _pinLeft, _pinRight (переименовать надо в нескольких местах, где они используются).
      В общем изменения минимальные. Попробуйте.

      Удалить
    2. Спасибо! Буду пробовать!

      Удалить
  11. Спасибо! Оформление материала на высшем уровне!

    ОтветитьУдалить
  12. В этом даташите Rev.1.8 https://datasheet.lcsc.com/szlcsc/1810010217_RDA-Microelectronics-RDA5807M_C82537.pdf более полная информация по регистрам.

    ОтветитьУдалить
    Ответы
    1. На первый взгляд больше похоже на описание от другой микросхемы с поддержкой I2S и GPIO. Проверю, может есть что недостающее, актуальное для RDA5807M. Спасибо.

      Удалить
  13. Владимир, здравствуйте! Может подскажите, как здесь работает режим моно (если конечно сталкивались с этим)? В регистре 02 прописываю 13 бит в 1, но разницы в звуке не ощущаю. Причем индикатор стерео также продолжает отображаться.

    ОтветитьУдалить
    Ответы
    1. Добрый день!
      Насколько я понимаю, данный индикатор сигнализирует о наличии стерео в принимаемом радиосигнале, который содержит как моно, так и стерео, плюс пилот-тон, еще RDS. И бит MONO на него не влияет.
      Установка бита MONO регистра 02 принуждает декодер использовать моно сигнал. Он в свою очередь состоит из суммы сигналов левого и правого каналов.
      А насчет разницы в звуке, думаю, её можно будет заметить на знакомых песнях, если в режиме стерео включить только один динамик. И если эти песни, действительно, со стереоэффектами.

      Удалить
  14. Ошибочку нашел в таблице регистров. 0Ah 9:0 созданы для удобства. Прочитать биты 03h 15:6 возможно. Их и читал пока не наткнулся на упоминание в таблице. Дело в том, что удобно прочитать 0Ah и определится что настройка(поиск) станции завершен и чтоб повторно не читать частоту из этого же регистра уже прочитана частота. Выглядит так:

    #define RDA5807M_READCHAN_MASK 0x3FF

    uint16_t getFrequency () {
    uint16_t Reg0Ah = 0;
    do { // Ждем окончания настройки частоты
    Reg0Ah = getRegister(RDA5807M_REG_STATE);
    } while (~Reg0Ah & (1 << RDA5807M_BIT_STC));
    Reg0Ah &= RDA5807M_READCHAN_MASK;
    return (Reg0Ah + 870);
    }
    А вообще спасибо, уже из вашего примера библиотеку написал под себя.

    ОтветитьУдалить
    Ответы
    1. Собственно вот и библиотека. Пример внутри. Все очень просто. Добавил несколько полезных функций из таблицы.
      radio.setFrequency(1070);//Задаем частоту 107,0 // После этой строки радио начнет играть. Остальные настройки не обязательны
      radio.antena(2);// 0 -отключена 1 - земля, 2 - вход FMIN 3 - оба источника
      radio.bass(true);// Усиление баса
      radio.setStereo(false); // включаем моно режим. По умолчанию стерео
      radio.setVolume(15);//0..15
      radio.mute(true);
      radio.mute(false);
      radio.powerOFF(true);
      radio.powerOFF(false); // тоже что и radio.begin();
      radio.noisReduction(31); // от 0 до 31 шумодав
      radio.nextStation();
      radio.getRSSI();
      https://drive.google.com/file/d/1UC76ne0fgzVc9WwlQz_wA7zIapd_Tx6e/view?usp=sharing

      Удалить
    2. Добрый день!
      1. RDA5807M имеет три I2C адреса: 0x10h, 0x11h, 0x60h.
      2. Адрес 0x10h используется для последовательного доступа к регистрам. Стартовые адреса регистров для операций чтения и записи фиксированные. Для записи это 0x02h, для чтения - 0x0Ah. После каждой операции чтения/записи счетчик адреса инкрементируется.
      3. Запросив по I2C адресу 0x10h N байт, вы прочитаете N/2 регистров, начиная с регистра 0x0Ah. К младшим регистрам доступа на чтение нет, и дублирование битов CHAN в 0x0Ah - это не удобство, а необходимость в данном режиме работы.
      4. В режиме произвольного доступа, конечно, можно прочитать 0x03h.

      Удалить
    3. radio.noisReduction(31);

      шумоподавление на холостых частотах

      Удалить
  15. Спасибо за наиболее полное и внятное обсуждение регистров, помогло подключить объект к stm32F103C8T6 ;-) Отлично работает, сигнал захватывает, звук хороший. Интересно, что CHIP_ID стабильно читает 5004, а не 5804. Возможно, микросхема-китайская копия.

    ОтветитьУдалить
  16. Скажите по командам они одинаковы с RDA5807FP

    ОтветитьУдалить
    Ответы
    1. В плане базового функционала - да.
      Вообще все приёмники данной серии, чьи даташаты я изучал (RDA5807M / N / P / FP / SP и другие), одинаковы в управлении. Различия только в конкретном функционале: наличии или отсутствии RDS, дополнительных выводов GPIO, аудио интерфейса I2S, управлении по I2C/SPI и т.д. Соответственно, в одних даташитах описаны отвечающие за этот функционал биты, в других они пропущены или Reserved.

      Удалить
  17. Владимир, спасибо! Отличный обзор! Все внятно, достаточно обстоятельно и без лишней воды! Хочу собрать FM-тюнер на чипсете Si4703. Случайно нет у Вас Вашей замечательной и удобной для понимания сути работы программы-монитора для ПК, чтобы читать/записывать данные в/из регистра FM-тюнера Si4703? Или может быть выложите исходный программы RDA5807M.exe, чтобы адаптировать ее под Si4703? Кстати, Вы не проводили сравнительный анализ этих двух чипов RDA5807 vs Si4703?

    ОтветитьУдалить
    Ответы
    1. Добрый день!
      Спасибо за отзыв.
      Только вернулся из отпуска.
      Для Si4703 у меня пока ничего нет, но сам приёмник лежит и в планах есть помучить его, может и программу для ПК написать. На мой взгляд, это очень удобный вариант изучения регистров микросхемы.
      Сравнение с RDA5807M я не проводил. Но, когда смотрел даташит на Si4703, для себя отметил, что по описанию и управлению они очень похожи. И что документация у этих двух микросхем - небо и земля. У SiliconLabs всё подробно, с апнотами по программированию и RDS.
      Обязательно доберусь до этой микросхемы. Сам хотел сравнить их

      Удалить
    2. Исходники на Делфи. Если нужны, могу выложить

      Удалить
  18. Владимир, спасибо за ответ. Делфи к сожалению не знаю, боюсь не смогу переделать Ваш исходник. Но если когда-нибудь появится у Вас программа для Si4703, буду сильно признателен, если ей поделитесь. Я провел сравнительный технический анализ Si4703 vs RDA5807M. Из минусов Si4703 - у нее менее чувствительный приемник, чем у RDA5807. У RDA больше ручных настроек, которые позволяют настроить FM-тюнер и на автопоиск (по сигнал/шум) и на шумоподавление даже в зоне неуверенного приема, где Si4703 работает совсем плохо. Зато в зоне уверенного радиоприема Si4703 работает очень хорошо, не требует никаких ручных настроек (особо их там никаких и нет). Понравился у Si4703 и хороший RDS-приемник (читал в обзорах, что у Si более правильный чем у RDA алгоритм аппаратной обработки ошибки RDS-данных). Правда под обработку RDS использовал стандартную библиотеку.
    Пока не понял какой из этих чипсетов я выберу для своей задачи.

    Но с удовольствием почитаю и Ваши статьи про Si4703, если надумаете ее поковырять!
    Заранее спасибо!

    ОтветитьУдалить
  19. Тоись, если передача стерео, то как бы я ни ставил бит 13 регистра 0х02 в принудительное моно, в 10 бите регистра 0х0А все равно будет выставлен бит "Стерео"?

    ОтветитьУдалить
    Ответы
    1. Проверил: выставляем моно в 02h - индикатор стерео в 0Ah сбрасывается.

      Удалить
    2. 0Ah это флаги состояния а не регистр настройки.

      Удалить
    3. Этта хорошо. Спасибо.

      Удалить
  20. А подскажи еще, какой бит отвечает за бесшумную настройку? Чтоб не шипело между станциями.
    И еще вопрос, биты RSSI, которые показывают уровень принимаемого сигнала я ни разу не видел в них значения больше 32, где бы я ни находился, хоть под вышкой, у всех так? На даче в 30 км от вышки в прямой видимости, RSSI = 19, приём нормальный, дома на этой же радиостанции RSSI = 32, до вышки 5 км по прямой. Или я чот не так делаю?

    ОтветитьУдалить
    Ответы
    1. Можно DHIZ (0 бит в 02h) сбрасывать при поиске. А при завершении, когда флаг STC (14 бит в 0Ah) = 1, снова выставлять DHIZ в 1.

      RSSI здесь меня самого смущает. Я уж думал, не ошибка ли в даташитах и значение 5-, а не 6-битное. Но, по-моему, я видел значения больше 31, поэтому забил на этот момент.

      Надо будет проверить RSSI при приёме с SI4713. Интересно, что покажет.

      Удалить
    2. У меня (Санкт-Петербург) до радиовышки 31 км, но хорошая антенна (1 метр) RSSI показывает и больше 60

      Удалить
  21. Здравствуйте, Владимир.
    Подскажите, пожалуйста, как ввести в этот проект отображение уровня сигнала.

    ОтветитьУдалить
    Ответы
    1. Добрый день!
      Пример кода для получения RTTI есть в этой статье. А как добавить его в скетч - тут разные варианты. Наверное, самый простой - это добавить блок в конец функции loop. Блок типа if (millis() - LastRTTIUpdate > 5000) {...}. Внутри блока чтение RTTI, вывод его на дисплей в виде палок (посмотрите пример создания и вывода пользовательских символов: https://github.com/fdebrabander/Arduino-LiquidCrystal-I2C-library/blob/master/examples/CustomChars/CustomChars.ino) и в конце блока LastRTTIUpdate = millis();

      Удалить
  22. Скетчь не компилируется подсвечивает 322 строчку EEPROM.get(EEPROM_START_ADDRESS, reg02h);
    с ошибкой exit status 1
    'reg02h' was not declared in this scope
    Не подскажите в чем проблема?

    ОтветитьУдалить
  23. Извините сам виноват. Запустил скетч из архива. А там 2 файла после распаковки архива все скомпилировалась

    ОтветитьУдалить
  24. Что сделать чтобы энкодер KY-040 глючит

    ОтветитьУдалить
    Ответы
    1. Этот комментарий был удален автором.

      Удалить
    2. Павел28 сентября 2021 г., 17:49
      Что бы он глючил соедините его на скрутках и намажте их слюной. Еще сам энкодер можно намочить, в идеале паяльной кислотой.

      Удалить
    3. Добрый день!
      Иван, что у вас не так с энкрдером?

      Удалить
    4. Как будто дребезг контактов сам выйдет с меню или проскочит несколько пунктов

      Удалить
    5. Почему "как будто", это дребезг и есть. Вы раньше не работали с энкодерами? Можно попробовать сгладить дребезг: между выходом энкодера и землёй установите ёмкость 0,01..0,1мкф и подтяните вывод к питанию резистором 10к или больше - пробуйте, как будет лучше.
      Есть ещё аппаратные подавители дребезга. MC14490 хорош

      Удалить
  25. Владимир, а не растолкуете как все таки правильно вычислять частоту
    У вас код
    // Регистр 03h - выбор радиостанции
    // После сброса в регистре 03h значение по умолчанию - 0
    // Таким образом BAND = 00 (87..108МГц), STEP = 00 (100кГц). Оставим их как есть
    reg03h = (freq - 870) << RDA5807M_CHAN_SHIFT; // chan = (freq - band) / space
    setRegister(RDA5807M_REG_TUNING, reg03h | RDA5807M_FLG_TUNE);
    Т.е.фактически если я хочу установить частоту 102,3 она вычислится как (1023-870)/1=153.Т.е это число я должен записать в регистр 03 и установить бит TUNE
    У вас же вычисленная частота еще сдвигается влево на 6.Откуда взялось 6 и почему влево?Ведь сдвиг влево это фактически умножение на 2 в степени 6 ( 64)
    Буду благодарен за ответ

    ОтветитьУдалить
    Ответы
    1. BAND = 00 - означает, что начало диапазона 87МГц. К нему будет прибавляться значение, определяемое:
      -значением SPACE (в данном случае 100кГц)
      -и значением CHAN

      Если в CHAN записано 10, то это значение умножится на 100кГц (SPACE) и прибавится к 87МГц (начало диапазона). Это даст результирующую частоту 88МГц. И наоборот, зная желаемую частоту, не составит труда найти CHAN по известным SPACE и BAND.

      У меня в расчетах SPACE явно не участвует, т.к. он уже учтён переходом от 87 к 870 и от 107,3 к 1073. Если бы SPACE был = 50 или 200 кГц, то простой перестановкой десятичной точки дело бы не обошлось, и его значение использовалось бы в формуле явно.

      (1023-870)/1=153.Т.е это число я должен записать в регистр 03 - ДА

      Посмотрите еще раз описание регистра 03H, значение CHAN хранится в битах с 6 по 15. Поэтому и сдвигаем влево на 6

      PS. Обратил внимание, что местами для битов 0-1 регистра 03H я использовал название STEP - шаг. Смысл тот же, но по факту эти биты обозначены как SPACE. Поправлю.

      Удалить
  26. Этот комментарий был удален автором.

    ОтветитьУдалить
    Ответы
    1. Разобрались? регистры и переменная reg03h 16 бит

      Удалить
    2. Да, взял лист экселя, разрисовал там регистры для наглядности, подвигал и все понял
      могу выслать файлик- вдруг понадобится для статьи или для просвещения других чайников)
      Кстати RDA5807FP можно вообще без контроллера управлять.6 ножку на плюс3В, 1 ножка через кнопку и резистор на землю - сброс, кнопки к 16 и 15 - громкость + - , кнопки к 7 и 8 - каналы вперед-назад

      Удалить
  27. вот ссылка на файлик
    https://turb.cc/etzhd7viqp1j.html

    ОтветитьУдалить
  28. Здравствуйте, как можно отключить звук на пустой (шумовой) частоте на фм радио, что для этого использовать?

    Я использую FM-модуль RDA5807M.

    пример :

    vzssssssssssssssssssssssssssssssssssssss (белый шум)

    мне нужна помощь сейчас, как я могу уменьшить шум пустой радиостанции, есть ли код для предотвращения шума на пустых частотах?

    мне просто нужен код для отображения значений шума на экране и код для вырезания шума

    ОтветитьУдалить
    Ответы
    1. Кто-нибудь знает об этом?
      Я открывал тему на эту тему.

      https://electronics.stackexchange.com/questions/633747/what-is-the-method-to-mute-sound-during-seek-on-rda5807-fm#633747

      Удалить