Merge branch 'contiki'

Conflicts:
	cpu/cc26xx-cc13xx/lib/cc13xxware
	cpu/cc26xx-cc13xx/lib/cc26xxware
This commit is contained in:
Harald Pichler 2017-01-31 15:00:59 +01:00
commit 2f8549aaae
319 changed files with 58114 additions and 6745 deletions

26
dev/bme280/README.bme280 Normal file
View file

@ -0,0 +1,26 @@
Contiki implementation for Bosch Sensortec BME280 sensor.
BME280 is compact and fast and includes temp, RH, and pressure.
Chip uses I2C or SPI. This implementation and follow Contiki
device API and I2C.
For better performance and less chip warm up and less I2C transactions
burst read is recommended. Here all T/RH/P is read in one single
I2C read and kept the measurements have the same time.
The burst read is stored in struct bme280_mea which accessible from
the Contiki. Also note that the full chip resolution is available.
The variables are overscaled to give the app full controlover resolution.
See bme280.h
The pressure can be calculated with 32 or 64. The define BME280_64BIT
controls this. Typically in your project-conf.h
Implemented according to datasheet Rev 1.1.
Limitations:
Implementation implements Weather Mode which uses forced one-shot
mode no oversampling nor filters and reads all T/RH/P in one read.

45
dev/bme280/bme280-arch.h Normal file
View file

@ -0,0 +1,45 @@
/*
* Copyright (c) 2016, Zolertia <http://www.zolertia.com>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the Institute nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
/*---------------------------------------------------------------------------*/
#ifndef BME280_ARCH_H
#define BME280_ARCH_H
/* Initialize the I2C module */
void bme280_arch_i2c_init();
/* I2C read registers */
void bme280_arch_i2c_read_mem(uint8_t addr, uint8_t reg, uint8_t *buf,
uint8_t bytes);
/* I2C write to a single register */
void bme280_arch_i2c_write_mem(uint8_t addr, uint8_t reg, uint8_t value);
#endif
/*---------------------------------------------------------------------------*/
/** @} */

View file

@ -0,0 +1,79 @@
/*
* Copyright (c) 2015, Copyright Robert Olsson
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the Institute nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* This file is part of the Contiki operating system.
*
*
* Author : Robert Olsson rolss@kth.se/robert@radio-sensors.com
* Created : 2016-09-14
*/
#include "contiki.h"
#include "lib/sensors.h"
#include "dev/bme280/bme280.h"
#include "dev/bme280/bme280-sensor.h"
/*---------------------------------------------------------------------------*/
static int
value(int type)
{
/* Read all measurements with one burst read */
bme280_read(BME280_MODE_WEATHER);
/* Return a la Contiki API */
switch(type) {
case BME280_SENSOR_TEMP:
return bme280_mea.t_overscale100 / 100;
case BME280_SENSOR_HUMIDITY:
return bme280_mea.h_overscale1024 >> 10;
case BME280_SENSOR_PRESSURE:
/* Scale down w. 10 not to overslow the signed int */
#ifdef BME280_64BIT
return bme280_mea.p_overscale256 / (256 * 10);
#else
return bme280_mea.p / 10;
#endif
}
return 0;
}
/*---------------------------------------------------------------------------*/
static int
status(int type)
{
return 0;
}
/*---------------------------------------------------------------------------*/
static int
configure(int type, int c)
{
return bme280_init(BME280_MODE_WEATHER);
}
/*---------------------------------------------------------------------------*/
SENSORS_SENSOR(bme280_sensor, "bme280", value, configure, status);

View file

@ -0,0 +1,48 @@
/*
* Copyright (c) 2015, Copyright Robert Olsson
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the Institute nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* This file is part of the Contiki operating system.
*
*
* Author : Robert Olsson rolss@kth.se/robert@radio-sensors.com
* Created : 2016-09-14
*/
#ifndef BME280_SENSOR_H_
#define BME280_SENSOR_H_
#include "lib/sensors.h"
#include "bme280.h"
extern const struct sensors_sensor bme280_sensor;
#define BME280_SENSOR_TEMP 0
#define BME280_SENSOR_HUMIDITY 1
#define BME280_SENSOR_PRESSURE 2
#endif /* BME280_SENSOR_H_ */

276
dev/bme280/bme280.c Normal file
View file

@ -0,0 +1,276 @@
/*
* Copyright (c) 2015, Copyright Robert Olsson
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the Institute nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* This file is part of the Contiki operating system.
*
*
* Author : Robert Olsson rolss@kth.se/robert@radio-sensors.com
* Created : 2016-09-14
*/
/**
* \file
* Basic functions for Bosch BME280 based on datasheet Rev 1.1
*/
#include "contiki.h"
#include <string.h>
#include "bme280.h"
#include "bme280-arch.h"
#include "lib/sensors.h"
static struct {
unsigned short dig_t1;
signed short dig_t2;
signed short dig_t3;
unsigned short dig_p1;
signed short dig_p2;
signed short dig_p3;
signed short dig_p4;
signed short dig_p5;
signed short dig_p6;
signed short dig_p7;
signed short dig_p8;
signed short dig_p9;
unsigned char dig_h1;
signed short dig_h2;
unsigned char dig_h3;
signed short dig_h4;
signed short dig_h5;
signed char dig_h6;
int32_t t_fine;
uint8_t mode;
} bm;
int32_t
bme280_t_overscale100(int32_t ut)
{
int32_t v1, v2, t;
v1 = ((((ut >> 3) - ((int32_t)bm.dig_t1 << 1))) *
((int32_t)bm.dig_t2)) >> 11;
v2 = (((((ut >> 4) - ((int32_t)bm.dig_t1)) * ((ut >> 4) -
((int32_t)bm.dig_t1))) >> 12) * ((int32_t)bm.dig_t3)) >> 14;
bm.t_fine = v1 + v2;
t = (bm.t_fine * 5 + 128) >> 8;
return t;
}
#ifdef BME280_32BIT
static uint32_t
bme280_p(int32_t up)
{
int32_t v1, v2;
uint32_t p;
v1 = (((int32_t)bm.t_fine) >> 1) - (int32_t)64000;
v2 = (((v1 >> 2) * (v1 >> 2)) >> 11) * ((int32_t)bm.dig_p6);
v2 = v2 + ((v1 * ((int32_t)bm.dig_p5)) << 1);
v2 = (v2 >> 2) + (((int32_t)bm.dig_p4) << 16);
v1 = (((bm.dig_p3 * (((v1 >> 2) * (v1 >> 2)) >> 13)) >> 3) +
((((int32_t)bm.dig_p2) * v1) >> 1)) >> 18;
v1 = ((((32768 + v1)) * ((int32_t)bm.dig_p1)) >> 15);
if(v1 == 0) {
return 0;
}
p = (((uint32_t)(((int32_t)1048576) - up) - (v2 >> 12))) * 3125;
if(p < 0x80000000) {
p = (p << 1) / ((uint32_t)v1);
} else {
p = (p / (uint32_t)v1) * 2;
}
v1 = (((int32_t)bm.dig_p9) * ((int32_t)(((p >> 3) * (p >> 3)) >> 13))) >> 12;
v2 = (((int32_t)(p >> 2)) * ((int32_t)bm.dig_p8)) >> 13;
p = (uint32_t)((int32_t)p + ((v1 + v2 + bm.dig_p7) >> 4));
return p;
}
#else
static uint32_t
bme280_p_overscale256(int32_t up)
{
int64_t v1, v2, p;
v1 = ((int64_t)bm.t_fine) - 128000;
v2 = v1 * v1 * (int64_t)bm.dig_p6;
v2 = v2 + ((v1 * (int64_t)bm.dig_p5) << 17);
v2 = v2 + (((int64_t)bm.dig_p4) << 35);
v1 = ((v1 * v1 * (int64_t)bm.dig_p3) >> 8) + ((v1 * (int64_t)bm.dig_p2) << 12);
v1 = (((((int64_t)1) << 47) + v1)) * ((int64_t)bm.dig_p1) >> 33;
if(v1 == 0) {
return 0;
}
p = 1048576 - up;
p = (((p << 31) - v2) * 3125) / v1;
v1 = (((int64_t)bm.dig_p9) * (p >> 13) * (p >> 13)) >> 25;
v2 = (((int64_t)bm.dig_p8) * p) >> 19;
p = (((p + v1 + v2) >> 8) + (((int64_t)bm.dig_p7) << 4));
return (uint32_t)p;
}
#endif
static uint32_t
bme280_h_overscale1024(int32_t uh)
{
int32_t v1;
v1 = (bm.t_fine - ((int32_t)76800));
v1 = (((((uh << 14) - (((int32_t)bm.dig_h4) << 20) - (((int32_t)bm.dig_h5) * v1)) + ((int32_t)16384)) >> 15)
* (((((((v1 * ((int32_t)bm.dig_h6)) >> 10) * (((v1 * ((int32_t)bm.dig_h3)) >> 11) + ((int32_t)32768)))
>> 10) + ((int32_t)2097152)) * ((int32_t)bm.dig_h2) + 8192) >> 14));
v1 = (v1 - (((((v1 >> 15) * (v1 >> 15)) >> 7) * ((int32_t)bm.dig_h1)) >> 4));
v1 = (v1 < 0 ? 0 : v1);
v1 = (v1 > 419430400 ? 419430400 : v1);
return (uint32_t)(v1 >> 12);
}
uint8_t
bme280_init(uint8_t mode)
{
uint16_t i;
uint8_t buf[26];
bme280_arch_i2c_init();
/* Do not mess with other chips */
bme280_arch_i2c_read_mem(BME280_ADDR, 0xD0, buf, 1);
if(buf[0] != BME280_CHIP_ID) {
return 0;
}
bme280_arch_i2c_write_mem(BME280_ADDR, BME280_CNTL_RESET, 0xB6);
for(i = 0; i < BME280_MAX_WAIT; i++) {
clock_delay_usec(1000);
}
memset(buf, 0, sizeof(buf));
/* Burst read of all calibration part 1 */
bme280_arch_i2c_read_mem(BME280_ADDR, BME280_DIG_T1_ADDR, buf, sizeof(buf));
bm.dig_t1 = ((uint16_t)buf[1] << 8) | (uint16_t)buf[0];
bm.dig_t2 = ((int16_t)buf[3] << 8) | (uint16_t)buf[2];
bm.dig_t3 = ((int16_t)buf[5] << 8) | (uint16_t)buf[4];
bm.dig_p1 = ((uint16_t)buf[7] << 8) | (uint16_t)buf[6];
bm.dig_p2 = ((int16_t)buf[9] << 8) | (uint16_t)buf[8];
bm.dig_p3 = ((int16_t)buf[11] << 8) | (uint16_t)buf[10];
bm.dig_p4 = ((int16_t)buf[13] << 8) | (uint16_t)buf[12];
bm.dig_p5 = ((int16_t)buf[15] << 8) | (uint16_t)buf[14];
bm.dig_p6 = ((int16_t)buf[17] << 8) | (uint16_t)buf[16];
bm.dig_p7 = ((int16_t)buf[19] << 8) | (uint16_t)buf[18];
bm.dig_p8 = ((int16_t)buf[21] << 8) | (uint16_t)buf[20];
bm.dig_p9 = ((int16_t)buf[23] << 8) | (uint16_t)buf[22];
/* A0 not used */
bm.dig_h1 = (unsigned char)buf[25];
/* Burst read of all calibration part 2 */
bme280_arch_i2c_read_mem(BME280_ADDR, BME280_DIG_H2_ADDR, buf, 8);
bm.dig_h2 = ((int16_t)buf[1] << 8) | (uint16_t)buf[0];
bm.dig_h3 = (unsigned char)buf[2];
bm.dig_h4 = ((int16_t)buf[3] << 4) | (((uint16_t)buf[4]) & 0xF);
bm.dig_h5 = ((int16_t)buf[6] << 4) | (((uint16_t)buf[5]) & 0xF);
bm.dig_h6 = (unsigned char)buf[7];
bm.mode = mode;
return 1;
}
void
bme280_read(uint8_t mode)
{
int32_t ut, uh, up;
uint8_t buf[8], sleep;
uint16_t i;
memset(buf, 0, sizeof(buf));
/* Are we initilized and in the right mode? */
if(mode == BME280_MODE_NONE || mode != bm.mode) {
return;
}
ut = uh = up = 0;
/* Weather mode. See sectiom 3.5 Datasheet */
if(mode == BME280_MODE_WEATHER) {
/* Humidity oversampling *1 */
bme280_arch_i2c_write_mem(BME280_ADDR, BME280_CNTL_HUM, 0x01);
/* 00100111 0x27 oversampling *1 for t and p plus normal mode */
/* 0.5 ms -- no filter -- no SPI */
bme280_arch_i2c_write_mem(BME280_ADDR, BME280_CONTROL, 0x00);
/* 00100110 0x26 oversampling *1 for t and p plus forced mode */
/* Trigger measurement needed for every time in forced mode */
bme280_arch_i2c_write_mem(BME280_ADDR, BME280_CNTL_MEAS, 0x26);
/* Wait to get into sleep mode == measurement done */
for(i = 0; i < BME280_MAX_WAIT; i++) {
bme280_arch_i2c_read_mem(BME280_ADDR, BME280_CNTL_MEAS, &sleep, 1);
sleep = sleep& 0x03;
if(sleep== 0) {
break;
} else {
clock_delay_usec(1000);
}
}
if(i == BME280_MAX_WAIT) {
return; /* error wait*/
}
} else { /* if(mode == BME280_MODE_WEATHER) */
return; /* error mode*/
}
/* Burst read of all measurements */
bme280_arch_i2c_read_mem(BME280_ADDR, BME280_PRESS, buf, 8);
ut = (uint32_t)(buf[3]) << 12 | (uint32_t)(buf[4]) << 4 | (uint32_t)buf[5] >> 4;
up = (uint32_t)(buf[0]) << 12 | (uint32_t)(buf[1]) << 4 | (uint32_t)buf[2] >> 4;
uh = (uint32_t)(buf[6]) << 8 | (uint32_t)buf[7];
bme280_mea.t_overscale100 = bme280_t_overscale100(ut);
bme280_mea.h_overscale1024 = bme280_h_overscale1024(uh);
#ifdef BME280_64BIT
bme280_mea.p_overscale256 = bme280_p_overscale256(up);
#else
bme280_mea.p = bme280_p(up);
#endif
#if TEST
printf("T_BME280=%5.2f", (double)bme280_mea.t_overscale100 / 100.);
printf(" RH_BME280=%5.2f ", (double)bme280_mea.h_overscale1024 / 1024.);
#ifdef BME280_64BIT
printf(" P_BME280=%5.2f\n", (double)bme280_mea.p_overscale256 / 256.);
#else
printf(" P_BME280=%5.2f\n", (double)bme280_mea.p);
#endif
#endif
}

110
dev/bme280/bme280.h Normal file
View file

@ -0,0 +1,110 @@
/*
* Copyright (c) 2015, Copyright Robert Olsson
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the Institute nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* This file is part of the Contiki operating system.
*
*
* Author : Robert Olsson rolss@kth.se/robert@radio-sensors.com
*
* Created : 2016-09-14
*/
/**
* \file
* Definitions for the Bosch BME280 based on datasheet Rev 1.1
*/
#ifndef BME280_H
#define BME280_H
#ifndef BME280_32BIT
#define BME280_64BIT
#endif
uint8_t bme280_init(uint8_t mode);
void bme280_read(uint8_t mode);
#ifdef BME280_CONF_ADDR
#define BME280_ADDR BME280_CONF_ADDR
#else
#define BME280_ADDR (0x77 << 1) /* Alternative 0x76 */
#endif
/* Diffrent BOSCH chip id's */
#define BMP085_CHIP_ID 0x55 /* And also BMP180 */
#define BMP280_CHIP_ID 0x58
#define BME280_CHIP_ID 0x60
/* Address map */
#define BME280_DIG_T1_ADDR 0x88
#define BME280_DIG_T2_ADDR 0x8A
#define BME280_DIG_T3_ADDR 0x8C
#define BME280_DIG_P1_ADDR 0x8E
#define BME280_DIG_P2_ADDR 0x90
#define BME280_DIG_P3_ADDR 0x92
#define BME280_DIG_P4_ADDR 0x94
#define BME280_DIG_P5_ADDR 0x96
#define BME280_DIG_P6_ADDR 0x98
#define BME280_DIG_P7_ADDR 0x9A
#define BME280_DIG_P8_ADDR 0x9C
#define BME280_DIG_P9_ADDR 0x9E
#define BME280_DIG_H1_ADDR 0xA1
#define BMP_CHIP_ID_ADDR 0xD0
#define BME280_CNTL_RESET 0xE0
#define BME280_DIG_H2_ADDR 0xE1
#define BME280_DIG_H3_ADDR 0xE3
#define BME280_DIG_H4_ADDR 0xE4
#define BME280_DIG_H5_ADDR 0xE5
#define BME280_DIG_H6_ADDR 0xE7
#define BME280_CNTL_HUM 0xF2
#define BME280_STATUS 0xF3
#define BME280_CNTL_MEAS 0xF4
#define BME280_CONTROL 0xF5
#define BME280_PRESS 0xF7
/* Function modes outlined in datasheet */
#define BME280_MODE_NONE 0
#define BME280_MODE_WEATHER 1
#define BME280_MODE_HUMIDITY 2
#define BME280_MODE_INDOOR_NAVIGATION 3
#define BME280_MODE_GAMING 4
#define BME280_MAX_WAIT 300 /* ms. Forced mode max wait */
struct {
int32_t t_overscale100;
uint32_t h_overscale1024;
#ifdef BME280_64BIT
uint32_t p_overscale256;
#else
uint32_t p;
#endif
} bme280_mea;
#endif /* BME280_H */

98
dev/disk/disk.h Normal file
View file

@ -0,0 +1,98 @@
/*
* Copyright (c) 2016, Benoît Thébaudeau <benoit@wsystem.com>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* 3. Neither the name of the copyright holder nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
/**
* \addtogroup dev
* @{
*
* \defgroup disk Disk device drivers
*
* Documentation for all the disk device drivers.
* @{
*
* \file
* Header file defining the disk device driver API.
*/
#ifndef DISK_H_
#define DISK_H_
#include <stdint.h>
/** Disk status flags. */
typedef enum {
DISK_STATUS_INIT = 0x01, /**< Device initialized and ready to work */
DISK_STATUS_DISK = 0x02, /**< Medium present in the drive */
DISK_STATUS_WRITABLE = 0x04 /**< Writable medium */
} disk_status_t;
/** Generic disk I/O control commands. */
typedef enum {
DISK_IOCTL_CTRL_SYNC, /**< Synchronize the cached writes to persistent storage */
DISK_IOCTL_GET_SECTOR_COUNT, /**< Get the sector count through the \c uint32_t pointed to by \c buff */
DISK_IOCTL_GET_SECTOR_SIZE, /**< Get the sector size through the \c uint16_t pointed to by \c buff */
DISK_IOCTL_GET_BLOCK_SIZE, /**< Get the erase block size (in sectors) through the \c uint32_t pointed to by \c buff */
DISK_IOCTL_CTRL_TRIM /**< Trim the sector range within the \c uint32_t boundaries pointed to by \c buff */
} disk_ioctl_t;
/** Disk access result codes. */
typedef enum {
DISK_RESULT_OK, /**< Success */
DISK_RESULT_IO_ERROR, /**< Unrecoverable I/O error */
DISK_RESULT_WR_PROTECTED, /**< Write-protected medium */
DISK_RESULT_NO_INIT, /**< Device not initialized */
DISK_RESULT_INVALID_ARG /**< Invalid argument */
} disk_result_t;
/** Disk driver API structure. */
struct disk_driver {
/** Get device status. */
disk_status_t (*status)(uint8_t dev);
/** Initialize device. */
disk_status_t (*initialize)(uint8_t dev);
/** Read sector(s). */
disk_result_t (*read)(uint8_t dev, void *buff, uint32_t sector,
uint32_t count);
/** Write sector(s). */
disk_result_t (*write)(uint8_t dev, const void *buff, uint32_t sector,
uint32_t count);
/** Control device-specific features. */
disk_result_t (*ioctl)(uint8_t dev, uint8_t cmd, void *buff);
};
#endif /* DISK_H_ */
/**
* @}
* @}
*/

98
dev/disk/mmc/mmc-arch.h Normal file
View file

@ -0,0 +1,98 @@
/*
* Copyright (c) 2016, Benoît Thébaudeau <benoit@wsystem.com>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* 3. Neither the name of the copyright holder nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
/**
* \addtogroup mmc
* @{
*
* \defgroup mmc-arch SD/MMC architecture-specific definitions
*
* SD/MMC device driver architecture-specific definitions.
* @{
*
* \file
* Header file for the SD/MMC device driver architecture-specific definitions.
*/
#ifndef MMC_ARCH_H_
#define MMC_ARCH_H_
#include <stddef.h>
#include <stdbool.h>
#include <stdint.h>
/** \brief Callback of the SD/MMC driver to call when the card-detection signal
* changes.
* \param dev Device
* \param cd Whether a card is detected
* \note Using this function is not mandatory. This only allows to detect a card
* replacement between two successive calls to the SD/MMC driver API.
*/
void mmc_arch_cd_changed_callback(uint8_t dev, bool cd);
/** \brief Gets the state of the card-detection signal.
* \param dev Device
* \return Whether a card is detected
*/
bool mmc_arch_get_cd(uint8_t dev);
/** \brief Gets the state of the write-protection signal.
* \param dev Device
* \return Whether the card is write-protected
*/
bool mmc_arch_get_wp(uint8_t dev);
/** \brief Sets the SPI /CS signal as indicated.
* \param dev Device
* \param sel Whether to assert /CS
*/
void mmc_arch_spi_select(uint8_t dev, bool sel);
/** \brief Sets the SPI clock frequency.
* \param dev Device
* \param freq Frequency (Hz)
*/
void mmc_arch_spi_set_clock_freq(uint8_t dev, uint32_t freq);
/** \brief Performs an SPI transfer.
* \param dev Device
* \param tx_buf Pointer to the transmission buffer, or \c NULL
* \param tx_cnt Number of bytes to transmit, or \c 0
* \param rx_buf Pointer to the reception buffer, or \c NULL
* \param rx_cnt Number of bytes to receive, or \c 0
*/
void mmc_arch_spi_xfer(uint8_t dev, const void *tx_buf, size_t tx_cnt,
void *rx_buf, size_t rx_cnt);
#endif /* MMC_ARCH_H_ */
/**
* @}
* @}
*/

608
dev/disk/mmc/mmc.c Normal file
View file

@ -0,0 +1,608 @@
/*
* Copyright (c) 2016, Benoît Thébaudeau <benoit@wsystem.com>
* All rights reserved.
*
* Based on the FatFs Module STM32 Sample Project,
* Copyright (c) 2014, ChaN
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* 3. Neither the name of the copyright holder nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
/**
* \addtogroup mmc
* @{
*
* \file
* Implementation of the SD/MMC device driver.
*/
#include <stddef.h>
#include <stdint.h>
#include "contiki.h"
#include "sys/clock.h"
#include "sys/rtimer.h"
#include "dev/watchdog.h"
#include "mmc-arch.h"
#include "mmc.h"
/* Data read/write block length */
#define BLOCK_LEN 512
/*
* Logical sector size exposed to the disk API, not to be confused with the SDSC
* sector size, which is the size of an erasable sector
*/
#define SECTOR_SIZE BLOCK_LEN
/* Clock frequency in card identification mode: fOD <= 400 kHz */
#define CLOCK_FREQ_CARD_ID_MODE 400000
/*
* Clock frequency in data transfer mode: fPP <= 20 MHz, limited by the
* backward-compatible MMC interface timings
*/
#define CLOCK_FREQ_DATA_XFER_MODE 20000000
/* SPI-mode command list */
#define CMD0 0 /* GO_IDLE_STATE */
#define CMD1 1 /* SEND_OP_COND */
#define CMD8 8 /* SEND_IF_COND */
#define CMD8_VHS_2_7_3_6 0x1
#define CMD8_ARG(vhs, check_pattern) ((vhs) << 8 | (check_pattern))
#define CMD8_ECHO_MASK 0x00000fff
#define CMD9 9 /* SEND_CSD */
#define CMD10 10 /* SEND_CID */
#define CMD12 12 /* STOP_TRANSMISSION */
#define CMD13 13 /* SEND_STATUS */
#define CMD16 16 /* SET_BLOCKLEN */
#define CMD17 17 /* READ_SINGLE_BLOCK */
#define CMD18 18 /* READ_MULTIPLE_BLOCK */
#define CMD23 23 /* SET_BLOCK_COUNT */
#define CMD24 24 /* WRITE_BLOCK */
#define CMD25 25 /* WRITE_MULTIPLE_BLOCK */
#define CMD32 32 /* ERASE_WR_BLK_START */
#define CMD33 33 /* ERASE_WR_BLK_END */
#define CMD38 38 /* ERASE */
#define CMD55 55 /* APP_CMD */
#define CMD58 58 /* READ_OCR */
#define ACMD 0x80 /* Application-specific command */
#define ACMD13 (ACMD | 13) /* SD_STATUS */
#define ACMD23 (ACMD | 23) /* SET_WR_BLK_ERASE_COUNT */
#define ACMD41 (ACMD | 41) /* SD_APP_OP_COND */
#define ACMD41_HCS (1 << 30)
#define CMD_TX 0x40 /* Command transmission bit */
#define R1_MSB 0x00
#define R1_SUCCESS 0x00
#define R1_IDLE_STATE (1 << 0)
#define R1_ERASE_RESET (1 << 1)
#define R1_ILLEGAL_COMMAND (1 << 2)
#define R1_COM_CRC_ERROR (1 << 3)
#define R1_ERASE_SEQUENCE_ERROR (1 << 4)
#define R1_ADDRESS_ERROR (1 << 5)
#define R1_PARAMETER_ERROR (1 << 6)
#define TOK_DATA_RESP_MASK 0x1f
#define TOK_DATA_RESP_ACCEPTED 0x05
#define TOK_DATA_RESP_CRC_ERROR 0x0b
#define TOK_DATA_RESP_WR_ERROR 0x0d
#define TOK_RD_SINGLE_WR_START_BLOCK 0xfe
#define TOK_MULTI_WR_START_BLOCK 0xfc
#define TOK_MULTI_WR_STOP_TRAN 0xfd
/* The SD Status is one data block of 512 bits. */
#define SD_STATUS_SIZE (512 / 8)
#define SD_STATUS_AU_SIZE(sd_status) ((sd_status)[10] >> 4)
#define OCR_CCS (1 << 30)
#define CSD_SIZE 16
#define CSD_STRUCTURE(csd) ((csd)[0] >> 6)
#define CSD_STRUCTURE_SD_V1_0 0
#define CSD_STRUCTURE_SD_V2_0 1
#define CSD_SD_V1_0_READ_BL_LEN(csd) ((csd)[5] & 0x0f)
#define CSD_SD_V1_0_BLOCK_LEN(csd) (1ull << CSD_SD_V1_0_READ_BL_LEN(csd))
#define CSD_SD_V1_0_C_SIZE(csd) \
(((csd)[6] & 0x03) << 10 | (csd)[7] << 2 | (csd)[8] >> 6)
#define CSD_SD_V1_0_C_SIZE_MULT(csd) \
(((csd)[9] & 0x03) << 1 | (csd)[10] >> 7)
#define CSD_SD_V1_0_MULT(csd) \
(1 << (CSD_SD_V1_0_C_SIZE_MULT(csd) + 2))
#define CSD_SD_V1_0_BLOCKNR(csd) \
(((uint32_t)CSD_SD_V1_0_C_SIZE(csd) + 1) * CSD_SD_V1_0_MULT(csd))
#define CSD_SD_V1_0_CAPACITY(csd) \
(CSD_SD_V1_0_BLOCKNR(csd) * CSD_SD_V1_0_BLOCK_LEN(csd))
#define CSD_SD_V1_0_SECTOR_SIZE(csd) \
(((csd)[10] & 0x3f) << 1 | (csd)[11] >> 7)
#define CSD_SD_V1_0_WRITE_BL_LEN(csd) \
(((csd)[12] & 0x03) << 2 | (csd)[13] >> 6)
#define CSD_SD_V2_0_C_SIZE(csd) \
(((csd)[7] & 0x3f) << 16 | (csd)[8] << 8 | (csd)[9])
#define CSD_SD_V2_0_CAPACITY(csd) \
(((uint64_t)CSD_SD_V2_0_C_SIZE(csd) + 1) << 19)
#define CSD_MMC_ERASE_GRP_SIZE(csd) (((csd)[10] & 0x7c) >> 2)
#define CSD_MMC_ERASE_GRP_MULT(csd) \
(((csd)[10] & 0x03) << 3 | (csd)[11] >> 5)
#define CSD_MMC_WRITE_BL_LEN(csd) \
(((csd)[12] & 0x03) << 2 | (csd)[13] >> 6)
typedef enum {
CARD_TYPE_MMC = 0x01, /* MMC v3 */
CARD_TYPE_SD1 = 0x02, /* SD v1 */
CARD_TYPE_SD2 = 0x04, /* SD v2 */
CARD_TYPE_SD = CARD_TYPE_SD1 | CARD_TYPE_SD2, /* SD */
CARD_TYPE_BLOCK = 0x08 /* Block addressing */
} card_type_t;
static struct mmc_priv {
uint8_t status;
uint8_t card_type;
} mmc_priv[MMC_CONF_DEV_COUNT];
/*----------------------------------------------------------------------------*/
static uint8_t
mmc_spi_xchg(uint8_t dev, uint8_t tx_byte)
{
uint8_t rx_byte;
mmc_arch_spi_xfer(dev, &tx_byte, 1, &rx_byte, 1);
return rx_byte;
}
/*----------------------------------------------------------------------------*/
static void
mmc_spi_tx(uint8_t dev, const void *buf, size_t cnt)
{
mmc_arch_spi_xfer(dev, buf, cnt, NULL, 0);
}
/*----------------------------------------------------------------------------*/
static void
mmc_spi_rx(uint8_t dev, void *buf, size_t cnt)
{
mmc_arch_spi_xfer(dev, NULL, 0, buf, cnt);
}
/*----------------------------------------------------------------------------*/
static bool
mmc_wait_ready(uint8_t dev, uint16_t timeout_ms)
{
rtimer_clock_t timeout_end =
RTIMER_NOW() + ((uint32_t)timeout_ms * RTIMER_SECOND + 999) / 1000;
uint8_t rx_byte;
do {
rx_byte = mmc_spi_xchg(dev, 0xff);
watchdog_periodic();
} while(rx_byte != 0xff && RTIMER_CLOCK_LT(RTIMER_NOW(), timeout_end));
return rx_byte == 0xff;
}
/*----------------------------------------------------------------------------*/
static bool
mmc_select(uint8_t dev, bool sel)
{
mmc_arch_spi_select(dev, sel);
mmc_spi_xchg(dev, 0xff); /* Dummy clock (force D0) */
if(sel && !mmc_wait_ready(dev, 500)) {
mmc_select(dev, false);
return false;
}
return true;
}
/*----------------------------------------------------------------------------*/
static uint8_t
mmc_send_cmd(uint8_t dev, uint8_t cmd, uint32_t arg)
{
uint8_t resp, n;
/* Send a CMD55 prior to a ACMD<n>. */
if(cmd & ACMD) {
cmd &= ~ACMD;
resp = mmc_send_cmd(dev, CMD55, 0);
if(resp != R1_SUCCESS && resp != R1_IDLE_STATE) {
return resp;
}
}
/*
* Select the card and wait for ready, except to stop a multiple-block read.
*/
if(cmd != CMD12) {
mmc_select(dev, false);
if(!mmc_select(dev, true)) {
return 0xff;
}
}
/* Send the command packet. */
mmc_spi_xchg(dev, CMD_TX | cmd); /* Start & tx bits, cmd index */
mmc_spi_xchg(dev, arg >> 24); /* Argument[31..24] */
mmc_spi_xchg(dev, arg >> 16); /* Argument[23..16] */
mmc_spi_xchg(dev, arg >> 8); /* Argument[15..8] */
mmc_spi_xchg(dev, arg); /* Argument[7..0] */
switch(cmd) {
case CMD0:
n = 0x95; /* CMD0(0) CRC7, end bit */
break;
case CMD8:
n = 0x87; /* CMD8(0x1aa) CRC7, end bit */
break;
default:
n = 0x01; /* Dummy CRC7, end bit */
break;
}
mmc_spi_xchg(dev, n);
/* Receive the command response. */
if(cmd == CMD12) {
mmc_spi_xchg(dev, 0xff); /* Discard following byte if CMD12. */
}
/* Wait for the response (max. 10 bytes). */
n = 10;
do {
resp = mmc_spi_xchg(dev, 0xff);
} while((resp & 0x80) != R1_MSB && --n);
return resp;
}
/*----------------------------------------------------------------------------*/
static bool
mmc_tx_block(uint8_t dev, const void *buf, uint8_t token)
{
uint8_t resp;
if(!mmc_wait_ready(dev, 500)) {
return false;
}
mmc_spi_xchg(dev, token);
if(token != TOK_MULTI_WR_STOP_TRAN) {
mmc_spi_tx(dev, buf, BLOCK_LEN);
mmc_spi_xchg(dev, 0xff); /* Dummy CRC */
mmc_spi_xchg(dev, 0xff);
resp = mmc_spi_xchg(dev, 0xff);
if((resp & TOK_DATA_RESP_MASK) != TOK_DATA_RESP_ACCEPTED) {
return false;
}
}
return true;
}
/*----------------------------------------------------------------------------*/
static bool
mmc_rx(uint8_t dev, void *buf, size_t cnt)
{
rtimer_clock_t timeout_end =
RTIMER_NOW() + (200ul * RTIMER_SECOND + 999) / 1000;
uint8_t token;
do {
token = mmc_spi_xchg(dev, 0xff);
watchdog_periodic();
} while(token == 0xff && RTIMER_CLOCK_LT(RTIMER_NOW(), timeout_end));
if(token != TOK_RD_SINGLE_WR_START_BLOCK) {
return false;
}
mmc_spi_rx(dev, buf, cnt);
mmc_spi_xchg(dev, 0xff); /* Discard CRC. */
mmc_spi_xchg(dev, 0xff);
return true;
}
/*----------------------------------------------------------------------------*/
void
mmc_arch_cd_changed_callback(uint8_t dev, bool cd)
{
uint8_t status;
if(dev >= MMC_CONF_DEV_COUNT) {
return;
}
if(cd) {
status = DISK_STATUS_DISK;
if(!mmc_arch_get_wp(dev)) {
status |= DISK_STATUS_WRITABLE;
}
} else {
status = 0;
}
mmc_priv[dev].status = status;
}
/*----------------------------------------------------------------------------*/
static disk_status_t
mmc_status(uint8_t dev)
{
bool cd;
struct mmc_priv *priv;
if(dev >= MMC_CONF_DEV_COUNT) {
return DISK_RESULT_INVALID_ARG;
}
cd = mmc_arch_get_cd(dev);
priv = &mmc_priv[dev];
if(cd == !(priv->status & DISK_STATUS_DISK)) {
mmc_arch_cd_changed_callback(dev, cd);
}
return priv->status;
}
/*----------------------------------------------------------------------------*/
static disk_status_t
mmc_initialize(uint8_t dev)
{
disk_status_t status;
uint8_t n, cmd;
card_type_t card_type;
rtimer_clock_t timeout_end;
uint32_t arg, resp, ocr;
struct mmc_priv *priv;
if(dev >= MMC_CONF_DEV_COUNT) {
return DISK_RESULT_INVALID_ARG;
}
status = mmc_status(dev);
if(!(status & DISK_STATUS_DISK)) {
return status;
}
mmc_arch_spi_select(dev, false);
clock_delay_usec(10000);
mmc_arch_spi_set_clock_freq(dev, CLOCK_FREQ_CARD_ID_MODE);
for(n = 10; n; n--) {
mmc_spi_xchg(dev, 0xff); /* Generate 80 dummy clock cycles. */
}
card_type = 0;
if(mmc_send_cmd(dev, CMD0, 0) == R1_IDLE_STATE) {
timeout_end = RTIMER_NOW() + RTIMER_SECOND;
arg = CMD8_ARG(CMD8_VHS_2_7_3_6, 0xaa); /* Arbitrary check pattern */
if(mmc_send_cmd(dev, CMD8, arg) == R1_IDLE_STATE) { /* SD v2? */
resp = 0;
for(n = 4; n; n--) {
resp = resp << 8 | mmc_spi_xchg(dev, 0xff);
}
/* Does the card support 2.7 V - 3.6 V? */
if((arg & CMD8_ECHO_MASK) == (resp & CMD8_ECHO_MASK)) {
/* Wait for end of initialization. */
while(RTIMER_CLOCK_LT(RTIMER_NOW(), timeout_end) &&
mmc_send_cmd(dev, ACMD41, ACMD41_HCS) != R1_SUCCESS) {
watchdog_periodic();
}
if(RTIMER_CLOCK_LT(RTIMER_NOW(), timeout_end) &&
mmc_send_cmd(dev, CMD58, 0) == R1_SUCCESS) { /* Read OCR. */
ocr = 0;
for(n = 4; n; n--) {
ocr = ocr << 8 | mmc_spi_xchg(dev, 0xff);
}
card_type = CARD_TYPE_SD2;
if(ocr & OCR_CCS) {
card_type |= CARD_TYPE_BLOCK;
}
}
}
} else { /* Not SD v2 */
resp = mmc_send_cmd(dev, ACMD41, 0);
if(resp == R1_SUCCESS || resp == R1_IDLE_STATE) { /* SD v1 or MMC? */
card_type = CARD_TYPE_SD1;
cmd = ACMD41;
} else {
card_type = CARD_TYPE_MMC;
cmd = CMD1;
}
/* Wait for end of initialization. */
while(RTIMER_CLOCK_LT(RTIMER_NOW(), timeout_end) &&
mmc_send_cmd(dev, cmd, 0) != R1_SUCCESS) {
watchdog_periodic();
}
/* Set block length. */
if(!RTIMER_CLOCK_LT(RTIMER_NOW(), timeout_end) ||
mmc_send_cmd(dev, CMD16, BLOCK_LEN) != R1_SUCCESS) {
card_type = 0;
}
}
}
priv = &mmc_priv[dev];
priv->card_type = card_type;
mmc_select(dev, false);
status = priv->status;
if(status & DISK_STATUS_DISK && card_type) { /* OK */
mmc_arch_spi_set_clock_freq(dev, CLOCK_FREQ_DATA_XFER_MODE);
status |= DISK_STATUS_INIT;
} else { /* Failed */
status &= ~DISK_STATUS_INIT;
}
priv->status = status;
return status;
}
/*----------------------------------------------------------------------------*/
static disk_result_t
mmc_read(uint8_t dev, void *buff, uint32_t sector, uint32_t count)
{
if(dev >= MMC_CONF_DEV_COUNT || !count) {
return DISK_RESULT_INVALID_ARG;
}
if(!(mmc_status(dev) & DISK_STATUS_INIT)) {
return DISK_RESULT_NO_INIT;
}
if(!(mmc_priv[dev].card_type & CARD_TYPE_BLOCK)) {
sector *= SECTOR_SIZE;
}
if(count == 1) {
if(mmc_send_cmd(dev, CMD17, sector) == R1_SUCCESS &&
mmc_rx(dev, buff, SECTOR_SIZE)) {
count = 0;
}
} else if(mmc_send_cmd(dev, CMD18, sector) == R1_SUCCESS) {
do {
if(!mmc_rx(dev, buff, SECTOR_SIZE)) {
break;
}
buff = (uint8_t *)buff + SECTOR_SIZE;
watchdog_periodic();
} while(--count);
mmc_send_cmd(dev, CMD12, 0); /* Stop transmission. */
}
mmc_select(dev, false);
return count ? DISK_RESULT_IO_ERROR : DISK_RESULT_OK;
}
/*----------------------------------------------------------------------------*/
static disk_result_t
mmc_write(uint8_t dev, const void *buff, uint32_t sector, uint32_t count)
{
disk_status_t status;
card_type_t card_type;
if(dev >= MMC_CONF_DEV_COUNT || !count) {
return DISK_RESULT_INVALID_ARG;
}
status = mmc_status(dev);
if(!(status & DISK_STATUS_INIT)) {
return DISK_RESULT_NO_INIT;
}
if(!(status & DISK_STATUS_WRITABLE)) {
return DISK_RESULT_WR_PROTECTED;
}
card_type = mmc_priv[dev].card_type;
if(!(card_type & CARD_TYPE_BLOCK)) {
sector *= SECTOR_SIZE;
}
if(count == 1) {
if(mmc_send_cmd(dev, CMD24, sector) == R1_SUCCESS &&
mmc_tx_block(dev, buff, TOK_RD_SINGLE_WR_START_BLOCK)) {
count = 0;
}
} else {
if(card_type & CARD_TYPE_SD) {
mmc_send_cmd(dev, ACMD23, count);
}
if(mmc_send_cmd(dev, CMD25, sector) == R1_SUCCESS) {
do {
if(!mmc_tx_block(dev, buff, TOK_MULTI_WR_START_BLOCK)) {
break;
}
buff = (uint8_t *)buff + BLOCK_LEN;
watchdog_periodic();
} while(--count);
if(!mmc_tx_block(dev, NULL, TOK_MULTI_WR_STOP_TRAN)) {
count = 1;
}
}
}
mmc_select(dev, false);
return count ? DISK_RESULT_IO_ERROR : DISK_RESULT_OK;
}
/*----------------------------------------------------------------------------*/
static disk_result_t
mmc_ioctl(uint8_t dev, uint8_t cmd, void *buff)
{
card_type_t card_type;
disk_result_t res;
uint8_t csd[CSD_SIZE], sd_status[SD_STATUS_SIZE], au_size;
uint64_t capacity;
uint32_t block_size;
static const uint8_t AU_TO_BLOCK_SIZE[] = {12, 16, 24, 32, 64};
if(dev >= MMC_CONF_DEV_COUNT) {
return DISK_RESULT_INVALID_ARG;
}
if(!(mmc_status(dev) & DISK_STATUS_INIT)) {
return DISK_RESULT_NO_INIT;
}
card_type = mmc_priv[dev].card_type;
res = DISK_RESULT_IO_ERROR;
switch(cmd) {
case DISK_IOCTL_CTRL_SYNC:
if(mmc_select(dev, true)) {
res = DISK_RESULT_OK;
}
break;
case DISK_IOCTL_GET_SECTOR_COUNT:
if(mmc_send_cmd(dev, CMD9, 0) == R1_SUCCESS && mmc_rx(dev, csd, CSD_SIZE)) {
capacity = CSD_STRUCTURE(csd) == CSD_STRUCTURE_SD_V2_0 ?
CSD_SD_V2_0_CAPACITY(csd) : CSD_SD_V1_0_CAPACITY(csd);
*(uint32_t *)buff = capacity / SECTOR_SIZE;
res = DISK_RESULT_OK;
}
break;
case DISK_IOCTL_GET_SECTOR_SIZE:
*(uint16_t *)buff = SECTOR_SIZE;
res = DISK_RESULT_OK;
break;
case DISK_IOCTL_GET_BLOCK_SIZE:
if(card_type & CARD_TYPE_SD2) {
if(mmc_send_cmd(dev, ACMD13, 0) == R1_SUCCESS) { /* Read SD status. */
mmc_spi_xchg(dev, 0xff);
if(mmc_rx(dev, sd_status, SD_STATUS_SIZE)) {
au_size = SD_STATUS_AU_SIZE(sd_status);
if(au_size) {
block_size = au_size <= 0xa ? 8192ull << au_size :
(uint32_t)AU_TO_BLOCK_SIZE[au_size - 0xb] << 20;
*(uint32_t *)buff = block_size / SECTOR_SIZE;
res = DISK_RESULT_OK;
}
}
}
} else if(mmc_send_cmd(dev, CMD9, 0) == R1_SUCCESS &&
mmc_rx(dev, csd, CSD_SIZE)) {
if(card_type & CARD_TYPE_SD1) {
block_size = (uint32_t)(CSD_SD_V1_0_SECTOR_SIZE(csd) + 1) <<
CSD_SD_V1_0_WRITE_BL_LEN(csd);
} else { /* MMC */
block_size = (uint32_t)(CSD_MMC_ERASE_GRP_SIZE(csd) + 1) *
(CSD_MMC_ERASE_GRP_MULT(csd) + 1) <<
CSD_MMC_WRITE_BL_LEN(csd);
}
*(uint32_t *)buff = block_size / SECTOR_SIZE;
res = DISK_RESULT_OK;
}
break;
default:
res = DISK_RESULT_INVALID_ARG;
break;
}
mmc_select(dev, false);
return res;
}
/*----------------------------------------------------------------------------*/
const struct disk_driver mmc_driver = {
.status = mmc_status,
.initialize = mmc_initialize,
.read = mmc_read,
.write = mmc_write,
.ioctl = mmc_ioctl
};
/*----------------------------------------------------------------------------*/
/** @} */

61
dev/disk/mmc/mmc.h Normal file
View file

@ -0,0 +1,61 @@
/*
* Copyright (c) 2016, Benoît Thébaudeau <benoit@wsystem.com>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* 3. Neither the name of the copyright holder nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
/**
* \addtogroup disk
* @{
*
* \defgroup mmc SD/MMC
*
* SD/MMC device driver.
* @{
*
* \file
* Header file for the SD/MMC device driver.
*/
#ifndef MMC_H_
#define MMC_H_
#include "contiki-conf.h"
#include "../disk.h"
#ifndef MMC_CONF_DEV_COUNT
/** Number of SD/MMC devices. */
#define MMC_CONF_DEV_COUNT 1
#endif
extern const struct disk_driver mmc_driver;
#endif /* MMC_H_ */
/**
* @}
* @}
*/