查看原文
其他

手把手教你做一个相机红外遥控器

wcc149 电子电路开发学习 2023-06-07

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的库。

程序非常简单,按下按键时,发出一个快门指令:

  1. #include<Adafruit_CircuitPlayground.h>


  2. #define IR_Pin 25

  3. #defineLed_Pin13

  4. #defineButtonA_Pin4

  5. #defineButtonB_Pin5


  6. #define LED_ON digitalWrite(Led_Pin, LOW)

  7. #define LED_OFF digitalWrite(Led_Pin, HIGH)

  8. #define LED_SET(x) digitalWrite(Led_Pin, x)


  9. #define IR_ON digitalWrite(IR_Pin, HIGH)

  10. #define IR_OFF digitalWrite(IR_Pin, LOW)


  11. #define GET_BUTTONA() digitalRead(ButtonA_Pin)

  12. #define GET_BUTTONB() digitalRead(ButtonB_Pin)


  13. int sts = 0;


  14. void setup()

  15. {

  16. pinMode(IR_Pin, OUTPUT);

  17. pinMode(Led_Pin, OUTPUT);

  18. pinMode(ButtonA_Pin, INPUT_PULLDOWN);

  19. pinMode(ButtonB_Pin, INPUT_PULLDOWN);


  20. Serial.begin(9600);

  21. }


  22. //Nikon ML-L3 红外遥控器快门编码:38KHz=26us

  23. void loop()

  24. {

  25. if(GET_BUTTONA())

  26. {

  27. delay(10);

  28. if(GET_BUTTONA())

  29. {

  30. sts = !sts;

  31. LED_SET(sts);

  32. Serial.println("Right button pressed!");

  33. OneShot();

  34. }

  35. }

  36. while(GET_BUTTONA()); //等待松开

  37. }


  38. voidOneShot()

  39. {

  40. int i = 0;

  41. for(i = 76; i > 0; i--) //2100ms

  42. {

  43. IR_ON; //13.5

  44. delayMicroseconds(12);

  45. IR_OFF; //13.7

  46. delayMicroseconds(12);

  47. }

  48. IR_OFF;

  49. delay(28); //2803us

  50. for(i = 15; i > 0; i--) //393us

  51. {

  52. IR_ON;

  53. delayMicroseconds(12);

  54. IR_OFF;

  55. delayMicroseconds(12);

  56. }

  57. IR_OFF;

  58. delayMicroseconds(1580); //1611us


  59. for(i = 15; i > 0; i--)

  60. {

  61. IR_ON;

  62. delayMicroseconds(12);

  63. IR_OFF;

  64. delayMicroseconds(12);

  65. }

  66. delayMicroseconds(3580);

  67. for(i = 15; i > 0; i--)

  68. {

  69. IR_ON;

  70. delayMicroseconds(12);

  71. IR_OFF;

  72. delayMicroseconds(12);

  73. }

  74. IR_OFF;

  75. }

基于STM32的实现

在STM32F103上的实现也是非常简单,主要用到了GPIO控制和精确延时函数。红外控制引脚和按键引脚可根据需要来调整。

  1. //根据Nikon ML-L3红外遥控器编码协议,产生快门指令

  2. voidOneShot(void)

  3. {

  4. int i = 0;

  5. for(i = 76; i > 0; i--) //2100ms

  6. {

  7. IR_ON; //13.5

  8. delay_us(12);

  9. IR_OFF; //13.7

  10. delay_us(12);

  11. }

  12. IR_OFF;

  13. delay_ms(28); //2803us

  14. for(i = 15; i > 0; i--) //393us

  15. {

  16. IR_ON;

  17. delay_us(12);

  18. IR_OFF;

  19. delay_us(12);

  20. }

  21. IR_OFF;

  22. delay_us(1580); //1611us


  23. for(i = 15; i > 0; i--)

  24. {

  25. IR_ON;

  26. delay_us(12);

  27. IR_OFF;

  28. delay_us(12);

  29. }

  30. delay_us(3580);

  31. for(i = 15; i > 0; i--)

  32. {

  33. IR_ON;

  34. delay_us(12);

  35. IR_OFF;

  36. delay_us(12);

  37. }

  38. IR_OFF;

  39. }

基于FPGA的实现

对于FPGA来说,这种波形的产生,时间可以控制的更精确,这取决于FPGA的时钟,时钟越高精度越高,而且可控性更强一些,就是实现起来稍微麻烦一些。

Verilog文件

  1. module ml_l3_pulse_gen(


  2. input clk_50M, //20ns

  3. input rst_n,

  4. input trig, //negedge trig


  5. output pulse

  6. );


  7. parameter T1_2000US = 100000;

  8. parameter T2_28000US = 1400000;

  9. parameter T3_400US = 20000;

  10. parameter T4_1580US = 79000;

  11. parameter T5_400US = T3_400US;

  12. parameter T6_3580US = 179000;

  13. parameter T7_400US = T3_400US;


  14. parameter T1_STS = 1;

  15. parameter T2_STS = 2;

  16. parameter T3_STS = 3;

  17. parameter T4_STS = 4;

  18. parameter T5_STS = 5;

  19. parameter T6_STS = 6;

  20. parameter T7_STS = 7;

  21. parameter T8_STS = 8;

  22. parameter T0_STS = 0;

  23. parameter TIME_38KHZ = 658;


  24. reg [7:0] cur_sts;

  25. reg [31:0] cnt_38khz;

  26. reg [31:0] cnt;

  27. reg [31:0] cnt_max;


  28. reg en;

  29. reg pwm_38k;

  30. reg trig_reg;


  31. assign pulse = (en) ? pwm_38k : 0;


  32. always @ (posedge clk_50M)

  33. begin

  34. trig_reg <= trig;

  35. end


  36. always @ (posedge clk_50M)

  37. begin

  38. if(!rst_n)

  39. cnt_max <= 0;

  40. else

  41. begin

  42. case(cur_sts)

  43. T0_STS : cnt_max <= 0;

  44. T1_STS : cnt_max <= T1_2000US;

  45. T2_STS : cnt_max <= T2_28000US;

  46. T3_STS : cnt_max <= T3_400US;

  47. T4_STS : cnt_max <= T4_1580US;

  48. T5_STS : cnt_max <= T5_400US;

  49. T6_STS : cnt_max <= T6_3580US;

  50. T7_STS : cnt_max <= T7_400US;

  51. default: cnt_max <= 0;

  52. endcase

  53. end

  54. end


  55. always @ (posedge clk_50M)

  56. begin

  57. if(!rst_n)

  58. en <= 0;

  59. else

  60. begin

  61. case(cur_sts)

  62. 1,3,5,7: en <= 1;

  63. 2,4,6,0: en <= 0;

  64. default: en <= 0;

  65. endcase

  66. end

  67. end


  68. always @ (posedge clk_50M)

  69. begin

  70. if(!rst_n)

  71. cnt <= 0;

  72. else

  73. begin

  74. if(cur_sts != T0_STS && cnt < cnt_max)

  75. cnt <= cnt + 1;

  76. else

  77. cnt <= 0;

  78. end

  79. end


  80. always @ (posedge clk_50M)

  81. begin

  82. if(!rst_n)

  83. cur_sts <= T0_STS;

  84. else

  85. begin

  86. case(cur_sts)

  87. T0_STS:

  88. if(trig_reg & !trig)

  89. cur_sts <= T1_STS;

  90. T1_STS:

  91. if(cnt == T1_2000US)

  92. cur_sts <= T2_STS;

  93. T2_STS:

  94. if(cnt == T2_28000US)

  95. cur_sts <= T3_STS;

  96. T3_STS:

  97. if(cnt == T3_400US)

  98. cur_sts <= T4_STS;

  99. T4_STS:

  100. if(cnt == T4_1580US)

  101. cur_sts <= T5_STS;

  102. T5_STS:

  103. if(cnt == T5_400US)

  104. cur_sts <= T6_STS;

  105. T6_STS:

  106. if(cnt == T6_3580US)

  107. cur_sts <= T7_STS;

  108. T7_STS:

  109. if(cnt == T7_400US)

  110. cur_sts <= T0_STS;

  111. default:

  112. cur_sts <= T0_STS;

  113. endcase

  114. end

  115. end


  116. /* 38KHz counter */

  117. always @ (posedge clk_50M)

  118. begin

  119. if(!rst_n)

  120. cnt_38khz <= 0;

  121. else

  122. begin

  123. if(en && cnt_38khz < TIME_38KHZ)

  124. cnt_38khz <= cnt_38khz + 1;

  125. else

  126. cnt_38khz <= 0;

  127. end

  128. end


  129. /* generate 38KHz pwm */

  130. always @ (posedge clk_50M)

  131. begin

  132. if(!rst_n)

  133. pwm_38k <= 0;

  134. elseif(cnt_38khz == TIME_38KHZ)

  135. pwm_38k <= ~pwm_38k;

  136. end


  137. endmodule

仿真test bench 文件

  1. `timescale 1ns/100ps


  2. module ml_l3_pulse_gen_tb;


  3. parameter SYSCLK_PERIOD = 20;// 50MHZ


  4. reg SYSCLK;

  5. reg NSYSRESET;

  6. reg trig;


  7. wire pulse;


  8. initial

  9. begin

  10. SYSCLK = 1'b0;

  11. NSYSRESET = 1'b0;

  12. trig = 0;

  13. end


  14. initial

  15. begin

  16. #(SYSCLK_PERIOD * 10 )

  17. NSYSRESET = 1'b0;

  18. trig = 0;

  19. #(SYSCLK_PERIOD * 1000 )

  20. NSYSRESET = 1'b1;

  21. #(SYSCLK_PERIOD * 10 )

  22. trig = 1;

  23. #SYSCLK_PERIOD

  24. trig = 0;

  25. end


  26. always @(SYSCLK)

  27. #(SYSCLK_PERIOD / 2.0) SYSCLK <= !SYSCLK;


  28. ml_l3_pulse_gen ml_l3_pulse_gen_0 (

  29. // Inputs

  30. .clk_50M(SYSCLK),

  31. .rst_n(NSYSRESET),

  32. .trig(trig),


  33. // Outputs

  34. .pulse(pulse)

  35. );


  36. endmodule

实际使用效果

对于实际的脉冲时间,不用特别的精确,误差不要太大就行,最好使用示波器测量以下脉冲的时间。对于制作好的遥控器,只需要在相机周围按下按钮就可实现遥控快门。相机机身的红外接收头前后各有一个,可以方便在不同的位置遥控。如下图所示。

总结

这款尼康ML-L3红外遥控器的实现原理非常简单,可扩展性强,可以根据需要自己添加功能,如添加固定时间间隔拍摄,固定张数拍摄,用于拍摄制作延时视频所需要的图片素材。当然,也可以使用手机上的遥控器来实现这个功能。

代码获取

以上代码已经开源在Github和Gitee平台,地址如下。

  • Github开源地址:

    https://github.com/whik/nikon-wireless-remote-control-ML-L3-DIY.git

  • Gitee开源地址 : 

    https://gitee.com/whik/nikon-wireless-remote-control-ML-L3-DIY.git

没有使用代码托管平台的朋友,可以在公众号后台回复【尼康遥控器】也可以获取代码。

参考资料

文中的ML-L3拆解图,Arduino代码参考自以下链接内容。

  • http://www.bigmike.it/ircontrol/

  • https://www.sbprojects.net/projects/nikon/index.php

  • https://learn.adafruit.com/ir-sensor/making-an-intervalometer

您可能也对以下帖子感兴趣

文章有问题?点此查看未经处理的缓存