Use tuners from PCI TV cards for video and FM radio reception with Arduino or other I2C interface
Since I had some old TV tuner cards that were no longer compatible with my PC or had poor performance, I decided to take the tuners out of them. To my surprise, different tuners from different manufacturers looked pretty much the same on the inside. All of them used the same integrated circuits. The tuners I found are actually complete receivers, with included demodulator. This means you can get analog audio and video straight from the module pins. There are also modules with FM support, with stereo decoder.
In the photo below, you can see two tuners. The top one has FM radio support. You can see that it's similar to the other, but the rightmost compartment has some additional filters for FM IF.
Tuners from TV cards |
Functional modules of the TV tuner |
Pinout of TV tuner module TCL2002MB-33F |
Basic connection requires SCL and SDA to be connected to an I2C interface of a microcontroller, CVBS and AF to be connected to a capture card inputs via RCA jacks. All that's left is to power up the module.
According to datasheet, I2C address of TSA5523 is 0xC2 is AS pin is left floating. The IC has 4 write registers and 1 read register. Here is an overview.
Overview of TSA5523 registers (refer to datasheet) |
Read register contains some important bits. FL is 1 when tuner is locked to frequency. I1 reads mono/stereo status (1 means stereo). A2, A1 and A0 represent ADC readout.
Let's write some Arduino compatible code. First, for tuning TV channels. Control byte is set to 0xCA. This means CP=1 (higher chargepump current, recommended in TV mode) and RSA=0; RSB=1 (resulting in 31.25kHz frequency step). Ports bytes is set according to the band in which the frequency fits. To set frequency of local oscillator, IF is added to receiving frequency and the sum is divided by tuning step (31.25 in this configuration). The number 1245 in freq_khz formula comes from IF frequency 38900 divided by 31.25.
#define TSA5523_ADDR (0xC2 >> 1) // tuner registers byte db1, db2, cb, pb; void tsa5523_setTVFrequency(uint32_t freq_khz) { // band bits if (freq_khz < 168000) pb = 0xA0; // band A, VHF-Lo else if (freq_khz < 448000) pb = 0x90; // band B, VHF-Hi else pb = 0x30; // band C, UHF // control cb = 0xCA; freq_khz = freq_khz / 31.25 + 1245; db1 = (freq_khz >> 8) & 0x7F; db2 = freq_khz & 0xFF; Wire.beginTransmission(TSA5523_ADDR); Wire.write(db1); Wire.write(db2); Wire.write(cb); Wire.write(pb); Wire.endTransmission(); }In FM mode, we're always in band A. Chargepump can use lower current to improve SNR (set CP=0) and tuning step needs to be 50kHz to cover all channels. RSA and RSB are both 0. Some changes are required for ports byte. Band A (which includes FM) requires P7 and P5 to be set. P2 also needs to be 1. IF frequency is 10.7MHz now. The sum is divided by 50 (tuning step).
#define TSA5523_ADDR (0xC2 >> 1) // tuner registers byte db1, db2, cb, pb; void tsa5523_setFMFrequency(uint32_t freq_khz) { pb = 0xa4; // band A only cb = 0x88; freq_khz += 10700; freq_khz /= 50; db1 = (freq_khz >> 8) & 0x7F; db2 = freq_khz & 0xFF; Wire.beginTransmission(TSA5523_ADDR); Wire.write(db1); Wire.write(db2); Wire.write(cb); Wire.write(pb); Wire.endTransmission(); }To get tuner status, read one byte from the read address. If this status byte (SB) has bit 6 set, tuner is locked (SB & 0x40). Then you can assess bit 4 for stereo (SB & 0x10) and compute ADC readout (SB & 0x07). Note that ADC reads what you configured it with the ports byte.
Due to the capability of tuning to frequencies between 45 to 860 MHz, programming a button based user interface for Arduino is rather difficult. A future post will show a programming example of Windows application that uses CH341A to control this tuner.
Thank you very much, Cornelius. This was very usefull.
ReplyDeletemuch appreciated,just what iwas looking for!
ReplyDeleteHi;
ReplyDeleteI have tried your project and it is working, thank you. So how can we listen to narrow bands?
It's difficult, but not impossible to listen to narrow band broadcasts or any other signals. You have to build a detector (demodulator) and connect its input at the output of the SAW filter. Obviously, the detector must be able to demodulate the frequency that passes through the filter.
DeleteThanks for sharing! Can you help me with a Samsung tuner that has an SN761672A?
ReplyDeleteI need to address it and control it. I have got some help but I'm still in the dark!
By reading the datasheet it seems to me that SN761672A and TSA5523 are similar regarding I2C instructions. What kind of issues did you encounter?
DeleteOh how kind of you to reply so soon! Believe me I downloaded a lot of examples and software related to tuners and arduino, I am immersed almost since 3 months. but I'm stuck in nowhere. Recently, I downloaded almost all FM1612 tuner projects and inos to learn from and find a starting point.
Deletejust to learn how and where I can access the frequency compute it and display it. Really, your two examples summarize a lot but it shows a huge knowledge behind. I need to grasp the frequency at the outset and then make expansions, I'm stuck here!
Today I wrote a demo code based on codes from FM1612(library, master, h etc) but once it came to set frequency or (Tuner.set(frq)) I got errors and the whole code became worthless. I am trying to get the tuner to receive (initialise)and to read the signal and from here increment/ decrement or search and display.
Best regards
p.s I am a hobbyist great electronic knowledge but in math and calculation a dummy!
/*Please help me tweak this code to be able to tune to 1 station*/
ReplyDelete#include
#include
LiquidCrystal_I2C lcd(0x27, 16, 2);
#define SN761672A_ADDR (0xC2 >> 1)
// tuner registers
byte DB1, DB2, CB, BB;
uint32_t freq_kHz;
void setup()
{
Wire.begin();
lcd.backlight();
lcd.begin(16, 2);
//Tuner_init();
SN761672A_setTVFrequency(985000);
lcd.setCursor(0,0);
lcd.print("Station:");
}
void loop()
{
lcd.setCursor(8,0);
lcd.print(freq_kHz);
void SN761672A_setTVFrequency(uint32_t freq_kHz);
}
void SN761672A_setTVFrequency(uint32_t freq_kHz)
{
// band bits
if (freq_kHz < 168000) BB = 0x00; // band A, VHF-Lo
else if (freq_kHz < 448000) BB = 0x01; // band B, VHF-Hi
else BB = 0x08; // band C, UHF
// control
CB = 0xCA;//11001010
freq_kHz = freq_kHz / 31.25 + 1245;
DB1 = (freq_kHz >> 8) & 0x1F;
DB2 = freq_kHz & 0xFF;
Wire.beginTransmission(SN761672A_ADDR);
Wire.write(DB1);
Wire.write(DB2);
Wire.write(CB);
Wire.write(BB);
Wire.endTransmission();
}