Using a RMUP74055AD or TNF0170U722 device to build an analog UHF TV modulator with Arduino and LCD display.
Using an Arduino board with LCD and keypad shield a full featured modulator can be built. Arduino can be used to generate video too, but a single board can't use I2C and generate video in the same sketch. You'll need different boards if that's what you want to do.
I used for this project a Samsung RMUP74055AD modulator with MBS74T1AEF controller. Some searching reveals the same IC is also used by Tena TNF0170U722 modulator. Some datasheets will come up too, if you search for them. Anyway, these modulators are 5V devices.
RMUP74055AD UHF RF modulator |
RMUP74055AD and TNF0170U722 pinout |
RF Modulator connected to Arduino and SDR dongle |
LCD shield is placed over Arduino UNO board. The modulator connects directly to A4 and A5 pins of the Arduino (for I2C) and to the power pins. There are the only connections you need.
To make this work, we need some software. The modulator has 4 write registers and 1 read register, each of 1 byte.
I2C registers of MBS74T1AEF |
To set frequency, its value in kHz must be divided by 250 and the result written in the 12 frequency bits. The analog video frequency of an UHF channel is 8 * channel + 303.25. Therefore:
Frequency (kHz) = 8 * channel * 1000 + 303250
Frequency (bits) = Frequency (kHz) / 250
Frequency (bits) = 8 * channel * 4 + 1213
Frequency (bits) = 32 * channel + 1213
Then, upper sixth bits go to FM register: Frequency (bits) >> 6
Lower sixth bits go to left of FL register: [Frequency (bits) & 0x3F] << 2
An I2C transmission may contain, after the device address, either C1-C0 bytes, either FM-FL bytes, or all of them (C1-C0-FM-FL or FM-FL-C1-C0). Here is the code:
#include <LiquidCrystal.h> #include <Wire.h> #include <EEPROM.h> #define MOD_ADDRESS (0xCA >> 1) // 4.5MHz, 5.5MHz, 6MHz, 6.5MHz const byte mod_sound[4] = { 0x00, 0x08, 0x10, 0x18 }; byte eChannel, eSoundIdx, eOn, eTest; #define btnRIGHT 0 #define btnUP 1 #define btnDOWN 2 #define btnLEFT 3 #define btnSELECT 4 #define btnNONE 5 unsigned long keyTime = 0; LiquidCrystal lcd(8, 9, 4, 5, 6, 7); /****** Keypad functions *****/ byte readKeypad() { int adc = analogRead(A0); if (adc < 50) return btnRIGHT; if (adc < 250) return btnUP; if (adc < 450) return btnDOWN; if (adc < 650) return btnLEFT; if (adc < 850) return btnSELECT; return btnNONE; } void keypadAction() { unsigned long currentTime = millis(); if (currentTime - keyTime < 300) return; // less than 100 ms passed; quit byte key = readKeypad(); keyTime = currentTime; // store last call time // perform actions if (key == btnNONE) return; // no key is pressed; quit switch (key) { case btnRIGHT: { if (eChannel < 69) eChannel++; EEPROM.write(0, eChannel); if (eOn) mod_changeChannel(eChannel, eTest); break; } case btnLEFT: { if (eChannel > 21) eChannel--; EEPROM.write(0, eChannel); if (eOn) mod_changeChannel(eChannel, eTest); break; } case btnUP: { if (eOn) eOn = 0; else eOn = 1; EEPROM.write(2, eOn); if (eOn) { mod_start(eChannel, eSoundIdx, eTest); lcd.setCursor(0, 0); lcd.print(" << ON AIR >> "); } else { mod_sleep(); lcd.setCursor(0, 0); lcd.print(" << OFF >> "); } break; } case btnDOWN: { if (eTest) eTest = 0; else eTest = 1; if (eOn) mod_changeChannel(eChannel, eTest); break; } case btnSELECT: { eSoundIdx++; eSoundIdx &= 3; EEPROM.write(1, eSoundIdx); if (eOn) mod_setSoundCarrier(eSoundIdx); break; } } lcdUpdate(eChannel, eSoundIdx, eTest); delay(100); mod_getStatus(); } /***** Modulator functions *****/ void mod_setFreqBytes(byte &fm, byte &fl, byte channel, boolean testMode) { if ((channel < 21) || (channel > 69)) channel = 21; uint16_t freq = 32 * channel + 1213; fm = freq >> 6; if (testMode) fm |= 0x40; fl = (freq & 0x3F) << 2; } void mod_start(byte channel, byte soundIdx, boolean testMode) { byte fm, fl; mod_setFreqBytes(fm, fl, channel, testMode); if (soundIdx > 3) soundIdx = 1; Wire.beginTransmission(MOD_ADDRESS); Wire.write(0x88); Wire.write(0x40 | mod_sound[soundIdx]); Wire.write(fm); Wire.write(fl); Wire.endTransmission(); } void mod_sleep() { Wire.beginTransmission(MOD_ADDRESS); Wire.write(0xA8); Wire.write(0x20); Wire.endTransmission(); } void mod_changeChannel(byte channel, boolean testMode) { byte fm, fl; mod_setFreqBytes(fm, fl, channel, testMode); Wire.beginTransmission(MOD_ADDRESS); Wire.write(fm); Wire.write(fl); Wire.endTransmission(); } void mod_setSoundCarrier(byte idx) { if (idx > 3) idx = 1; Wire.beginTransmission(MOD_ADDRESS); Wire.write(0x88); Wire.write(0x40 | mod_sound[idx]); Wire.endTransmission(); } void mod_getStatus() { if (!eOn) return; byte s = 1; int c = Wire.requestFrom(MOD_ADDRESS, 1); if (c == 1) s = Wire.read(); if (s & 0x01) { lcd.setCursor(0, 0); lcd.print(" << ERROR !! >> "); } } void lcdUpdate(byte channel, byte soundIdx, boolean testMode) { lcd.setCursor(0, 1); lcd.print("CH"); lcd.print(channel); lcd.setCursor(6, 1); if (testMode) lcd.print("TEST"); else lcd.print(" "); lcd.setCursor(12, 1); switch (soundIdx) { case 0: lcd.print("4.5M"); break; case 1: lcd.print("5.5M"); break; case 2: lcd.print("6.0M"); break; case 3: lcd.print("6.5M"); break; } } void setup() { pinMode(10, INPUT); // for LCD shield Wire.begin(); lcd.begin(16, 2); lcd.print(" TV Modulator "); delay(1000); lcd.clear(); // get/set initial EEPROM parameters eChannel = EEPROM.read(0); if ((eChannel < 21) || (eChannel > 69)) eChannel = 21; EEPROM.write(0, eChannel); eSoundIdx = EEPROM.read(1); if (eSoundIdx > 3) eSoundIdx = 1; EEPROM.write(1, eSoundIdx); eTest = 0; eOn = EEPROM.read(2); if (eOn) { lcd.setCursor(0, 0); lcd.print(" << ON AIR >> "); mod_start(eChannel, eSoundIdx, eTest); } else { lcd.setCursor(0, 0); lcd.print(" << OFF >> "); } lcdUpdate(eChannel, eSoundIdx, eTest); } void loop() { keypadAction(); }The software makes use of integrated EEPROM of ATmega MCU to store modulator parameters like on/off state, frequency and sound information. These are restored after reset. Using LEFT/RIGHT keys, output channel can be changed from 21 to 69 UHF. SELECT key cycles through sound carrier frequencies which are displayed on the bottom line, in the right of LCD screen. UP key turns modulator on and off and DOWN key enables/disables test pattern. Enabling the test pattern is not stored in EEPROM, therefor after reset, modulator will start, but not in test mode.
In a future post I will build an analog TV receiver using a tuner with included demodulator, an Arduino board to control it and an USB video capture card to send video to PC.
Great project! Thank you man!
ReplyDeleteHave you some example of demodulator digital TV using Realtek RTL2832U + Arduino?
I guess an Arduino doesn't have enough processing power for capturing and performing demodulation on samples from RTL2832U. If the dongle is used for digital TV, a transport stream is sent over USB port. This is a high bitrate stream which can't be decoded by a small microcontroller.
DeleteThanks. Also very nice worked with fm-mc016a4f5g modulator with ase74t1aef chip inside.
ReplyDeleteThanks. But i want to ask something. If we only write this commands (i want to see only test pattern) it doesnt work. Can you help?
ReplyDelete#include
void setup() {
Wire.begin();
Serial.begin(9600);
}
void loop() {
Wire.beginTransmission(0xCA);
Wire.write(B10001000);
Wire.write(B01001000);
Wire.write(B01100100);
Wire.write(B11110100);
Wire.endTransmission();
}
Your commands are in loop. It is enough to send them to the modulator only once after power up, not continusously, without any delay. You can move them into setup function. If it still doesn't work double check the validity of the bits you sent.
DeleteThansk for your answer. I'm trying it now
Delete#include
Deletevoid setup() {
Wire.begin();
Wire.beginTransmission(0xCA);
Wire.write(0x84);
Wire.write(0x48);
Wire.write(0x64);
Wire.write(0xF4);
Wire.endTransmission();
}
void loop() {
while(1);
}
I changed my codes as you said. But it still not worked. I try a new modulator one. Its same too. Where is my mistakes? I dont know :(
ATT=0 , OSC=1, SFD1=0, SFD0=1, PS=0, SO=0, PWC=0, TPEN=1,
Frequency bits are, N11 ==> 100100111101 CH36 = 591,25Mhz
I checked Arduino's sda, scl pins a basic code. It's not damaged. It still work.
At hardware,
MB+ and B+ are 5 volts
Please help..
Hi, again. A little time ago. I tried Nick Gammon's "i2c scanner" program.
DeleteI2C scanner. Scanning ...
Found address: 101 (0x65)
Done.
Found 1 device(s).
This program said that my chip adress is 0x65, but our chip adress is 0xCA isnt it?
Yes, finally I finished. I see two vertical lines on tv. But how was it?
ReplyDeleteMy chip adress is not 0xCA. Chip adress is 0x65.
0xCA is full chip address, with R/W bit included. 0x65 is actually 0xCA >> 1. This is the 7-bit address without R/W bit and this is what you need to specify to Wire.beginTransmission. See the definition #define MOD_ADDRESS (0xCA >> 1) which means MOD_ADDRESS is 0x65.
DeleteThanks a lot..
ReplyDeleteGreat work. Can you do something similar with Satellite / DVB-S or DVB-S2 tuner. There is very little information about it.
ReplyDeleteI controlled satellite tuners too: https://www.onetransistor.eu/2016/05/control-s7vz6306-tuner-ix2470.html. This kind of tuners can be used to build ADS-B receivers.
DeleteHi, Could You provide me any assistance? I have almost the same RF Modulator (RMUP74055DD) but I cannot accomplish what You did. I'm unable to see the address of the modulator in I2C Scanner and while using Your code it is unable to go "on air". It freezes ... and when I disconnect modulator then I see error instead. I'm unable to make I2C connection with the rf modulator What can I check ? I've tried with pullup resistors but no change.
ReplyDeleteMake sure the modulator is powered (there are two power pins for +5V) and try again with I2C scanner sketch. Do not connect pull up resistors. Make sure ground is connected to metal case. Try to switch SDA and SCL, maybe you got them wrong. If it still does not work, maybe there is something wrong with your modulator.
DeleteI did all of that. I'm also powering the modulator from external 5V source of course connecting all grounds. While powering from Arduino 5v pin voltage was to much below 5V. Modulator is ok for sure. It was working fine just a few hours ago in VCR :) Moreover this is the second one I'm trying. I'm powering module as You showed . I have no idea what to check more.
DeleteIt could be different pin configuration between 74055AD and 74055DD. If it draws so much current that Arduino can't supply 5V, then something seems to be wrong.
DeleteWhen it was still in VCR I checked all pins. +5V were in the same place as in AD
DeleteI have one more (and last) question. Are You familiar with python or perl? RF modulator will be used with Raspberry Pi and I don't want to add any extra devices (I'm trying to make it as small as possible) so I'd like to turn the modulator on while booting raspberry. That is why I need to transform arduino code to perl/python. I just need to send a command to switch on, on one of the channels and then switch off. For test purpose I would also need to run test signal on modulator. Can You help me with that? I just need to know what to sent? Thanks in advance
DeleteI'm not familiar with specific Raspberry Pi programming. I don't know whether you can send I2C data while Linux kernel is booting. I guess you can use bash scripting too and make a script that will be launched after startup. Have a look at mod_start function that sends all bytes required to configure and start modulator. You'll have to write your code based on it.
DeletePlease explain me a bit "i2c bit map" in modulator.
DeleteWire.beginTransmission(MOD_ADDRESS);
Wire.write(0x88);
Wire.write(0x40 | mod_sound[soundIdx]);
Wire.write(fm);
Wire.write(fl);
Wire.endTransmission();
Are there any registers and every time I'm sending data I need to set register or offset or You just send bytes one by one to 0x65 address?
Have a look at the registers table above. After the device address you need to send 2 or 4 bytes of data. These bytes are either (C1, C0) or (FM, FL) or (C1, C0, FM, FL) or (FM, FL, C1, C0).
DeleteFound the proper pinout for RMUP74055DD
ReplyDeletepin1 -> 5V
pin2 -> Audio in
pin3 -> SDA
pin4 -> 5V
pin5 -> SCL
pin6 -> Video in
Now everything works great!
That's great! Only voltage supply pins are the same. A/V and I2C are totally different.
DeleteHi
ReplyDeleteThank you for your post.
Is it possible to run the modulator without the micro-controller on fixed settings for example?
Regards.
Unfortunately this type of modulator needs a microcontroller to set it up after power on.
DeleteNice. It works. Thanks
ReplyDeleteThis comment has been removed by a blog administrator.
ReplyDeleteRMUP74055AD is it the same as with V5151MUP????
ReplyDeleteOpen the metal case and see if it contains MBS74T1AEF IC. If yes, then they are the same.
DeleteHello, congratulations! I have a question about the same modulator, RMUP74055AD. I have my father's old CRT TV which has no scart or rca input, that's why found this module and try to make "DIY Scart/RCA to RF Project". However I couldn't determine whether SDA and SCL pins are used. Which pins should I use? Best regards
ReplyDeleteYes, SCL and SDA pins are used. Both must be wired to a microcontroller which has to set the frequency and other parameters of the modulator. If you are using Arduino, connect SDA to A4 and SCL to A5 pin, then use my sketch to control the modulator.
DeleteBonjour.Votre montage fonctionne très bien.Merci pour votre description
ReplyDeleteI tried to do it on raspberry pi 4 but it doesn't work (
ReplyDeletedon't think that I used the Arduino code, I translated it for Raspberry, but I always got the answer 200 255 255 255
DeleteIt seems to me that the addressing table for the pictures is mixed up? I studied your code and implemented mine on putana via a USB-CH341A programmer! It allows you to use I2C! The table shows that the addressing table is CA,C0,C1,FL,FM ! But in your code, the package goes like this CA,C1,C0,FM,FL ! Anyway, everything worked for me as in your code! Do you have documentation for the block or chip? Where did you get the information on him? I will be very grateful for the original documentation!
ReplyDelete