diff --git a/lib/cube/i2c_master.py b/lib/cube/i2c_master.py index ee8b44de3ef6466568012d4b8f6fb6f98e59fcc7..1b6ffd24aa3fc32eff6c5a51343a7906a6c1c636 100644 --- a/lib/cube/i2c_master.py +++ b/lib/cube/i2c_master.py @@ -1,19 +1,21 @@ -from machine import I2C, Pin +import time +from machine import I2C, Pin, Timer +from micropython import const + from lib.hw_defs.pins import UTZ_I2C_SDA_PIN, UTZ_I2C_SCK_PIN, I2C_STRONG_PULL_RPIN_BIT -from lib.robot_consts import I2C_MULTICUBE_FREQ +from lib.robot_consts import I2C_MULTICUBE_FREQ, I2C_NXT_UTZ_BUS + +I2C_TIME_TRIGGER_REG = const(256-4) class I2C_master: - def __init__(self, pcf_buttons, general_add=0x41): - self.general_add = general_add + def __init__(self, pcf_buttons): self.pcf_buttons = pcf_buttons self.data_received = None self.data_sending = None - self.start() - - def start(self): + self.trigger_timer = Timer(-1) self.pcf_buttons.set_pin(I2C_STRONG_PULL_RPIN_BIT, False) - self.i2c = I2C(id=0,scl=Pin(UTZ_I2C_SCK_PIN), + self.i2c = I2C(id=I2C_NXT_UTZ_BUS,scl=Pin(UTZ_I2C_SCK_PIN), sda=Pin(UTZ_I2C_SDA_PIN), freq=I2C_MULTICUBE_FREQ) def read(self, slave_add, len=1, mem_add=None): @@ -37,6 +39,23 @@ class I2C_master: return False return True + def trigger(self, add, time_us, callback=None): + try: + time_start = time.ticks_us() + self.i2c.writeto_mem(add, I2C_TIME_TRIGGER_REG, time_us.to_bytes(4, 'little')) + time_diff = time_us-time.ticks_diff(time.ticks_us(), time_start) + print("Time diff: ", time_diff) + if callback: + if time_diff > 0: + self.trigger_timer.init(freq=1000000/time_diff, mode=Timer.ONE_SHOT, callback=callback) + else: + callback(None) + + except Exception as e: + return False + return True + def deinit(self): + self.trigger_timer.deinit() self.i2c = None - self.pcf_buttons.set_pin(I2C_STRONG_PULL_RPIN_BIT, True) \ No newline at end of file + self.pcf_buttons.set_pin(I2C_STRONG_PULL_RPIN_BIT, True) diff --git a/menu_programs/i2c_master.py b/menu_programs/i2c_master.py index 1df93a4c7d9b149693aa97e0e7658b4159131a06..9d633247f17a5e2ec49334df233b2cd0a689038c 100644 --- a/menu_programs/i2c_master.py +++ b/menu_programs/i2c_master.py @@ -9,19 +9,25 @@ ADDRESS = 0x44 W_ADDRESS = 0x0b W_ADDRESS_MAX = 0x1b R_ADDRESS = 0x0a +SLAVE_TRIGGER_TIME = 1000000 # us +TIMEOUT = 2 -TIMEOUT = 3 def i2c_master_run(robot): + + def master_trigger_callback(timer): + robot.led.toggle() + robot.init_i2c_master() write_mem_add = W_ADDRESS data_sending = 0 while True: + robot.led.off() data_received_parsed = 0x5F data_received = None - debounce = False + debounce = True timeout = 0 robot.display.fill(0) @@ -41,6 +47,7 @@ def i2c_master_run(robot): return slave_add = all_add[0] + robot.led.off() while timeout < TIMEOUT: robot.display.fill(0) robot.display.centered_text(f"I2C master", 0, 1) @@ -52,14 +59,12 @@ def i2c_master_run(robot): robot.display.draw_arrow(84, 61, robot.display.DOWN, 1) robot.display.show() - data_written = robot.i2c_master.write(slave_add, (data_sending + ASCII_a).to_bytes(1, 'big'), mem_add=write_mem_add) + data_written = robot.i2c_master.write(slave_add, (data_sending + ASCII_a).to_bytes(1, 'little'), mem_add=write_mem_add) data_received = robot.i2c_master.read(slave_add, 1, mem_add=R_ADDRESS) if data_received and data_received != b'\x00': - print("Received: ", data_received) - data_received_parsed = int.from_bytes(data_received, 'big') + data_received_parsed = int.from_bytes(data_received, 'little') elif not data_written: timeout += 1 - print(data_written, data_received) buttons = robot.buttons.pressed_since() if buttons[Button.LEFT]: @@ -74,6 +79,9 @@ def i2c_master_run(robot): data_sending += 1 data_sending = data_sending % 26 debounce = True + if buttons[Button.OK]: + robot.i2c_master.trigger(slave_add, SLAVE_TRIGGER_TIME, callback=master_trigger_callback) + debounce = True if buttons[Button.RIGHT]: #robot.buttons.set_pin(I2C_STRONG_PULL_RPIN_BIT, True) write_mem_add += 1 @@ -82,6 +90,6 @@ def i2c_master_run(robot): debounce = True - if not buttons[Button.UP] and not buttons[Button.DOWN] and not buttons[Button.RIGHT] and not buttons[Button.LEFT]: + if not buttons[Button.UP] and not buttons[Button.DOWN] and not buttons[Button.RIGHT] and not buttons[Button.LEFT] and not buttons[Button.OK]: debounce = False utime.sleep(0.1) \ No newline at end of file diff --git a/menu_programs/i2c_slave.py b/menu_programs/i2c_slave.py index c6d428ea19d80f69a71f697632e9ed2eef8f1694..ab431f03baa55cc0a9b32973941f2c2e732f1c02 100644 --- a/menu_programs/i2c_slave.py +++ b/menu_programs/i2c_slave.py @@ -1,25 +1,29 @@ import utime -from machine import mem32,mem8,Pin, +from machine import mem32,mem8,Pin + from lib.robot_consts import Button -from lib.hw_defs.pins import UTZ_I2C_SDA_PIN, UTZ_I2C_SCK_PIN, I2C_STRONG_PULL_RPIN ASCII_a = 97 ADDRESS = 0x44 ADDRESS_MAX = 0x50 W_ADDRESS = 0x0b R_ADDRESS = 0x0a -drdy = 0 -def i2c_slave_irq(i2c_slave): - global drdy - if (i2c_slave.irq().flags() & i2c_slave.RECEIVED): - drdy = 1 +drdy = 0 def i2c_slave_run(robot): global drdy,irq_c + + def i2c_slave_irq(i2c_slave): + global drdy + if (i2c_slave.irq().flags() & i2c_slave.RECEIVED): + drdy = 1 + if (i2c_slave.irq().flags() & i2c_slave.TRIGGER): + robot.led.toggle() robot.init_i2c_slave(ADDRESS) - robot.i2c_slave.irq(i2c_slave_irq, (robot.i2c_slave.READ | robot.i2c_slave.RECEIVED), False) + I2C_SLAVE_IRQ_FLAGS = robot.i2c_slave.TRIGGER | robot.i2c_slave.RECEIVED + robot.i2c_slave.irq(i2c_slave_irq, I2C_SLAVE_IRQ_FLAGS, False) data_received = None data_show = 0x5F @@ -35,6 +39,8 @@ def i2c_slave_run(robot): robot.display.text('< exit', 0, 54, 1) robot.display.show() while True: + print(robot.i2c_slave) + print(robot.i2c_slave.written_flags()) if drdy: flags = robot.i2c_slave.written_flags() for i in range(len(flags)): @@ -83,7 +89,8 @@ def i2c_slave_run(robot): slave_add = ADDRESS robot.i2c_slave.init(address=slave_add) robot.i2c_slave.write(R_ADDRESS, data_sending + ASCII_a) - robot.i2c_slave.irq(i2c_slave_irq, (robot.i2c_slave.READ | robot.i2c_slave.RECEIVED), False) + robot.i2c_slave.irq(i2c_slave_irq, I2C_SLAVE_IRQ_FLAGS, False) + robot.led.off() debounce = True if not buttons[Button.UP] and not buttons[Button.DOWN] and not buttons[Button.RIGHT]: diff --git a/micropython/modules/opencube_brick/oc_i2c_slave.c b/micropython/modules/opencube_brick/oc_i2c_slave.c index 3b7411ee97c17df01fb2b11aa88868813d07796f..b40a1f1c85d9217b14e58362850ae093fe16b235 100644 --- a/micropython/modules/opencube_brick/oc_i2c_slave.c +++ b/micropython/modules/opencube_brick/oc_i2c_slave.c @@ -1,3 +1,9 @@ +/* +* The OpenCube I2C slave module allows the RP2040 to act as an I2C slave device. +* The slave can be enabled and disabled, and the I2C address can be set. +* The slave can also be configured to trigger an IRQ when a specific event occurs. +* The IRQ can be triggered on read, write, or time trigger events. +*/ #include <string.h> #include <py/runtime.h> #include <py/mperrno.h> @@ -5,13 +11,21 @@ #include <hardware/gpio.h> #include <pico/i2c_slave.h> #include <shared/runtime/mpirq.h> +#include "pico/time.h" #include <opencube_hw.h> #include "PCF8575.h" +#define ALARM_ID_INVALID (-1) + #define OC_I2C_SLAVE_READ _u(0x00000001) #define OC_I2C_SLAVE_RECEIVE _u(0x00000002) -#define OC_I2C_SLAVE_ALLOWED_FLAGS (OC_I2C_SLAVE_READ | OC_I2C_SLAVE_RECEIVE) +#define OC_I2C_SLAVE_TRIGGER _u(0x00000004) +#define OC_I2C_SLAVE_ALLOWED_FLAGS (OC_I2C_SLAVE_READ | OC_I2C_SLAVE_RECEIVE | OC_I2C_SLAVE_TRIGGER) + +#define OC_I2C_SLAVE_TIME_TRIGGER_REG_START (256-4) +#define OC_I2C_SLAVE_TIME_TRIGGER_REG_END (256-1) + // The slave implements a 256 byte memory. To write a series of bytes, the master first // writes the memory address, followed by the data. The address is automatically incremented @@ -23,17 +37,20 @@ typedef struct { uint64_t mem_read[4]; uint8_t mem_address; bool mem_address_written; -} oc_i2c_slave_memory; +} oc_i2c_slave_memory_t; typedef struct _oc_i2c_slave_obj_t { mp_obj_base_t base; bool enabled; + uint32_t time_trigger; uint8_t address; uint16_t mp_irq_trigger; // user IRQ trigger mask uint16_t mp_irq_flags; // user IRQ active IRQ flags uint16_t mp_irq_flags_unfinished; // future IRQ flags mp_irq_obj_t *mp_irq_obj; // user IRQ object - oc_i2c_slave_memory context; + struct alarm_pool *pool; // alarm pool for master time trigger + alarm_id_t alarm_id; + oc_i2c_slave_memory_t context; } oc_i2c_slave_obj_t; const mp_obj_type_t oc_i2c_slave_type; @@ -45,12 +62,17 @@ static oc_i2c_slave_obj_t oc_i2c_slave_obj = { 0, 0, 0, + 0, NULL, + NULL, + ALARM_ID_INVALID, {{0}}, }; static bool reserved_addr(uint8_t addr); static void oc_i2c_slave_handler(i2c_inst_t *i2c, i2c_slave_event_t event); +static int64_t alarm_callback(alarm_id_t id, void *user_data); +static void oc_cancel_alarm(oc_i2c_slave_obj_t *self); static mp_obj_t oc_i2c_slave_init_helper(oc_i2c_slave_obj_t *self, size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args); static mp_irq_obj_t *oc_i2c_slave_irq_helper(oc_i2c_slave_obj_t *self, bool any_args, mp_arg_val_t *args); @@ -63,36 +85,55 @@ static bool reserved_addr(uint8_t addr) { // Our handler is called from the I2C ISR, so it must complete quickly. Blocking calls / // printing to stdio may interfere with interrupt handling. static void oc_i2c_slave_handler(i2c_inst_t *i2c, i2c_slave_event_t event) { - + oc_i2c_slave_memory_t *context = &oc_i2c_slave_obj.context; switch (event) { case I2C_SLAVE_RECEIVE: // master has written some data - if (!oc_i2c_slave_obj.context.mem_address_written) { + if (!context->mem_address_written) { // writes always start with the memory address - oc_i2c_slave_obj.context.mem_address = i2c_read_byte_raw(i2c); - oc_i2c_slave_obj.context.mem_address_written = true; + context->mem_address = i2c_read_byte_raw(i2c); + context->mem_address_written = true; } else { // save into memory - oc_i2c_slave_obj.context.mem[oc_i2c_slave_obj.context.mem_address] = i2c_read_byte_raw(i2c); - // save a flag of the written memory address using bits withou using division uint64_t - oc_i2c_slave_obj.context.mem_written[oc_i2c_slave_obj.context.mem_address >> 6] |= 1 << (oc_i2c_slave_obj.context.mem_address & (0xff>>2)); - oc_i2c_slave_obj.context.mem_address++; + context->mem[context->mem_address] = i2c_read_byte_raw(i2c); + if (context->mem_address == OC_I2C_SLAVE_TIME_TRIGGER_REG_END) { + uint32_t* delay_us = (uint32_t*) &context->mem[OC_I2C_SLAVE_TIME_TRIGGER_REG_START]; + oc_i2c_slave_obj.time_trigger = *delay_us; + if (oc_i2c_slave_obj.mp_irq_trigger & OC_I2C_SLAVE_TRIGGER) { + oc_cancel_alarm(&oc_i2c_slave_obj); + oc_i2c_slave_obj.alarm_id = alarm_pool_add_alarm_in_us(oc_i2c_slave_obj.pool, + (uint64_t)(*delay_us), alarm_callback, &oc_i2c_slave_obj, true); + if (oc_i2c_slave_obj.alarm_id == -1) { + mp_raise_OSError(MP_ENOMEM); + } + } + *delay_us = 0; + } + // save a flag of the written memory address + context->mem_written[context->mem_address >> 6] |= 1 << (context->mem_address & (0xff>>2)); + context->mem_address++; + if (context->mem_address == OC_I2C_SLAVE_TIME_TRIGGER_REG_START) { + context->mem_address = 0; + } oc_i2c_slave_obj.mp_irq_flags_unfinished |= OC_I2C_SLAVE_RECEIVE; } break; case I2C_SLAVE_REQUEST: // master is requesting data // load from memory - i2c_write_byte_raw(i2c, oc_i2c_slave_obj.context.mem[oc_i2c_slave_obj.context.mem_address]); + i2c_write_byte_raw(i2c, context->mem[context->mem_address]); //save a flag of read memory address - oc_i2c_slave_obj.context.mem_read[oc_i2c_slave_obj.context.mem_address >> 6] |= 1 << (oc_i2c_slave_obj.context.mem_address & (0xff>>2)); - - oc_i2c_slave_obj.context.mem_address++; + context->mem_read[context->mem_address >> 6] |= 1 << (context->mem_address & (0xff>>2)); + context->mem_address++; + if (context->mem_address == OC_I2C_SLAVE_TIME_TRIGGER_REG_START) { + context->mem_address = 0; + } oc_i2c_slave_obj.mp_irq_flags_unfinished |= OC_I2C_SLAVE_READ; break; case I2C_SLAVE_FINISH: // master has signalled Stop / Restart - //oc_i2c_slave_obj.context.mem_address = 0; - oc_i2c_slave_obj.context.mem_address_written = false; + // TODO: check osciloscope if I2C master sends stop condition during readfrom_mem + //context->mem_address = 0; + context->mem_address_written = false; // Check the flags to see if the user handler should be called if (oc_i2c_slave_obj.mp_irq_trigger & oc_i2c_slave_obj.mp_irq_flags_unfinished) { oc_i2c_slave_obj.mp_irq_flags = oc_i2c_slave_obj.mp_irq_flags_unfinished; @@ -105,6 +146,22 @@ static void oc_i2c_slave_handler(i2c_inst_t *i2c, i2c_slave_event_t event) { } } +static int64_t alarm_callback(alarm_id_t id, void *user_data) { + oc_i2c_slave_obj_t *oc_i2c_slave = (oc_i2c_slave_obj_t*) user_data; + if (oc_i2c_slave->mp_irq_trigger & OC_I2C_SLAVE_TRIGGER) { + oc_i2c_slave->mp_irq_flags = OC_I2C_SLAVE_TRIGGER; + mp_irq_handler(oc_i2c_slave->mp_irq_obj); + } + return 0; +} + +static void oc_cancel_alarm(oc_i2c_slave_obj_t *self) { + if (self->alarm_id != ALARM_ID_INVALID) { + alarm_pool_cancel_alarm(self->pool, self->alarm_id); + self->alarm_id = ALARM_ID_INVALID; + } +} + static mp_obj_t oc_i2c_slave_init_helper(oc_i2c_slave_obj_t *self, size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { enum { ARG_address }; static const mp_arg_t allowed_args[] = { @@ -124,12 +181,15 @@ static mp_obj_t oc_i2c_slave_init_helper(oc_i2c_slave_obj_t *self, size_t n_args } if (self->enabled) { i2c_slave_deinit(UTZ_I2C_BUS); + oc_cancel_alarm(self); } + self->pool = alarm_pool_get_default(); + self->alarm_id = ALARM_ID_INVALID; self->mp_irq_obj = NULL; self->mp_irq_trigger = 0; self->mp_irq_flags = 0; - memset(&self->context, 0, sizeof(oc_i2c_slave_memory)); + memset(&self->context, 0, sizeof(oc_i2c_slave_memory_t)); self->enabled = true; self->address = args[ARG_address].u_int; @@ -176,8 +236,8 @@ static MP_DEFINE_CONST_FUN_OBJ_KW(oc_i2c_slave_init_obj, 1, oc_i2c_slave_init); static void oc_i2c_slave_print(const mp_print_t *print, mp_obj_t self_in) { oc_i2c_slave_obj_t *self = MP_OBJ_TO_PTR(self_in); - mp_printf(print, "Open-Cube I2C slave at address %u, flags=%d", - self->address,self->mp_irq_trigger, self->mp_irq_flags); + mp_printf(print, "Open-Cube I2C slave at address %u, flags=%d, time=%d, mem_add=%u", + self->address, self->mp_irq_trigger, self->time_trigger, self->context.mem_address); } // I2C_slave.read() @@ -246,6 +306,7 @@ static MP_DEFINE_CONST_FUN_OBJ_KW(oc_i2c_slave_irq_obj, 1, oc_i2c_slave_irq); // Called on i2c_slave deinit static mp_obj_t oc_i2c_slave_deinit(mp_obj_t self_in) { oc_i2c_slave_obj_t *self = MP_OBJ_TO_PTR(self_in); + oc_cancel_alarm(self); i2c_slave_deinit(UTZ_I2C_BUS); i2c_deinit(UTZ_I2C_BUS); self->enabled = false; @@ -321,6 +382,7 @@ static const mp_rom_map_elem_t oc_i2c_slave_locals_dict[] = { { MP_ROM_QSTR(MP_QSTR_RECEIVED), MP_ROM_INT(OC_I2C_SLAVE_RECEIVE) }, \ { MP_ROM_QSTR(MP_QSTR_READ), MP_ROM_INT(OC_I2C_SLAVE_READ) }, \ + { MP_ROM_QSTR(MP_QSTR_TRIGGER), MP_ROM_INT(OC_I2C_SLAVE_TRIGGER) }, \ }; static MP_DEFINE_CONST_DICT(oc_i2c_slave_locals_dict_obj, oc_i2c_slave_locals_dict); diff --git a/micropython/mpy b/micropython/mpy index 3823aeb0f14c04084eba6566164d9b7dbd9e7ced..af2770e02c1fcd9e6517223ae81c9a0d089a23e1 160000 --- a/micropython/mpy +++ b/micropython/mpy @@ -1 +1 @@ -Subproject commit 3823aeb0f14c04084eba6566164d9b7dbd9e7ced +Subproject commit af2770e02c1fcd9e6517223ae81c9a0d089a23e1