LIN 2.0

LIN2.0 통신 프로그래밍 강좌 2 - PID 수신 처리

BLDC 2013. 2. 13. 19:55

 

 

 

프로토콜 규약 그림4.2 : 프레임 진행 상태도

 

 

● UART_RX() framework

 

UART_RX 인터럽트 서비스 루틴은 프레임 헤더의 BREAK(SYNC는 input capture에서 처리), PID와 프레임 응답의 데이터를 처리해야 한다. 따라서 UART_RX 인터럽트가 발생하면 서비스 루틴에서는 현재 순서가 어떤 필드를 처리해야할 순서인가를 기억해야하며, 수신 데이터를 처리한 후에는 다음에 수신한 필드 데이터를 처리해야할 순서를 미리 지정해 놓는 것이 좋다. 이를 위하여 다음과 같은 열거형과 순서를 저장하는 변수를 도입한다.

 

enum {BREAK_STEP=1, SYNC_STEP, PID_STEP, RX_STEP, TX_STEP, CRC_STEP, SLEEP_STEP};

int linStep = BREAK_STEP;

 

그림 4.2를 각 단계별로 구분하여 코딩하면 아래와 같은 형식이 될 것이다.

 

interrupt_service_routine UART_RX(void)

{

if (linStep == BREAK_STEP){

.......

linStep = SYNC_STEP;    // 다음 순서

}

else if (linStep == PID_STEP){

.......

pidHandle();    // 다음 순서 RX_STEP or TX_STEP or BREAK_STEP

}

else if (linStep == RX_STEP){

.......

if (last data RX) linStep = CRC_STEP;

}

else if (linStep == TX_STEP){

.......

if (last data TX) linStep = CRC_STEP

}

else {  //linStep == CRC_STEP

.......

linStep = BREAK_STEP;

}

}

interrupt_service_routine INPUT_CAPTURE(void)

{

.....

else if (edge_count >= 6)

{

// input capture 종료 && UART 복구

....

linStep = PID_STEP

}

.....

}

 

그러나 그림 4.2에서와 같이 모든 단계에는 UART 수신 프레임 오류에 대한 처리가 필수적이다. 각 단계마다 별도의 오류 처리를 추가해야하나 이는 중복된 오류 처리 코드가 여러 번 반복된다. 따라서 위의 코드는 다음과 같이 UART 수신 프레임 오류의 유무에 따라 분류하고, 세부적으로 수신 단계를 처리하는 것이 효율적이다.

 

interrupt_service_routine UART_RX(void)

{

if (RX_FRAME_ERROR) {

if (RX_BUFFER == 0) {    //BREAK 수신

if (linStep == BREAK_STEP){

.......

linStep = SYNC_STEP;    // 다음 순서

}

}

else{

// BREAK가 아닌 다른 필드에서의 프레임 오류

}

}

else {    // 프레임 오류 없음.

if (linStep == BREAK_STEP) {

//BREAK_STEP에서는 반드시 수신 프레임 오류가 발생해야하므로 오류처리

}

else if (linStep == PID_STEP){

.......

pidHandle();    // 다음 순서 RX_STEP or TX_STEP or BREAK_STEP

}

else if (linStep == RX_STEP){

.......

if (last data RX) linStep = CRC_STEP;

}

else if (linStep == TX_STEP){

.......

if (last data TX) linStep = CRC_STEP

}

else {  //linStep == CRC_STEP

.......

linStep = BREAK_STEP;

}

}

}

interrupt_service_routine INPUT_CAPTURE(void)

{

.....

else if (edge_count >= 6)

{

// input capture 종료 && UART 복구

....

linStep = PID_STEP

}

.....

}

 

LIN은 자신이 전송한 신호도 수신하므로 UART_TX 인터럽트 서비스 루틴은 필요 없으며, UART_RX()에서 송/수신 모두 처리하면 된다.

 

 

●PID 수신 처리 함수 pidHandle()

 

PID 필드를 수신하면 처리해야할 일은 다음과 같다.

  1. 패러티 검사
  2. LIN 프레임 헤더에 대한 응답 준비(RX_STEP, TX_STEP, domant - unknown PID)
    • TX_STEP의 경우 첫 DATA(D0) 전송
  3. CRC 계산

패러티를 검사하려면 수신된 필드를 다음과 같은 공용체(union)으로 복사하여 코딩하는 것이 효율적이다.

------------------------------------------------------------------------------------------------

union{

unsigned char PID;

unsigned char ID : 6;

struct {

unsigned char D0 : 1;

unsigned char D1 : 1;

unsigned char D2 : 1;

unsigned char D3 : 1;

unsigned char D4 : 1;

unsigned char D5 : 1;

unsigned char P0 : 1;

unsigned char P1 : 1;

}bits;

}pidParity;

 

// 패러티 검사

 

linParity.PID = RX_BUFFER;    // 수신 PID 복사

linParity.PID &= 0x3F;    // 패러티 비트 지움.

linParity.bits.P0 = linParity.bits.D0 ^ linParity.bits.D1 ^ linParity.bits.D2 ^ linParity.bits.D4;

linParity.bits.P1 = ~(linParity.bits.D1 ^ linParity.bits.D3 ^ linParity.bits.D4 ^ linParity.bits.D5);

 

if (RX_BUFFER == pidParity.PID)

{

//패러티 맞음.

.....

}

else{

//패러티 오류 처리

.....

}

------------------------------------------------------------------------------------------------

패러티가 맞은 경우, 다음 데이터 수신 준비를 위하여 수신 데이터 수를 세는 카운터 변수를 초기화한다. 이 작업은 헤더에 대한 응답이 수신이든 송신이든 상관없다. 왜냐하면 송신하는 데이터도 자신이 수신하기 때문이다. Check sum을 계산할 변수도 초기화한다.

------------------------------------------------------------------------------------------------

static rxCount = 0, txCount = 0;    // 송수신 데이터 바이트 수

int checkSum = 0;    // check sum은 256 이상인지 검사해야하므로 16-bit 데이터로 지정

 

if (RX_BUFFER == pidParity.PID)

{

//패러티 맞음.

rxCount = 0;

pidHandle(pidParity.PID);

checkSum = pidParity.PID;

.....

}

------------------------------------------------------------------------------------------------

pidHandle() 함수는 편의상 함수로 만들었지만, 실제 프로그램에서는 인라인으로 작성하는 것이 바람직하다. 함수에서는 수신된 PID로부터 슬레이브가 어떤 동작을 해야할지를 결정하고, 이에 따른 준비 작업을 수행한다.

프로그램을 작성하기 전에 슬레이브는 다음과 2개의 메시지를 처리한다고 가정한다.

 

① COMMAND_MSG (수신 메시지,슬레이브가 메시지의 소비자)

: 4-byte 데이터, 무조건 프레임, COMMAND_PID = 0x01

② STATE_MSG (송신 메시지,슬레이브가 메시지의 생산자)

: 8-byte 데이터, 무조건 프레임, STATE_PID = 0x02 

------------------------------------------------------------------------------------------------

char rx_data[8], tx_data[8];

int dataLength = 0;

 

pidHandle(char PID)

{

if (PID == COMMAND_PID)

{

linStep = RX_STEP;

dataLength = 8;    // 수신할 응답 데이터 길이

}

else if (PID = STATE_PID)

{

linStep = TX_STEP;

dataLength = 4;    // 전송할 응답 데이터 길이

for(i=0; i<8; i++)

tx_data[i] = TX_DATA[i];    // 전송할 데이터 복사

}

else

{

// 노드와 관련 없는 PID 수신, dormant 상태로 전환

.....

}

}

------------------------------------------------------------------------------------------------

 

일견 pidHandle()함수가 간단해 보이지만 아직 완성된 것이 아니다. LIN 2.0 설정과 진단 규약이 아직 구현되지 않았기 때문인데, 설정과 규약은 별도의 강좌로 진행할 예정이다.

pidHandle() 실행 이후, 다음 단계가 RX_STEP이면 추가적인 처리가 없지만, TX_STEP인 경우에는 헤더에 대한 응답으로 데이터 송신을 개시하고, check sum을 계산해야한다.

------------------------------------------------------------------------------------------------

if (RX_BUFFER == pidParity.PID)

{

//패러티 맞음.

rxCount = 0;

pidHandle(pidParity.PID);

checkSum = pidParity.PID;    // LIN2.0 enhanced check sum

 

if (linRxStep == TXSTEP) 

{

UART_TX_ENABLE = ENABLE; // TX enable
txCount = 0;
TX_BUFFER = tx_data[0];    // 첫 데이터 송신

 

checkSum += tx_data;    // checkSum 계산
if (checkSum > 255)
checkSum = checkSum - 256 +1;

}

}

------------------------------------------------------------------------------------------------

여기까지 일단 간단한 PID 수신 처리 함수를 작성하였다. 명심할 것은 아직 LIN 2.0 설정과 진단 규약을 구현하지 않았기 때문에 실제로는 2.0 클러스터에 사용할 수 없다는 것이다. 설정과 진단 규약은 RX_STEP, TX_STEP, CRC_STEP, 오류 처리, time-out 관리 등을 완성한 이후에 설명할 예정이다.

 

강좌 2 끝.

오류가 있거나 궁금한 사항 있으면 댓글이나 연락(87c196mc@daum.net, 017-279-9381) 바랍니다.