Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add ULP support with uPython assembler #214

Open
wants to merge 5 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
*.dis
#*.exe

# Packages
# Packages
############

# Logs and Databases
Expand Down Expand Up @@ -52,3 +52,5 @@ xtensa-esp32-elf/
xtensa-esp32-elf_psram/

backup.sh
.DS_Store
*.gch
Original file line number Diff line number Diff line change
@@ -0,0 +1,171 @@



"""
================================================================================

Pulse Counter using ESP32 ULP processor

Uses ULP to count pulses on GPIO4 (which is TOUCH0/RTCIO10 in RTC domain)

================================================================================

Includes debouncer taken from code as popularised by Kenneth A. Kuhn.

This is an algorithm that debounces or removes random or spurious
transitions of a digital signal read as an input by a computer. This is
particularly applicable when the input is from a mechanical contact.

The algorithm uses integration as opposed to edge logic
(differentiation), which makes it very robust in the presence of noise.

An integrator is used to perform a time hysteresis so that the signal must
persistently be in a logical state (0 or 1) in order for the output to change
to that state. Random transitions of the input will not affect the output
except in the rare case where statistical clustering is longer than the
specified integration time.

The following example illustrates how this algorithm works. "real signal"
represents the real intended signal with no noise. "corrupted" has significant
random transitions added to the real signal. "integrator" represents the
integration which is constrained to be between 0 and 3. "output" only makes a
transition when the integrator reaches either 0 or 3.

Note that the output signal lags the input signal by the integration time but
is free of spurious transitions.

real signal 0000111111110000000111111100000000011111111110000000000111111100000

corrupted 0100111011011001000011011010001001011100101111000100010111011100010
integrator 0100123233233212100012123232101001012321212333210100010123233321010
output 0000001111111111100000001111100000000111111111110000000001111111000


--------------------------------------------------------------------------------

"""

from machine import mem32
import utime
import machine

from esp32_ulp.__main__ import src_to_binary

source = """\
pulse_count: .long 0 # 16 bit count of pulses returned to main processor
debounce: .long 0 # maximum saturation value of integrator, from main
last_state: .long 0 # internal record of last debounced state of pin
integrator: .long 0


.set RTC_GPIO_IN_REG, 0x3ff48424 # = DR_REG_RTCIO_BASE + 0x24

/* Step 1: Update the integrator based on the input signal. Note that the
integrator follows the input, decreasing or increasing towards the limits as
determined by the input state (0 or 1). */

entry: move r3, integrator # get integrator address
reg_rd RTC_GPIO_IN_REG, 14, 24 # get all 16 ULP GPIOs into r0
and r0, r0, 1 # isolate TOUCH0/RTCIO10/GPIO4
jump pin_low, eq # if pin high
ld r0, r3, 0 # get integrator value
jumpr done, debounce, ge # if integrator < debounce max count
add r0, r0, 1 # integrator +=1
st r0, r3, 0
jump chk_state

pin_low:
ld r0, r3, 0 # get integrator value
jumpr chk_state, 1, lt # if integrator >= 1
sub r0, r0, 1 # integrator -=1
st r0, r3, 0

/* Step 2: Update the output state based on the integrator. Note that the
output will only change state if the integrator has reached a limit, either
0 or MAXIMUM. */

chk_state:
move r2, last_state # get last qualified state of pin
ld r0, r2, 0
jumpr chk_debounce, 1, lt # if previous state was high
ld r0, r3, 0 # and
jumpr done, 1, ge # integrator < 1
move r0, 0 # falling edge qualified
st r0, r2, 0 # so update last_state=0
jump inc_pulse_count # update count on falling edge

chk_debounce: # previous state was low
ld r0, r3, 0 # get integrator value
jumpr done, debounce, lt # if integrator >= debounce max
move r0,1 # rising edge qualified
st r0, r2, 0 # update last_state=1
jump done # halt 'til next wakeup

inc_pulse_count: # increment pulse count
move r3, pulse_count
ld r1, r3, 0
add r1, r1, 1
st r1, r3, 0
done: halt # halt 'til next wakeup

"""



ULP_MEM_BASE = 0x50000000
ULP_DATA_MASK = 0xffff # ULP data is only in lower 16 bits

ULP_VARS = 4 # number of 32-bit vars used by ULP program

load_addr = 0
entry_addr = ULP_MEM_BASE + (ULP_VARS * 4) # entry address is offset by length of vars

ulp_pulse_count = ULP_MEM_BASE + 0
ulp_debounce = ULP_MEM_BASE + 4


machine.Pin(4, machine.Pin.IN, machine.Pin.PULL_UP)
pin21 = machine.Pin(21, machine.Pin.OUT)


print ("\nLoading ULP...\n")

binary = src_to_binary(source)

ulp = machine.ULP()
ulp.set_wakeup_period(0, 100) # use timer0, wakeup after ~100us
ulp.load_binary(load_addr, binary)


if machine.nvs_getint('pulse_count_nv') == None:
machine.nvs_setint('pulse_count_nv', 0)

init_count = machine.nvs_getint('pulse_count_nv')

pulse_count = init_count # init pulse counts
pulse_gen_count = init_count
mem32[ulp_pulse_count] = init_count

mem32[ulp_debounce] = 3 # 3 sample debounce

ulp.run(entry_addr)


print ("\n\nPulse Count Test: \n----------------- \n")

while True:
pulse_countL = mem32[ulp_pulse_count] & ULP_DATA_MASK
if (pulse_count & 0xffff) > pulse_countL:
pulse_count += 0x10000
pulse_count = (pulse_count & ~0xffff) + pulse_countL
machine.nvs_setint('pulse_count_nv', pulse_count)

print('Pulses Out:', pulse_gen_count)
print('Pulses In :', pulse_count, '\n')

for t in range (500):
pin21.value(1)
utime.sleep_ms(1)
pin21.value(0)
pulse_gen_count += 1
utime.sleep_ms(1)
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
"""
Very basic example showing data exchange main CPU <--> ULP coprocessor.

To show that the ULP is doing something, it just increments the value <data>.
It does that once per ulp timer wakeup (and then the ULP halts until it gets
waked up via the timer again).

The timer is set to a rather long period, so you can watch the data value
incrementing (see loop at the end).
"""

from machine import ULP
from machine import mem32
import utime
import machine

from esp32_ulp.__main__ import src_to_binary

source = """\
data: .long 0

entry: move r3, data # load address of data into r3
ld r2, r3, 0 # load data contents ([r3+0]) into r2
add r2, r2, 1 # increment r2
st r2, r3, 0 # store r2 contents into data ([r3+0])
halt # halt ULP co-processor (until next wake-up)
"""


print ("Wake Reason:",machine.wake_reason())
binary = src_to_binary(source)

load_addr, entry_addr = 0, 4

ULP_MEM_BASE = 0x50000000
ULP_DATA_MASK = 0xffff # ULP data is only in lower 16 bits

ulp = ULP()
ulp.set_wakeup_period(0, 1000000) # use timer0, wakeup after 1s
ulp.load_binary(load_addr, binary)

mem32[ULP_MEM_BASE + load_addr] = 0x1000
ulp.run(entry_addr)

while True:
print(hex(mem32[ULP_MEM_BASE + load_addr] & ULP_DATA_MASK))
utime.sleep(1)
1 change: 1 addition & 0 deletions MicroPython_BUILD/components/micropython/component.mk
Original file line number Diff line number Diff line change
Expand Up @@ -181,6 +181,7 @@ SRC_C = $(addprefix esp32/,\
modymodem.c \
machine_hw_i2c.c \
machine_neopixel.c \
machine_ulp.c \
machine_dht.c \
machine_ow.c \
)
Expand Down
100 changes: 100 additions & 0 deletions MicroPython_BUILD/components/micropython/esp32/machine_ulp.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
/*
* This file is part of the MicroPython project, http://micropython.org/
*
* The MIT License (MIT)
*
* Copyright (c) 2017 "Eric Poulsen" <[email protected]>
*
* 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 "machine_ulp.h"

#include "py/obj.h"
#include "py/runtime.h"
#include "py/mperrno.h"

#include "ulp.h"
#include "esp_err.h"

typedef struct _machine_ulp_obj_t {
mp_obj_base_t base;
} machine_ulp_obj_t;

const mp_obj_type_t machine_ulp_type;

// singleton ULP object
STATIC const machine_ulp_obj_t machine_ulp_obj = {{&machine_ulp_type}};

STATIC mp_obj_t machine_ulp_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args) {
// check arguments
mp_arg_check_num(n_args, n_kw, 0, 0, false);

// return constant object
return (mp_obj_t)&machine_ulp_obj;
}

STATIC mp_obj_t machine_ulp_set_wakeup_period(mp_obj_t self_in, mp_obj_t period_index_in, mp_obj_t period_us_in) {
mp_uint_t period_index = mp_obj_get_int(period_index_in);
mp_uint_t period_us = mp_obj_get_int(period_us_in);
int _errno = ulp_set_wakeup_period(period_index, period_us);
if (_errno != ESP_OK) {
mp_raise_OSError(_errno);
}
return mp_const_none;
}
STATIC MP_DEFINE_CONST_FUN_OBJ_3(machine_ulp_set_wakeup_period_obj, machine_ulp_set_wakeup_period);

STATIC mp_obj_t machine_ulp_load_binary(mp_obj_t self_in, mp_obj_t load_addr_in, mp_obj_t program_binary_in) {
mp_uint_t load_addr = mp_obj_get_int(load_addr_in);

mp_buffer_info_t bufinfo;
mp_get_buffer_raise(program_binary_in, &bufinfo, MP_BUFFER_READ);

int _errno = ulp_load_binary(load_addr, bufinfo.buf, bufinfo.len/sizeof(uint32_t));
if (_errno != ESP_OK) {
mp_raise_OSError(_errno);
}
return mp_const_none;
}
STATIC MP_DEFINE_CONST_FUN_OBJ_3(machine_ulp_load_binary_obj, machine_ulp_load_binary);

STATIC mp_obj_t machine_ulp_run(mp_obj_t self_in, mp_obj_t entry_point_in) {
mp_uint_t entry_point = mp_obj_get_int(entry_point_in);
int _errno = ulp_run(entry_point/sizeof(uint32_t));
if (_errno != ESP_OK) {
mp_raise_OSError(_errno);
}
return mp_const_none;
}
STATIC MP_DEFINE_CONST_FUN_OBJ_2(machine_ulp_run_obj, machine_ulp_run);

STATIC const mp_map_elem_t machine_ulp_locals_dict_table[] = {
{ MP_OBJ_NEW_QSTR(MP_QSTR_set_wakeup_period), (mp_obj_t)&machine_ulp_set_wakeup_period_obj },
{ MP_OBJ_NEW_QSTR(MP_QSTR_load_binary), (mp_obj_t)&machine_ulp_load_binary_obj },
{ MP_OBJ_NEW_QSTR(MP_QSTR_run), (mp_obj_t)&machine_ulp_run_obj },
{ MP_OBJ_NEW_QSTR(MP_QSTR_COPROC_RESERVE_MEM), MP_ROM_INT(CONFIG_ULP_COPROC_RESERVE_MEM) },
};
STATIC MP_DEFINE_CONST_DICT(machine_ulp_locals_dict, machine_ulp_locals_dict_table);

const mp_obj_type_t machine_ulp_type = {
{ &mp_type_type },
.name = MP_QSTR_ULP,
.make_new = machine_ulp_make_new,
.locals_dict = (mp_obj_t)&machine_ulp_locals_dict,
};
30 changes: 30 additions & 0 deletions MicroPython_BUILD/components/micropython/esp32/machine_ulp.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
/*
* This file is part of the MicroPython project, http://micropython.org/
*
* The MIT License (MIT)
*
* Copyright (c) 2017 "Eric Poulsen" <[email protected]>
*
* 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.
*/

#ifndef MICROPY_INCLUDED_ESP32_MACINE_ULP_H
#define MICROPY_INCLUDED_ESP32_MACINE_ULP_H

#endif
2 changes: 2 additions & 0 deletions MicroPython_BUILD/components/micropython/esp32/modmachine.c
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@
#include "extmod/machine_pulse.h"
#include "extmod/vfs_native.h"
#include "modmachine.h"
#include "machine_ulp.h"
#include "mpsleep.h"
#include "machine_rtc.h"
#include "uart.h"
Expand Down Expand Up @@ -1013,6 +1014,7 @@ STATIC const mp_rom_map_elem_t machine_module_globals_table[] = {
{ MP_ROM_QSTR(MP_QSTR_ADC), MP_ROM_PTR(&machine_adc_type) },
{ MP_ROM_QSTR(MP_QSTR_DAC), MP_ROM_PTR(&machine_dac_type) },
{ MP_ROM_QSTR(MP_QSTR_I2C), MP_ROM_PTR(&machine_hw_i2c_type) },
{ MP_ROM_QSTR(MP_QSTR_ULP), MP_ROM_PTR(&machine_ulp_type) },
{ MP_ROM_QSTR(MP_QSTR_PWM), MP_ROM_PTR(&machine_pwm_type) },
{ MP_ROM_QSTR(MP_QSTR_SPI), MP_ROM_PTR(&machine_hw_spi_type) },
{ MP_ROM_QSTR(MP_QSTR_UART), MP_ROM_PTR(&machine_uart_type) },
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,7 @@ extern const mp_obj_type_t machine_pin_type;
extern const mp_obj_type_t machine_touchpad_type;
extern const mp_obj_type_t machine_adc_type;
extern const mp_obj_type_t machine_dac_type;
extern const mp_obj_type_t machine_ulp_type;
extern const mp_obj_type_t machine_pwm_type;
extern const mp_obj_type_t machine_hw_spi_type;
extern const mp_obj_type_t machine_hw_i2c_type;
Expand Down
Loading