forked from min-protocol/min
-
Notifications
You must be signed in to change notification settings - Fork 0
/
min.c
271 lines (239 loc) · 7.38 KB
/
min.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
/* Reference implementation of Layer 1 (frame) of MIN 1.0
* Adapted to use Arduino Streams
*
* Author: Ken Tindell, Marcus Nowotny
* Copyright (c) 2014-2015 JK Energy Ltd.
* Licensed under MIT License.
*/
#include "min.h"
#if ARDUINO >= 100
#include <Arduino.h>
#else
#include <WProgram.h>
#endif
/* Maximum size of a MIN frame:
*
* 3 (header) + 1 (id) + 1 (control) + 15 (payload) + 2 (checksum) + 1 (EOF) + stuff bytes
*
* Stuff bytes can be inserted (in worst case) after every 2 bytes in id/control/payload/checksum
* = (1 + 1 + 15 + 2) / 2 = 10.
*
* Maximum size of a frame is therefore 32 bytes.
*/
#define MAX_FRAME_SIZE (256U)
/* Magic bytes */
#define HEADER_BYTE (0xaaU)
#define STUFF_BYTE (0x55U)
#define EOF_BYTE (0x55U)
/* Receiving state machine */
#define SEARCHING_FOR_SOF (0)
#define RECEIVING_ID (1U)
#define RECEIVING_CONTROL (2U)
#define RECEIVING_PAYLOAD (3U)
#define RECEIVING_CHECKSUM_HIGH (4U)
#define RECEIVING_CHECKSUM_LOW (5U)
#define RECEIVING_EOF (6U)
static uint8_t rx_frame_buf[MAX_FRAME_PAYLOAD_SIZE];
/* Fletcher's checksum algorithm (for 16-bit checksums)
*
* Declared static so a good compiler will inline these calls.
*/
static uint16_t rx_sum1;
static uint16_t rx_sum2;
static void fletcher16_rx_init(void)
{
rx_sum1 = 0x00ffu;
rx_sum2 = 0x00ffu;
}
static void fletcher16_rx_step(uint8_t byte)
{
rx_sum2 += rx_sum1 += byte;
}
static uint16_t fletcher16_rx_finalize(void)
{
rx_sum1 = (rx_sum1 & 0x00ffu) + (rx_sum1 >> 8);
rx_sum2 = (rx_sum2 & 0x00ffu) + (rx_sum2 >> 8);
return rx_sum2 << 8 | rx_sum1;
}
static uint8_t rx_header_bytes_seen = 0;
static uint8_t rx_frame_state = SEARCHING_FOR_SOF; /* State of receiver */
static uint16_t rx_frame_checksum; /* Checksum received over the wire */
static uint8_t rx_payload_bytes; /* Length of payload received so far */
static uint8_t rx_frame_id; /* ID of frame being received */
static uint8_t rx_remaining_frame_length; /* Length of frame */
static uint8_t rx_frame_length; /* Control byte */
//a callback to notify of dropped frames
min_frame_dropped_function min_frame_dropped_callback=NULL;
void min_init_layer1()
{
rx_header_bytes_seen = 0;
rx_frame_state = SEARCHING_FOR_SOF;
}
/* Main receive function for a byte
*
* Typically called from a background task that's reading a FIFO in a loop.
*/
void min_rx_byte(uint8_t byte)
{
/* Regardless of state, three header bytes means "start of frame" and
* should reset the frame buffer and be ready to receive frame data
*
* Two in a row in over the frame means to expect a stuff byte.
*/
if(rx_header_bytes_seen == 2) {
rx_header_bytes_seen = 0;
if(byte == HEADER_BYTE) {
rx_frame_state = RECEIVING_ID;
return;
}
if(byte == STUFF_BYTE) {
/* Discard this byte; carry on receiving on the next character */
return;
}
else {
/* Something has gone wrong, give up on this frame and look for header again */
if (min_frame_dropped_callback!=NULL) {
min_frame_dropped_callback();
}
rx_frame_state = SEARCHING_FOR_SOF;
return;
}
}
if(byte == HEADER_BYTE) {
rx_header_bytes_seen++;
}
else {
rx_header_bytes_seen = 0;
//TODO if we are in SEARCHING_FOR_SOF this could be a dropped frame (perhaps some header bytes have to been seen)
}
switch(rx_frame_state) {
case SEARCHING_FOR_SOF:
break;
case RECEIVING_ID:
rx_frame_id = byte;
rx_payload_bytes = 0;
fletcher16_rx_init();
fletcher16_rx_step(byte);
rx_frame_state = RECEIVING_CONTROL;
break;
case RECEIVING_CONTROL:
rx_remaining_frame_length = byte ;
rx_frame_length = byte;
fletcher16_rx_step(byte);
if(rx_remaining_frame_length > 0) {
rx_frame_state = RECEIVING_PAYLOAD;
}
else {
rx_frame_state = RECEIVING_CHECKSUM_HIGH;
}
break;
case RECEIVING_PAYLOAD:
rx_frame_buf[rx_payload_bytes++] = byte;
fletcher16_rx_step(byte);
if(--rx_remaining_frame_length == 0) {
rx_frame_state = RECEIVING_CHECKSUM_HIGH;
}
break;
case RECEIVING_CHECKSUM_HIGH:
rx_frame_checksum = (uint16_t)byte << 8;
rx_frame_state = RECEIVING_CHECKSUM_LOW;
break;
case RECEIVING_CHECKSUM_LOW:
rx_frame_checksum |= byte;
if(rx_frame_checksum != fletcher16_rx_finalize()) {
if (min_frame_dropped_callback!=NULL) {
min_frame_dropped_callback();
}
/* Frame fails the checksum and so is dropped */
rx_frame_state = SEARCHING_FOR_SOF;
}
else {
/* Checksum passes, go on to check for the end-of-frame marker */
rx_frame_state = RECEIVING_EOF;
}
break;
case RECEIVING_EOF:
if(byte == 0x55u) {
/* Frame received OK, pass up data to handler */
min_frame_received(rx_frame_buf, rx_frame_length, rx_frame_id);
} else {
/* else discard */
if (min_frame_dropped_callback!=NULL) {
min_frame_dropped_callback();
}
}
/* Look for next frame */
rx_frame_state = SEARCHING_FOR_SOF;
break;
}
}
static uint16_t tx_sum1;
static uint16_t tx_sum2;
static void fletcher16_tx_init(void)
{
tx_sum1 = 0x00ffu;
tx_sum2 = 0x00ffu;
}
static void fletcher16_tx_step(uint8_t byte)
{
tx_sum2 += tx_sum1 += byte;
}
static uint16_t fletcher16_tx_finalize(void)
{
tx_sum1 = (tx_sum1 & 0x00ffu) + (tx_sum1 >> 8);
tx_sum2 = (tx_sum2 & 0x00ffu) + (tx_sum2 >> 8);
return tx_sum2 << 8 | tx_sum1;
}
static uint8_t tx_header_byte_countdown;
/* Queue a byte for transmission but also insert a stuff byte of 0x55 where two 0xaa bytes
* occur in sequence
*/
static void stuffed_tx_byte(uint8_t byte)
{
min_tx_byte(byte);
fletcher16_tx_step(byte);
if(byte == HEADER_BYTE) {
if(--tx_header_byte_countdown == 0) {
min_tx_byte(STUFF_BYTE); /* Stuff byte */
tx_header_byte_countdown = 2U;
}
}
else {
tx_header_byte_countdown = 2U;
}
}
/* Main transmit function. Calls out to a byte transmit function,
* typically connected to a FIFO with a UART driver.
*
* payload is a pointer to a buffer containing the rest of the payload,
* and id is the frame ID within a message set.
*
* control encodes the length of the frame in the bottom four bits. The
* top four bits are reserved and must be passed through unchanged.
*
* Frames should not be transmitted until sending is enabled (so that
* the system can be quiescent until other devices have initialized).
*
*/
void min_tx_frame(uint8_t id, uint8_t payload[], uint8_t length)
{
uint8_t n, i;
uint16_t checksum;
tx_header_byte_countdown = 2U;
fletcher16_tx_init();
/* Header is 3 bytes; because unstuffed will reset receiver immediately */
min_tx_byte(HEADER_BYTE);
min_tx_byte(HEADER_BYTE);
min_tx_byte(HEADER_BYTE);
stuffed_tx_byte(id);
stuffed_tx_byte(length);
for(i = 0, n = length; n > 0; n--, i++) {
stuffed_tx_byte(payload[i]);
}
checksum = fletcher16_tx_finalize();
/* Network order is big-endian */
stuffed_tx_byte(checksum >> 8);
stuffed_tx_byte(checksum &0x00ff);
/* Ensure end-of-frame doesn't contain 0xaa and confuse search for start-of-frame */
min_tx_byte(EOF_BYTE);
}