Measuring time accurately has through out history presented a number of challenges. It was only with the John Harrison's design of a reliable clock which could maintain a reference time at the Greenwich Meridian which enable accurate navigation in the 1700's and the charting of as yet unexplored seas and lands.
Today we live in increasingly interconnected world and a accurate time reference is just as critical. Without a common time reference cell towers cannot easily synchronize codes and hand off calls as we pass from one tower to the next. Similarly time-stamping business and banking transactions without a common time reference can also be challenging and become an arbitration nightmare as to the sequence of events.
Of course the most accurate clocks available are atomic clocks, sadly however, they are also the most expensive, which limits the ability of institutions to use them.
There is however a time reference which is accurate to +/-10 ns and can be pulled from the air for free and that is the time reference provided by Global Navigation Satellite System. Of course, the most well known of these is the Global Positioning System (GPS) although there are several e.g. GLONASS and Galileo.
All of these satellites contain atomic clocks, which enable us to achieve a highly accurate time reference across systems.
In this project we are going to use a GPS receiver Pmod to receive a GPS signal and process it such that we can display the time on a binary clock.
To do this we will be using Vivado and SDK, this will be the last project we use SDK for now that Vitis is available.
Vivado DesignTo get started with the design the first thing we need to do is install the Digilent library to enable us to configure and communicate with the PmodGPS/
This library supports Pmod use in both hardware and software development in Vivado and SDK.
If you do not have the Pmod libraries installed you can install these from here. Once you have them on your system the next step is to make them visible to our Vivado design.
To do this in our Vivado project select, project options and select IP and navigate to the new IP repository.
We also need to something to drive the NeoPixel display, again for a previous project I pushed a neopixel IP module to my github here
We are then able to create a Vivado project which will make use of these IP modules to talk to the PmodGPS.
Within a new Vivado project we need the following IP
- Zynq PS - Configured for the Minized
- PmodGPS - Will communicate with the PmodGPS under SW control
- AXI BRAM Controller - enables the Zynq PS to read and write BRAM in the PL
- BRAM - Dual port BRAM contains the Neo Pixel Values
- NeoPixel IP - Drives out the Neo Pixel drive signal
- Processor Reset Block - Provides reset for the system
- AXI Interconnect - enables multiple AXI slaves to connect to a single PS master
The PS needs to be configured to provide
- Fabric clock at 50 MHz
- Fabric clock at 20 MHz - Used to clock the Neo Pixel IP
- GP Master AXI interface
- Fabric Interrupts enabled
This should result in a block diagram as shown below
Before we can generate and export the bit stream we also need to set the IO location of the pins.
I am going to use Pmod2 on the Minized for the PmodGPS and Pmod1 for the Neo Pixel drive we do this in a XDC file as below.
set_property PACKAGE_PIN P13 [get_ports PMOD2_pin1_io]
set_property IOSTANDARD LVCMOS33 [get_ports PMOD2_pin1_io]
set_property PACKAGE_PIN P14 [get_ports PMOD2_pin2_io]
set_property IOSTANDARD LVCMOS33 [get_ports PMOD2_pin2_io]
set_property PACKAGE_PIN N11 [get_ports PMOD2_pin3_io]
set_property IOSTANDARD LVCMOS33 [get_ports PMOD2_pin3_io]
set_property PACKAGE_PIN N12 [get_ports PMOD2_pin4_io]
set_property IOSTANDARD LVCMOS33 [get_ports PMOD2_pin4_io]
set_property PACKAGE_PIN P15 [get_ports PMOD2_pin7_io]
set_property IOSTANDARD LVCMOS33 [get_ports PMOD2_pin7_io]
set_property PACKAGE_PIN R15 [get_ports PMOD2_pin8_io]
set_property IOSTANDARD LVCMOS33 [get_ports PMOD2_pin8_io]
set_property PACKAGE_PIN R12 [get_ports PMOD2_pin9_io]
set_property IOSTANDARD LVCMOS33 [get_ports PMOD2_pin9_io]
set_property PACKAGE_PIN R13 [get_ports PMOD2_pin10_io]
set_property IOSTANDARD LVCMOS33 [get_ports PMOD2_pin10_io]
set_property PACKAGE_PIN L15 [get_ports dout_0]
set_property IOSTANDARD LVCMOS33 [get_ports dout_0]
With that we can implement the design and export the hardware design to SDK to generate the SW required.
Software DesignWithin SDK we need to create a new application and BSP based on the hardware design we have just exported from Vivado.
Once this has been created within the BSP we should see the Pmod Driver within the BSP MSS file.
For a change our software application is going to be interrupt driven.
The architecture our software solution will follow is
- Configure and initialize the PmodGPS
- Configure and initialize the BRAM controller
- Configure the Interrupt controller and enable interrupts from the PmodGPS UART.
- Wait for the PmodGPS to lock on to the signal
- Read the Location and timing data from the PmodGPS
- Convert the time to into binary elements for display
- Update the Neo Pixel display
To make use of the PmodGPS and AXI BRAM controller we can use the APIs provided within PmodGPS.h and xbram.h
ConfigPtr = XBram_LookupConfig(XPAR_BRAM_0_DEVICE_ID);
XBram_CfgInitialize(&Bram, ConfigPtr,ConfigPtr->CtrlBaseAddress);
print("GPS Set Up\n\r");
GPS_begin(&GPS, XPAR_PMODGPS_0_AXI_LITE_GPIO_BASEADDR,XPAR_PMODGPS_0_AXI_LITE_UART_BASEADDR, PERIPHERAL_CLK);
SetupInterruptSystem(&GPS, XPAR_PS7_SCUGIC_0_DEVICE_ID, XPAR_FABRIC_PMODGPS_0_GPS_UART_INTERRUPT_INTR);
GPS_setUpdateRate(&GPS, 1000);
While the interrupt controller can be configured using the APIs provided by xscugic.h
int SetupInterruptSystem(PmodGPS *InstancePtr, u32 interruptDeviceID, u32 interruptID) {
int Result;
u16 Options;
INTC *IntcInstancePtr = &intc;
XScuGic_Config *IntcConfig;
IntcConfig = XScuGic_LookupConfig(interruptDeviceID);
if (NULL == IntcConfig) {
return XST_FAILURE;
}
Result = XScuGic_CfgInitialize(IntcInstancePtr, IntcConfig,
IntcConfig->CpuBaseAddress);
if (Result != XST_SUCCESS) {
return XST_FAILURE;
}
XScuGic_SetPriorityTriggerType(IntcInstancePtr, interruptID, 0xA0, 0x3);
Result = XScuGic_Connect(IntcInstancePtr, interruptID,
(Xil_ExceptionHandler) XUartNs550_InterruptHandler,
&InstancePtr->GPSUart);
if (Result != XST_SUCCESS) {
return Result;
}
XScuGic_Enable(IntcInstancePtr, interruptID);
XUartNs550_SetHandler(&InstancePtr->GPSUart, (void*) GPS_intHandler,
InstancePtr);
Xil_ExceptionInit();
Xil_ExceptionRegisterHandler(XIL_EXCEPTION_ID_INT,
(Xil_ExceptionHandler) INTC_HANDLER, IntcInstancePtr);
Xil_ExceptionEnable() ;
Options = XUN_OPTION_DATA_INTR | XUN_OPTION_FIFOS_ENABLE;
XUartNs550_SetOptions(&InstancePtr->GPSUart, Options);
return XST_SUCCESS;
}
The first thing I wanted to do was to ensure the PmodGPS would lock on to the signal and provide me with an accurate location.
if (GPS.ping) {
GPS_formatSentence(&GPS);
if (GPS_isFixed(&GPS)) {
xil_printf("UTC %d \n\r",time);
xil_printf("Latitude: %s\n\r", GPS_getLatitude(&GPS));
xil_printf("Longitude: %s\n\r", GPS_getLongitude(&GPS));
xil_printf("Altitude: %s\n\r", GPS_getAltitudeString(&GPS));
xil_printf("Number of Satellites: %d\n\n\r", GPS_getNumSats(&GPS));
} else {
xil_printf("Number of Satellites: %d\n\r", GPS_getNumSats(&GPS));
}
When this code is executed on the Minized with the PmodGPS attached to Pmod2 we see the following output on the terminal giving me a location.
To check this is the correct location, entering it into google maps provides the location of my office to a reasonable accuracy.
With the ability to lock on to the GPS signal, the code was updated to provide the time. This time is known as Coordinated Universal Time or UTC for short, UTC references Greenwich Mean Time which at the moment also happens to be the same time as in the UK.
The time is output from the PmodGPS as a floating number, as can be seen below in the terminal output.
To be able to create a binary clock we need to break this down into the following
- Hours Tens
- Hours Units
- Minutes Tens
- Minutes Units
- Seconds Tens
- Seconds Units
This will enable us to plot the time using the Neo Pixel Array.
A binary clock presents the time as below
On the Neo Pixel I am going to use an array of 6 by 4 LEDs in the middle of the array.
But first we need to convert the UTC time into the format required we can use the approach below to split a time such as 153400 into the correct hours, minutes and seconds grouping.
hours = time / 10000;
minutes = (time - (hours * 10000)) / 100 ;
seconds = time - ((hours * 10000) + (minutes *100));
We can then split each element into the tens and units as required for the binary time display.
hrs_tens = hours / 10;
hrs_units = (hours > 10) ? hours - (hrs_tens*10) : hours;
min_tens = minutes / 10;
min_units = (minutes > 10) ? minutes - (min_tens*10) : minutes;
sec_tens = seconds / 10;
sec_units = (seconds > 10) ? seconds - (sec_tens*10) : seconds;
Once we have separated the numbers we then need to convert the value into a binary one, for this I created a simple sub routine
int * dec2bin(int n)
{
int i;
static int a[4] ={0,0,0,0};
for(i=0;i<4;i++){
a[i]=0;
}
for(i=0;n>0;i++)
{
a[i]=n%2;
n=n/2;
}
return a;
}
This function returns a four bit vector representing the binary number, for this example we do not need more than 4 bits.
As the Neo Pixel array is seen as one long 64 Neo Pixel element, for each of the count positions we just need to change the led offset position.
Depending upon if the bit is set or not the colour of the LED then changed, a set bit is green otherwise it is blue.
I used the code below to determine the value to set the LED as the binary conversion function returns a pointer.
clock[10+i] = (*(binary + i)==1)?0x0f0000 : 0x000007;
When I put all this together the binary clock was functioning as intended once the PmodGPS locked on the signal.
VerificationReading a Binary clock maybe different for many people to a traditional clock. Therefore looking at the Neo Pixel display and the UTC time output over the terminal we can verify the display is correct.
When decoded the time looks correct.
ConclusionThis project demonstrates how we can easily and quickly make use of GPS for systems other than navigation and create a fun example which can be used as a display etc.
NoteThe PmodGPS may take several minutes to lock on if indoors.
You can find the files associated with this project here:
https://github.com/ATaylorCEngFIET/Hackster
See previous projects here.
More on on Xilinx using FPGA development weekly at MicroZed Chronicles.
Comments