51單片機tea5767收音機 紅外遙控 自動搜台 存台 DIY


先看效果圖: 顯示 頻道CH , 頻率 100.0Mhz

 歡迎信息,1602 內置日文平假名, 正好用來顯示博主名稱。

焊接前,已經萬能面包板上試驗成功。

 

 焊接完成以后,1602 的D0 - D7 接到了 P1 上面,為了布線簡單,這里是接反的 P1.0 => D7 .. 實際寫入 讀取數據時,還需要轉換高低位。

 背面走線圖

 

元件清單:stc89c52,  lcd1602,  tea5767完整版 , at24c04 , DS18B20 (未實現功能,打算后期在加一個 RTC 芯片,和 GPS 做 精准的電子鍾)

下面是一些元件介紹:

紅外一體化接收頭,常見有2種。

 

lcd1602 顯示日文假名字符對應表

高位 + 低位,組成一個 unsigned char 放入就能顯示出來了。

 

下面講源程序:

 main.c 主程序中 初始化了 LCD1602 , TEA5767 , 初始化紅外線中斷 , 對按鍵進行掃描。 1 #include <reg52.h>

 2 #include "tea5767.h"
 3 #include "delay.h"
 4 #include "lcd1602.h"
 5 #include "IR.h"
 6 //K1:上一台 K2:下一台 K3:從低向高搜索台
 9 sbit K1 = P2 ^ 1;
10 sbit K2 = P2 ^ 4;
11 sbit K3 = P2 ^ 7;
12 
13 //當前頻道號 從 0 開始
14 unsigned char ch = 0;
15 
16 void main()
17 {
18     
19     //初始化 1602lcd
20     lcd1602_init();
21     
22     //初始化 串口
23     init_uart();
24     
25     //初始化紅外遙控接口
26     init_IR();
27     
28     //主函數中掃描按鍵
29     while(1)
30     {
31         //上一台
32         if(0 == K1)
33         {
34             //去抖動
35             delayms(100);
36             if(0 == K1)
37             {
38                 set_ch(--ch);
39             }
40         }
41         //下一台
42         if(0 == K2)
43         {
44             //去抖動
45             delayms(100);
46             if(0 == K2)
47             {
48                 set_ch(++ch);
49             }
50         }
51         //自動搜索
52         if(0 == K3)
53         {
54             //去抖動
55             delayms(100);
56             if(0 == K3)
57             {
58                 tea5767_tiny_auto_search();
59             }
60         }
61     }
62 }
63 
64 void IR_int() interrupt 0
65 {
66     IR_CODE ir_code;
67     EX0 = 0;//處理過程中 關中斷
68     ir_code = IR_recv();
69     if(ir_code.ir_code)
70     {
73         /**
74          * custom:0xCC1D code:0x05FA 上一台
75          * custom:0xCC1D code:0x06F9 下一台
76          * custom:0xCC1D code:0x01FE 靜音
77          */
78         //比較機器碼
79         if(ir_code.custom_height == 0xcc && ir_code.custom_lower == 0x1d)
80         {
81             //比較功能碼
82             switch(ir_code.ir_code)
83             {
84                 case 0x5: set_ch(++ch); break;
85                 case 0x6: set_ch(--ch); break;
86                 case 0x1: tea5767_mute(ch); break;
87             }
88             //發送到串口,顯示出編碼,可用於記錄編碼,做學習型遙控器

92 send_code(ir_code); 93 } 94 } 95 EX0 = 1;//處理結束后 開中斷 96 }

 

lcd1602.h

 

 1 #ifndef __LCD1602__
 2 #define __LCD1602__
 3 #include <reg52.h>
 4 #include "delay.h"
 9 #define DATA P1
10 sbit RS = P3 ^ 7;//數據 H 命令 L
11 sbit RW = P3 ^ 4;//讀 H 寫 L
12 sbit E  = P3 ^ 3;//高電平 H 使能
13 
14 /**19  * E = 1 后需要等一小段時間, 在手冊中並沒有說明 - 
20  */
21 void lcd1602_init();
22 void lcd1602_clear();
23 char lcd1602_is_busy();
24 void lcd1602_write_cmd(unsigned char cmd);
25 void lcd1602_write_data(unsigned char dat);
26 void lcd1602_pos(unsigned char pos);
27 unsigned char lcd1602_reverse(unsigned char dat);
28 #endif

 

lcd1602.c

  1 #include "lcd1602.h"
  2 
  3 void lcd1602_init()
  4 {
  5     //初始化 復位 lcd1602
  6     lcd1602_write_cmd(0x38);//設置顯示模式 指令碼 00111000 => 0x38
  7     delayms(1);
  8     lcd1602_write_cmd(0x0c);//開顯示 不顯示光標 不閃爍
  9     delayms(1);
 10     lcd1602_write_cmd(0x06);//光標設置 寫字符后指針加一
 11     delayms(1);
 12     lcd1602_write_cmd(0x01);//光標清0 指針清0
 13     delayms(1);
 14     //設置初始位置為 0
 15     lcd1602_pos(0);
 16     //打印歡迎信息 日文假名
 17     /**
 18      * こんにちわ   ねじ
 20      * こ 高4位是 b 低4位是 a 合並就是 0xba
 21      */
 22     lcd1602_write_data(0xba);
 23     lcd1602_write_data(0xdd);
 24     lcd1602_write_data(0xc6);
 25     lcd1602_write_data(0xc1);
 26     lcd1602_write_data(0xca);
 27     lcd1602_pos(13);
 28     lcd1602_write_data(0xc8);
 29     lcd1602_write_data(0xbd);
 30     lcd1602_write_data(0xde);
 31     
 32     //第二行顯示 Welcome Radio
 33     lcd1602_pos(40);
 34     lcd1602_write_data('W');
 35     lcd1602_write_data('e');
 36     lcd1602_write_data('l');
 37     lcd1602_write_data('c');
 38     lcd1602_write_data('o');
 39     lcd1602_write_data('m');
 40     lcd1602_write_data('e');    
 41     lcd1602_pos(0x4b);
 42     lcd1602_write_data('R');
 43     lcd1602_write_data('a');
 44     lcd1602_write_data('d');
 45     lcd1602_write_data('i');
 46     lcd1602_write_data('o');
 47 }
 48 
 49 
 50 void lcd1602_clear()
 51 {
 52     lcd1602_write_cmd(0x01);//光標清0 指針清0
 53 }
 54 
 55 void lcd1602_pos(unsigned char pos)
 56 {
 57     //設置指針位置 0x80 | 位置
 58     lcd1602_write_cmd(pos|0x80);
 59 }
 60 
 61 void lcd1602_write_data(unsigned char dat)
 62 {
 63     while(lcd1602_is_busy());
 64     RS = 1;
 65     RW = 0;
 66     E  = 0;
 67     DATA = lcd1602_reverse(dat);
 68     delayms(1);
 69     E = 1;//在E 高向低變化時傳輸
 70     E = 0;
 71 }
 72 
 73 void lcd1602_write_cmd(unsigned char cmd)
 74 {
 75     while(lcd1602_is_busy());
 76     RS = 0;
 77     RW = 0;
 78     E  = 0;
 79     DATA = lcd1602_reverse(cmd);
 80     delayms(1);
 81     E = 1; //在E 高向低變化時傳輸
 82     E = 0;
 83 }
 84 
 85 char lcd1602_is_busy()
 86 {
 87     char result;
 88     RS = 0;  //發送的是命令
 89     RW = 1;  //
 90     E  = 1;  //使能
 91     delayms(1);
 92     result = (1<<7 & lcd1602_reverse(DATA));//7bit 1 忙 0 不忙
 93     E = 0;   //取消使能
 94     return result;
 95 }
 96 
 97 
 98 //字符高低位互換
 99 unsigned char lcd1602_reverse(unsigned char dat)
100 {
101     unsigned char chr;
102     chr = (dat>>7 & 1) << 0;
103     chr |= (dat>>6 & 1) << 1;
104     chr |= (dat>>5 & 1) << 2;
105     chr |= (dat>>4 & 1) << 3;
106     chr |= (dat>>3 & 1) << 4;
107     chr |= (dat>>2 & 1) << 5;
108     chr |= (dat>>1 & 1) << 6;
109     chr |= (dat>>0 & 1) << 7;
110     return chr;
111 }

 

串口 URAT 

uart.h

1 #ifndef __UART__
2 #define __UART__
3 #include "IR.h"
4 void init_uart();
5 void send_hex(unsigned char);
6 void send_str(unsigned char *);
7 void send_code(IR_CODE);
8 #endif

uart.c

 1 #include <reg52.h>
 2 #include "uart.h"
 3 void init_uart()
 4 {
 5     //定時器1 溢出決定波特率
 6     EA  = 1;      //總中斷開
 7     TMOD |= 1<<5; //定時器1 自動重裝模式
 8     TH1 = 0xfd;   //當TL1中溢出時 TH1 的值自動重裝進去
 9     TL1 = 0xfd;   //省去一個中斷處理函數
10     TR1 = 1;      //開始計數
11     SM0 = 0;
12     SM1 = 1;      //8bit UART 波特率可變
13 }
14 
15 void send_str(unsigned char *str)
16 {
17     while(*str)
18     {
19         SBUF = *str;
20         while(! TI);
21         TI = 0;
22         str++;
23     }
24 }
25 
26 void send_hex(unsigned char hex)
27 {
28     SBUF = hex;
29     while(! TI);
30     TI = 0;
31 }
32 
33 void send_code(IR_CODE ir_code)
34 {
35     unsigned char c;
36     unsigned char *p;
37     int i,j;
38     p = (unsigned char *)&ir_code;
39     send_str("custom:0x");
40     for(i=0; i<4; i++)
41     {
42         if(2 == i)
43         {
44             send_str(" code:0x");
45         }
46         for(j=1; j>=0; j--)
47         {
48             c = (*p>>(4*(j))) & 0xf;
49             if(0<=c && c<=9)
50             {
51                 send_hex('0' + c);
52             }
53             else
54             {
55                 send_hex('A' + c - 0xa);
56             }
57         }
58         p++;
59     }
60     send_str("\r\n");
61 }

 

eeprom.h

 1 #include <reg52.h>
 2 #ifndef __EEPROM__
 3 #define __EEPROM__
 4 /**
 5  * STC90C52 結尾是 90C 
 6  * EEPROM 5K
 7  * SRAM 215字節
 8  * 每個扇區512字節  5K / 512 = 10 個扇區
 9  * 扇區首地址 2000h 結束地址 33ffh
10  */
11 
12 /* FLASH 首地址 */
13 #define BASE_ADDR 0x2000   //stc89c52 stc89c52rd
14 //#define BASE_ADDR 0x4000 //stc89c54rd+
15 
16 /* 特殊功能寄存器聲明 */
17 sfr ISP_DATA  = 0xe2;  
18 sfr ISP_ADDRH = 0xe3;    
19 sfr ISP_ADDRL = 0xe4;
20 sfr ISP_CMD   = 0xe5;
21 sfr ISP_TRIG  = 0xe6;     
22 sfr ISP_CONTR = 0xe7;
23 
24 /* 定義命令字節 */
25 #define CMD_Read    0x01 //字節讀數據命令     
26 #define CMD_Prog    0x02 //字節編程數據命令     
27 #define CMD_Erase   0x03 //扇區擦除數據命令
28 #define En_Wait_ISP 1<<7 | 1<<1  //設置等待時間 ,並使能ISP/IAP 11.0592 晶振
29 
30 void eeprom_lock_ISP();
31 void eeprom_erase(unsigned int);
32 unsigned char eeprom_read(unsigned int);
33 void eeprom_prog(unsigned int addr, unsigned char dat);
34 
35 #endif

 

延時

delay.h

1 #ifndef __DELAY__
2 #define __DELAY__
3 void delayms(int);
4 void delay700us();
5 #endif

delay.c

 1 #include <intrins.h>
 2 #include "delay.h"
 3 void delayms(int ms) //@11.0592MHz
 4 {
 5     unsigned char i, j;
 6     while(ms--)
 7     {
 8         _nop_();
 9         i = 2;
10         j = 199;
11         do
12         {
13             while (--j);
14         } 
15         while (--i);
16     }
17 }
18 
19 void delay700us() //@11.0592MHz
20 {
21     unsigned char i, j;
22     _nop_();
23     i = 2;
24     j = 61;
25     do
26     {
27         while (--j);
28     } while (--i);
29 }

i2c 通信

i2c.h

 1 #ifndef __I2C__
 2 #define __I2C__
 3 
 4 #include <reg52.h>
 5 
 6 /* 引腳定義 */
 7 sbit I2C_SCL = P3 ^ 5;
 8 sbit I2C_SDA = P3 ^ 6;
 9 
10 void i2c_start();
11 void i2c_stop();
12 void i2c_send(unsigned char dat);
13 unsigned char i2c_recv();
14 char i2c_wait_ack();
15 void i2c_send_ack();
16 #endif

 

i2c.c

 1 #include "i2c.h" 
 2 
 3 void i2c_start()
 4 {
 5     /* SCL SDA 為高電平時 SDA 變為低電平 */
 6     I2C_SCL = 1;
 7     I2C_SDA = 1;
 8     I2C_SDA = 0; 
 9     I2C_SCL = 0; /* 鉗住I2C總線,准備發送或接收數據 */
10 }
11 
12 void i2c_stop()
13 {
14     /* SCK 高電平期間 SDA 由低變高 */
15     I2C_SCL = 0;
16     I2C_SDA = 0;
17     I2C_SCL = 1;
18     I2C_SDA = 1;    
19 }
20 
21 void i2c_send(unsigned char dat)
22 {
23     char i = 8;
24     while(i--)
25     {
26         I2C_SCL = 0;
27         if(dat & 0x80)
28         {
29             I2C_SDA = 1;
30         }
31         else
32         {
33             I2C_SDA = 0;
34         }
35         I2C_SCL = 1;
36         dat = dat<<1;
37     }
38     //等待ACK回復信號
39     i2c_wait_ack();
40 }
41 
42 unsigned char i2c_recv()
43 {
44     unsigned char dat = 0;
45     char i = 8;
46     I2C_SDA = 1;
47     while(i--)
48     {
49         I2C_SCL = 0;
50         dat<<=1; //這里要有一定延時要求是 > 1.3us 
51         I2C_SCL = 1;    
52         if(I2C_SDA)
53         {
54             //以下3者結果一樣
55             //dat++;
56             //dat += 1;
57             dat |= 1;
58         }
59         //dat |= (unsigned char)I2C_SDA;
60     }
61     i2c_send_ack();
62     return dat;
63 }
64 
65 //無響應返回0 
66 char i2c_wait_ack()
67 {
68     unsigned char time_out = 0xff;
69     I2C_SCL = 0;
70     I2C_SDA = 1;
71     I2C_SCL = 1;
72     
73     //由 slaver 拉低 SDA 表示回應
74     while(I2C_SDA)
75     {
76         time_out--;
77         if(0 == time_out)
78         {
79             i2c_stop();
80             return 0;
81         }
82     }
83     return 1;
84 }
85 
86 void i2c_send_ack()
87 {
88     //拉低SDA 響應ACK 當SCL 由低電平變高電平時 從機接收
89     I2C_SCL = 0;
90     I2C_SDA = 0;
91     I2C_SCL = 1;
92     I2C_SCL = 0;
93 }

 

IR 紅外接收

IR.h

 1 #ifndef __IR__
 2 #define __IR__
 3 #include <reg52.h>
 4 #include "delay.h"
 5 #include <string.h>
 6 
 7 //公用
 8 typedef struct {
 9     unsigned char custom_height;
10     unsigned char custom_lower;
11     unsigned char ir_code;
12     unsigned char re_ir_code;
13 } IR_CODE, *pIR_CODE;
14 
15 //接收
16 sbit IR     = P3 ^ 2;//紅外線一體接收頭 OUT 
17 
18 void init_IR();
19 IR_CODE IR_recv();
20 #endif

IR.c

 1 #include "IR.h"
 2 
 3 void init_IR()
 4 {
 5     //接收
 6     EA  = 1;  //總中斷開
 7     EX0 = 1;  //IR 接收頭使用外部中斷0 來處理
 8     IT0 = 1;  //下降沿觸發
 9 }
10 
11 //接收
12 IR_CODE IR_recv()
13 {
14     /**
15      * 數據格式:
16      * 9ms低電平 4.5ms高電平 頭部
17      * 定制高位  定制低位  數據碼  數據反碼
18      * 1: 560us低電平 1680us高電平  0.56ms 1.7ms 
19      * 0: 560us低電平 560us高電平   0.56ms 0.56ms
20      */
21     IR_CODE ir_code;
22     unsigned char i,k;
23     unsigned char *ir_char_p;
24     unsigned char ir_char;
25     ir_char_p = (unsigned char *)&ir_code;
26     
27     //棧分配 IR_CODE 竟然還要手動清0
28     memset(&ir_code, 0, 4);
29     
30     delayms(5); //9ms 內必須是低電平否則就不是頭信息
31     if(0 == IR)
32     {
33         while(! IR);//等待4.5ms的高電平
34         
35         //檢測是否是 2.5ms 重碼 
36         delayms(3);
37         if(1 == IR)
38         {
39             //k 4位編碼
40             for(k=0; k<4; k++)
41             {
42                 ir_char = 0x0;
43                 //i 每一個編碼的 8bit
44                 for(i=0;i<8;i++)
45                 {
46                     while(IR);     //等待變為低電平時
47                     while(! IR);   //等待變為高電平后
48                     delay700us();  //休眠700us 后讀值
49                     ir_char |= (char)IR << i;//先存低位
50                     //使用下面指針操作就會失敗出現不穩定
51                     //*ir_char_p |= (char)IR << i;
52                 }
53                 *ir_char_p = ir_char;
54                 ir_char_p++;
55             }
56             
57             //計算反碼 code碼是否正確
58             if(ir_code.ir_code != ~(ir_code.re_ir_code))
59             {
60                 memset(&ir_code, 0, 4);
61             }
62         }
63     }
64     return ir_code;
65 }

str 處理相關 

str.h

1 #ifndef __STR__
2 #define __STR__
4 unsigned char num_to_str(unsigned char num);
5 #endif

str.c

 1 #include "str.h"
 2 
 3 unsigned char num_to_str(unsigned char num)
 4 {
 5     unsigned char chr;
 6     //asc2 表 0 在前面
 7     if(0<= num && 9>= num)
 8     {
 9         chr = num + '0';
10     }
11     return chr;
12 }

重點TEA5767 特別說明的是,我先是在TB上找了許多資料,百度上找,但是就發現就是那一種格式的寫法,還是個錯誤的寫法,根本就不對,很多人還轉來轉去。(自動搜台的寫法不對。)

GOOGLE 上搜了下,C51 的不多,其它的有不少。

自動搜台,是利用第6位的 SM ,寫1 表示進入這個模式,同時還需要設置 SSL1 SSL0 ,經過我的測試,發現 HLSI 只能設為1 的時候,才能搜到台。

別人的程序錯誤之處在於, 自動搜索后,從 第3個 讀的數據中找 HLSI 在計算是 + 225 還是 -225 ,這個在讀的時候,就變成IF 了,沒有看datasheet 就寫程序,還說 自動搜索模式有問題,不好用。

經過我的試驗,發現 自動搜台模式,基本能用,但是程序要復雜一些。不像是寫入一個頻率,讀 ADC 比較數值,大於多少就表示有台。 比這種要復雜的多。

以下 2 種算法,都有編寫,並測試成功,但實際試驗效果來看, 第2種,普通的寫法,效果好些。(問題是,自動搜台,檢測到信號停住的地方,可能並不准,有可能是,100.023 這種。如果把 每次步進頻率改為100K 就會錯過很多台,這里是每次進10K)

tea5767.h

 1 #ifndef __TEA5767__
 2 #define __TEA5767__
 3 
 4 #include <string.h>
 5 #include "i2c.h"
 6 #include "uart.h"
 7 #include "str.h"
 8 #include "delay.h"
 9 #include "lcd1602.h"
10 #include "eeprom.h" 
11 
12 typedef struct{
13     unsigned char st1;
14     unsigned char st2;
15     unsigned char st3;
16     unsigned char st4;
17     unsigned char st5;
18 } TEA_DAT, *PTEA_DAT;
19 
20 typedef struct {
21     unsigned char high; //表示整數 100
22     unsigned char low;  //表示小數 5
23 } TEA_CH, *PTEA_CH;
24 
25 /* 地址定義 */
26 #define TEA5767_ADDR_W  0xc0   //tea5767 寫地址
27 #define TEA5767_ADDR_R  0xc1   //tea5767 讀地址
28 #define TEA5767_CLK     32768  //tea5767 晶振
29 #define TEA5767_MAX_KHZ 108000 //最高頻率 108M
30 #define TEA5767_MIN_KHZ 87500  //最低頻率 87.5M
31 #define TEA5756_HLSI //高混頻器 經過測試,使用SM自動搜索功能時必須設此值 手動搜索 無所謂
32 
33 unsigned long tea5767_Khz_to_pll(unsigned long Khz);
34 unsigned long tea5767_pll_to_Khz(TEA_DAT dat);
35 TEA_DAT tea5767_set_receiver(unsigned long Khz);
36 TEA_DAT tea5767_search(unsigned long Khz);
37 void tea5767_auto_search();
38 void tea5767_tiny_auto_search();
39 void tea5767_write(TEA_DAT dat);
40 TEA_DAT tea5767_read();
41 void tea5767_mute(unsigned char ch_num);
42 
43 //Lcd顯示
44 void sohw_search(unsigned long Khz, unsigned char channel);
45 //存儲電台
46 void save_eeprom(unsigned long Khz, unsigned char channel);
47 //設定ch
48 unsigned long set_ch(unsigned char ch_num);
49 #endif

 

tea5767.c

  1 #include "tea5767.h"
  2 
  3 unsigned char mute = 1;
  4 void tea5767_mute(unsigned char ch_num)
  5 {
  6     TEA_DAT dat;
  7     unsigned long Khz,pll;
  8     Khz = set_ch(ch_num);
  9     //靜音位寫 1
 10     pll = tea5767_Khz_to_pll(Khz);
 11     dat.st1  = (mute%2) <<7 | (pll>>8) & 0x3f;
 12     dat.st2  = pll;
 13     dat.st3  = 1;
 14     #ifdef TEA5756_HLSI
 15     dat.st3  |= 1<<4;
 16     #endif
 17     dat.st4  = 0x11;
 18     dat.st5  = 0x40;
 19     tea5767_write(dat);
 20     
 21     if(mute%2)
 22     {
 23         lcd1602_pos(40);
 24         lcd1602_write_data('M');
 25         lcd1602_write_data('U');
 26         lcd1602_write_data('T');
 27         lcd1602_write_data('E');
 28     }
 29     mute++;
 30 }
 31 
 32 unsigned long tea5767_Khz_to_pll(unsigned long Khz)
 33 {
 34     #ifdef TEA5756_HLSI
 35     return 4*(Khz*1000+225000)/TEA5767_CLK;
 36     #else
 37     return 4*(Khz*1000-225000)/TEA5767_CLK;
 38     #endif
 39 }
 40 
 41 unsigned long tea5767_pll_to_Khz(TEA_DAT dat)
 42 {
 43     unsigned long Khz,pll;     
 44     pll = ((dat.st1 & 0x3f)<<8 | dat.st2);
 45     #ifdef TEA5756_HLSI
 46     Khz = (pll*TEA5767_CLK/4-225*1000)/1000;
 47     #else
 48     Khz = (pll*TEA5767_CLK/4+225*1000)/1000;
 49     #endif
 50     
 51     return Khz;
 52 }
 53 
 54 void tea5767_write(TEA_DAT dat)
 55 {
 56     i2c_start();
 57     i2c_send(TEA5767_ADDR_W);
 58     i2c_send(dat.st1);
 59     i2c_send(dat.st2);
 60     i2c_send(dat.st3);
 61     i2c_send(dat.st4);
 62     i2c_send(dat.st5);
 63     i2c_stop();
 64 }
 65 
 66 TEA_DAT tea5767_read()
 67 {
 68     TEA_DAT dat;
 69     i2c_start();
 70     i2c_send(TEA5767_ADDR_R);
 71     dat.st1 = i2c_recv();
 72     dat.st2 = i2c_recv();
 73     dat.st3 = i2c_recv();
 74     dat.st4 = i2c_recv();
 75     dat.st5 = i2c_recv();
 76     i2c_stop();
 77     return dat;
 78 }
 79 
 80 TEA_DAT tea5767_set_receiver(unsigned long Khz)
 81 {
 82     unsigned long pll;
 83     TEA_DAT dat;
 84     
 85     pll = tea5767_Khz_to_pll(Khz);
 86     
 87     /* 發送5個控制位 順序是 1 2 3 4 5 字節的高位先發 
 88      * HLSL = 0 : 4*(102.4*1000000-225*1000)/32768
 89      * HLSL = 1 : 4*(102.4*1000000+225*1000)/32768
 90      * PLL WORD = 0x30ef
 91      */
 92     dat.st1  = (pll>>8) & 0x3f;
 93     dat.st2  = pll;
 94     dat.st3  = 1;
 95     #ifdef TEA5756_HLSI
 96     dat.st3  |= 1<<4;
 97     #endif
 98     dat.st4  = 0x11;
 99     dat.st5  = 0x40;
100     
101     tea5767_write(dat);
102     delayms(100);
103     dat = tea5767_read();
104     return dat;
105 }
106 
107 //自動搜索 87.5 ~ 108M 由低向高搜索
108 TEA_DAT tea5767_search(unsigned long Khz)
109 {
110     unsigned long pll;
111     TEA_DAT dat;
112     
113     pll = tea5767_Khz_to_pll(Khz);
114     dat.st1  = ((pll>>8) & 0x3f) | (1<<6);
115     dat.st2  = pll;
116     //搜索停止 信號強度 01:5 10:7 11:10 
117     //dat.st3  = 1 | 1<<7 | 1<<6 |1<<5 ;
118     dat.st3  = 1 | 1<<7 | 1<<6  ;
119     #ifdef TEA5756_HLSI
120     dat.st3  |= 1<<4;
121     #endif
122     
123     dat.st4  = 0x11;
124     dat.st5  = 0x40;
125     tea5767_write(dat);
126     delayms(100);
127     dat = tea5767_read();
128     while(dat.st1 != (dat.st1 | (1<<7)))
129     {
130         dat = tea5767_read();
131     }
132     return dat;
133 }
134 
135 //自動搜台 存台到 eeprom
136 void tea5767_auto_search()
137 {
138     TEA_DAT dat;
139     unsigned long Khz;
140     unsigned char if_counter;
141     unsigned char channel = 0;
142     Khz = TEA5767_MIN_KHZ;
143     
144     //清液晶屏
145     lcd1602_clear();
146     
147     dat = tea5767_search(Khz);
148                 
149     //檢測信號是否滿足要求
150     while(dat.st1 != (dat.st1 | (1<<6)))
151     {
152         //if count 在 0x31 ~ 0x3e 之間
153         if_counter = (dat.st3 & 0x7f);
154         if((0x31 < if_counter) && (0x3e > if_counter))
155         {                    
156             //比較leve 電平確認是否收到台 實際測試使用此數 不會漏台
157             if((dat.st4>>4) > 10)
158             {
159                 Khz = tea5767_pll_to_Khz(dat);
160     
161                 save_eeprom(Khz, channel);
162                 channel++;
163             }
164         }
165         
166         //顯示當前頻率
167         sohw_search(Khz, channel);
168         
169         //計算搜到台的頻率 加上10Khz 后重新搜索 實際測試使用此數 不會漏台
170         Khz += 10;
171         dat = tea5767_search(Khz);
172     }
173 }
174 
175 //細致的搜台
176 void tea5767_tiny_auto_search()
177 {
178     TEA_DAT dat;    
179     unsigned long Khz;
180     unsigned char channel = 0;
181     Khz = TEA5767_MIN_KHZ;
182     //Khz = 100000;
183     
184     //清液晶屏
185     lcd1602_clear();
186     
187     //擦除eeprom
188     eeprom_erase(1);
189     
190     while(Khz <= TEA5767_MAX_KHZ)
191     {
192         dat = tea5767_set_receiver(Khz);
193         //比較leve 電平確認是否收到台 實際測試使用此數 不會漏台
194         if((dat.st4>>4) > 8)
195         {
196             //存儲電台
197             save_eeprom(Khz, channel++);
198         }
199         
200         //顯示當前頻率
201         sohw_search(Khz, channel);
202         //頻率由低到高 每次增加10Khz
203         Khz += 100;
204     }
205 }
206 
207 
208 void sohw_search(unsigned long Khz, unsigned char channel)
209 {
210     unsigned char high, low;
211     lcd1602_pos(0);
212     lcd1602_write_data('S');
213     lcd1602_write_data('e');
214     lcd1602_write_data('a');
215     lcd1602_write_data('r');
216     lcd1602_write_data('c');
217     lcd1602_write_data('h');
218     lcd1602_write_data(':');
219     //輸出頻率 如果是 100M 以下第1位為空
220     high = Khz/1000;
221     low  = Khz%1000/100;
222     if(high>= 100)
223     {
224         lcd1602_write_data(num_to_str(high/100));
225     }
226     else
227     {
228         lcd1602_write_data(' ');
229     }
230     lcd1602_write_data(num_to_str(high%100/10));
231     lcd1602_write_data(num_to_str(high%10));
232     lcd1602_write_data('.');
233     lcd1602_write_data(num_to_str(low));
234     lcd1602_write_data('M');
235     lcd1602_write_data('h');
236     lcd1602_write_data('z');
237     
238     //顯示收到的頻道
239     lcd1602_pos(40);
240     lcd1602_write_data('C');
241     lcd1602_write_data('h');
242     lcd1602_write_data('a');
243     lcd1602_write_data('n');
244     lcd1602_write_data('n');
245     lcd1602_write_data('e');
246     lcd1602_write_data('l');
247     lcd1602_write_data(':');
248     
249     lcd1602_write_data(num_to_str(channel / 10));
250     lcd1602_write_data(num_to_str(channel % 10));
251 }
252 
253 //存儲電台
254 void save_eeprom(unsigned long Khz, unsigned char channel)
255 {
256     TEA_CH ch;     
257     ch.high = Khz/1000;
258     ch.low  = Khz%1000/100;
259     eeprom_prog(channel*2,   ch.high);
260     eeprom_prog(channel*2+1, ch.low);
261 }
262 
263 //設定ch
264 unsigned long set_ch(unsigned char ch_num)
265 {
266     unsigned long Khz;
267     TEA_CH ch;
268     ch.high = eeprom_read(ch_num*2);
269     ch.low  = eeprom_read(ch_num*2+1);
270     //合並為 Khz
271     Khz  = ch.high*100; //直接使用 *1000 會計算錯誤 分為2部正確
272     Khz *= 10;
273     Khz += ch.low*100;
274     
275     //設定接收頻率
276     tea5767_set_receiver(Khz);
277     
278     //清液晶屏
279     lcd1602_clear();
280     
281     //顯示頻道信息
282     lcd1602_pos(0);
283     lcd1602_write_data('C');
284     lcd1602_write_data('h');
285     lcd1602_write_data(':');
286     
287     //頻道數從 0 開始計
288     lcd1602_write_data(num_to_str(ch_num / 10));
289     lcd1602_write_data(num_to_str(ch_num % 10));
290     
291     lcd1602_pos(8);
292     if(ch.high>= 100)
293     {
294         lcd1602_write_data(num_to_str(ch.high/100));
295     }
296     else
297     {
298         lcd1602_write_data(' ');
299     }
300     lcd1602_write_data(num_to_str(ch.high%100/10));
301     lcd1602_write_data(num_to_str(ch.high%10));
302     lcd1602_write_data('.');
303     lcd1602_write_data(num_to_str(ch.low));
304     lcd1602_write_data('M');
305     lcd1602_write_data('h');
306     lcd1602_write_data('z');
307     
308     return Khz;
309 }

eeprom.c 

 1 #include "eeprom.h"
 2 
 3 /* 執行完操作以后安全鎖 */
 4 void eeprom_lock_ISP()
 5 {
 6     ISP_CONTR = 0;
 7     ISP_CMD = 0;
 8     ISP_TRIG = 0;
 9     ISP_ADDRH = 0xff;
10     ISP_ADDRL = 0xff;
11 }
12 
13 /* 擦除指定地址所在的整個扇區  */
14 void eeprom_erase(unsigned int addr)
15 {
16     addr += BASE_ADDR;
17     
18     //發送地址
19     ISP_ADDRH = addr >> 8;
20     ISP_ADDRL = addr;
21     
22     //發送解鎖命令
23     ISP_CONTR = En_Wait_ISP;
24     
25     //發擦除命令
26     ISP_CMD = CMD_Erase;
27     
28     //發送觸發命令
29     ISP_TRIG = 0x46;
30     ISP_TRIG = 0xB9;
31     
32     //最后鎖定 ISP 仿誤操作
33     eeprom_lock_ISP();
34 }
35 
36 unsigned char eeprom_read(unsigned int addr)
37 {
38     addr += BASE_ADDR;
39     
40     //發送地址
41     ISP_ADDRH = addr >> 8;
42     ISP_ADDRL = addr;
43     
44     //發送解鎖命令
45     ISP_CONTR = En_Wait_ISP;
46     
47     //發讀命令
48     ISP_CMD = CMD_Read;
49     
50     //發送觸發命令
51     ISP_TRIG = 0x46;
52     ISP_TRIG = 0xB9;
53     
54     //最后鎖定 ISP 仿誤操作
55     eeprom_lock_ISP();
56     
57     return ISP_DATA;
58 }
59 
60 void eeprom_prog(unsigned int addr, unsigned char dat)
61 {
62     addr += BASE_ADDR;
63     
64     //發送要保存的數據
65     ISP_DATA = dat;
66     
67     //發送地址
68     ISP_ADDRH = addr >> 8;
69     ISP_ADDRL = addr;
70     
71     //發送解鎖命令
72     ISP_CONTR = En_Wait_ISP;
73     
74     //發編程命令
75     ISP_CMD = CMD_Prog;
76     
77     //發送觸發命令
78     ISP_TRIG = 0x46;
79     ISP_TRIG = 0xB9;
80     
81     //最后鎖定 ISP 仿誤操作
82     eeprom_lock_ISP();
83 }

 

最后,在簡單說下,自動搜台 和 手動搜台的區別。

自動搜台特點:

1,設一個 LEVE 值后,當到這個值的時候,能自動停止,但是靈活性只有3種,不夠靈活。

2,能增長到最高頻率時,停止 

3,只能用 TEA5756_HLSI 

自動搜台流程:

1,設定 好 SM 位 和 SSL 后

2,設一個頻率 

3,TEA5767 就會自動向上 或 向下 查找,直到 確認收到了信號,就停止 有標識位, 同時比較是否到了最終端,108M

4,程序需要判斷,有信號的標識位, 108M 到終點的標識位2個。

5,程序也要比較ADC 電平,再次確認是否搜到了台

 

手動搜台特點:

1,控制靈活,但要自行控制以上參數。程序編寫簡單。

手動搜台流程:

1,寫入一個頻率

2,讀取ADC 比較高於某個值時就表示收到了台

3,自行判斷,頻率范圍

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM