Files
Boris Lovosevic 0de63ef395 Changed the method of passing parameters from tasks/events/interrupts to MicroPython callbacks
Updated MicroPython scheduler functions
  Updated all affected modules

esp-idf sdspi_host driver refactored
  Using SDCard in SPI mode and display at the same time now works
  Tested on M5Stack & Adafruit 2.4" TFT Featherwing

Display module refactored
  uses (modified) esp-idf spi-master driver
  16-bit color mode added
  low level display functions added
  get TP calibration constants function added
  Backlight on/off function added
  M5Stack & GENERIC display types added
  display initialization sequence for unknown display types can now be handled from MicroPython
  tpcalib frozen module updated

I2C module refactored
  SLAVE mode added
  Low level I2C functions added
  esp-idf i2c driver modified

SPI module updated

UART module updated

network module updated

machine module: added method for reading internal ESP32 temperature sensor

_thread module: status of the system tasks is now available in _thread.list()

os module: SDCard mode can now be configured from MicroPython

time.ticks_xx() functions now returns correct tick count after time update
rtc module updated

Added MPU9250 frozen module (available on M5Stack)

Experimental Bluetooth support, not yet finished
Experimental support for eve display modules, not yet finished

License file added
License information in many source files updated/added
2018-02-28 14:46:02 +01:00

424 lines
13 KiB
C

/*
* This file is part of the MicroPython project, http://micropython.org/
*
* The MIT License (MIT)
*
* Copyright (c) 2016 Damien P. George
* Copyright (c) 2018 LoBo (https://github.com/loboris)
*
* 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.
*/
/* ****************************************************************************
*
* ESP32 platform interface for DHT temperature & humidity sensors
*
* Copyright (c) 2017, Arnim Laeuger
*
* 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 <stdio.h>
#include <math.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "driver/rmt.h"
#include "driver/gpio.h"
#include "libs/esp_rmt.h"
#include "py/nlr.h"
#include "py/runtime.h"
#include "modmachine.h"
#include "mphalport.h"
#undef DHT_DEBUG
#define PLATFORM_ERR 0
#define PLATFORM_OK 1
#define PLATFORM_DHT11_WAKEUP_MS 22
#define PLATFORM_DHT2X_WAKEUP_MS 2
// RX idle threshold [us]
// needs to be larger than any duration occurring during bit slots
// datasheet specs up to 200us for "Bus master has released time"
#define DHT_DURATION_RX_IDLE (250)
// zero bit duration threshold [us]
// a high phase
// * shorter than this is detected as a zero bit
// * longer than this is detected as a one bit
#define DHT_DURATION_ZERO (50)
// grouped information for RMT management
static struct {
int channel;
RingbufHandle_t rb;
int gpio;
} dht_rmt = {-1, NULL, -1};
typedef enum {
LDHT_OK = 0,
LDHT_ERROR_CHECKSUM = -1,
LDHT_ERROR_TIMEOUT = -2,
LDHT_INVALID_VALUE = -999
} ldht_result_t;
typedef enum {
LDHT11,
LDHT2X
} ldht_type_t;
//----------------------------
static void dht_deinit( void )
{
// drive idle level 1
gpio_set_level( dht_rmt.gpio, 1 );
rmt_rx_stop( dht_rmt.channel );
rmt_driver_uninstall( dht_rmt.channel );
platform_rmt_release( dht_rmt.channel );
// invalidate channel and gpio assignments
dht_rmt.channel = -1;
dht_rmt.gpio = -1;
}
//-------------------------------------
static int dht_init( uint8_t gpio_num )
{
// acquire an RMT module for RX
if ((dht_rmt.channel = platform_rmt_allocate( 1 )) >= 0) {
#ifdef DHT_DEBUG
mp_printf(&mp_plat_print, "[dht] RMT RX channel: %d\n", dht_rmt.channel);
#endif
rmt_config_t rmt_rx;
rmt_rx.channel = dht_rmt.channel;
rmt_rx.gpio_num = gpio_num;
rmt_rx.clk_div = 80; // base period is 1us
rmt_rx.mem_block_num = 1;
rmt_rx.rmt_mode = RMT_MODE_RX;
rmt_rx.rx_config.filter_en = true;
rmt_rx.rx_config.filter_ticks_thresh = 30;
rmt_rx.rx_config.idle_threshold = DHT_DURATION_RX_IDLE;
if (rmt_config( &rmt_rx ) == ESP_OK) {
if (rmt_driver_install( rmt_rx.channel, 512, ESP_INTR_FLAG_LOWMED | ESP_INTR_FLAG_IRAM | ESP_INTR_FLAG_SHARED ) == ESP_OK) {
//rmt_get_ringbuf_handler( dht_rmt.channel, &dht_rmt.rb );
rmt_get_ringbuf_handle( dht_rmt.channel, &dht_rmt.rb );
dht_rmt.gpio = gpio_num;
// use gpio for TX direction
// drive idle level 1
gpio_set_level( gpio_num, 1 );
gpio_pullup_dis( gpio_num );
gpio_pulldown_dis( gpio_num );
gpio_set_direction( gpio_num, GPIO_MODE_INPUT_OUTPUT_OD );
gpio_set_intr_type( gpio_num, GPIO_INTR_DISABLE );
return PLATFORM_OK;
}
}
platform_rmt_release( dht_rmt.channel );
}
return PLATFORM_ERR;
}
//-------------------------------------------------------------------------
int platform_dht_read( uint8_t gpio_num, uint8_t wakeup_ms, uint8_t *data )
{
if (dht_init( gpio_num ) != PLATFORM_OK)
return PLATFORM_ERR;
// send start signal and arm RX channel
TickType_t xDelay = wakeup_ms / portTICK_PERIOD_MS;
if (xDelay == 0) xDelay = 1;
gpio_set_level( gpio_num, 0 ); // pull wire low
vTaskDelay( xDelay ); // time low phase
rmt_rx_start( dht_rmt.channel, true ); // arm RX channel
gpio_set_level( gpio_num, 1 ); // release wire
// wait for incoming bit stream
size_t rx_size;
rmt_item32_t* rx_items = (rmt_item32_t *)xRingbufferReceive( dht_rmt.rb, &rx_size, pdMS_TO_TICKS( 100 ) );
// default is "no error"
// error conditions have to overwrite this with PLATFORM_ERR
int res = PLATFORM_OK;
if (rx_items) {
#ifdef DHT_DEBUG
mp_printf(&mp_plat_print, "[dht] rx_items received: %d\n", rx_size);
for (size_t i = 0; i < rx_size / 4; i++) {
mp_printf(&mp_plat_print, "[dht] level: %d, duration %d", rx_items[i].level0, rx_items[i].duration0);
mp_printf(&mp_plat_print, " level: %d, duration %d\n", rx_items[i].level1, rx_items[i].duration1);
}
#endif
// we expect 40 bits of payload plus a response bit and two edges for start and stop signals
// each bit on the wire consumes 2 rmt samples (and 2 rmt samples stretch over 4 bytes)
if (rx_size >= (5*8 + 1+1) * 4) {
// check framing
if (rx_items[ 0].level0 == 1 && // rising edge of the start signal
rx_items[ 0].level1 == 0 && rx_items[1].level0 == 1 && // response signal
rx_items[41].level1 == 0) { // falling edge of stop signal
#ifdef DHT_DEBUG
mp_printf(&mp_plat_print, "[dht] data: ");
#endif
// run through the bytes
for (size_t byte = 0; byte < 5 && res == PLATFORM_OK; byte++) {
size_t bit_pos = 1 + byte*8;
data[byte] = 0;
// decode the bits inside a byte
for (size_t bit = 0; bit < 8; bit++, bit_pos++) {
if (rx_items[bit_pos].level1 != 0) {
// not a falling edge, terminate decoding
res = PLATFORM_ERR;
break;
}
// ignore duration of low level
// data is sent MSB first
data[byte] <<= 1;
if (rx_items[bit_pos + 1].level0 == 1 && rx_items[bit_pos + 1].duration0 > DHT_DURATION_ZERO)
data[byte] |= 1;
}
#ifdef DHT_DEBUG
mp_printf(&mp_plat_print, "%02x ", data[byte]);
#endif
}
#ifdef DHT_DEBUG
mp_printf(&mp_plat_print, "\n");
#endif
// all done
} else {
// framing mismatch on start, response, or stop signals
res = PLATFORM_ERR;
}
} else {
// too few bits received
res = PLATFORM_ERR;
}
vRingbufferReturnItem( dht_rmt.rb, (void *)rx_items );
} else {
// time out occurred, this indicates an unconnected / misconfigured bus
res = PLATFORM_ERR;
}
dht_deinit();
return res;
}
//-------------------------------------------------------------------------
static int ldht_compute_data11( uint8_t *data, double *temp, double *humi )
{
*humi = data[0];
*temp = data[2];
uint8_t sum = data[0] + data[1] + data[2] + data[3];
return sum == data[4] ? LDHT_OK : LDHT_ERROR_CHECKSUM;
}
//-------------------------------------------------------------------------
static int ldht_compute_data2x( uint8_t *data, double *temp, double *humi )
{
*humi = (uint16_t)((data[0] * 256 + data[1])) / 10;
*temp = (uint16_t)(((data[2] & 0x7f) * 256 + data[3])) / 10;
if (data[2] & 0x80)
*temp = - *temp;
uint8_t sum = data[0] + data[1] + data[2] + data[3];
return sum == data[4] ? LDHT_OK : LDHT_ERROR_CHECKSUM;
}
// ==== MicroPython bindings for DHT ===========================================
typedef struct _machine_dht_obj_t {
mp_obj_base_t base;
uint8_t pin;
uint8_t type;
} machine_dht_obj_t;
//--------------------------------------------------------------------------------------------
STATIC void machine_dht_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind)
{
machine_dht_obj_t *self = self_in;
char stype[8];
if (self->type == LDHT11) sprintf(stype, "DHT11");
else if (self->type == LDHT2X) sprintf(stype, "DHT2X");
else sprintf(stype, "??");
mp_printf(print, "DHT(Pin=%u, Type=%s)", self->pin, stype);
}
//-------------------------------------------------------
STATIC const mp_arg_t machine_dht_init_allowed_args[] = {
{ MP_QSTR_pin, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} },
{ MP_QSTR_type, MP_ARG_INT, {.u_int = LDHT11} },
};
//------------------------------------------------------------------------------------------------------------
mp_obj_t machine_dht_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args)
{
enum { ARG_pin, ARG_type };
mp_arg_val_t args[MP_ARRAY_SIZE(machine_dht_init_allowed_args)];
mp_arg_parse_all_kw_array(n_args, n_kw, all_args, MP_ARRAY_SIZE(machine_dht_init_allowed_args), machine_dht_init_allowed_args, args);
uint8_t pin;
uint8_t dht_type = args[ARG_type].u_int;
pin = machine_pin_get_id(args[ARG_pin].u_obj);
// Setup the DHT object
machine_dht_obj_t *self = m_new_obj(machine_dht_obj_t );
self->base.type = &machine_dht_type;
self->pin = pin;
self->type = dht_type;
return MP_OBJ_FROM_PTR(self);
}
//------------------------------------------------
STATIC mp_obj_t machine_dht_read(mp_obj_t self_in)
{
machine_dht_obj_t *self = self_in;
uint8_t data[5];
double temp = -999.0, humi= -999.0;
mp_obj_t tuple[3];
int res = platform_dht_read( self->pin, self->type == LDHT11 ? PLATFORM_DHT11_WAKEUP_MS : PLATFORM_DHT2X_WAKEUP_MS, data );
if (res == PLATFORM_OK) {
switch (self->type) {
case LDHT11:
res = ldht_compute_data11( data, &temp, &humi );
break;
case LDHT2X:
res = ldht_compute_data2x( data, &temp, &humi );
break;
default:
res = LDHT_INVALID_VALUE;
temp = 0; humi = 0;
break;
}
if (res == LDHT_OK) tuple[0] = mp_const_true;
else tuple[0] = mp_const_false;
}
else {
tuple[0] = mp_const_false;
}
tuple[1] = mp_obj_new_float(temp);
tuple[2] = mp_obj_new_float(humi);
return mp_obj_new_tuple(3, tuple);
}
MP_DEFINE_CONST_FUN_OBJ_1(machine_dht_read_obj, machine_dht_read);
//---------------------------------------------------------------------
STATIC mp_obj_t machine_dht_readinto(mp_obj_t self_in, mp_obj_t buf_in)
{
machine_dht_obj_t *self = self_in;
mp_buffer_info_t bufinfo;
mp_get_buffer_raise(buf_in, &bufinfo, MP_BUFFER_WRITE);
if (bufinfo.len < 5) {
nlr_raise(mp_obj_new_exception_msg(&mp_type_ValueError, "buffer too small"));
}
int res = platform_dht_read( self->pin, self->type == LDHT11 ? PLATFORM_DHT11_WAKEUP_MS : PLATFORM_DHT2X_WAKEUP_MS, bufinfo.buf );
if (res != PLATFORM_OK) {
return mp_const_false;
}
return mp_const_true;
}
MP_DEFINE_CONST_FUN_OBJ_2(machine_dht_readinto_obj, machine_dht_readinto);
//
//================================================================
STATIC const mp_rom_map_elem_t machine_dht_locals_dict_table[] = {
{ MP_ROM_QSTR(MP_QSTR_readinto), (mp_obj_t)&machine_dht_readinto_obj },
{ MP_ROM_QSTR(MP_QSTR_read), (mp_obj_t)&machine_dht_read_obj },
{ MP_ROM_QSTR(MP_QSTR_DHT11), MP_ROM_INT(LDHT11) },
{ MP_ROM_QSTR(MP_QSTR_DHT2X), MP_ROM_INT(LDHT2X) },
};
STATIC MP_DEFINE_CONST_DICT(machine_dht_locals_dict, machine_dht_locals_dict_table);
//======================================
const mp_obj_type_t machine_dht_type = {
{ &mp_type_type },
.name = MP_QSTR_DHT,
.print = machine_dht_print,
.make_new = machine_dht_make_new,
.locals_dict = (mp_obj_dict_t*)&machine_dht_locals_dict,
};