手把手教你做一个相机红外遥控器
扫描二维码
随时随地手机看文章
拆解ML-L3遥控器
为了实现ML-L3遥控器的功能,我们首先要了解无线遥控器的原理。当然最好的方式就是拆解一个ML-L3,然后看看内部的电路,然后测出红外的编码。但是手头又没有这样的一个遥控器,有国外的网友已经拆解了并且测出了红外编码的波形,如下图。官方遥控器PCB板:山寨遥控器PCB板:从PCB板来看,果然还是官方的用料更足一些,通过测量红外发射引脚,在按下按钮时,红外发射头会发出一串脉冲信号,如下图所示:其中黑色的部分是38KHz的PWM方波,空白部分是低电平,以上波形就表示一个快门指令。红外遥控协议主要有两种:NEC协议和Philips RC-5协议,NEC采用PWM方式调制,RC-5采用PPM方式调制。其中使用最多的是NEC协议,38KHz载波,一般是由引导码 地址码 地址反码 数据 数据反码构成。其中逻辑0和逻辑1的编码如下:
基于Arduino的实现
好了,知道了快门指令的红外波形,我们只需要写个函数实现这一串脉冲信号就可以了。Arduino开发板,我手头上有的是Circuit Playground Express这款开发板,板载一对红外发射接收头,和两路按键,对于我们的功能已经是足够用了。在使用前需要先安装Cortex-M0的库。程序非常简单,按下按键时,发出一个快门指令:#include
#define IR_Pin 25
#defineLed_Pin13
#defineButtonA_Pin4
#defineButtonB_Pin5
#define LED_ON digitalWrite(Led_Pin, LOW)
#define LED_OFF digitalWrite(Led_Pin, HIGH)
#define LED_SET(x) digitalWrite(Led_Pin, x)
#define IR_ON digitalWrite(IR_Pin, HIGH)
#define IR_OFF digitalWrite(IR_Pin, LOW)
#define GET_BUTTONA() digitalRead(ButtonA_Pin)
#define GET_BUTTONB() digitalRead(ButtonB_Pin)
int sts = 0;
void setup()
{
pinMode(IR_Pin, OUTPUT);
pinMode(Led_Pin, OUTPUT);
pinMode(ButtonA_Pin, INPUT_PULLDOWN);
pinMode(ButtonB_Pin, INPUT_PULLDOWN);
Serial.begin(9600);
}
//Nikon ML-L3 红外遥控器快门编码:38KHz=26us
void loop()
{
if(GET_BUTTONA())
{
delay(10);
if(GET_BUTTONA())
{
sts = !sts;
LED_SET(sts);
Serial.println("Right button pressed!");
OneShot();
}
}
while(GET_BUTTONA()); //等待松开
}
voidOneShot()
{
int i = 0;
for(i = 76; i > 0; i--) //2100ms
{
IR_ON; //13.5
delayMicroseconds(12);
IR_OFF; //13.7
delayMicroseconds(12);
}
IR_OFF;
delay(28); //2803us
for(i = 15; i > 0; i--) //393us
{
IR_ON;
delayMicroseconds(12);
IR_OFF;
delayMicroseconds(12);
}
IR_OFF;
delayMicroseconds(1580); //1611us
for(i = 15; i > 0; i--)
{
IR_ON;
delayMicroseconds(12);
IR_OFF;
delayMicroseconds(12);
}
delayMicroseconds(3580);
for(i = 15; i > 0; i--)
{
IR_ON;
delayMicroseconds(12);
IR_OFF;
delayMicroseconds(12);
}
IR_OFF;
}
基于STM32的实现
在STM32F103上的实现也是非常简单,主要用到了GPIO控制和精确延时函数。红外控制引脚和按键引脚可根据需要来调整。//根据Nikon ML-L3红外遥控器编码协议,产生快门指令
voidOneShot(void)
{
int i = 0;
for(i = 76; i > 0; i--) //2100ms
{
IR_ON; //13.5
delay_us(12);
IR_OFF; //13.7
delay_us(12);
}
IR_OFF;
delay_ms(28); //2803us
for(i = 15; i > 0; i--) //393us
{
IR_ON;
delay_us(12);
IR_OFF;
delay_us(12);
}
IR_OFF;
delay_us(1580); //1611us
for(i = 15; i > 0; i--)
{
IR_ON;
delay_us(12);
IR_OFF;
delay_us(12);
}
delay_us(3580);
for(i = 15; i > 0; i--)
{
IR_ON;
delay_us(12);
IR_OFF;
delay_us(12);
}
IR_OFF;
}
基于FPGA的实现
对于FPGA来说,这种波形的产生,时间可以控制的更精确,这取决于FPGA的时钟,时钟越高精度越高,而且可控性更强一些,就是实现起来稍微麻烦一些。Verilog文件module ml_l3_pulse_gen(
input clk_50M, //20ns
input rst_n,
input trig, //negedge trig
output pulse
);
parameter T1_2000US = 100000;
parameter T2_28000US = 1400000;
parameter T3_400US = 20000;
parameter T4_1580US = 79000;
parameter T5_400US = T3_400US;
parameter T6_3580US = 179000;
parameter T7_400US = T3_400US;
parameter T1_STS = 1;
parameter T2_STS = 2;
parameter T3_STS = 3;
parameter T4_STS = 4;
parameter T5_STS = 5;
parameter T6_STS = 6;
parameter T7_STS = 7;
parameter T8_STS = 8;
parameter T0_STS = 0;
parameter TIME_38KHZ = 658;
reg [7:0] cur_sts;
reg [31:0] cnt_38khz;
reg [31:0] cnt;
reg [31:0] cnt_max;
reg en;
reg pwm_38k;
reg trig_reg;
assign pulse = (en) ? pwm_38k : 0;
always @ (posedge clk_50M)
begin
trig_reg <= trig;
end
always @ (posedge clk_50M)
begin
if(!rst_n)
cnt_max <= 0;
else
begin
case(cur_sts)
T0_STS : cnt_max <= 0;
T1_STS : cnt_max <= T1_2000US;
T2_STS : cnt_max <= T2_28000US;
T3_STS : cnt_max <= T3_400US;
T4_STS : cnt_max <= T4_1580US;
T5_STS : cnt_max <= T5_400US;
T6_STS : cnt_max <= T6_3580US;
T7_STS : cnt_max <= T7_400US;
default: cnt_max <= 0;
endcase
end
end
always @ (posedge clk_50M)
begin
if(!rst_n)
en <= 0;
else
begin
case(cur_sts)
1,3,5,7: en <= 1;
2,4,6,0: en <= 0;
default: en <= 0;
endcase
end
end
always @ (posedge clk_50M)
begin
if(!rst_n)
cnt <= 0;
else
begin
if(cur_sts != T0_STS