한국과학기술연구소 인턴
uCAN Converter (API)
Aconite Green
2023. 3. 25. 18:13
배경
모터를 CAN 통신으로 제어하기 위한 방법들에 대해서 공부해 보았고, 그 중에서도 uCAN Converter 에 대해서 정리해 보고자 합니다
API
SendCANTxFrame
quint8 SendCANTxFrame(CAN_Struct Tx, quint8 * SerialBuffer, quint8 BufferSize, quint8 * ConvertSize)
{
int i;
quint8 ind=0;
quint8 tmp_index;
quint8 buf[40];
// (1) Tx 포맷에 따라 버퍼에 적절한 명령 헤더를 저장
switch (Tx.Format)
{
case CAN_StructFormat::TX_STD_DATA: buf[ind++] = CAN_SerialCommandHeader::STD_DATA; break;
case CAN_StructFormat::TX_STD_REMOTE: buf[ind++] = CAN_SerialCommandHeader::STD_REMOTE; break;
case CAN_StructFormat::TX_EXT_DATA: buf[ind++] = CAN_SerialCommandHeader::EXT_DATA; break;
case CAN_StructFormat::TX_EXT_REMOTE: buf[ind++] = CAN_SerialCommandHeader::EXT_REMOTE; break;
default: return Invalid_Arg;
}
// (2) 표준 형식인지 확장 형식인지에 따라 ID의 길이를 결정
if(IsSTD(Tx.Format))
tmp_index = 3;
else if(IsEXT(Tx.Format))
tmp_index = 8;
// (3) Tx.ID를 ASCII Hex 값으로 변환하고 버퍼에 저장
for(i=1;i<=tmp_index;i++){
buf[ind++] = toASCII_HEX(Div_4(Tx.ID,tmp_index - i));
}
// (4) Tx.DLC를 ASCII Hex 값으로 변환하고 버퍼에 저장
buf[ind++] = toASCII_HEX(Tx.DLC);
// (5) 데이터 프레임인 경우 Tx.DATA를 ASCII Hex 값으로 변환하고 버퍼에 저장
if(IsDATA(Tx.Format)){
for(i=0;i<Tx.DLC;i++){
buf[ind++] = toASCII_HEX(Div_4(Tx.DATA[i],1));
buf[ind++] = toASCII_HEX(Div_4(Tx.DATA[i],0));
}
}
// (6) 버퍼에 CR 문자를 추가하여 프레임의 끝을 표시
buf[ind++] = CR;
// (7) 변환된 길이가 입력된 버퍼 크기를 초과하면 오류 반환
if(ind > BufferSize)
return Invalid_Arg;
// 결과 길이를 ConvertSize에 저장
*ConvertSize = ind;
// 변환된 버퍼를 SerialBuffer로 복사
memcpy(SerialBuffer,buf,ind);
// 정상 작동 시 No_Error 반환
return No_Error;
}
SetSerialConfig
quint8 SetCANConfig(CANConfigInfo info, quint8 * SerialBuffer, quint8 BufferSize, quint8 * ConvertSize){
quint8 len;
quint8 ind;
unsigned char buf[30];
quint8 function;
function =0;
// 버퍼 초기화 및 설정 시작
buf[0] = 'W';
buf[1] = 'C';
// CAN 스펙에 따라 버퍼에 적절한 문자를 저장
switch (info.Spec){
case CAN_A: buf[2] = 'A'; break;
case CAN_B: buf[2] = 'B'; break;
default: return Invalid_Arg;
}
buf[3] = ',';
// Baudrate를 ASCII로 변환하여 버퍼에 저장
len = int2ascii(info.Baudrate,&buf[4],4);
if(len == 0 ){
return Invalid_Arg;
}
buf[len+4] = ',';
ind=len+5;
// ID를 Hex 값으로 변환하여 버퍼에 저장
len = hex2ID(info.ID,info.Spec,&buf[ind],8);
if(len == 0 ){
return Invalid_Arg;
}
buf[len+ind] = ',';
ind=len+ind+1;
// 마스크를 Hex 값으로 변환하여 버퍼에 저장
len = hex2ID(info.Mask,info.Spec,&buf[ind],8);
if(len == 0 ){
return Invalid_Arg;
}
buf[len+ind] = ',';
ind=len+ind+1;
// DAR 및 ABOR 플래그를 설정하여 기능을 활성화
if(info.DAR)
SETDAR(function);
if(info.ABOR)
SETABOR(function);
// 기능을 ASCII Hex 값으로 변환하여 버퍼에 저장
buf[ind++] = toASCII_HEX(function);
buf[ind++] = CR;
// 변환된 길이가 입력된 버퍼 크기를 초과하면 오류 반환
if(ind > BufferSize)
return Invalid_Arg;
// 결과 길이를 ConvertSize에 저장
*ConvertSize = ind;
// 변환된 버퍼를 SerialBuffer로 복사
memcpy(SerialBuffer,buf,ind);
// 정상 작동 시 No_Error 반환
return No_Error;
}
SetCANConfig
quint8 SetSerialConfig(SerialConfigInfo info, quint8 * SerialBuffer, quint8 BufferSize, quint8 * ConvertSize){
quint8 len;
unsigned char buf[20];
// 버퍼 초기화 및 설정 시작
buf[0] = 'W';
buf[1] = 'S';
// 흐름 제어에 따라 버퍼에 적절한 문자를 저장
switch (info.flow){
case NoFlowControl: buf[2] = 'N'; break;
case HardwareControl: buf[2] = 'H'; break;
default: return Invalid_Arg;
}
// 데이터 비트 수에 따라 버퍼에 적절한 문자를 저장
switch (info.data){
case Data8: buf[3] = '8'; break;
default: return Invalid_Arg;
}
// 패리티 비트 설정에 따라 버퍼에 적절한 문자를 저장
switch (info.parity){
case NoParity: buf[4] = 'N'; break;
case EvenParity: buf[4] = 'E'; break;
case OddParity: buf[4] = 'O'; break;
case SpaceParity: buf[4] = 'S'; break;
case MarkParity: buf[4] = 'M'; break;
default: return Invalid_Arg;
}
// 스톱 비트 설정에 따라 버퍼에 적절한 문자를 저장
switch (info.stop){
case OneStop: buf[5] = '1'; break;
case TwoStop: buf[5] = '2'; break;
default: return Invalid_Arg;
}
// Baudrate를 ASCII로 변환하여 버퍼에 저장
len = int2ascii(info.Baudrate,&buf[6],20-6);
if(len == 0 ){
return Invalid_Arg;
}
buf[6+len] = CR;
// 변환된 길이가 입력된 버퍼 크기를 초과하면 오류 반환
if((len+7) > BufferSize)
return Invalid_Arg;
// 결과 길이를 ConvertSize에 저장
*ConvertSize = len+7;
// 변환된 버퍼를 SerialBuffer로 복사
memcpy(SerialBuffer,buf,len+7);
// 정상 작동 시 No_Error 반환
return No_Error;
}
- 스톱 비트는 시리얼 통신에서 프레임의 끝을 나타내는 비트로 사용. 스톱 비트를 사용하면 수신 측이 데이터 프레임의 끝을 정확하게 감지할 수 있고, 다음 프레임을 올바르게 해석이 가능, 일반적으로 시리얼 통신에서는 1개 또는 2개의 스톱 비트를 사용할 수 있습니다. 스톱 비트가 더 많으면 데이터 전송 속도는 느려지지만, 노이즈에 의한 오류 감지 확률이 증가
- 처음부터 SerialBuffer를 조작하지 않고 buf 객체를 생성한 이유
- 코드의 가독성과 유지 보수성 향상: 함수 내에서 작업을 위한 임시 버퍼를 사용하면 코드의 구조와 데이터 흐름을 이해하기 쉽습니다. 또한 임시 버퍼를 사용하면 함수의 다른 부분에 영향을 주지 않고 특정 부분을 수정할 수 있어 유지 보수성이 향상됩니다.
- 오류 처리 용이성: 함수 내에서 발생할 수 있는 여러 오류 상황을 처리하기가 쉬워집니다. 작업 중간에 오류가 발생하면 원래의 SerialBuffer를 수정하지 않았기 때문에 원래 상태를 유지할 수 있습니다. 작업이 정상적으로 완료된 후에만 변환된 결과를 SerialBuffer로 복사하는 방식으로 오류 처리를 용이하게 할 수 있습니다.