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,自行判断,频率范围

 

posted @ 2016-05-06 13:11  宁次  阅读(11178)  评论(1编辑  收藏  举报