GNSS Emulator using STM32 MCU
Last update: 2022-05-07
Table of Content
Requirements#
The emulator should output:
-
PPS signal at 1Hz, 25% duty cycle
-
GPRMC
at 1Hz, about 83 bytes per message -
GNGGA
at 1Hz, about 86 bytes per message -
OBSVMA
at 1Hz, about 2700 bytes per message -
All messages are aligned to the rising edge of PPS signal.
Outputs are concurrent.
Configuration#
The main MCU is STM32F103C8Tx, mounted on a Blue Pill dev board.
PPS Signal#
PPS signal will be output by PWM function of the Timer 1 module:
- Clock source:
Internal Clock, at 72 MHz
- Channel 1:
PWM Generation CH1
- Pre-scaler
PSC
:36000-1
- Counter Period
AAR
:2000-1
- PWM Generation Channel 1:
- Mode:
1
(Positive at start) - Pulse:
500
(25% of AAR)
- Mode:
- Interrupt:
on Update
UART Protocol#
Using a setting of 8-N-1 (8 bit, No parity, 1 stop bit), each byte requires 10 bits.
Baud rate at 115200 bit-per-second means the speed is 11520 byte-per-second.
At this speed, 4 OBSVMA
messages can be transfer completely in a second.
All messages on UART ports will be sent in DMA mode which allows sending data concurrently. Interrupt must be enabled to make DMA function work properly.
The UART1, UART2, UART3 are configured as:
- Mode:
Asynchronous
- Hardware Flow Control:
Disabled
- Baud Rate:
115200
bps,8-N-1
- Interrupt:
Enabled
- DMA:
- Channel:
TX
- Direction:
Memory to Peripheral
, - Mode:
Normal
- Data width:
Byte
- Increment Address:
Memory
only
- Channel:
Messages#
All messages are pre-filled, only some necessary parts will be modified to reduce processing time.
- UTC Time must be updated every second in
GPRMC
andGNGGA
GPRMC
message must have correct CRC value
GPRMC:
$GPRMC,000000.00,A,2057.59811106,N,10546.17288672,E,0.109,193.8,200821,1.5,W,A*21
GNGGA:
$GNGGA,000000.00,2057.59811106,N,10546.17288672,E,1,18,2.2,16.5017,M,-28.2478,M,,*61
OBSVMA:
#OBSVMA,92,GPS,FINE,2172,171764000,0,0,18,4;36,0,6,21338531.558,...,02331d40*a302f998
OBSVMA is a long message, at least 2600 bytes
Implementation#
Messages
- Messages are predefined. Only timestamp bytes and CRC need to be updated. Pre-calculate an CRC byte except timestamp bytes at startup.
- When timestamp is update, new CRC is calculated using pre-calculated CRC and timestamp bytes. This reduces time of updating message.
- All messages will be sent using DMA mode to not block each other.
Example for GPRMC messages:
uint8_t GPRMC_Buffer[]="$GPRMC,000000.00,A,2057.59811106,N,10546.17288672,E,0.109,193.8,200821,1.5,W,A*21\r\n";
uint32_t GPRMC_Length = 0;
uint8_t GPRMC_CRC_Old = 0; // CRC except time, calculated once at startup
uint8_t GPRMC_CRC_New = 0; // CRC New = CRC Old + Timestamps[]
Main loop will send 3 messages using DMA:
int main(void)
{
GPRMC_Length = strlen((char*)GPRMC_Buffer);
GPRMC_CRC_Old = CalculateCRC(GPRMC_Buffer, GPRMC_Length);
while (1)
{
if (PPS_flag == 1) {
UpdateTime(GPRMC_Buffer+7, &Clock);
GPRMC_CRC_New = RecalculatedCRC(GPRMC_Buffer+7, 6, &GPRMC_CRC_Old);
UpdateCRC(GPRMC_Buffer+79, &GPRMC_CRC_New);
HAL_UART_Transmit_DMA(&huart1, GPRMC_Buffer, GPRMC_Length);
... other code ...
HAL_UART_Transmit_DMA(&huart2, GNGGA_Buffer, GNGGA_Length);
... other code ...
HAL_UART_Transmit_DMA(&huart3, OBSVMA_Buffer, OBSVMA_Length);
}
}
}
Result: