line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
# You may distribute under the terms of either the GNU General Public License |
2
|
|
|
|
|
|
|
# or the Artistic License (the same terms as Perl itself) |
3
|
|
|
|
|
|
|
# |
4
|
|
|
|
|
|
|
# (C) Paul Evans, 2019-2020 -- leonerd@leonerd.org.uk |
5
|
|
|
|
|
|
|
|
6
|
6
|
|
|
6
|
|
499215
|
use v5.26; # postfix-deref, signatures |
|
6
|
|
|
|
|
76
|
|
7
|
6
|
|
|
6
|
|
429
|
use Object::Pad 0.57; |
|
6
|
|
|
|
|
8258
|
|
|
6
|
|
|
|
|
27
|
|
8
|
|
|
|
|
|
|
|
9
|
|
|
|
|
|
|
package Device::Chip::CC1101 0.07; |
10
|
|
|
|
|
|
|
class Device::Chip::CC1101 |
11
|
1
|
|
|
1
|
|
457
|
:isa(Device::Chip); |
|
1
|
|
|
|
|
12025
|
|
|
1
|
|
|
|
|
30
|
|
12
|
|
|
|
|
|
|
|
13
|
6
|
|
|
6
|
|
1635
|
use Carp; |
|
6
|
|
|
|
|
8
|
|
|
6
|
|
|
|
|
446
|
|
14
|
6
|
|
|
6
|
|
2358
|
use Data::Bitfield 0.04 qw( bitfield boolfield enumfield intfield signed_intfield ); |
|
6
|
|
|
|
|
10044
|
|
|
6
|
|
|
|
|
439
|
|
15
|
6
|
|
|
6
|
|
45
|
use Future::AsyncAwait; |
|
6
|
|
|
|
|
14
|
|
|
6
|
|
|
|
|
42
|
|
16
|
6
|
|
|
6
|
|
2727
|
use Future::IO; |
|
6
|
|
|
|
|
55762
|
|
|
6
|
|
|
|
|
373
|
|
17
|
|
|
|
|
|
|
|
18
|
6
|
|
|
6
|
|
49
|
use constant PROTOCOL => "SPI"; |
|
6
|
|
|
|
|
12
|
|
|
6
|
|
|
|
|
2174
|
|
19
|
|
|
|
|
|
|
|
20
|
|
|
|
|
|
|
my %BANDS; |
21
|
|
|
|
|
|
|
my %PRESET_MODES; |
22
|
|
|
|
|
|
|
|
23
|
|
|
|
|
|
|
my @CACHED_CONFIG = qw( APPEND_STATUS PACKET_LENGTH LENGTH_CONFIG ); |
24
|
|
|
|
|
|
|
|
25
|
|
|
|
|
|
|
=head1 NAME |
26
|
|
|
|
|
|
|
|
27
|
|
|
|
|
|
|
C - chip driver for a F |
28
|
|
|
|
|
|
|
|
29
|
|
|
|
|
|
|
=head1 DESCRIPTION |
30
|
|
|
|
|
|
|
|
31
|
|
|
|
|
|
|
This L subclass provides specific communication to a |
32
|
|
|
|
|
|
|
F F radio transceiver chip attached to a computer |
33
|
|
|
|
|
|
|
via an SPI adapter. |
34
|
|
|
|
|
|
|
|
35
|
|
|
|
|
|
|
The reader is presumed to be familiar with the general operation of this chip; |
36
|
|
|
|
|
|
|
the documentation here will not attempt to explain or define chip-specific |
37
|
|
|
|
|
|
|
concepts or features, only the use of this module to access them. |
38
|
|
|
|
|
|
|
|
39
|
|
|
|
|
|
|
=cut |
40
|
|
|
|
|
|
|
|
41
|
|
|
|
|
|
|
=head1 CONSTRUCTOR |
42
|
|
|
|
|
|
|
|
43
|
|
|
|
|
|
|
=head2 new |
44
|
|
|
|
|
|
|
|
45
|
|
|
|
|
|
|
$chip = Device::Chip::CC1101->new( %ops ) |
46
|
|
|
|
|
|
|
|
47
|
|
|
|
|
|
|
Constructs a new C instance. Takes the following |
48
|
|
|
|
|
|
|
optional named arguments: |
49
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
=over 4 |
51
|
|
|
|
|
|
|
|
52
|
|
|
|
|
|
|
=item * fosc |
53
|
|
|
|
|
|
|
|
54
|
|
|
|
|
|
|
Gives the XTAL oscillator frequency in Hz. This is used by the |
55
|
|
|
|
|
|
|
L to calculate the actual frequency from the chip config. |
56
|
|
|
|
|
|
|
A default of 26MHz applies if not supplied. |
57
|
|
|
|
|
|
|
|
58
|
|
|
|
|
|
|
=item * poll_interval |
59
|
|
|
|
|
|
|
|
60
|
|
|
|
|
|
|
Interval in seconds to poll the chip status after transmitting. A default of |
61
|
|
|
|
|
|
|
20msec applies if not supplied. |
62
|
|
|
|
|
|
|
|
63
|
|
|
|
|
|
|
=back |
64
|
|
|
|
|
|
|
|
65
|
|
|
|
|
|
|
=cut |
66
|
|
|
|
|
|
|
|
67
|
|
|
|
|
|
|
has $_fosc :param = 26E6; # presets presume 26MHz XTAL |
68
|
|
|
|
|
|
|
has $_poll_interval :param = 0.05; |
69
|
|
|
|
|
|
|
|
70
|
|
|
|
|
|
|
method SPI_options |
71
|
5
|
|
|
5
|
0
|
1565
|
{ |
72
|
|
|
|
|
|
|
return ( |
73
|
5
|
|
|
|
|
31
|
mode => 0, |
74
|
|
|
|
|
|
|
max_bitrate => 1E6, |
75
|
|
|
|
|
|
|
); |
76
|
|
|
|
|
|
|
} |
77
|
|
|
|
|
|
|
|
78
|
0
|
|
|
0
|
0
|
0
|
method power ( $on ) { $self->protocol->power( $on ) } |
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
79
|
|
|
|
|
|
|
|
80
|
|
|
|
|
|
|
=head1 METHODS |
81
|
|
|
|
|
|
|
|
82
|
|
|
|
|
|
|
The following methods documented in an C expression return L |
83
|
|
|
|
|
|
|
instances. |
84
|
|
|
|
|
|
|
|
85
|
|
|
|
|
|
|
=cut |
86
|
|
|
|
|
|
|
|
87
|
|
|
|
|
|
|
use constant { |
88
|
6
|
|
|
|
|
9124
|
REG_WRITE => 0x00, |
89
|
|
|
|
|
|
|
REG_BURST => 0x40, |
90
|
|
|
|
|
|
|
REG_READ => 0x80, |
91
|
|
|
|
|
|
|
|
92
|
|
|
|
|
|
|
REG_PKTSTATUS => 0x38, |
93
|
|
|
|
|
|
|
REG_MARCSTATE => 0x35, |
94
|
|
|
|
|
|
|
REG_RXBYTES => 0x3B, |
95
|
|
|
|
|
|
|
REG_PATABLE => 0x3E, |
96
|
|
|
|
|
|
|
REG_TXFIFO => 0x3F, # write-only |
97
|
|
|
|
|
|
|
REG_RXFIFO => 0x3F, # read-only |
98
|
|
|
|
|
|
|
|
99
|
|
|
|
|
|
|
CMD_SRES => 0x30, |
100
|
|
|
|
|
|
|
CMD_SFSTXON => 0x31, |
101
|
|
|
|
|
|
|
CMD_SXOFF => 0x32, |
102
|
|
|
|
|
|
|
CMD_SCAL => 0x33, |
103
|
|
|
|
|
|
|
CMD_SRX => 0x34, |
104
|
|
|
|
|
|
|
CMD_STX => 0x35, |
105
|
|
|
|
|
|
|
CMD_SIDLE => 0x36, |
106
|
|
|
|
|
|
|
CMD_SWOR => 0x38, |
107
|
|
|
|
|
|
|
CMD_SPWD => 0x39, |
108
|
|
|
|
|
|
|
CMD_SFRX => 0x3A, |
109
|
|
|
|
|
|
|
CMD_SFTX => 0x3B, |
110
|
|
|
|
|
|
|
CMD_SWORRST => 0x3C, |
111
|
|
|
|
|
|
|
CMD_SNOP => 0x3D, |
112
|
6
|
|
|
6
|
|
38
|
}; |
|
6
|
|
|
|
|
11
|
|
113
|
|
|
|
|
|
|
|
114
|
|
|
|
|
|
|
=head2 read_register |
115
|
|
|
|
|
|
|
|
116
|
|
|
|
|
|
|
$value = await $chip->read_register( $addr ); |
117
|
|
|
|
|
|
|
|
118
|
|
|
|
|
|
|
Reads a single byte register and returns its numerical value. |
119
|
|
|
|
|
|
|
|
120
|
|
|
|
|
|
|
C<$addr> should be between 0 and 0x3D, giving the register address. |
121
|
|
|
|
|
|
|
|
122
|
|
|
|
|
|
|
=cut |
123
|
|
|
|
|
|
|
|
124
|
17
|
|
|
|
|
24
|
async method read_register ( $addr ) |
|
17
|
|
|
|
|
25
|
|
|
17
|
|
|
|
|
24
|
|
125
|
17
|
|
|
|
|
32
|
{ |
126
|
17
|
50
|
33
|
|
|
105
|
$addr >= 0 and $addr <= 0x3D or |
127
|
|
|
|
|
|
|
croak "Invalid register address"; |
128
|
17
|
100
|
|
|
|
46
|
$addr |= REG_BURST if $addr >= 0x30; |
129
|
|
|
|
|
|
|
|
130
|
17
|
|
|
|
|
68
|
return unpack "C", await $self->protocol->write_then_read( |
131
|
|
|
|
|
|
|
pack( "C", REG_READ | $addr ), 1 |
132
|
|
|
|
|
|
|
); |
133
|
17
|
|
|
17
|
1
|
9615
|
} |
134
|
|
|
|
|
|
|
|
135
|
|
|
|
|
|
|
my @GDO_CFGs = qw( |
136
|
|
|
|
|
|
|
rx-fifo-full rx-fifo-or-eop tx-fifo-above-threshold tx-fifo-full |
137
|
|
|
|
|
|
|
rx-fifo-overflow tx-fifo-underflow packet-in-flight packet-received |
138
|
|
|
|
|
|
|
pqi-reached cca pll-lock sync-sck |
139
|
|
|
|
|
|
|
sync-sdo async-sdo carrier-sense CRC_OK |
140
|
|
|
|
|
|
|
. . . . |
141
|
|
|
|
|
|
|
. . RX_HARD_DATA[1] RX_HARD_DATA[0] |
142
|
|
|
|
|
|
|
. . . PA_PD |
143
|
|
|
|
|
|
|
LNA_PD RX_SYMBOL_TICK . . |
144
|
|
|
|
|
|
|
. . . . |
145
|
|
|
|
|
|
|
WOR_EVNT0 WOR_EVNT1 CLK_256 CLK_32k |
146
|
|
|
|
|
|
|
. CHIP_RDYn . XOSC_STABLE |
147
|
|
|
|
|
|
|
. . hiZ low |
148
|
|
|
|
|
|
|
CLK_XOSC/1 CLK_XOSC/1.5 CLK_XOSC/2 CLK_XOSC/3 |
149
|
|
|
|
|
|
|
CLK_XOSC/4 CLK_XOSC/6 CLK_XOSC/8 CLK_XOSC/12 |
150
|
|
|
|
|
|
|
CLK_XOSC/16 CLK_XOSC/24 CLK_XOSC/32 CLK_XOSC/48 |
151
|
|
|
|
|
|
|
CLK_XOSC/64 CLK_XOSC/96 CLK_XOSC/128 CLK_XOSC/196 |
152
|
|
|
|
|
|
|
); |
153
|
|
|
|
|
|
|
|
154
|
|
|
|
|
|
|
bitfield { format => "bytes-BE" }, CONFIG => |
155
|
|
|
|
|
|
|
# IOCFG2 |
156
|
|
|
|
|
|
|
GDO2_INV => boolfield( 6), |
157
|
|
|
|
|
|
|
GDO2_CFG => enumfield( 0, @GDO_CFGs), |
158
|
|
|
|
|
|
|
# IOCFG1 |
159
|
|
|
|
|
|
|
GDO_DS => enumfield( 1*8+7, qw( low high )), |
160
|
|
|
|
|
|
|
GDO1_INV => boolfield( 1*8+6), |
161
|
|
|
|
|
|
|
GDO1_CFG => enumfield( 1*8+0, @GDO_CFGs), |
162
|
|
|
|
|
|
|
# IOCFG0 |
163
|
|
|
|
|
|
|
TEMP_SENSOR_ENABLE => boolfield( 2*8+7), |
164
|
|
|
|
|
|
|
GDO0_INV => boolfield( 2*8+6), |
165
|
|
|
|
|
|
|
GDO0_CFG => enumfield( 2*8+0, @GDO_CFGs), |
166
|
|
|
|
|
|
|
# FIFOTHR |
167
|
|
|
|
|
|
|
ADC_RETENTION => boolfield( 3*8+6), |
168
|
|
|
|
|
|
|
CLOSE_IN_RX => enumfield( 3*8+4, qw( 0dB 6dB 12dB 18dB )), |
169
|
|
|
|
|
|
|
FIFO_THR => intfield ( 3*8+0, 4), # TODO enum |
170
|
|
|
|
|
|
|
# SYNC0..1 |
171
|
|
|
|
|
|
|
SYNC => intfield ( 4*8+0, 16), |
172
|
|
|
|
|
|
|
# PKTLEN |
173
|
|
|
|
|
|
|
PACKET_LENGTH => intfield ( 6*8+0, 8), |
174
|
|
|
|
|
|
|
# PKTCTRL1 |
175
|
|
|
|
|
|
|
PQT => intfield ( 7*8+5, 3), |
176
|
|
|
|
|
|
|
CRC_AUTOFLUSH => boolfield( 7*8+3), |
177
|
|
|
|
|
|
|
APPEND_STATUS => boolfield( 7*8+2), |
178
|
|
|
|
|
|
|
ADR_CHK => enumfield( 7*8+0, qw( none addr addr+bc addr+2bc )), |
179
|
|
|
|
|
|
|
# PKTCTRL0 |
180
|
|
|
|
|
|
|
WHITE_DATA => boolfield( 8*8+6), |
181
|
|
|
|
|
|
|
PKT_FORMAT => enumfield( 8*8+4, qw( fifo sync random async )), |
182
|
|
|
|
|
|
|
CRC_EN => boolfield( 8*8+2), |
183
|
|
|
|
|
|
|
LENGTH_CONFIG => enumfield( 8*8+0, qw( fixed variable infinite . )), |
184
|
|
|
|
|
|
|
# ADDR |
185
|
|
|
|
|
|
|
DEVICE_ADDR => intfield ( 9*8+0, 8), |
186
|
|
|
|
|
|
|
# CHANNR |
187
|
|
|
|
|
|
|
CHAN => intfield (10*8+0, 8), |
188
|
|
|
|
|
|
|
# FSCTRL1 |
189
|
|
|
|
|
|
|
FREQ_IF => intfield (11*8+0, 5), |
190
|
|
|
|
|
|
|
# FSCTRL0 |
191
|
|
|
|
|
|
|
FREQOFF => signed_intfield(12*8+0, 8), |
192
|
|
|
|
|
|
|
# FREQ0..2 |
193
|
|
|
|
|
|
|
FREQ => intfield (13*8+0, 24), |
194
|
|
|
|
|
|
|
# MDMCFG4 |
195
|
|
|
|
|
|
|
CHANBW_E => intfield (16*8+6, 2), |
196
|
|
|
|
|
|
|
CHANBW_M => intfield (16*8+4, 2), |
197
|
|
|
|
|
|
|
DRATE_E => intfield (16*8+0, 4), |
198
|
|
|
|
|
|
|
# MDMCFG3 |
199
|
|
|
|
|
|
|
DRATE_M => intfield (17*8+0, 8), |
200
|
|
|
|
|
|
|
# MDMCFG2 |
201
|
|
|
|
|
|
|
DEM_DCFILT_OFF => boolfield(18*8+7), |
202
|
|
|
|
|
|
|
MOD_FORMAT => enumfield(18*8+4, qw( 2-FSK GFSK . ASK 4-FSK . . MSK )), |
203
|
|
|
|
|
|
|
MANCHESTER_EN => boolfield(18*8+3), |
204
|
|
|
|
|
|
|
SYNC_MODE => enumfield(18*8+0, qw( none 15/16 16/16 30/32 cs 15/16+cs 16/16+cs 30/32+cs )), |
205
|
|
|
|
|
|
|
# MDMCFG1 |
206
|
|
|
|
|
|
|
FEC_EN => boolfield(19*8+7), |
207
|
|
|
|
|
|
|
NUM_PREAMBLE => enumfield(19*8+4, qw( 2B 3B 4B 6B 8B 12B 16B 24B )), |
208
|
|
|
|
|
|
|
CHANSPC_E => intfield (19*8+0, 2), |
209
|
|
|
|
|
|
|
# MDMCFG0 |
210
|
|
|
|
|
|
|
CHANSPC_M => intfield (20*8+0, 8), |
211
|
|
|
|
|
|
|
# DEVIATN |
212
|
|
|
|
|
|
|
DEVIATION_E => intfield (21*8+4, 3), |
213
|
|
|
|
|
|
|
DEVIATION_M => intfield (21*8+0, 3), |
214
|
|
|
|
|
|
|
# MSCM2 |
215
|
|
|
|
|
|
|
RX_TIME_RSSI => boolfield(22*8+4), |
216
|
|
|
|
|
|
|
RX_TIME_QUAL => boolfield(22*8+3), |
217
|
|
|
|
|
|
|
RX_TIME => intfield (22*8+0, 3), |
218
|
|
|
|
|
|
|
# MSCM1 |
219
|
|
|
|
|
|
|
CCA_MODE => enumfield(23*8+4, qw( always rssi unless-rx rssi-unless-rx )), |
220
|
|
|
|
|
|
|
RXOFF_MODE => enumfield(23*8+2, qw( IDLE FSTXON TX RX )), |
221
|
|
|
|
|
|
|
TXOFF_MODE => enumfield(23*8+0, qw( IDLE FSTXON TX RX )), |
222
|
|
|
|
|
|
|
# MSCM0 |
223
|
|
|
|
|
|
|
FS_AUTOCAL => enumfield(24*8+4, qw( never on-unidle on-idle on-idle/4 )), |
224
|
|
|
|
|
|
|
PO_TIMEOUT => enumfield(24*8+2, qw( x1 x16 x64 x256 )), |
225
|
|
|
|
|
|
|
PIN_CTRL_EN => boolfield(24*8+1), |
226
|
|
|
|
|
|
|
XOSC_FORCE_ON => boolfield(24*8+0), |
227
|
|
|
|
|
|
|
# FOCCFG |
228
|
|
|
|
|
|
|
FOC_BS_CS_GATE => boolfield(25*8+5), |
229
|
|
|
|
|
|
|
FOC_PRE_K => enumfield(25*8+3, qw( K 2K 3K 4K )), |
230
|
|
|
|
|
|
|
FOC_POST_K => enumfield(25*8+2, qw( PRE K/2 )), |
231
|
|
|
|
|
|
|
FOC_LIMIT => enumfield(25*8+0, qw( 0 BW/8 BW/4 BW/2 )), |
232
|
|
|
|
|
|
|
# BSCFG |
233
|
|
|
|
|
|
|
BS_PRE_KI => enumfield(26*8+6, qw( KI 2KI 3KI 4KI )), |
234
|
|
|
|
|
|
|
BS_PRE_KP => enumfield(26*8+4, qw( KP 2KP 3KP 4KP )), |
235
|
|
|
|
|
|
|
BS_POST_KI => enumfield(26*8+3, qw( PRE KI/2 )), |
236
|
|
|
|
|
|
|
BS_POST_KP => enumfield(26*8+2, qw( PRE KP )), |
237
|
|
|
|
|
|
|
BS_LIMIT => enumfield(26*8+0, qw( 0 3.125% 6.25% 12.5% )), |
238
|
|
|
|
|
|
|
# AGCCTRL2 |
239
|
|
|
|
|
|
|
MAX_DVGA_GAIN => enumfield(27*8+6, qw( max not-top not-top-2 not-top-3 )), |
240
|
|
|
|
|
|
|
MAX_LNA_GAIN => enumfield(27*8+3, "max", map { "max-${_}dB"} qw( 2.6 6.1 7.4 9.2 11.5 14.6 17.1 )), |
241
|
|
|
|
|
|
|
MAGN_TARGET => enumfield(27*8+0, qw( 24dB 27dB 30dB 33dB 36dB 38dB 40dB 42dB )), |
242
|
|
|
|
|
|
|
# AGCCTRL1 |
243
|
|
|
|
|
|
|
AGC_LNA_PRIORITY => enumfield(28*8+6, qw( lna-first lna2-first )), |
244
|
|
|
|
|
|
|
CARRIER_SENSE_REL_THR => enumfield(28*8+4, qw( disabled 5dB 10dB 14dB )), |
245
|
|
|
|
|
|
|
CARRIER_SENSE_ABS_THR => enumfield(28*8+0, |
246
|
|
|
|
|
|
|
"at-magn-target", ( map { "${_}dB-above" } 1 .. 7 ), |
247
|
|
|
|
|
|
|
"disabled", ( map { "${_}dB-below" } -7 .. -1 ) ), |
248
|
|
|
|
|
|
|
# AGCCTRL0 |
249
|
|
|
|
|
|
|
HYST_LEVEL => enumfield(29*8+6, qw( no low medium high )), |
250
|
|
|
|
|
|
|
WAIT_TIME => enumfield(29*8+4, qw( 8sa 16sa 24sa 32sa )), |
251
|
|
|
|
|
|
|
AGC_FREEZE => enumfield(29*8+2, qw( never after-sync freeze-analog freeze-all )), |
252
|
|
|
|
|
|
|
FILTER_LENGTH => enumfield(29*8+0, qw( 8sa 16sa 32sa 64sa )), # TODO: in OOK/ASK modes this is different |
253
|
|
|
|
|
|
|
# WOREVT0..1 |
254
|
|
|
|
|
|
|
EVENT0 => intfield (30*8+0, 16), |
255
|
|
|
|
|
|
|
# WORCTRL |
256
|
|
|
|
|
|
|
RC_PD => boolfield(32*8+7), |
257
|
|
|
|
|
|
|
EVENT1 => enumfield(32*8+4, qw( 4clk 6clk 8clk 12clk 16clk 24clk 32clk 48clk )), |
258
|
|
|
|
|
|
|
RC_CAL => boolfield(32*8+3), |
259
|
|
|
|
|
|
|
WOR_RES => enumfield(32*8+0, qw( 1P 2^5P 2^10P 2^15P )), |
260
|
|
|
|
|
|
|
# FREND1 |
261
|
|
|
|
|
|
|
LNA_CURRENT => intfield (33*8+6, 2), |
262
|
|
|
|
|
|
|
LNA2MIX_CURRENT => intfield (33*8+4, 2), |
263
|
|
|
|
|
|
|
LODIV_BUF_CURRENT_RX => intfield (33*8+2, 2), |
264
|
|
|
|
|
|
|
MIX_CURRENT => intfield (33*8+0, 2), |
265
|
|
|
|
|
|
|
# FREND0 |
266
|
|
|
|
|
|
|
LODIV_BUF_CURRENT_TX => intfield (34*8+4, 2), |
267
|
|
|
|
|
|
|
PA_POWER => intfield (34*8+0, 3), |
268
|
|
|
|
|
|
|
# The FSCAL registers are basically opaque |
269
|
|
|
|
|
|
|
FSCAL => intfield (35*8+0, 32), |
270
|
|
|
|
|
|
|
# RCCTRL too |
271
|
|
|
|
|
|
|
RCCTRL => intfield (39*8+0, 16), |
272
|
|
|
|
|
|
|
# The remaining registers are test registers not for user use |
273
|
|
|
|
|
|
|
; |
274
|
|
|
|
|
|
|
|
275
|
|
|
|
|
|
|
# Not used directly by this code, but helpful for unit tests, etc.. |
276
|
6
|
|
|
|
|
25234
|
use constant CONFIG_DEFAULT => |
277
|
|
|
|
|
|
|
"\x29\x2E\x3F\x07\xD3\x91\xFF\x04\x45\x00\x00\x0F\x00\x1E\xC4\xEC" . |
278
|
|
|
|
|
|
|
"\x8C\x22\x02\x22\xF8\x47\x07\x30\x04\x36\x6C\x03\x40\x91\x87\x6B" . |
279
|
6
|
|
|
6
|
|
52
|
"\xF8\x56\x10\xA9\x0A\x20\x0D\x41\x00"; |
|
6
|
|
|
|
|
11
|
|
280
|
|
|
|
|
|
|
|
281
|
|
|
|
|
|
|
=head2 read_config |
282
|
|
|
|
|
|
|
|
283
|
|
|
|
|
|
|
$config = await $chip->read_config; |
284
|
|
|
|
|
|
|
|
285
|
|
|
|
|
|
|
Reads and returns the current chip configuration as a C reference. |
286
|
|
|
|
|
|
|
|
287
|
|
|
|
|
|
|
The returned hash will contain keys with capitalized names representing all of |
288
|
|
|
|
|
|
|
the config register fields in the datasheet, from registers C to |
289
|
|
|
|
|
|
|
C. Values are returned either as integers, or converted enumeration |
290
|
|
|
|
|
|
|
names. Where documented by the datasheet, the enumeration values are |
291
|
|
|
|
|
|
|
capitalised. Where invented by this module from the description they are given |
292
|
|
|
|
|
|
|
in lowercase. |
293
|
|
|
|
|
|
|
|
294
|
|
|
|
|
|
|
The value of C is also returned, rendered as a human-readable hex |
295
|
|
|
|
|
|
|
string in the form |
296
|
|
|
|
|
|
|
|
297
|
|
|
|
|
|
|
PATABLE => "01.23.45.67.89.AB.CD.EF", |
298
|
|
|
|
|
|
|
|
299
|
|
|
|
|
|
|
The following values are also returned, derived from the actual register |
300
|
|
|
|
|
|
|
values as a convenience. |
301
|
|
|
|
|
|
|
|
302
|
|
|
|
|
|
|
carrier_frequency => "800.000MHz", |
303
|
|
|
|
|
|
|
channel_spacing => "191.951kHz", |
304
|
|
|
|
|
|
|
deviation => "47.607kHz", |
305
|
|
|
|
|
|
|
|
306
|
|
|
|
|
|
|
data_rate => "115.1kbps", |
307
|
|
|
|
|
|
|
|
308
|
|
|
|
|
|
|
=cut |
309
|
|
|
|
|
|
|
|
310
|
3
|
|
|
3
|
|
3437
|
sub _tohex ( $v ) { return sprintf "%v02X", $v } |
|
3
|
|
|
|
|
7
|
|
|
3
|
|
|
|
|
3
|
|
|
3
|
|
|
|
|
83
|
|
311
|
2
|
|
|
2
|
|
4
|
sub _fromhex ( $v ) { return pack "H*", $v =~ s/\.//gr } |
|
2
|
|
|
|
|
4
|
|
|
2
|
|
|
|
|
3
|
|
|
2
|
|
|
|
|
20
|
|
312
|
|
|
|
|
|
|
|
313
|
3
|
|
|
|
|
6
|
async method _read_CONFIG () |
|
3
|
|
|
|
|
5
|
|
314
|
3
|
|
|
|
|
10
|
{ |
315
|
3
|
|
|
|
|
11
|
return await $self->protocol->write_then_read( |
316
|
|
|
|
|
|
|
pack( "C", REG_READ | REG_BURST | 0 ), 41 |
317
|
|
|
|
|
|
|
); |
318
|
3
|
|
|
3
|
|
56
|
} |
319
|
|
|
|
|
|
|
|
320
|
3
|
|
|
|
|
6
|
async method _read_PATABLE () |
|
3
|
|
|
|
|
6
|
|
321
|
3
|
|
|
|
|
19
|
{ |
322
|
3
|
|
|
|
|
18
|
return await $self->protocol->write_then_read( |
323
|
|
|
|
|
|
|
pack( "C", REG_READ | REG_BURST | REG_PATABLE ), 8 |
324
|
|
|
|
|
|
|
); |
325
|
3
|
|
|
3
|
|
23888
|
} |
326
|
|
|
|
|
|
|
|
327
|
|
|
|
|
|
|
has $_config; |
328
|
|
|
|
|
|
|
has $_patable; |
329
|
|
|
|
|
|
|
has %_cached_config; |
330
|
|
|
|
|
|
|
|
331
|
3
|
|
|
|
|
6
|
async method read_config () |
|
3
|
|
|
|
|
6
|
|
332
|
3
|
|
|
|
|
10
|
{ |
333
|
3
|
|
33
|
|
|
21
|
my %config = ( |
|
|
|
33
|
|
|
|
|
334
|
|
|
|
|
|
|
unpack_CONFIG( $_config //= await $self->_read_CONFIG ), |
335
|
|
|
|
|
|
|
PATABLE => _tohex $_patable //= await $self->_read_PATABLE, |
336
|
|
|
|
|
|
|
); |
337
|
|
|
|
|
|
|
|
338
|
|
|
|
|
|
|
# Post-convert some derived fields just for user convenience |
339
|
3
|
|
|
|
|
14
|
my $fosc = $_fosc; |
340
|
|
|
|
|
|
|
|
341
|
3
|
|
|
|
|
20
|
my $channel_spacing = $fosc * ( 256 + $config{CHANSPC_M} ) * 2 ** $config{CHANSPC_E} / 2**18; |
342
|
|
|
|
|
|
|
|
343
|
3
|
|
|
|
|
43
|
$config{channel_spacing} = sprintf "%.3fkHz", $channel_spacing / 1E3; |
344
|
|
|
|
|
|
|
|
345
|
|
|
|
|
|
|
$config{carrier_frequency} = sprintf "%.3fMHz", |
346
|
3
|
|
|
|
|
21
|
( $fosc * $config{FREQ} / 2**16 + $config{CHAN} * $channel_spacing ) / 1E6; |
347
|
|
|
|
|
|
|
|
348
|
3
|
|
|
|
|
8
|
my $mod_format = $config{MOD_FORMAT}; |
349
|
3
|
50
|
|
|
|
18
|
if( $mod_format =~ m/-FSK$|^GFSK$/ ) { |
350
|
|
|
|
|
|
|
$config{deviation} = sprintf "%.3fkHz", |
351
|
3
|
|
|
|
|
24
|
$fosc * ( 8 + $config{DEVIATION_M} ) * 2 ** $config{DEVIATION_E} / 2**17 / 1E3; |
352
|
|
|
|
|
|
|
} |
353
|
|
|
|
|
|
|
|
354
|
|
|
|
|
|
|
$config{data_rate} = sprintf "%.1fkbps", |
355
|
3
|
|
|
|
|
21
|
$fosc * ( 256 + $config{DRATE_M} ) * 2 ** $config{DRATE_E} / 2**28 / 1E3; |
356
|
|
|
|
|
|
|
|
357
|
3
|
|
|
|
|
20
|
$_cached_config{$_} = $config{$_} for @CACHED_CONFIG; |
358
|
3
|
|
|
|
|
104
|
return %config; |
359
|
3
|
|
|
3
|
1
|
829
|
} |
360
|
|
|
|
|
|
|
|
361
|
|
|
|
|
|
|
=head2 change_config |
362
|
|
|
|
|
|
|
|
363
|
|
|
|
|
|
|
await $chip->change_config( %changes ); |
364
|
|
|
|
|
|
|
|
365
|
|
|
|
|
|
|
Writes the configuration registers to apply the given changes. Any fields not |
366
|
|
|
|
|
|
|
specified will retain their current values. The value of C can also |
367
|
|
|
|
|
|
|
be set here. Values should be given using the same converted forms as the |
368
|
|
|
|
|
|
|
C returns. |
369
|
|
|
|
|
|
|
|
370
|
|
|
|
|
|
|
The following additional lowercase-named keys are also provided as shortcuts. |
371
|
|
|
|
|
|
|
|
372
|
|
|
|
|
|
|
=over 4 |
373
|
|
|
|
|
|
|
|
374
|
|
|
|
|
|
|
=item * band => STRING |
375
|
|
|
|
|
|
|
|
376
|
|
|
|
|
|
|
A convenient shortcut to setting the C and C configuration to |
377
|
|
|
|
|
|
|
one of the standard ISM bands. The names of these bands are |
378
|
|
|
|
|
|
|
|
379
|
|
|
|
|
|
|
433MHz |
380
|
|
|
|
|
|
|
868MHz |
381
|
|
|
|
|
|
|
|
382
|
|
|
|
|
|
|
=item * mode => STRING |
383
|
|
|
|
|
|
|
|
384
|
|
|
|
|
|
|
A convenient shortcut to setting the configuration state to one of the presets |
385
|
|
|
|
|
|
|
supplied with the module. The names of these presets are |
386
|
|
|
|
|
|
|
|
387
|
|
|
|
|
|
|
GFSK-1.2kb |
388
|
|
|
|
|
|
|
GFSK-38.4kb |
389
|
|
|
|
|
|
|
GFSK-100kb |
390
|
|
|
|
|
|
|
MSK-250kb |
391
|
|
|
|
|
|
|
MSK-500kb |
392
|
|
|
|
|
|
|
|
393
|
|
|
|
|
|
|
=back |
394
|
|
|
|
|
|
|
|
395
|
|
|
|
|
|
|
=cut |
396
|
|
|
|
|
|
|
|
397
|
7
|
|
|
|
|
13
|
async method _write_CONFIG ( $addr, $bytes ) |
|
7
|
|
|
|
|
14
|
|
|
7
|
|
|
|
|
22
|
|
|
7
|
|
|
|
|
9
|
|
398
|
7
|
|
|
|
|
39
|
{ |
399
|
7
|
|
|
|
|
170
|
await $self->protocol->write( |
400
|
|
|
|
|
|
|
pack "C a*", REG_WRITE | REG_BURST | $addr, $bytes |
401
|
|
|
|
|
|
|
); |
402
|
7
|
|
|
7
|
|
13
|
} |
403
|
|
|
|
|
|
|
|
404
|
2
|
|
|
|
|
6
|
async method _write_PATABLE ( $bytes ) |
|
2
|
|
|
|
|
5
|
|
|
2
|
|
|
|
|
4
|
|
405
|
2
|
|
|
|
|
11
|
{ |
406
|
2
|
|
|
|
|
13
|
await $self->protocol->write( |
407
|
|
|
|
|
|
|
pack "C a*", REG_WRITE | REG_BURST | REG_PATABLE, $bytes |
408
|
|
|
|
|
|
|
); |
409
|
2
|
|
|
2
|
|
6
|
} |
410
|
|
|
|
|
|
|
|
411
|
8
|
|
|
|
|
15
|
async method change_config ( %changes ) |
|
8
|
|
|
|
|
28
|
|
|
8
|
|
|
|
|
14
|
|
412
|
8
|
|
|
|
|
37
|
{ |
413
|
8
|
50
|
|
|
|
28
|
defined $_config or await $self->read_config; |
414
|
|
|
|
|
|
|
# Use unpack_CONFIG() directly to avoid the derived keys |
415
|
8
|
|
|
|
|
43
|
my %config = unpack_CONFIG( $_config ); |
416
|
|
|
|
|
|
|
|
417
|
8
|
100
|
|
|
|
5110
|
if( defined( my $mode = delete $changes{mode} ) ) { |
418
|
1
|
50
|
|
|
|
6
|
$PRESET_MODES{$mode} or |
419
|
|
|
|
|
|
|
croak "Unrecognised preset mode name '$mode'"; |
420
|
|
|
|
|
|
|
|
421
|
1
|
|
|
|
|
38
|
%config = ( %config, $PRESET_MODES{common}->%*, $PRESET_MODES{$mode}->%* ); |
422
|
|
|
|
|
|
|
} |
423
|
|
|
|
|
|
|
|
424
|
8
|
100
|
|
|
|
33
|
if( defined( my $band = delete $changes{band} ) ) { |
425
|
1
|
50
|
|
|
|
5
|
$BANDS{$band} or |
426
|
|
|
|
|
|
|
croak "Unrecognised band name '$band'"; |
427
|
|
|
|
|
|
|
|
428
|
1
|
|
|
|
|
25
|
%config = ( %config, $BANDS{$band}->%* ); |
429
|
|
|
|
|
|
|
} |
430
|
|
|
|
|
|
|
|
431
|
8
|
|
|
|
|
391
|
%config = ( %config, %changes ); |
432
|
8
|
|
|
|
|
66
|
my $newpatable = delete $config{PATABLE}; |
433
|
8
|
100
|
|
|
|
31
|
$newpatable = _fromhex $newpatable if defined $newpatable; |
434
|
|
|
|
|
|
|
|
435
|
8
|
|
|
|
|
14
|
my $oldconfig = $_config; |
436
|
8
|
|
|
|
|
84
|
my $newconfig = pack_CONFIG( %config ); |
437
|
|
|
|
|
|
|
|
438
|
8
|
|
|
|
|
10402
|
my $addr = 0; |
439
|
8
|
|
100
|
|
|
268
|
$addr++ while $addr < length $newconfig and |
440
|
|
|
|
|
|
|
substr( $newconfig, $addr, 1 ) eq substr( $oldconfig, $addr, 1 ); |
441
|
|
|
|
|
|
|
|
442
|
8
|
|
|
|
|
19
|
my $until = length( $newconfig ); |
443
|
8
|
|
100
|
|
|
263
|
$until-- while $until > $addr and |
444
|
|
|
|
|
|
|
substr( $newconfig, $until-1, 1 ) eq substr( $oldconfig, $until-1, 1 ); |
445
|
|
|
|
|
|
|
|
446
|
8
|
100
|
|
|
|
34
|
if( my $len = $until - $addr ) { |
447
|
7
|
|
|
|
|
44
|
await $self->_write_CONFIG( $addr, substr( $newconfig, $addr, $len ) ); |
448
|
7
|
|
|
|
|
9168
|
$_config = $newconfig; |
449
|
|
|
|
|
|
|
} |
450
|
|
|
|
|
|
|
|
451
|
8
|
100
|
66
|
|
|
42
|
if( defined $newpatable and $newpatable ne $_patable ) { |
452
|
2
|
|
|
|
|
11
|
await $self->_write_PATABLE( $newpatable ); |
453
|
2
|
|
|
|
|
2513
|
$_patable = $newpatable; |
454
|
|
|
|
|
|
|
} |
455
|
|
|
|
|
|
|
|
456
|
8
|
|
33
|
|
|
225
|
defined $config{$_} and $_cached_config{$_} = $config{$_} for @CACHED_CONFIG; |
457
|
8
|
|
|
8
|
1
|
20676
|
} |
458
|
|
|
|
|
|
|
|
459
|
|
|
|
|
|
|
=head2 read_marcstate |
460
|
|
|
|
|
|
|
|
461
|
|
|
|
|
|
|
$state = await $chip->read_marcstate; |
462
|
|
|
|
|
|
|
|
463
|
|
|
|
|
|
|
Reads the C register and returns the state name. |
464
|
|
|
|
|
|
|
|
465
|
|
|
|
|
|
|
=cut |
466
|
|
|
|
|
|
|
|
467
|
|
|
|
|
|
|
my @MARCSTATE = qw( |
468
|
|
|
|
|
|
|
SLEEP IDLE XOFF VCOON_MC REGON_MC MANCAL VCOON REGON |
469
|
|
|
|
|
|
|
STARTCAL BWBOOST FS_LOCK IFADCON ENDCAL RX RX_END RX_RST |
470
|
|
|
|
|
|
|
TXRX_SWITCH RXFIFO_OVERFLOW FSTXON TX TXEND RXTX_SWITCH TXFIFO_UNDERFLOW |
471
|
|
|
|
|
|
|
); |
472
|
|
|
|
|
|
|
|
473
|
11
|
|
|
|
|
30
|
async method read_marcstate () |
|
11
|
|
|
|
|
20
|
|
474
|
11
|
|
|
|
|
62
|
{ |
475
|
11
|
|
|
|
|
30
|
my $marcstate = await $self->read_register( REG_MARCSTATE ); |
476
|
11
|
|
33
|
|
|
11230
|
return $MARCSTATE[$marcstate] // $marcstate; |
477
|
11
|
|
|
11
|
1
|
3146
|
} |
478
|
|
|
|
|
|
|
|
479
|
|
|
|
|
|
|
=head2 read_chipstatus_rx |
480
|
|
|
|
|
|
|
|
481
|
|
|
|
|
|
|
=head2 read_chipstatus_tx |
482
|
|
|
|
|
|
|
|
483
|
|
|
|
|
|
|
$status = await $chip->read_chipstatus_rx; |
484
|
|
|
|
|
|
|
|
485
|
|
|
|
|
|
|
$status = await $chip->read_chipstatus_tx; |
486
|
|
|
|
|
|
|
|
487
|
|
|
|
|
|
|
Reads the chip status word and returns a reference to a hash containing the |
488
|
|
|
|
|
|
|
following: |
489
|
|
|
|
|
|
|
|
490
|
|
|
|
|
|
|
STATE => string |
491
|
|
|
|
|
|
|
FIFO_BYTES_AVAILABLE => integer |
492
|
|
|
|
|
|
|
|
493
|
|
|
|
|
|
|
=cut |
494
|
|
|
|
|
|
|
|
495
|
1
|
|
|
1
|
1
|
2125
|
method read_chipstatus_rx () { $self->_read_chipstatus( REG_READ ) } |
|
1
|
|
|
|
|
3
|
|
|
1
|
|
|
|
|
1
|
|
|
1
|
|
|
|
|
6
|
|
496
|
3
|
|
|
3
|
1
|
520
|
method read_chipstatus_tx () { $self->_read_chipstatus( REG_WRITE ) } |
|
3
|
|
|
|
|
6
|
|
|
3
|
|
|
|
|
4
|
|
|
3
|
|
|
|
|
9
|
|
497
|
|
|
|
|
|
|
|
498
|
|
|
|
|
|
|
my @STATES = qw( IDLE RX TX FSTXON CALIBRATE SETTLINE RXFIFO_OVERFLOW TXFIFO_UNDERFLOW ); |
499
|
|
|
|
|
|
|
|
500
|
4
|
|
|
|
|
4
|
async method _read_chipstatus ( $rw ) |
|
4
|
|
|
|
|
6
|
|
|
4
|
|
|
|
|
6
|
|
501
|
4
|
|
|
|
|
10
|
{ |
502
|
4
|
|
|
|
|
11
|
my $status = unpack "C", await $self->protocol->readwrite( |
503
|
|
|
|
|
|
|
pack "C", $rw | CMD_SNOP |
504
|
|
|
|
|
|
|
); |
505
|
|
|
|
|
|
|
|
506
|
|
|
|
|
|
|
return { |
507
|
4
|
|
|
|
|
3516
|
STATE => $STATES[ ( $status & 0x70 ) >> 4 ], |
508
|
|
|
|
|
|
|
FIFO_BYTES_AVAILABLE => ( $status & 0x0F ), |
509
|
|
|
|
|
|
|
}; |
510
|
4
|
|
|
4
|
|
6
|
} |
511
|
|
|
|
|
|
|
|
512
|
|
|
|
|
|
|
=head2 read_pktstatus |
513
|
|
|
|
|
|
|
|
514
|
|
|
|
|
|
|
$status = await $chip->read_pktstatus; |
515
|
|
|
|
|
|
|
|
516
|
|
|
|
|
|
|
Reads the C register and returns a reference to a hash containing |
517
|
|
|
|
|
|
|
boolean fields of the following names: |
518
|
|
|
|
|
|
|
|
519
|
|
|
|
|
|
|
CRC_OK CS PQT_REACHED CCA SFD GDO0 GDO2 |
520
|
|
|
|
|
|
|
|
521
|
|
|
|
|
|
|
=cut |
522
|
|
|
|
|
|
|
|
523
|
1
|
|
|
|
|
1
|
async method read_pktstatus () |
|
1
|
|
|
|
|
3
|
|
524
|
1
|
|
|
|
|
3
|
{ |
525
|
1
|
|
|
|
|
4
|
my $pktstatus = unpack "C", await $self->protocol->write_then_read( |
526
|
|
|
|
|
|
|
pack( "C", REG_READ|REG_BURST | REG_PKTSTATUS ), 1 |
527
|
|
|
|
|
|
|
); |
528
|
|
|
|
|
|
|
|
529
|
|
|
|
|
|
|
return { |
530
|
1
|
|
|
|
|
868
|
CRC_OK => !!( $pktstatus & ( 1 << 7 ) ), |
531
|
|
|
|
|
|
|
CS => !!( $pktstatus & ( 1 << 6 ) ), |
532
|
|
|
|
|
|
|
PQT_REACHED => !!( $pktstatus & ( 1 << 5 ) ), |
533
|
|
|
|
|
|
|
CCA => !!( $pktstatus & ( 1 << 4 ) ), |
534
|
|
|
|
|
|
|
SFD => !!( $pktstatus & ( 1 << 3 ) ), |
535
|
|
|
|
|
|
|
GDO2 => !!( $pktstatus & ( 1 << 2 ) ), |
536
|
|
|
|
|
|
|
GDO0 => !!( $pktstatus & ( 1 << 0 ) ), |
537
|
|
|
|
|
|
|
}; |
538
|
1
|
|
|
1
|
1
|
2711
|
} |
539
|
|
|
|
|
|
|
|
540
|
11
|
|
|
|
|
19
|
async method command ( $cmd ) |
|
11
|
|
|
|
|
20
|
|
|
11
|
|
|
|
|
17
|
|
541
|
11
|
|
|
|
|
28
|
{ |
542
|
11
|
50
|
33
|
|
|
58
|
$cmd >= 0x30 and $cmd <= 0x3D or |
543
|
|
|
|
|
|
|
croak "Invalid command byte"; |
544
|
|
|
|
|
|
|
|
545
|
11
|
|
|
|
|
47
|
await $self->protocol->write( pack( "C", $cmd ) ); |
546
|
11
|
|
|
11
|
0
|
19
|
} |
547
|
|
|
|
|
|
|
|
548
|
|
|
|
|
|
|
=head2 reset |
549
|
|
|
|
|
|
|
|
550
|
|
|
|
|
|
|
await $chip->reset; |
551
|
|
|
|
|
|
|
|
552
|
|
|
|
|
|
|
Command the chip to perform a software reset. |
553
|
|
|
|
|
|
|
|
554
|
|
|
|
|
|
|
=cut |
555
|
|
|
|
|
|
|
|
556
|
1
|
|
|
|
|
2
|
async method reset () |
|
1
|
|
|
|
|
2
|
|
557
|
1
|
|
|
|
|
2
|
{ |
558
|
1
|
|
|
|
|
5
|
await $self->command( CMD_SRES ); |
559
|
1
|
|
|
1
|
1
|
180
|
} |
560
|
|
|
|
|
|
|
|
561
|
|
|
|
|
|
|
=head2 flush_fifos |
562
|
|
|
|
|
|
|
|
563
|
|
|
|
|
|
|
await $chip->flush_fifos; |
564
|
|
|
|
|
|
|
|
565
|
|
|
|
|
|
|
Command the chip to flush the RX and TX FIFOs. |
566
|
|
|
|
|
|
|
|
567
|
|
|
|
|
|
|
=cut |
568
|
|
|
|
|
|
|
|
569
|
1
|
|
|
|
|
2
|
async method flush_fifos () |
|
1
|
|
|
|
|
1
|
|
570
|
1
|
|
|
|
|
5
|
{ |
571
|
1
|
|
|
|
|
5
|
await $self->command( CMD_SFRX ); |
572
|
1
|
|
|
|
|
1242
|
await $self->command( CMD_SFTX ); |
573
|
1
|
|
|
1
|
1
|
9923
|
} |
574
|
|
|
|
|
|
|
|
575
|
|
|
|
|
|
|
=head2 start_rx |
576
|
|
|
|
|
|
|
|
577
|
|
|
|
|
|
|
await $chip->start_rx; |
578
|
|
|
|
|
|
|
|
579
|
|
|
|
|
|
|
Command the chip to enter RX mode. |
580
|
|
|
|
|
|
|
|
581
|
|
|
|
|
|
|
=cut |
582
|
|
|
|
|
|
|
|
583
|
1
|
|
|
|
|
2
|
async method start_rx () |
|
1
|
|
|
|
|
2
|
|
584
|
1
|
|
|
|
|
5
|
{ |
585
|
1
|
|
|
|
|
5
|
await $self->command( CMD_SIDLE ); |
586
|
1
|
|
|
|
|
1217
|
1 until ( await $self->read_marcstate ) eq "IDLE"; |
587
|
|
|
|
|
|
|
|
588
|
1
|
|
|
|
|
72
|
await $self->command( CMD_SRX ); |
589
|
1
|
|
|
|
|
1011
|
1 until ( await $self->read_marcstate ) eq "RX"; |
590
|
1
|
|
|
1
|
1
|
3617
|
} |
591
|
|
|
|
|
|
|
|
592
|
|
|
|
|
|
|
=head2 start_tx |
593
|
|
|
|
|
|
|
|
594
|
|
|
|
|
|
|
await $chip->start_tx; |
595
|
|
|
|
|
|
|
|
596
|
|
|
|
|
|
|
Command the chip to enter TX mode. |
597
|
|
|
|
|
|
|
|
598
|
|
|
|
|
|
|
=cut |
599
|
|
|
|
|
|
|
|
600
|
3
|
|
|
|
|
5
|
async method start_tx () |
|
3
|
|
|
|
|
5
|
|
601
|
3
|
|
|
|
|
8
|
{ |
602
|
3
|
|
|
|
|
10
|
await $self->command( CMD_SIDLE ); |
603
|
3
|
|
|
|
|
3453
|
1 until ( await $self->read_marcstate ) eq "IDLE"; |
604
|
|
|
|
|
|
|
|
605
|
3
|
|
|
|
|
141
|
await $self->command( CMD_STX ); |
606
|
3
|
|
|
|
|
3351
|
1 until ( await $self->read_marcstate ) eq "TX"; |
607
|
3
|
|
|
3
|
1
|
3526
|
} |
608
|
|
|
|
|
|
|
|
609
|
|
|
|
|
|
|
=head2 idle |
610
|
|
|
|
|
|
|
|
611
|
|
|
|
|
|
|
await $chip->idle; |
612
|
|
|
|
|
|
|
|
613
|
|
|
|
|
|
|
Command the chip to enter IDLE mode. |
614
|
|
|
|
|
|
|
|
615
|
|
|
|
|
|
|
=cut |
616
|
|
|
|
|
|
|
|
617
|
0
|
|
|
|
|
0
|
async method idle () |
|
0
|
|
|
|
|
0
|
|
618
|
0
|
|
|
|
|
0
|
{ |
619
|
0
|
|
|
|
|
0
|
await $self->command( CMD_SIDLE ); |
620
|
0
|
|
|
0
|
1
|
0
|
} |
621
|
|
|
|
|
|
|
|
622
|
|
|
|
|
|
|
=head2 read_rxfifo |
623
|
|
|
|
|
|
|
|
624
|
|
|
|
|
|
|
$bytes = await $chip->read_rxfifo( $len ); |
625
|
|
|
|
|
|
|
|
626
|
|
|
|
|
|
|
Reads the given number of bytes from the RX FIFO. |
627
|
|
|
|
|
|
|
|
628
|
|
|
|
|
|
|
=cut |
629
|
|
|
|
|
|
|
|
630
|
4
|
|
|
|
|
8
|
async method read_rxfifo ( $len ) |
|
4
|
|
|
|
|
9
|
|
|
4
|
|
|
|
|
6
|
|
631
|
4
|
|
|
|
|
15
|
{ |
632
|
4
|
50
|
|
|
|
17
|
await( $self->read_register( REG_RXBYTES ) ) >= $len or |
633
|
|
|
|
|
|
|
croak "RX UNDERFLOW - not enough bytes available"; |
634
|
|
|
|
|
|
|
|
635
|
4
|
|
|
|
|
4726
|
return await $self->protocol->write_then_read( |
636
|
|
|
|
|
|
|
pack( "C", REG_READ | REG_BURST | REG_RXFIFO ), $len |
637
|
|
|
|
|
|
|
); |
638
|
4
|
|
|
4
|
1
|
7265
|
} |
639
|
|
|
|
|
|
|
|
640
|
|
|
|
|
|
|
=head2 write_txfifo |
641
|
|
|
|
|
|
|
|
642
|
|
|
|
|
|
|
await $chip->write_txfifo( $bytes ); |
643
|
|
|
|
|
|
|
|
644
|
|
|
|
|
|
|
Writes the given bytes into the TX FIFO. |
645
|
|
|
|
|
|
|
|
646
|
|
|
|
|
|
|
=cut |
647
|
|
|
|
|
|
|
|
648
|
3
|
|
|
|
|
5
|
async method write_txfifo ( $bytes ) |
|
3
|
|
|
|
|
6
|
|
|
3
|
|
|
|
|
4
|
|
649
|
3
|
|
|
|
|
8
|
{ |
650
|
3
|
|
|
|
|
12
|
await $self->protocol->write( |
651
|
|
|
|
|
|
|
pack "C a*", REG_WRITE | REG_BURST | REG_TXFIFO, $bytes |
652
|
|
|
|
|
|
|
); |
653
|
3
|
|
|
3
|
1
|
4639
|
} |
654
|
|
|
|
|
|
|
|
655
|
|
|
|
|
|
|
=head2 receive |
656
|
|
|
|
|
|
|
|
657
|
|
|
|
|
|
|
$packet = await $chip->receive; |
658
|
|
|
|
|
|
|
|
659
|
|
|
|
|
|
|
Retrieves a packet from the RX FIFO, returning a HASH reference. |
660
|
|
|
|
|
|
|
|
661
|
|
|
|
|
|
|
data => STRING |
662
|
|
|
|
|
|
|
|
663
|
|
|
|
|
|
|
This method automatically strips the C, C and C fields from |
664
|
|
|
|
|
|
|
the data and adds them to the returned hash if the chip is configured with |
665
|
|
|
|
|
|
|
C. |
666
|
|
|
|
|
|
|
|
667
|
|
|
|
|
|
|
RSSI => NUM (in units of dBm) |
668
|
|
|
|
|
|
|
LQI => INT |
669
|
|
|
|
|
|
|
CRC_OK => BOOL |
670
|
|
|
|
|
|
|
|
671
|
|
|
|
|
|
|
This method automatically handles prepending the packet length if the chip is |
672
|
|
|
|
|
|
|
configured in variable-length packet mode. |
673
|
|
|
|
|
|
|
|
674
|
|
|
|
|
|
|
B: Note that, despite its name, this method does not currently wait for |
675
|
|
|
|
|
|
|
a packet to be available - the caller is responsible for calling L |
676
|
|
|
|
|
|
|
and waiting for a packet to be received. This may be provided in a later |
677
|
|
|
|
|
|
|
version by polling chip status or using interrupts if C makes |
678
|
|
|
|
|
|
|
them available. |
679
|
|
|
|
|
|
|
|
680
|
|
|
|
|
|
|
=cut |
681
|
|
|
|
|
|
|
|
682
|
2
|
|
|
|
|
6
|
async method receive () |
|
2
|
|
|
|
|
4
|
|
683
|
2
|
|
|
|
|
7
|
{ |
684
|
|
|
|
|
|
|
# TODO: Check for RX UNDERFLOW somehow? |
685
|
|
|
|
|
|
|
|
686
|
2
|
|
|
|
|
5
|
my $fixedlen = $_cached_config{LENGTH_CONFIG} eq "fixed"; |
687
|
2
|
|
|
|
|
7
|
my $append_status = $_cached_config{APPEND_STATUS}; |
688
|
|
|
|
|
|
|
|
689
|
|
|
|
|
|
|
my $len = $fixedlen ? |
690
|
|
|
|
|
|
|
$_cached_config{PACKET_LENGTH} : |
691
|
2
|
100
|
|
|
|
11
|
unpack "C", await $self->read_rxfifo( 1 ); |
692
|
|
|
|
|
|
|
|
693
|
2
|
50
|
|
|
|
958
|
$len += 2 if $append_status; |
694
|
|
|
|
|
|
|
|
695
|
2
|
|
|
|
|
7
|
my $bytes = await $self->read_rxfifo( $len ); |
696
|
2
|
|
|
|
|
2184
|
my %ret; |
697
|
|
|
|
|
|
|
|
698
|
2
|
50
|
|
|
|
26
|
if( $append_status ) { |
699
|
2
|
|
|
|
|
15
|
my ( $rssi, $lqi ) = unpack( "c C", substr( $bytes, -2, 2, "" ) ); |
700
|
|
|
|
|
|
|
|
701
|
|
|
|
|
|
|
# RSSI is 2s complement in 0.5dBm units offset from -74dBm |
702
|
2
|
|
|
|
|
11
|
$ret{RSSI} = $rssi / 2 - 74; |
703
|
|
|
|
|
|
|
|
704
|
|
|
|
|
|
|
# LQI/CRC_OK |
705
|
2
|
|
|
|
|
5
|
$ret{LQI} = $lqi & 0x7F; |
706
|
2
|
|
|
|
|
7
|
$ret{CRC_OK} = !!( $lqi & 0x80 ); |
707
|
|
|
|
|
|
|
} |
708
|
|
|
|
|
|
|
|
709
|
2
|
|
|
|
|
5
|
$ret{data} = $bytes; |
710
|
|
|
|
|
|
|
|
711
|
2
|
|
|
|
|
11
|
return \%ret; |
712
|
2
|
|
|
2
|
1
|
297
|
} |
713
|
|
|
|
|
|
|
|
714
|
|
|
|
|
|
|
=head2 transmit |
715
|
|
|
|
|
|
|
|
716
|
|
|
|
|
|
|
await $chip->transmit( $bytes ); |
717
|
|
|
|
|
|
|
|
718
|
|
|
|
|
|
|
Enters TX mode and sends a packet containing the given bytes. |
719
|
|
|
|
|
|
|
|
720
|
|
|
|
|
|
|
This method automatically handles prepending the packet length if the chip is |
721
|
|
|
|
|
|
|
configured in variable-length packet mode. |
722
|
|
|
|
|
|
|
|
723
|
|
|
|
|
|
|
=cut |
724
|
|
|
|
|
|
|
|
725
|
2
|
|
|
|
|
4
|
async method transmit ( $bytes ) |
|
2
|
|
|
|
|
3
|
|
|
2
|
|
|
|
|
3
|
|
726
|
2
|
|
|
|
|
4
|
{ |
727
|
2
|
|
|
|
|
5
|
my $fixedlen = $_cached_config{LENGTH_CONFIG} eq "fixed"; |
728
|
|
|
|
|
|
|
|
729
|
2
|
|
|
|
|
4
|
my $pktlen = length $bytes; |
730
|
2
|
100
|
|
|
|
5
|
if( $fixedlen ) { |
731
|
|
|
|
|
|
|
$pktlen == $_cached_config{PACKET_LENGTH} or |
732
|
1
|
50
|
|
|
|
3
|
croak "Expected a packet $_cached_config{PACKET_LENGTH} bytes long"; |
733
|
|
|
|
|
|
|
} |
734
|
|
|
|
|
|
|
else { |
735
|
|
|
|
|
|
|
# Ensure we can't overflow either TX or RX FIFO |
736
|
1
|
50
|
|
|
|
3
|
$pktlen <= 62 or |
737
|
|
|
|
|
|
|
croak "Expected no more than 62 bytes of packet data" |
738
|
|
|
|
|
|
|
} |
739
|
|
|
|
|
|
|
|
740
|
2
|
|
|
|
|
6
|
await $self->start_tx; |
741
|
|
|
|
|
|
|
|
742
|
2
|
100
|
|
|
|
184
|
$bytes = pack "C a*", $pktlen, $bytes if !$fixedlen; |
743
|
|
|
|
|
|
|
|
744
|
2
|
|
|
|
|
5
|
await $self->write_txfifo( $bytes ); |
745
|
|
|
|
|
|
|
|
746
|
2
|
|
|
|
|
1936
|
my $timeout = 20; # TODO: configuration |
747
|
2
|
|
|
|
|
6
|
while( await( $self->read_chipstatus_tx )->{STATE} eq "TX" ) { |
748
|
0
|
0
|
|
|
|
|
$timeout-- or croak "Timed out waiting for TX to complete"; |
749
|
0
|
|
|
|
|
|
await Future::IO->sleep( $_poll_interval ); |
750
|
|
|
|
|
|
|
} |
751
|
2
|
|
|
2
|
1
|
304
|
} |
752
|
|
|
|
|
|
|
|
753
|
|
|
|
|
|
|
{ |
754
|
|
|
|
|
|
|
while( readline DATA ) { |
755
|
|
|
|
|
|
|
chomp; |
756
|
|
|
|
|
|
|
next if m/^#/; |
757
|
|
|
|
|
|
|
my ( $name, $fields ) = split m/\|/, $_; |
758
|
|
|
|
|
|
|
|
759
|
|
|
|
|
|
|
$PRESET_MODES{$name} = +{ |
760
|
|
|
|
|
|
|
map { m/(.*?)=(.*)/ } split m/,/, $fields |
761
|
|
|
|
|
|
|
}; |
762
|
|
|
|
|
|
|
} |
763
|
|
|
|
|
|
|
|
764
|
|
|
|
|
|
|
%BANDS = ( |
765
|
|
|
|
|
|
|
"433MHz" => { |
766
|
|
|
|
|
|
|
FREQ => 1091426, |
767
|
|
|
|
|
|
|
# From the datasheet presuming 433MHz on multilayer inductors |
768
|
|
|
|
|
|
|
PATABLE => "12.0E.1D.34.60.84.C8.C0", |
769
|
|
|
|
|
|
|
}, |
770
|
|
|
|
|
|
|
|
771
|
|
|
|
|
|
|
"868MHz" => { |
772
|
|
|
|
|
|
|
FREQ => 2188650, |
773
|
|
|
|
|
|
|
# From the datasheet presuming 868MHz on multilayer inductors |
774
|
|
|
|
|
|
|
PATABLE => "03.0F.1E.27.50.81.CB.C2", |
775
|
|
|
|
|
|
|
}, |
776
|
|
|
|
|
|
|
); |
777
|
|
|
|
|
|
|
} |
778
|
|
|
|
|
|
|
|
779
|
|
|
|
|
|
|
0x55AA; |
780
|
|
|
|
|
|
|
|
781
|
|
|
|
|
|
|
=head1 TODO |
782
|
|
|
|
|
|
|
|
783
|
|
|
|
|
|
|
=over 4 |
784
|
|
|
|
|
|
|
|
785
|
|
|
|
|
|
|
=item * |
786
|
|
|
|
|
|
|
|
787
|
|
|
|
|
|
|
Polling/interrupts to wait for RX packet |
788
|
|
|
|
|
|
|
|
789
|
|
|
|
|
|
|
=item * |
790
|
|
|
|
|
|
|
|
791
|
|
|
|
|
|
|
Support addressing modes in L and L |
792
|
|
|
|
|
|
|
|
793
|
|
|
|
|
|
|
=back |
794
|
|
|
|
|
|
|
|
795
|
|
|
|
|
|
|
=head1 AUTHOR |
796
|
|
|
|
|
|
|
|
797
|
|
|
|
|
|
|
Paul Evans |
798
|
|
|
|
|
|
|
|
799
|
|
|
|
|
|
|
=cut |
800
|
|
|
|
|
|
|
|
801
|
|
|
|
|
|
|
__DATA__ |