반응형

예전에 차량의 CAN, KLINE, LIN 통신 값을 읽어들이기 위해 구성해봤던 프로토타입이다.

그 프로토타입을 이렇게 샘플로 만들었었다.
그런데 최근에 ESP32를 더 많이 활용하고 있고 기존에 만들었던 샘플의 K-LINE과 LIN통신도 활용할 곳이 점점 사라져서 ESP32와 CAN통신 조합으로 프로토타입을 만들어 보려고 한다.

정말 깔끔해졌다.
ESP32는 CAN컨트롤러가 내장되어 있어 기존에 있던 MCP2515 모듈을 없애고 CAN트랜시버는 로직레벨이 다르기 때문에 기존의 TJA1050에서 SN65HVD230로 바꿔서 사용하기로 했다.
이외 K-LINE 트랜시버 MCZ33290 모듈과 LIN 트랜시버 TJA1020 모듈도 없애 더 간결해졌고 전원입력부는 원래 사용했던 7505 레귤레이터를 사용하여 ESP32 5V에 인가하기로 한다.
라이브러리 소스코드는 아래에 공유하겠다.
커맨드 혹은 파워쉘로 모듈의 시리얼 통신을 수신한다면 어느 플랫폼, 어느 프레임워크로 개발을 하던 CAN 값들을 모니터링 할 수 있으며 현재는 일반 CAN 값들을 모니터링 하는 정도이니 참고 바란다. (추후에 CANFD 혹은 여러 종류의 CAN을 자주 만지게 된다면 업데이트 하도록 하겠다.)
* 가동시간을 얻어오는 함수(get_time)은 프레임워크에 따라 수정해야한다.
can_monitor.h
#pragma once
#include <stdio.h>
typedef struct {
uint8_t data;
uint32_t time;
} can_data_t;
typedef struct {
uint32_t id;
uint8_t len;
can_data_t data[8];
} can_msg_t;
typedef enum {
CAN_NUM_10 = 10,
CAN_NUM_20 = 20,
CAN_NUM_30 = 30,
CAN_NUM_50 = 50,
CAN_NUM_100 = 100,
CAN_NUM_MAX,
} can_num_t;
typedef enum {
CAN_DISPLAY_TIME_1SEC = 1,
CAN_DISPLAY_TIME_3SEC = 3,
CAN_DISPLAY_TIME_5SEC = 5,
CAN_DISPLAY_TIME_10SEC = 10,
CAN_DISPLAY_TIME_30SEC = 30,
CAN_DISPLAY_TIME_50SEC = 50,
CAN_DISPLAY_TIME_100SEC = 100,
CAN_DISPLAY_TIME_MAX,
} can_disp_time_t;
void can_monitor_begin(can_num_t _can_num, can_disp_time_t _can_disp_time);
void can_monitor_end();
void can_monitor_input(uint32_t* _can_id, uint8_t* _can_len, uint8_t* _can_data, int* _data_idx);
void can_monitor_output();
can_monitor.c
#include <string.h>
#include "esp_timer.h"
#include "can_monitor.h"
can_num_t can_num = 0;
can_disp_time_t can_disp_time = 0;
can_msg_t* can_msg = NULL;
void can_monitor_begin(can_num_t _can_num, can_disp_time_t _can_disp_time) {
can_num = _can_num;
can_disp_time = _can_disp_time;
can_msg = malloc(sizeof(can_msg_t) * can_num);
memset(can_msg, 0, sizeof(can_msg_t) * can_num);
}
void can_monitor_end() {
free(can_msg);
}
// 1sec = 1
static uint32_t get_time() {
/**
* 개발 프레임워크에 따라 수정 해야하는 부분
*/
return esp_timer_get_time() / 1000000;
}
// 삽입 정렬 알고리즘 적용
static void can_msg_sort(can_msg_t* _can_msg) {
for (int i = 1; i < can_num; i++) {
if (_can_msg[i].id == 0) continue;
can_msg_t key = _can_msg[i];
int j = i - 1;
while (j >= 0 && _can_msg[j].id > key.id) {
memcpy(&_can_msg[j + 1], &_can_msg[j], sizeof(can_msg_t));
j--;
}
memcpy(&_can_msg[j + 1], &key, sizeof(can_msg_t));
}
}
void can_monitor_input(uint32_t* _can_id, uint8_t* _can_len, uint8_t* _can_data, int* _data_idx) {
if (can_msg == NULL) {
printf("can_monitor_begin Not Initialized");
return;
}
for (int i = 0; i < can_num; i++) {
if (can_msg[i].id == *_can_id || can_msg[i].id == 0) {
can_msg[i].id = *_can_id;
can_msg[i].len = *_can_len;
if (can_msg[i].data[*_data_idx].data != *_can_data || can_msg[i].data[*_data_idx].time == 0) {
can_msg[i].data[*_data_idx].time = get_time();
}
can_msg[i].data[*_data_idx].data = *_can_data;
break;
}
}
can_msg_sort(can_msg);
}
void can_monitor_output() {
if (can_msg == NULL) {
printf("can_monitor_begin Not Initialized");
return;
}
printf("\033[2J\033[H");
printf("\033[1;32m");
printf("%4s %4s %s\n", "ID", "LEN", "DATA");
for (int i = 0; i < can_num; i++) {
printf("%4ld %4d ", can_msg[i].id, can_msg[i].len);
for (int j = 0; j < 8; j++) {
if (get_time() - can_msg[i].data[j].time < can_disp_time) {
printf("\033[1;4;7;31m");
printf("%02x ", can_msg[i].data[j].data);
printf("\033[0m");
printf("\033[1;32m");
} else {
printf("%02x ", can_msg[i].data[j].data);
}
}
printf("\n");
}
printf("\033[0m");
}
반응형