mirror of
https://github.com/m5stack/M5Stack_MicroPython.git
synced 2026-05-20 10:14:44 -07:00
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
424 lines
13 KiB
C
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,
|
|
};
|