Добавить идентификацию прошивки и чтение EEPROM Бальзам-167
This commit is contained in:
@@ -149,6 +149,14 @@ if(SETP_BUILD_TESTS)
|
||||
add_executable(test_periph28335 tests/test_periph28335.c)
|
||||
target_link_libraries(test_periph28335 PRIVATE setprotocol_static)
|
||||
add_test(NAME shared_periph28335 COMMAND test_periph28335)
|
||||
add_executable(test_periph28335_eeprom tests/test_periph28335_eeprom.c)
|
||||
target_link_libraries(test_periph28335_eeprom PRIVATE setprotocol_static)
|
||||
if(MSVC)
|
||||
target_compile_options(test_periph28335_eeprom PRIVATE /UNDEBUG)
|
||||
else()
|
||||
target_compile_options(test_periph28335_eeprom PRIVATE -UNDEBUG)
|
||||
endif()
|
||||
add_test(NAME periph28335_eeprom COMMAND test_periph28335_eeprom)
|
||||
add_executable(test_tms2812 tests/test_tms2812.c)
|
||||
target_link_libraries(test_tms2812 PRIVATE setprotocol_static)
|
||||
add_test(NAME shared_tms2812 COMMAND test_tms2812)
|
||||
|
||||
@@ -1,5 +1,11 @@
|
||||
# SETProtocol
|
||||
|
||||
`periph28335_eeprom_*` — read-only reader физической EEPROM Бальзам-167
|
||||
(AT25256B, 32 КиБ), со стабильным C ABI и портом
|
||||
`python/protocan/periph28335_eeprom.py`. Разметка платы задана в
|
||||
`include/balsam167_eeprom_layout.h`; PEEK, CRC и порядок байтов реализованы
|
||||
в `src/periph28335.c`. Проверка полного диапазона — `test_periph28335_eeprom`.
|
||||
|
||||
Универсальные сервисы эмуляторов: [адресное пространство, API и порт новой платы](docs/EMULATOR_SERVICES.md).
|
||||
`set_regmap` объединяет УМП, 2812, IGBT и DAC за общим API для RTU, CAN и других оболочек.
|
||||
|
||||
|
||||
17
c/set-protocol/include/balsam167_eeprom_layout.h
Normal file
17
c/set-protocol/include/balsam167_eeprom_layout.h
Normal file
@@ -0,0 +1,17 @@
|
||||
/* Board-specific layout shared by firmware, linker and host tools.
|
||||
* Fitted part: AT25256B, top marking 5EBL = 256 Kbit = 32768 OCTETS.
|
||||
* ATMLH516 is a manufacturer/date code, not a 512-Kbit density code.
|
||||
* Numeric defines intentionally have no C suffix: TI linker also includes this.
|
||||
*/
|
||||
#ifndef BALSAM167_EEPROM_LAYOUT_H
|
||||
#define BALSAM167_EEPROM_LAYOUT_H
|
||||
#define BALSAM167_EEPROM_BYTES 0x8000
|
||||
#define BALSAM167_EEPROM_PAGE_BYTES 64
|
||||
#define BALSAM167_EEPROM_PARAM_WORDS 129
|
||||
#define BALSAM167_EEPROM_WORDS (BALSAM167_EEPROM_BYTES / 2)
|
||||
/* Preserve the historical end-exclusive parameter boundary. The final word
|
||||
* remains unused; moving it would break existing saved configurations. */
|
||||
#define BALSAM167_EEPROM_PARAMS_END_WORD (BALSAM167_EEPROM_WORDS - 1)
|
||||
#define BALSAM167_EEPROM_PARAMS_START_WORD (BALSAM167_EEPROM_PARAMS_END_WORD - BALSAM167_EEPROM_PARAM_WORDS)
|
||||
#define BALSAM167_EEPROM_FIRMWARE_BYTES (BALSAM167_EEPROM_PARAMS_START_WORD * 2)
|
||||
#endif
|
||||
@@ -82,6 +82,15 @@ PCAN_ABI_API size_t pcan_abi_balsam_register_name(
|
||||
/* PM35/TMS320F28335 direct RS232/485 register terminal. */
|
||||
PCAN_ABI_API uint16_t pcan_abi_periph28335_crc16(
|
||||
const uint8_t *data, size_t size);
|
||||
/* Caller-owned EEPROM reader state: uint32_t[4], 16 bytes, see periph28335.h.
|
||||
* Exported independently of PM67: the EEPROM routing is specific to 167. */
|
||||
PCAN_ABI_API int pcan_abi_periph28335_eeprom_begin(void *state,
|
||||
uint32_t controller, uint32_t start, uint32_t length);
|
||||
PCAN_ABI_API size_t pcan_abi_periph28335_eeprom_request(const void *state,
|
||||
uint8_t *output, size_t capacity);
|
||||
PCAN_ABI_API int pcan_abi_periph28335_eeprom_accept(void *state,
|
||||
const uint8_t *reply, size_t size, uint8_t *output, size_t capacity);
|
||||
PCAN_ABI_API uint32_t pcan_abi_periph28335_eeprom_capacity(void);
|
||||
PCAN_ABI_API size_t pcan_abi_periph28335_append_crc(
|
||||
const uint8_t *payload, size_t payload_size,
|
||||
uint8_t *output, size_t output_size);
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include "balsam167_eeprom_layout.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
@@ -25,6 +26,30 @@ extern "C" {
|
||||
#define PERIPH28335_REQUEST_SIZE 8U
|
||||
#define PERIPH28335_COMMAND_COUNT 17U
|
||||
|
||||
/* Balsam-167 fitted AT25256B (marking 5EBL): 256 Kbit, NOT 256 Kbyte.
|
||||
* All reader positions below count physical EEPROM octets, not C28x words.
|
||||
* Four uint32_t fields make the caller-owned state stable for ctypes/JNI. */
|
||||
#define PERIPH28335_EEPROM_BYTES BALSAM167_EEPROM_BYTES
|
||||
typedef struct {
|
||||
uint32_t controller;
|
||||
uint32_t start;
|
||||
uint32_t length;
|
||||
uint32_t received;
|
||||
} periph28335_eeprom_reader;
|
||||
|
||||
/* No writes or capacity probes. Odd starts/lengths are allowed: the core reads
|
||||
* the containing 16-bit word and returns only the requested physical octets. */
|
||||
int periph28335_eeprom_begin(periph28335_eeprom_reader *reader,
|
||||
uint32_t controller, uint32_t start, uint32_t length);
|
||||
size_t periph28335_eeprom_request(const periph28335_eeprom_reader *reader,
|
||||
uint8_t *output, size_t capacity);
|
||||
/* Returns 1/2 decoded octets, or a negative status; errors do not advance state.
|
||||
* PEEK payload = little-endian data WORD + timer WORD, not four memory bytes.
|
||||
* SPI16 shifts the data word high-octet first, so physical EEPROM order is
|
||||
* response[3], response[2]. This differs from a C28x little-endian RAM dump. */
|
||||
int periph28335_eeprom_accept(periph28335_eeprom_reader *reader,
|
||||
const uint8_t *reply, size_t size, uint8_t *output, size_t capacity);
|
||||
|
||||
typedef enum {
|
||||
PERIPH28335_OK = 0,
|
||||
PERIPH28335_ERROR_ARGUMENT = -1,
|
||||
|
||||
@@ -17,6 +17,10 @@ extern "C" {
|
||||
#define UMP_BASE 0x1000U
|
||||
#define UMP_DATA 0x1010U
|
||||
#define UMP_LIVE 0x1100U
|
||||
/* Read-only running firmware identity, same 12-word v1 contract as Climate.
|
||||
* Kept outside logger control/data and the EEPROM-backed legacy parameters. */
|
||||
#define UMP_FIRMWARE_INFO 0x1200U
|
||||
#define UMP_FIRMWARE_INFO_WORDS 12U
|
||||
#define UMP_MARKER 0x1F00U
|
||||
#define UMP_MAX_WORDS 124U
|
||||
#define UMP_RTU_CAPACITY 253U
|
||||
|
||||
@@ -8,7 +8,17 @@
|
||||
extern unsigned int ump_log_read(unsigned int address);
|
||||
extern int ump_log_write(unsigned int address, unsigned int value);
|
||||
static uint16_t read_word(void *context, uint16_t address)
|
||||
{ (void)context; return (uint16_t)ump_log_read(address); }
|
||||
{
|
||||
(void)context;
|
||||
/* Optional board binding to the common firmware-info static port. Older
|
||||
* consumers still link without it and return contract=0 (unknown version).
|
||||
* This hook changes neither PM67 CAN nor the saved parameter layout. */
|
||||
#ifdef UMP_FIRMWARE_INFO_READ
|
||||
if (address >= UMP_FIRMWARE_INFO && address < UMP_FIRMWARE_INFO + UMP_FIRMWARE_INFO_WORDS)
|
||||
return UMP_FIRMWARE_INFO_READ(address - UMP_FIRMWARE_INFO);
|
||||
#endif
|
||||
return (uint16_t)ump_log_read(address);
|
||||
}
|
||||
static int write_word(void *context, uint16_t address, uint16_t value)
|
||||
{ (void)context; return ump_log_write(address,value); }
|
||||
unsigned ump_tms28335_prepare(unsigned fn, uint16_t a, uint16_t n,
|
||||
|
||||
@@ -101,6 +101,17 @@ uint16_t pcan_abi_periph28335_crc16(const uint8_t *data, size_t size)
|
||||
return periph28335_crc16_modbus(data, size);
|
||||
}
|
||||
|
||||
int pcan_abi_periph28335_eeprom_begin(void *state,
|
||||
uint32_t controller, uint32_t start, uint32_t length)
|
||||
{ return periph28335_eeprom_begin((periph28335_eeprom_reader *)state, controller, start, length); }
|
||||
size_t pcan_abi_periph28335_eeprom_request(const void *state, uint8_t *out, size_t cap)
|
||||
{ return periph28335_eeprom_request((const periph28335_eeprom_reader *)state, out, cap); }
|
||||
int pcan_abi_periph28335_eeprom_accept(void *state, const uint8_t *reply,
|
||||
size_t size, uint8_t *out, size_t cap)
|
||||
{ return periph28335_eeprom_accept((periph28335_eeprom_reader *)state, reply, size, out, cap); }
|
||||
uint32_t pcan_abi_periph28335_eeprom_capacity(void)
|
||||
{ return PERIPH28335_EEPROM_BYTES; }
|
||||
|
||||
size_t pcan_abi_periph28335_append_crc(
|
||||
const uint8_t *payload, size_t payload_size,
|
||||
uint8_t *output, size_t output_size)
|
||||
|
||||
@@ -10,6 +10,60 @@
|
||||
|
||||
#include "set_crc.h"
|
||||
|
||||
static int eeprom_reader_valid(const periph28335_eeprom_reader *r)
|
||||
{
|
||||
return r && r->controller >= 1U && r->controller <= 255U &&
|
||||
r->start < PERIPH28335_EEPROM_BYTES && r->length &&
|
||||
r->length <= PERIPH28335_EEPROM_BYTES - r->start && r->received < r->length;
|
||||
}
|
||||
|
||||
int periph28335_eeprom_begin(periph28335_eeprom_reader *r,
|
||||
uint32_t controller, uint32_t start, uint32_t length)
|
||||
{
|
||||
if (!r) return PERIPH28335_ERROR_ARGUMENT;
|
||||
/* Invalidate first: a rejected new range must not leave an old read active. */
|
||||
memset(r, 0, sizeof(*r));
|
||||
if (!controller || controller > 255U || start >= PERIPH28335_EEPROM_BYTES ||
|
||||
!length || length > PERIPH28335_EEPROM_BYTES - start)
|
||||
return PERIPH28335_ERROR_RANGE;
|
||||
r->controller = controller; r->start = start; r->length = length;
|
||||
return PERIPH28335_OK;
|
||||
}
|
||||
|
||||
size_t periph28335_eeprom_request(const periph28335_eeprom_reader *r,
|
||||
uint8_t *output, size_t capacity)
|
||||
{
|
||||
uint8_t payload[6];
|
||||
uint32_t word;
|
||||
if (!eeprom_reader_valid(r)) return 0;
|
||||
word = (r->start + r->received) / 2U;
|
||||
payload[0] = (uint8_t)r->controller; payload[1] = 56U; /* CMD_PEEK only */
|
||||
/* 167 routes words 0..0x3fff to SPI EEPROM. The old terminal's
|
||||
* 0x10000000 memory selector is NOT implemented by this firmware. */
|
||||
payload[2] = (uint8_t)word; payload[3] = (uint8_t)(word >> 8);
|
||||
payload[4] = 0; payload[5] = 0;
|
||||
return periph28335_append_crc(payload, sizeof(payload), output, capacity);
|
||||
}
|
||||
|
||||
int periph28335_eeprom_accept(periph28335_eeprom_reader *r,
|
||||
const uint8_t *reply, size_t size, uint8_t *output, size_t capacity)
|
||||
{
|
||||
unsigned odd, count;
|
||||
if (!eeprom_reader_valid(r) || !reply || !output) return PERIPH28335_ERROR_ARGUMENT;
|
||||
/* The UART layer may strip two legacy zero-padding bytes after CRC. */
|
||||
if (size != 8U && size != 10U) return PERIPH28335_ERROR_LENGTH;
|
||||
if (periph28335_crc16_modbus(reply, 8U) != 0) return PERIPH28335_ERROR_CRC;
|
||||
if (reply[0] != r->controller || reply[1] != 56U) return PERIPH28335_ERROR_HEADER;
|
||||
odd = (unsigned)((r->start + r->received) & 1U);
|
||||
count = 2U - odd;
|
||||
if (count > r->length - r->received) count = (unsigned)(r->length - r->received);
|
||||
if (capacity < count) return PERIPH28335_ERROR_CAPACITY;
|
||||
output[0] = reply[odd ? 2U : 3U];
|
||||
if (count == 2U) output[1] = reply[2];
|
||||
r->received += count;
|
||||
return (int)count;
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
const char *project;
|
||||
const char *commands[PERIPH28335_COMMAND_COUNT];
|
||||
|
||||
@@ -12,6 +12,11 @@ unsigned ump_validate(unsigned fn, uint16_t a, uint16_t n, int can)
|
||||
{
|
||||
if (fn == 6) return a >= UMP_BASE && a <= UMP_BASE+3 ? 0 : 2;
|
||||
if (fn != 3) return 1;
|
||||
/* CAN reads the whole immutable block (six fragments); RTU may read slices.
|
||||
* Check subtraction only after the lower bound, avoiding uint16 wraparound. */
|
||||
if (a >= UMP_FIRMWARE_INFO && a < UMP_FIRMWARE_INFO + UMP_FIRMWARE_INFO_WORDS)
|
||||
return can ? ((a == UMP_FIRMWARE_INFO && n == UMP_FIRMWARE_INFO_WORDS) ? 0 : 2)
|
||||
: ((n && n <= UMP_FIRMWARE_INFO + UMP_FIRMWARE_INFO_WORDS - a) ? 0 : 2);
|
||||
if (can) return ((a == UMP_BASE && n == 16) ||
|
||||
(a == UMP_DATA && (n == 40 || n == 80 || n == 120 || n == 42 || n == 84)) ||
|
||||
(a == UMP_LIVE && n == 124)) ? 0 : 2;
|
||||
|
||||
64
c/set-protocol/tests/test_periph28335_eeprom.c
Normal file
64
c/set-protocol/tests/test_periph28335_eeprom.c
Normal file
@@ -0,0 +1,64 @@
|
||||
#include "periph28335.h"
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
|
||||
static void response(uint8_t *out, unsigned high, unsigned low)
|
||||
{
|
||||
/* Reply is low data byte, high data byte, then an unrelated timer.
|
||||
* Nonzero timer detects accidentally copying four memory bytes per PEEK. */
|
||||
uint8_t payload[] = {16,56,0,0,0xef,0xbe};
|
||||
payload[2]=(uint8_t)low; payload[3]=(uint8_t)high;
|
||||
assert(periph28335_append_crc(payload,6,out,10)==8);
|
||||
out[8]=out[9]=0;
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
periph28335_eeprom_reader reader;
|
||||
uint8_t request[8], reply[10], data[2];
|
||||
unsigned i;
|
||||
assert(sizeof(reader)==16);
|
||||
assert(periph28335_eeprom_begin(&reader,16,0,0)==PERIPH28335_ERROR_RANGE);
|
||||
assert(periph28335_eeprom_begin(&reader,0,0,2)==PERIPH28335_ERROR_RANGE);
|
||||
assert(periph28335_eeprom_begin(&reader,16,0x8000,1)==PERIPH28335_ERROR_RANGE);
|
||||
assert(periph28335_eeprom_begin(&reader,16,0x7fff,2)==PERIPH28335_ERROR_RANGE);
|
||||
assert(periph28335_eeprom_begin(&reader,16,1,0xffffffffUL)==PERIPH28335_ERROR_RANGE);
|
||||
assert(periph28335_eeprom_request(&reader,request,8)==0);
|
||||
assert(periph28335_eeprom_begin(&reader,16,1,3)==0);
|
||||
assert(periph28335_eeprom_request(&reader,request,7)==0);
|
||||
assert(periph28335_eeprom_request(&reader,request,8)==8);
|
||||
assert(memcmp(request,"\x10\x38\0\0\0\0",6)==0);
|
||||
response(reply,0x12,0x34);
|
||||
assert(periph28335_eeprom_accept(&reader,reply,8,data,2)==1 && data[0]==0x34);
|
||||
assert(reader.received==1);
|
||||
assert(periph28335_eeprom_request(&reader,request,8)==8 && request[2]==1);
|
||||
reply[2]^=1;
|
||||
assert(periph28335_eeprom_accept(&reader,reply,8,data,2)==PERIPH28335_ERROR_CRC);
|
||||
assert(reader.received==1);
|
||||
response(reply,0x56,0x78);
|
||||
assert(periph28335_eeprom_accept(&reader,reply,8,data,1)==PERIPH28335_ERROR_CAPACITY);
|
||||
assert(reader.received==1);
|
||||
assert(periph28335_eeprom_accept(&reader,reply,10,data,2)==2);
|
||||
assert(data[0]==0x56 && data[1]==0x78 && reader.received==3);
|
||||
assert(periph28335_eeprom_request(&reader,request,8)==0);
|
||||
|
||||
/* Every requested word of a complete dump remains in the EEPROM window.
|
||||
* In particular, neither the RAM route nor selector 0x10000000 is used. */
|
||||
assert(periph28335_eeprom_begin(&reader,16,0,32768)==0);
|
||||
for (i=0;i<16384;i++) {
|
||||
assert(periph28335_eeprom_request(&reader,request,8)==8);
|
||||
assert(request[1]==56 && request[4]==0 && request[5]==0);
|
||||
assert(((unsigned)request[3]*256+request[2])==i);
|
||||
assert(periph28335_crc16_modbus(request,8)==0);
|
||||
response(reply,i>>8,i);
|
||||
assert(periph28335_eeprom_accept(&reader,reply,8,data,2)==2);
|
||||
assert(data[0]==(i>>8) && data[1]==(i&255));
|
||||
}
|
||||
assert(reader.received==32768 && periph28335_eeprom_request(&reader,request,8)==0);
|
||||
assert(periph28335_eeprom_begin(&reader,16,0x7fff,1)==0);
|
||||
assert(periph28335_eeprom_request(&reader,request,8)==8);
|
||||
assert(request[2]==0xff && request[3]==0x3f);
|
||||
response(reply,0x9a,0xbc);
|
||||
assert(periph28335_eeprom_accept(&reader,reply,8,data,2)==1 && data[0]==0xbc);
|
||||
return 0;
|
||||
}
|
||||
Reference in New Issue
Block a user