This post is older than 2 years and might not be relevant anymore
More Info: Consider searching for newer posts

Dynamic reconfiguration of peripheral HW from TWI to SPI

Our nRF52840 is communicating with a peripheral chip (let's call it FOO) through an SPI interface. The FOO chip can be configured to use either TWI or SPI on the same pins (well TWI uses a subset of the SPI pins), the default mode after FOO reset is TWI and a register TWI_DISABLE in FOO has to be configured to ensure that TWI does not risk to spoil SPI by accident.

We need SPI, because the TWI does not have sufficient throughput to use all of the features of the FOO chip.

When the FOO chip starts up, the TWI_DISABLE is not set, so FOO is in a sort of blind detection mode in which it will accept both SPI and TWI at the same time. In this blind mode, the TWI cannot be disturbed by the SPI, because the SPI needs chip select (CS) pin active, and the CS pin default setting is inactive when in TWI mode. However, the converse statement is not true : theoretically it is possible that bad luck causes that you accidentally have TWI acting while in SPI and make the SPI link fail. 

It is possible after startup of FOO to make a loop so that, while communicating in SPI, you write the TWI_DISABLE register and check it by reading it afterwards until TWI_DISABLE is really set, and then you are OK. We checked this solution and it is working fine to do so and seems to be the standard approach.

However that sounds like a dirty solution that we cannot guarantee will always in all circumstances and over millions of items work. We would like to make it really bulletproof and another solution is to start to communicating with FOO over TWI, write the TWI disable register of FOO so that the TWI is disabled in FOO, and then we are OK too for using SPI safely.

We checked this solution too with some quick and dirty test FW and it works fine.  However it is not possible to make it easily with the Nordic SDK 15 because the SDK ensures at compile time that you cannot use the same HW peripheral in TWI and in SPI. Say for instance that I want to use SPI0 to communicate with FOO, then I cannot enable TWI0 too, even though there would not be any conflict because the FW would use TWI0 only for a short time at the beginning just to set TWI_DISABLE FOO register and then be uninit'ed.

It is not possible for us to use TWI1 instead of TWI0 because all the HW ressources of nRF52840 are taken in our application, so if we go to the bulletproof solution we really need to reconfigure dynamically (at runtime, not at compile time) the same nRF52840 peripheral. The nRF52840 HW allows this, but this is a limitation of the SDK that makes it impossible. This limitation is really quite usefully and a very good idea for most of the cases because it prevents people from having HW conflicts in their design, however, in our very specific corner case it is preventing us to make it.

Your support is most welcome…

Parents Reply Children
  • Hello,

    Yes the NRF_PRS_BOX_0_ENABLED is set. This was anyway a good point to mention, as in the first place I did the mistake of not setting it. I have made a suggestion to improve the sdk_validation.h to that respect.

    I think I have made some progress in understanding what happens.

    First of all the twi_irq_handler is not called, I have tried with a debugger and a JLink, with putting a breakpoint on this function.

    I also checked that p_cb->handler and p_cb->context are correctly set to my callback function (but anyway, that does not help as the irq handler is not called).

    Then, I saw that when nrfx_twi_xfer is called by my FreeRTOS task, it calls in turn twi_xfer, which in turn calls twi_tx_start_transfer. In twi_tx_start_transfer, p_cb->int_mask is set to 0x286 which should be NRF_TWI_INT_STOPPED_MASK | NRF_TWI_INT_ERROR_MASK | NRF_TWI_INT_TXDSENT_MASK | NRF_TWI_INT_RXDREADY_MASK, and nrf_twi_int_enable(p_twi, p_cb->int_mask) is called.

    However, I noticed that this call occurs after the call to (void)twi_send_byte(p_twi, p_data, length, &p_cb->bytes_transferred, no_stop). Hopefully it is not too late, and the interrupt is still pending.

    Well, if there is any experiment (like reading some memory) you would like me to do, please feel free to ask !

  • Just as a side comment, I am rather confident in the way that the TWI is configured, because I first did the experiment with two different peripherals, TWI1 and SPI0 and the legacy API. And in this case the TWI transfer works OK.

    My problem occurred after porting to the new API, and trying to use the PRS stuff to share TWI0 + SPI0. 

  • I've reported your issue to the SDK developers, but It will take some time before they can look into it. We are nevertheless very greatful for your report. 

  • The only two reasons I can think of is that the interrupts are disabled or the CPU is stuck in a higher priority ISR. The TWI peripheral should fire the ERROR event if any operation outside the spec is occuring. 

    Do you see any strange behavior on the TWI lines?

    What is the state of the TWI0 registers before the STARTRX or TX  is triggered?

    Have you tried using the TWIM/TWIS drivers? As they use EasyDMA and PPI shortcuts they require less CPU resources, whom I assume you are in dire need of if your running an RTOS on top of the SoftDevice. 

  • Hello,

    Thank you vey much for the feedback, I will try to answer point by point.

    Do you see any strange behavior on the TWI lines?

    Well, I cannot say this now, I need to make some setup so that I can observes the line on the scope, I am afraid I don't have easy easy test point to get them right away.

    What is the state of the TWI0 registers before the STARTRX or TX  is triggered?

    The first function that is called is nrfx_twi_xfer, when I am at line 601 of file nrfx_twi.c here is what I can print in gdb:

    nrfx_twi_xfer (p_instance=0x1d210 <m_FOO_twi_transient_i2c>, p_xfer_desc=0x200041cc, flags=0)
    at /Users/vincentbelaiche/Projects/biosency-fw_dev3.4/boraconnect-firmware/nrf-sdk/modules/nrfx/drivers/src/nrfx_twi.c:583
    583 nrfx_err_t err_code = NRFX_SUCCESS;
    (gdb) until
    until
    584 twi_control_block_t * p_cb = &m_cb[p_instance->drv_inst_idx];
    (gdb) until
    until
    601 err_code = twi_xfer(p_cb, (NRF_TWI_Type *)p_instance->p_twi, p_xfer_desc, flags);
    (gdb) print *p_cb
    print *p_cb
    $1 = {handler = 0x7461 <FOO_transient_twi_cb>, p_context = 0x20002790 <m_FOO_context_s>, int_mask = 0, xfer_desc = {
    type = NRFX_TWI_XFER_TX, address = 0 '\000', primary_length = 0, secondary_length = 0, p_primary_buf = 0x0 <__isr_vector>,
    p_secondary_buf = 0x0 <__isr_vector>}, flags = 0, p_curr_buf = 0x0 <__isr_vector>, curr_length = 0 '\000',
    curr_no_stop = false, state = NRFX_DRV_STATE_POWERED_ON, error = false, busy = false, repeated = false,
    bytes_transferred = 0 '\000', hold_bus_uninit = true}
    (gdb) print /x *((NRF_TWI_Type *)p_instance->p_twi)
    print /x *((NRF_TWI_Type *)p_instance->p_twi)
    $3 = {TASKS_STARTRX = 0x0, RESERVED0 = 0x0, TASKS_STARTTX = 0x0, RESERVED1 = {0x0, 0x0}, TASKS_STOP = 0x0, RESERVED2 = 0x0,
    TASKS_SUSPEND = 0x0, TASKS_RESUME = 0x0, RESERVED3 = {0x0 <repeats 56 times>}, EVENTS_STOPPED = 0x0, EVENTS_RXDREADY = 0x0,
    RESERVED4 = {0x0, 0x0, 0x0, 0x0}, EVENTS_TXDSENT = 0x0, RESERVED5 = 0x0, EVENTS_ERROR = 0x0, RESERVED6 = {0x0, 0x0, 0x0, 0x0},
    EVENTS_BB = 0x0, RESERVED7 = {0x0, 0x0, 0x0}, EVENTS_SUSPENDED = 0x0, RESERVED8 = {0x0 <repeats 45 times>}, SHORTS = 0x0,
    RESERVED9 = {0x0 <repeats 64 times>}, INTENSET = 0x0, INTENCLR = 0x0, RESERVED10 = {0x0 <repeats 61 times>, 0x1,
    0x0 <repeats 48 times>}, ERRORSRC = 0x0, RESERVED11 = {0x0 <repeats 14 times>}, ENABLE = 0x5, RESERVED12 = 0x0, PSEL = {
    SCL = 0x1b, SDA = 0x8}, RESERVED13 = {0xffffffff, 0xffffffff}, RXD = 0x0, TXD = 0x0, RESERVED14 = 0x0, FREQUENCY = 0x6680000,
    RESERVED15 = {0x0 <repeats 12 times>, 0x2, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x0}, ADDRESS = 0x0}
    (gdb)

    Please note that in the above I have renamed some of my symbols with …_FOO_… to be consistent with the beginning of this discussion.

    Have you tried using the TWIM/TWIS drivers? As they use EasyDMA and PPI shortcuts they require less CPU resources, whom I assume you are in dire need of if your running an RTOS on top of the SoftDevice. 

    No, I have not tried that yet, though it is a good idea certainly.

    The FW which I am using is not the full application FW, it is a test FW with only the modules at stake. There is no SoftDevice in it, but the RTOS is there.

    I must admit that the NRF_LOGGER is used and may take some resource, however I don't think that it is the root cause because for some other module with TWI interfacing in a similar way and quite intensive logging there is no problem. 

Related