WIP: process_can #89

Draft
lulko wants to merge 4 commits from process_can into main
8 changed files with 353 additions and 115 deletions

View file

@ -22,7 +22,8 @@ enum {
firmw, firmw,
foc_id, foc_id,
angl, angl,
vel vel,
torq
}; };
/* for saved in FLASH float data*/ /* for saved in FLASH float data*/

View file

@ -4,13 +4,19 @@
#include "flash.h" #include "flash.h"
#include "reg_cah.h" #include "reg_cah.h"
#define CAN_TIMEOUT 50
#define CAN_SILENCE_TIMEOUT 500
#define MAX_CAN_READS 2
extern FLASH_RECORD *flash_rec; extern FLASH_RECORD *flash_rec;
extern volatile uint16_t msg_id; extern volatile uint16_t msg_id;
extern volatile uint16_t id_x; extern volatile uint16_t id_x;
extern volatile uint8_t msg_ch; extern volatile uint8_t msg_ch;
extern volatile uint8_t crc_h; extern volatile uint8_t crc_h;
extern volatile uint8_t crc_l; extern volatile uint8_t crc_l;
extern volatile bool send_blocked;
static volatile bool need_send_angle = false;
static volatile bool need_send_velocity = false;
void send_velocity(); void send_velocity();
void send_angle(); void send_angle();
@ -19,9 +25,12 @@ void send_motor_enabled();
void send_id(); void send_id();
void firmware_update(); void firmware_update();
void send_pid_angle(uint8_t param_pid); void send_pid_angle(uint8_t param_pid);
// void send_motor_torque(); void send_with_confirmation(void (*send_func)(void));
// void send_torque();
void send_pid(uint8_t param_pid); void send_pid(uint8_t param_pid);
void setup_id(uint8_t my_id); void setup_id(uint8_t my_id);
void setup_angle(float target_angle); void setup_angle(float target_angle);
void setup_pid_angle(uint8_t param_pid, float data); void setup_velocity(float target_velocity);
void listen_can(const CAN_message_t &msg); void process_can_messages();
void listen_can(const CAN_message_t &msg);

View file

@ -1,5 +1,11 @@
#ifndef REG_CAH_H_ #pragma once
#define REG_CAH_H_
enum{
error_foc = 0,
error_canRX,
error_canTX
};
#define APP_ADDR 0x0800400 // 16KB - Application #define APP_ADDR 0x0800400 // 16KB - Application
#define ADDR_VAR 0x8040000 #define ADDR_VAR 0x8040000
@ -16,6 +22,10 @@
#define REG_ID 0x01 #define REG_ID 0x01
#define REG_BAUDRATE 0x02 #define REG_BAUDRATE 0x02
#define DATA_TYPE_ANGLE 0x03
#define DATA_TYPE_VELOCITY 0x04
#define DATA_TYPE_TORQUE 0x05
#define REG_MOTOR_POSPID_Kp 0x30 #define REG_MOTOR_POSPID_Kp 0x30
#define REG_MOTOR_POSPID_Ki 0x31 #define REG_MOTOR_POSPID_Ki 0x31
#define REG_MOTOR_POSPID_Kd 0x32 #define REG_MOTOR_POSPID_Kd 0x32
@ -43,6 +53,3 @@
#define CAN_MSG_MAX_LEN 7 #define CAN_MSG_MAX_LEN 7
#define CRC_SIZE 2 #define CRC_SIZE 2
#define ID_SIZE sizeof(uint8_t) #define ID_SIZE sizeof(uint8_t)
#endif // REG_CAH_H_

View file

@ -14,10 +14,10 @@ build_flags =
-D STM32F446xx -D STM32F446xx
-D HAL_CAN_MODULE_ENABLED -D HAL_CAN_MODULE_ENABLED
-D SIMPLEFOC_PWM_LOWSIDE_ACTIVE_HIGH -D SIMPLEFOC_PWM_LOWSIDE_ACTIVE_HIGH
lib_deps = lib_deps =
askuric/Simple FOC@^2.3.4 askuric/Simple FOC@^2.3.4
pazi88/STM32_CAN@^1.1.2 pazi88/STM32_CAN@^1.1.2
extra_scripts = extra_scripts =
pre:gen_compile_commands.py pre:gen_compile_commands.py

View file

@ -18,6 +18,7 @@
#include "config.h" #include "config.h"
#include "process_can.h" #include "process_can.h"
void SysTick_Handler(void) { void SysTick_Handler(void) {
HAL_IncTick(); HAL_IncTick();
} }
@ -29,7 +30,7 @@ uint8_t flag_can = 0;
uint32_t flash_error; uint32_t flash_error;
FLASH_EraseInitTypeDef pEraseInit; FLASH_EraseInitTypeDef pEraseInit;
uint32_t SectorError; uint32_t SectorError;
uint32_t timeout;
/* bool for test CAN */ /* bool for test CAN */
volatile bool CAN_GET = false; volatile bool CAN_GET = false;
@ -94,22 +95,22 @@ MotorControlInputs motor_control_inputs;
} }
bool is_can_busy() {
return (CAN2->TSR & (CAN_TSR_TME0 | CAN_TSR_TME1 | CAN_TSR_TME2)) != 0x07;
void loop() {
__enable_irq();
foc_step(&motor);
CAN_message_t msg;
// Process incoming CAN messages
while (Can.read(msg)) {
listen_can(msg);
CAN_GET = true;
}
/* If receive data from CAN */
if(CAN_GET) {
CAN_GET = false;
}
} }
void loop() {
process_can_messages();
static uint32_t last_send = 0;
if(!send_blocked && !is_can_busy() && (HAL_GetTick() - last_send >= 50)) {
send_angle();
send_velocity();
last_send = HAL_GetTick();
}
foc_step(&motor);
HAL_Delay(1);
}

View file

@ -3,8 +3,8 @@
static CAN_message_t CAN_TX_msg; static CAN_message_t CAN_TX_msg;
static CAN_message_t CAN_inMsg; static CAN_message_t CAN_inMsg;
static uint8_t data_type = DATA_TYPE_ANGLE;
volatile bool send_blocked = false;
template <typename T> template <typename T>
void send_can_with_id_crc(uint8_t id, uint8_t message_type, T* data) { void send_can_with_id_crc(uint8_t id, uint8_t message_type, T* data) {
// Create CAN message // Create CAN message
@ -40,11 +40,11 @@ void send_velocity() {
// Error handling: logging, alerts, etc. // Error handling: logging, alerts, etc.
return; return;
} }
float value = flash_rec[vel].value; // float value = flash_rec[vel].value;
uint8_t id = flash_rec[addr_id].value; uint8_t id = flash_rec[addr_id].value;
send_can_with_id_crc(id,'V',&value); send_can_with_id_crc(id,'V',&current_velocity);
} }
void send_angle() { void send_angle() {
float current_angle = motor.shaftAngle(); float current_angle = motor.shaftAngle();
if (flash_rec == nullptr) { // Null check if (flash_rec == nullptr) { // Null check
@ -67,6 +67,15 @@ void send_motor_enabled() {
send_can_with_id_crc(id,'M',&value); send_can_with_id_crc(id,'M',&value);
} }
// void send_torque() {
// float i_q = motor.current.q; // Q-axis current (A)
// float torque = 100 * i_q; // Torque calculation
// if (flash_rec == nullptr) return;
// uint8_t id = flash_rec[addr_id].value;
// send_can_with_id_crc(id, 'T', &torque);
// }
void send_id() { void send_id() {
/* Firmware data reading */ /* Firmware data reading */
if (flash_rec == nullptr) { // Null check if (flash_rec == nullptr) { // Null check
@ -78,16 +87,10 @@ void send_id() {
send_can_with_id_crc(id,'I',&id); send_can_with_id_crc(id,'I',&id);
} }
// void send_motor_torque() { void send_error(uint8_t error_code){
// float i_q = motor.current.q; // Q-axis current (A) uint8_t id = flash_rec[addr_id].value;
// float torque = kt * i_q; // Torque calculation send_can_with_id_crc(id,'P',&error_code);
// torque *= 100; }
// CAN_TX_msg.id = flash_rec->value;
// CAN_TX_msg.buf[0] = 'T';
// CAN_TX_msg.len = 5;
// memcpy(&CAN_TX_msg.buf[1], &torque, sizeof(torque));
// Can.write(CAN_TX_msg);
// }
void send_pid_angle(uint8_t param_pid){ void send_pid_angle(uint8_t param_pid){
if (flash_rec == nullptr) { // Null check if (flash_rec == nullptr) { // Null check
@ -129,31 +132,68 @@ void setup_angle(float target_angle) {
// motor.move(target_angle); // motor.move(target_angle);
} }
// void setup_pid_angle(uint8_t param_pid, uint32_t data){ /**
// conv_float_to_int.f = data; * @brief Set the up velocity object
// switch (param_pid) { *
// case pid_p: * @param target_velocity
// motor.P_angle.P = conv_float_to_int.f; */
// break; void setup_velocity(float target_velocity) {
// case pid_i: motor.enable();
// motor.P_angle.I = conv_float_to_int.f; motor_control_inputs.target_velocity = target_velocity;
// break; }
// case pid_d:
// motor.P_angle.D = conv_float_to_int.f;
// break;
// default:
// break;
// }
// write_param(param_pid,data); void send_data_type(uint8_t type_d){
// } uint8_t id = flash_rec[addr_id].value;
send_can_with_id_crc(id,'D',&type_d);
}
// Вспомогательные функции
void send_with_confirmation(void (*send_func)(void)) {
uint32_t t_start = HAL_GetTick();
send_func();
// Ожидание подтверждения отправки
while (!(CAN2->TSR & (CAN_TSR_RQCP0 | CAN_TSR_RQCP1 | CAN_TSR_RQCP2))) {
if (HAL_GetTick() - t_start > CAN_TIMEOUT) break;
}
}
void process_can_messages() {
static uint32_t last_received = HAL_GetTick();
CAN_message_t msg;
uint8_t count = 0;
// block send while data cant
send_blocked = true;
// Чтение сообщений с ограничением
while(count < MAX_CAN_READS && Can.read(msg)) {
listen_can(msg);
last_received = HAL_GetTick();
count++;
}
// Проверка таймаута молчания
if(HAL_GetTick() - last_received > CAN_SILENCE_TIMEOUT) {
// Разблокируем отправку при отсутствии сообщений
send_blocked = false;
}
}
/**
* @brief Function for process data from CAN
* @details Function check your ID deviceю. Compare receive and calculated CRC.
* If ID && CRC == TRUE, then process message or dont send data.
* If if and crc = true:
* Check CAN_REG
* Gives your data type
* Write with data or send data
* @param msg
*/
void listen_can(const CAN_message_t &msg) { void listen_can(const CAN_message_t &msg) {
msg_id = msg.id; msg_id = msg.id;
msg_ch = msg_id & 0xF; // Extract message channel msg_ch = msg_id & 0xF; // Extract message channel
uint16_t id_x = (msg_id >> 4) & 0x7FF; // Extract device address uint16_t id_x = (msg_id >> 4) & 0x7FF; // Extract device address
/* CRC Calculation */ /* CRC Calculation */
uint16_t received_crc = (msg.buf[msg.len - 2]) | (msg.buf[msg.len - 1] << 8); uint16_t received_crc = (msg.buf[msg.len - 2]) | (msg.buf[msg.len - 1] << 8);
uint8_t data[10] = {0}; // Message buffer for CRC verification uint8_t data[10] = {0}; // Message buffer for CRC verification
@ -171,6 +211,7 @@ void listen_can(const CAN_message_t &msg) {
return; // Ignore message on CRC mismatch return; // Ignore message on CRC mismatch
} }
flash_rec = load_params(); flash_rec = load_params();
/* Message Structure: 0x691 /* Message Structure: 0x691
69 - Device address 69 - Device address
1 - Action code */ 1 - Action code */
@ -211,6 +252,18 @@ void listen_can(const CAN_message_t &msg) {
write_param(pid_d,conv_float_to_int.i); write_param(pid_d,conv_float_to_int.i);
break; break;
case DATA_TYPE_ANGLE:
data_type = DATA_TYPE_ANGLE;
break;
case DATA_TYPE_VELOCITY:
data_type = DATA_TYPE_VELOCITY;
break;
case DATA_TYPE_TORQUE:
data_type = DATA_TYPE_TORQUE;
break;
case FIRMWARE_UPDATE: case FIRMWARE_UPDATE:
firmware_update(); firmware_update();
break; break;
@ -233,6 +286,9 @@ void listen_can(const CAN_message_t &msg) {
case MOTOR_VELOCITY: send_velocity(); break; case MOTOR_VELOCITY: send_velocity(); break;
case MOTOR_ANGLE: send_angle(); break; case MOTOR_ANGLE: send_angle(); break;
case MOTOR_ENABLED: send_motor_enabled(); break; case MOTOR_ENABLED: send_motor_enabled(); break;
case DATA_TYPE_ANGLE: send_data_type(uint8_t(DATA_TYPE_ANGLE)); break;
case DATA_TYPE_VELOCITY: send_data_type(uint8_t(DATA_TYPE_VELOCITY)); break;
case DATA_TYPE_TORQUE: send_data_type(uint8_t(DATA_TYPE_TORQUE)); break;
// case MOTOR_TORQUE: send_motor_torque(); break; // case MOTOR_TORQUE: send_motor_torque(); break;
// case FOC_STATE: send_foc_state(); break; // case FOC_STATE: send_foc_state(); break;
case REG_MOTOR_POSPID_Kp: send_pid_angle(pid_p); break; case REG_MOTOR_POSPID_Kp: send_pid_angle(pid_p); break;
@ -241,5 +297,35 @@ void listen_can(const CAN_message_t &msg) {
default: break; default: break;
} }
} }
/* If msg_ch != REG_WRITE or REG_READ, then SimpleFOC*/
else{
switch(data_type) {
/* Read after write*/
case DATA_TYPE_ANGLE:
send_angle();
delay(200);
memcpy(&motor_control_inputs.target_angle, &msg.buf[1], sizeof(float));
setup_angle(motor_control_inputs.target_angle);
break;
case DATA_TYPE_VELOCITY:{
send_velocity();
float vel = 0.0f;
memcpy(&vel, &msg.buf[1], sizeof(float));
setup_velocity(vel);
break;
}
case DATA_TYPE_TORQUE:
// send_torque();
break;
default:
send_error(error_foc);
break;
}
}
} }
} }

View file

@ -1,12 +1,23 @@
import can import can
import time import time
import sys import sys
# Конфигурация # Конфигурация
CAN_INTERFACE = 'can0' CAN_INTERFACE = 'can0'
OLD_DEVICE_ID = int(sys.argv[1]) # Текущий ID устройства (по умолчанию) OLD_DEVICE_ID = int(sys.argv[1]) # Текущий ID устройства
REG_READ = 0x7 # Код команды чтения REG_READ = 0x7 # Код команды чтения
REG_ID = 0x01 # Адрес регистра с ID устройства REG_ID = 0x01 # Адрес регистра с ID устройства
def flush_can_buffer(bus, duration=0.3):
"""Очистка входного буфера CAN"""
start_time = time.time()
flushed_count = 0
while time.time() - start_time < duration:
msg = bus.recv(timeout=0)
if msg:
flushed_count += 1
print(f"Очищено сообщений из буфера: {flushed_count}")
def send_can_message(bus, can_id, data): def send_can_message(bus, can_id, data):
"""Отправка CAN-сообщения""" """Отправка CAN-сообщения"""
try: try:
@ -23,14 +34,14 @@ def send_can_message(bus, can_id, data):
return False return False
def receive_response(bus, timeout=1.0): def receive_response(bus, timeout=1.0):
"""Ожидание ответа от устройства""" """Ожидание ответа от устройства (сохраняем вашу оригинальную логику)"""
start_time = time.time() start_time = time.time()
while time.time() - start_time < timeout: while time.time() - start_time < timeout:
msg = bus.recv(timeout=0.1) msg = bus.recv(timeout=0.1)
if msg: if msg:
print(f"[Прием] CAN ID: 0x{msg.arbitration_id:03X}, Данные: {list(msg.data)}") print(f"[Прием] CAN ID: 0x{msg.arbitration_id:03X}, Данные: {list(msg.data)}")
return msg return msg
print("[Ошибка] Таймаут") print("[Ошибка] Таймаут приема")
return None return None
def validate_crc16(data): def validate_crc16(data):
@ -45,64 +56,91 @@ def validate_crc16(data):
crc >>= 1 crc >>= 1
return crc return crc
# Инициализация CAN-интерфейса def main():
bus = can.interface.Bus(channel=CAN_INTERFACE, bustype='socketcan') # Инициализация CAN-интерфейса
try:
bus = can.interface.Bus(
channel=CAN_INTERFACE,
bustype='socketcan',
bitrate=1000000 # Совпадает с устройством
)
except Exception as e:
print(f"Ошибка инициализации CAN: {e}")
sys.exit(1)
# ======= 1. Запрос текущего ID устройства ======= # ======= 1. Подготовка запроса =======
can_id_read = (OLD_DEVICE_ID << 4) | REG_READ
data_read = [REG_ID, 0x00]
# Формируем полные данные для расчета CRC:
full_data_for_crc = list(can_id_read.to_bytes(2, 'little')) + data_read
# Рассчитываем CRC
crc = validate_crc16(full_data_for_crc)
crc_bytes = list(crc.to_bytes(2, 'little'))
# Собираем итоговый пакет
packet_read = data_read + crc_bytes
# Формируем CAN ID для чтения: (OLD_DEVICE_ID << 4) | REG_READ # ======= 2. Отправка запроса с повторами =======
can_id_read = (OLD_DEVICE_ID << 4) | REG_READ max_retries = 3
response = None
# Данные для запроса: [регистр, резервный байт]
data_read = [REG_ID, 0x00] for attempt in range(max_retries):
print(f"\nПопытка {attempt+1}/{max_retries}")
# Формируем полные данные для расчета CRC:
# - CAN ID разбивается на 2 байта (little-endian) # Очистка буфера перед отправкой
# - Добавляем данные запроса flush_can_buffer(bus, 0.3)
full_data_for_crc = list(can_id_read.to_bytes(2, 'little')) + data_read
# Отправка запроса
# Рассчитываем CRC и разбиваем на байты (little-endian) print(f"Отправка запроса на чтение ID: {packet_read}")
crc = validate_crc16(full_data_for_crc) if not send_can_message(bus, can_id_read, packet_read):
crc_bytes = list(crc.to_bytes(2, 'little')) print("Ошибка отправки, повтор...")
time.sleep(0.2)
# Собираем итоговый пакет: данные + CRC continue
packet_read = data_read + crc_bytes
# Ожидание ответа
print("Запрос на чтение ID:", packet_read) response = receive_response(bus, timeout=0.5)
send_can_message(bus, can_id_read, packet_read) if response:
break
# ======= 2. Получение и проверка ответа =======
response = receive_response(bus) print("Ответ не получен, повтор...")
if response: time.sleep(0.2)
# ======= 3. Обработка ответа =======
if not response:
print("Устройство не ответило после всех попыток")
bus.shutdown()
sys.exit(1)
data = response.data data = response.data
if len(data) < 4: if len(data) < 4:
print("Слишком короткий ответ") print("Слишком короткий ответ")
bus.shutdown()
sys.exit(1)
# Проверяем минимальную длину ответа (данные + CRC) # Проверяем минимальную длину ответа (данные + CRC)
id_bytes = response.arbitration_id.to_bytes(1, byteorder='little')
full_data = list(id_bytes) + list(data[:-2])
print(f"Полные данные для CRC: {full_data}")
received_crc = int.from_bytes(data[-2:], byteorder='little')
calc_crc = validate_crc16(full_data)
print(f"Расчитанный CRC: 0x{calc_crc:04X}, Полученный CRC: 0x{received_crc:04X}")
if received_crc == calc_crc:
print(f"Текущий ID устройства: 0x{data[1]:02X}")
else: else:
id_bytes = response.arbitration_id.to_bytes(1,byteorder='little') print("Ошибка: CRC не совпадает")
#buff with id and data without CRC
full_data = list(id_bytes) + list(data[:-2]) # Завершаем работу с шиной
print(f"Received full_data: {list(full_data)}") bus.shutdown()
received_crc = int.from_bytes(data[-2:], byteorder='little')
#calc CRC
calc_crc = validate_crc16(full_data)
print(f"Расчитанный CRC PYTHON : 0x{calc_crc:02X}")
if received_crc == calc_crc:
# Если CRC совпадает, проверяем структуру ответа:
print(f"Текущий ID устройства: 0x{data[1]:02X}")
else:
print("Ошибка: CRC не совпадает")
else:
print("Устройство не ответило")
# Завершаем работу с шиной
bus.shutdown()
if __name__ == "__main__": if __name__ == "__main__":
import sys
if len(sys.argv) != 2: if len(sys.argv) != 2:
print("Использование: python3 can_flasher.py address") print("Использование: python3 can_flasher.py <адрес_устройства>")
print("Пример: python3 can_flasher.py 1")
sys.exit(1) sys.exit(1)
main()

View file

@ -0,0 +1,96 @@
import can
import time
import sys
import struct
# Конфигурация
CAN_INTERFACE = 'can0'
DEVICE_ID = 0x69 # Текущий ID устройства
REG_READ = 0x7
REG_WRITE = 0x8
DATA_TYPE_ANGLE = 0x03
DATA_TYPE_VELOCITY = 0x04
DATA_TYPE_TORQUE = 0x05
# CRC функция (аналогичная устройству)
def validate_crc16(data):
crc = 0xFFFF
for byte in data:
crc ^= byte
for _ in range(8):
if crc & 0x0001:
crc = (crc >> 1) ^ 0xA001
else:
crc >>= 1
return crc
def send_can_message(bus, can_id, data):
try:
msg = can.Message(
arbitration_id=can_id,
data=data,
is_extended_id=False
)
bus.send(msg)
print(f"[Отправка] CAN ID: 0x{can_id:03X}, Данные: {list(data)}")
return True
except can.CanError as e:
print(f"Ошибка CAN: {e}")
return False
def receive_response(bus, timeout=1.0):
start_time = time.time()
while time.time() - start_time < timeout:
msg = bus.recv(timeout=0.1)
if msg:
print(f"[Прием] CAN ID: 0x{msg.arbitration_id:03X}, Данные: {list(msg.data)}")
return msg
print("[Ошибка] Таймаут")
return None
def test_simplefoc_else_block():
bus = can.interface.Bus(channel=CAN_INTERFACE, bustype='socketcan')
# 1. Установка типа данных (DATA_TYPE_ANGLE)
can_id_write = (DEVICE_ID << 4) | REG_WRITE
data_set_type = [DATA_TYPE_ANGLE, 0x00]
full_data = list(can_id_write.to_bytes(2, 'little')) + data_set_type
crc = validate_crc16(full_data)
crc_bytes = list(crc.to_bytes(2, 'little'))
packet = data_set_type + crc_bytes
send_can_message(bus, can_id_write, packet)
time.sleep(0.1) # Ожидание обработки
# 2. Отправка SimpleFOC сообщения (угол)
target_angle = 45.0
angle_bytes = struct.pack('<f', target_angle)
can_id_simplefoc = (DEVICE_ID << 4) | 0x01 # Не REG_READ/REG_WRITE
payload = [0x00] + list(angle_bytes) + [0x00] # [type placeholder, angle, padding]
# Расчет CRC
full_data_sf = list(can_id_simplefoc.to_bytes(2, 'little')) + payload
crc_sf = validate_crc16(full_data_sf)
payload += list(crc_sf.to_bytes(2, 'little'))
# Отправка
print("\nТест SimpleFOC (блок else):")
send_can_message(bus, can_id_simplefoc, payload)
# 3. Проверка ответа (отправка угла + установка нового угла)
response = receive_response(bus)
if response:
# Проверка структуры ответа
if response.data[0] == ord('A'):
print("Успех: Отправлен текущий угол")
else:
print("Ошибка: Неверный тип ответа")
else:
print("Ошибка: Нет ответа от устройства")
# 4. Проверка установки нового угла (интеграционно)
# ... (может требовать дополнительной проверки на устройстве)

Можно добавить тест на изменение режима управленя и отправку скорости. 2 раза подряд угол и 2 раза подряд скорость

Можно добавить тест на изменение режима управленя и отправку скорости. 2 раза подряд угол и 2 раза подряд скорость
bus.shutdown()
if __name__ == "__main__":
test_simplefoc_else_block()