Files
sakabin 070e06db00 update micropython
add m5stack.py
add m5ui.py
2018-12-04 14:06:15 +08:00

1047 lines
37 KiB
C

/*
* This file is part of the MicroPython ESP32 project, https://github.com/loboris/MicroPython_ESP32_psRAM_LoBo
*
* The MIT License (MIT)
*
* 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.
*/
#include <stdio.h>
#include <string.h>
#include <math.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "driver/gpio.h"
#include "driver/adc.h"
#include "driver/timer.h"
#include "driver/i2s.h"
#include "esp_adc_cal.h"
#include "soc/rtc_cntl_reg.h"
#include "soc/sens_reg.h"
#include "esp_log.h"
#include "py/runtime.h"
#include "py/mphal.h"
#include "modmachine.h"
#include "py/objarray.h"
#include "extmod/vfs_native.h"
#define ADC1_CHANNEL_HALL ADC1_CHANNEL_MAX
#define I2S_RD_BUF_SIZE (1024*2)
typedef struct _madc_obj_t {
mp_obj_base_t base;
int gpio_id;
adc_unit_t adc_num;
adc1_channel_t adc_chan;
adc_atten_t atten;
adc_bits_width_t width;
mp_obj_t callback;
void *buffer;
FILE *fhndl;
uint8_t val_shift;
size_t buf_len;
size_t buf_ptr;
int64_t interval;
int64_t summ;
int64_t rms_summ;
int min;
int max;
uint8_t cal_read;
} madc_obj_t;
extern int MainTaskCore;
bool adc_timer_active = false;
bool collect_active = false;
intr_handle_t adc_timer_handle = NULL;
static uint16_t adc1_chan_used = 0;
static uint16_t adc2_chan_used = 0;
static int8_t adc_width = -1;
static int8_t last_adc_width = -1;
static int8_t last_adc_num = -1;
static uint32_t adc_vref = 1100;
static uint32_t last_adc_vref = 0;
static adc_atten_t last_atten = ADC_ATTEN_MAX;
static adc_atten_t last_atten2 = ADC_ATTEN_MAX;
static esp_adc_cal_characteristics_t characteristics;
static uint64_t collect_start_time = 0;
static uint64_t collect_end_time = 0;
static bool task_running = false;
static bool task_stop = false;
static const uint8_t adc1_gpios[ADC1_CHANNEL_MAX] = {36, 37, 38, 39, 32, 33, 34, 35};
static const uint8_t adc2_gpios[ADC2_CHANNEL_MAX] = {4, 0, 2, 15, 13, 12, 14, 27, 25, 26};
//-------------------------------------
static void set_width(madc_obj_t *self)
{
if (adc_width != self->width) {
if (self->adc_num == ADC_UNIT_1) {
esp_err_t err = adc1_config_width(self->width);
if (err != ESP_OK) mp_raise_ValueError("Set width Error");
}
else {
adc_set_data_width(self->adc_num, self->width);
}
adc_width = self->width;
}
}
//-----------------------------------------------------
static int get_adc_channel(adc_unit_t adc_num, int pin)
{
int channel = -1;
if (adc_num == ADC_UNIT_1) {
for (int i=0; i < ADC1_CHANNEL_MAX; i++) {
if (adc1_gpios[i] == pin) {
channel = i;
break;
}
}
}
else {
for (int i=0; i < ADC2_CHANNEL_MAX; i++) {
if (adc2_gpios[i] == pin) {
channel = i;
break;
}
}
}
return channel;
}
//======================================
static void adc_task(void *pvParameters)
{
task_running = true;
madc_obj_t *self = (madc_obj_t *)pvParameters;
uint8_t *buff8 = NULL;
uint16_t *buff16 = NULL;
//char* i2s_read_buff = NULL;
uint16_t *i2s_read_buff = NULL;
int arr_idx = 0;
size_t bytes_read;
//ESP_LOGE("ADC", "To file: %s, len=%d, shift=%d", self->fhndl ? "True" : "False", self->buf_len, self->val_shift);
if (self->fhndl) {
// Allocate temporary data buffer
if (self->val_shift) buff8 = malloc(I2S_RD_BUF_SIZE/2);
else buff16 = malloc(I2S_RD_BUF_SIZE);
if ((buff8 == NULL) && (buff16 == NULL)) {
fclose(self->fhndl);
ESP_LOGE("ADC", "Error allocating adc buffer");
goto exit;
}
}
else {
if (self->val_shift) buff8 = (uint8_t *)self->buffer;
else buff16 = (uint16_t *)self->buffer;
}
// allocate i2s read buffer
i2s_read_buff = calloc(I2S_RD_BUF_SIZE, 1);
if (i2s_read_buff == NULL) {
if (self->fhndl) {
fclose(self->fhndl);
if (buff8) free(buff8);
if (buff16) free(buff16);
}
ESP_LOGE("ADC", "Error allocating i2s read buffer");
goto exit;
}
self->buf_ptr = 0;
collect_start_time = esp_timer_get_time(); //mp_hal_ticks_us();
collect_end_time = collect_start_time;
// read ADC data
while (self->buf_ptr < self->buf_len) {
// read data from I2S bus, in this case, from ADC.
i2s_read(0, (void *)i2s_read_buff, I2S_RD_BUF_SIZE, &bytes_read, 1000);
if (bytes_read != I2S_RD_BUF_SIZE) {
ESP_LOGE("ADC", "I2S error reading (%d)", bytes_read);
break;
}
// save read ADC values to the output buffer
for (int i=0; i<(I2S_RD_BUF_SIZE/2); i++) {
if (self->buf_ptr < self->buf_len) {
uint16_t val = i2s_read_buff[i] & 0x0fff;
if (self->val_shift) buff8[arr_idx++] = (uint8_t)(val >> self->val_shift);
else buff16[arr_idx++] = val;
self->buf_ptr++;
}
}
if ( (self->fhndl) && ( (arr_idx >= (I2S_RD_BUF_SIZE/2)) || (self->buf_ptr < self->buf_len) ) ) {
// save buffer to file
int res;
if (self->val_shift) res = fwrite(buff8, 1, arr_idx, self->fhndl);
else res = fwrite(buff16, 2, arr_idx, self->fhndl);
if (res != arr_idx) {
ESP_LOGE("ADC", "Error writing to file at %d", arr_idx);
break;
}
arr_idx = 0;
}
//#if CONFIG_TASK_WDT_CHECK_IDLE_TASK_CPU0 || CONFIG_TASK_WDT_CHECK_IDLE_TASK_CPU1
//vTaskDelay(0); // allow other core idle task to reset the watchdog
//#endif
}
collect_end_time = esp_timer_get_time(); //mp_hal_ticks_us();
if (self->fhndl) {
// reading to file, close file and free the buffer
fclose(self->fhndl);
self->fhndl = NULL;
if (buff8) free(buff8);
if (buff16) free(buff16);
}
if (self->callback) mp_sched_schedule(self->callback, self, NULL);
exit:
// i2s cleanup
i2s_adc_disable(0);
i2s_driver_uninstall(0);
i2s_driver_installed = false;
if (i2s_read_buff) free(i2s_read_buff);
esp_log_level_set("I2S", CONFIG_LOG_DEFAULT_LEVEL);
task_stop = false;
task_running = false;
vTaskDelete(NULL);
}
//======================================
// ADC Timer interrupt function
//======================================
STATIC void adc_timer_isr(void *self_in)
{
madc_obj_t *self = (madc_obj_t *)self_in;
// Clear timer interrupt
if (ADC_TIMER_NUM & 2) {
if (ADC_TIMER_NUM & 1) TIMERG1.int_clr_timers.t1 = 1;
else TIMERG1.int_clr_timers.t0 = 1;
}
else {
if (ADC_TIMER_NUM & 1) TIMERG0.int_clr_timers.t1 = 1;
else TIMERG0.int_clr_timers.t0 = 1;
}
uint16_t *buffer16 = (uint16_t *)self->buffer;
uint8_t *buffer8 = (uint8_t *)self->buffer;
if (self->buf_ptr == 0) collect_start_time = esp_timer_get_time(); //mp_hal_ticks_us();
// --- Read ADC value ---
int val = 0;
esp_err_t err = ESP_OK;
if (self->cal_read) {
err = esp_adc_cal_get_voltage(self->adc_chan, &characteristics, (uint32_t *)&val);
}
else {
if (self->adc_num == ADC_UNIT_1) {
val = adc1_get_raw(self->adc_chan);
if (val == -1) err = ESP_FAIL;
}
else err = adc2_get_raw(self->adc_chan, self->atten, &val); //if (err != ESP_OK) : Cannot read, ADC2 used by Wi-Fi"
}
// --- Calculate values & save to buffer if provided ---
if (err == ESP_OK) {
if (self->buf_ptr < self->buf_len) {
if (self->buffer) {
// store value in buffer
if (self->val_shift == 0) buffer16[self->buf_ptr] = (uint16_t)val;
else {
if (self->cal_read) {
// calibrated read (mV) converted to 8-bit value
if (val > 2500) buffer8[self->buf_ptr] = 255;
else buffer8[self->buf_ptr] = val / 10;
}
else buffer8[self->buf_ptr] = (uint8_t)(val >> self->val_shift);
}
}
self->buf_ptr++;
self->summ += val;
self->rms_summ += (val * val);
if (val < self->min) self->min = val;
if (val > self->max) self->max = val;
}
}
if ((err != ESP_OK) || (self->buf_ptr >= self->buf_len)) {
// --- Finished, all data read or ADC read error ---
timer_disable_intr((ADC_TIMER_NUM >> 1) & 1, ADC_TIMER_NUM & 1);
timer_pause((ADC_TIMER_NUM >> 1) & 1, ADC_TIMER_NUM & 1);
if (adc_timer_handle) {
esp_intr_free(adc_timer_handle);
adc_timer_handle = NULL;
}
collect_end_time = esp_timer_get_time(); //mp_hal_ticks_us();
if (self->callback) mp_sched_schedule(self->callback, self, NULL);
self->buffer = NULL;
adc_timer_active = false;
collect_active = false;
}
else {
// --- Not yet finished, enable alarm interrupt ---
if (ADC_TIMER_NUM & 2) TIMERG1.hw_timer[ADC_TIMER_NUM & 1].config.alarm_en = true;
else TIMERG0.hw_timer[ADC_TIMER_NUM & 1].config.alarm_en = true;
}
}
// Initialize and start the timer for ADC data collection
//--------------------------------------------------------------
STATIC esp_err_t start_adc_timer(madc_obj_t *adc_obj, bool wait)
{
if (adc_timer_handle) {
esp_intr_free(adc_timer_handle);
adc_timer_handle = NULL;
}
adc_obj->summ = 0;
adc_obj->rms_summ = 0;
adc_obj->min = 999999;
adc_obj->max = -999999;
collect_start_time = 0;
collect_end_time = 0;
esp_err_t err = ESP_OK;
// Set width if needed
if (adc_width != adc_obj->width) {
if (adc_obj->adc_num == ADC_UNIT_1) err = adc1_config_width(adc_obj->width);
else err = adc_set_data_width(adc_obj->adc_num, adc_obj->width);
adc_width = adc_obj->width;
if (err != ESP_OK) return err;
}
if (adc_obj->adc_num == ADC_UNIT_2) {
if (last_atten2 != adc_obj->atten) {
err = adc2_config_channel_atten(adc_obj->adc_chan, adc_obj->atten);
last_atten2 = adc_obj->atten;
}
if (err != ESP_OK) return err;
}
if (adc_obj->cal_read) {
if ((last_adc_num != adc_obj->adc_num) || (last_adc_vref != adc_vref) || (last_atten != adc_obj->atten) || (last_adc_width != adc_obj->width)) {
// New characterization needed
esp_adc_cal_characterize(adc_obj->adc_num, adc_obj->atten, adc_obj->width, adc_vref, &characteristics);
adc_vref = characteristics.vref;
last_adc_vref = adc_vref;
last_atten = adc_obj->atten;
last_adc_width = adc_obj->width;
last_adc_num = adc_obj->adc_num;
}
}
timer_config_t config;
config.counter_dir = TIMER_COUNT_UP;
config.intr_type = TIMER_INTR_LEVEL;
config.counter_en = TIMER_PAUSE;
config.alarm_en = TIMER_ALARM_EN;
config.auto_reload = true;
config.divider = ADC_TIMER_DIVIDER;
err = timer_init((ADC_TIMER_NUM >> 1) & 1, ADC_TIMER_NUM & 1, &config);
if (err != ESP_OK) return err;
// Timer's counter will initially start from value below.
// Also, if auto_reload is set, this value will be automatically reload on alarm
err = timer_set_counter_value((ADC_TIMER_NUM >> 1) & 1, ADC_TIMER_NUM & 1, 0x00000000ULL);
if (err != ESP_OK) return err;
// Configure the alarm value and the interrupt on alarm.
err = timer_set_alarm_value((ADC_TIMER_NUM >> 1) & 1, ADC_TIMER_NUM & 1, adc_obj->interval);
if (err != ESP_OK) return err;
// Enable timer interrupt
err = timer_enable_intr((ADC_TIMER_NUM >> 1) & 1, ADC_TIMER_NUM & 1);
if (err != ESP_OK) return err;
// Register interrupt callback
err = timer_isr_register((ADC_TIMER_NUM >> 1) & 1, ADC_TIMER_NUM & 1, adc_timer_isr, (void*)adc_obj, ESP_INTR_FLAG_LEVEL1, &adc_timer_handle);
if (err != ESP_OK) return err;
// Start the timer
err = timer_start((ADC_TIMER_NUM >> 1) & 1, ADC_TIMER_NUM & 1);
if (err != ESP_OK) {
timer_pause((ADC_TIMER_NUM >> 1) & 1, ADC_TIMER_NUM & 1);
if (adc_timer_handle) {
esp_intr_free(adc_timer_handle);
adc_timer_handle = NULL;
}
return err;
}
if (wait) {
while (collect_active) {
mp_hal_delay_ms(5);
}
}
return ESP_OK;
}
//------------------------------------------------------------------------------------------------------------
STATIC mp_obj_t madc_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_unit };
static const mp_arg_t allowed_args[] = {
{ MP_QSTR_pin, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_obj = mp_const_none}},
{ MP_QSTR_unit, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = ADC_UNIT_1}},
};
// parse arguments
mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
mp_arg_parse_all_kw_array(n_args, n_kw, all_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
esp_err_t err = 0;
mp_arg_check_num(n_args, n_kw, 1, 1, true);
int pin_id = 0;
pin_id = machine_pin_get_gpio(args[ARG_pin].u_obj);
madc_obj_t *self = m_new_obj(madc_obj_t);
self->base.type = &machine_adc_type;
self->buffer = NULL;
self->buf_len = 0;
self->buf_ptr = 0;
self->interval = 0;
self->cal_read = 0;
self->callback = NULL;
self->adc_num = args[ARG_unit].u_int;
if ((self->adc_num != 0) && (self->adc_num != ADC_UNIT_1) && (self->adc_num != ADC_UNIT_2)) {
mp_raise_ValueError("invalid ADC unit (1 and 2 allowed)");
}
self->atten = ADC_ATTEN_DB_0;
self->width = ADC_WIDTH_BIT_12;
if (pin_id != ADC1_CHANNEL_HALL) {
int channel = -1;
if (self->adc_num == 0) {
channel = get_adc_channel(ADC_UNIT_1, pin_id);
if (channel >= 0) self->adc_num = ADC_UNIT_1;
else {
channel = get_adc_channel(ADC_UNIT_2, pin_id);
if (channel >= 0) self->adc_num = ADC_UNIT_2;
}
}
else channel = get_adc_channel(self->adc_num, pin_id);
if (channel < 0) mp_raise_ValueError("invalid Pin for ADC");
self->adc_chan = channel;
self->gpio_id = pin_id;
if (self->adc_num == ADC_UNIT_1) {
if ((adc1_chan_used & 0x0100) && ((pin_id == 36) || (pin_id == 39))) mp_raise_ValueError("hall used, cannot use pins 36 & 39");
if (adc1_chan_used & (1 << self->adc_chan)) mp_raise_ValueError("pin already used for adc");
adc1_chan_used |= (1 << self->adc_chan);
err = adc_gpio_init(self->adc_num, self->adc_chan);
if (err != ESP_OK) mp_raise_ValueError("Error configuring ADC gpio");
err = adc1_config_channel_atten(self->adc_chan, ADC_ATTEN_DB_0);
if (err != ESP_OK) mp_raise_ValueError("Error configuring attenuation");
}
else {
if (adc2_chan_used & (1 << self->adc_chan)) mp_raise_ValueError("pin already used for adc");
adc2_chan_used |= (1 << self->adc_chan);
gpio_pad_select_gpio(self->gpio_id);
gpio_set_direction(self->gpio_id, GPIO_MODE_DISABLE);
gpio_set_pull_mode(self->gpio_id, GPIO_FLOATING);
adc_gpio_init(self->adc_num, self->adc_chan);
if (err != ESP_OK) mp_raise_ValueError("Error configuring ADC gpio");
if (last_atten2 != self->atten) {
adc2_config_channel_atten(self->adc_chan, self->atten);
last_atten2 = self->atten;
}
}
}
else {
self->adc_num = ADC_UNIT_1;
if (adc1_chan_used & 0x09) mp_raise_ValueError("adc on gpio 36 or 39 used");
if (adc1_chan_used & 0x0100) mp_raise_ValueError("hall already used");
adc1_chan_used |= 0x0100;
self->adc_chan = ADC1_CHANNEL_HALL;
self->gpio_id = GPIO_NUM_MAX;
}
set_width(self);
return MP_OBJ_FROM_PTR(self);
}
//------------------------------------------------------------
static void _is_init(madc_obj_t *self, bool iflag, bool cflag)
{
if ((iflag) && (self->gpio_id < 0)) {
mp_raise_ValueError("Not initialized");
}
if ((cflag) && (collect_active | task_running)) {
mp_raise_ValueError("collecting data in progress");
}
if ((cflag) && (adc_timer_active)) {
mp_raise_ValueError("ADC timer used by other module");
}
}
//-------------------------------------------
STATIC mp_obj_t madc_deinit(mp_obj_t self_in)
{
madc_obj_t *self = self_in;
_is_init(self, false, true);
if (self->gpio_id < 0) return mp_const_none;
if (self->adc_num == ADC_UNIT_1) {
if (self->adc_chan == ADC1_CHANNEL_HALL) {
adc1_chan_used &= 0x00FF;
gpio_pad_select_gpio(36);
gpio_pad_select_gpio(39);
}
else {
adc1_chan_used &= (~(1 << self->adc_chan) & 0x1FF);
gpio_pad_select_gpio(self->gpio_id);
gpio_set_direction(self->gpio_id, GPIO_MODE_INPUT);
gpio_set_pull_mode(self->gpio_id, GPIO_FLOATING);
}
}
else {
adc2_chan_used &= (~(1 << self->adc_chan) & 0x3FF);
gpio_pad_select_gpio(self->gpio_id);
gpio_set_direction(self->gpio_id, GPIO_MODE_INPUT);
gpio_set_pull_mode(self->gpio_id, GPIO_FLOATING);
}
self->gpio_id = -1;
return mp_const_none;
}
MP_DEFINE_CONST_FUN_OBJ_1(madc_deinit_obj, madc_deinit);
//---------------------------------------------------------------------------------------
STATIC void madc_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) {
madc_obj_t *self = self_in;
if (self->gpio_id < 0) {
mp_printf(print, "ADC( deinitialized )");
return;
}
char satten[16];
char spin[8];
if (self->atten == ADC_ATTEN_DB_0) sprintf(satten, "0dB (1.1V)");
else if (self->atten == ADC_ATTEN_DB_2_5) sprintf(satten, "2.5dB (1.5V)");
else if (self->atten == ADC_ATTEN_DB_6) sprintf(satten, "6dB (2.5V)");
else if (self->atten == ADC_ATTEN_DB_11) sprintf(satten, "11dB (3.9V)");
else sprintf(satten, "Unknown");
if (self->gpio_id == GPIO_NUM_MAX) sprintf(spin, "HALL");
else sprintf(spin, "Pin(%u)", self->gpio_id);
mp_printf(print, "ADC(%s: unit=ADC%d, chan=%d, width=%u bits, atten=%s, Vref=%u mV)", spin, self->adc_num, self->adc_chan, self->width+9, satten, adc_vref);
}
//----------------------------------------------
STATIC mp_obj_t madc_readraw(mp_obj_t self_in) {
madc_obj_t *self = self_in;
_is_init(self, true, true);
int val = 0;
if (self->adc_num == ADC_UNIT_1) {
set_width(self);
if (self->gpio_id == GPIO_NUM_MAX) val= hall_sensor_read();
else val = adc1_get_raw(self->adc_chan);
if (val == -1) mp_raise_ValueError("Parameter Error (ADC raw read)");
}
else {
if (last_atten2 != self->atten) {
adc2_config_channel_atten(self->adc_chan, self->atten);
last_atten2 = self->atten;
}
esp_err_t err = adc2_get_raw(self->adc_chan, self->atten, &val);
if (err != ESP_OK) mp_raise_ValueError("Cannot read, ADC2 used by Wi-Fi");
}
return MP_OBJ_NEW_SMALL_INT(val);
}
MP_DEFINE_CONST_FUN_OBJ_1(madc_readraw_obj, madc_readraw);
//-------------------------------------------
STATIC mp_obj_t madc_read(mp_obj_t self_in) {
madc_obj_t *self = self_in;
_is_init(self, true, true);
set_width(self);
int adc_val = 0;
if (self->gpio_id == GPIO_NUM_MAX) adc_val= hall_sensor_read();
else {
if ((last_adc_num != self->adc_num) || (last_adc_vref != adc_vref) || (last_atten != self->atten) || (last_adc_width != self->width)) {
// New characterization needed
esp_adc_cal_characterize(self->adc_num, self->atten, self->width, adc_vref, &characteristics);
adc_vref = characteristics.vref;
last_adc_vref = adc_vref;
last_atten = self->atten;
last_adc_width = self->width;
last_adc_num = self->adc_num;
}
if ((self->adc_num == ADC_UNIT_2) && (last_atten2 != self->atten)) {
adc2_config_channel_atten(self->adc_chan, self->atten);
last_atten2 = self->atten;
}
esp_err_t err = esp_adc_cal_get_voltage(self->adc_chan, &characteristics, (uint32_t *)&adc_val);
if (err != ESP_OK) {
if (self->adc_num == ADC_UNIT_2) mp_raise_ValueError("Cannot read, ADC2 used by Wi-Fi");
else mp_raise_ValueError("Error reading");
}
}
return MP_OBJ_NEW_SMALL_INT(adc_val);
}
MP_DEFINE_CONST_FUN_OBJ_1(madc_read_obj, madc_read);
//---------------------------------------------------------------
STATIC mp_obj_t madc_atten(mp_obj_t self_in, mp_obj_t atten_in) {
madc_obj_t *self = self_in;
_is_init(self, true, true);
if (self->gpio_id == GPIO_NUM_MAX) return mp_const_none;
adc_atten_t atten = mp_obj_get_int(atten_in);
if ((atten < ADC_ATTEN_DB_0) || (atten > ADC_ATTEN_DB_11)) mp_raise_ValueError("Unsupported atten value");
esp_err_t err;
if (self->adc_num == ADC_UNIT_1) {
err = adc1_config_channel_atten(self->adc_chan, atten);
if (err != ESP_OK) mp_raise_ValueError("Parameter Error (config attenuation)");
}
else {
if (last_atten2 != atten) {
adc2_config_channel_atten(self->adc_chan, atten);
last_atten2 = self->atten;
}
}
self->atten = atten;
return mp_const_none;
}
MP_DEFINE_CONST_FUN_OBJ_2(madc_atten_obj, madc_atten);
//---------------------------------------------------------------
STATIC mp_obj_t madc_width(mp_obj_t self_in, mp_obj_t width_in) {
madc_obj_t *self = self_in;
_is_init(self, true, true);
if (self->gpio_id == GPIO_NUM_MAX) return mp_const_none;
adc_bits_width_t width = mp_obj_get_int(width_in);
if ((width < ADC_WIDTH_9Bit) || (width > ADC_WIDTH_12Bit)) mp_raise_ValueError("Unsupported width value");
if (self) self->width = width;
return mp_const_none;
}
STATIC MP_DEFINE_CONST_FUN_OBJ_2(madc_width_obj, madc_width);
//-----------------------------------------------------------------------------------------------
STATIC mp_obj_t madc_vref_togpio(mp_uint_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
const mp_arg_t allowed_args[] = {
{ MP_QSTR_vref, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = -1} },
{ MP_QSTR_vref_topin, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = -1} },
};
mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
if (args[0].u_int > 0) {
uint32_t vref = args[0].u_int;
if ((vref < 1000) || (vref > 1200)) mp_raise_ValueError("Vref range: 1000~1200 (mV)");
adc_vref = vref;
}
uint32_t gpio = 0;
if (args[1].u_int > 0) {
gpio = args[1].u_int;
if ((gpio < 25) || (gpio > 27)) mp_raise_ValueError("Only gpios 25,26,27 can be used");
esp_err_t status = adc2_vref_to_gpio(gpio);
if (status != ESP_OK) mp_raise_ValueError("Error routing Vref to gpio");
}
mp_obj_t tuple[2];
tuple[0] = mp_obj_new_int(adc_vref);
if (gpio > 0) tuple[1] = mp_obj_new_int(gpio);
else tuple[1] = mp_const_false;
return mp_obj_new_tuple(2, tuple);
}
STATIC MP_DEFINE_CONST_FUN_OBJ_KW(madc_vref_togpio_obj, 0, madc_vref_togpio);
// ==== Collect and i2s read functions ======================================================
//-------------------------------------------------------------------------------------------
STATIC mp_obj_t madc_collect(mp_uint_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
enum { ARG_freq, ARG_len, ARG_readmv, ARG_data, ARG_callback, ARG_wait };
const mp_arg_t allowed_args[] = {
{ MP_QSTR_freq, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_obj = mp_const_none} },
{ MP_QSTR_len, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 0} },
{ MP_QSTR_readmv, MP_ARG_KW_ONLY | MP_ARG_BOOL, {.u_bool = false}},
{ MP_QSTR_data, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = mp_const_none} },
{ MP_QSTR_callback, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = mp_const_none} },
{ MP_QSTR_wait, MP_ARG_KW_ONLY | MP_ARG_BOOL, {.u_bool = false}},
};
madc_obj_t *self = MP_OBJ_TO_PTR(pos_args[0]);
_is_init(self, true, true);
// Get arguments
mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
mp_arg_parse_all(n_args-1, pos_args+1, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
adc_timer_active = true;
if (mpy_timers_used[ADC_TIMER_NUM]) {
adc_timer_active = false;
mp_raise_ValueError("ADC timer used by Timer module!");
}
if (self->gpio_id == GPIO_NUM_MAX) {
adc_timer_active = false;
mp_raise_ValueError("collect for hall sensor not allowed");
}
double freq = mp_obj_get_float(args[ARG_freq].u_obj);
if ((freq < 0.001) || (freq > 18000.0)) {
mp_raise_ValueError("frequency out of range (0.001 - 18000 Hz)");
}
double interv = (1.0 / freq) * ADC_TIMER_FREQ;
self->callback = NULL;
self->buffer = NULL;
self->buf_ptr = 0;
self->cal_read = args[ARG_readmv].u_bool;
self->buf_len = args[ARG_len].u_int;
self->interval = (int64_t)(round(interv));
if (args[ARG_callback].u_obj != mp_const_none) {
if ((!MP_OBJ_IS_FUN(args[ARG_callback].u_obj)) && (!MP_OBJ_IS_METH(args[ARG_callback].u_obj))) {
adc_timer_active = false;
mp_raise_ValueError("callback function expected");
}
self->callback = args[ARG_callback].u_obj;
}
if (args[ARG_data].u_obj != mp_const_none) {
// Collect to the provided array
if (!MP_OBJ_IS_TYPE(args[ARG_data].u_obj, &mp_type_array)) {
adc_timer_active = false;
mp_raise_ValueError("array argument expected");
}
mp_obj_array_t * arr = (mp_obj_array_t *)MP_OBJ_TO_PTR(args[ARG_data].u_obj);
if ((arr->typecode == 'h') || (arr->typecode == 'H')) {
self->val_shift = 0;
}
else if (arr->typecode == 'B') {
self->val_shift = self->width + 1;
}
else {
adc_timer_active = false;
mp_raise_ValueError("array argument of type 'h', 'H' or 'B' expected");
}
if (arr->len < 1) {
self->buf_len = 0;
adc_timer_active = false;
mp_raise_ValueError("array argument length must be >= 1");
}
self->buffer = arr->items;
if (self->buf_len < 1) self->buf_len = arr->len;
else if (arr->len < self->buf_len) self->buf_len = arr->len;
}
else if (self->buf_len < 1) {
self->buf_len = 0;
adc_timer_active = false;
mp_raise_ValueError("length must be >= 1");
}
collect_active = true;
if (start_adc_timer(self, args[ARG_wait].u_bool) != ESP_OK) {
adc_timer_active = false;
collect_active = false;
mp_raise_ValueError("Error starting ADC timer");
}
return mp_const_none;
}
STATIC MP_DEFINE_CONST_FUN_OBJ_KW(madc_collect_obj, 0, madc_collect);
//----------------------------------------------------------------------------------------------
STATIC mp_obj_t madc_read_timed(mp_uint_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
enum { ARG_data, ARG_freq, ARG_len, ARG_byte, ARG_wait, ARG_callback };
const mp_arg_t allowed_args[] = {
{ MP_QSTR_data, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_obj = mp_const_none} },
{ MP_QSTR_freq, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_obj = mp_const_none} },
{ MP_QSTR_nsamples, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = -1} },
{ MP_QSTR_byte, MP_ARG_KW_ONLY | MP_ARG_BOOL, {.u_bool = true} },
{ MP_QSTR_wait, MP_ARG_KW_ONLY | MP_ARG_BOOL, {.u_bool = false}},
{ MP_QSTR_callback, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = mp_const_none} },
};
madc_obj_t *self = MP_OBJ_TO_PTR(pos_args[0]);
_is_init(self, true, true);
if (self->gpio_id == GPIO_NUM_MAX) {
mp_raise_ValueError("timed read for hall sensor not allowed");
}
if (i2s_driver_installed) {
mp_raise_ValueError("Error: i2s used by other module");
}
// Get arguments
mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
mp_arg_parse_all(n_args-1, pos_args+1, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
self->callback = NULL;
self->buffer = NULL;
self->buf_ptr = 0;
self->cal_read = false;
self->buf_len = 0;
self->interval = 0;
self->val_shift = 0;
int freq = mp_obj_get_int(args[ARG_freq].u_obj);
if ((freq < 5000) || (freq > 500000)) {
mp_raise_ValueError("frequency out of range (5000 - 500000 Hz)");
}
if (args[ARG_callback].u_obj != mp_const_none) {
if ((!MP_OBJ_IS_FUN(args[ARG_callback].u_obj)) && (!MP_OBJ_IS_METH(args[ARG_callback].u_obj))) {
mp_raise_ValueError("callback function expected");
}
self->callback = args[ARG_callback].u_obj;
}
bool wait = args[ARG_wait].u_bool;
size_t length = args[ARG_len].u_int;
if (MP_OBJ_IS_STR(args[ARG_data].u_obj)) {
// reading to file
if (length < 1) {
mp_raise_ValueError("file length must be >= 1");
}
const char *dac_file = NULL;
char fullname[128] = {'\0'};
dac_file = mp_obj_str_get_str(args[0].u_obj);
int res = physicalPath(dac_file, fullname);
if ((res != 0) || (strlen(fullname) == 0)) {
mp_raise_ValueError("Error resolving file name");
}
self->fhndl = fopen(fullname, "wb");
if (self->fhndl == NULL) {
mp_raise_ValueError("Error opening file");
}
// Allocate temporary data buffer
if (args[ARG_byte].u_bool) {
self->val_shift = self->width + 1;
}
self->buf_len = length;
}
else if (MP_OBJ_IS_TYPE(args[ARG_data].u_obj, &mp_type_array)) {
// reading to array
mp_buffer_info_t src;
mp_get_buffer_raise(args[ARG_data].u_obj, &src, MP_BUFFER_WRITE);
mp_obj_array_t * arr = (mp_obj_array_t *)MP_OBJ_TO_PTR(args[ARG_data].u_obj);
if ((arr->typecode == 'h') && (arr->typecode != 'H') && (arr->typecode != 'B')) {
mp_raise_ValueError("array argument of type 'h', 'H' or 'B' expected");
}
if (arr->typecode == 'B') {
self->val_shift = self->width + 1;
}
self->buffer = arr->items;
if (arr->len < 1) {
mp_raise_ValueError("array argument length must be >= 1");
}
if ((length > 0) && (length < arr->len)) self->buf_len = length;
else self->buf_len = arr->len;
}
else {
mp_raise_ValueError("array or file name argument expected");
}
// configure i2s
i2s_config_t i2s_config = {
.mode = I2S_MODE_MASTER | I2S_MODE_RX | I2S_MODE_ADC_BUILT_IN, // Only RX, ADC input
//.mode = I2S_MODE_MASTER | I2S_MODE_RX | I2S_MODE_TX | I2S_MODE_DAC_BUILT_IN | I2S_MODE_ADC_BUILT_IN,
.sample_rate = freq,
.bits_per_sample = 16,
.channel_format = I2S_CHANNEL_FMT_RIGHT_LEFT,
.communication_format = I2S_COMM_FORMAT_I2S_MSB,
.dma_buf_count = 2,
.dma_buf_len = 1024,
.use_apll = false,
.intr_alloc_flags = ESP_INTR_FLAG_LEVEL1,
.fixed_mclk = 0
};
// install and start i2s driver
i2s_driver_install(0, &i2s_config, 0, NULL);
i2s_driver_installed = true;
// init ADC pad
i2s_set_adc_mode(self->adc_num, self->adc_chan);
i2s_adc_enable(0);
//i2s_set_sample_rates(0, freq);
task_stop = false;
esp_log_level_set("I2S", ESP_LOG_ERROR);
#if CONFIG_MICROPY_USE_BOTH_CORES
xTaskCreate(adc_task, "ADC_task", 2048, (void *)self, CONFIG_MICROPY_TASK_PRIORITY, NULL);
#else
xTaskCreatePinnedToCore(adc_task, "ADC_task", 2048, (void *)self, CONFIG_MICROPY_TASK_PRIORITY, NULL, MainTaskCore);
#endif
if (wait) {
mp_hal_delay_ms(3);
while (task_running) {
mp_hal_delay_ms(3);
}
}
return mp_const_none;
}
STATIC MP_DEFINE_CONST_FUN_OBJ_KW(madc_read_timed_obj, 0, madc_read_timed);
//----------------------------------------------------
STATIC mp_obj_t madc_get_collected(mp_obj_t self_in) {
madc_obj_t *self = self_in;
_is_init(self, true, true);
if (self->buf_len == 0) {
mp_raise_ValueError("no data collected");
}
// return statistics
int rms = self->rms_summ / self->buf_len;
int avrg = self->summ / self->buf_len;
double drms = sqrt((double)(rms));
rms = (int)(round(drms));
mp_obj_t tuple[4];
tuple[0] = mp_obj_new_int(self->min);
tuple[1] = mp_obj_new_int(self->max);
tuple[2] = mp_obj_new_int(avrg);
tuple[3] = mp_obj_new_int(rms);
return mp_obj_new_tuple(4, tuple);
}
MP_DEFINE_CONST_FUN_OBJ_1(madc_get_collected_obj, madc_get_collected);
//-----------------------------------------------
STATIC mp_obj_t madc_progress(mp_obj_t self_in) {
madc_obj_t *self = self_in;
_is_init(self, true, false);
bool active = collect_active | task_running;
mp_obj_t tuple[4];
tuple[0] = mp_obj_new_bool(active);
tuple[1] = mp_obj_new_int(self->buf_ptr);
tuple[2] = mp_obj_new_int(self->buf_len);
if (active) tuple[3] = mp_obj_new_int_from_ull(esp_timer_get_time() /*mp_hal_ticks_us()*/ - collect_start_time);
else tuple[3] = mp_obj_new_int_from_ull(collect_end_time - collect_start_time);
return mp_obj_new_tuple(4, tuple);
}
MP_DEFINE_CONST_FUN_OBJ_1(madc_progress_obj, madc_progress);
//---------------------------------------------------
STATIC mp_obj_t madc_stop_collect(mp_obj_t self_in) {
madc_obj_t *self = self_in;
_is_init(self, true, false);
if (task_running) {
task_stop = true;
while (task_running) {
vTaskDelay(2);
}
}
if (collect_active) {
timer_disable_intr((ADC_TIMER_NUM >> 1) & 1, ADC_TIMER_NUM & 1);
timer_pause((ADC_TIMER_NUM >> 1) & 1, ADC_TIMER_NUM & 1);
if (adc_timer_handle) {
esp_intr_free(adc_timer_handle);
adc_timer_handle = NULL;
}
collect_end_time = esp_timer_get_time(); //mp_hal_ticks_us();
self->buffer = NULL;
adc_timer_active = false;
collect_active = false;
}
return mp_const_none;
}
MP_DEFINE_CONST_FUN_OBJ_1(madc_stop_collect_obj, madc_stop_collect);
//=========================================================
STATIC const mp_rom_map_elem_t madc_locals_dict_table[] = {
{ MP_ROM_QSTR(MP_QSTR_read), MP_ROM_PTR(&madc_read_obj) },
{ MP_ROM_QSTR(MP_QSTR_readraw), MP_ROM_PTR(&madc_readraw_obj) },
{ MP_ROM_QSTR(MP_QSTR_read_timed), MP_ROM_PTR(&madc_read_timed_obj) },
{ MP_ROM_QSTR(MP_QSTR_collect), MP_ROM_PTR(&madc_collect_obj) },
{ MP_ROM_QSTR(MP_QSTR_collected), MP_ROM_PTR(&madc_get_collected_obj) },
{ MP_ROM_QSTR(MP_QSTR_stopcollect), MP_ROM_PTR(&madc_stop_collect_obj) },
{ MP_ROM_QSTR(MP_QSTR_progress), MP_ROM_PTR(&madc_progress_obj) },
{ MP_ROM_QSTR(MP_QSTR_atten), MP_ROM_PTR(&madc_atten_obj) },
{ MP_ROM_QSTR(MP_QSTR_width), MP_ROM_PTR(&madc_width_obj) },
{ MP_ROM_QSTR(MP_QSTR_vref), MP_ROM_PTR(&madc_vref_togpio_obj) },
{ MP_ROM_QSTR(MP_QSTR_deinit), MP_ROM_PTR(&madc_deinit_obj) },
{ MP_ROM_QSTR(MP_QSTR_HALL), MP_ROM_INT(ADC1_CHANNEL_MAX) },
{ MP_ROM_QSTR(MP_QSTR_ATTN_0DB), MP_ROM_INT(ADC_ATTEN_0db) },
{ MP_ROM_QSTR(MP_QSTR_ATTN_2_5DB), MP_ROM_INT(ADC_ATTEN_2_5db) },
{ MP_ROM_QSTR(MP_QSTR_ATTN_6DB), MP_ROM_INT(ADC_ATTEN_6db) },
{ MP_ROM_QSTR(MP_QSTR_ATTN_11DB), MP_ROM_INT(ADC_ATTEN_11db) },
{ MP_ROM_QSTR(MP_QSTR_WIDTH_9BIT), MP_ROM_INT(ADC_WIDTH_9Bit) },
{ MP_ROM_QSTR(MP_QSTR_WIDTH_10BIT), MP_ROM_INT(ADC_WIDTH_10Bit) },
{ MP_ROM_QSTR(MP_QSTR_WIDTH_11BIT), MP_ROM_INT(ADC_WIDTH_11Bit) },
{ MP_ROM_QSTR(MP_QSTR_WIDTH_12BIT), MP_ROM_INT(ADC_WIDTH_12Bit) },
};
STATIC MP_DEFINE_CONST_DICT(madc_locals_dict, madc_locals_dict_table);
//======================================
const mp_obj_type_t machine_adc_type = {
{ &mp_type_type },
.name = MP_QSTR_ADC,
.print = madc_print,
.make_new = madc_make_new,
.locals_dict = (mp_obj_t)&madc_locals_dict,
};