Move PM35 protocol into shared C core

This commit is contained in:
2026-09-04 21:06:40 +03:00
parent 2316a5a26a
commit 6a82b309cc
16 changed files with 919 additions and 95 deletions

View File

@@ -18,6 +18,7 @@ set(SETPROTOCOL_V2_SOURCES
# Совместимые ProtoCAN/SETGUI v1 форматы переходного периода.
set(SETPROTOCOL_LEGACY_SOURCES
src/balsam_can.c
src/periph28335.c
src/gui_catalog.c
src/gui_frame.c
src/pcan_abi.c
@@ -98,6 +99,9 @@ if(SETP_BUILD_TESTS)
add_executable(test_balsam_can tests/test_balsam_can.c)
target_link_libraries(test_balsam_can PRIVATE setprotocol_static)
add_test(NAME legacy_balsam_can COMMAND test_balsam_can)
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_trends tests/test_trends.c)
target_link_libraries(test_trends PRIVATE setprotocol_static)
add_test(NAME shared_trends COMMAND test_trends)

View File

@@ -73,6 +73,34 @@ PCAN_ABI_API const char *pcan_abi_balsam_device_name(uint8_t device);
PCAN_ABI_API size_t pcan_abi_balsam_register_name(
uint8_t device, uint16_t address, char *output, size_t output_size);
/* PM35/TMS320F28335 direct RS232/485 register terminal. */
PCAN_ABI_API uint16_t pcan_abi_periph28335_crc16(
const uint8_t *data, size_t size);
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);
PCAN_ABI_API size_t pcan_abi_periph28335_build_read(
uint8_t controller, uint16_t start, uint16_t count,
uint8_t *output, size_t output_size);
PCAN_ABI_API size_t pcan_abi_periph28335_build_write(
uint8_t controller, uint16_t address, uint16_t value,
uint8_t *output, size_t output_size);
PCAN_ABI_API size_t pcan_abi_periph28335_build_command(
uint8_t controller, uint8_t command_index,
uint8_t *output, size_t output_size);
PCAN_ABI_API size_t pcan_abi_periph28335_expected_read_size(uint16_t count);
PCAN_ABI_API int pcan_abi_periph28335_decode_read(
const uint8_t *data, size_t size, uint8_t controller, uint16_t count,
uint16_t *output, size_t output_count);
PCAN_ABI_API int pcan_abi_periph28335_validate_write(
const uint8_t *response, size_t response_size,
const uint8_t *request, size_t request_size);
PCAN_ABI_API size_t pcan_abi_periph28335_project_count(void);
PCAN_ABI_API const char *pcan_abi_periph28335_project_name(
size_t project_index);
PCAN_ABI_API const char *pcan_abi_periph28335_command_name(
size_t project_index, size_t command_index);
PCAN_ABI_API size_t pcan_abi_frame_encode(uint8_t sequence, uint8_t flags,
uint32_t can_id,
const uint8_t *data, uint8_t dlc,

View File

@@ -0,0 +1,68 @@
/**
* @file periph28335.h
* @brief Shared PM35/TMS320F28335 register protocol.
*
* The protocol is independent from PM67/TMS320F2812 CAN and RS channels.
* It implements the direct RS232/485 register terminal used by PM35.
*/
#ifndef PERIPH28335_H
#define PERIPH28335_H
#include <stddef.h>
#include <stdint.h>
#ifdef __cplusplus
extern "C" {
#endif
#define PERIPH28335_REGISTER_COUNT 128U
#define PERIPH28335_REQUEST_SIZE 8U
#define PERIPH28335_COMMAND_COUNT 17U
typedef enum {
PERIPH28335_OK = 0,
PERIPH28335_ERROR_ARGUMENT = -1,
PERIPH28335_ERROR_RANGE = -2,
PERIPH28335_ERROR_LENGTH = -3,
PERIPH28335_ERROR_CRC = -4,
PERIPH28335_ERROR_HEADER = -5,
PERIPH28335_ERROR_CAPACITY = -6
} periph28335_status_t;
uint16_t periph28335_crc16_modbus(const uint8_t *data, size_t size);
size_t periph28335_append_crc(const uint8_t *payload, size_t payload_size,
uint8_t *output, size_t output_size);
size_t periph28335_build_read_registers(
uint8_t controller, uint16_t start, uint16_t count,
uint8_t *output, size_t output_size);
size_t periph28335_build_write_register(
uint8_t controller, uint16_t address, uint16_t value,
uint8_t *output, size_t output_size);
size_t periph28335_build_command(
uint8_t controller, uint8_t command_index,
uint8_t *output, size_t output_size);
size_t periph28335_expected_read_response_size(uint16_t count);
int periph28335_decode_read_response(
const uint8_t *data, size_t size, uint8_t controller, uint16_t count,
uint16_t *output, size_t output_count);
int periph28335_validate_write_response(
const uint8_t *response, size_t response_size,
const uint8_t *request, size_t request_size);
size_t periph28335_project_count(void);
const char *periph28335_project_name(size_t project_index);
const char *periph28335_command_name(size_t project_index,
size_t command_index);
#ifdef __cplusplus
}
#endif
#endif /* PERIPH28335_H */

View File

@@ -6,6 +6,7 @@
#define SETPROTOCOL_H
#include "balsam_can.h"
#include "periph28335.h"
/* Основной SET protocol v2. */
#include "set_protocol.h"

View File

@@ -13,6 +13,7 @@ LOCAL_SRC_FILES := \
../../src/set_trends.c \
../../src/set_spectrum.c \
../../src/balsam_can.c \
../../src/periph28335.c \
../../src/gui_catalog.c \
../../src/gui_frame.c \
../../src/pcan_abi.c \

View File

@@ -4,7 +4,10 @@ package ru.setcorp.setprotocol
object NativeSetProtocol {
val available: Boolean by lazy {
runCatching {
System.loadLibrary("setprotocol")
val hostLibrary = System.getProperty("setprotocol.library")
?: System.getProperty("setplot.library")
if (hostLibrary != null) System.load(hostLibrary)
else System.loadLibrary("setprotocol")
nativeAbiVersion() == 1
}.getOrDefault(false)
}
@@ -32,6 +35,17 @@ object NativeSetProtocol {
external fun nativeBalsamDecode(canId: Long, input: ByteArray): IntArray?
external fun nativeBalsamDeviceName(device: Int): String
external fun nativeBalsamRegisterName(device: Int, address: Int): String
external fun nativePeriph28335Crc16(input: ByteArray): Int
external fun nativePeriph28335AppendCrc(input: ByteArray): ByteArray?
external fun nativePeriph28335BuildRead(controller: Int, start: Int, count: Int): ByteArray?
external fun nativePeriph28335BuildWrite(controller: Int, address: Int, value: Int): ByteArray?
external fun nativePeriph28335BuildCommand(controller: Int, commandIndex: Int): ByteArray?
external fun nativePeriph28335ExpectedReadSize(count: Int): Int
external fun nativePeriph28335DecodeRead(input: ByteArray, controller: Int, count: Int): IntArray?
external fun nativePeriph28335ValidateWrite(response: ByteArray, request: ByteArray): Int
external fun nativePeriph28335ProjectCount(): Int
external fun nativePeriph28335ProjectName(projectIndex: Int): String?
external fun nativePeriph28335CommandName(projectIndex: Int, commandIndex: Int): String?
external fun nativeEncodeFrame(
sequence: Int,
flags: Int,

View File

@@ -1,37 +1,42 @@
package ru.setcorp.setprotocol.periph28335
/** Shared wire protocol migrated from Set_Terminal_28335/DTrans.pas and UNiiefa.pas. */
import ru.setcorp.setprotocol.NativeSetProtocol
/** Kotlin UI adapter; all PM35 wire logic and the command catalog live in C99. */
object Periph28335Protocol {
const val REGISTER_COUNT = 128
const val DEFAULT_CONTROLLER = 16
const val DEFAULT_BAUD_RATE = 115_200
val projectCommands: Map<String, List<String>> = linkedMapOf(
"По умолчанию" to listOf("Test", "Def", "Save", "Load", "Calibr", "Calcul", "Secret", "Light", "Raw", "-", "-", "-", "-", "-", "-", "Reset", "Nothing at all"),
"23470" to listOf("Test", "Def", "Save", "Load", "Calibr", "Read", "Secret", "-", "-", "-", "-", "-", "-", "-", "-", "Reset", "Nothing at all"),
"23550" to listOf("Test", "Def", "Save", "Load", "Calibr", "Read", "Secret", "Send", "-", "-", "-", "-", "-", "-", "-", "Reset", "Nothing at all"),
"23550.2" to listOf("Test", "Def", "Save", "Load", "Calibr", "Calcul", "Secret", "Send", "Raw", "Beep", "", "", "", "", "Log", "Reset", "Nothing at all"),
"ICE 22220.1-3" to listOf("Test", "Zero", "Save", "Def", "Calibr", "Read", "ExtLamp", "ExtLite", "-", "-", "-", "-", "-", "-", "-", "Reset", "Nothing at all"),
"ICE 22220.4-5" to listOf("Test", "Def", "Save", "Load", "Raw", "Read", "ExtLamp", "ExtLite", "No log", "-", "-", "-", "-", "-", "-", "Reset", "Nothing at all"),
"Бальзам 161" to listOf("Test", "Zero", "Save", "Def", "Calibr", "Clbr 400", "Stop", "Start", "Init", "Secret", "-", "-", "-", "-", "-", "Reset", "Nothing at all"),
"Бальзам 162" to listOf("Test", "Def", "Save", "Load", "Calibr", "Secret", "Stop", "Start", "Init", "Tune", "-", "-", "-", "-", "-", "Reset", "Nothing at all"),
"Бальзам 163" to listOf("Test", "Def", "Save", "Load", "Calibr", "Calcul", "Stop", "Start", "Init", "Tune", "Secret", "-", "-", "-", "-", "Reset", "Nothing at all"),
)
fun crc16Modbus(data: ByteArray, initial: Int = 0xFFFF): Int {
var crc = initial and 0xFFFF
data.forEach { value ->
crc = crc xor (value.toInt() and 0xFF)
repeat(8) {
crc = if (crc and 1 != 0) (crc ushr 1) xor 0xA001 else crc ushr 1
val projectCommands: Map<String, List<String>> by lazy {
requireNative()
buildMap {
repeat(NativeSetProtocol.nativePeriph28335ProjectCount()) { project ->
val name = requireNotNull(
NativeSetProtocol.nativePeriph28335ProjectName(project),
) { "Повреждён каталог проектов ПМ35" }
put(name, List(17) { command ->
requireNotNull(
NativeSetProtocol.nativePeriph28335CommandName(project, command),
) { "Повреждён каталог команд ПМ35" }
})
}
}
return crc and 0xFFFF
}
fun crc16Modbus(data: ByteArray, initial: Int = 0xFFFF): Int {
require(initial == 0xFFFF) {
"Произвольное начальное значение CRC не входит в протокол ПМ35"
}
requireNative()
return NativeSetProtocol.nativePeriph28335Crc16(data)
}
fun withCrc(payload: ByteArray): ByteArray {
val crc = crc16Modbus(payload)
return payload + byteArrayOf(crc.toByte(), (crc ushr 8).toByte())
requireNative()
return requireNotNull(NativeSetProtocol.nativePeriph28335AppendCrc(payload)) {
"SETProtocol отклонил данные ПМ35"
}
}
fun buildReadRegisters(controller: Int, start: Int, count: Int): ByteArray {
@@ -40,50 +45,51 @@ object Periph28335Protocol {
require(count in 1..REGISTER_COUNT && start + count <= REGISTER_COUNT) {
"Диапазон регистров должен находиться в 0..127"
}
return withCrc(byteArrayOf(
controller.toByte(), 3,
(start ushr 8).toByte(), start.toByte(),
(count ushr 8).toByte(), count.toByte(),
))
requireNative()
return requireNotNull(
NativeSetProtocol.nativePeriph28335BuildRead(controller, start, count),
) { "SETProtocol отклонил запрос чтения ПМ35" }
}
fun buildWriteRegister(controller: Int, address: Int, value: Int): ByteArray {
requireRange("Адрес контроллера", controller, 0xFF)
requireRange("Адрес регистра", address, REGISTER_COUNT - 1)
requireRange("Значение", value, 0xFFFF)
return withCrc(byteArrayOf(
controller.toByte(), 6,
(address ushr 8).toByte(), address.toByte(),
(value ushr 8).toByte(), value.toByte(),
))
requireNative()
return requireNotNull(
NativeSetProtocol.nativePeriph28335BuildWrite(controller, address, value),
) { "SETProtocol отклонил запрос записи ПМ35" }
}
fun buildCommand(controller: Int, commandIndex: Int): ByteArray {
require(commandIndex in 0..16) { "Номер команды должен быть в диапазоне 0..16" }
return buildWriteRegister(controller, 127, if (commandIndex < 16) 1 shl commandIndex else 0)
requireNative()
return requireNotNull(
NativeSetProtocol.nativePeriph28335BuildCommand(controller, commandIndex),
) { "SETProtocol отклонил команду ПМ35" }
}
fun expectedReadResponseSize(count: Int): Int {
require(count in 1..REGISTER_COUNT)
return count * 2 + 5
requireNative()
return NativeSetProtocol.nativePeriph28335ExpectedReadSize(count)
}
fun decodeReadResponse(data: ByteArray, controller: Int, count: Int): List<Int> {
val expected = expectedReadResponseSize(count)
require(data.size == expected) { "Ожидалось $expected байт, получено ${data.size}" }
validateCrc(data)
require(data[0].toInt() and 0xFF == controller) { "Ответ другого контроллера" }
require(data[1].toInt() and 0xFF == 3) { "Неверная функция ответа" }
require(data[2].toInt() and 0xFF == count * 2) { "Неверная длина данных ответа" }
return (0 until count).map { index ->
val offset = 3 + index * 2
((data[offset].toInt() and 0xFF) shl 8) or (data[offset + 1].toInt() and 0xFF)
}
requireNative()
return requireNotNull(
NativeSetProtocol.nativePeriph28335DecodeRead(data, controller, count),
) { "Повреждён ответ ПМ35: заголовок, длина или CRC" }.toList()
}
fun validateWriteResponse(data: ByteArray, request: ByteArray): Boolean =
data.size == 8 && request.size == 8 && data.contentEquals(request) && runCatching { validateCrc(data) }.isSuccess
fun validateWriteResponse(data: ByteArray, request: ByteArray): Boolean {
requireNative()
return NativeSetProtocol.nativePeriph28335ValidateWrite(data, request) == 0
}
// Presentation-only conversions stay in the GUI port; they do not define wire bytes.
fun bitsLsbFirst(value: Int): List<Boolean> {
requireRange("Значение", value, 0xFFFF)
return (0 until 16).map { bit -> value and (1 shl bit) != 0 }
@@ -91,7 +97,9 @@ object Periph28335Protocol {
fun wordFromBits(bits: List<Boolean>): Int {
require(bits.size == 16) { "Должно быть ровно 16 бит" }
return bits.foldIndexed(0) { bit, value, checked -> if (checked) value or (1 shl bit) else value }
return bits.foldIndexed(0) { bit, value, checked ->
if (checked) value or (1 shl bit) else value
}
}
fun signedWord(value: Int): Int {
@@ -99,11 +107,8 @@ object Periph28335Protocol {
return if (value < 0x8000) value else value - 0x10000
}
private fun validateCrc(data: ByteArray) {
require(data.size >= 2)
val expected = crc16Modbus(data.copyOf(data.size - 2))
val actual = (data[data.lastIndex - 1].toInt() and 0xFF) or ((data.last().toInt() and 0xFF) shl 8)
require(actual == expected) { "Ошибка CRC ответа" }
private fun requireNative() {
check(NativeSetProtocol.available) { "Нативное ядро SETProtocol недоступно" }
}
private fun requireRange(name: String, value: Int, maximum: Int) {

View File

@@ -7,6 +7,182 @@
#include "set_trends.h"
#include "set_spectrum.h"
#include "balsam_can.h"
#include "periph28335.h"
JNIEXPORT jint JNICALL
Java_ru_setcorp_setprotocol_NativeSetProtocol_nativePeriph28335Crc16(
JNIEnv *env, jobject self, jbyteArray input)
{
(void)self;
if (input == NULL) return 0;
jsize size = (*env)->GetArrayLength(env, input);
jbyte *data = (*env)->GetByteArrayElements(env, input, NULL);
if ((data == NULL) && (size != 0)) return 0;
uint16_t crc = periph28335_crc16_modbus((const uint8_t *)data, (size_t)size);
if (data != NULL) (*env)->ReleaseByteArrayElements(env, input, data, JNI_ABORT);
return (jint)crc;
}
JNIEXPORT jbyteArray JNICALL
Java_ru_setcorp_setprotocol_NativeSetProtocol_nativePeriph28335AppendCrc(
JNIEnv *env, jobject self, jbyteArray input)
{
(void)self;
if (input == NULL) return NULL;
jsize size = (*env)->GetArrayLength(env, input);
jbyte *data = (*env)->GetByteArrayElements(env, input, NULL);
if ((data == NULL) && (size != 0)) return NULL;
uint8_t *output = (uint8_t *)malloc((size_t)size + 2U);
if (output == NULL) {
if (data != NULL) (*env)->ReleaseByteArrayElements(env, input, data, JNI_ABORT);
return NULL;
}
size_t written = periph28335_append_crc(
(const uint8_t *)data, (size_t)size, output, (size_t)size + 2U);
if (data != NULL) (*env)->ReleaseByteArrayElements(env, input, data, JNI_ABORT);
if (written == 0U) { free(output); return NULL; }
jbyteArray result = (*env)->NewByteArray(env, (jsize)written);
if (result != NULL) (*env)->SetByteArrayRegion(
env, result, 0, (jsize)written, (const jbyte *)output);
free(output);
return result;
}
JNIEXPORT jbyteArray JNICALL
Java_ru_setcorp_setprotocol_NativeSetProtocol_nativePeriph28335BuildRead(
JNIEnv *env, jobject self, jint controller, jint start, jint count)
{
(void)self;
uint8_t output[PERIPH28335_REQUEST_SIZE];
if (controller < 0 || controller > 255 || start < 0 || start > 65535 ||
count < 0 || count > 65535) return NULL;
size_t written = periph28335_build_read_registers(
(uint8_t)controller, (uint16_t)start, (uint16_t)count,
output, sizeof output);
if (written == 0U) return NULL;
jbyteArray result = (*env)->NewByteArray(env, (jsize)written);
if (result != NULL) (*env)->SetByteArrayRegion(
env, result, 0, (jsize)written, (const jbyte *)output);
return result;
}
JNIEXPORT jbyteArray JNICALL
Java_ru_setcorp_setprotocol_NativeSetProtocol_nativePeriph28335BuildWrite(
JNIEnv *env, jobject self, jint controller, jint address, jint value)
{
(void)self;
uint8_t output[PERIPH28335_REQUEST_SIZE];
if (controller < 0 || controller > 255 || address < 0 || address > 65535 ||
value < 0 || value > 65535) return NULL;
size_t written = periph28335_build_write_register(
(uint8_t)controller, (uint16_t)address, (uint16_t)value,
output, sizeof output);
if (written == 0U) return NULL;
jbyteArray result = (*env)->NewByteArray(env, (jsize)written);
if (result != NULL) (*env)->SetByteArrayRegion(
env, result, 0, (jsize)written, (const jbyte *)output);
return result;
}
JNIEXPORT jbyteArray JNICALL
Java_ru_setcorp_setprotocol_NativeSetProtocol_nativePeriph28335BuildCommand(
JNIEnv *env, jobject self, jint controller, jint command_index)
{
(void)self;
uint8_t output[PERIPH28335_REQUEST_SIZE];
if (controller < 0 || controller > 255 || command_index < 0 || command_index > 255) return NULL;
size_t written = periph28335_build_command(
(uint8_t)controller, (uint8_t)command_index, output, sizeof output);
if (written == 0U) return NULL;
jbyteArray result = (*env)->NewByteArray(env, (jsize)written);
if (result != NULL) (*env)->SetByteArrayRegion(
env, result, 0, (jsize)written, (const jbyte *)output);
return result;
}
JNIEXPORT jint JNICALL
Java_ru_setcorp_setprotocol_NativeSetProtocol_nativePeriph28335ExpectedReadSize(
JNIEnv *env, jobject self, jint count)
{
(void)env; (void)self;
if (count < 0 || count > 65535) return 0;
return (jint)periph28335_expected_read_response_size((uint16_t)count);
}
JNIEXPORT jintArray JNICALL
Java_ru_setcorp_setprotocol_NativeSetProtocol_nativePeriph28335DecodeRead(
JNIEnv *env, jobject self, jbyteArray input, jint controller, jint count)
{
(void)self;
if (input == NULL || controller < 0 || controller > 255 ||
count < 1 || count > (jint)PERIPH28335_REGISTER_COUNT) return NULL;
jsize size = (*env)->GetArrayLength(env, input);
jbyte *data = (*env)->GetByteArrayElements(env, input, NULL);
if ((data == NULL) && (size != 0)) return NULL;
uint16_t words[PERIPH28335_REGISTER_COUNT];
int status = periph28335_decode_read_response(
(const uint8_t *)data, (size_t)size, (uint8_t)controller,
(uint16_t)count, words, PERIPH28335_REGISTER_COUNT);
if (data != NULL) (*env)->ReleaseByteArrayElements(env, input, data, JNI_ABORT);
if (status != PERIPH28335_OK) return NULL;
jint values[PERIPH28335_REGISTER_COUNT];
for (jint index = 0; index < count; ++index) values[index] = words[index];
jintArray result = (*env)->NewIntArray(env, count);
if (result != NULL) (*env)->SetIntArrayRegion(env, result, 0, count, values);
return result;
}
JNIEXPORT jint JNICALL
Java_ru_setcorp_setprotocol_NativeSetProtocol_nativePeriph28335ValidateWrite(
JNIEnv *env, jobject self, jbyteArray response, jbyteArray request)
{
(void)self;
if (response == NULL || request == NULL) return PERIPH28335_ERROR_ARGUMENT;
jsize response_size = (*env)->GetArrayLength(env, response);
jsize request_size = (*env)->GetArrayLength(env, request);
jbyte *response_data = (*env)->GetByteArrayElements(env, response, NULL);
jbyte *request_data = (*env)->GetByteArrayElements(env, request, NULL);
if (response_data == NULL || request_data == NULL) {
if (response_data != NULL) (*env)->ReleaseByteArrayElements(env, response, response_data, JNI_ABORT);
if (request_data != NULL) (*env)->ReleaseByteArrayElements(env, request, request_data, JNI_ABORT);
return PERIPH28335_ERROR_ARGUMENT;
}
int status = periph28335_validate_write_response(
(const uint8_t *)response_data, (size_t)response_size,
(const uint8_t *)request_data, (size_t)request_size);
(*env)->ReleaseByteArrayElements(env, response, response_data, JNI_ABORT);
(*env)->ReleaseByteArrayElements(env, request, request_data, JNI_ABORT);
return status;
}
JNIEXPORT jint JNICALL
Java_ru_setcorp_setprotocol_NativeSetProtocol_nativePeriph28335ProjectCount(
JNIEnv *env, jobject self)
{
(void)env; (void)self;
return (jint)periph28335_project_count();
}
JNIEXPORT jstring JNICALL
Java_ru_setcorp_setprotocol_NativeSetProtocol_nativePeriph28335ProjectName(
JNIEnv *env, jobject self, jint project_index)
{
(void)self;
const char *name = project_index >= 0
? periph28335_project_name((size_t)project_index) : NULL;
return name != NULL ? (*env)->NewStringUTF(env, name) : NULL;
}
JNIEXPORT jstring JNICALL
Java_ru_setcorp_setprotocol_NativeSetProtocol_nativePeriph28335CommandName(
JNIEnv *env, jobject self, jint project_index, jint command_index)
{
(void)self;
const char *name = project_index >= 0 && command_index >= 0
? periph28335_command_name((size_t)project_index, (size_t)command_index)
: NULL;
return name != NULL ? (*env)->NewStringUTF(env, name) : NULL;
}
JNIEXPORT jintArray JNICALL
Java_ru_setcorp_setprotocol_NativeSetProtocol_nativeBalsamDecode(

View File

@@ -4,6 +4,7 @@
#include "pcan_crc.h"
#include "balsam_can.h"
#include "periph28335.h"
#include "pcan_frame.h"
#include "pcan_id.h"
#include "gui_frame.h"
@@ -88,6 +89,79 @@ size_t pcan_abi_balsam_register_name(uint8_t device, uint16_t address,
return balsam_can_register_name(device, address, output, output_size);
}
uint16_t pcan_abi_periph28335_crc16(const uint8_t *data, size_t size)
{
return periph28335_crc16_modbus(data, size);
}
size_t pcan_abi_periph28335_append_crc(
const uint8_t *payload, size_t payload_size,
uint8_t *output, size_t output_size)
{
return periph28335_append_crc(payload, payload_size, output, output_size);
}
size_t pcan_abi_periph28335_build_read(
uint8_t controller, uint16_t start, uint16_t count,
uint8_t *output, size_t output_size)
{
return periph28335_build_read_registers(
controller, start, count, output, output_size);
}
size_t pcan_abi_periph28335_build_write(
uint8_t controller, uint16_t address, uint16_t value,
uint8_t *output, size_t output_size)
{
return periph28335_build_write_register(
controller, address, value, output, output_size);
}
size_t pcan_abi_periph28335_build_command(
uint8_t controller, uint8_t command_index,
uint8_t *output, size_t output_size)
{
return periph28335_build_command(
controller, command_index, output, output_size);
}
size_t pcan_abi_periph28335_expected_read_size(uint16_t count)
{
return periph28335_expected_read_response_size(count);
}
int pcan_abi_periph28335_decode_read(
const uint8_t *data, size_t size, uint8_t controller, uint16_t count,
uint16_t *output, size_t output_count)
{
return periph28335_decode_read_response(
data, size, controller, count, output, output_count);
}
int pcan_abi_periph28335_validate_write(
const uint8_t *response, size_t response_size,
const uint8_t *request, size_t request_size)
{
return periph28335_validate_write_response(
response, response_size, request, request_size);
}
size_t pcan_abi_periph28335_project_count(void)
{
return periph28335_project_count();
}
const char *pcan_abi_periph28335_project_name(size_t project_index)
{
return periph28335_project_name(project_index);
}
const char *pcan_abi_periph28335_command_name(
size_t project_index, size_t command_index)
{
return periph28335_command_name(project_index, command_index);
}
size_t pcan_abi_frame_encode(uint8_t sequence, uint8_t flags,
uint32_t can_id, const uint8_t *data, uint8_t dlc,
uint8_t *output, size_t output_size)

View File

@@ -0,0 +1,171 @@
#include "periph28335.h"
#include <string.h>
typedef struct {
const char *project;
const char *commands[PERIPH28335_COMMAND_COUNT];
} periph28335_project_t;
static const periph28335_project_t projects[] = {
{ "По умолчанию", { "Test", "Def", "Save", "Load", "Calibr", "Calcul", "Secret", "Light", "Raw", "-", "-", "-", "-", "-", "-", "Reset", "Nothing at all" } },
{ "23470", { "Test", "Def", "Save", "Load", "Calibr", "Read", "Secret", "-", "-", "-", "-", "-", "-", "-", "-", "Reset", "Nothing at all" } },
{ "23550", { "Test", "Def", "Save", "Load", "Calibr", "Read", "Secret", "Send", "-", "-", "-", "-", "-", "-", "-", "Reset", "Nothing at all" } },
{ "23550.2", { "Test", "Def", "Save", "Load", "Calibr", "Calcul", "Secret", "Send", "Raw", "Beep", "", "", "", "", "Log", "Reset", "Nothing at all" } },
{ "ICE 22220.1-3", { "Test", "Zero", "Save", "Def", "Calibr", "Read", "ExtLamp", "ExtLite", "-", "-", "-", "-", "-", "-", "-", "Reset", "Nothing at all" } },
{ "ICE 22220.4-5", { "Test", "Def", "Save", "Load", "Raw", "Read", "ExtLamp", "ExtLite", "No log", "-", "-", "-", "-", "-", "-", "Reset", "Nothing at all" } },
{ "Бальзам 161", { "Test", "Zero", "Save", "Def", "Calibr", "Clbr 400", "Stop", "Start", "Init", "Secret", "-", "-", "-", "-", "-", "Reset", "Nothing at all" } },
{ "Бальзам 162", { "Test", "Def", "Save", "Load", "Calibr", "Secret", "Stop", "Start", "Init", "Tune", "-", "-", "-", "-", "-", "Reset", "Nothing at all" } },
{ "Бальзам 163", { "Test", "Def", "Save", "Load", "Calibr", "Calcul", "Stop", "Start", "Init", "Tune", "Secret", "-", "-", "-", "-", "Reset", "Nothing at all" } }
};
static void put_be16(uint8_t *output, uint16_t value)
{
output[0] = (uint8_t)(value >> 8);
output[1] = (uint8_t)value;
}
static uint16_t get_be16(const uint8_t *input)
{
return (uint16_t)(((uint16_t)input[0] << 8) | input[1]);
}
uint16_t periph28335_crc16_modbus(const uint8_t *data, size_t size)
{
uint16_t crc = 0xFFFFU;
size_t index;
uint8_t bit;
if ((data == NULL) && (size != 0U)) return 0U;
for (index = 0U; index < size; ++index) {
crc ^= data[index];
for (bit = 0U; bit < 8U; ++bit) {
crc = (crc & 1U) != 0U
? (uint16_t)((crc >> 1) ^ 0xA001U)
: (uint16_t)(crc >> 1);
}
}
return crc;
}
size_t periph28335_append_crc(const uint8_t *payload, size_t payload_size,
uint8_t *output, size_t output_size)
{
uint16_t crc;
if ((output == NULL) || ((payload == NULL) && (payload_size != 0U)) ||
(payload_size > output_size) || (output_size - payload_size < 2U)) {
return 0U;
}
if (payload_size != 0U) memcpy(output, payload, payload_size);
crc = periph28335_crc16_modbus(payload, payload_size);
output[payload_size] = (uint8_t)crc;
output[payload_size + 1U] = (uint8_t)(crc >> 8);
return payload_size + 2U;
}
size_t periph28335_build_read_registers(
uint8_t controller, uint16_t start, uint16_t count,
uint8_t *output, size_t output_size)
{
uint8_t payload[6];
if ((count == 0U) || (count > PERIPH28335_REGISTER_COUNT) ||
(start >= PERIPH28335_REGISTER_COUNT) ||
((uint32_t)start + count > PERIPH28335_REGISTER_COUNT)) return 0U;
payload[0] = controller;
payload[1] = 3U;
put_be16(&payload[2], start);
put_be16(&payload[4], count);
return periph28335_append_crc(payload, sizeof payload, output, output_size);
}
size_t periph28335_build_write_register(
uint8_t controller, uint16_t address, uint16_t value,
uint8_t *output, size_t output_size)
{
uint8_t payload[6];
if (address >= PERIPH28335_REGISTER_COUNT) return 0U;
payload[0] = controller;
payload[1] = 6U;
put_be16(&payload[2], address);
put_be16(&payload[4], value);
return periph28335_append_crc(payload, sizeof payload, output, output_size);
}
size_t periph28335_build_command(
uint8_t controller, uint8_t command_index,
uint8_t *output, size_t output_size)
{
uint16_t value;
if (command_index >= PERIPH28335_COMMAND_COUNT) return 0U;
value = command_index < 16U ? (uint16_t)(1UL << command_index) : 0U;
return periph28335_build_write_register(
controller, PERIPH28335_REGISTER_COUNT - 1U, value,
output, output_size);
}
size_t periph28335_expected_read_response_size(uint16_t count)
{
return (count >= 1U && count <= PERIPH28335_REGISTER_COUNT)
? (size_t)count * 2U + 5U : 0U;
}
int periph28335_decode_read_response(
const uint8_t *data, size_t size, uint8_t controller, uint16_t count,
uint16_t *output, size_t output_count)
{
size_t expected = periph28335_expected_read_response_size(count);
uint16_t crc;
size_t index;
if ((data == NULL) || (output == NULL)) return PERIPH28335_ERROR_ARGUMENT;
if (expected == 0U) return PERIPH28335_ERROR_RANGE;
if (size != expected) return PERIPH28335_ERROR_LENGTH;
if (output_count < count) return PERIPH28335_ERROR_CAPACITY;
crc = periph28335_crc16_modbus(data, size - 2U);
if ((data[size - 2U] != (uint8_t)crc) ||
(data[size - 1U] != (uint8_t)(crc >> 8))) {
return PERIPH28335_ERROR_CRC;
}
if ((data[0] != controller) || (data[1] != 3U) ||
(data[2] != (uint8_t)(count * 2U))) {
return PERIPH28335_ERROR_HEADER;
}
for (index = 0U; index < count; ++index) {
output[index] = get_be16(&data[3U + index * 2U]);
}
return PERIPH28335_OK;
}
int periph28335_validate_write_response(
const uint8_t *response, size_t response_size,
const uint8_t *request, size_t request_size)
{
uint16_t crc;
if ((response == NULL) || (request == NULL)) return PERIPH28335_ERROR_ARGUMENT;
if ((response_size != PERIPH28335_REQUEST_SIZE) ||
(request_size != PERIPH28335_REQUEST_SIZE)) return PERIPH28335_ERROR_LENGTH;
crc = periph28335_crc16_modbus(response, response_size - 2U);
if ((response[response_size - 2U] != (uint8_t)crc) ||
(response[response_size - 1U] != (uint8_t)(crc >> 8))) {
return PERIPH28335_ERROR_CRC;
}
return memcmp(response, request, PERIPH28335_REQUEST_SIZE) == 0
? PERIPH28335_OK : PERIPH28335_ERROR_HEADER;
}
size_t periph28335_project_count(void)
{
return sizeof projects / sizeof projects[0];
}
const char *periph28335_project_name(size_t project_index)
{
return project_index < periph28335_project_count()
? projects[project_index].project : NULL;
}
const char *periph28335_command_name(size_t project_index,
size_t command_index)
{
return project_index < periph28335_project_count() &&
command_index < PERIPH28335_COMMAND_COUNT
? projects[project_index].commands[command_index] : NULL;
}

View File

@@ -0,0 +1,32 @@
#include <stdio.h>
#include <string.h>
#include "periph28335.h"
int main(void)
{
uint8_t request[PERIPH28335_REQUEST_SIZE];
static const uint8_t read_prefix[] = { 0x10U, 0x03U, 0x00U, 0x18U, 0x00U, 0x40U };
static const uint8_t response_payload[] = { 0x10U, 0x03U, 0x04U, 0x80U, 0x05U, 0x12U, 0x34U };
uint8_t response[sizeof response_payload + 2U];
uint16_t words[2];
if (periph28335_build_read_registers(16U, 24U, 64U, request, sizeof request)
!= sizeof request || memcmp(request, read_prefix, sizeof read_prefix) != 0) return 1;
if (periph28335_build_write_register(16U, 7U, 0x1234U, request, sizeof request)
!= sizeof request || memcmp(request, "\x10\x06\x00\x07\x12\x34", 6U) != 0) return 2;
if (periph28335_build_command(16U, 15U, request, sizeof request)
!= sizeof request || memcmp(request, "\x10\x06\x00\x7F\x80\x00", 6U) != 0) return 3;
if (periph28335_append_crc(response_payload, sizeof response_payload,
response, sizeof response) != sizeof response) return 4;
if (periph28335_decode_read_response(response, sizeof response, 16U, 2U,
words, 2U) != PERIPH28335_OK || words[0] != 0x8005U || words[1] != 0x1234U) return 5;
response[0] = 17U;
if (periph28335_decode_read_response(response, sizeof response, 16U, 2U,
words, 2U) != PERIPH28335_ERROR_CRC) return 6;
if (periph28335_project_count() != 9U ||
strcmp(periph28335_project_name(2U), "23550") != 0 ||
strcmp(periph28335_command_name(2U, 7U), "Send") != 0) return 7;
puts("PM35/TMS320F28335 protocol tests passed");
return 0;
}

View File

@@ -21,7 +21,7 @@ JNI_INCLUDES: list[Path] = []
SOURCES = [
ROOT / "src" / name for name in (
"set_protocol.c", "set_can.c", "set_firmware.c", "set_telemetry.c", "set_plot.c", "set_trends.c", "set_spectrum.c",
"balsam_can.c", "gui_catalog.c", "gui_frame.c", "pcan_abi.c", "pcan_crc.c",
"balsam_can.c", "periph28335.c", "gui_catalog.c", "gui_frame.c", "pcan_abi.c", "pcan_crc.c",
"pcan_frame.c", "pcan_id.c", "pcan_link.c", "pcan_ring.c",
"pcan_gas.c",
)
@@ -84,7 +84,10 @@ def main() -> int:
if not (include / "jni.h").is_file():
parser.error("--java-home must contain include/jni.h")
JNI_INCLUDES.extend([include, include / {"Windows": "win32", "Darwin": "darwin"}.get(platform.system(), "linux")])
SOURCES.append(ROOT / "ports" / "android" / "set_plot_jni.c")
SOURCES.extend([
ROOT / "ports" / "android" / "set_plot_jni.c",
ROOT / "ports" / "android" / "setprotocol_jni.c",
])
output = args.output.resolve()
output.parent.mkdir(parents=True, exist_ok=True)
build_dir = output.parent / ".setprotocol-build"