Се­год­ня мы раз­берем, как из мик­росхе­мы RDA5807 выжать мак­симум: под­клю­чим ее к кон­трол­леру STM32, добавим дис­плей, реали­зуем управле­ние энко­дером и научим­ся при­нимать RDS-дан­ные с эфи­ра. Пог­рузим­ся в струк­туру FM-сиг­нала и соберем свой пол­ноцен­ный при­емник, который будет показы­вать на дис­плее наз­вания стан­ций и ради­отекст.

Пом­ню, в далеком 2006 году я впер­вые уви­дел маг­нитолу, отоб­ража­ющую наз­вание при­нима­емой стан­ции. Тог­да меня это, пря­мо ска­жем, оза­дачи­ло. В то вре­мя вер­шиной моего ради­оэлек­трон­ного искусс­тва был при­емник на К174ХА34 (TDA7021), а там такого и близ­ко не было. И если прин­цип отоб­ражения час­тоты я мог понять — типа циф­ровая шка­ла или син­тезатор, — то отоб­ражение наз­вания прос­то сно­сило кры­шу. Сегод­ня мы обсу­дим, как это дела­ется, и соберем акту­аль­ный вещатель­ный при­емник.

Про ра­диоп­рием­ник на RDA5807 мы говори­ли и тог­да исполь­зовали недоку­мен­тирован­ный режим stand alone, в этот раз мы задей­ству­ем все воз­можнос­ти этой замеча­тель­ной мик­росхе­мы.

 

Самый низ

Рас­смот­рим струк­туру демоду­лиро­ван­ного FM-сиг­нала. Сна­чала идет ауди­осиг­нал сум­мы двух сте­реока­налов, поэто­му если такой сиг­нал подать пря­мо на науш­ники, то в них будет впол­не себе слы­шен монофо­ничес­кий звук, а все про­чие сиг­налы обре­жут­ся АЧХ науш­ников и нашего уха. Такое положе­ние дел обес­печива­ет обратную сов­мести­мость с уста­рев­шими типами при­емни­ков, вплоть до свер­хре­гене­рато­ра. Даль­ше идет пилот: тон 19 кГц для вос­ста­нов­ления под­несущих сте­реосиг­нала (38 кГц) и RDS (57 кГц), потом DSB (ампли­туд­ная модуля­ция с подав­ленной под­несущей), модули­рован­ный сиг­нал с раз­ностью двух сте­реока­налов. Выше уже идет RDS-сиг­нал, это тоже DSB-модули­рован­ный сиг­нал.

Час­тоты эти не слу­чай­ны: 38 кГц — это вто­рая гар­моника пилот‑тона, а 57 кГц — третья. Даль­ше идут сиг­налы RDS2, но о них мы погово­рим в дру­гой раз, все рав­но наш чип работать с RDS2 не уме­ет.

Структура демодулированного FM-сигнала
Струк­тура демоду­лиро­ван­ного FM-сиг­нала

Собс­твен­но, под­несущая RDS-сиг­нала про­моду­лиро­вана диф­ферен­циаль­ным ман­честер­ским кодом, который и содер­жит циф­ровые дан­ные.

Дифференциальный манчестерский код
Диф­ферен­циаль­ный ман­честер­ский код

Дан­ные переда­ются груп­пами по 104 бита с бит­рей­том 1187,5 бит/c (57 кГц / 48), каж­дая груп­па содер­жит 26 бит и сос­тоит из 16-бит­ного сло­ва и 10 бит кон­троль­ной сум­мы.

Структура RDS
Струк­тура RDS

Но эта низ­коуров­невая жесть ско­рее для обще­го раз­вития, так как всем этим за нас зай­мет­ся мик­росхе­ма RDA5807, которая дает в наше рас­поряже­ние сра­зу четыре сло­ва RDS-груп­пы, и это опре­делен­но к луч­шему. На этом пока отло­жим RDS и погово­рим о железе.

 

Схема и конструкция

Со схе­мой тут, конеч­но, пос­ложнее, чем в режиме stand alone, но все рав­но она дос­таточ­но прос­та.

Схема
Схе­ма

Нам, само собой, понадо­бит­ся кон­трол­лер — я оста­новил­ся на STM32F103C8T6, вер­нее даже на пла­те BluePill. Еще понадо­бит­ся дис­плей TFT 128 × 128 ST7735. Модуля на RDA5807 у меня не было, впро­чем, «модуль» — это гром­ко ска­зано: там из обве­са кварц и пара кон­деров, и все это доб­ро лег­ко раз­меща­ется на пла­те адап­тера SOIC в DIP. Ну и в завер­шение нужен энко­дер для управле­ния. Все прос­то до пре­дела: на SPI-шину веша­ем дис­плей, на I2C веша­ем RDA5807, под­клю­чаем энко­дер, готово. В сбо­ре получа­ется как‑то так.

Конструкция
Конс­трук­ция

Из под­водных кам­ней тут могут воз­никнуть раз­ве что навод­ки от шины экра­на на антенну, но это про­явля­ется толь­ко при сла­бом сиг­нале стан­ции и лечит­ся под­клю­чени­ем антенны через кусок коак­сиаль­ного кабеля, что­бы отод­винуть ее от шины. Мощ­ности зву­ковых выходов RDA5807 для науш­ников хва­тает с боль­шим запасом. Одна­ко если захочет­ся под­клю­чить динами­ки, то советую обра­тить вни­мание на сте­реоуси­литель TDA4863, так как на нем удоб­но соб­рать УЗЧ с уси­лени­ем по нап­ряжению 1, что поз­волит не ста­вить физичес­кий регуля­тор гром­кости, а огра­ничить­ся прог­рам­мным.

Опциональный усилитель на TDA4863
Оп­циональ­ный уси­литель на TDA4863
 

Прошивка

Есть очень хорошая статья в двух час­тях, пос­вящен­ная работе с RDA5807: часть 1, часть 2. Но пов­торять мы ее не будем, ско­рее ста­нем ори­енти­ровать­ся на нее, как на один из воз­можных кон­цептов. Да и железо у меня сов­сем дру­гое.

Ини­циали­зация пор­тов — это три­виаль­но, энко­дер на пре­рыва­ниях и работу с дис­пле­ем мы здесь, пожалуй, опус­тим. А вот работа с самой RDA5807 — это инте­рес­но. Исполь­зовать будем, как и рань­ше, libopenCM3, но перепи­сывать монс­тру­озную биб­лиоте­ку для «Арду­ино» мы не ста­нем, а реали­зуем толь­ко необ­ходимые нам фун­кции.

www

Все исходни­ки и бинар­ники про­екта мож­но най­ти на GitHub.

 

Чтение и запись в регистры

Пос­коль­ку чип естес­твен­ным обра­зом управля­ется через регис­тры, то без фун­кции чте­ния и записи в эти самые регис­тры и говорить не о чем. В дос­тупных даташи­тах крат­ко опи­сан спо­соб ком­муника­ции, пред­полага­ющий чте­ние и запись регис­тров стро­го груп­пами. При­чем чте­ние идет с адре­са 0Ah, запись с адре­са 02h, и читать их мож­но вплоть до адре­са 3Ah. Для это­го надо сту­чать­ся по адре­су 0x10 на шине I2C.

Са­мое забав­ное, что в ману­але опи­саны толь­ко 16 регис­тров, при­чем начиная с 02h. Пыт­ливые ради­олю­бите­ли раз­нюхали еще один спо­соб ком­муника­ции с ран­домным дос­тупом, там уже мож­но читать и записы­вать любой регистр. Такой режим реали­зует­ся, если сту­чать­ся по адре­су 0x11 на шине I2C. Любопыт­но, что этот спо­соб показан лишь на кар­тинке в ману­але, при­чем на китай­ском язы­ке, а в тек­сте об этом ни сло­ва. Китай­цы такое любят.

Та самая картинка
Та самая кар­тинка

Итак, в чипе 16 шес­тнад­цатибит­ных регис­тров. Реали­зуем запись и чте­ние ран­домно­го регис­тра, а сверх того — пос­ледова­тель­ное чте­ние шес­ти регис­тров, но об этом поз­же. Пер­вое удоб­но для управле­ния чипом.

#define RDA5807I2C I2C1
#define RDA5807ADDR_RANDOMACSESS 0x11
uint16_t RDA5807_read_random_register(uint8_t registr){
uint8_t temp[2];
i2c_transfer7(RDA5807I2C,RDA5807ADDR_RANDOMACSESS,&registr,1,temp,2);
return (uint16_t)(temp[0]<<8|temp[1]);
}
void RDA5807_write_random_register(uint8_t registr, uint16_t data){
uint8_t temp[3]={registr,(uint8_t)(data>>8),(uint8_t)(data&0xff)};
i2c_transfer7(RDA5807I2C,RDA5807ADDR_RANDOMACSESS,temp,3,0,0);
}

Те­перь мы можем читать и писать регис­тры, мож­но запус­кать чип!

 

Инициализация

Тут все про­ще, чем кажет­ся: дос­таточ­но выс­тавить нуж­ные биты в регис­тре 02h, при­чем если хочет­ся быс­тро про­верить работос­пособ­ность схе­мы, то дос­таточ­но записать в регистр 0xC101.

...
RDA5807_write_random_register((0x02, 0xC101); // set ENABLE, DHIZ, DMUTE, SEEK
...
Регистр 02h RDA5807
Ре­гистр 02h RDA5807

Это вклю­чит чип, отклю­чит MUTE, вклю­чит выход­ной кас­кад ауди­оуси­лите­ля и запус­тит авто­поиск стан­ции. Через пару секунд радио запо­ет. Поп­робова­ли, про­вери­ли, теперь мож­но и по‑серь­езно­му сде­лать. Авто­поиск я исполь­зовать не буду, мне нра­вит­ся кру­тить руч­ки, для того я и под­клю­чил энко­дер. Поэто­му бит SEEK выс­тавлять не будем, зато добавим баса (бит BASS), вклю­чим аль­тер­натив­ный метод демоду­ляции (бит NEW_METHOD) и вклю­чим RDS (бит RDS_EN).

Ес­ли верить ману­алу, аль­тер­натив­ный метод демоду­ляции нем­ного под­нимет чувс­тви­тель­ность. Заод­но выберем рас­ширен­ный диапа­зон 76–108 МГц. Боль­шого смыс­ла в этом нет, так как у нас нет стан­ций на учас­тке 76–87 МГц, но будем счи­тать это заделом на будущее.

Ну и соот­ветс­твен­но, нам нуж­ны фун­кции для управле­ния час­тотой и гром­костью, что­бы два раза не вста­вать. За гром­кость отве­чают млад­шие 8 бит регис­тра 05h. Что­бы не затирать осталь­ные дан­ные в регис­тре перед уста­нов­кой гром­кости, счи­тыва­ем ста­рое зна­чение, бла­го наш ран­домный дос­туп к регис­трам это поз­воля­ет. За час­тоту отве­чает регистр 08h, час­тота ука­зыва­ется в килогер­цах отно­ситель­но начала диапа­зона, в нашем слу­чае это 76 000 кГц.

#define RDA5807_DHIZ 1<<15
#define RDA5807_DMUTE 1<<14
#define RDA5807_BASS 1<<12
#define RDA5807_ENABLE 1
#define RDA5807_SEEK 1<<8
#define RDA5807_RDS_EN 1<<3
#define RDA5807_NEW_METHOD 1<<2
#define RDA5807_WWBAND 0b10<<2
void RDA5807_init(void){
uint16_t temp=RDA5807_ENABLE|RDA5807_BASS|RDA5807_DHIZ|RDA5807_DMUTE|RDA5807_RDS_EN|RDA5807_NEW_METHOD;
RDA5807_write_random_register(0x2, temp);
temp=RDA5807_read_random_register(0x3);
// Регистр 3h, биты 2, 3, выбирается диапазон 76–108 МГц
temp|=RDA5807_WWBAND;
RDA5807_write_random_register(0x3, temp);
// Включаем direct freq
uint16_t temp=RDA5807_read_random_register(0x7);
temp|=1;
RDA5807_write_random_register(0x7, temp);
//RDA5807_set_freq(89100);
}
void RDA5807_set_freq(uint32_t freq){
temp=(uint16_t)(freq-76000);
RDA5807_write_random_register(0x8, temp);
}
void RDA5807_set_vol(uint8_t vol){
if(vol>15)vol=15;
uint16_t temp=RDA5807_read_random_register(0x5);
temp=temp&0xFFF0|(uint16_t)vol;
RDA5807_write_random_register(0x5, temp);
}
 

RDS

Как мы уже говори­ли, декодер RDS-сиг­нала в RDA5807 аппа­рат­ный, поэто­му от нас тре­бует­ся толь­ко ана­лизи­ровать с некото­рой пери­одич­ностью содер­жимое бло­ков A—D. Одна­ко здесь есть один нюанс: это радио, и качес­тво сиг­нала обыч­но не очень, поэто­му край­не полез­но счи­тать биты, ука­зыва­ющие на количес­тво оши­бок в бло­ках A—D. Я пред­лагаю уста­новить тре­бова­ние к пол­ному отсутс­твию оши­бок, а сверх того при­менить валида­цию пакетов, как в упо­мяну­той выше статье.

Ошиб­ки все рав­но про­лезут, но это хотя бы будет не так раз­дра­жать. Для удобс­тва обра­бот­ки, навер­ное, име­ет смысл соеди­нить четыре сло­ва бло­ка в одну 64-бит­ную перемен­ную, тог­да битовые опе­рации силь­но упростят­ся и будут наг­ляднее, но эта идея приш­ла мне в голову, ког­да все уже было реали­зова­но и отла­жено, и переде­лывать я не стал.

Продолжение доступно только участникам

Материалы из последних выпусков становятся доступны по отдельности только через два месяца после публикации. Чтобы продолжить чтение, необходимо стать участником сообщества «Xakep.ru».

Присоединяйся к сообществу «Xakep.ru»!

Членство в сообществе в течение указанного срока откроет тебе доступ ко ВСЕМ материалам «Хакера», позволит скачивать выпуски в PDF, отключит рекламу на сайте и увеличит личную накопительную скидку! Подробнее

  • Подпишись на наc в Telegram!

    Только важные новости и лучшие статьи

    Подписаться

  • Подписаться
    Уведомить о
    0 комментариев
    Старые
    Новые Популярные
    Межтекстовые Отзывы
    Посмотреть все комментарии