한국과학기술연구소 인턴

uCAN Converter (API)

Aconite Green 2023. 3. 25. 18:13

 

배경

모터를 CAN 통신으로 제어하기 위한 방법들에 대해서 공부해 보았고, 그 중에서도 uCAN Converter 에 대해서 정리해 보고자 합니다

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로 복사하는 방식으로 오류 처리를 용이하게 할 수 있습니다.