红外遥控器
回帖(1)
2022-2-15 14:01:30
ML-L3是用于尼康部分型号相机的无线红外遥控器,可以通过红外方式来控制快门的释放,支持B门拍摄。官方售价100RMB左右,山寨版售价10RMB左右。虽然也能实现基本的遥控功能,但是功能还是比较单一,如不能实现定时拍摄,即用来拍摄制作延时视频的素材。本篇文章介绍如何通过Arduino、MCU或FPGA来控制红外发射器,产生快门指令从而实现无线遥控快门的功能。
拆解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=26usvoid 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); //2803usfor(i = 15; i > 0; i--) //393us{ IR_ON; delayMicroseconds(12); IR_OFF; delayMicroseconds(12);} IR_OFF; delayMicroseconds(1580); //1611usfor(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); //2803usfor(i = 15; i > 0; i--) //393us{ IR_ON; delay_us(12); IR_OFF; delay_us(12);} IR_OFF; delay_us(1580); //1611usfor(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, //20nsinput rst_n,input trig, //negedge trigoutput 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;endalways @ (posedge clk_50M)beginif(!rst_n) cnt_max <= 0;else begincase(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; endcaseendendalways @ (posedge clk_50M)beginif(!rst_n) en <= 0;elsebegincase(cur_sts)1,3,5,7: en <= 1;2,4,6,0: en <= 0;default: en <= 0; endcaseendendalways @ (posedge clk_50M)beginif(!rst_n) cnt <= 0;elsebeginif(cur_sts != T0_STS && cnt < cnt_max) cnt <= cnt + 1;else cnt <= 0;endendalways @ (posedge clk_50M)beginif(!rst_n) cur_sts <= T0_STS;else begincase(cur_sts) T0_STS:if(trig_reg & !trig) cur_sts <= T1_STS; T1_STS:if(cnt == T1_2000US) cur_sts <= T2_STS; T2_STS:if(cnt == T2_28000US) cur_sts <= T3_STS; T3_STS:if(cnt == T3_400US) cur_sts <= T4_STS; T4_STS:if(cnt == T4_1580US) cur_sts <= T5_STS; T5_STS:if(cnt == T5_400US) cur_sts <= T6_STS; T6_STS:if(cnt == T6_3580US) cur_sts <= T7_STS; T7_STS:if(cnt == T7_400US) cur_sts <= T0_STS;default: cur_sts <= T0_STS; endcaseendend/* 38KHz counter */always @ (posedge clk_50M)beginif(!rst_n) cnt_38khz <= 0;else beginif(en && cnt_38khz < TIME_38KHZ) cnt_38khz <= cnt_38khz + 1;else cnt_38khz <= 0;endend/* generate 38KHz pwm */always @ (posedge clk_50M)beginif(!rst_n) pwm_38k <= 0;elseif(cnt_38khz == TIME_38KHZ) pwm_38k <= ~pwm_38k;endendmodule仿真test bench 文件
`timescale 1ns/100psmodule ml_l3_pulse_gen_tb;parameter SYSCLK_PERIOD = 20;// 50MHZreg SYSCLK;reg NSYSRESET;reg trig;wire pulse;initialbegin SYSCLK = 1'b0; NSYSRESET = 1'b0; trig = 0;endinitialbegin #(SYSCLK_PERIOD * 10 ) NSYSRESET = 1'b0; trig = 0; #(SYSCLK_PERIOD * 1000 ) NSYSRESET = 1'b1; #(SYSCLK_PERIOD * 10 ) trig = 1; #SYSCLK_PERIOD trig = 0;endalways @(SYSCLK) #(SYSCLK_PERIOD / 2.0) SYSCLK <= !SYSCLK;ml_l3_pulse_gen ml_l3_pulse_gen_0 ( // Inputs .clk_50M(SYSCLK), .rst_n(NSYSRESET), .trig(trig), // Outputs .pulse(pulse));endmodule实际使用效果
对于实际的脉冲时间,不用特别的精确,误差不要太大就行,最好使用示波器测量以下脉冲的时间。对于制作好的遥控器,只需要在相机周围按下按钮就可实现遥控快门。相机机身的红外接收头前后各有一个,可以方便在不同的位置遥控。如下图所示。
总结
这款尼康ML-L3红外遥控器的实现原理非常简单,可扩展性强,可以根据需要自己添加功能,如添加固定时间间隔拍摄,固定张数拍摄,用于拍摄制作延时视频所需要的图片素材。当然,也可以使用手机上的遥控器来实现这个功能。
ML-L3是用于尼康部分型号相机的无线红外遥控器,可以通过红外方式来控制快门的释放,支持B门拍摄。官方售价100RMB左右,山寨版售价10RMB左右。虽然也能实现基本的遥控功能,但是功能还是比较单一,如不能实现定时拍摄,即用来拍摄制作延时视频的素材。本篇文章介绍如何通过Arduino、MCU或FPGA来控制红外发射器,产生快门指令从而实现无线遥控快门的功能。
拆解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=26usvoid 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); //2803usfor(i = 15; i > 0; i--) //393us{ IR_ON; delayMicroseconds(12); IR_OFF; delayMicroseconds(12);} IR_OFF; delayMicroseconds(1580); //1611usfor(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); //2803usfor(i = 15; i > 0; i--) //393us{ IR_ON; delay_us(12); IR_OFF; delay_us(12);} IR_OFF; delay_us(1580); //1611usfor(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, //20nsinput rst_n,input trig, //negedge trigoutput 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;endalways @ (posedge clk_50M)beginif(!rst_n) cnt_max <= 0;else begincase(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; endcaseendendalways @ (posedge clk_50M)beginif(!rst_n) en <= 0;elsebegincase(cur_sts)1,3,5,7: en <= 1;2,4,6,0: en <= 0;default: en <= 0; endcaseendendalways @ (posedge clk_50M)beginif(!rst_n) cnt <= 0;elsebeginif(cur_sts != T0_STS && cnt < cnt_max) cnt <= cnt + 1;else cnt <= 0;endendalways @ (posedge clk_50M)beginif(!rst_n) cur_sts <= T0_STS;else begincase(cur_sts) T0_STS:if(trig_reg & !trig) cur_sts <= T1_STS; T1_STS:if(cnt == T1_2000US) cur_sts <= T2_STS; T2_STS:if(cnt == T2_28000US) cur_sts <= T3_STS; T3_STS:if(cnt == T3_400US) cur_sts <= T4_STS; T4_STS:if(cnt == T4_1580US) cur_sts <= T5_STS; T5_STS:if(cnt == T5_400US) cur_sts <= T6_STS; T6_STS:if(cnt == T6_3580US) cur_sts <= T7_STS; T7_STS:if(cnt == T7_400US) cur_sts <= T0_STS;default: cur_sts <= T0_STS; endcaseendend/* 38KHz counter */always @ (posedge clk_50M)beginif(!rst_n) cnt_38khz <= 0;else beginif(en && cnt_38khz < TIME_38KHZ) cnt_38khz <= cnt_38khz + 1;else cnt_38khz <= 0;endend/* generate 38KHz pwm */always @ (posedge clk_50M)beginif(!rst_n) pwm_38k <= 0;elseif(cnt_38khz == TIME_38KHZ) pwm_38k <= ~pwm_38k;endendmodule仿真test bench 文件
`timescale 1ns/100psmodule ml_l3_pulse_gen_tb;parameter SYSCLK_PERIOD = 20;// 50MHZreg SYSCLK;reg NSYSRESET;reg trig;wire pulse;initialbegin SYSCLK = 1'b0; NSYSRESET = 1'b0; trig = 0;endinitialbegin #(SYSCLK_PERIOD * 10 ) NSYSRESET = 1'b0; trig = 0; #(SYSCLK_PERIOD * 1000 ) NSYSRESET = 1'b1; #(SYSCLK_PERIOD * 10 ) trig = 1; #SYSCLK_PERIOD trig = 0;endalways @(SYSCLK) #(SYSCLK_PERIOD / 2.0) SYSCLK <= !SYSCLK;ml_l3_pulse_gen ml_l3_pulse_gen_0 ( // Inputs .clk_50M(SYSCLK), .rst_n(NSYSRESET), .trig(trig), // Outputs .pulse(pulse));endmodule实际使用效果
对于实际的脉冲时间,不用特别的精确,误差不要太大就行,最好使用示波器测量以下脉冲的时间。对于制作好的遥控器,只需要在相机周围按下按钮就可实现遥控快门。相机机身的红外接收头前后各有一个,可以方便在不同的位置遥控。如下图所示。
总结
这款尼康ML-L3红外遥控器的实现原理非常简单,可扩展性强,可以根据需要自己添加功能,如添加固定时间间隔拍摄,固定张数拍摄,用于拍摄制作延时视频所需要的图片素材。当然,也可以使用手机上的遥控器来实现这个功能。
举报
更多回帖