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>
#include "tea5767.h"
#include "delay.h"
#include "lcd1602.h"
#include "IR.h"
//K1:上一台 K2:下一台 K3:从低向高搜索台
sbit K1 = P2 ^ ;
sbit K2 = P2 ^ ;
sbit K3 = P2 ^ ; //当前频道号 从 0 开始
unsigned char ch = ; void main()
{ //初始化 1602lcd
lcd1602_init(); //初始化 串口
init_uart(); //初始化红外遥控接口
init_IR(); //主函数中扫描按键
while()
{
//上一台
if( == K1)
{
//去抖动
delayms();
if( == K1)
{
set_ch(--ch);
}
}
//下一台
if( == K2)
{
//去抖动
delayms();
if( == K2)
{
set_ch(++ch);
}
}
//自动搜索
if( == K3)
{
//去抖动
delayms();
if( == K3)
{
tea5767_tiny_auto_search();
}
}
}
} void IR_int() interrupt
{
IR_CODE ir_code;
EX0 = ;//处理过程中 关中断
ir_code = IR_recv();
if(ir_code.ir_code)
{
/**
* custom:0xCC1D code:0x05FA 上一台
* custom:0xCC1D code:0x06F9 下一台
* custom:0xCC1D code:0x01FE 静音
*/
//比较机器码
if(ir_code.custom_height == 0xcc && ir_code.custom_lower == 0x1d)
{
//比较功能码
switch(ir_code.ir_code)
{
case 0x5: set_ch(++ch); break;
case 0x6: set_ch(--ch); break;
case 0x1: tea5767_mute(ch); break;
}
//发送到串口,显示出编码,可用于记录编码,做学习型遥控器 send_code(ir_code);
}
}
EX0 = ;//处理结束后 开中断
}
lcd1602.h
#ifndef __LCD1602__
#define __LCD1602__
#include <reg52.h>
#include "delay.h"
#define DATA P1
sbit RS = P3 ^ ;//数据 H 命令 L
sbit RW = P3 ^ ;//读 H 写 L
sbit E = P3 ^ ;//高电平 H 使能 /** * E = 1 后需要等一小段时间, 在手册中并没有说明 -
*/
void lcd1602_init();
void lcd1602_clear();
char lcd1602_is_busy();
void lcd1602_write_cmd(unsigned char cmd);
void lcd1602_write_data(unsigned char dat);
void lcd1602_pos(unsigned char pos);
unsigned char lcd1602_reverse(unsigned char dat);
#endif
lcd1602.c
#include "lcd1602.h" void lcd1602_init()
{
//初始化 复位 lcd1602
lcd1602_write_cmd(0x38);//设置显示模式 指令码 00111000 => 0x38
delayms();
lcd1602_write_cmd(0x0c);//开显示 不显示光标 不闪烁
delayms();
lcd1602_write_cmd(0x06);//光标设置 写字符后指针加一
delayms();
lcd1602_write_cmd(0x01);//光标清0 指针清0
delayms();
//设置初始位置为 0
lcd1602_pos();
//打印欢迎信息 日文假名
/**
* こんにちわ ねじ
* こ 高4位是 b 低4位是 a 合并就是 0xba
*/
lcd1602_write_data(0xba);
lcd1602_write_data(0xdd);
lcd1602_write_data(0xc6);
lcd1602_write_data(0xc1);
lcd1602_write_data(0xca);
lcd1602_pos();
lcd1602_write_data(0xc8);
lcd1602_write_data(0xbd);
lcd1602_write_data(0xde); //第二行显示 Welcome Radio
lcd1602_pos();
lcd1602_write_data('W');
lcd1602_write_data('e');
lcd1602_write_data('l');
lcd1602_write_data('c');
lcd1602_write_data('o');
lcd1602_write_data('m');
lcd1602_write_data('e');
lcd1602_pos(0x4b);
lcd1602_write_data('R');
lcd1602_write_data('a');
lcd1602_write_data('d');
lcd1602_write_data('i');
lcd1602_write_data('o');
} void lcd1602_clear()
{
lcd1602_write_cmd(0x01);//光标清0 指针清0
} void lcd1602_pos(unsigned char pos)
{
//设置指针位置 0x80 | 位置
lcd1602_write_cmd(pos|0x80);
} void lcd1602_write_data(unsigned char dat)
{
while(lcd1602_is_busy());
RS = ;
RW = ;
E = ;
DATA = lcd1602_reverse(dat);
delayms();
E = ;//在E 高向低变化时传输
E = ;
} void lcd1602_write_cmd(unsigned char cmd)
{
while(lcd1602_is_busy());
RS = ;
RW = ;
E = ;
DATA = lcd1602_reverse(cmd);
delayms();
E = ; //在E 高向低变化时传输
E = ;
} char lcd1602_is_busy()
{
char result;
RS = ; //发送的是命令
RW = ; //读
E = ; //使能
delayms();
result = (<< & lcd1602_reverse(DATA));//7bit 1 忙 0 不忙
E = ; //取消使能
return result;
} //字符高低位互换
unsigned char lcd1602_reverse(unsigned char dat)
{
unsigned char chr;
chr = (dat>> & ) << ;
chr |= (dat>> & ) << ;
chr |= (dat>> & ) << ;
chr |= (dat>> & ) << ;
chr |= (dat>> & ) << ;
chr |= (dat>> & ) << ;
chr |= (dat>> & ) << ;
chr |= (dat>> & ) << ;
return chr;
}
串口 URAT
uart.h
#ifndef __UART__
#define __UART__
#include "IR.h"
void init_uart();
void send_hex(unsigned char);
void send_str(unsigned char *);
void send_code(IR_CODE);
#endif
uart.c
#include <reg52.h>
#include "uart.h"
void init_uart()
{
//定时器1 溢出决定波特率
EA = ; //总中断开
TMOD |= <<; //定时器1 自动重装模式
TH1 = 0xfd; //当TL1中溢出时 TH1 的值自动重装进去
TL1 = 0xfd; //省去一个中断处理函数
TR1 = ; //开始计数
SM0 = ;
SM1 = ; //8bit UART 波特率可变
} void send_str(unsigned char *str)
{
while(*str)
{
SBUF = *str;
while(! TI);
TI = ;
str++;
}
} void send_hex(unsigned char hex)
{
SBUF = hex;
while(! TI);
TI = ;
} void send_code(IR_CODE ir_code)
{
unsigned char c;
unsigned char *p;
int i,j;
p = (unsigned char *)&ir_code;
send_str("custom:0x");
for(i=; i<; i++)
{
if( == i)
{
send_str(" code:0x");
}
for(j=; j>=; j--)
{
c = (*p>>(*(j))) & 0xf;
if(<=c && c<=)
{
send_hex('' + c);
}
else
{
send_hex('A' + c - 0xa);
}
}
p++;
}
send_str("\r\n");
}
eeprom.h
#include <reg52.h>
#ifndef __EEPROM__
#define __EEPROM__
/**
* STC90C52 结尾是 90C
* EEPROM 5K
* SRAM 215字节
* 每个扇区512字节 5K / 512 = 10 个扇区
* 扇区首地址 2000h 结束地址 33ffh
*/ /* FLASH 首地址 */
#define BASE_ADDR 0x2000 //stc89c52 stc89c52rd
//#define BASE_ADDR 0x4000 //stc89c54rd+ /* 特殊功能寄存器声明 */
sfr ISP_DATA = 0xe2;
sfr ISP_ADDRH = 0xe3;
sfr ISP_ADDRL = 0xe4;
sfr ISP_CMD = 0xe5;
sfr ISP_TRIG = 0xe6;
sfr ISP_CONTR = 0xe7; /* 定义命令字节 */
#define CMD_Read 0x01 //字节读数据命令
#define CMD_Prog 0x02 //字节编程数据命令
#define CMD_Erase 0x03 //扇区擦除数据命令
#define En_Wait_ISP 1<<7 | 1<<1 //设置等待时间 ,并使能ISP/IAP 11.0592 晶振 void eeprom_lock_ISP();
void eeprom_erase(unsigned int);
unsigned char eeprom_read(unsigned int);
void eeprom_prog(unsigned int addr, unsigned char dat); #endif
延时
delay.h
#ifndef __DELAY__
#define __DELAY__
void delayms(int);
void delay700us();
#endif
delay.c
#include <intrins.h>
#include "delay.h"
void delayms(int ms) //@11.0592MHz
{
unsigned char i, j;
while(ms--)
{
_nop_();
i = ;
j = ;
do
{
while (--j);
}
while (--i);
}
} void delay700us() //@11.0592MHz
{
unsigned char i, j;
_nop_();
i = ;
j = ;
do
{
while (--j);
} while (--i);
}
i2c 通信
i2c.h
#ifndef __I2C__
#define __I2C__ #include <reg52.h> /* 引脚定义 */
sbit I2C_SCL = P3 ^ ;
sbit I2C_SDA = P3 ^ ; void i2c_start();
void i2c_stop();
void i2c_send(unsigned char dat);
unsigned char i2c_recv();
char i2c_wait_ack();
void i2c_send_ack();
#endif
i2c.c
#include "i2c.h" void i2c_start()
{
/* SCL SDA 为高电平时 SDA 变为低电平 */
I2C_SCL = ;
I2C_SDA = ;
I2C_SDA = ;
I2C_SCL = ; /* 钳住I2C总线,准备发送或接收数据 */
} void i2c_stop()
{
/* SCK 高电平期间 SDA 由低变高 */
I2C_SCL = ;
I2C_SDA = ;
I2C_SCL = ;
I2C_SDA = ;
} void i2c_send(unsigned char dat)
{
char i = ;
while(i--)
{
I2C_SCL = ;
if(dat & 0x80)
{
I2C_SDA = ;
}
else
{
I2C_SDA = ;
}
I2C_SCL = ;
dat = dat<<;
}
//等待ACK回复信号
i2c_wait_ack();
} unsigned char i2c_recv()
{
unsigned char dat = ;
char i = ;
I2C_SDA = ;
while(i--)
{
I2C_SCL = ;
dat<<=; //这里要有一定延时要求是 > 1.3us
I2C_SCL = ;
if(I2C_SDA)
{
//以下3者结果一样
//dat++;
//dat += 1;
dat |= ;
}
//dat |= (unsigned char)I2C_SDA;
}
i2c_send_ack();
return dat;
} //无响应返回0
char i2c_wait_ack()
{
unsigned char time_out = 0xff;
I2C_SCL = ;
I2C_SDA = ;
I2C_SCL = ; //由 slaver 拉低 SDA 表示回应
while(I2C_SDA)
{
time_out--;
if( == time_out)
{
i2c_stop();
return ;
}
}
return ;
} void i2c_send_ack()
{
//拉低SDA 响应ACK 当SCL 由低电平变高电平时 从机接收
I2C_SCL = ;
I2C_SDA = ;
I2C_SCL = ;
I2C_SCL = ;
}
IR 红外接收
IR.h
#ifndef __IR__
#define __IR__
#include <reg52.h>
#include "delay.h"
#include <string.h> //公用
typedef struct {
unsigned char custom_height;
unsigned char custom_lower;
unsigned char ir_code;
unsigned char re_ir_code;
} IR_CODE, *pIR_CODE; //接收
sbit IR = P3 ^ ;//红外线一体接收头 OUT void init_IR();
IR_CODE IR_recv();
#endif
IR.c
#include "IR.h" void init_IR()
{
//接收
EA = ; //总中断开
EX0 = ; //IR 接收头使用外部中断0 来处理
IT0 = ; //下降沿触发
} //接收
IR_CODE IR_recv()
{
/**
* 数据格式:
* 9ms低电平 4.5ms高电平 头部
* 定制高位 定制低位 数据码 数据反码
* 1: 560us低电平 1680us高电平 0.56ms 1.7ms
* 0: 560us低电平 560us高电平 0.56ms 0.56ms
*/
IR_CODE ir_code;
unsigned char i,k;
unsigned char *ir_char_p;
unsigned char ir_char;
ir_char_p = (unsigned char *)&ir_code; //栈分配 IR_CODE 竟然还要手动清0
memset(&ir_code, , ); delayms(); //9ms 内必须是低电平否则就不是头信息
if( == IR)
{
while(! IR);//等待4.5ms的高电平 //检测是否是 2.5ms 重码
delayms();
if( == IR)
{
//k 4位编码
for(k=; k<; k++)
{
ir_char = 0x0;
//i 每一个编码的 8bit
for(i=;i<;i++)
{
while(IR); //等待变为低电平时
while(! IR); //等待变为高电平后
delay700us(); //休眠700us 后读值
ir_char |= (char)IR << i;//先存低位
//使用下面指针操作就会失败出现不稳定
//*ir_char_p |= (char)IR << i;
}
*ir_char_p = ir_char;
ir_char_p++;
} //计算反码 code码是否正确
if(ir_code.ir_code != ~(ir_code.re_ir_code))
{
memset(&ir_code, , );
}
}
}
return ir_code;
}
str 处理相关
str.h
#ifndef __STR__
#define __STR__
unsigned char num_to_str(unsigned char num);
#endif
str.c
#include "str.h" unsigned char num_to_str(unsigned char num)
{
unsigned char chr;
//asc2 表 0 在前面
if(<= num && >= num)
{
chr = num + '';
}
return chr;
}
重点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
#ifndef __TEA5767__
#define __TEA5767__ #include <string.h>
#include "i2c.h"
#include "uart.h"
#include "str.h"
#include "delay.h"
#include "lcd1602.h"
#include "eeprom.h" typedef struct{
unsigned char st1;
unsigned char st2;
unsigned char st3;
unsigned char st4;
unsigned char st5;
} TEA_DAT, *PTEA_DAT; typedef struct {
unsigned char high; //表示整数 100
unsigned char low; //表示小数 5
} TEA_CH, *PTEA_CH; /* 地址定义 */
#define TEA5767_ADDR_W 0xc0 //tea5767 写地址
#define TEA5767_ADDR_R 0xc1 //tea5767 读地址
#define TEA5767_CLK 32768 //tea5767 晶振
#define TEA5767_MAX_KHZ 108000 //最高频率 108M
#define TEA5767_MIN_KHZ 87500 //最低频率 87.5M
#define TEA5756_HLSI //高混频器 经过测试,使用SM自动搜索功能时必须设此值 手动搜索 无所谓 unsigned long tea5767_Khz_to_pll(unsigned long Khz);
unsigned long tea5767_pll_to_Khz(TEA_DAT dat);
TEA_DAT tea5767_set_receiver(unsigned long Khz);
TEA_DAT tea5767_search(unsigned long Khz);
void tea5767_auto_search();
void tea5767_tiny_auto_search();
void tea5767_write(TEA_DAT dat);
TEA_DAT tea5767_read();
void tea5767_mute(unsigned char ch_num); //Lcd显示
void sohw_search(unsigned long Khz, unsigned char channel);
//存储电台
void save_eeprom(unsigned long Khz, unsigned char channel);
//设定ch
unsigned long set_ch(unsigned char ch_num);
#endif
tea5767.c
#include "tea5767.h" unsigned char mute = ;
void tea5767_mute(unsigned char ch_num)
{
TEA_DAT dat;
unsigned long Khz,pll;
Khz = set_ch(ch_num);
//静音位写 1
pll = tea5767_Khz_to_pll(Khz);
dat.st1 = (mute%) << | (pll>>) & 0x3f;
dat.st2 = pll;
dat.st3 = ;
#ifdef TEA5756_HLSI
dat.st3 |= <<;
#endif
dat.st4 = 0x11;
dat.st5 = 0x40;
tea5767_write(dat); if(mute%)
{
lcd1602_pos();
lcd1602_write_data('M');
lcd1602_write_data('U');
lcd1602_write_data('T');
lcd1602_write_data('E');
}
mute++;
} unsigned long tea5767_Khz_to_pll(unsigned long Khz)
{
#ifdef TEA5756_HLSI
return *(Khz*+)/TEA5767_CLK;
#else
return *(Khz*-)/TEA5767_CLK;
#endif
} unsigned long tea5767_pll_to_Khz(TEA_DAT dat)
{
unsigned long Khz,pll;
pll = ((dat.st1 & 0x3f)<< | dat.st2);
#ifdef TEA5756_HLSI
Khz = (pll*TEA5767_CLK/-*)/;
#else
Khz = (pll*TEA5767_CLK/+*)/;
#endif return Khz;
} void tea5767_write(TEA_DAT dat)
{
i2c_start();
i2c_send(TEA5767_ADDR_W);
i2c_send(dat.st1);
i2c_send(dat.st2);
i2c_send(dat.st3);
i2c_send(dat.st4);
i2c_send(dat.st5);
i2c_stop();
} TEA_DAT tea5767_read()
{
TEA_DAT dat;
i2c_start();
i2c_send(TEA5767_ADDR_R);
dat.st1 = i2c_recv();
dat.st2 = i2c_recv();
dat.st3 = i2c_recv();
dat.st4 = i2c_recv();
dat.st5 = i2c_recv();
i2c_stop();
return dat;
} TEA_DAT tea5767_set_receiver(unsigned long Khz)
{
unsigned long pll;
TEA_DAT dat; pll = tea5767_Khz_to_pll(Khz); /* 发送5个控制位 顺序是 1 2 3 4 5 字节的高位先发
* HLSL = 0 : 4*(102.4*1000000-225*1000)/32768
* HLSL = 1 : 4*(102.4*1000000+225*1000)/32768
* PLL WORD = 0x30ef
*/
dat.st1 = (pll>>) & 0x3f;
dat.st2 = pll;
dat.st3 = ;
#ifdef TEA5756_HLSI
dat.st3 |= <<;
#endif
dat.st4 = 0x11;
dat.st5 = 0x40; tea5767_write(dat);
delayms();
dat = tea5767_read();
return dat;
} //自动搜索 87.5 ~ 108M 由低向高搜索
TEA_DAT tea5767_search(unsigned long Khz)
{
unsigned long pll;
TEA_DAT dat; pll = tea5767_Khz_to_pll(Khz);
dat.st1 = ((pll>>) & 0x3f) | (<<);
dat.st2 = pll;
//搜索停止 信号强度 01:5 10:7 11:10
//dat.st3 = 1 | 1<<7 | 1<<6 |1<<5 ;
dat.st3 = | << | << ;
#ifdef TEA5756_HLSI
dat.st3 |= <<;
#endif dat.st4 = 0x11;
dat.st5 = 0x40;
tea5767_write(dat);
delayms();
dat = tea5767_read();
while(dat.st1 != (dat.st1 | (<<)))
{
dat = tea5767_read();
}
return dat;
} //自动搜台 存台到 eeprom
void tea5767_auto_search()
{
TEA_DAT dat;
unsigned long Khz;
unsigned char if_counter;
unsigned char channel = ;
Khz = TEA5767_MIN_KHZ; //清液晶屏
lcd1602_clear(); dat = tea5767_search(Khz); //检测信号是否满足要求
while(dat.st1 != (dat.st1 | (<<)))
{
//if count 在 0x31 ~ 0x3e 之间
if_counter = (dat.st3 & 0x7f);
if((0x31 < if_counter) && (0x3e > if_counter))
{
//比较leve 电平确认是否收到台 实际测试使用此数 不会漏台
if((dat.st4>>) > )
{
Khz = tea5767_pll_to_Khz(dat); save_eeprom(Khz, channel);
channel++;
}
} //显示当前频率
sohw_search(Khz, channel); //计算搜到台的频率 加上10Khz 后重新搜索 实际测试使用此数 不会漏台
Khz += ;
dat = tea5767_search(Khz);
}
} //细致的搜台
void tea5767_tiny_auto_search()
{
TEA_DAT dat;
unsigned long Khz;
unsigned char channel = ;
Khz = TEA5767_MIN_KHZ;
//Khz = 100000; //清液晶屏
lcd1602_clear(); //擦除eeprom
eeprom_erase(); while(Khz <= TEA5767_MAX_KHZ)
{
dat = tea5767_set_receiver(Khz);
//比较leve 电平确认是否收到台 实际测试使用此数 不会漏台
if((dat.st4>>) > )
{
//存储电台
save_eeprom(Khz, channel++);
} //显示当前频率
sohw_search(Khz, channel);
//频率由低到高 每次增加10Khz
Khz += ;
}
} void sohw_search(unsigned long Khz, unsigned char channel)
{
unsigned char high, low;
lcd1602_pos();
lcd1602_write_data('S');
lcd1602_write_data('e');
lcd1602_write_data('a');
lcd1602_write_data('r');
lcd1602_write_data('c');
lcd1602_write_data('h');
lcd1602_write_data(':');
//输出频率 如果是 100M 以下第1位为空
high = Khz/;
low = Khz%/;
if(high>= )
{
lcd1602_write_data(num_to_str(high/));
}
else
{
lcd1602_write_data(' ');
}
lcd1602_write_data(num_to_str(high%/));
lcd1602_write_data(num_to_str(high%));
lcd1602_write_data('.');
lcd1602_write_data(num_to_str(low));
lcd1602_write_data('M');
lcd1602_write_data('h');
lcd1602_write_data('z'); //显示收到的频道
lcd1602_pos();
lcd1602_write_data('C');
lcd1602_write_data('h');
lcd1602_write_data('a');
lcd1602_write_data('n');
lcd1602_write_data('n');
lcd1602_write_data('e');
lcd1602_write_data('l');
lcd1602_write_data(':'); lcd1602_write_data(num_to_str(channel / ));
lcd1602_write_data(num_to_str(channel % ));
} //存储电台
void save_eeprom(unsigned long Khz, unsigned char channel)
{
TEA_CH ch;
ch.high = Khz/;
ch.low = Khz%/;
eeprom_prog(channel*, ch.high);
eeprom_prog(channel*+, ch.low);
} //设定ch
unsigned long set_ch(unsigned char ch_num)
{
unsigned long Khz;
TEA_CH ch;
ch.high = eeprom_read(ch_num*);
ch.low = eeprom_read(ch_num*+);
//合并为 Khz
Khz = ch.high*; //直接使用 *1000 会计算错误 分为2部正确
Khz *= ;
Khz += ch.low*; //设定接收频率
tea5767_set_receiver(Khz); //清液晶屏
lcd1602_clear(); //显示频道信息
lcd1602_pos();
lcd1602_write_data('C');
lcd1602_write_data('h');
lcd1602_write_data(':'); //频道数从 0 开始计
lcd1602_write_data(num_to_str(ch_num / ));
lcd1602_write_data(num_to_str(ch_num % )); lcd1602_pos();
if(ch.high>= )
{
lcd1602_write_data(num_to_str(ch.high/));
}
else
{
lcd1602_write_data(' ');
}
lcd1602_write_data(num_to_str(ch.high%/));
lcd1602_write_data(num_to_str(ch.high%));
lcd1602_write_data('.');
lcd1602_write_data(num_to_str(ch.low));
lcd1602_write_data('M');
lcd1602_write_data('h');
lcd1602_write_data('z'); return Khz;
}
eeprom.c
#include "eeprom.h" /* 执行完操作以后安全锁 */
void eeprom_lock_ISP()
{
ISP_CONTR = ;
ISP_CMD = ;
ISP_TRIG = ;
ISP_ADDRH = 0xff;
ISP_ADDRL = 0xff;
} /* 擦除指定地址所在的整个扇区 */
void eeprom_erase(unsigned int addr)
{
addr += BASE_ADDR; //发送地址
ISP_ADDRH = addr >> ;
ISP_ADDRL = addr; //发送解锁命令
ISP_CONTR = En_Wait_ISP; //发擦除命令
ISP_CMD = CMD_Erase; //发送触发命令
ISP_TRIG = 0x46;
ISP_TRIG = 0xB9; //最后锁定 ISP 仿误操作
eeprom_lock_ISP();
} unsigned char eeprom_read(unsigned int addr)
{
addr += BASE_ADDR; //发送地址
ISP_ADDRH = addr >> ;
ISP_ADDRL = addr; //发送解锁命令
ISP_CONTR = En_Wait_ISP; //发读命令
ISP_CMD = CMD_Read; //发送触发命令
ISP_TRIG = 0x46;
ISP_TRIG = 0xB9; //最后锁定 ISP 仿误操作
eeprom_lock_ISP(); return ISP_DATA;
} void eeprom_prog(unsigned int addr, unsigned char dat)
{
addr += BASE_ADDR; //发送要保存的数据
ISP_DATA = dat; //发送地址
ISP_ADDRH = addr >> ;
ISP_ADDRL = addr; //发送解锁命令
ISP_CONTR = En_Wait_ISP; //发编程命令
ISP_CMD = CMD_Prog; //发送触发命令
ISP_TRIG = 0x46;
ISP_TRIG = 0xB9; //最后锁定 ISP 仿误操作
eeprom_lock_ISP();
}
最后,在简单说下,自动搜台 和 手动搜台的区别。
自动搜台特点:
1,设一个 LEVE 值后,当到这个值的时候,能自动停止,但是灵活性只有3种,不够灵活。
2,能增长到最高频率时,停止
3,只能用 TEA5756_HLSI
自动搜台流程:
1,设定 好 SM 位 和 SSL 后
2,设一个频率
3,TEA5767 就会自动向上 或 向下 查找,直到 确认收到了信号,就停止 有标识位, 同时比较是否到了最终端,108M
4,程序需要判断,有信号的标识位, 108M 到终点的标识位2个。
5,程序也要比较ADC 电平,再次确认是否搜到了台
手动搜台特点:
1,控制灵活,但要自行控制以上参数。程序编写简单。
手动搜台流程:
1,写入一个频率
2,读取ADC 比较高于某个值时就表示收到了台
3,自行判断,频率范围
51单片机tea5767收音机 红外遥控 自动搜台 存台 DIY的更多相关文章
- TEA5676 + AT24C08 FM收音机 搜台 存台 mmap 实现读写
硬件说明TEA5767 + AT24c08 要使用耳机收听,不加功放芯片,声音非常小. 这2个芯片都支持 3.3 或 5.0 电源支持连线比较简单,sda scl 接到 2440 对应的 排针上,找出 ...
- 4-51单片机WIFI学习(开发板51单片机自动冷启动下载原理)
上一篇链接 http://www.cnblogs.com/yangfengwu/p/8743936.html 这一篇说一下自己板子的51单片机自动冷启动下载原理,我挥舞着键盘和鼠标,发誓要把世界写个明 ...
- 2-51单片机ESP8266学习-AT指令(开发板51单片机自动冷启动下载原理)
前言:了解就行,不必深究 上一篇链接 http://www.cnblogs.com/yangfengwu/p/8720148.html 源码链接:https://pan.baidu.com/s/1wT ...
- 基于Arduino、STM32进行红外遥控信号接收
catalogue . 遥控器原理简介 . 红外遥控原理 . 常见红外遥控器红外线信号传输协议 . 遙控器的发展 . 实验过程 . 攻击面 . 基于STM32实现红外信号解码 1. 遥控器原理简介 0 ...
- 基于STC89C52的oled红外遥控闹钟
这个红外遥控主要是程序通过对按下的键的键码进行解析,并运行相应的功能代码 一次按键动作的遥控编码信息为 32 位串行二进制码.对于二进制信号“0”,一个脉冲占 1.2ms:对于二进制信号“1”,一个脉 ...
- 基于STM32单片机的简单红外循迹的实现
初步接触STM32,采用两路红外传感器实现小车循迹,稍显简略,如有不好的地方,欢迎大家指点改正
- 红外遥控NEC协议使用总结
最近做了一个调试红外遥控三色灯的实习,花了一个多月的时间研究基于NEC协议的红外遥控,下面是这次实习技术方面的总结. 一.NEC协议特征: 8位地址和8位命令长度 每次传输两遍地址(用户码)和命令(按 ...
- 51单片机C语言学习笔记3: 存储器结构
MCS-51单片机在物理结构上有四个存储空间: 1.片内程序存储器2.片外程序存储器3.片内数据存储器4.片外数据存储器 但在逻辑上,即从用户的角度上,8051单片机有三个存储空间: 1.片内外统一编 ...
- 51单片机引脚ALE/PROG/PSEN/EA/Vpp
51单片机的4个控制引脚,其中一个引脚是复位引脚(RST/Vpd),RST是复位引脚,当RST输入端保持2个机器周期以上高电平时,就实现复位. ALE/PROG是地址锁存允许信号输出端,在读写外部存储 ...
随机推荐
- HTML5表单
1.placeholder placeholder="e.g. King Kong" 只需在input元素中加入placeholder属性,其属性值就会默认显示为占位符文字,输入框 ...
- iOS Crash日志
Understanding Crash Reports on iPhone OS https://developer.apple.com/videos/wwdc/2010/?id=317 http:/ ...
- rabbitmq使用心得
因为公司项目需要使用消息中间件,实现相关业务的异步处理,所有选用了rabbitmq.通过看文档,爬过一个一个坑,终于还是实现了相关功能. 直接上配置文件: <?xml version=" ...
- python计算apache总内存
#!/usr/bin/env python import os from subprocess import Popen, PIPE def getPid(): p=Popen(['pidof','h ...
- VECTOR COMPUTATION
COMPUTER OR GANIZATION AND ARCHITECTURE DESIGNING FOR PERFORMANCE NINTH EDITION Although the perform ...
- 关于sort排序
JavaScript的数组排序函数 sort方法,默认是按照ASCII 字符顺序进行升序排列.arrayobj.sort(sortfunction);参数:sortFunction可选项.是用来确定元 ...
- 带你玩转JavaWeb开发之六-mysql基本语法详解及实例(4)
按照分类的名称统计每个分类商品所花的总钱数[排序查询] SQL中对查询的列进行排序,使用关键字order by.默认情况下是升序的排序(从小到大的排序顺序关键字 asc).使用降序排序需要使用关键字d ...
- LeetCode记录(1)——Array
1.Two Sum naive 4.Median of Two Sorted Arrays 找两个已排序数组的中位数 直接数可以过,但很蠢,O(m+n)时间 class Solution { publ ...
- 自动化小应用系列----利用selenium启动多个独立的浏览器
在我们测试的时候对于同一个系统,我们往往需要登陆多个不同的账号,由于cookie的原因,我们只能退出一个账号在登陆另外一颗账号,非常麻烦.我们可以使用selenium来启动浏览器,这样每个浏览器的属性 ...
- tar命令实用介绍
tar -c: 建立压缩档案-x:解压-t:查看内容-r:向压缩归档文件末尾追加文件-u:更新原压缩包中的文件 这五个是独立的命令,压缩解压都要用到其中一个,可以和别的命令连用但只能用其中一个.下面的 ...