element14 Community
element14 Community
    Register Log In
  • Site
  • Search
  • Log In Register
  • About Us
  • Community Hub
    Community Hub
    • What's New on element14
    • Feedback and Support
    • Benefits of Membership
    • Personal Blogs
    • Members Area
    • Achievement Levels
  • Learn
    Learn
    • Ask an Expert
    • eBooks
    • element14 presents
    • Learning Center
    • Tech Spotlight
    • STEM Academy
    • Webinars, Training and Events
    • Learning Groups
  • Technologies
    Technologies
    • 3D Printing
    • FPGA
    • Industrial Automation
    • Internet of Things
    • Power & Energy
    • Sensors
    • Technology Groups
  • Challenges & Projects
    Challenges & Projects
    • Design Challenges
    • element14 presents Projects
    • Project14
    • Arduino Projects
    • Raspberry Pi Projects
    • Project Groups
  • Products
    Products
    • Arduino
    • Avnet Boards Community
    • Dev Tools
    • Manufacturers
    • Multicomp Pro
    • Product Groups
    • Raspberry Pi
    • RoadTests & Reviews
  • Store
    Store
    • Visit Your Store
    • Choose another store...
      • Europe
      •  Austria (German)
      •  Belgium (Dutch, French)
      •  Bulgaria (Bulgarian)
      •  Czech Republic (Czech)
      •  Denmark (Danish)
      •  Estonia (Estonian)
      •  Finland (Finnish)
      •  France (French)
      •  Germany (German)
      •  Hungary (Hungarian)
      •  Ireland
      •  Israel
      •  Italy (Italian)
      •  Latvia (Latvian)
      •  
      •  Lithuania (Lithuanian)
      •  Netherlands (Dutch)
      •  Norway (Norwegian)
      •  Poland (Polish)
      •  Portugal (Portuguese)
      •  Romania (Romanian)
      •  Russia (Russian)
      •  Slovakia (Slovak)
      •  Slovenia (Slovenian)
      •  Spain (Spanish)
      •  Sweden (Swedish)
      •  Switzerland(German, French)
      •  Turkey (Turkish)
      •  United Kingdom
      • Asia Pacific
      •  Australia
      •  China
      •  Hong Kong
      •  India
      •  Korea (Korean)
      •  Malaysia
      •  New Zealand
      •  Philippines
      •  Singapore
      •  Taiwan
      •  Thailand (Thai)
      • Americas
      •  Brazil (Portuguese)
      •  Canada
      •  Mexico (Spanish)
      •  United States
      Can't find the country/region you're looking for? Visit our export site or find a local distributor.
  • Translate
  • Profile
  • Settings
Rube Goldberg Device
  • Challenges & Projects
  • Project14
  • Rube Goldberg Device
  • More
  • Cancel
Rube Goldberg Device
Blog The Quantenkompassinator
  • Blog
  • Forum
  • Documents
  • Events
  • Polls
  • Files
  • Members
  • Mentions
  • Sub-Groups
  • Tags
  • More
  • Cancel
  • New
Join Rube Goldberg Device to participate - click to join for free!
  • Share
  • More
  • Cancel
Group Actions
  • Group RSS
  • More
  • Cancel
Engagement
  • Author Author: javagoza
  • Date Created: 9 Aug 2022 6:09 PM Date Created
  • Views 3623 views
  • Likes 15 likes
  • Comments 5 comments
  • tachometer
  • arty-s7
  • fpga-projects
  • AMD XILINX
  • pwm
  • fpga
  • rubegoldbergch
  • digilent
  • spartan-7
  • compass
Related
Recommended

The Quantenkompassinator

javagoza
javagoza
9 Aug 2022
The Quantenkompassinator

The Quantenkompassinator is an over-engineered motorized compass. Built around a Digilent Arty S7 50 board with an AMD Xilinx Spartan-7 FPGA. It is a differential drive mobile robot with PID control that always points in the same direction using Memsic's MMC34160PJ magnetometer, two DC motors and rotational positon and speed feedback with hall effect magnetic sensors.

What is it for? According to our empirical tests, the Quantenkompassinator compass allows us to locate quantum portals inside a house. See demo video:

You don't have permission to edit metadata of this video.
Edit media
x
image
Upload Preview
image

The Quantenkompassinator is a stubborn robot who is determined to always face south. Why south? Very simple, it is very vain and likes to look handsome in the videos.

The following video shows its stubbornness.

You don't have permission to edit metadata of this video.
Edit media
x
image
Upload Preview
image


System description

The system is composed by a differential wheeled robot and a magnetometer.

A differential wheeled robot is a mobile robot whose movement is based on two separately driven wheels placed on either side of the robot body. It can thus change its direction by varying the relative rate of rotation of its wheels and hence does not require an additional steering motion. When the two wheels spin in opposite directions because the two wheels are constrained by the physical mechanism to remain in a fixed geometric relationship to one another, these opposite but equal forward wheel speeds cause the robot to rotate, with both wheels velocities tangent to a circle of diameter centered at the origin of the body-attached frame.

System parts:

  • A magnetometer
  • A small OLED display
  • A motor driver board with 2 H-bridges
  • Two DC motors
  • A Microblaze soft processor
  • PWM motor controller

image

The horizontally positioned magnetometer informs the robot of its orientation. The robot continuously tries to orient the sensor at 0 degrees, towards North by turning the robot along the shortest path.

The turns are made by moving one wheel in one direction and the other in the opposite direction using PWM signals controlled by a PID control system, keeping the center of the robot at the same point, without moving, just rotating.

Four magnetic Hall sensors and two disks with small magnets attached to the shafts of the DC motors give feedback of the position and angular speed of the wheels.

The desired effect is that the robot simulates the movement of the arrow of a classic magnetic compass.

The OLED display is attached to JA Pmod connector and the Pmod CMPS2 compass sensor is attached to the JC Pmod connector on the Digilent Arty S7 board.

North direction on the board is opposite to the arrow direction. The arrow is pointing to the South in the following image.

Quantenkompassinator FPGA Motorized Compass



Bill of materials

If you want to replicate this project you will need:

Product Name Manufacturer Datasheet
Arty S7 50  Digilent  Buy Now
TI-RSLK element14 Buy Now
PmodOLED Digilent Buy Now
PmodCMPS Digilent Buy Now

Pmod CMPS2 : 3-Axis Compass sensor

The Digilent Pmod CMPS2 is a digital compass module using Memsic's MMC34160PJ magnetometer. The local magnetic field strength can be measured in a ±16 Gauss range with a heading accuracy of 1° and up to 0.5 mG of resolution.

{gallery}Digilent Pmod CMPS2

image

image

image

References

  • Pmod CMPS2 Reference Manual - Digilent Reference
  • Pmod CMPS2 schematics (digilent.com)

Digilent PmodOLED

The Digilent Pmod OLED (Revision A) is an organic LED module with a 128×32 pixel display. Primary IC  SSD1306

image

References:

  • Pmod OLED - Digilent Reference
  • Pmod OLED Reference Manual - Digilent Reference
{gallery}Digilent PmodOLED

Digilent PmodOLED unboxing

Digilent PmodOLED unboxing

PmodOLED unboxing

Digilent PmodOLED unboxing

Digilent PmodOLED unboxing

Digilent PmodOLED Front

Digilent PmodOLED Back

Digilent PmodOLED Back

The OLED display is attached to JA Pmod connector and the Pmod CMPS2 compass sensor is attached to the JC Pmod connector on the Digilent Arty S7 board.

image


Robot Chassis

The robot is built using the Pololu Romi chassis, is a differential-drive mobile robot platform with a diameter of 6.5″ (165 mm) and an integrated battery holder for six AA batteries. The drive wheels are located on a diameter of the circular base plate, allowing for turns or spinning in place. A large, fixed ball caster in the rear provides a smooth third point of contact.

image

Reference: Pololu - Romi Chassis Kit - Red


Motor driver and power distribution board

The robot uses the Pololu Motor Driver and Power Distribution Board for Romi Chassis. This motor driver and power distribution board is designed specifically for the Romi chassis as a convenient way to drive the chassis' motors and power the rest of the electronics that make up your robot. It features two DRV8838 motor drivers, one for each of the chassis' motors, and a powerful switching step-down regulator that can supply a continuous 2.5 A at 5 V or 3.3 V.

image

The output driver block consists of Nchannel power MOSFETs configured as an H-bridge to drive the motor winding. An internal charge pump generates needed gate drive voltages.

Reference: Pololu - Motor Driver and Power Distribution Board for Romi Chassis


DRV8838 Low-Voltage H-Bridge Driver

The DRV8838 on the Motor Driver and Power Distribution Board is an H-bridge driver that can drive one dc motor or other devices like solenoids. The outputs are controlled using a PH-EN interface. It includes a low-power sleep mode, which can be enabled using the nSLEEP pin. It integrates the necessary driver FETs and FET control circuitry into a single device. In addition, it adds protection features beyond traditional discrete implementations: undervoltage lockout, overcurrent protection, and thermal shutdown.

image

Reference: DRV8838 data sheet, product information and support | TI.com


H-Bridge Control

The DRV8838 is controlled using a PHASE/ENABLE interface. This interface uses one pin to control the H-Bridge current direction, and one pin to enable or disable the H-Bridge.

Logic table for the DRV8838

nSLEEP PH EN OUT1 OUT2 FUNCTION (DC Motor)
0 X X Z Z Coast
1 X 0 L L Brake
1 1 1 L H Reverse
1 0 1 H L Forward

Reference:

DRV883x Low-Voltage H-Bridge Driver datasheet (Rev. F) (ti.com)


Motor Driver and Power Distribution Board Modifications

To configure the Motor Driver and Power Distribution Board (MDPDB), we need to set the logic levels of the Motor Driver board to the logic levels used in the GPIOs of the Arty S7 board.

Follow these steps to configure the motor driver board for the robot:

  1. Cut the VPU—VREG jumper on the MDPDB
  2. Cut the VCCMD—VREG jumper on the MDPDB
  3. Connect a ground wire from the MDPDB to ground on the Arty S7
  4. Connect VREG (+5V) from the MDPDB to +5V VCC5V0 on the Arty S7
  5. Connect VPU from the MDPDB to 3.3V VCC on the Arty S7. Use VCC form a PMOD header
  6. Connect VCCMD from the MDPDB to 3.3V VCC on the Arty S7. Use VCC form a PMOD header

We can power the Arty S7 board through the VCC5V0 pin (JP13 power select) 

Connect VREG (+5V) from the MDPDB to +5V VCC5V0 on the Arty S7

image

Powering the Arty S7 with the Power Distribution Board 

Powering the Arty S7 with the Motor Driver and Power Distribution Board (MDPDB)

For the reference logic levels we can use the two VCC3V3 (3.3V) outputs from an unused PMOD connector.

image

image

Initially we used this configuration but sometimes when starting the motors the Arty S7 board restarts, so we are definitely powering the Arty S7 board through the USB port using a Powerbank, so the power of the chassis batteries is exclusively for the motors.

Reference: arty_s7_sch-rev_b.pdf(digilent.com)


Arduino/chipKIT Shield Connector

We will design the PWM, DIR, nSLEEP outputs connected to the standard Arduino and chipKIT shield of the Arty S7. The shield connector has 45 pins connected to the FPGA for general purpose Digital I/O. It is possible to use these pins for just about anything including digital read/write, SPI connections, UART connections, I2C connections, and PWM.

Six of these pins (labeled AN0-AN5) can also be used as single-ended analog inputs with an input range of 0V-3.3V, and another four (labeled AN6-9) can be used as differential analog input pairs with an input range of 0V-1.0V.

The Arty S7's shield connector is implemented on headers J1, J2, J3, J5, J6, and J8.

image

MDPDB Arty S7
PWM Right IO0
DIR  Right IO1
nSLEEP Right IO2
PWM Left IO3
DIR Left IO4
nSLEEP Left IO5

nSLEEP signals are no used, but reserved for future uses.


Pulse Width Modulation

We will use PWM to control the speed of the two motors. Pulse Width Modulation (PWM) control or Pulse Duration Modulation (PDM) is a very ingenious method used to control the average power delivered by an electrical signal to a load, by effectively chopping it up into discrete parts. It is a method that is very easy to implement and has high efficiency. The PWM signal is essentially a high frequency (typically greater than 1KHz) square wave. The duty cycle of this square wave is varied to vary the power supplied to the load. The main uses of PWM, but not limited to these, are power delivery control, voltage regulation, audio effects and amplification, dimming lights, modulation of signals in telecommunications, control of motor rotation angle in servomotors.

image

Motor speed control with PWM

The electrical power (P) delivered to the motor is the product of voltage (V), current (C) and duty cycle.

P = V * I * duty

Depending of the polarity of the current and voltage the motors can spin forward or backward.

The motor converts electrical power into mechanical power that delivers torque causing the wheel to spin. Torque is force * distance. 

We will fix the voltage and the current will depend on the friction caused by the mechanical load.

Reference: Pulse-width modulation - Wikipedia


AXI Timer/Counter Intellectual Property (IP)

There are many ways to generate a PWM signal with FPGA. We will use the simplest way a preconfigured IP block. The AXI Timer/Counter is a 32-bit timer module that attaches to the AXI4-Lite interface. In Cascade mode, it can be used as 64-bit timer module. This Xilinx® LogiCORE IP module is provided at no additional cost with the Xilinx Vivado® Design Suite.


Control System

We want to build a control system for our robot. With a control system, we can independently set the rotation speed of each motor. The control system measures speed and uses feedback to adjust the PWM duty cycle of each motor to achieve the desired speed. With the control system, the robot can move in a straight line, run at a desired speed, travel a prescribed distance, or turn at a prescribed angle.

Tachometer

A tachometer is a sensor with digital outputs that relate to rotational speed.

Hall Effect Magnetic Quadrature Encoder

We use the Pololu - Romi Encoder Pair Kit. These quadrature encoders use a magnetic disc and Hall effect sensors to provide 12 counts per revolution of the motor shaft.

The motors used on the Romi have a 120:1 gear reduction and the encoders are connected directly to the motor output shaft and have 12 Counts Per Revolution (CPR). With the provided gear ratio, this nets 1440 counts per wheel revolution. We will use only one sensor output and will count only rising edges so each output pulses 360 times per rotation. If we measure the Period (in sec) of one of the tachometer signals, we can calculate the motor Speed in rpm as

Speed = 360*60/Period

image

The sensors operate from 3.5 V to 18 V and provide digital outputs. The outputs (ELA, ELB, ERA, ERB) are pull up to VPU 3V3 from the Arty S7 board.

image


Differential Motors Position Module.

We have designed a Differential Motors Position Module.

This module that allow us to:

  • Get Speeds :  Measure current angular speeds of motor1 and motor2 (in RPM) and store them in motor speed.
  • Get Distance Traveled: Return mean distance traveled by motor1 and motor2
  • Get Edge Counts: Get sensor and clock edge counts of each motor for computing speed
  • Clear Speed Counters:  Clear the registers storing counts of sensor and clock edges for computing speed
  • Get Motor Positions:  Get current positions of the two motors
  • Get Position Difference: Get current position difference between motor1 and motor2
  • Clear Position Counters: Clear the cumulative position counters for both motors

The design on the FPGA allow us to increment the counters in parallel and not lose any increments and also get the difference of two counters at the same time.

The module has three inputs. Two inputs for the motor sensors and another input to clear the control counters.

image

Inputs

  • clk: Clock
  • hallSensorM1: Sensor input signal from the encoder of motor 1
  • hallSensorM2:  Sensor input signal from the encoder of motor 2
  • clearPosition: Signal for resetting position counters, controlPosition1  and/or controlPosition2 

Outputs

  • speedPosition1: Counter for speed calculation relative to the motor1
  • controlPosition1 : Counter for position control relative to the motor1
  • speedPosition2: Counter for speed calculation relative to the motor2
  • controlPosition2: Counter for position control relative to the motor1
  • differentialPosition: position difference between motor1 and motor2
  • clockCounter: Clock pulse counter.

This module will use two Motor Position Counter modules to keep track of the individual counters of each motor.

Motor Position Counter Module

For each DC motor we have a separate module that keeps track of the counters.

It keeps track of two counters, one for position calculation and one for velocity calculation.

image

Inputs

  • clk: clock
  • hallSensor: hall sensor input signal
  • clearPosition: clearPosition[0] clears position1, // clearPosition[2] clears position2
  • subtractDistance: substract distance from controlPosition
  • distance: distance amount to subtract from controlPosition

Outputs

  • speedPosition: for speed calculation
  • controlPosition: for position control

FPGA Hardware Block Design

The hardware design has been built around the MicroBlaze soft processor.

image


Software Application

Once all the hardware abstraction layers have been created, the application is very simple.

image

First there is a calibration stage in which the robot rotates in left and right directions while sampling the strength of the magnetic field. After the calibration period, if the switch sw03 is on, the robot rotates in order to leave the orientation at 0 degrees (between 6º and -6º). When it achieves the objective, it stops. If the switch sw03 is going to be measuring and showing the data on the display but without moving the robot.

Source code

You can download the libraries with the drivers and the hardware abstraction layer from the github repository. 

/******************************************************************************/
/*                                                                            */
/* compass_app.c -- Differential robot compass app                            */
/*                                                                            */
/******************************************************************************/
/* Author: Enrique Albertos                                                   */
/*                                                                            */
/******************************************************************************/
/* File Description:                                                          */
/* Over-engineered motorized compass made with a FPGA based differential      */
/* drive mobile robot with PID control that always points in the same         */
/* direction using Memsic's MMC34160PJ magnetometer, two DC motors and        */
/* rotational positon and speed feedback with hall effect magnetic sensors.   */
/******************************************************************************/
/* Revision History:                                                          */
/*                                                                            */
/*    06/08/2022 :   Created                                                  */
/*                                                                            */
/******************************************************************************/

#include "compass_app.h"

int compass_app(void);
void CompassInitialize();
int readOrientation(CMPS2_DataPacket* data, PmodCMPS2* myDevice,
		CMPS2_CalibrationData* myCalibrationData, const int myDeclination,
		char** cardinal, char* cardinal_table[]);

void displayOrientation(BotDrivers* botDrivers, int deg, char* cardinal);

PmodCMPS2 myDevice;
CMPS2_CalibrationData myCalibrationData;

const int myDeclination = 1; // Magnetic declination for Zaragoza, Spain: +0.85° (1°51')

const u8 chip_address = 0x30; // I2C chip address

BotDrivers botDrivers;

int compass_app(void) {
	CompassInitialize();
	CompassRun();
	return 0;
}

void CompassRun() {
	char *cardinal_table[] = { "North", "North East", "East", "South East",
			"South", "South West", "West", "North West" };
	char *cardinal;
	int deg;
	CMPS2_DataPacket data;

	CompassClearCalibration(&myCalibrationData);
	OLED_SetCharUpdate(&botDrivers.oled, 0);
	OLED_ClearBuffer(&botDrivers.oled);
	OLED_SetCursor(&botDrivers.oled, 0, 0);
	OLED_PutString(&botDrivers.oled, "   MOTORIZED");
	OLED_SetCursor(&botDrivers.oled, 0, 1);
	OLED_PutString(&botDrivers.oled, "  COMPASS v1.0");
	OLED_SetCursor(&botDrivers.oled, 0, 3);
	OLED_PutString(&botDrivers.oled, "SW3 -> RUN");
	OLED_Update(&botDrivers.oled);
	while (!SWITCHES_DRIVER_poll_switch4(&botDrivers.switchesDriver)) {
	}

	sleep(1);

	for (int i = 0; i < 270; i += 6) {
		DRIVING_DRIVER_turn_left_continuous_degrees(&botDrivers.drivingDriver,
				6);
		deg = readOrientation(&data, &myDevice, &myCalibrationData,
				myDeclination, &cardinal, cardinal_table);
		xil_printf("Degree: %d;  Y Direction: %s\r\n", deg, cardinal);

	}
	DRIVING_DRIVER_delay_until_stop(&botDrivers.drivingDriver);
	for (int i = 0; i < 270; i += 6) {
		DRIVING_DRIVER_turn_right_continuous_degrees(&botDrivers.drivingDriver,
				6);
		deg = readOrientation(&data, &myDevice, &myCalibrationData,
				myDeclination, &cardinal, cardinal_table);
		xil_printf("Degree: %d;  Y Direction: %s\r\n", deg, cardinal);

	}
	DRIVING_DRIVER_delay_until_stop(&botDrivers.drivingDriver);

	while (1) {
		if (SWITCHES_DRIVER_poll_switch4(&botDrivers.switchesDriver)) {
			deg = readOrientation(&data, &myDevice, &myCalibrationData,
					myDeclination, &cardinal, cardinal_table);

			xil_printf("Degree: %d;  Y Direction: %s\r\n", deg, cardinal);
			if (deg > 180 && deg < 354) {
				xil_printf("turn left: %d\r\n", 360 - deg);
				while (deg > 180 && deg < 354) {
					DRIVING_DRIVER_turn_right_continuous_degrees(
							&botDrivers.drivingDriver, 2);
					deg = readOrientation(&data, &myDevice, &myCalibrationData,
							myDeclination, &cardinal, cardinal_table);
					displayOrientation(&botDrivers, deg, cardinal);
				}
				xil_printf("turn left: %d\r\n", 360 - deg);
			} else if (deg > 6 && deg <= 180) {
				while (deg > 6 && deg <= 180) {
					DRIVING_DRIVER_turn_left_continuous_degrees(
							&botDrivers.drivingDriver, 2);
					deg = readOrientation(&data, &myDevice, &myCalibrationData,
							myDeclination, &cardinal, cardinal_table);
					displayOrientation(&botDrivers, deg, cardinal);
				}
				xil_printf("turn right: %d\r\n", deg);
			}

		} else {
			deg = readOrientation(&data, &myDevice, &myCalibrationData,
					myDeclination, &cardinal, cardinal_table);
		}
		displayOrientation(&botDrivers, deg, cardinal);
	}
}

void CompassInitialize() {

	CMPS2_begin(&myDevice, XPAR_PMODCMPS2_0_AXI_LITE_IIC_BASEADDR,
			chip_address);
	usleep(10000);
	CMPS2_SetSensor(&myDevice);
	usleep(10000);
	CMPS2_SetOutputResolution(&myDevice, 0b00);

	xil_printf("COMPASS application Started\r\n");

	BOT_init(&botDrivers);

}

int readOrientation(CMPS2_DataPacket* data, PmodCMPS2* myDevice,
		CMPS2_CalibrationData* myCalibrationData, const int myDeclination,
		char** cardinal, char* cardinal_table[]) {

	int deg;
	*data = CMPS2_GetData(&*myDevice);
	CompassCalibrate(&*myDevice, &*myCalibrationData, *data);
	deg = CompassConvertDegree(&*myDevice, *myCalibrationData, *data,
			myDeclination);
	*cardinal = CompassGetCardinalDirectionString(deg, cardinal_table);
	return deg;
}

void displayOrientation(BotDrivers* botDrivers, int deg, char* cardinal) {
	OLED_SetCharUpdate(&botDrivers->oled, 0);
	OLED_ClearBuffer(&botDrivers->oled);
	OLED_SetCursor(&botDrivers->oled, 0, 0);
	OLED_PutString(&botDrivers->oled, cardinal);
	OLED_SetCursor(&botDrivers->oled, 0, 1);
	OLED_putIntVariable(&botDrivers->oled, deg);
	OLED_PutString(&botDrivers->oled, " degrees");
	OLED_SetCursor(&botDrivers->oled, 0, 3);
	OLED_PutString(&botDrivers->oled, "SW3 -> STOP");
	OLED_Update(&botDrivers->oled);
}

int CompassConvertDegree(PmodCMPS2 *InstancePtr, CMPS2_CalibrationData calib,
		CMPS2_DataPacket data, int declination) {
	int tx, ty;
	int deg;

	if (data.x < calib.mid.x)
		tx = (calib.mid.x - data.x);
	else
		tx = (data.x - calib.mid.x);

	if (data.y < calib.mid.y)
		ty = (calib.mid.y - data.y);
	else
		ty = (data.y - calib.mid.y);

	if (data.x < calib.mid.x) {
		if (data.y > calib.mid.y)
			deg = 90 - atan2f(ty, tx) * 180 / 3.14159;
		else
			deg = 90 + atan2f(ty, tx) * 180 / 3.14159;
	} else {
		if (data.y < calib.mid.y)
			deg = 270 - atan2f(ty, tx) * 180 / 3.14159;
		else
			deg = 270 + atan2f(ty, tx) * 180 / 3.14159;
	}

	deg += declination;

	while (deg >= 360)
		deg -= 360;
	while (deg < 0)
		deg += 360;

	return deg;
}

void CompassClearCalibration(CMPS2_CalibrationData *calib) {
	calib->max.x = 0x8000; // Center point of 0x0000 -> 0xFFFF
	calib->max.y = 0x8000;
	calib->max.z = 0x8000;
	calib->min.x = 0x8000;
	calib->min.y = 0x8000;
	calib->min.z = 0x8000;
	calib->mid.x = 0x8000;
	calib->mid.y = 0x8000;
	calib->mid.z = 0x8000;
}

void CompassCalibrate(PmodCMPS2 *InstancePtr, CMPS2_CalibrationData *calib,
		CMPS2_DataPacket data) {
	if (data.x > calib->max.x)
		calib->max.x = data.x; // Track maximum / minimum
	if (data.y > calib->max.y)
		calib->max.y = data.y; // value seen per axis
	if (data.z > calib->max.z)
		calib->max.z = data.z;
	if (data.x < calib->min.x)
		calib->min.x = data.x;
	if (data.y < calib->min.y)
		calib->min.y = data.y;
	if (data.z < calib->min.z)
		calib->min.z = data.z;
	calib->mid.x = (calib->max.x >> 1) + (calib->min.x >> 1); // Find average
	calib->mid.y = (calib->max.y >> 1) + (calib->min.y >> 1);
	calib->mid.z = (calib->max.z >> 1) + (calib->min.z >> 1);
}

char *CompassGetCardinalDirectionString(int deg, char *cardinal_table[]) {
	float fdeg = deg;
	if (fdeg > 337.5)
		fdeg -= 337.5;
	else
		fdeg += 22.5;
	fdeg /= 45.0;
	return cardinal_table[(int) fdeg];
}

Conclusion

It has been a very fun project. We have taken advantage of everything we have learned in the last two months about FPGAs by participating in element14's "7 Ways to Leave Your Spartan-6" program. You can see the summary with everything we learned in "7 Ways to Leave Your Spartan-6 FPGA" program. My summary. - Blog - FPGA - element14 Community

  • Sign in to reply

Top Comments

  • beacon_dave
    beacon_dave over 3 years ago +1
    Reminds me a bit of the 'south-pointing chariot' that often features in educational projects using differential gears. https://en.wikipedia.org/wiki/South-pointing_chariot
Parents
  • Andrew J
    Andrew J over 3 years ago

    Pretty amazing - looks like a lot of work went into it.

    • Cancel
    • Vote Up 0 Vote Down
    • Sign in to reply
    • More
    • Cancel
Comment
  • Andrew J
    Andrew J over 3 years ago

    Pretty amazing - looks like a lot of work went into it.

    • Cancel
    • Vote Up 0 Vote Down
    • Sign in to reply
    • More
    • Cancel
Children
No Data
element14 Community

element14 is the first online community specifically for engineers. Connect with your peers and get expert answers to your questions.

  • Members
  • Learn
  • Technologies
  • Challenges & Projects
  • Products
  • Store
  • About Us
  • Feedback & Support
  • FAQs
  • Terms of Use
  • Privacy Policy
  • Legal and Copyright Notices
  • Sitemap
  • Cookies

An Avnet Company © 2025 Premier Farnell Limited. All Rights Reserved.

Premier Farnell Ltd, registered in England and Wales (no 00876412), registered office: Farnell House, Forge Lane, Leeds LS12 2NE.

ICP 备案号 10220084.

Follow element14

  • X
  • Facebook
  • linkedin
  • YouTube