tool78 stuff
This commit is contained in:
parent
42e447f3a8
commit
1b1ed215eb
|
@ -0,0 +1,636 @@
|
|||
|
||||
#include <pico/time.h>
|
||||
#include <pico/timeout_helper.h>
|
||||
|
||||
#include "tool78_defs.h"
|
||||
#include "tool78_hw.h"
|
||||
#include "tool78_cmds.h"
|
||||
|
||||
static uint8_t databuf[255];
|
||||
|
||||
// variable name | /fRH=/8MHz us us
|
||||
// 78k RL78 | 78k0 78k0r rl78
|
||||
// -----------------+----------------------------------------------------------
|
||||
// tCOM tSDNx/tMB | 106 13.2 62 (good enough)
|
||||
// tSF | 215 X X
|
||||
// tWT0 tCS1 | 172 0 255
|
||||
// tWT1 (tSD8) |857883+128*44160 (1420100+128*281100) 212
|
||||
// tWT2 tCS3 |214714*TODO+128*44160 (3300+271600+128*275000) (248862+299307)
|
||||
// tWT3 tCS5 | 1506 0 1432
|
||||
// tWT4 tDS5 |893355/2 149900 (309870+488315)
|
||||
// tWT5 tSS5 |100407 1187500 (1732+58+(17403+29293)*128+(184+44))
|
||||
// tWT6 tCS2 | 686 0 351
|
||||
// tWT7 tDS2 | 12827 0 11981
|
||||
// tWT8 tCS4 | 55044 18000 (3805+168+(5035+1110)*128+(5827+318))
|
||||
// tWT9 (tCS8) | 1238 X 154
|
||||
// tWT10 tCS6 | X 379.2 4735
|
||||
// tWT11 tCS11 | 1233 0 111
|
||||
// tWT12 (tCS9) | 252 0 (146110+534723+(5035+1110)*128+(203+57))
|
||||
// tWT13 tCS7 | 975 0 168
|
||||
// tWT14 tDS7 |66005812/2 70.2 (277095+1075967)
|
||||
// tWT15 |66018156/2 1152.3 X
|
||||
// tWT16 tCS10 | 583 0 219
|
||||
// tFD1 tSD10 | 54368 0 (72+128*30720)
|
||||
// tFD2 tSD11 | 321 11.4 512
|
||||
// tFD3 tSD2 | 163 416.4 41
|
||||
// tFD3 tSD5 | 163 416.4 41
|
||||
// tFD4 tSD7 | 163 985.8 32
|
||||
// ~tCS8~ |
|
||||
// ~tSD8~ |
|
||||
// ~tCS9~ |
|
||||
|
||||
|
||||
static enum tool78_stat tool78_data_send(struct tool78_hw* hw,
|
||||
size_t len, const uint8_t* data, bool final_block) {
|
||||
if (len == 0) return tool78_stat_ack;
|
||||
if (len > 0x100) return tool78_stat_internal_error;
|
||||
len &= 0xff;
|
||||
|
||||
uint8_t pre[2], // soh, len
|
||||
post[2]; // cksum, etx
|
||||
pre[0] = tool78_frame_soh;
|
||||
pre[1] = len;
|
||||
post[0] = 0; // checksum
|
||||
post[1] = final_block ? tool78_frame_etx : tool78_frame_etb;
|
||||
|
||||
uint8_t r = 0;
|
||||
r = tool78_digest_checksum8(r, 1, &pre[1]); // len
|
||||
if (len) r = tool78_digest_checksum8(r, len, data);
|
||||
post[0] = r;
|
||||
|
||||
int rr = hw->send(2, pre, -1);
|
||||
if (rr != 3) return tool78_stat_internal_error;
|
||||
|
||||
if (len) {
|
||||
rr = hw->send(len, data, -1);
|
||||
if (rr != len) return tool78_stat_internal_error;
|
||||
}
|
||||
|
||||
rr = hw->send(2, post, -1);
|
||||
if (rr != 2) return tool78_stat_internal_error;
|
||||
|
||||
return tool78_stat_ack;
|
||||
}
|
||||
|
||||
static enum tool78_stat tool78_cmd_send(struct tool78_hw* hw, uint8_t cmd,
|
||||
uint8_t len, const uint8_t* data) {
|
||||
if (len == 0) return tool78_stat_ack;
|
||||
if (len > 0x100) return tool78_stat_internal_error;
|
||||
len &= 0xff;
|
||||
|
||||
busy_wait_us_32(tCOM); // TODO // aka tSN6(/tDN6?) (reset?) or tMB (others?)
|
||||
|
||||
uint8_t pre[3], // soh, len, cmd
|
||||
post[2]; // cksum, etx
|
||||
pre[0] = tool78_frame_soh;
|
||||
pre[1] = len + 1;
|
||||
pre[2] = cmd;
|
||||
post[0] = 0; // checksum
|
||||
post[1] = tool78_frame_etx;
|
||||
|
||||
uint8_t r = 0;
|
||||
r = tool78_digest_checksum8(r, 2, &pre[1]); // len and cmd
|
||||
if (len) r = tool78_digest_checksum8(r, len, data);
|
||||
post[0] = r;
|
||||
|
||||
int rr = hw->send(3, pre, -1);
|
||||
if (rr != 3) return tool78_stat_internal_error;
|
||||
|
||||
if (len) {
|
||||
rr = hw->send(len, data, -1);
|
||||
if (rr != len) return tool78_stat_internal_error;
|
||||
}
|
||||
|
||||
rr = hw->send(2, post, -1);
|
||||
if (rr != 2) return tool78_stat_internal_error;
|
||||
|
||||
return tool78_stat_ack;
|
||||
}
|
||||
|
||||
static enum tool78_stat tool78_data_recv(struct tool78_hw* hw, uint8_t* buf,
|
||||
int expected_len, int timeout_us_first) {
|
||||
uint8_t pre[2]; // stx, len
|
||||
uint8_t post[2]; // cksum, etx/etb
|
||||
|
||||
int rr = hw->recv(1, &pre[0], timeout_us_first);
|
||||
if (rr != 1) return tool78_stat_internal_error;
|
||||
if (hw->target == tool78k0_spi && pre[0] == tool78_stat_busy)
|
||||
return tool78_stat_busy;
|
||||
if (pre[0] != tool78_frame_soh) return tool78_stat_protocol_error;
|
||||
|
||||
rr = hw->recv(1, &pre[1], 100);
|
||||
if (rr != 1) return tool78_stat_internal_error;
|
||||
|
||||
uint8_t len = pre[1];
|
||||
if (!len) len = 256;
|
||||
|
||||
if (hw->target == tool78k0_spi && pre[1] == tool78_stat_busy)
|
||||
return tool78_stat_busy;
|
||||
if (expected_len >= 0 && len != expected_len)
|
||||
return tool78_stat_protocol_error;
|
||||
|
||||
rr = hw->recv(len, buf, 100*len);
|
||||
if (rr != len) return tool78_stat_internal_error;
|
||||
|
||||
rr = hw->recv(2, post, 200);
|
||||
if (rr != 2) return tool78_stat_internal_error;
|
||||
if (post[1] != tool78_frame_etx && post[1] != tool78_frame_etb)
|
||||
return tool78_stat_protocol_error;
|
||||
|
||||
uint8_t ck = tool78_calc_checksum(len, buf);
|
||||
if (ck != post[0])
|
||||
return tool78_stat_protocol_error;
|
||||
|
||||
return tool78_stat_ack;
|
||||
}
|
||||
|
||||
static enum tool78_stat tool78_wait_status(struct tool78_hw* hw, int l, int timeout_us) {
|
||||
if (hw->target == tool78k0_spi && timeout_us >= 0) {
|
||||
busy_wait_us_32(timeout_us);
|
||||
timeout_us *= 2;
|
||||
}
|
||||
|
||||
bool blockinf = timeout_us < 0;
|
||||
absolute_time_t at = make_timeout_time_us(timeout_us);
|
||||
timeout_state_t ts = {0};
|
||||
check_timeout_fn ct = NULL;
|
||||
if (!blockinf) ct = init_single_timeout_until(&ts, at);
|
||||
|
||||
for (int i = 0; (blockinf && i < 64) || !ct(&ts); ++i) {
|
||||
if (hw->target == tool78k0_spi) {
|
||||
// with SPI, a status request must be sent manually
|
||||
enum tool78_stat st = tool78_cmd_send(hw, tool78_cmd_status, 0, NULL);
|
||||
if (st != tool78_stat_ack) return st;
|
||||
|
||||
busy_wait_us_32(tSF); // TODO
|
||||
}
|
||||
|
||||
st = tool78_data_recv(hw, databuf, l, timeout_us);
|
||||
if (st == tool78_status_busy) goto cont;
|
||||
if (st != tool78_stat_ack) return st;
|
||||
|
||||
st = databuf[0];
|
||||
if (st == tool78_status_busy) goto cont;
|
||||
|
||||
return st;
|
||||
|
||||
cont:
|
||||
busy_wait_us_32(timeout_us/64);
|
||||
}
|
||||
|
||||
return tool78_stat_timeout_error;
|
||||
}
|
||||
|
||||
// ----
|
||||
|
||||
enum tool78_stat tool78_do_reset(struct tool78_hw* hw) {
|
||||
enum tool78_stat st = tool78_cmd_send(hw, tool78_cmd_reset, 0, NULL);
|
||||
if (st != tool78_stat_ack) return st;
|
||||
|
||||
st = tool78_wait_status(hw, 1, tWT0); // TODO // aka tCS1
|
||||
if (st != tool78_stat_ack) return st;
|
||||
|
||||
return st;
|
||||
}
|
||||
|
||||
enum tool78_stat tool78_do_baud_rate_set(struct tool78_hw* hw) {
|
||||
uint8_t d0x[5];
|
||||
int len = 0;
|
||||
enum tool78_stat st;
|
||||
|
||||
switch (hw->target & tool78_mcu_mask) {
|
||||
case tool78_mcu_78k0r:
|
||||
// on the 78K0R/Kx3-L, baudrate set also switches to a fixed 115200 baud
|
||||
// line, much like the 78K0/Kx2 interface
|
||||
d0x[0] = 0x00; // uC correction mode
|
||||
d0x[1] = 0x00; // fixed value
|
||||
d0x[2] = 0x0a; // 115.2k
|
||||
d0x[3] = 0x00; // noise filter off (0x01: noise filter on)
|
||||
d0x[4] = 0x00; // we're most likely in full-speed mode (Vdd in 2.7V..5.5V
|
||||
// range, needs to be 0x01 to also support down to 1.8V)
|
||||
|
||||
st = tool78_cmd_send(hw, tool78_cmd_baud_rate_set, 5, d0x);
|
||||
if (st != tool78_stat_ack) return st;
|
||||
|
||||
busy_wait_us_32(tWT10); // TODO
|
||||
|
||||
// now at 115.2kbaud, so let the physical layer switch to it
|
||||
hw->set_baudrate(115200);
|
||||
|
||||
st = tool78_do_reset(hw);
|
||||
break;
|
||||
case tool78_mcu_rl78:
|
||||
// TODO: don't hardcode baudrate? idk
|
||||
d0x[0] = 0x00; // 0=115.2k 1=250k 2=500k 3=1M
|
||||
d0x[1] = 33; // 3.3V, will switch to full-speed mode
|
||||
|
||||
st = tool78_cmd_send(hw, tool78_cmd_baud_rate_set, 5, d0x);
|
||||
if (st != tool78_stat_ack) return st;
|
||||
|
||||
st = tool78_wait_status(hw, 3, tWT10); // TODO // aka tCS6
|
||||
if (st != tool78_stat_ack) return st;
|
||||
|
||||
// now at 115.2kbaud, so let the physical layer switch to it
|
||||
hw->set_baudrate(115200);
|
||||
|
||||
// let's ignore the two data bytes for now
|
||||
|
||||
st = tool78_do_reset(hw);
|
||||
break;
|
||||
case tool78_mcu_78k0: default:
|
||||
return tool78_stat_bad_mcu_for_cmd;
|
||||
}
|
||||
|
||||
return st;
|
||||
}
|
||||
|
||||
enum tool78_stat tool78_do_osc_freq_set(struct tool78_hw* hw) {
|
||||
if ((hw->target & tool78_mcu_mask) != tool78_mcu_78k0)
|
||||
return tool78_stat_bad_mcu_for_cmd;
|
||||
|
||||
// OFS specifies the frequency of the EXTCLK signal (see 78k0_uart2_extclk)
|
||||
// otherwise kinda irrelevant, EXCEPT THAT IS ALSO SWITCHES THE BAUDRATE TO
|
||||
// A FIXED VALUE OF 115200 BAUD!
|
||||
// so for now we kinda hardcode it to the value for the EXTCLK frequency to
|
||||
// the one also used in 78k0_uart2_extclk.c, aka 8 MHz
|
||||
uint8_t d0x[4];
|
||||
// one-digit BCD, last element is an exponent
|
||||
// i.e. value is (d00*100 + d01*10 + d02) * 10^d03 in Hz
|
||||
d0x[0] = 8;
|
||||
d0x[1] = 0;
|
||||
d0x[2] = 0;
|
||||
d0x[3] = 4;
|
||||
|
||||
enum tool78_stat st = tool78_cmd_send(hw, tool78_cmd_osc_freq_set, 4, d0x);
|
||||
if (st != tool78_stat_ack) return st;
|
||||
|
||||
// now at 115.2kbaud, so let the physical layer switch to it
|
||||
if (hw->target != tool78k0_spi) hw->set_baudrate(115200);
|
||||
|
||||
st = tool78_wait_status(hw, 1, tWT9); // TODO
|
||||
if (st != tool78_stat_ack) return st;
|
||||
|
||||
return st;
|
||||
}
|
||||
|
||||
// -
|
||||
|
||||
enum tool78_stat tool78_do_chip_erase(struct tool78_hw* hw) {
|
||||
if ((hw->target & tool78_mcu_mask) == tool78_mcu_rl78)
|
||||
return tool78_stat_bad_mcu_for_cmd;
|
||||
|
||||
enum tool78_stat st = tool78_cmd_send(hw, tool78_cmd_chip_erase, 0, NULL);
|
||||
if (st != tool78_stat_ack) return st;
|
||||
|
||||
return tool78_wait_status(hw, 1, tWT1); // TODO
|
||||
}
|
||||
|
||||
enum tool78_stat tool78_do_block_erase(struct tool78_hw* hw, uint32_t start, uint32_t end) {
|
||||
bool isrl78 = (hw->target & tool78_mcu_mask) == tool78_mcu_rl78;
|
||||
|
||||
uint8_t data[6];
|
||||
// on the rl78, the end address is ignored/unused and it's little-endian
|
||||
if (isrl78) {
|
||||
data[0] = (start >> 0) & 0xff;
|
||||
data[1] = (start >> 8) & 0xff;
|
||||
data[2] = (start >> 16) & 0xff;
|
||||
} else {
|
||||
data[0] = (start >> 16) & 0xff;
|
||||
data[1] = (start >> 8) & 0xff;
|
||||
data[2] = (start >> 0) & 0xff;
|
||||
data[3] = (end >> 16) & 0xff;
|
||||
data[4] = (end >> 8) & 0xff;
|
||||
data[5] = (end >> 0) & 0xff;
|
||||
}
|
||||
|
||||
enum tool78_stat st = tool78_cmd_send(hw, tool78_cmd_block_erase,
|
||||
isrl78 ? 3 : 6, data);
|
||||
if (st != tool78_stat_ack) return st;
|
||||
|
||||
st = tool78_wait_status(hw, 1, tWT2); // TODO // aka tCS3
|
||||
if (st != tool78_stat_ack) return st;
|
||||
|
||||
//st = tool78_wait_status(hw, 1, tWTx); // TODO: ?
|
||||
return st;
|
||||
}
|
||||
|
||||
enum tool78_stat tool78_do_programming(struct tool78_hw* hw, uint32_t start,
|
||||
uint32_t end, const uint8_t* src) {
|
||||
bool isrl78 = (hw->target & tool78_mcu_mask) == tool78_mcu_rl78;
|
||||
|
||||
uint8_t data[6];
|
||||
if (isrl78) {
|
||||
data[0] = (start >> 0) & 0xff;
|
||||
data[1] = (start >> 8) & 0xff;
|
||||
data[2] = (start >> 16) & 0xff;
|
||||
data[3] = (end >> 0) & 0xff;
|
||||
data[4] = (end >> 8) & 0xff;
|
||||
data[5] = (end >> 16) & 0xff;
|
||||
} else {
|
||||
data[0] = (start >> 16) & 0xff;
|
||||
data[1] = (start >> 8) & 0xff;
|
||||
data[2] = (start >> 0) & 0xff;
|
||||
data[3] = (end >> 16) & 0xff;
|
||||
data[4] = (end >> 8) & 0xff;
|
||||
data[5] = (end >> 0) & 0xff;
|
||||
}
|
||||
|
||||
enum tool78_stat st = tool78_cmd_send(hw, tool78_cmd_programming, 6, data);
|
||||
if (st != tool78_stat_ack) return st;
|
||||
|
||||
st = tool78_wait_status(hw, 1, tWT3); // TODO // aka tCS5
|
||||
if (st != tool78_stat_ack) return st;
|
||||
|
||||
int nblocks = ((end - start + 255) / 256);
|
||||
for (size_t off = 0; off < end - start; off += 256) {
|
||||
busy_wait_us_32(tSD5); // TODO // aka tFD3
|
||||
|
||||
size_t todo = (end - start) - off;
|
||||
if (todo > 256) todo = 256;
|
||||
bool finalblk = off + 256 >= (end - start);
|
||||
st = tool78_data_send(hw, todo, &src[off], finalblk);
|
||||
if (st != tool78_stat_ack) return st;
|
||||
|
||||
st = tool78_wait_status(hw, 2, tWT4); // TODO // aka tDS5
|
||||
if (st != tool78_stat_ack) return st;
|
||||
st = databuf[1];
|
||||
if (st != tool78_stat_ack) return st;
|
||||
}
|
||||
|
||||
st = tool78_wait_status(hw, 1, isrl78 ? tSS5 : (tWT5 * nblocks)); // TODO
|
||||
if (st != tool78_stat_ack) return st;
|
||||
|
||||
return st;
|
||||
}
|
||||
|
||||
enum tool78_stat tool78_do_verify(struct tool78_hw* hw, uint32_t start,
|
||||
uint32_t end, const uint8_t* src) {
|
||||
bool isrl78 = (hw->target & tool78_mcu_mask) == tool78_mcu_rl78;
|
||||
|
||||
uint8_t data[6];
|
||||
if (isrl78) {
|
||||
data[0] = (start >> 0) & 0xff;
|
||||
data[1] = (start >> 8) & 0xff;
|
||||
data[2] = (start >> 16) & 0xff;
|
||||
data[3] = (end >> 0) & 0xff;
|
||||
data[4] = (end >> 8) & 0xff;
|
||||
data[5] = (end >> 16) & 0xff;
|
||||
} else {
|
||||
data[0] = (start >> 16) & 0xff;
|
||||
data[1] = (start >> 8) & 0xff;
|
||||
data[2] = (start >> 0) & 0xff;
|
||||
data[3] = (end >> 16) & 0xff;
|
||||
data[4] = (end >> 8) & 0xff;
|
||||
data[5] = (end >> 0) & 0xff;
|
||||
}
|
||||
|
||||
enum tool78_stat st = tool78_cmd_send(hw, tool78_cmd_verify, 6, data);
|
||||
if (st != tool78_stat_ack) return st;
|
||||
|
||||
st = tool78_wait_status(hw, 1, tWT6); // TODO // aka tCS2
|
||||
if (st != tool78_stat_ack) return st;
|
||||
|
||||
//int nblocks = ((end - start + 255) / 256);
|
||||
for (size_t off = 0; off < end - start; off += 256) {
|
||||
busy_wait_us_32(tSD2); // TODO // aka tFD3
|
||||
|
||||
size_t todo = (end - start) - off;
|
||||
if (todo > 256) todo = 256;
|
||||
bool finalblk = off + 256 >= (end - start);
|
||||
st = tool78_data_send(hw, todo, &src[off], finalblk);
|
||||
if (st != tool78_stat_ack) return st;
|
||||
|
||||
st = tool78_wait_status(hw, 2, tWT7); // TODO // aka tDS2
|
||||
if (st != tool78_stat_ack) return st;
|
||||
st = databuf[1];
|
||||
if (st != tool78_stat_ack) return st;
|
||||
}
|
||||
|
||||
return st;
|
||||
}
|
||||
|
||||
enum tool78_stat tool78_do_block_blank_check(struct tool78_hw* hw,
|
||||
uint32_t start, uint32_t end, bool check_flash_opt /* RL78 only */) {
|
||||
bool isrl78 = (hw->target & tool78_mcu_mask) == tool78_mcu_rl78;
|
||||
|
||||
uint8_t data[7];
|
||||
if (isrl78) {
|
||||
data[0] = (start >> 0) & 0xff;
|
||||
data[1] = (start >> 8) & 0xff;
|
||||
data[2] = (start >> 16) & 0xff;
|
||||
data[3] = (end >> 0) & 0xff;
|
||||
data[4] = (end >> 8) & 0xff;
|
||||
data[5] = (end >> 16) & 0xff;
|
||||
data[6] = check_flash_opt ? 1 : 0; // RL78 only
|
||||
} else {
|
||||
data[0] = (start >> 16) & 0xff;
|
||||
data[1] = (start >> 8) & 0xff;
|
||||
data[2] = (start >> 0) & 0xff;
|
||||
data[3] = (end >> 16) & 0xff;
|
||||
data[4] = (end >> 8) & 0xff;
|
||||
data[5] = (end >> 0) & 0xff;
|
||||
}
|
||||
|
||||
int nblocks = ((end - start + 255) / 256);
|
||||
enum tool78_stat st = tool78_cmd_send(hw, tool78_cmd_block_blank_check,
|
||||
isrl78 ? 7 : 6, data);
|
||||
if (st != tool78_stat_ack) return st;
|
||||
|
||||
st = tool78_wait_status(hw, 1, isrl78 ? tCS4 : (tWT8 * nblocks)); // TODO
|
||||
if (st != tool78_stat_ack) return st;
|
||||
|
||||
return st;
|
||||
}
|
||||
|
||||
enum tool78_stat tool78_do_silicon_signature(struct tool78_hw* hw,
|
||||
tool78_silicon_sig_t* sig_dest) {
|
||||
int nbyte = 0;
|
||||
switch (hw->target & tool78_mcu_mask) {
|
||||
case tool78_mcu_78k0: nbyte = 19; break;
|
||||
case tool78_mcu_78k0r: nbyte = 27; break;
|
||||
case tool78_mcu_rl78: nbyte = 22; break;
|
||||
default: // oops
|
||||
return tool78_stat_bad_cmu_for_cmd;
|
||||
}
|
||||
|
||||
memset(sig_Dest, 0, 27);
|
||||
enum tool78_stat st = tool78_cmd_send(hw, tool78_cmd_silicon_signature, 0, NULL);
|
||||
if (st != tool78_stat_ack) return st;
|
||||
|
||||
st = tool78_wait_status(hw, 1, tWT11); // TODO // aka tCS11
|
||||
if (st != tool78_stat_ack) return st;
|
||||
|
||||
st = tool78_data_recv(hw, sig_dest, nbyte, tFD2); // TODO // aka tSD11
|
||||
if (st != tool78_stat_ack) return st;
|
||||
|
||||
// TODO: parity check of data
|
||||
|
||||
return st;
|
||||
}
|
||||
|
||||
enum tool78_stat tool78_do_version_get(struct tool78_hw* hw, tool78_version_t* vout) {
|
||||
if ((hw->target & tool78_mcu_mask) == tool78_mcu_rl78)
|
||||
return tool78_stat_bad_mcu_for_cmd;
|
||||
|
||||
enum tool78_stat st = tool78_cmd_send(hw, tool78_cmd_version_get, 0, NULL);
|
||||
if (st != tool78_stat_ack) return st;
|
||||
|
||||
st = tool78_wait_status(hw, 1, tWT12); // TODO
|
||||
if (st != tool78_stat_ack) return st;
|
||||
|
||||
memset(vout, 0, 6);
|
||||
st = tool78_data_recv(hw, vout, 6, tFD2); // TODO
|
||||
if (st != tool78_stat_ack) return st;
|
||||
|
||||
return st;
|
||||
}
|
||||
|
||||
enum tool78_stat tool78_do_checksum(struct tool78_hw* hw, uint32_t start,
|
||||
uint32_t end, uint16_t* ckout) {
|
||||
bool isrl78 = (hw->target & tool78_mcu_mask) == tool78_mcu_rl78;
|
||||
|
||||
uint8_t data[6];
|
||||
if (isrl78) {
|
||||
data[0] = (start >> 0) & 0xff;
|
||||
data[1] = (start >> 8) & 0xff;
|
||||
data[2] = (start >> 16) & 0xff;
|
||||
data[3] = (end >> 0) & 0xff;
|
||||
data[4] = (end >> 8) & 0xff;
|
||||
data[5] = (end >> 16) & 0xff;
|
||||
} else {
|
||||
data[0] = (start >> 16) & 0xff;
|
||||
data[1] = (start >> 8) & 0xff;
|
||||
data[2] = (start >> 0) & 0xff;
|
||||
data[3] = (end >> 16) & 0xff;
|
||||
data[4] = (end >> 8) & 0xff;
|
||||
data[5] = (end >> 0) & 0xff;
|
||||
}
|
||||
|
||||
enum tool78_stat st = tool78_cmd_send(hw, tool78_cmd_checksum, 6, data);
|
||||
if (st != tool78_stat_ack) return st;
|
||||
|
||||
st = tool78_wait_status(hw, 1, tWT16); // TODO // aka tCS10
|
||||
if (st != tool78_stat_ack) return st;
|
||||
|
||||
*ckout = 0;
|
||||
st = tool78_data_recv(hw, ckout, 2, tFD1); // TODO // aka tSD10
|
||||
if (st != tool78_stat_ack) return st;
|
||||
|
||||
return st;
|
||||
}
|
||||
|
||||
enum tool78_stat tool78_do_security_set(struct tool78_hw* hw,
|
||||
const struct tool78_security* sec) {
|
||||
uint8_t data[8];
|
||||
|
||||
// 78k0/78k0r
|
||||
data[1] = data[0] = 0;
|
||||
bool isrl78 = (hw->target & tool78_mcu_mask) == tool78_mcu_rl78;
|
||||
enum tool78_stat st = tool78_cmd_send(hw, tool78_cmd_security_set,
|
||||
isrl78 ? 0 : 2, data);
|
||||
if (st != tool78_stat_ack) return st;
|
||||
|
||||
st = tool78_wait_status(hw, 1, tWT13); // TODO // aka tCS7
|
||||
if (st != tool78_stat_ack) return st;
|
||||
|
||||
busy_wait_us_32(tSD7); // TODO // aka tFD3
|
||||
switch (hw->target & tool78_mcu_mask) {
|
||||
case tool78_mcu_78k0:
|
||||
data[0] = sec->flg;
|
||||
data[1] = 0x03; // 78k0: fixed value
|
||||
st = tool78_data_send(hw, 2, data, true);
|
||||
break;
|
||||
case tool78_mcu_78k0r:
|
||||
case tool78_mcu_rl78:
|
||||
data[0] = sec->flg;
|
||||
data[1] = isrl78 ? sec->bot : 0x03; // 78k0r: fixed value
|
||||
// yes they switched around the endianness. sigh.
|
||||
if (isrl78) {
|
||||
data[2] = (sec->fsws >> 0) & 0xff;
|
||||
data[3] = (sec->fsws >> 8) & 0xff;
|
||||
data[4] = (sec->fswe >> 0) & 0xff;
|
||||
data[5] = (sec->fswe >> 8) & 0xff;
|
||||
} else {
|
||||
data[2] = (sec->fsws >> 8) & 0xff;
|
||||
data[3] = (sec->fsws >> 0) & 0xff;
|
||||
data[4] = (sec->fswe >> 8) & 0xff;
|
||||
data[5] = (sec->fswe >> 0) & 0xff;
|
||||
}
|
||||
data[7] = data[6] = 0xff; // fixed/reserved
|
||||
st = tool78_data_send(hw, 8, data, true);
|
||||
break;
|
||||
default: st = tool78_stat_bad_mcu_for_cmd;
|
||||
}
|
||||
if (st != tool78_stat_ack) return st;
|
||||
|
||||
st = tool78_wait_status(hw, 1, tWT14); // TODO // aka tDS7
|
||||
if (st != tool78_stat_ack) return st;
|
||||
|
||||
if (!srl78) { // yeah...
|
||||
st = tool78_wait_status(hw, 1, tWT15); // TODO
|
||||
if (st != tool78_stat_ack) return st;
|
||||
}
|
||||
|
||||
return st;
|
||||
}
|
||||
|
||||
enum tool78_stat tool78_do_security_get(struct tool78_hw* hw,
|
||||
struct tool78_security* sec) {
|
||||
if ((hw->target & tool78_mcu_mask) != tool78_mcu_rl78)
|
||||
return tool78_stat_bad_mcu_for_cmd;
|
||||
|
||||
uint8_t data[8];
|
||||
|
||||
enum tool78_stat st = tool78_cmd_send(hw, tool78_cmd_security_get, 0, NULL);
|
||||
if (st != tool78_stat_ack) return st;
|
||||
|
||||
st = tool78_wait_status(hw, 1, tCS8);
|
||||
if (st != tool78_stat_ack) return st;
|
||||
|
||||
st = tool78_data_recv(hw, data, 8, tSD8);
|
||||
if (st != tool78_stat_ack) return st;
|
||||
|
||||
sec->flg = data[0];
|
||||
sec->bot = data[1];
|
||||
sec->fsws = data[2] | ((uint16_t)data[3]<<8);
|
||||
sec->fswe = data[4] | ((uint16_t)data[5]<<8);
|
||||
|
||||
return st
|
||||
}
|
||||
|
||||
enum tool78_stat tool78_do_security_release(struct tool78_hw* hw) {
|
||||
// basically does the same as chip erase... oh well
|
||||
if ((hw->target & tool78_mcu_mask) != tool78_mcu_rl78)
|
||||
return tool78_stat_bad_mcu_for_cmd;
|
||||
|
||||
enum tool78_stat st = tool78_cmd_send(hw, tool78_cmd_security_release, 0, NULL);
|
||||
if (st != tool78_stat_ack) return st;
|
||||
|
||||
st = tool78_wait_status(hw, 1, tCS9); // TODO
|
||||
if (st != tool78_stat_ack) return st;
|
||||
|
||||
// TODO: needs a modeset/re-entry sequence now?
|
||||
|
||||
return st;
|
||||
}
|
||||
|
||||
// ----
|
||||
|
||||
enum tool78_stat tool78_init(struct tool78_hw* hw) {
|
||||
if (!hw) return tool78_stat_internal_error;
|
||||
|
||||
for (size_t i = 0; i < 16; ++i) {
|
||||
if (!hw->init()) return tool78_stat_fatal_hw_error;
|
||||
|
||||
enum tool78_stat st = tool78_do_reset(hw);
|
||||
if (st == tool78_stat_timeout_error) {
|
||||
hw->deinit();
|
||||
continue;
|
||||
}
|
||||
return st;
|
||||
}
|
||||
|
||||
// TODO: baudrate set/oscfreq set (if not 78k0 SPI)
|
||||
// TODO: silicon sig get (not reqd if 78k0 but oh well)
|
||||
}
|
||||
|
|
@ -1,3 +1,46 @@
|
|||
|
||||
// big TODO (implement separate commands & handle MCU differences)
|
||||
#ifndef TOOL78_CMDS_H_
|
||||
#define TOOL78_CMDS_H_
|
||||
|
||||
#include "tool78_defs.h"
|
||||
#include "tool78_hw.h"
|
||||
|
||||
enum tool78_stat tool78_do_reset(struct tool78_hw* hw);
|
||||
|
||||
enum tool78_stat tool78_do_baud_rate_set(struct tool78_hw* hw);
|
||||
enum tool78_stat tool78_do_osc_freq_set(struct tool78_hw* hw);
|
||||
|
||||
static inline enum tool78_stat tool78_do_generic_baudrate(struct tool78_hw* hw) {
|
||||
if ((hw->target & tool78_mcu_mask) == tool78_mcu_78k0)
|
||||
return tool78_do_osc_freq_set(hw);
|
||||
else
|
||||
return tool78_do_baud_rate_set(hw);
|
||||
}
|
||||
|
||||
enum tool78_stat tool78_do_chip_erase(struct tool78_hw* hw);
|
||||
enum tool78_stat tool78_do_block_erase(struct tool78_hw* hw, uint32_t start, uint32_t end);
|
||||
|
||||
enum tool78_stat tool78_do_programming(struct tool78_hw* hw, uint32_t start,
|
||||
uint32_t end, const uint8_t* src);
|
||||
enum tool78_stat tool78_do_verify(struct tool78_hw* hw, uint32_t start,
|
||||
uint32_t end, const uint8_t* src);
|
||||
enum tool78_stat tool78_do_block_blank_check(struct tool78_hw* hw,
|
||||
uint32_t start, uint32_t end, bool check_flash_opt /* RL78 only */);
|
||||
enum tool78_stat tool78_do_checksum(struct tool78_hw* hw, uint32_t start,
|
||||
uint32_t end, uint16_t* ckout);
|
||||
|
||||
enum tool78_stat tool78_do_silicon_signature(struct tool78_hw* hw,
|
||||
tool78_silicon_sig_t* sig_dest);
|
||||
enum tool78_stat tool78_do_version_get(struct tool78_hw* hw, tool78_version_t* vout);
|
||||
enum tool78_stat tool78_do_security_set(struct tool78_hw* hw,
|
||||
const struct tool78_security* sec);
|
||||
enum tool78_stat tool78_do_security_get(struct tool78_hw* hw,
|
||||
struct tool78_security* sec);
|
||||
enum tool78_stat tool78_do_security_release(struct tool78_hw* hw);
|
||||
|
||||
// ----
|
||||
|
||||
enum tool78_stat tool78_init(struct tool78_hw* hw);
|
||||
|
||||
#endif
|
||||
|
||||
|
|
|
@ -84,6 +84,8 @@ enum tool78_cmd {
|
|||
tool78_cmd_security_set = 0xa0,
|
||||
// RL78 only
|
||||
tool78_cmd_security_get = 0xa1,
|
||||
// RL78 only
|
||||
tool78_cmd_security_release = 0xa2,
|
||||
tool78_cmd_checksum = 0xb0,
|
||||
// response format & length differ between all MCU versions!
|
||||
// length & first few bytes can be used to discern between MCUs
|
||||
|
@ -94,6 +96,8 @@ enum tool78_cmd {
|
|||
};
|
||||
|
||||
enum tool78_stat {
|
||||
tool78_stat_internal_error = 0x00, // error internal to this implementation code
|
||||
|
||||
tool78_stat_cmd_not_supported = 0x04,
|
||||
tool78_stat_parameter_error = 0x05,
|
||||
tool78_stat_ack = 0x06,
|
||||
|
@ -109,6 +113,11 @@ enum tool78_stat {
|
|||
tool78_stat_read_error = 0x20,
|
||||
// 78k0/Kx2 SPI only
|
||||
tool78_stat_busy = 0xff
|
||||
|
||||
tool78_stat_protocol_error = 0xc1, // low-level wire protocol violation
|
||||
tool78_stat_bad_mcu_for_cmd = 0xc2,
|
||||
tool78_stat_timeout_error = 0xc3,
|
||||
tool78_stat_fatal_hw_error = 0xc4,
|
||||
};
|
||||
|
||||
// TODO: only known for RL78 from fail0overflow. verify!
|
||||
|
@ -145,15 +154,87 @@ enum tool78_frame {
|
|||
tool78_frame_eot = 0x17,
|
||||
};
|
||||
|
||||
static inline uint8_t tool78_calc_checksum8(size_t len, const uint8_t* data) {
|
||||
uint8_t r = 0;
|
||||
static inline uint8_t tool78_digest_checksum8(uint8_t r, const uint8_t* data) {
|
||||
for (size_t i = 0; i < len; ++i) r = r - data[i];
|
||||
return r;
|
||||
}
|
||||
static inline uint8_t tool78_calc_checksum8(size_t len, const uint8_t* data) {
|
||||
return tool78_digest_checksum8(r, len, data);
|
||||
}
|
||||
static inline uint8_t tool78_calc_ocd_checksum8(size_t len, const uint8_t* data) {
|
||||
return ~tool78_calc_checksum8(len, data);
|
||||
}
|
||||
// TODO: 16bit checksum (cf. command B0 Checksum)
|
||||
|
||||
__attribute__((__packed__, __align__(1))) struct tool78_silicon_sig_78k0 {
|
||||
uint8_t ven; // vendor code. 0x10 == NEC
|
||||
uint8_t met; // macro extension (0x7f)
|
||||
uint8_t msc; // macro function code (0x04)
|
||||
uint8_t dec; // device extension code (0x07)
|
||||
uint8_t end[3]; // internal flash memory last address
|
||||
uint8_t dev[10]; // device name
|
||||
uint8_t scf; // security flag info
|
||||
uint8_t bot; // boot block number (0x03)
|
||||
// "for above fields except BOT, the lower 7 bits are used as data entity,
|
||||
// and the highest bit is used as an odd parity"
|
||||
};
|
||||
__attribute__((__packed__, __align__(1))) struct tool78_silicon_sig_78k0r {
|
||||
uint8_t ven; // vendor code. 0x10 == NEC
|
||||
uint8_t met; // macro extension (0x6f or 0x7f)
|
||||
uint8_t msc; // macro function code (0x04)
|
||||
uint8_t dec[3]; // device extension code
|
||||
uint8_t uae[3]; // internal flash memory last address
|
||||
uint8_t dev[10]; // device name
|
||||
uint8_t scf; // security flag info
|
||||
uint8_t bot; // boot block number (0x03)
|
||||
uint8_t fswsh; // flash shield window start hi
|
||||
uint8_t fswsl; // flash shield window start lo
|
||||
uint8_t fsweh; // flash shield window end hi
|
||||
uint8_t fswel; // flash shield window end lo
|
||||
uint8_t res[2]; // reserved
|
||||
// "for VEN, MET, MSC and DEC, the lower 7 bits are used as data entity,
|
||||
// and the highest bit is used as an odd parity."
|
||||
};
|
||||
__attribute__((__packed__, __align__(1))) struct tool78_silicon_sig_rl78 {
|
||||
uint8_t dec[3]; // device code
|
||||
uint8_t dev[10]; // device name
|
||||
uint8_t cen[3]; // last address of code flash rom, little-endian
|
||||
uint8_t den[3]; // last address of data flash rom, little-endian
|
||||
uint8_t ver[3]; // firmware version number
|
||||
// no parity bits
|
||||
};
|
||||
__attribute__((__packed__, __align__(1))) typedef union tool78_silicon_sig {
|
||||
struct tool78_silicon_sig_78k0 k780 ;
|
||||
struct tool78_silicon_sig_78k0r k780r;
|
||||
struct tool78_silicon_sig_rl78 rl78 ;
|
||||
} tool78_silicon_sig_t;
|
||||
|
||||
// same structure for 78k0 and 78k0r, not used in rl78
|
||||
__attribute__((__packed__, __align__(1))) typedef struct tool78_version {
|
||||
uint8_t dv[3]; // all zero
|
||||
uint8_t fw[3];
|
||||
} tool78_version_t;
|
||||
|
||||
enum tool78_sec_flag { // 78k0/78k0r
|
||||
tool78_sec_flag_boot_rw_allow = 1<<4,
|
||||
tool78_sec_flag_prog_allow = 1<<2,
|
||||
tool78_sec_flag_blk_erase_allow = 1<<1,
|
||||
// always 1 on RL78 as chip erase doesn't exist on it
|
||||
tool78_sec_flag_chip_erase_allow = 1<<0,
|
||||
|
||||
tool78_sec_flag_default_78k = 0xe8, // 0b11101000: all set to 1 by default
|
||||
tool78_sec_flag_default_rl78= 0xe9, // 0b11101000: all set to 1 by default
|
||||
|
||||
tool78_sec_flag_boot_xchg = 1<<0 // rl78 only
|
||||
};
|
||||
|
||||
struct tool78_security {
|
||||
uint8_t flg;
|
||||
uint8_t bot;
|
||||
// ^ only these 2 used for 78k0
|
||||
uint16_t fsws;
|
||||
uint16_t fswe;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
|
|
|
@ -16,6 +16,7 @@ struct tool78_hw {
|
|||
bool (*init)(void);
|
||||
void (*deinit)(void);
|
||||
|
||||
void (*set_baudrate)(uint32_t rate);
|
||||
// returns -1 if an rx overrun occurred
|
||||
int (*has_available)(void);
|
||||
|
||||
|
|
|
@ -93,12 +93,18 @@ static int t78k0_spi_send(int len, const uint8_t* data, int32_t timeout_us) {
|
|||
return t78k0_spi_xfer(len, data, NULL, timeout_us);
|
||||
}
|
||||
|
||||
static void t78k0_spi_set_baudrate(uint32_t baudrate) {
|
||||
float div = (float)clock_get_hz(clk_sys) / (1*baudrate);
|
||||
pio_sm_set_clkdiv(PINOUT_TOOL78_PIO, vars.smrx, div);
|
||||
}
|
||||
|
||||
struct tool78_hw tool78_hw_78k0_spi = {
|
||||
.target = tool78k0_spi,
|
||||
|
||||
.init = t78k0_spi_init,
|
||||
.deinit = t78k0_spi_deinit,
|
||||
|
||||
.set_baudrate = t78k0_spi_set_baudrate,
|
||||
.has_available = t78k0_spi_has_available,
|
||||
|
||||
.recv = t78k0_spi_recv,
|
||||
|
|
|
@ -71,12 +71,19 @@ static int t78k0_uart2_send(int len, const uint8_t* data, int32_t timeout_us) {
|
|||
len, data, timeout_us);
|
||||
}
|
||||
|
||||
static void t78k0_uart2_set_baudrate(uint32_t baudrate) {
|
||||
float div = (float)clock_get_hz(clk_sys) / (8*baudrate);
|
||||
pio_sm_set_clkdiv(PINOUT_TOOL78_PIO, vars.smrx, div);
|
||||
pio_sm_set_clkdiv(PINOUT_TOOL78_PIO, vars.smtx, div);
|
||||
}
|
||||
|
||||
struct tool78_hw tool78_hw_78k0_uart2 = {
|
||||
.target = tool78k0_uart2,
|
||||
|
||||
.init = t78k0_uart2_init,
|
||||
.deinit = t78k0_uart2_deinit,
|
||||
|
||||
.set_baudrate = t78k0_uart2_set_baudrate,
|
||||
.has_available = t78k0_uart2_has_available,
|
||||
|
||||
.recv = t78k0_uart2_recv,
|
||||
|
|
|
@ -79,6 +79,11 @@ static int t78k0_uart2_extclk_send(int len, const uint8_t* data, int32_t timeout
|
|||
len, data, timeout_us);
|
||||
}
|
||||
|
||||
static void t78k0_uart2_extclk_set_baudrate(uint32_t baudrate) {
|
||||
float div = (float)clock_get_hz(clk_sys) / (8*baudrate);
|
||||
pio_sm_set_clkdiv(PINOUT_TOOL78_PIO, vars.smrx, div);
|
||||
pio_sm_set_clkdiv(PINOUT_TOOL78_PIO, vars.smtx, div);
|
||||
}
|
||||
|
||||
struct tool78_hw tool78_hw_78k0_uart2_extclk = {
|
||||
.target = tool78k0_uart2_extclk,
|
||||
|
@ -86,6 +91,7 @@ struct tool78_hw tool78_hw_78k0_uart2_extclk = {
|
|||
.init = t78k0_uart2_extclk_init,
|
||||
.deinit = t78k0_uart2_extclk_deinit,
|
||||
|
||||
.set_baudrate = t78k0_uart2_extclk_set_baudrate,
|
||||
.has_available = t78k0_uart2_extclk_has_available,
|
||||
|
||||
.recv = t78k0_uart2_extclk_recv,
|
||||
|
|
|
@ -30,19 +30,20 @@ static bool t78k0r_uart1_init(void) {
|
|||
tool78_uart_rx_program_init(PINOUT_TOOL78_PIO, vars.smrx, vars.rxoff,
|
||||
PINOUT_TOOL78_78K0R_TOOL0, 9600, true);
|
||||
|
||||
// wait for 0x00 byte
|
||||
// wait for 0x00 "READY" byte
|
||||
uint8_t byte = 0xff;
|
||||
size_t s = t78k0r_uart1_recv(1, &byte, 50*1000);
|
||||
if (s == 0 || byte != 0x00) {
|
||||
t78k0r_uart1_deinit();
|
||||
return false;
|
||||
}
|
||||
busy_wait_us_32(120 + 20); // t01 or tCOM
|
||||
|
||||
busy_wait_us_32(120); // t01
|
||||
|
||||
byte = 0;
|
||||
t78k0r_uart1_send(1, &byte, -1);
|
||||
|
||||
// send two 0x00 "LOW" bytes
|
||||
uint8_t byte = 0x00;
|
||||
t78k0_uart2_send(1, &byte, -1);
|
||||
busy_wait_us_32(120); // t12
|
||||
t78k0_uart2_send(1, &byte, -1);
|
||||
busy_wait_us_32(610); // t2C
|
||||
|
||||
// now a reset command needs to be sent, but we leave that to the upper
|
||||
|
@ -69,12 +70,19 @@ static int t78k0r_uart1_send(int len, const uint8_t* data, int32_t timeout_us) {
|
|||
len, data, timeout_us);
|
||||
}
|
||||
|
||||
static void t78k0r_uart1_set_baudrate(uint32_t baudrate) {
|
||||
float div = (float)clock_get_hz(clk_sys) / (8*baudrate);
|
||||
pio_sm_set_clkdiv(PINOUT_TOOL78_PIO, vars.smrx, div);
|
||||
pio_sm_set_clkdiv(PINOUT_TOOL78_PIO, vars.smtx, div);
|
||||
}
|
||||
|
||||
struct tool78_hw tool78_hw_78k0r_uart1 = {
|
||||
.target = tool78rl_uart1,
|
||||
.target = tool78k0r_uart1,
|
||||
|
||||
.init = t78k0r_uart1_init,
|
||||
.deinit = t78k0r_uart1_deinit,
|
||||
|
||||
.set_baudrate = t78k0r_uart1_set_baudrate,
|
||||
.has_available = t78k0r_uart1_has_available,
|
||||
|
||||
.recv = t78k0r_uart1_recv,
|
||||
|
|
|
@ -59,12 +59,19 @@ static int trl78_uart1_send(int len, const uint8_t* data, int32_t timeout_us) {
|
|||
len, data, timeout_us);
|
||||
}
|
||||
|
||||
static void trl78_uart1_set_baudrate(uint32_t baudrate) {
|
||||
float div = (float)clock_get_hz(clk_sys) / (8*baudrate);
|
||||
pio_sm_set_clkdiv(PINOUT_TOOL78_PIO, vars.smrx, div);
|
||||
pio_sm_set_clkdiv(PINOUT_TOOL78_PIO, vars.smtx, div);
|
||||
}
|
||||
|
||||
struct tool78_hw tool78_hw_rl78_uart1 = {
|
||||
.target = tool78rl_uart1,
|
||||
|
||||
.init = trl78_uart1_init,
|
||||
.deinit = trl78_uart1_deinit,
|
||||
|
||||
.set_baudrate = trl78_uart1_set_baudrate,
|
||||
.has_available = trl78_uart1_has_available,
|
||||
|
||||
.recv = trl78_uart1_recv,
|
||||
|
|
|
@ -65,12 +65,19 @@ static int trl78_uart2_send(int len, const uint8_t* data, int32_t timeout_us) {
|
|||
len, data, timeout_us);
|
||||
}
|
||||
|
||||
static void trl78_uart2_set_baudrate(uint32_t baudrate) {
|
||||
float div = (float)clock_get_hz(clk_sys) / (8*baudrate);
|
||||
pio_sm_set_clkdiv(PINOUT_TOOL78_PIO, vars.smrx, div);
|
||||
pio_sm_set_clkdiv(PINOUT_TOOL78_PIO, vars.smtx, div);
|
||||
}
|
||||
|
||||
struct tool78_hw tool78_hw_rl78_uart2 = {
|
||||
.target = tool78rl_uart2,
|
||||
|
||||
.init = trl78_uart2_init,
|
||||
.deinit = trl78_uart2_deinit,
|
||||
|
||||
.set_baudrate = trl78_uart2_set_baudrate,
|
||||
.has_available = trl78_uart2_has_available,
|
||||
|
||||
.recv = trl78_uart2_recv,
|
||||
|
|
|
@ -54,12 +54,19 @@ static int test_send(int len, const uint8_t* data, int32_t timeout_us) {
|
|||
len, data, timeout_us);
|
||||
}
|
||||
|
||||
static void test_set_baudrate(uint32_t baudrate) {
|
||||
float div = (float)clock_get_hz(clk_sys) / (8*baudrate);
|
||||
pio_sm_set_clkdiv(PINOUT_TOOL78_PIO, vars.smrx, div);
|
||||
pio_sm_set_clkdiv(PINOUT_TOOL78_PIO, vars.smtx, div);
|
||||
}
|
||||
|
||||
struct tool78_hw tool78_hw_test_uart2 = {
|
||||
.target = tool78k0_uart2,
|
||||
|
||||
.init = test_init,
|
||||
.deinit = test_deinit,
|
||||
|
||||
.set_baudrate = test_set_baudrate,
|
||||
.has_available = test_has_available,
|
||||
|
||||
.recv = test_recv,
|
||||
|
|
Loading…
Reference in New Issue