本帖最后由 雷精灵2046 于 2019-6-14 09:28 编辑
$ W8 K3 r, X3 c% `0 h1 \* n/ I3 p4 E
没有用复杂的分立元件搭建发射接收电路,直接用的现成的2.4G无线模块jdy-40。
w' x5 f$ N1 G( ?" j
8 ~6 l) v+ S& X
9 t0 T# [$ ~' Z. r发射端靠3.7V锂电池供电,用了一片SOP8封装的STC单片机,负责产生连发脉冲,同时负责检测电池电量,电压低于3V左右的时候触发低压中断,低压指示LED就会亮起来告知该充电了。平时发射模块和单片机均处于睡眠状态,有按键按下才会唤醒,从而实现省电。实测睡眠状态下整机电流约1~2uA。板子洗了一下稍微干净了一点。因为对于微功耗设备来说,焊接残余的助焊剂可能会增加漏电流。+ `" v* B9 F( X
6 d- P" U5 n, W: U买了一个火花的FC山寨手柄,去掉里面的牛屎板子,并修正一下塑料挡板,把锂电池和充电模块装进去。
+ x% t. c. y* L& ^$ q7 E5 K# C s4 Q为了减少体积,充电模块裁剪得十分小,并且更换了充电电阻使之适应这个小小的锂电池。这么小容量的电池,也不需要多么大的充电电流是吧?
& b0 V3 C; D3 T" B& s3 O然后统统用热熔胶粘上。充电电流比较小,所以发热很少,不会熔化热熔胶。' Q/ m9 M# P z7 K
4 u- F% L2 l7 C' Y3 D* K) P8 Z- x) ?7 Z. ~
* ?7 }9 Q( A& G6 Q; q, c最后把按键、导电橡胶和电路板装上去。! s9 G. _+ z- l$ R
精心裁剪了电路板,正好扣上,不会阻碍其他元件,也不会影响后盖和拧螺丝。
, I0 b- V$ |/ R! U, L- b% c1 B; u+ l5 z" j0 B
/ }/ r! W2 J% `) \: n
完工!2 S4 a6 m9 L6 Q
7 t( R: J' ~- \. u. B0 e9 a7 p+ } N# R
% U- w" ]9 A+ ]9 ^# {/ y这是充电的MicroUSB插座。旁边的小孔用于透出低压报警LED的光线。0 G2 h) v2 R8 }/ S
& w6 ^: G4 P0 H2 H8 m- `
6 U5 x) y" T% j$ P
接收端用一片DIP16封装的STC单片机模拟CD4021。为啥要用单片机模拟而不是直接用CD4021?因为jdy-40模块接收端在有按键按下的时候输出高电平,无按键按下输出低电平,和普通手柄正好相反。当然我也可以用两片74HC00或者74HC04之类的逻辑门进行反相,但那就增加了芯片的数量。对于FC手柄这种工作频率并不高的设备来说,STC单片机完全可以胜任模拟工作。
* [+ n* e# A5 l
. i9 S# ]& Z5 a; w
# p! b: ~8 |; M8 q
7 C" v* a/ P5 H2 R( B8 C电路设计有点小小的错误,所以有几根飞线。原本设计使用低压差LDO是XC6206,结果在做这个接收模块的时候,买的LDO居然找不到了!无奈只好用了AMS1117。幸好对于接收模块来说是5V降到3.3V,1117可以胜任。要是发射端手柄的话,3.7V降到3.3V,那就非得用6206不可了。3 v, \6 Z- l) k# d' e8 c0 [
8 D$ L3 u- \2 h$ x+ l) e
实测十分灵敏,延迟极小几乎感觉不出来。可惜这山寨手柄的手感并不佳,尤其是方向键,软塌塌的。看来我得买个剪线手柄改装。% q/ J1 u; ?; g; D8 m: E
+ h1 S- l {& c }3 z2 n
这是电路图,有兴趣的朋友可以参考。
$ z, p) Z# h- b G3 Y+ b
: D5 P7 W* @0 `7 N. x8 B/ s, W: I% Q4 u' Y; ?. c1 J$ e6 g. @
我信奉开源主义,十分痛恨把技术藏着掖着。好东西大家分享嘛!所以两个单片机的源代码大公开!7 E# q: T3 _7 W- Q: c v1 v8 p
编译器:Keil 51。7 D9 Y! `! @4 @6 C. f+ F5 d
7 W3 L% |' ~: ]+ F2 O
发射端:
' a/ t. A A' }/ g+ n+ _, H t# T- #include <INTRINS.H>
% ~; ?$ P2 l; Q& [1 f0 H - #include "STC15W.h"/ \; \; c2 s& T7 E6 x! Z# V9 R
- # @6 ?8 e$ J& z4 P; S2 _) _
- & ~- R$ q. `5 {6 ]1 ?
- /*
/ Q6 h) h7 k4 [% X! s - --------------------------; n! B9 C" s! ]9 `+ Z; y
- |1 (OUT) \__/ (INT1) 8| {2 _1 F( L4 \$ s/ u) l$ t; k6 N
- | |1 A1 z N% x4 h9 c$ @, @
- |2 (VCC) (INT0) 7|3 w* X3 {* ?& [ I8 [; d
- | STC15W204S |; u) x! {: b" j2 l- `. B6 s
- |3 (LED) (TXD) 6|" S4 V4 E- x7 q3 F5 ]4 _6 Z
- | |4 B; t3 |8 [/ y2 m
- |4 (GND) (RXD) 5|
) |" u- x5 @. ]+ B# I5 q3 f - --------------------------
$ X. m( d5 ?* |& D- f8 K: t - 5 C/ R! J, o: i
- LED ---|<|---[===]--- VCC
( b( \, [! G, _" V5 n) \5 R - Red 3306 j. f; f _* M8 t7 W( z- ~
# G- y* `2 }( y: z- Fosc = 6MHz
1 W/ ^8 S3 R" e& y. H9 m - */3 \" W& y# |+ e" R
# N& @$ K4 i) v! `0 H# h8 }7 a8 o
1 |) C8 c9 x9 {7 J/ c# \0 l! A( e5 D- // 矩形波输出脚
8 P# x) P& q# k, H* m8 E - sbit OUT = P5^4;
; y7 R+ M, g/ h - // 低压指示灯引脚# K* n# z7 s' |- N# |# A; U
- sbit LED = P5^5;
& O5 p9 s k/ c2 @4 c* y0 }
% K! g! p0 h* m6 L- // 停机标志位1 m2 @9 y, G3 o' t# m8 a0 Y
- bit isReadyToHalt = 0;
) b9 V/ f0 w1 D6 l4 U9 @5 M% a
: c& j( S) ?4 H- // 矩形波次数累计
- U# S( f& o& j. G U/ V - static volatile unsigned char count = 0;6 ~' k; m0 z$ P, V! S
4 G b G- n* g- n
5 k0 C7 z! p% y6 S+ S- static void GPIO_Init(){
; l7 W8 E0 t1 c - // P3口设置为准双向,默认靠内部弱上拉输出高电平8 ~. a- s7 @2 z( U3 Z
- P3M0 = 0;/ L4 i7 P) G( M. C- y9 {
- P3M1 = 0;$ q/ O/ C' U6 T; B( f% T0 V& t
- P3 = 0xFF;
- u1 W; M* L7 g: {- Y1 T - // P5口设置为准双向,默认靠内部弱上拉输出高电平
4 a1 U5 g( S( c! M/ |, `3 l' }( c - P5M0 = 0;
2 w! y- N4 d" ` - P5M1 = 0;" Y9 A2 S% V; V' [- |5 j9 Q
- P5 = 0xFF;/ E$ m: [6 i1 a$ T0 z
- }
% b$ G' n' |; q7 ?1 I - " ~' I% |$ `) F0 H( p: u7 Q( L
- static void Timer0_Init(){7 R: [( [2 N, i L# g" p
- // 16毫秒@6.000MHz
1 C) x5 M* g9 D* p - AUXR &= 0x7F; // 定时器时钟12T模式- r8 T/ V9 t2 j8 `3 p0 P
- TMOD &= 0xF0; // 设置定时器模式6 O+ S* P' Q4 A) [2 r8 o1 j
- TL0 = 0xC0; // 设置定时初值
! ]8 }' x% [" i5 Y - TH0 = 0xE0; // 设置定时初值
( t" f' K6 F& z2 d" s; A- K8 S' ` - TF0 = 0; // 清除TF0标志
; g$ r/ {: h) N3 `- @2 Y. l$ E - TR0 = 1; // 定时器0开始计时
# R o* ~' I7 a# V4 O - }
/ u0 u1 @+ {+ B4 ^; g - / u/ l1 [* A9 g9 L9 O
- static void Interrupt_Init(){) ^+ W& y2 y6 q' v+ M8 U( G7 N
- // INT0中断触发类型为下降沿触发
/ V2 p( }( z2 G) I( Q. i; H7 I - IT0 = 1;& ]. z1 H* l7 A4 _. N
- // 允许INT0中断8 A0 D2 h: I6 E$ [) S6 e
- EX0 = 1;
8 p9 t. W' g# p - // INT1中断触发类型为下降沿触发1 r2 F2 g# K9 E2 L3 i
- IT1 = 1;
' Y0 U" d+ F6 d% y1 G - // 允许INT1中断
( }+ S3 X3 d& b; j9 k" J5 H - EX1 = 1;6 o2 n% Q n; V: k8 }1 e( R! T
- // 允许定时器0中断
% y6 f5 P" N& z) ~- u( d6 h; x - ET0 = 1;
0 m4 D6 J5 b" d# B; m9 @ - // 允许全局中断
/ j( n& I/ {6 N! [- m/ X - EA = 1;
) ?9 A6 {1 I# ?$ o2 i - }" ^9 ], e) L5 r* U3 |# l) ~ j
- 5 T5 }7 |0 P& B% L
- static void INT0_Isr() interrupt 0{
4 h( U, v" p& d1 b1 v( [ - // 矩形波次数累计归零( ], K% H" ^+ D, R4 d% g
- count = 0;; \0 k# {2 P8 ~5 o3 i
- // 重开定时器0中断! `2 a' C6 A ~5 V9 v
- ET0 = 1;
8 ~( M# v0 c$ S. V' C - }1 S, x; L% e! H4 ~. o* h. X
* I3 h/ z3 k; B7 f- static void Timer0_Isr() interrupt 1{
2 p5 Y: l2 b u: k8 G# F/ [- L - // 输出矩形波
& E/ R) {: k" b! Y - OUT = ~OUT;4 a5 y. r) w6 Q8 x0 _8 T. N
- // 累计矩形波次数
3 }& c" M# N- M6 w - count++;
) H( ]4 g8 Y# B# c. Y. c - // 超过250个矩形波,准备停机
4 ^& d0 o7 L1 |3 W* T- M - if(count > 250){0 `. m! W0 A. f" G* i
- count = 0;! W2 |: C! y3 E- L
- isReadyToHalt = 1;
# z0 T3 C0 z$ T7 y! e8 F - }; U8 r2 H- u) u5 W
- }
/ m g6 _& {. ^& z - & f9 W) T% A+ P/ v
- static void INT1_Isr() interrupt 2{
4 r3 m- F- d' [" `; S3 R5 F; H& _$ K - // 矩形波次数累计归零
+ M1 r) ~2 p# _+ r" T! G* Z' `7 n - count = 0;
$ A$ Y! U3 r* }7 W0 k - // 重开定时器0中断
. c2 l% g- B4 i - ET0 = 1;
/ j, L' G: j. q4 {3 Y4 H" I9 j - }
, F/ z4 y+ e) o0 n Z - , h# F/ ^3 P4 q+ O) \6 i0 D+ y
, @( r5 x" F* |3 z- i. a" n6 Z" q- void main(){
- y4 F9 G2 D I+ h3 d - // 初始化P3和P5口
$ w& T+ p& X/ @! o V7 c1 F- z - GPIO_Init();2 R& j7 F' O$ l. j; A
- // 初始化定时器0
. k- s3 [! \" v( `- l - Timer0_Init();
t: H% C- N. e' t u6 Y - // 初始化中断
4 }- S& Z, o, g: [+ G - Interrupt_Init();
( q) ~' u8 N i. k- d -
/ ?! a1 n# k: d$ D' w8 A - while(1){
; T6 {+ w" O+ o$ V9 D - // 检测电量) s* [( G7 f" W% @: O- O
- if(PCON & (1 << 5)){
" T$ s0 M" {# ]0 {! v' z+ X - // 如果电量低,低压指示灯亮" r0 M4 N8 E6 R( ]4 X8 v5 h
- LED = 0;
% ?% b% D5 \" Q! ` k. W+ l5 g - // 清电量检测硬件标志位( x) R Y W( S
- PCON &= ~(1 << 5);
! Y3 x. p$ L) U2 h; J R# k" z - }else{
5 e! {+ B, D3 {5 {. {* s- E$ { - // 电量正常,低压指示灯灭
# `- e8 N$ x) G5 ^ - LED = 1;* ?0 J8 U& J) ~& Q. U1 e" a q
- }; d8 U @* b/ X& K' n0 x
. F2 ?) F0 Q& C. V" ~! F- J- // 检测到停机标志
* {) k3 S% i" m6 l8 S( i - if(isReadyToHalt == 1){
6 x d! p) F J& \ - // 暂时关闭定时器0中断/ j1 ~! Q8 A7 K1 H" `$ D2 g6 M+ q
- ET0 = 0;) E/ }5 ?+ j% p6 e6 E. u- O1 I
- // 停机之前先把矩形波输出脚置低电平,以方便INT0和INT1唤醒CPU
: F( K8 Q7 P5 a6 S - OUT = 0;5 `1 ]: i \. w% S+ [ }
- // 停机之前先把低压指示灯灭掉,以省电) c- q( V- F) E4 v5 H) v
- LED = 1; n }* q& ^+ t0 C9 [9 x
- // 进入停机模式
( @% ]- p& M' y( r - PCON |= 0x02;0 O8 a( N1 c2 q% @/ W+ c+ S' j
- _nop_();
) u4 ~" i% D# b( h8 Q8 Z4 y4 Y - _nop_();7 m$ T8 }+ C: ~
- _nop_();
6 Z! d) w% J# a$ B" v' D0 C% l - _nop_();
2 @: l& G6 t% v8 H& [1 D% j' o: b0 k9 M - // 唤醒,清标志位
3 W% ~# x$ |4 l/ Y4 P4 s0 P - isReadyToHalt = 0;
: J) p3 S9 n- B- K- n. M/ j - // 重开定时器0中断, Q4 ?* z& y, O
- ET0 = 1;5 S: m4 n- O5 |; \/ b8 r
- }7 r+ V }5 ~0 Q
- }
9 l+ c2 I" H z: S; u; } - }4 u1 |6 E4 R* ~0 c0 f/ Z. G
复制代码
* r0 u! S- D7 G1 ?- K+ x. E6 ]/ `硬件参数配置:" J# Z3 \) G t
5 ~2 [) \0 {; x' z: ]) z* O
# A/ }- f- Z) G' W接收端:! F5 G$ u6 s9 N1 b: o
- #include <INTRINS.H>! I' b B: M6 K
- #include "STC15W.h"( P" `- e5 F# M+ A! o& B
: z* `5 ]( ]; X6 l: S- ' I" }( u2 g+ y& O) X$ ~
- /*4 s! A( Q& B* |
- & A! {: I6 E& F; l1 j
- *---------------------------*
1 j3 N7 n" d& F+ Z9 [3 ~# U' ~ - |1 (GPIO2) \__/ (GPIO1)16|
# D- O% }2 {; ? - | |
, S0 @8 y* X% i2 P; H! P4 H' N - |2 (GPIO3) (GPIO0)15|
" J& L) F! J5 Z3 G, y. P7 F - | |0 ^/ o- D+ L" d8 Z4 Z _. f
- |3 (GPIO4) 14|
- B3 x$ Q K8 K5 \ z0 j- o& j - | |- V! W4 b" H( g# {- R
- |4 (GPIO5) (DATA)13|) |" e4 @; Y; A& \
- | STC15W204S |0 [* {0 @6 Z9 z8 v1 s
- |5 (GPIO6) (LATCH)12|6 m9 n/ h- j7 k- _3 ?
- | |( A4 u( |) A V9 @$ @! Z5 D" @
- |6 (VCC) (CLK)11|2 I: p# e9 A1 e( b m0 ]
- | |' G Z7 ], O3 s" b; `6 G
- |7 (GPIO7) (TXD)10| r" G+ [2 m# T! W
- | |% F+ u( y1 }" d* \- l
- |8 (GND) (RXD) 9|
; L: ^" x2 k; d% G- A8 G) G1 H' } - *---------------------------*
1 t4 Q9 U1 ]% k' _7 r - Fosc = 12MHz
1 ~+ X$ ]* Z9 J+ G# V" n8 g - / i. J( l. c2 z. U& }; M( y4 Q
- P1.0 -> 上" j" T+ i* J9 j- Z b4 l- R2 c
- P1.1 -> 左+ \" y1 I% M) h/ P& Q7 `( z2 d
- P1.2 -> 下3 p) J& }/ F( `, e% q
- P1.3 -> 右 v: w5 k' ?8 ?. [; g8 h# o
- P1.4 -> SEL
" _: c4 A, R' H" P - P1.5 -> STA$ p% k; l0 K2 x5 n! y
- P5.4 -> B3 p: t8 T1 k. B
- P5.5 -> A
; D7 V, A: z: n
' F) q' e, k7 q- */
0 C4 q& ]$ G8 `' U) i) P - 6 z4 J" F5 e) @: W5 T y
( A) a) e5 e& I% V! D8 \. }* H( G/ D- sbit CLK = P3^2;+ l! @, ]' j, E: q$ [ a+ _3 _
- sbit LATCH = P3^3;/ J5 ]& B4 S# w) z% z7 O. X9 r
- sbit DATA = P3^6;
# P) S; o1 H$ B2 n n
; ?& E) m2 ^7 \- l* {- bit isReady = 0;2 c# Y O& T0 l% {9 n# f
- static unsigned char key = 0;' G b& g2 O* ^: n2 r* \
- static unsigned char buf = 0; // 双缓冲。这个缓冲区保存从P1和P5组合而来的键值
7 K0 S6 x; m6 R% `% S; R9 I - static unsigned char bufReady = 0; // 双缓冲。这个缓冲区保存上面那个缓冲区的备份
! B! ?- x6 P4 @3 ~) [ - static const unsigned char data mask[] = {0x80, 0x40, 0x10, 0x20, 0x01, 0x04, 0x02, 0x08}; // A B SELECT START UP DOWN LEFT RIGHT。为提升速度,这个表放到RAM中6 Y. G2 `% T7 k" \8 j
- static unsigned char idx = 0;4 Z/ L! H6 t. c" I9 J1 S
4 C" y5 N- O3 l- x- ( {' C! J7 H" \1 x, W. d9 F L5 Y
- static void GPIO_Init(){
5 M9 j; s0 a( c% S& O0 F& h - // P1口和P5口用于接收并行信号,全部初始化为准双向,依靠内部弱上拉输出高电平/ F# ?2 }; l: N) Y3 v' f) u
- P1M0 = 0;
+ s" B1 A# ~4 u8 l. o - P1M1 = 0;
% K/ t' X7 f5 ?$ N: u' `8 y - P1= 0xFF;
' `1 |! E' O5 I4 f - P5M0 = 0;" G9 R) B5 t; }, ^1 C5 x0 D
- P5M1 = 0;# a) }* G) Q) ?, \' @. {8 w9 }7 H
- P5= 0xFF;( [2 r* |' }" a# r7 O( F m1 d8 b
- // P3口初始化为准双向- J9 \( [4 E) @6 w9 `
- P3M0 = 0;
- t$ V$ B X! I - P3M1 = 0;
6 y, a4 e1 p- n- o( k# Y - P3 = 0xFF;
2 U+ F3 j: r) ` f$ l e* P2 c - }
* S; o0 A( T7 p; h, R) t \ - 4 p9 Y+ Y1 g. @3 }$ c2 ~6 C8 r
- static void Interrupt_Init(){% L$ e: f D6 Y+ {& D
- // INT0中断(CLK)触发类型为上升沿+下降沿
% |4 ?' g% e, O3 J5 m. K' S - IT0 = 0;
% P& ^7 l: L9 @2 |; _5 z5 w. b0 f - // 允许INT0中断7 t3 G1 I, j. Y- c" V+ k8 W9 l* T
- EX0 = 1;
* O0 j e* c: N6 Q) p( I - // INT1中断(LATCH)触发类型为上升沿+下降沿" v% L; G/ ^( U' t0 ?
- IT1 = 0;" d* M3 G6 T, Y+ c8 { j2 k N& \
- // 允许INT1中断
& l T" H- D+ w; ^) Q6 v x - EX1 = 1;
2 i' H3 p% w3 A7 ~, D1 c - // 开启全局中断! j" M2 V0 K, U6 u6 D+ z
- EA = 1;
, `% b) a3 U5 T* Q9 C# S - }+ \3 |. ?) ]; [
- 8 I- V. k* Q# j; m( P. T! ~# t" c
- static void INT0_Isr() interrupt 0{
& z0 y; B$ O% N8 Y% H2 k9 { - // 只有已经成功锁存才允许CLK移位
- z( X4 ^/ o$ @1 _& l" W* K. j - if(isReady == 0)
! t3 ?7 ^! s2 g; z) c8 d/ ^# Z - return;1 ^$ Z8 D4 D/ K+ J: t$ L
- // 读当前引脚电平,如果是低电平则说明是下降沿,此时直接忽略该中断
8 b- _1 V1 [3 B" f: y8 t' D - if(CLK == 0)0 ~" a( C& m$ C T0 U! h+ } g2 Z
- return;. t( D0 T9 Z9 w4 D1 p8 e4 O
- // CLK上升沿到来的时候,取锁存值的下一位输出
+ o9 j! H" J+ g7 v - idx++;8 c, _. z/ [+ d# _- ^4 e* z& S
- DATA = key & mask[idx];" v# ?* Y3 e* G" ?* T7 M
- // 如果已经完成7次移位,则一轮读取完成
/ b1 _/ K8 }- Z - if(idx >= 7)
) ^1 e- D2 }% [6 O- v3 E - isReady = 0;
, q0 E1 h7 v$ ?- N# c7 B - }
. E, ~% X C7 N& N" r+ r" | - / x8 r, L$ H$ N: f% F4 y1 u
- static void INT1_Isr() interrupt 2{" Y# [' c' z2 _1 A6 Q
- // 读当前引脚电平,如果是低电平则说明是下降沿,此时直接忽略该中断
& A: X0 h7 C. o' R; M* x/ p, e' j8 m - if(LATCH == 0)
5 P0 {5 b3 `9 h8 Z+ X - return;
4 q8 f b( e) \* L; j4 ? m - // 当LATCH上升沿到来的时候,锁存所有按键状态,同时把键值A输出到DATA2 _6 @$ k3 j* B @+ t7 u; D
- key = bufReady;& _, Y/ w& t" u' S$ y1 X
- DATA = key & 0x80;//mask[0]; // 为了加速运算,直接取表中的值而不是读表
3 r/ {# x2 u P) M5 H - idx = 0;
) E, w# g2 N2 z: j - // 允许CLK进行移位
% a( a% {- ]" b7 b$ r5 ?; g) W( v - isReady = 1;# x* i- `- I- t+ ?& m. t
- }# Z# `# H( W1 B- }5 p& j
! h( ?2 U, o# H- x* S- void main(){+ S, }2 C& X/ ]+ N) R9 F, K! r1 G+ Y
- GPIO_Init();5 v2 W' M) N* Y4 C
- Interrupt_Init();- `: N: _) ]2 O6 l
- . @" w( X1 R* W% ]" N3 Z/ X
- while(1){
& z- T, O+ H% n% P( M9 x - //PCON |= 0x01; // 进入省电模式. X0 \0 s" h$ N4 G
- //_nop_();
: P) o) }. a k* P% s - //_nop_();
* i7 R1 P4 H6 c, f' G4 { - //_nop_();+ D# @+ L7 v/ i9 w: Q, y: f
- //_nop_();
% `- ^& P: z5 W. X - buf = ~((P1 & 0x3F) | ((P5 << 2) & 0xC0));
; Q+ n6 N; n7 ^# q2 z, o9 N - bufReady = buf;
7 V1 T. Y6 F% C" N8 b - }
8 t9 N/ H/ t; @+ O" q3 | - }
5 b5 G8 Q. {' Q. Z- N3 L( Y. B# r
复制代码
`! y+ l( R1 N. r: h硬件参数配置无特殊要求,晶振频率选择12M即可。
; {$ X: }1 C. b! m& C- _. u g0 [1 m! e; _$ g0 d1 A7 p
这是编译好的固件。 |