RP Lidar A1 MCU환경에서 Uart통신으로 사용하기

PassionYim·2024년 3월 27일
0

자율주행 모형차

목록 보기
1/5

2021년 월초에 했었던 프로젝트를 정리하면서, 과정들을 다시 업로드 시작.

What is RP Lidar A1

2차원 360도의 거리정보를 측정하는 센서

Pin Setting

How to Use

Uart통신을 사용해서 MCU와 동작함

연결 후, 자동으로 데이터를 보내주지 않기 때문에 통신을 해야함.

Request 요청 : 호스트 -> Lidar (데이터를 주는 방향)

Response 응답 : Lidar -> 호스트

이 센서에는 3가지 모드가 존재 함.

  1. 요청 / 응답 모드

    스캔을 시작하기 전 or Lidar가 스캔 중 다른 일을 하고 있을 때

  2. 요청 / 다수 응답 모드

    스캔을 시작하고, 데이터를 받을 때

  3. 요청 / 응답 없음 모드

    Stop or Reset을 할 때

동작 방식

데이터 요청 패킷

Star Flag는 0xA5로 고정. Command로만 scan, stop, reset 결정.

데이터 응답 패킷

주요 동작 상태, 천이 조건

System Power Up : 전원이 들어온 후, Idle(대기 상태)

Request Processing : 데이터 요청 패킷을 받은 후, 처리하는 과정. 이 과정동안 스캔 작업, 데이터 송신을 하지않음.

Scannig : 측정 시작. 거리를 측정하고, 결과값을 계속해서 전송. 모터의 회전 속도가 안정할 때에만 거리를 측정함.

스캔 상태

요청 command

STOP


측정 시스템을 종료하고, Idle 상태로 들어감. 다른 요청을 위해서는 최소 1ms 대기.

Reset

Lidar 전원을 리셋. 다른 요청을 위해서는 최소 2ms 대기.

Scan

전송되는 데이터 패킷.

(패킷이 의미하는 값)

Code

위의 설명을 토대로 XMC4500환경에서 펌웨어 개발.

동작상태의 요청에 따라 기능을 수행하는 함수

: 스케쥴러 함수에서 1ms마다 동작 시켰음.
(센서는 0.5ms마다 측정가능하지만, 스케쥴러의 타이머가 1ms마다 동작하게 했기 때문에)

void TaskIdle(){

	switch(test_cmd)
		{
			case 1:
				RPLidar_Scan();
				test_cmd = 0;
				break;
			case 2:
				RPLidar_Stop();
				test_cmd = 0;
				break;
			case 3:
				RPLidar_Reset();
				test_cmd = 0;
				break;
			default:
				break;
		}
		RPLidar_Process();

}

Scan, Stop, Reset

Scan에서 송신하는 Tx_Data의 바이트들은 0xA5, 0x20이다.

void RPLidar_Scan()
{
	while(UART_IsTxBusy(RPLidar.handle) == true) {};
	uint8_t _Tx_Data[] = {RPLIDAR_CMD_SYNC_BYTE, RPLIDAR_CMD_SCAN};
	UART_Transmit(RPLidar.handle, _Tx_Data, 2);
	RPLidar.Rx_Size = 5;
	RPLidar.Mode = scan;
}

void RPLidar_Stop()
{
	while(UART_IsTxBusy(RPLidar.handle) == true) {};
	uint8_t _Tx_Data[] = {RPLIDAR_CMD_SYNC_BYTE, RPLIDAR_CMD_STOP};
	UART_Transmit(RPLidar.handle, _Tx_Data, 2);
	RPLidar.Mode = stop;
}

void RPLidar_Reset()
{
	while(UART_IsTxBusy(RPLidar.handle) == true) {};
	UART_ClearRXFIFOStatus(RPLidar.handle, XMC_USIC_CH_RXFIFO_EVENT_STANDARD);
	uint8_t _Tx_Data[] = {RPLIDAR_CMD_SYNC_BYTE, RPLIDAR_CMD_RESET};
	UART_Transmit(RPLidar.handle, _Tx_Data, 2);
	RPLidar.Mode = reset;
}

데이터 응답을 송신한 후, 값 저장하는 함수

void RPLidar_Process()
{
	if(RPLidar.Mode != scan){
		test_cmd = 1;
	}
	if(RPLidar.Trans_hex2dec_flag)
	{
		uint16_t temp_angle = (((uint16_t)Rx_data[2]<<7)|((Rx_data[1]>>1)&0x7F));
		temp_angle = (float)temp_angle/64.0;
		if(temp_angle < 360)	// 360' 이상의 각도는 에러라고 판단
		{
			RPLidar.angle = temp_angle;
		}

		float temp_distance = ((uint16_t)Rx_data[4]<<8|Rx_data[3]);
		temp_distance = temp_distance/4/10;		// 단위 : cm

		if((temp_distance < MAX_DISTANCE) && (temp_distance > MIN_DISTANCE))
		{
			RPLidar.distance[RPLidar.angle] = temp_distance;
		}
		else
		{
			RPLidar.distance[RPLidar.angle] = MAX_DISTANCE;
		}
		RPLidar.Trans_hex2dec_flag = false;
	}
}

Dave개발환경에서 인터럽트 콜백함수 정의

void CB_Transmit_End()
{
	if(RPLidar.Mode == scan)
	{
		UART_Receive(RPLidar.handle, Rx_response_descript, 7);
	}
}

데이터를 송신한 후, 응답 패킷을 받아야 함.
응답 패킷을 받은 이후, 데이터 패킷을 받음.
(스캔모드에서만 하는 이유는, 다른모드에서는 응답 패킷을 송신하지 않기 때문에)

void CB_Receive_End()
{
	switch(RPLidar.Mode)
	{
		case scan:
			RPLidar.Trans_hex2dec_flag = true;
			UART_Receive(RPLidar.handle, Rx_data, RPLidar.Rx_Size);
			break;
		case stop:
			Set_UART_RXFIFOEmpty();
			RPLidar.Mode = idle;
			break;
		case reset:
			Set_UART_RXFIFOEmpty();
			RPLidar.Mode = idle;
			break;
		default:
			break;
	}
}

데이터 출력

(포스트의 사진은 RP Lidar A1의 Protocol Datasheet에서 캡쳐함)

profile
열정이 전부였던 개발자

0개의 댓글

관련 채용 정보