티스토리 뷰
앞에서 알아 본 TIMEUTC와 POSLLH 메시지를 분석하여 현재 시간과 위치를 출력해서 제대로 동작되는 프로그램을 만들어 볼 것이다.
위에 보이는 그림처럼 일정한 주기로 보내주는 시간과 위치 정보를 분석하여 콘솔화면에 출력해 보는 것이다.
void setup()
{
uart0.init();
uart1.init();
stdout = std_inout;
_delay_ms(500);
printf("\x1B[2J\x1B[H GPS Test Program\n");
send_cfg_prt();
_delay_ms(100);
gps.cfg_msg_utc();
_delay_ms(100);
gps.cfg_msg_loc();
sei();
}
두개의 UART를 초기화 한다음 UART0로 디버깅 메시지를 출력해주기 위해서 stdout에 대한 설정을 해준다.
static int uart_put(char c, FILE* stream)
{
if (c == '\n')
uart0.put_char('\r');
uart0.put_char(c);
return 0;
}
extern "C"
{
static FILE* std_inout = fdevopen(uart_put, NULL);
}
UART 설정이 완료 되면 '$' 글자가 들어올 때까지 계속 모니터링 하다가 $가 수신되면 GPS모듈이 정상 동작 되고 있다고 판단해서 CFG-PRT 메시지를 송신한다.
static void send_cfg_prt()
{
uint8_t ch;
while (1)
{
if (uart1.get_char(ch))
{
if (ch == '$')
break;
}
}
gps.cfg_prt();
}
뒤 이어 TIMEUTC와 POSLLH 패킷을 주기적으로 보내줄 수 있도록 CFG-MSG 패킷을 연속으로 보낸다.
GPS 모듈이 이 두 메시지를 정상적으로 수신하게 되면 매초 두 메시지를 계속 보내주게 된다.
void Gps::cfg_msg_utc()
{
struct cfg_msg_msg* msg = (struct cfg_msg_msg*)&gps_data[6];
uint32_t pkt_size = sizeof(struct cfg_msg_msg);
make_header(CLASS_CFG, CFG_MSG, pkt_size);
msg->cls_id = CLASS_NAV;
msg->msg_id = NAV_TIMEUTC;
msg->rate = 1;
chksum(pkt_size);
uart1.send_data(gps_data, pkt_size + 8);
}
void Gps::cfg_msg_loc()
{
struct cfg_msg_msg* msg = (struct cfg_msg_msg*)&gps_data[6];
uint32_t pkt_size = sizeof(struct cfg_msg_msg);
make_header(CLASS_CFG, CFG_MSG, pkt_size);
msg->cls_id = CLASS_NAV;
msg->msg_id = NAV_POSLLH;
msg->rate = 1;
chksum(pkt_size);
uart1.send_data(gps_data, pkt_size + 8);
}
GPS로 부터 들어오는 데이터를 수신하기 위해서 인터럽트를 이용하도록 하였다.
ISR(_VECTOR(30))
{
gps.rx_data(uart1.get_char());
}
UART1으로 들어오는 데이터를 처리하는 코드는 아래와 같다.
void Gps::rx_data(uint8_t data)
{
if (prev_data == SYNC1 && data == SYNC2)
{
data_index = 0;
remain_data = 64; // set maximum value
}
else
{
gps_rx_data[data_index++] = data;
if (data_index == 4) // just received payload length
{
remain_data = (gps_rx_data[3] << 8) | gps_rx_data[2];
}
if (remain_data-- == 0)
{
parse_msg();
}
}
prev_data = data;
}
바로 직전에 들어온 데이터가 SYNC1(0xB5)이고 지금 수신된 데이터가 SYNC2(0x62)라면 패킷의 시작이라고 판단하여 data index값을 0으로 초기화 하고 앞으로 수신될 데이터의 크기를 최대값으로 설정해 둔다.
SYNC 데이터 이후에 수신된 데이터는 gps_rx_data 배열에 차례로 저장한다.
수신된 데이터가 4바이트가 되었다면 패킷의 크기까지 수신되었으므로 앞으로 수신될 패킷의 크기를 알수 있다.
마지막 데이터까지 수신이 완료되었으면 어떤 데이터인지 분석한다.
void Gps::parse_msg()
{
if (gps_rx_data[0] == CLASS_NAV)
{
if (gps_rx_data[1] == NAV_POSLLH)
{
struct nav_posllh* msg = (struct nav_posllh*)&gps_rx_data[4];
longitude = msg->longitude;
latitude = msg->latitude;
loc_updated = true;
}
else if (gps_rx_data[1] == NAV_TIMEUTC)
{
struct nav_timeutc* msg = (struct nav_timeutc*)&gps_rx_data[4];
year = msg->year;
month = msg->month;
day = msg->day;
hour = msg->hour;
min = msg->min;
sec = msg->sec;
time_updated = true;
}
else
{
printf("Not supported NAV MSG : %02X\n", gps_rx_data[1]);
}
}
else if (gps_rx_data[0] == CLASS_ACK)
{
printf("ACK : %u\n", gps_rx_data[1]);
}
else
printf("Not supported class ID : %02X\n", gps_rx_data[0]);
}
수신된 데이터의 첫번째와 두번째 데이터를 분석하여 어떤 데이터인지를 알아내어 필요한 데이터를 별도의 변수에 저장한다.
void loop()
{
gps.update();
}
int main(void)
{
setup();
while (1)
loop();
}
초기화가 실행된 이후에는 계속 새로운 데이터가 수신되었는지 확인하여 데이터가 업데이트 되었으면 화면에 출력하면 된다.
void Gps::update()
{
if (time_updated)
{
printf("\x1B[5;H\x1B[2K%4d-%2d-%2d %2d:%2d:%2d",
year, month, day, hour, min, sec);
time_updated = false;
}
if (loc_updated)
{
if (longitude && latitude)
{
int32_t tmp = longitude / 10000000;
printf("\x1B[6;H\x1B[2K%3ld", tmp);
tmp *= 10000000;
tmp = longitude - tmp;
printf(".%ld", tmp / 100000);
tmp = latitude / 10000000;
printf("\x1B[7;H\x1B[2K%3ld", tmp);
tmp *= 10000000;
tmp = latitude - tmp;
printf(".%ld", tmp / 100000);
}
loc_updated = false;
}
}
시간 정보 데이터가 업데이트 된경우 5번째 줄에 연월일, 시분초 순서로 출력한다.
위치정보가 수신된 경우 유효한 데이터인지 확인한 후 경도와 위도를 각각 출력한다.
'프로젝트 > GPS' 카테고리의 다른 글
GPS - CFG:MSG (0) | 2023.01.28 |
---|---|
GPS - NAV:POSLLH (0) | 2023.01.28 |
GPS - NAV:TIMEUTC (0) | 2023.01.28 |
GPS - CFG:PRT (0) | 2023.01.28 |
GPS - UBX (0) | 2023.01.28 |