diff --git a/firmware/inc/bmp180.h b/firmware/inc/bmp180.h new file mode 100644 index 0000000..ebd08ed --- /dev/null +++ b/firmware/inc/bmp180.h @@ -0,0 +1,42 @@ +/* + * BMP180 (also BMP085) + * Copyright (C) 2014 Richard Meadows + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#ifndef BMP180_H +#define BMP180_H + +#include "samd20.h" + +/** + * Barometer data structure + */ +struct barometer { + double temperature; + int32_t pressure; + int valid; // 1 = valid, 0 = invalid +}; + +struct barometer* get_barometer(void); +void bmp180_init(void); + +#endif /* BMP180_H */ diff --git a/firmware/src/bmp180.c b/firmware/src/bmp180.c new file mode 100644 index 0000000..85ac114 --- /dev/null +++ b/firmware/src/bmp180.c @@ -0,0 +1,303 @@ +/* + * BMP180 (also BMP085) + * Copyright (C) 2014 Richard Meadows + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#include +#include + +#include "samd20.h" +#include "bmp180.h" +#include "sercom/i2c_master.h" + + +#define BMP180_ADDRESS 0xEE +#define BMP180_REG_ID 0xD0 +#define BMP180_REG_SOFTRESET 0xE0 +#define BMP180_REG_CTRLMEAS 0xF4 +#define BMP180_REG_DATA 0xF6 + +/** + * Control Register Values + */ +typedef enum { + TEMPERATURE = 0x2E, + PRESSURE_ULTRALOW = 0x34, // 4500µS Delay + PRESSURE_STANDARD = 0x74, // 7500µS Delay + PRESSURE_HIGHRES = 0xB4, // 13500µS Delay + PRESSURE_ULTRAHIGHRES = 0xF4 // 22500µS Delay +} bmp085_command; + +/** + * The mode of pressure measurement + */ +#define PRESSURE_MODE PRESSURE_HIGHRES// PRESSURE_STANDARD +#define PRESSURE_DELAY 13500//7500 + +#define TEMPERATURE_DELAY 4500 + +/** + * Barometer data structure + */ +struct barometer barometer; + +/** + * Calibration Values + */ +struct calibration { + int16_t AC1, AC2, AC3, B1, B2, MB, MC, MD; + uint16_t AC4, AC5, AC6; +} calibration; + +/** + * Lookup table for oversampling values. + */ +uint8_t oversampling(void) { + switch(PRESSURE_MODE) { + case PRESSURE_ULTRALOW: + return 0; + case PRESSURE_HIGHRES: + return 2; + case PRESSURE_ULTRAHIGHRES: + return 3; + default: // PRESSURE_STANDARD + return 1; + } +} + +/** + * Implements a microsecond delay + */ +void delay_us(uint16_t microseconds) { + int32_t i = microseconds * 16; + + while(i--); +} + + +void master_transfer(uint16_t address, uint8_t* data, uint16_t data_length, + uint8_t read_not_write) +{ + uint32_t timeout = 0; + struct i2c_master_packet packet = { + .address = address, + .data_length = data_length, + .data = data, + .ten_bit_address = false, + .high_speed = false, + .hs_master_code = 0x0, + }; + + if (read_not_write) { + while (i2c_master_read_packet_wait(&packet) != + I2C_STATUS_OK) { + /* Increment timeout counter and check if timed out. */ + if (timeout++ > 1000) { + break; + } + } + } else { + + while (i2c_master_write_packet_wait(&packet) != + I2C_STATUS_OK) { + /* Increment timeout counter and check if timed out. */ + if (timeout++ > 1000) { + break; + } + } + + } +} + +/** + * Utility function to read from a 8-bit register + */ +uint8_t read_8(char address) { + uint8_t buffer[3]; + buffer[0] = address; + + /* Set regsiter to read */ + master_transfer(BMP180_ADDRESS >> 1, buffer, 1, false); + + /* Read it */ + master_transfer(BMP180_ADDRESS >> 1, buffer, 1, true); + + return buffer[0]; +} +/** + * Utility function to read a 16-bit register. + */ +uint16_t read_16(char address) { + uint8_t buffer[3]; + buffer[0] = address; + + /* Set regsiter to read */ + master_transfer(BMP180_ADDRESS, buffer, 1, false); + + /* Read it */ + master_transfer(BMP180_ADDRESS | 1, buffer, 2, true); + + return (buffer[0] << 8) | buffer[1]; +} +/** + * Reads off the BMP180's calibration values. + */ +void get_cal_param(struct calibration *c) { + c->AC1 = read_16(0xAA); + c->AC2 = read_16(0xAC); + c->AC3 = read_16(0xAE); + c->AC4 = read_16(0xB0); + c->AC5 = read_16(0xB2); + c->AC6 = read_16(0xB4); + c->B1 = read_16(0xB6); + c->B2 = read_16(0xB8); + c->MB = read_16(0xBA); + c->MC = read_16(0xBC); + c->MD = read_16(0xBE); +} +/** + * Writes a command to the BMP085's control register. + */ +void write_command(bmp085_command command) { + + uint8_t buffer[3]; + buffer[0] = BMP180_REG_CTRLMEAS; + buffer[1] = command; + + /* Write to command register */ + master_transfer(BMP180_ADDRESS, buffer, 2, true); +} + + +/** + * Takes a temperature measurement and returns the uncompensated value. + */ +int32_t get_ut(void) { + write_command(TEMPERATURE); + + delay_us(TEMPERATURE_DELAY); + + return read_16(BMP180_REG_DATA); +} +/** + * Takes a pressure measurement and returns the uncompenstated value. + */ +int32_t get_up(void) { + uint8_t buffer[3]; + write_command(PRESSURE_MODE); + + delay_us(PRESSURE_DELAY); + + + buffer[0] = BMP180_REG_DATA; + + /* Set regsiter to read */ + master_transfer(BMP180_ADDRESS, buffer, 1, false); + + /* Read it */ + master_transfer(BMP180_ADDRESS | 1, buffer, 3, true); + + return ((buffer[0] << 16) | (buffer[1] << 8) | + buffer[2]) >> (8 - oversampling()); + +} + +/* ----------------------------------------------------------------------------- + * Calculations + */ + + +/** + * Returns the variable B5, which is used for both temperature and + * pressure calculations. + */ +int32_t get_b5(struct calibration *c, int32_t ut) { + int32_t x1, x2; + + x1 = ((ut - c->AC6) * c->AC5) >> 15; + x2 = (double)(c->MC << 11) / (x1 + c->MD); + return x1 + x2; // B5 +} +/** + * Returns the temperature in °C using variable B5 + */ +double get_temperature(int32_t B5) { + return (double)((B5 + 8) >> 4) / 10; +} +/** + * Returns the pressure in pascals using the calibration and variable + * B5. + */ +int32_t get_pressure(struct calibration *c, int32_t B5, int32_t up) { + int64_t B6, X1, X2, X3, B3, pressure; + uint64_t B4, B7; + + B6 = B5 - 4000; + X1 = (c->B2 * ((B6 * B6) >> 12)) >> 11; + X2 = (c->AC2 * B6) >> 11; + X3 = X1 + X2; + B3 = ((((((int64_t)(c->AC1) * 4) + X3) << oversampling()) + 2) >> 2); + X1 = (c->AC3 * B6) >> 13; + X2 = (c->B1 * ((B6 * B6) >> 12)) >> 16; + X3 = ((X1 + X2) + 2) >> 2; + B4 = (c->AC4 * (uint64_t)(X3 + 32768)) >> 15; + B7 = ((uint64_t)(up - B3) * (50000 >> oversampling())); + + if (B7 < 0x80000000) { + pressure = (B7 << 1) / B4; + } else { + pressure = (B7 / B4) << 1; + } + + X1 = (pressure >> 8); + X1 *= X1; + X1 = (X1 * 3038) >> 16; + X2 = (-7357 * pressure) >> 16; + pressure += (X1 + X2 + 3791) >> 4; + + return pressure; +} + +struct barometer* get_barometer(void) +{ + int32_t ut = get_ut(); + int32_t up = get_up(); + int32_t b5 = get_b5(&calibration, ut); + + barometer.temperature = get_temperature(b5); + barometer.pressure = get_pressure(&calibration, b5, up); + + return &barometer; +} + +/** + * Assume twi_master_init has already been called + */ +void bmp180_init(void) +{ + /* Read and check id */ + uint8_t id = read_8(BMP180_REG_ID); + if (id != 0x55) while (1); + + /* Get the calibration parameters */ + //get_cal_param(&calibration); +} diff --git a/firmware/test/tc/pressure_temperature.h b/firmware/test/tc/pressure_temperature.h new file mode 100644 index 0000000..5cdf03d --- /dev/null +++ b/firmware/test/tc/pressure_temperature.h @@ -0,0 +1,34 @@ +#ifndef __verification__ +#define __verification__ +#endif + +/****************************//* pressure_temperature_tc *//****************************/ +/** + * Write a description of your test case here + */ + +/* Parameters in */ +struct pressure_temperature_tc_params { + + /* Input paramters to your test case go here */ + uint32_t dummy; + +} pressure_temperature_tc_params; +/* Results out */ +struct pressure_temperature_tc_results { + + /* Result values should be populated here */ + float pressure, temperature; + +} pressure_temperature_tc_results; +/* Function */ +__verification__ void pressure_temperature_tc(void) { + + /** + * The main body of the test case goes here. + * + * Use the input parameters to run the test case. Populate the + * results structure at the end + */ + +} diff --git a/firmware/test/tc/pressure_temperature.py b/firmware/test/tc/pressure_temperature.py new file mode 100644 index 0000000..848e74f --- /dev/null +++ b/firmware/test/tc/pressure_temperature.py @@ -0,0 +1,41 @@ +#!/usr/bin/env python + +# ------------------------------------------------------------------------------ +# Imports +# ------------------------------------------------------------------------------ + +import sys +sys.path.append("./test") +import main + +from random import randint + +# ------------------------------------------------------------------------------ +# Test Script +# ------------------------------------------------------------------------------ + +class pressure_temperature_tc: + def __init__(self): + self.name = self.__class__.__name__ + self.iterations = 20 + + + def get_test(self): + """Returns some suitable test parameters""" + params = main.struct_pressure_temperature_tc_params() + + """ + Assign input parameters here + """ + + return params + + def is_correct(self, params, result, print_info): + """Returns if a result is correct for the given parameters""" + + """ + Compare result and params here, decide sth. + Can use print_info + """ + + return True