-
Notifications
You must be signed in to change notification settings - Fork 0
/
rfsend.c
287 lines (242 loc) · 8.55 KB
/
rfsend.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
// rfsend.c
// Send RF commands to devices using the common PT2260 / EV1527 protocols.
//
// Uses one of the built in 200MHZ Programmable Realtime Units (PRU's)
// of the Sitara processor used on the Beaglebone series of Microcontrollers.
// Based on work done for the Arduino with the rcswitch library.
// (https://github.com/sui77/rc-switch)
//
//
// It uses Linux Userspace IO (uio).
// Make sure uio_pruss module is loaded along with a suitable cape
// (uio can be selected in /boot/uEnv.txt)
// cape_universal will work - use config-pin p8.11 pruout
#include <prussdrv.h>
#include <pruss_intc_mapping.h>
#include <stdint.h>
#include <stdlib.h>
#include <stdio.h>
#include <stdbool.h>
#include <argp.h>
#include "rfsendpru_bin.h"
#define HIGH_PULSE 0
#define LOW_PULSE 1
typedef struct Protocol {
uint16_t pulseLength; // in us
uint8_t syncFactor[2];
uint8_t zero[2];
uint8_t one[2];
/** @brief if true inverts the high and low logic levels */
bool invert;
} Protocol;
typedef struct Message {
uint8_t repeat_count;
int8_t signal_index;
uint16_t pulse_length;
uint8_t gpio_pin;
uint8_t signal_length;
uint8_t sub_bit;
uint32_t command;
Protocol protocol;
} Message_t;
typedef struct {
uint32_t result;
uint32_t syncHigh;
uint32_t syncLow;
uint32_t zeroHigh;
uint32_t zeroLow;
uint32_t oneHigh;
uint32_t oneLow;
uint32_t command;
uint8_t length;
uint8_t repeats;
uint8_t inverted;
uint8_t spare;
} PrussDataRam_t;
//const uint32_t rc5sockets0313[]={1070396,1070387,1070540,1070531,1070860,1070851,1072396,1072387,1078540,1078531};
static const Protocol proto[] = {
{ 350, { 1, 31 }, { 1, 3 }, { 3, 1 }, false }, // protocol 1
{ 650, { 1, 10 }, { 1, 2 }, { 2, 1 }, false }, // protocol 2
{ 100, { 30, 71 }, { 4, 11 }, { 9, 6 }, false }, // protocol 3
{ 380, { 1, 6 }, { 1, 3 }, { 3, 1 }, false }, // protocol 4
{ 500, { 6, 14 }, { 1, 2 }, { 2, 1 }, false }, // protocol 5
{ 450, { 23, 1 }, { 1, 2 }, { 2, 1 }, true } // protocol 6 (HT6P20B)
};
const char *argp_program_version =
"rfsend 1.2";
/* Program documentation. */
static char doc[] =
"For Beaglebone Microcontrollers only.\nSends a string of commands over RF to control RC5 type switches\
\vUses PRU0 via the pru_uio overlay.\nEnsure you have an appropriate cape loaded\n\
(e.g. cape_universal + config-pin p8.11 pruout)\n\
Connect P8_11 to <DATA> on a cheap 433Mhz transmitter module\n\
Protocol ID: 1 - PT2260 (350us pulse timing)\n\
Protocol ID: 2 - ?? (650us pulse timing)\n\
Protocol ID: 3 - ?? (100us pulse timing)\n\
Protocol ID: 4 - ?? (380us pulse timing)\n\
Protocol ID: 5 - ?? (500us pulse timing)\n\
Protocol ID: 6 - HT6P20B (450us pulse timing)\n\n\
** MUST BE RUN WITH ROOT PRIVILEGES **";
/* A description of the arguments we accept. */
static char args_doc[] = "[COMMAND...]";
/* The options we understand. */
static struct argp_option options[] = {
{"debug", 'd', 0, 0, "Produce debug output" },
{0,0,0,0, "" },
{"binary", 'b', 0, 0, "Command is in binary" },
{"invert", 'i', 0, 0, "Invert signal" },
{"repeat", 'r', "COUNT", OPTION_ARG_OPTIONAL,
"Repeat the output COUNT (default 3) times"},
{"protocol", 'p', "ID", OPTION_ARG_OPTIONAL,
"Message protocol id (default 1)"},
{"time", 't', "MICROSECS", OPTION_ARG_OPTIONAL,
"Override default protocol pulse timing"},
{"length", 'l', "BITS", OPTION_ARG_OPTIONAL,
"Command length (default 24) bits"},
{ 0 }
};
/* Used by main to communicate with parse_opt. */
struct arguments
{
uint32_t time;
int debug;
int invert;
int base;
int repeat_count;
int protocol_id;
int length;
char **commands;
};
/* Parse a single option. */
static error_t
parse_opt (int key, char *arg, struct argp_state *state)
{
/* Get the input argument from argp_parse, which we
know is a pointer to our arguments structure. */
struct arguments *arguments = state->input;
switch (key)
{
case 'b':
arguments->base = 2;
break;
case 'd':
arguments->debug = 1;
break;
case 'i':
arguments->invert = 1;
break;
case 'r':
arguments->repeat_count = arg ? atoi (arg) : 3;
break;
case 'p':
arguments->protocol_id = arg ? atoi (arg) : 1;
break;
case 't':
arguments->time = arg ? atoi (arg) : 0;
break;
case 'l':
arguments->length = arg ? atoi (arg) : 24;
break;
case ARGP_KEY_NO_ARGS:
argp_usage (state);
case ARGP_KEY_ARG:
arguments->commands = &state->argv[state->next-1];
state->next = state->argc;
break;
default:
return ARGP_ERR_UNKNOWN;
}
return 0;
}
/* Our argp parser. */
static struct argp argp = { options, parse_opt, args_doc, doc };
int main(int argc, char * argv[])
{
tpruss_intc_initdata prussIntCInitData = PRUSS_INTC_INITDATA;
PrussDataRam_t * prussDataRam;
Message_t msg;
int ret, i;
struct arguments arguments;
/* Default values. */
arguments.debug = 0;
arguments.repeat_count = 3;
arguments.protocol_id = 1;
arguments.time = 0;
arguments.length = 24;
arguments.invert = -1;
arguments.base = 0;
/* Parse our arguments; every option seen by parse_opt will
be reflected in arguments. */
argp_parse (&argp, argc, argv, 0, 0, &arguments);
// First, initialize the driver and open the kernel device
prussdrv_init();
ret = prussdrv_open(PRU_EVTOUT_0);
if(ret != 0) {
printf("Failed to open PRUSS driver!\n");
exit(ret);
}
// Set up the interrupt mapping so we can wait on INTC later
prussdrv_pruintc_init(&prussIntCInitData);
// Map PRU DATARAM; reinterpret the pointer type as a pointer to
// our defined memory mapping struct. We could also use uint8_t *
// to access the RAM as a plain array of bytes, or uint32_t * to
// access it as words.
prussdrv_map_prumem(PRUSS0_PRU0_DATARAM, (void * *)&prussDataRam);
// Manually initialize PRU DATARAM - this struct is mapped to the
// PRU, so these assignments actually modify DATARAM directly.
msg.protocol = proto[arguments.protocol_id-1];
uint32_t pulse_length = arguments.time * 100;
if (pulse_length == 0)
pulse_length = msg.protocol.pulseLength * 100; //nanoseconds/10
prussDataRam->syncHigh = msg.protocol.syncFactor[HIGH_PULSE] * pulse_length;
prussDataRam->syncLow = msg.protocol.syncFactor[LOW_PULSE] * pulse_length;
prussDataRam->zeroHigh = msg.protocol.zero[HIGH_PULSE] * pulse_length;
prussDataRam->zeroLow = msg.protocol.zero[LOW_PULSE] * pulse_length;
prussDataRam->oneHigh = msg.protocol.one[HIGH_PULSE] * pulse_length;
prussDataRam->oneLow = msg.protocol.one[LOW_PULSE] * pulse_length;
prussDataRam->length = arguments.length;
prussDataRam->result = 0;
if (arguments.invert != -1)
prussDataRam->inverted = arguments.invert;
else
prussDataRam->inverted = msg.protocol.invert;
for (i = 0; arguments.commands[i]; i++)
{
prussDataRam->command = strtoul(arguments.commands[i],NULL,arguments.base);
prussDataRam->repeats = arguments.repeat_count;
if (arguments.debug)
{
printf("Command: %d\n",prussDataRam->command);
}
// Memory fence: not strictly needed here, as compiler will insert
// an implicit fence when prussdrv_exec_code(...) is called, but
// a good habit to be in.
// This ensures that the writes to x, y, sum are fully complete
// before the PRU code is executed: imagine what kind of painful-
// to-debug problems you'd see if the compiler or hardware deferred
// the writes until after the PRU started running!
__sync_synchronize();
prussdrv_exec_code(0, prussPru0Code, sizeof prussPru0Code);
// Wait for INTC from the PRU, signaling it's about to HALT...
prussdrv_pru_wait_event(PRU_EVTOUT_0);
// Clear the event: if you don't do this you will not be able to
// wait again.
prussdrv_pru_clear_event(PRU_EVTOUT_0, PRU0_ARM_INTERRUPT);
// Make absolutely sure we read sum again below, after the PRU
// writes to it. Otherwise, the compiler or hardware might cache
// the value we wrote above and just return us that. Again, not
// actually necessary because the compiler inserts an implicit
// fence at prussdrv_pru_wait_event(...), but a good habit.
__sync_synchronize();
}
// Read the result returned by the PRU
// Disable the PRU and exit; if we don't do this the PRU may
// continue running after our program quits! The TI kernel driver
// is not very careful about cleaning up after us.
// Since it is possible for the PRU to trash memory and otherwise
// cause lockups or crashes, especially if it's manipulating
// peripherals or writing to shared DDR, this is important!
prussdrv_pru_disable(0);
prussdrv_exit();
exit(0);
}