19static inline void wr_u16(uint8_t *p, uint16_t v)
21 p[0] = (uint8_t)(v >> 8);
22 p[1] = (uint8_t)(v & 0xFF);
25static inline uint16_t rd_u16(
const uint8_t *p)
27 return (uint16_t)((uint16_t)p[0] << 8 | p[1]);
34uint8_t pc_3964r_bcc(
const uint8_t *data,
size_t len)
37 for (
size_t i = 0; i < len; i++)
44size_t pc_3964r_build_block(uint8_t *buf,
size_t cap,
const uint8_t *data,
size_t len,
bool with_bcc)
46 if (!buf || (!data && len))
51 for (
size_t i = 0; i < len; i++)
58 if (data[i] == SIMATIC_DLE)
64 buf[o++] = SIMATIC_DLE;
71 buf[o++] = SIMATIC_DLE;
72 buf[o++] = SIMATIC_ETX;
79 buf[o] = pc_3964r_bcc(buf, o);
86static bool put_byte(uint8_t *out,
size_t out_cap,
size_t *oo, uint8_t b)
98static bool bcc_ok(
const uint8_t *buf,
size_t i,
size_t len,
bool with_bcc)
108 return pc_3964r_bcc(buf, i) == buf[i];
111bool pc_3964r_parse_block(
const uint8_t *buf,
size_t len,
bool with_bcc, uint8_t *out,
size_t out_cap,
size_t *out_len)
113 if (!buf || !out || !out_len)
122 if (b != SIMATIC_DLE)
124 if (!put_byte(out, out_cap, &oo, b))
135 uint8_t n = buf[i + 1];
136 if (n == SIMATIC_DLE)
138 if (!put_byte(out, out_cap, &oo, SIMATIC_DLE))
145 if (n != SIMATIC_ETX)
150 if (!bcc_ok(buf, i, len, with_bcc))
164static inline void emit(Simatic3964Ctx *ctx, uint8_t b)
168 ctx->tx(ctx->user, b);
172static void send_stx_await_conn(Simatic3964Ctx *ctx, uint32_t now_ms)
174 emit(ctx, SIMATIC_STX);
175 ctx->state = Simatic3964State::TX_AWAIT_CONN;
179static void send_block(Simatic3964Ctx *ctx, uint32_t now_ms)
181 for (
size_t i = 0; i < ctx->txlen; i++)
183 emit(ctx, ctx->txbuf[i]);
185 ctx->state = Simatic3964State::TX_AWAIT_END;
189static void begin_receive(Simatic3964Ctx *ctx, uint32_t now_ms)
191 emit(ctx, SIMATIC_DLE);
192 ctx->state = Simatic3964State::RX_COLLECT;
194 ctx->prev_dle =
false;
195 ctx->await_bcc =
false;
199void pc_3964r_init(Simatic3964Ctx *ctx,
bool high_priority,
bool with_bcc, Simatic3964TxFn tx, Simatic3964RxFn rx,
202 memset(ctx, 0,
sizeof(*ctx));
203 ctx->state = Simatic3964State::IDLE;
204 ctx->high_priority = high_priority;
205 ctx->with_bcc = with_bcc;
211bool pc_3964r_send(Simatic3964Ctx *ctx,
const uint8_t *data,
size_t len, uint32_t now_ms)
213 if (ctx->state != Simatic3964State::IDLE)
217 size_t n = pc_3964r_build_block(ctx->txbuf,
sizeof(ctx->txbuf), data, len, ctx->with_bcc);
223 ctx->block_retries = 0;
224 ctx->conn_retries = 0;
225 send_stx_await_conn(ctx, now_ms);
229static void deliver_or_nak(Simatic3964Ctx *ctx)
233 if (pc_3964r_parse_block(ctx->rxbuf, ctx->rxpos, ctx->with_bcc, out,
sizeof(out), &olen))
235 emit(ctx, SIMATIC_DLE);
238 ctx->state = Simatic3964State::IDLE;
241 ctx->rx(ctx->user, out, olen);
246 emit(ctx, SIMATIC_NAK);
247 ctx->state = Simatic3964State::IDLE;
251static void rx_collect_byte(Simatic3964Ctx *ctx, uint8_t b, uint32_t now_ms)
253 if (ctx->rxpos >=
sizeof(ctx->rxbuf))
255 emit(ctx, SIMATIC_NAK);
256 ctx->state = Simatic3964State::IDLE;
259 ctx->rxbuf[ctx->rxpos++] = b;
269 ctx->prev_dle =
false;
270 if (b == SIMATIC_DLE)
274 if (b == SIMATIC_ETX)
278 ctx->await_bcc =
true;
287 emit(ctx, SIMATIC_NAK);
288 ctx->state = Simatic3964State::IDLE;
291 if (b == SIMATIC_DLE)
293 ctx->prev_dle =
true;
297void pc_3964r_rx_byte(Simatic3964Ctx *ctx, uint8_t b, uint32_t now_ms)
301 case Simatic3964State::IDLE:
302 if (b == SIMATIC_STX)
304 begin_receive(ctx, now_ms);
307 case Simatic3964State::TX_AWAIT_CONN:
308 if (b == SIMATIC_DLE)
310 send_block(ctx, now_ms);
312 else if (b == SIMATIC_STX)
314 if (!ctx->high_priority)
316 begin_receive(ctx, now_ms);
320 else if (b == SIMATIC_NAK)
322 if (++ctx->conn_retries < SIMATIC_MAX_CONN_RETRY)
324 send_stx_await_conn(ctx, now_ms);
328 ctx->state = Simatic3964State::IDLE;
332 case Simatic3964State::TX_AWAIT_END:
333 if (b == SIMATIC_DLE)
335 ctx->state = Simatic3964State::IDLE;
337 else if (b == SIMATIC_NAK)
339 if (++ctx->block_retries < SIMATIC_MAX_BLOCK_RETRY)
341 send_stx_await_conn(ctx, now_ms);
345 ctx->state = Simatic3964State::IDLE;
349 case Simatic3964State::RX_COLLECT:
350 rx_collect_byte(ctx, b, now_ms);
355void pc_3964r_tick(Simatic3964Ctx *ctx, uint32_t now_ms)
357 if (ctx->state == Simatic3964State::IDLE)
361 if ((int32_t)(now_ms - ctx->deadline_ms) < 0)
367 case Simatic3964State::TX_AWAIT_CONN:
368 if (++ctx->conn_retries < SIMATIC_MAX_CONN_RETRY)
370 send_stx_await_conn(ctx, now_ms);
374 ctx->state = Simatic3964State::IDLE;
377 case Simatic3964State::TX_AWAIT_END:
378 if (++ctx->block_retries < SIMATIC_MAX_BLOCK_RETRY)
380 send_stx_await_conn(ctx, now_ms);
384 ctx->state = Simatic3964State::IDLE;
387 case Simatic3964State::RX_COLLECT:
388 emit(ctx, SIMATIC_NAK);
389 ctx->state = Simatic3964State::IDLE;
396bool pc_3964r_idle(
const Simatic3964Ctx *ctx)
398 return ctx->state == Simatic3964State::IDLE;
407#define RK512_HDR_LEN 8
409size_t pc_rk512_build_send(uint8_t *buf,
size_t cap, Rk512Area area, uint8_t dbnr, uint16_t addr,
const uint16_t *words,
412 if (!buf || (!words && wcount))
416 size_t need = RK512_HDR_LEN + (size_t)wcount * 2;
421 buf[0] = (uint8_t)Rk512Cmd::SEND;
423 buf[2] = (uint8_t)area;
425 wr_u16(buf + 4, addr);
426 wr_u16(buf + 6, wcount);
427 for (uint16_t i = 0; i < wcount; i++)
429 wr_u16(buf + RK512_HDR_LEN + (
size_t)i * 2, words[i]);
434size_t pc_rk512_build_fetch(uint8_t *buf,
size_t cap, Rk512Area area, uint8_t dbnr, uint16_t addr, uint16_t wcount)
436 if (!buf || cap < RK512_HDR_LEN)
440 buf[0] = (uint8_t)Rk512Cmd::FETCH;
442 buf[2] = (uint8_t)area;
444 wr_u16(buf + 4, addr);
445 wr_u16(buf + 6, wcount);
446 return RK512_HDR_LEN;
450size_t pc_rk512_build_reaction(uint8_t *buf,
size_t cap, uint16_t status)
456 buf[0] = (uint8_t)Rk512Cmd::REACTION;
457 wr_u16(buf + 1, status);
461static bool area_valid(uint8_t a)
463 return a >= (uint8_t)Rk512Area::DB && a <= (uint8_t)Rk512Area::TB;
466bool pc_rk512_parse_header(
const uint8_t *buf,
size_t len, Rk512Header *out)
468 if (!buf || !out || len < RK512_HDR_LEN)
472 uint8_t cmd = buf[0];
473 if (cmd != (uint8_t)Rk512Cmd::SEND && cmd != (uint8_t)Rk512Cmd::FETCH)
477 if (!area_valid(buf[2]))
481 out->cmd = (Rk512Cmd)cmd;
482 out->area = (Rk512Area)buf[2];
484 out->addr = rd_u16(buf + 4);
485 out->count = rd_u16(buf + 6);
489bool pc_rk512_parse_reaction(
const uint8_t *buf,
size_t len, uint16_t *status,
const uint8_t **data,
size_t *dlen)
491 if (!buf || !status || len < 3)
495 if (buf[0] != (uint8_t)Rk512Cmd::REACTION)
499 *status = rd_u16(buf + 1);
502 *data = (len > 3) ? buf + 3 : nullptr;
#define PC_SIMATIC_ZVZ_MS
3964R ZVZ (Zeichenverzugszeit): inter-character timeout while receiving a block, ms.
#define PC_SIMATIC_QVZ_MS
3964R QVZ (Quittungsverzugszeit): handshake acknowledge-delay timeout, ms.
#define PC_SIMATIC_BLOCK_MAX
3964R block-body buffer size (built/received bytes: DLE-stuffed payload + DLE ETX + BCC).
Siemens SIMATIC serial point-to-point link (PC_ENABLE_SIMATIC) - the 3964R link protocol + the RK512 ...