mirror of
https://github.com/m5stack/M5Stack_MicroPython.git
synced 2026-05-20 10:14:44 -07:00
1092 lines
37 KiB
C
1092 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 <sys/stat.h>
|
|
#include <math.h>
|
|
|
|
#include "freertos/FreeRTOS.h"
|
|
#include "freertos/task.h"
|
|
#include "driver/i2s.h"
|
|
|
|
#include "driver/gpio.h"
|
|
#include "driver/dac.h"
|
|
#include "driver/timer.h"
|
|
#include "soc/rtc_io_reg.h"
|
|
#include "soc/rtc_cntl_reg.h"
|
|
#include "soc/sens_reg.h"
|
|
#include "soc/rtc.h"
|
|
#include "esp_task_wdt.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"
|
|
|
|
|
|
typedef struct _mdac_obj_t {
|
|
mp_obj_base_t base;
|
|
int gpio_id;
|
|
dac_channel_t dac_id;
|
|
uint8_t *buffer;
|
|
size_t buf_len;
|
|
size_t buf_ptr;
|
|
FILE *fhndl;
|
|
uint64_t timer_interval;
|
|
uint8_t dac_timer_mode;
|
|
} mdac_obj_t;
|
|
|
|
extern int MainTaskCore;
|
|
|
|
static bool trepeat = false;
|
|
static bool task_running = false;
|
|
static bool task_stop = false;
|
|
static bool timer_stop = false;
|
|
static bool cosine_enabled = false;
|
|
static bool dac_i2s_driver_installed = false;
|
|
static bool dac_timer_active = false;
|
|
static intr_handle_t dac_timer_handle = NULL;
|
|
|
|
// === ESP32 cosine generator functions ===
|
|
|
|
//------------------------------
|
|
static void dac_cosine_disable()
|
|
{
|
|
// Disable tone generator common to both channels
|
|
CLEAR_PERI_REG_MASK(SENS_SAR_DAC_CTRL1_REG, SENS_SW_TONE_EN);
|
|
|
|
// Disable / disconnect tone tone generator
|
|
CLEAR_PERI_REG_MASK(SENS_SAR_DAC_CTRL2_REG, SENS_DAC_CW_EN1_M);
|
|
CLEAR_PERI_REG_MASK(SENS_SAR_DAC_CTRL2_REG, SENS_DAC_CW_EN2_M);
|
|
// Invert MSB, otherwise part of waveform will have inverted
|
|
SET_PERI_REG_BITS(SENS_SAR_DAC_CTRL2_REG, SENS_DAC_INV1, 0, SENS_DAC_INV1_S);
|
|
SET_PERI_REG_BITS(SENS_SAR_DAC_CTRL2_REG, SENS_DAC_INV2, 0, SENS_DAC_INV2_S);
|
|
|
|
cosine_enabled = false;
|
|
}
|
|
|
|
//--------------------------------------------------
|
|
static void dac_cosine_enable(dac_channel_t channel)
|
|
{
|
|
dac_cosine_disable();
|
|
// Enable tone generator common to both channels
|
|
SET_PERI_REG_MASK(SENS_SAR_DAC_CTRL1_REG, SENS_SW_TONE_EN);
|
|
switch(channel) {
|
|
case DAC_CHANNEL_1:
|
|
// Enable / connect tone tone generator on / to this channel
|
|
SET_PERI_REG_MASK(SENS_SAR_DAC_CTRL2_REG, SENS_DAC_CW_EN1_M);
|
|
// Invert MSB, otherwise part of waveform will have inverted
|
|
SET_PERI_REG_BITS(SENS_SAR_DAC_CTRL2_REG, SENS_DAC_INV1, 2, SENS_DAC_INV1_S);
|
|
break;
|
|
case DAC_CHANNEL_2:
|
|
SET_PERI_REG_MASK(SENS_SAR_DAC_CTRL2_REG, SENS_DAC_CW_EN2_M);
|
|
SET_PERI_REG_BITS(SENS_SAR_DAC_CTRL2_REG, SENS_DAC_INV2, 2, SENS_DAC_INV2_S);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
cosine_enabled = true;
|
|
}
|
|
|
|
/*
|
|
* Set frequency of internal CW generator common to both DAC channels
|
|
*
|
|
* clk_8m_div: 0b000 - 0b111
|
|
* frequency_step: range 0x0001 - 0xFFFF
|
|
*
|
|
*/
|
|
//---------------------------------------------------------------
|
|
static void dac_frequency_set(int clk_8m_div, int frequency_step)
|
|
{
|
|
REG_SET_FIELD(RTC_CNTL_CLK_CONF_REG, RTC_CNTL_CK8M_DIV_SEL, clk_8m_div);
|
|
SET_PERI_REG_BITS(SENS_SAR_DAC_CTRL1_REG, SENS_SW_FSTEP, frequency_step, SENS_SW_FSTEP_S);
|
|
}
|
|
|
|
|
|
/*
|
|
* Scale output of a DAC channel using two bit pattern:
|
|
*
|
|
* - 00: no scale
|
|
* - 01: scale to 1/2
|
|
* - 10: scale to 1/4
|
|
* - 11: scale to 1/8
|
|
*
|
|
*/
|
|
//---------------------------------------------------------
|
|
static void dac_scale_set(dac_channel_t channel, int scale)
|
|
{
|
|
switch(channel) {
|
|
case DAC_CHANNEL_1:
|
|
SET_PERI_REG_BITS(SENS_SAR_DAC_CTRL2_REG, SENS_DAC_SCALE1, scale, SENS_DAC_SCALE1_S);
|
|
break;
|
|
case DAC_CHANNEL_2:
|
|
SET_PERI_REG_BITS(SENS_SAR_DAC_CTRL2_REG, SENS_DAC_SCALE2, scale, SENS_DAC_SCALE2_S);
|
|
break;
|
|
default :
|
|
printf("Channel %d\n", channel);
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
* Offset output of a DAC channel
|
|
*
|
|
* Range 0x00 - 0xFF
|
|
*
|
|
*/
|
|
//-----------------------------------------------------------
|
|
static void dac_offset_set(dac_channel_t channel, int offset)
|
|
{
|
|
switch(channel) {
|
|
case DAC_CHANNEL_1:
|
|
SET_PERI_REG_BITS(SENS_SAR_DAC_CTRL2_REG, SENS_DAC_DC1, offset, SENS_DAC_DC1_S);
|
|
break;
|
|
case DAC_CHANNEL_2:
|
|
SET_PERI_REG_BITS(SENS_SAR_DAC_CTRL2_REG, SENS_DAC_DC2, offset, SENS_DAC_DC2_S);
|
|
break;
|
|
default :
|
|
printf("Channel %d\n", channel);
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
* Invert output pattern of a DAC channel
|
|
*
|
|
* - 00: does not invert any bits,
|
|
* - 01: inverts all bits,
|
|
* - 10: inverts MSB,
|
|
* - 11: inverts all bits except for MSB
|
|
*
|
|
*/
|
|
//-----------------------------------------------------------
|
|
static void dac_invert_set(dac_channel_t channel, int invert)
|
|
{
|
|
switch(channel) {
|
|
case DAC_CHANNEL_1:
|
|
SET_PERI_REG_BITS(SENS_SAR_DAC_CTRL2_REG, SENS_DAC_INV1, invert, SENS_DAC_INV1_S);
|
|
break;
|
|
case DAC_CHANNEL_2:
|
|
SET_PERI_REG_BITS(SENS_SAR_DAC_CTRL2_REG, SENS_DAC_INV2, invert, SENS_DAC_INV2_S);
|
|
break;
|
|
default :
|
|
printf("Channel %d\n", channel);
|
|
}
|
|
}
|
|
|
|
// === DAC functions ===
|
|
|
|
//======================================
|
|
static void dac_task(void *pvParameters)
|
|
{
|
|
task_running = true;
|
|
mdac_obj_t *self = (mdac_obj_t *)pvParameters;
|
|
|
|
int buf_idx = 0;
|
|
int write_size = self->buf_len;
|
|
if (write_size > 4096) write_size = 4096;
|
|
|
|
size_t i2s_bytes_write;
|
|
while (true) {
|
|
if (task_stop) break;
|
|
if (buf_idx >= self->buf_len) break;
|
|
// write the buffer/file
|
|
while (buf_idx < self->buf_len) {
|
|
if (task_stop) break;
|
|
if (self->fhndl) {
|
|
// from file
|
|
write_size = fread(self->buffer, 1, write_size, self->fhndl);
|
|
if (write_size > 0) {
|
|
i2s_write(0, self->buffer, write_size, &i2s_bytes_write, 1000);
|
|
if (i2s_bytes_write != write_size) {
|
|
ESP_LOGE("DAC", "I2S error writing");
|
|
task_stop = true;
|
|
break;
|
|
}
|
|
}
|
|
else {
|
|
ESP_LOGE("DAC", "error reading from file");
|
|
task_stop = true;
|
|
break;
|
|
}
|
|
}
|
|
else {
|
|
// from buffer
|
|
i2s_write(0, self->buffer + buf_idx, write_size, &i2s_bytes_write, 1000);
|
|
if (i2s_bytes_write != write_size) {
|
|
ESP_LOGE("DAC", "I2S error writing");
|
|
task_stop = true;
|
|
break;
|
|
}
|
|
}
|
|
buf_idx += write_size;
|
|
if (buf_idx >= self->buf_len) break;
|
|
write_size = self->buf_len - buf_idx;
|
|
if (write_size > 4096) write_size = 4096;
|
|
|
|
//#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
|
|
}
|
|
if (trepeat == 0) break;
|
|
// Repeat writing from start of file/buffer
|
|
buf_idx = 0;
|
|
write_size = self->buf_len;
|
|
if (write_size > 4096) write_size = 4096;
|
|
if (self->fhndl) {
|
|
if (fseek(self->fhndl, 0, SEEK_SET) != 0) break;
|
|
}
|
|
}
|
|
|
|
if (self->fhndl) {
|
|
fclose(self->fhndl);
|
|
free(self->buffer);
|
|
}
|
|
i2s_set_dac_mode(I2S_DAC_CHANNEL_DISABLE);
|
|
i2s_stop(0);
|
|
i2s_driver_uninstall(0);
|
|
dac_i2s_driver_installed = false;
|
|
i2s_driver_installed = false;
|
|
dac_i2s_disable();
|
|
dac_output_enable(self->dac_id);
|
|
dac_output_voltage(self->dac_id, 128);
|
|
|
|
esp_log_level_set("I2S", CONFIG_LOG_DEFAULT_LEVEL);
|
|
task_stop = false;
|
|
task_running = false;
|
|
|
|
vTaskDelete(NULL);
|
|
}
|
|
|
|
//--------------------------------------
|
|
STATIC void dac_timer_isr(void *self_in)
|
|
{
|
|
mdac_obj_t *self = (mdac_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;
|
|
}
|
|
|
|
if (self->dac_timer_mode == 1) {
|
|
// Generate random noise
|
|
uint32_t rnd = esp_random();
|
|
rnd = ((rnd & 0xff) ^ ((rnd >> 8) & 0xff) ^ ((rnd >> 16) & 0xff) ^ ((rnd >> 24) & 0xff));
|
|
//rnd = ((rnd & 0xff) + ((rnd >> 8) & 0xff) + ((rnd >> 16) & 0xff) + ((rnd >> 24) & 0xff));
|
|
dac_output_voltage(self->dac_id, (uint8_t)rnd);
|
|
}
|
|
else if (self->dac_timer_mode == 2) {
|
|
// Output DAC values from buffer
|
|
dac_output_voltage(self->dac_id, self->buffer[self->buf_ptr]);
|
|
self->buf_ptr++;
|
|
if (self->buf_ptr >= self->buf_len) {
|
|
if (trepeat) self->buf_ptr = 0;
|
|
else timer_stop = true;
|
|
}
|
|
}
|
|
else timer_stop = true;
|
|
|
|
if (timer_stop) {
|
|
// --- 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 (dac_timer_handle) {
|
|
esp_intr_free(dac_timer_handle);
|
|
dac_timer_handle = NULL;
|
|
}
|
|
adc_timer_active = false;
|
|
dac_timer_active = false;
|
|
timer_stop = 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;
|
|
}
|
|
}
|
|
|
|
//------------------------------------------------
|
|
STATIC esp_err_t start_dac_timer(mdac_obj_t *self)
|
|
{
|
|
if (dac_timer_handle) {
|
|
esp_intr_free(dac_timer_handle);
|
|
dac_timer_handle = NULL;
|
|
}
|
|
|
|
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; // 1 MHz
|
|
|
|
esp_err_t err = timer_init((ADC_TIMER_NUM >> 1) & 1, ADC_TIMER_NUM & 1, &config);
|
|
if (err != ESP_OK) return -1;
|
|
|
|
// 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 -2;
|
|
|
|
// Configure the alarm value and the interrupt on alarm.
|
|
err = timer_set_alarm_value((ADC_TIMER_NUM >> 1) & 1, ADC_TIMER_NUM & 1, self->timer_interval);
|
|
if (err != ESP_OK) return -3;
|
|
|
|
// Enable timer interrupt
|
|
err = timer_enable_intr((ADC_TIMER_NUM >> 1) & 1, ADC_TIMER_NUM & 1);
|
|
if (err != ESP_OK) return -4;
|
|
|
|
// Register interrupt callback
|
|
err = timer_isr_register((ADC_TIMER_NUM >> 1) & 1, ADC_TIMER_NUM & 1, dac_timer_isr, (void*)self, 0, &dac_timer_handle);
|
|
if (err != ESP_OK) return -5;
|
|
|
|
// 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 (dac_timer_handle) {
|
|
esp_intr_free(dac_timer_handle);
|
|
dac_timer_handle = NULL;
|
|
}
|
|
return -6;
|
|
}
|
|
return ESP_OK;
|
|
}
|
|
|
|
|
|
//-----------------------------------------
|
|
static void dac_func_stop(mdac_obj_t *self)
|
|
{
|
|
if (dac_timer_active) {
|
|
timer_stop = true;
|
|
while (dac_timer_active) {
|
|
vTaskDelay(2);
|
|
}
|
|
}
|
|
if (cosine_enabled) dac_cosine_disable();
|
|
if (task_running) {
|
|
task_stop = true;
|
|
while (task_running) {
|
|
vTaskDelay(2);
|
|
}
|
|
}
|
|
if (dac_i2s_driver_installed) {
|
|
i2s_set_dac_mode(I2S_DAC_CHANNEL_DISABLE);
|
|
i2s_stop(0);
|
|
i2s_driver_uninstall(0);
|
|
dac_i2s_driver_installed = false;
|
|
i2s_driver_installed = false;
|
|
dac_i2s_disable();
|
|
dac_output_enable(self->dac_id);
|
|
dac_output_voltage(self->dac_id, 128);
|
|
}
|
|
}
|
|
|
|
//------------------------------------------------------------
|
|
static void _is_init(mdac_obj_t *self, bool iflag, bool cflag)
|
|
{
|
|
if ((iflag) && (self->gpio_id < 0)) {
|
|
mp_raise_ValueError("Not initialized");
|
|
}
|
|
if ((cflag) && ((task_running) || (dac_i2s_driver_installed) || (cosine_enabled) || (dac_timer_active))) {
|
|
mp_raise_ValueError("timed write or waveform in progress");
|
|
}
|
|
}
|
|
|
|
//------------------------------------------
|
|
static bool gen_waveform(int type, int freq)
|
|
{
|
|
if ((type < 1) || (type > 3)) return false;
|
|
|
|
size_t i2s_bytes_write;
|
|
int j = 0;
|
|
int buflen = 1024;
|
|
int nsamples = 256;
|
|
|
|
if (type == 1) {
|
|
nsamples *= 2;
|
|
buflen *= 2;
|
|
}
|
|
else freq = freq / 2;
|
|
|
|
uint8_t *samples_data = malloc(buflen);
|
|
if (samples_data == NULL) return false;
|
|
|
|
if (type == 1) {
|
|
// triangle wave
|
|
for (int i = 0; i<nsamples; i++) {
|
|
if ((i & 0x100) == 0) {
|
|
samples_data[j++] = 0;
|
|
samples_data[j++] = i & 0xFF;
|
|
samples_data[j++] = 0;
|
|
samples_data[j++] = i & 0xFF;
|
|
}
|
|
else {
|
|
samples_data[j++] = 0;
|
|
samples_data[j++] = 255 - (i & 0xFF);
|
|
samples_data[j++] = 0;
|
|
samples_data[j++] = 255 - (i & 0xFF);
|
|
}
|
|
}
|
|
}
|
|
else if (type == 2) {
|
|
// ramp wave
|
|
for (int i = 0; i<nsamples; i++) {
|
|
samples_data[j++] = 0;
|
|
samples_data[j++] = i & 0xFF;
|
|
samples_data[j++] = 0;
|
|
samples_data[j++] = i & 0xFF;
|
|
}
|
|
}
|
|
else if (type == 3) {
|
|
// tooth saw wave
|
|
for (int i = 0; i<nsamples; i++) {
|
|
samples_data[j++] = 0;
|
|
samples_data[j++] = 255 - (i & 0xFF);
|
|
samples_data[j++] = 0;
|
|
samples_data[j++] = 255 - (i & 0xFF);
|
|
}
|
|
}
|
|
|
|
i2s_set_clk(0, freq*64, 16, I2S_CHANNEL_MONO);
|
|
//ToDo: check why multiple writes are needed?!
|
|
for (int i=0; i<32; i++) {
|
|
i2s_write(0, samples_data, buflen, &i2s_bytes_write, 100);
|
|
}
|
|
free(samples_data);
|
|
return true;
|
|
}
|
|
|
|
|
|
// === MicroPython DAC bindings ===
|
|
|
|
//----------------------------------------------------------------------------------------------------------
|
|
STATIC mp_obj_t mdac_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args) {
|
|
|
|
mp_arg_check_num(n_args, n_kw, 1, 1, true);
|
|
|
|
gpio_num_t pin_id = machine_pin_get_gpio(args[0]);
|
|
|
|
if ((pin_id != GPIO_NUM_25) && (pin_id != GPIO_NUM_26)) {
|
|
mp_raise_ValueError("invalid Pin for DAC");
|
|
}
|
|
|
|
mdac_obj_t *self = m_new_obj(mdac_obj_t);
|
|
self->base.type = &machine_dac_type;
|
|
self->gpio_id = pin_id;
|
|
if (pin_id == 25) self->dac_id = DAC_CHANNEL_1;
|
|
else self->dac_id = DAC_CHANNEL_2;
|
|
self->buffer = NULL;
|
|
self->buf_len = 0;
|
|
self->fhndl = NULL;
|
|
|
|
dac_i2s_disable();
|
|
esp_err_t err = dac_output_enable(self->dac_id);
|
|
if (err == ESP_OK) {
|
|
err = dac_output_voltage(self->dac_id, 0);
|
|
}
|
|
if (err != ESP_OK) mp_raise_ValueError("DAC Parameter Error");
|
|
return MP_OBJ_FROM_PTR(self);
|
|
}
|
|
|
|
//---------------------------------------------------------------------------------------
|
|
STATIC void mdac_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) {
|
|
mdac_obj_t *self = self_in;
|
|
|
|
if (self->gpio_id < 0) {
|
|
mp_printf(print, "DAC( deinitialized )");
|
|
return;
|
|
}
|
|
mp_printf(print, "DAC(Pin(%u), channel: %d)", self->gpio_id, self->dac_id);
|
|
}
|
|
|
|
//---------------------------------------------------------------
|
|
STATIC mp_obj_t mdac_write(mp_obj_t self_in, mp_obj_t value_in) {
|
|
mdac_obj_t *self = self_in;
|
|
_is_init(self, true, true);
|
|
|
|
int value = mp_obj_get_int(value_in);
|
|
if (value < 0 || value > 255) mp_raise_ValueError("Value out of range");
|
|
|
|
esp_err_t err = dac_output_voltage(self->dac_id, value);
|
|
if (err != ESP_OK) mp_raise_ValueError("Parameter Error");
|
|
return mp_const_none;
|
|
}
|
|
MP_DEFINE_CONST_FUN_OBJ_2(mdac_write_obj, mdac_write);
|
|
|
|
|
|
//--------------------------------------------------------------------------------------------
|
|
STATIC mp_obj_t mdac_waveform(mp_uint_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
|
|
enum { ARG_freq, ARG_type, ARG_duration, ARG_scale, ARG_offset, ARG_invert, ARG_len };
|
|
const mp_arg_t allowed_args[] = {
|
|
{ MP_QSTR_freq, MP_ARG_REQUIRED | MP_ARG_INT, {.u_int = 1000} },
|
|
{ MP_QSTR_type, MP_ARG_REQUIRED | MP_ARG_INT, {.u_int = 0}},
|
|
{ MP_QSTR_duration, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 0}},
|
|
{ MP_QSTR_scale, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 0}},
|
|
{ MP_QSTR_offset, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 0}},
|
|
{ MP_QSTR_invert, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 2}},
|
|
};
|
|
|
|
mdac_obj_t *self = MP_OBJ_TO_PTR(pos_args[0]);
|
|
_is_init(self, true, false);
|
|
|
|
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);
|
|
|
|
dac_func_stop(self);
|
|
|
|
int type = args[ARG_type].u_int;
|
|
int freq = args[ARG_freq].u_int;
|
|
if (type < 0 || type > 4) mp_raise_ValueError("Unknown function type");
|
|
|
|
if (type == 0) {
|
|
// --- sine generator using ESP32 hw cosine generator ---
|
|
// freq = 8000000 x frequency_step / 65536
|
|
// frequency_step = (freq * 65536) / 8000000
|
|
if (freq < 16 || freq > 32000) mp_raise_ValueError("Frequency out of range (16-32000 Hz)");
|
|
uint64_t fs = freq * 65536;
|
|
int fdiv = 1;
|
|
if (freq < 256) fdiv = 4;
|
|
if (freq < 64) fdiv = 8;
|
|
fs /= (8000000 / fdiv);
|
|
dac_frequency_set((fdiv-1), (int)fs);
|
|
dac_scale_set(self->dac_id, args[ARG_scale].u_int & 3);
|
|
dac_offset_set(self->dac_id, (int8_t)args[ARG_offset].u_int);
|
|
dac_invert_set(self->dac_id, args[ARG_invert].u_int & 3);
|
|
dac_cosine_enable(self->dac_id);
|
|
|
|
goto exit;
|
|
}
|
|
|
|
if (type == 4) {
|
|
// Noise
|
|
if ((mpy_timers_used[ADC_TIMER_NUM]) || (adc_timer_active)) {
|
|
mp_raise_ValueError("DAC timer used by other module!");
|
|
}
|
|
adc_timer_active = true;
|
|
dac_timer_active = true;
|
|
self->dac_timer_mode = 1;
|
|
timer_stop = false;
|
|
if (freq < 500 || freq > 32000) mp_raise_ValueError("Frequency out of range (500-32000 Hz)");
|
|
self->timer_interval = (int)ADC_TIMER_FREQ / freq;
|
|
int err = start_dac_timer(self) != ESP_OK;
|
|
if (err) {
|
|
ESP_LOGE("DAC", "Error starting DAC timer (%d)", err);
|
|
}
|
|
goto exit;
|
|
}
|
|
|
|
// --- For other waveforms we use I2S peripheral to generate the waveform ---
|
|
if ((!dac_i2s_driver_installed) && (i2s_driver_installed)) {
|
|
mp_raise_ValueError("Error: i2s used by other module");
|
|
}
|
|
|
|
if ((type == 1) && (freq < 170 || freq > 3600)) mp_raise_ValueError("Frequency out of range (170 - 3600 Hz)");
|
|
else if ((freq < 170 || freq > 7200)) mp_raise_ValueError("Frequency out of range (170 - 7200 Hz)");
|
|
|
|
i2s_config_t i2s_config = {
|
|
//.mode = I2S_MODE_MASTER | I2S_MODE_TX | I2S_MODE_DAC_BUILT_IN, // Only TX, DAC output
|
|
.mode = I2S_MODE_MASTER | I2S_MODE_RX | I2S_MODE_TX | I2S_MODE_DAC_BUILT_IN | I2S_MODE_ADC_BUILT_IN,
|
|
.sample_rate = freq * 64,
|
|
.bits_per_sample = 16,
|
|
.channel_format = (self->dac_id == DAC_CHANNEL_1) ? I2S_CHANNEL_FMT_ALL_RIGHT : I2S_CHANNEL_FMT_ALL_LEFT,
|
|
.communication_format = I2S_COMM_FORMAT_I2S_MSB,
|
|
.dma_buf_count = 2,
|
|
.dma_buf_len = 256,
|
|
.use_apll = true,
|
|
.intr_alloc_flags = ESP_INTR_FLAG_LEVEL1, //Interrupt level 1
|
|
.fixed_mclk = 0
|
|
};
|
|
//install and start i2s driver
|
|
i2s_driver_install(0, &i2s_config, 0, NULL);
|
|
i2s_driver_installed = true;
|
|
dac_i2s_driver_installed = true;
|
|
//init DAC pad
|
|
i2s_set_dac_mode((self->dac_id == DAC_CHANNEL_1) ? I2S_DAC_CHANNEL_RIGHT_EN : I2S_DAC_CHANNEL_LEFT_EN);
|
|
//i2s_set_dac_mode(I2S_DAC_CHANNEL_BOTH_EN);
|
|
|
|
// start the wave
|
|
if (!gen_waveform(type, freq)) {
|
|
i2s_set_dac_mode(I2S_DAC_CHANNEL_DISABLE);
|
|
i2s_stop(0);
|
|
i2s_driver_uninstall(0);
|
|
dac_i2s_driver_installed = false;
|
|
i2s_driver_installed = false;
|
|
dac_i2s_disable();
|
|
dac_output_enable(self->dac_id);
|
|
dac_output_voltage(self->dac_id, 128);
|
|
mp_raise_ValueError("Error allocating wave buffer");
|
|
}
|
|
|
|
exit:
|
|
if (args[ARG_duration].u_int > 0) {
|
|
mp_hal_delay_ms(args[ARG_duration].u_int);
|
|
dac_func_stop(self);
|
|
}
|
|
return mp_const_none;
|
|
}
|
|
STATIC MP_DEFINE_CONST_FUN_OBJ_KW(mdac_waveform_obj, 0, mdac_waveform);
|
|
|
|
//------------------------------------------------------------------------------------------------
|
|
STATIC mp_obj_t mdac_write_buffer(mp_uint_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
|
|
enum { ARG_data, ARG_freq, ARG_mode, ARG_wait };
|
|
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_mode, MP_ARG_KW_ONLY | MP_ARG_BOOL, {.u_bool = false}},
|
|
{ MP_QSTR_wait, MP_ARG_KW_ONLY | MP_ARG_BOOL, {.u_bool = false}},
|
|
};
|
|
|
|
mdac_obj_t *self = MP_OBJ_TO_PTR(pos_args[0]);
|
|
_is_init(self, true, false);
|
|
|
|
dac_func_stop(self);
|
|
|
|
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);
|
|
|
|
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;
|
|
|
|
// Get arguments
|
|
bool wait = false;
|
|
trepeat = args[ARG_mode].u_bool;
|
|
// only wait if if not continuous mode
|
|
if (!trepeat) wait = args[ARG_wait].u_bool;
|
|
|
|
adc_timer_active = true;
|
|
dac_timer_active = true;
|
|
self->buffer = NULL;
|
|
self->buf_len = 0;
|
|
self->buf_ptr = 0;
|
|
|
|
if (args[ARG_data].u_obj != mp_const_none) {
|
|
// Play from the provided array
|
|
if (!MP_OBJ_IS_TYPE(args[ARG_data].u_obj, &mp_type_array)) {
|
|
adc_timer_active = false;
|
|
dac_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 != 'B') {
|
|
adc_timer_active = false;
|
|
dac_timer_active = false;
|
|
mp_raise_ValueError("array argument of type 'B' expected");
|
|
}
|
|
if (arr->len < 1) {
|
|
self->buf_len = 0;
|
|
adc_timer_active = false;
|
|
dac_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 {
|
|
adc_timer_active = false;
|
|
dac_timer_active = false;
|
|
mp_raise_ValueError("array argument expected");
|
|
}
|
|
|
|
self->dac_timer_mode = 2;
|
|
self->timer_interval = (int64_t)(round(interv));
|
|
int err = start_dac_timer(self) != ESP_OK;
|
|
if (err) {
|
|
adc_timer_active = false;
|
|
dac_timer_active = false;
|
|
self->buffer = NULL;
|
|
self->buf_len = 0;
|
|
self->buf_ptr = 0;
|
|
ESP_LOGE("DAC", "Error starting DAC timer (%d)", err);
|
|
}
|
|
|
|
if (wait) {
|
|
mp_hal_delay_ms(3);
|
|
while (dac_timer_active) {
|
|
mp_hal_delay_ms(3);
|
|
}
|
|
}
|
|
|
|
return mp_const_true;
|
|
}
|
|
STATIC MP_DEFINE_CONST_FUN_OBJ_KW(mdac_write_buffer_obj, 0, mdac_write_buffer);
|
|
|
|
//-----------------------------------------------------------------------------------------------
|
|
STATIC mp_obj_t mdac_write_timed(mp_uint_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
|
|
enum { ARG_data, ARG_freq, ARG_mode, ARG_wait };
|
|
const mp_arg_t allowed_args[] = {
|
|
{ MP_QSTR_data, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_obj = mp_const_none} },
|
|
{ MP_QSTR_samplerate, MP_ARG_REQUIRED | MP_ARG_INT, {.u_int = 1000} },
|
|
{ MP_QSTR_mode, MP_ARG_KW_ONLY | MP_ARG_BOOL, {.u_bool = false}},
|
|
{ MP_QSTR_wait, MP_ARG_KW_ONLY | MP_ARG_BOOL, {.u_bool = false}},
|
|
};
|
|
|
|
mdac_obj_t *self = MP_OBJ_TO_PTR(pos_args[0]);
|
|
_is_init(self, true, false);
|
|
if ((!dac_i2s_driver_installed) && (i2s_driver_installed)) {
|
|
mp_raise_ValueError("Error: i2s used by other module");
|
|
}
|
|
|
|
dac_func_stop(self);
|
|
|
|
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);
|
|
|
|
int freq = args[ARG_freq].u_int;
|
|
if ((freq < 5000) || (freq > 500000)) {
|
|
mp_raise_ValueError("sample rate out of range (5000 - 500000 Hz)");
|
|
}
|
|
|
|
// Get arguments
|
|
bool wait = false;
|
|
trepeat = args[ARG_mode].u_bool;
|
|
// only wait if if not continuous mode
|
|
if (!trepeat) wait = args[ARG_wait].u_bool;
|
|
|
|
mp_buffer_info_t src;
|
|
self->fhndl = NULL;
|
|
self->timer_interval = 0;
|
|
|
|
if (MP_OBJ_IS_STR(args[ARG_data].u_obj)) {
|
|
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->buffer = NULL;
|
|
struct stat sb;
|
|
if (stat(fullname, &sb) != 0) {
|
|
mp_raise_ValueError("Error opening file");
|
|
}
|
|
self->fhndl = fopen(fullname, "rb");
|
|
if (self->fhndl == NULL) {
|
|
mp_raise_ValueError("Error opening file");
|
|
}
|
|
self->buffer = malloc(4096);
|
|
if (self->buffer == NULL) {
|
|
fclose(self->fhndl);
|
|
mp_raise_ValueError("Error allocating dac buffer");
|
|
}
|
|
self->buf_len = sb.st_size;
|
|
wait = false;
|
|
}
|
|
else {
|
|
mp_get_buffer_raise(args[ARG_data].u_obj, &src, MP_BUFFER_READ);
|
|
self->buffer = (uint8_t *)src.buf;
|
|
self->buf_len = src.len;
|
|
}
|
|
|
|
i2s_config_t i2s_config = {
|
|
.mode = I2S_MODE_MASTER | I2S_MODE_TX | I2S_MODE_DAC_BUILT_IN, // Only TX
|
|
//.mode = I2S_MODE_MASTER | I2S_MODE_RX | I2S_MODE_TX | I2S_MODE_DAC_BUILT_IN | I2S_MODE_ADC_BUILT_IN,
|
|
.sample_rate = freq/2,
|
|
.bits_per_sample = 16,
|
|
.channel_format = (self->dac_id == DAC_CHANNEL_1) ? I2S_CHANNEL_FMT_ALL_RIGHT : I2S_CHANNEL_FMT_ALL_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, //Interrupt level 1
|
|
.fixed_mclk = 0
|
|
};
|
|
//install and start i2s driver
|
|
i2s_driver_install(0, &i2s_config, 0, NULL);
|
|
i2s_driver_installed = true;
|
|
dac_i2s_driver_installed = true;
|
|
//init DAC pad
|
|
i2s_set_dac_mode((self->dac_id == DAC_CHANNEL_1) ? I2S_DAC_CHANNEL_RIGHT_EN : I2S_DAC_CHANNEL_LEFT_EN);
|
|
//i2s_set_dac_mode(I2S_DAC_CHANNEL_BOTH_EN);
|
|
//i2s_set_clk(0, freq, 16, I2S_CHANNEL_MONO);
|
|
//i2s_set_sample_rates(0, freq/2);
|
|
|
|
task_stop = false;
|
|
esp_log_level_set("I2S", ESP_LOG_ERROR);
|
|
#if CONFIG_MICROPY_USE_BOTH_CORES
|
|
xTaskCreate(dac_task, "DAC_task", 2048, (void *)self, CONFIG_MICROPY_TASK_PRIORITY, NULL);
|
|
#else
|
|
xTaskCreatePinnedToCore(dac_task, "DAC_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_true;
|
|
}
|
|
STATIC MP_DEFINE_CONST_FUN_OBJ_KW(mdac_write_timed_obj, 0, mdac_write_timed);
|
|
|
|
//------------------------------------------------------------------
|
|
STATIC mp_obj_t mdac_play_wav(size_t n_args, const mp_obj_t *args) {
|
|
|
|
mdac_obj_t *self = MP_OBJ_TO_PTR(args[0]);
|
|
_is_init(self, true, false);
|
|
if ((!dac_i2s_driver_installed) && (i2s_driver_installed)) {
|
|
mp_raise_ValueError("Error: i2s used by other module");
|
|
}
|
|
|
|
dac_func_stop(self);
|
|
|
|
self->buffer = NULL;
|
|
self->buf_len = 0;
|
|
self->fhndl = NULL;
|
|
int freq = 0;
|
|
float fdiv = 0;
|
|
|
|
if (n_args == 3) {
|
|
fdiv = mp_obj_get_float(args[2]);
|
|
if ((fdiv < -8.0) || (fdiv > 8.0)) fdiv = 0.0;
|
|
}
|
|
const char *dac_file = NULL;
|
|
char fullname[128] = {'\0'};
|
|
uint8_t hdr[44];
|
|
|
|
if (!MP_OBJ_IS_STR(args[1])) {
|
|
mp_raise_ValueError("File name expected");
|
|
}
|
|
dac_file = mp_obj_str_get_str(args[1]);
|
|
int res = physicalPath(dac_file, fullname);
|
|
if ((res != 0) || (strlen(fullname) == 0)) {
|
|
mp_raise_ValueError("Error resolving file name");
|
|
}
|
|
struct stat sb;
|
|
if (stat(fullname, &sb) != 0) {
|
|
mp_raise_ValueError("Error opening file");
|
|
}
|
|
self->buf_len = sb.st_size;
|
|
if (self->buf_len < 45) {
|
|
mp_raise_ValueError("Not a WAV file");
|
|
}
|
|
|
|
self->fhndl = fopen(fullname, "rb");
|
|
if (self->fhndl == NULL) {
|
|
mp_raise_ValueError("Error opening file");
|
|
}
|
|
if (fread(hdr, 1, 44, self->fhndl) != 44) {
|
|
fclose(self->fhndl);
|
|
mp_raise_ValueError("Not a WAV file");
|
|
}
|
|
if (((hdr[0] != 'R') || (hdr[1] != 'I') || (hdr[2] != 'F') || (hdr[3] != 'F')) ||
|
|
((hdr[8] != 'W') || (hdr[9] != 'A') || (hdr[10] != 'V') || (hdr[11] != 'E')) ||
|
|
((hdr[12] != 'f') || (hdr[13] != 'm') || (hdr[14] != 't') || (hdr[15] != ' ')) ||
|
|
((hdr[36] != 'd') || (hdr[37] != 'a') || (hdr[38] != 't') || (hdr[39] != 'a')) ) {
|
|
fclose(self->fhndl);
|
|
mp_raise_ValueError("Not a WAV file");
|
|
}
|
|
if (((uint16_t)(hdr[20] | (hdr[21] << 8)) != 1) || ((uint16_t)(hdr[22] | (hdr[23] << 8)) != 1) ||
|
|
((uint16_t)(hdr[32] | (hdr[33] << 8)) != 1) || ((uint16_t)(hdr[34] | (hdr[35] << 8)) != 8)) {
|
|
fclose(self->fhndl);
|
|
mp_raise_ValueError("Only PCM, 8-bit mono can be played");
|
|
}
|
|
int ffreq = (int)((int)hdr[24] | (int)(hdr[25] << 8) | (int)(hdr[26] << 16) | (int)(hdr[27] << 24));
|
|
freq = ffreq / 4;
|
|
if (fdiv < -0.999) freq = (int)(round((float)freq / (fdiv * -1.0)));
|
|
else if (fdiv > 0.999) freq = (int)(round((float)freq * fdiv));
|
|
if ((freq < 5000) || (freq > 500000)) {
|
|
fclose(self->fhndl);
|
|
mp_raise_ValueError("invalid sample rate (5000 - 500000 Hz)");
|
|
}
|
|
|
|
int data_size = (int)((int)hdr[40] | (int)(hdr[41] << 8) | (int)(hdr[42] << 16) | (int)(hdr[43] << 24));
|
|
if ((data_size + 44) > sb.st_size) {
|
|
fclose(self->fhndl);
|
|
mp_raise_ValueError("invalid file size");
|
|
}
|
|
self->buf_len = data_size;
|
|
|
|
self->buffer = malloc(4096);
|
|
if (self->buffer == NULL) {
|
|
fclose(self->fhndl);
|
|
mp_raise_ValueError("Error allocating dac buffer");
|
|
}
|
|
|
|
ESP_LOGD("DAC", "Playing WAV, %d Hz, %d bytes", ffreq, data_size);
|
|
i2s_config_t i2s_config = {
|
|
.mode = I2S_MODE_MASTER | I2S_MODE_TX | I2S_MODE_DAC_BUILT_IN, // Only TX
|
|
//.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 = (self->dac_id == DAC_CHANNEL_1) ? I2S_CHANNEL_FMT_ALL_RIGHT : I2S_CHANNEL_FMT_ALL_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, //Interrupt level 1
|
|
.fixed_mclk = 0
|
|
};
|
|
//install and start i2s driver
|
|
i2s_driver_install(0, &i2s_config, 0, NULL);
|
|
i2s_driver_installed = true;
|
|
dac_i2s_driver_installed = true;
|
|
//init DAC pad
|
|
i2s_set_dac_mode((self->dac_id == DAC_CHANNEL_1) ? I2S_DAC_CHANNEL_RIGHT_EN : I2S_DAC_CHANNEL_LEFT_EN);
|
|
|
|
task_stop = false;
|
|
esp_log_level_set("I2S", ESP_LOG_ERROR);
|
|
#if CONFIG_MICROPY_USE_BOTH_CORES
|
|
xTaskCreate(dac_task, "DAC_task", 2048, (void *)self, CONFIG_MICROPY_TASK_PRIORITY, NULL);
|
|
#else
|
|
xTaskCreatePinnedToCore(dac_task, "DAC_task", 2048, (void *)self, CONFIG_MICROPY_TASK_PRIORITY, NULL, MainTaskCore);
|
|
#endif
|
|
|
|
return mp_const_true;
|
|
}
|
|
MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mdac_play_wav_obj, 2, 3, mdac_play_wav);
|
|
|
|
//-----------------------------------------------
|
|
STATIC mp_obj_t mdac_stopfunc(mp_obj_t self_in) {
|
|
mdac_obj_t *self = self_in;
|
|
|
|
dac_func_stop(self);
|
|
|
|
return mp_const_none;
|
|
}
|
|
MP_DEFINE_CONST_FUN_OBJ_1(mdac_stopfunc_obj, mdac_stopfunc);
|
|
|
|
//--------------------------------------------------------------
|
|
STATIC mp_obj_t mdac_beep(size_t n_args, const mp_obj_t *args) {
|
|
mdac_obj_t *self = args[0];
|
|
|
|
_is_init(self, true, false);
|
|
|
|
dac_func_stop(self);
|
|
|
|
int freq = mp_obj_get_int(args[1]);
|
|
int duration = mp_obj_get_int(args[2]);
|
|
int scale = 0;
|
|
if (n_args == 4) scale = mp_obj_get_int(args[3]) & 3;
|
|
if (freq < 16 || freq > 32000) mp_raise_ValueError("Frequency out of range (16-32000 Hz)");
|
|
if (duration < 10 || duration > 2000) mp_raise_ValueError("Duration out of range (10-2000 ms)");
|
|
|
|
// use cosine generator
|
|
uint64_t fs = freq * 65536;
|
|
int fdiv = 1;
|
|
if (freq < 256) fdiv = 4;
|
|
if (freq < 64) fdiv = 8;
|
|
fs /= (8000000 / fdiv);
|
|
dac_frequency_set((fdiv-1), (int)fs);
|
|
dac_scale_set(self->dac_id, scale);
|
|
dac_cosine_enable(self->dac_id);
|
|
|
|
// wait for duration ms
|
|
mp_hal_delay_ms(duration);
|
|
dac_cosine_disable();
|
|
dac_output_voltage(self->dac_id, 128);
|
|
|
|
return mp_const_none;
|
|
}
|
|
MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mdac_beep_obj, 3, 4, mdac_beep);
|
|
|
|
//----------------------------------------------------------------
|
|
STATIC mp_obj_t mdac_setfreq(mp_obj_t self_in, mp_obj_t freq_in) {
|
|
//mdac_obj_t *self = self_in;
|
|
|
|
int freq = mp_obj_get_int(freq_in);
|
|
if (cosine_enabled) {
|
|
if (freq < 130 || freq > 32000) mp_raise_ValueError("Frequency out of range (130-32000 Hz)");
|
|
uint64_t fs = freq * 65536;
|
|
fs /= 8000000;
|
|
dac_frequency_set(0, (int)fs);
|
|
}
|
|
else if (dac_i2s_driver_installed && !task_running) {
|
|
if (freq < 170 || freq > 3600) mp_raise_ValueError("Frequency out of range (170-3600 Hz)");
|
|
i2s_set_clk(0, freq * 64, 16, I2S_CHANNEL_MONO);
|
|
}
|
|
|
|
return mp_const_none;
|
|
}
|
|
MP_DEFINE_CONST_FUN_OBJ_2(mdac_setfreq_obj, mdac_setfreq);
|
|
|
|
//-------------------------------------------
|
|
STATIC mp_obj_t mdac_deinit(mp_obj_t self_in)
|
|
{
|
|
mdac_obj_t *self = self_in;
|
|
_is_init(self, false, true);
|
|
|
|
if (self->gpio_id < 0) return mp_const_none;
|
|
|
|
dac_cosine_disable();
|
|
dac_output_disable(self->dac_id);
|
|
gpio_pad_select_gpio(self->gpio_id);
|
|
self->gpio_id = -1;
|
|
|
|
return mp_const_none;
|
|
}
|
|
MP_DEFINE_CONST_FUN_OBJ_1(mdac_deinit_obj, mdac_deinit);
|
|
|
|
|
|
//=========================================================
|
|
STATIC const mp_rom_map_elem_t mdac_locals_dict_table[] = {
|
|
{ MP_ROM_QSTR(MP_QSTR_write), MP_ROM_PTR(&mdac_write_obj) },
|
|
{ MP_ROM_QSTR(MP_QSTR_write_timed), MP_ROM_PTR(&mdac_write_timed_obj) },
|
|
{ MP_ROM_QSTR(MP_QSTR_write_buffer),MP_ROM_PTR(&mdac_write_buffer_obj) },
|
|
{ MP_ROM_QSTR(MP_QSTR_wavplay), MP_ROM_PTR(&mdac_play_wav_obj) },
|
|
{ MP_ROM_QSTR(MP_QSTR_waveform), MP_ROM_PTR(&mdac_waveform_obj) },
|
|
{ MP_ROM_QSTR(MP_QSTR_stopwave), MP_ROM_PTR(&mdac_stopfunc_obj) },
|
|
{ MP_ROM_QSTR(MP_QSTR_deinit), MP_ROM_PTR(&mdac_deinit_obj) },
|
|
{ MP_ROM_QSTR(MP_QSTR_freq), MP_ROM_PTR(&mdac_setfreq_obj) },
|
|
{ MP_ROM_QSTR(MP_QSTR_beep), MP_ROM_PTR(&mdac_beep_obj) },
|
|
|
|
{ MP_ROM_QSTR(MP_QSTR_SINE), MP_ROM_INT(0) },
|
|
{ MP_ROM_QSTR(MP_QSTR_TRIANGLE), MP_ROM_INT(1) },
|
|
{ MP_ROM_QSTR(MP_QSTR_RAMP), MP_ROM_INT(2) },
|
|
{ MP_ROM_QSTR(MP_QSTR_SAWTOOTH), MP_ROM_INT(3) },
|
|
{ MP_ROM_QSTR(MP_QSTR_NOISE), MP_ROM_INT(4) },
|
|
|
|
{ MP_ROM_QSTR(MP_QSTR_CIRCULAR), MP_ROM_INT(1) },
|
|
{ MP_ROM_QSTR(MP_QSTR_NORMAL), MP_ROM_INT(1) },
|
|
};
|
|
STATIC MP_DEFINE_CONST_DICT(mdac_locals_dict, mdac_locals_dict_table);
|
|
|
|
//======================================
|
|
const mp_obj_type_t machine_dac_type = {
|
|
{ &mp_type_type },
|
|
.name = MP_QSTR_DAC,
|
|
.print = mdac_print,
|
|
.make_new = mdac_make_new,
|
|
.locals_dict = (mp_obj_t)&mdac_locals_dict,
|
|
};
|