<?xml version="1.0" encoding="UTF-8" ?>
<?xml-stylesheet type="text/xsl" href="https://devzone.nordicsemi.com/cfs-file/__key/system/syndication/rss.xsl" media="screen"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Errata 219 is incomplete or misleading. Unexpected behaviour at 100kHz. TWIM clock too short after clock stretch.</title><link>https://devzone.nordicsemi.com/f/nordic-q-a/116214/errata-219-is-incomplete-or-misleading-unexpected-behaviour-at-100khz-twim-clock-too-short-after-clock-stretch</link><description>Potential TLDR: 
 The nRF52 and possibly nRF53 are unusable at 100kHz with targets that require clock stretching and enforce a reasonable minimum clock period. This covers a very broad selection of targets, in particular lot of TI parts included in COTS</description><dc:language>en-US</dc:language><generator>Telligent Community 13</generator><lastBuildDate>Tue, 11 Feb 2025 03:29:56 GMT</lastBuildDate><atom:link rel="self" type="application/rss+xml" href="https://devzone.nordicsemi.com/f/nordic-q-a/116214/errata-219-is-incomplete-or-misleading-unexpected-behaviour-at-100khz-twim-clock-too-short-after-clock-stretch" /><item><title>RE: Errata 219 is incomplete or misleading. Unexpected behaviour at 100kHz. TWIM clock too short after clock stretch.</title><link>https://devzone.nordicsemi.com/thread/522330?ContentTypeID=1</link><pubDate>Tue, 11 Feb 2025 03:29:56 GMT</pubDate><guid isPermaLink="false">137ad170-7792-4731-bb38-c0d22fbe4515:d034d006-da90-4c5b-ae01-35ecf2b1ebcb</guid><dc:creator>hmolesworth</dc:creator><description>&lt;p&gt;Errata 219; I spent too much time on this but it is a big issue given quite a few devices use clock stretching. All my comments in the previous posts in this thread apply, this is just a summery of the work-around code I am using in the expectation it will be useful to others.&lt;/p&gt;
&lt;p&gt;The code below uses two spare output pins and a spare 6-CC timer; the 64Mhz/16MHz clock source is not important, either the internal RC or an external 32MHz crystal, as the clock is supplied by the TWIM peripheral to both timer and slave. Both spare output pins are configured as open-drain (H0D1); the optional spoof ACK pin connects to SDA and the required spoof STRETCH pin connects to SCL. The optional spoof ACK pin allows testing without a slave SPIS device attached; the spoof STRETCH pin is required to extend any asynchronous slave stretch to a safe length where the following SCL clock pulse is normal width and not corrupted as a consequence of badly-handled external asynchronous signals. Should the cases where stretch from the slave is involved be predictable the stretch can be on selective bytes, but if&amp;nbsp;not predictable every transmitted byte has to be spoof stretched.&lt;/p&gt;
&lt;p&gt;Tested on nRF52832 and nRF52833. There is an option to use a separate counter to count clock edges for the spoof ACK, but that&amp;#39;s not required if only 2 ACKs are required.&lt;/p&gt;
&lt;p&gt;&lt;pre class="ui-code" data-mode="c_cpp"&gt;// Errata 219
//
// Set a spare port pin to be H0D1 output and connect to SCL
// Set another spare port pin to be H0D1 output and connect to SDA if spoofing the ACK
// Start a Timer at the same time as the TWIM events STARTED using PPI
// Set the Timer CC[0] register to trigger the spare port pin Low via PPI after a time equal to 9 x SCL
//  clock cycles, say 90 uSecs, after the 9th clock and coincident with a slave clock stretch if one is
//  in progress but before the next byte clock if no slave stretch is in progress
// Set the Timer CC[1] to clear the timer and set the port pin High (actually float as open-drain) via
//  PPI to end the synchronous stretch say after another 100uSec, ie at 190uSec
// Stop and Clear the Timer and set the port pin High (actually float as open-drain) on the TWIM events
//  STOPPED using PPI
// Tune this synchronous stretch to be longer than the longest slave stretch but without generating
//  the race-hazard pulse
// The clocks for both TWIM and Timer are synchronous from 16MHz regardless of whether using the crystal.
// The synchronous stretch could be added for every byte regardless of whether requested by the slave.

#define WHO_AM_I          0x0F
#define I_AM_LSM6DS       0x6A
#define LSM6DS3TR_ADDRESS 0xD4

// Set sizes to suit, will be prefixed by i2c address byte
static uint8_t TxBuffer[] = {WHO_AM_I};
static uint8_t RxBuffer[3];
static NRF_TWIM_Type * const pTWIM = NRF_TWIM0;
// nRF52833 DK
#define I2C_SCL_PIN    13  // open-drain output pin
#define I2C_SDA_PIN    14  // open-drain output pin
#define LED_RED_PIN    15
#define LED_GREEN_PIN  16
#define LED_BLUE_PIN   17

// Uncomment this next line to use feedback pin from SCL to operate Ack counter
//#define FEATURE_COUNTER_FOR_SPOOF_ACK
// Uncomment this next line to just Tx, don&amp;#39;t start a subsequent Rx
#define FEATURE_TX_ONLY_NO_RX

#define PIN_TWIM_STRETCH   3  // synchronous stretch open-drain output pin
#define PIN_TWIM_SPOOF_ACK 4  // spoof ACK open-drain output pin
#if defined(FEATURE_COUNTER_FOR_SPOOF_ACK)
#define I2C_SCL_INPUT_PIN  28 // feedback pin from SCL to operate Ack counter
#endif

#define PPI_STRETCH_TXSTART     0 // Start pTimerStretch with TWIM Tx
#define PPI_STRETCH_RXSTART     1 // Start pTimerStretch with TWIM Rx
#define PPI_STRETCH_STOP        2 // Stop pTimerStretch after TWIM tx
#define PPI_STRETCH_LOW         3 // Drive synchronous stretch output pin low
#define PPI_STRETCH_HIGH        4 // Float synchronous stretch output pin
#define PPI_SPOOF_ACK_1_LOW     5 // Drive synchronous spoof ACK output pin low
#define PPI_SPOOF_ACK_1_HIGH    6 // Float synchronous spoof ACK output pin
#define PPI_SPOOF_ACK_2_LOW     7 // Drive synchronous spoof ACK output pin low
#define PPI_SPOOF_ACK_2_HIGH    8 // Float synchronous spoof ACK output pin
#if defined(FEATURE_COUNTER_FOR_SPOOF_ACK)
#define PPI_SCL_INPUT           9 // Monitor SCL output pin
#endif

#define GPIOTE_TWIM_STRETCH   0 // GPIOTE to control synchronous stretch output pin
#define GPIOTE_TWIM_SPOOF_ACK 1 // GPIOTE to control spoof ACK output pin
#if defined(FEATURE_COUNTER_FOR_SPOOF_ACK)
#define GPIOTE_TWIM_SPOOF_SCL 2 // Monitor SCL output pin
#endif

// TIMER0 is used by the RADIO, so don&amp;#39;t use for now
static NRF_TIMER_Type * const pTimerStretch = NRF_TIMER3; // 6 x CC
#if defined(FEATURE_COUNTER_FOR_SPOOF_ACK)
static NRF_TIMER_Type * const pCounterAck   = NRF_TIMER4; // 6 x CC
#endif

void I2C_init(void)
{
   pTWIM-&amp;gt;ENABLE = 0;
   // Testing i2c twi twim, i2c pins both released
   NRF_P0-&amp;gt;OUT = (1 &amp;lt;&amp;lt; I2C_SCL_PIN) | (1 &amp;lt;&amp;lt; I2C_SDA_PIN);
   // Start with green led on
   NRF_P0-&amp;gt;OUT |= (1 &amp;lt;&amp;lt; LED_RED_PIN) | (0 &amp;lt;&amp;lt; LED_GREEN_PIN) | (1 &amp;lt;&amp;lt; LED_BLUE_PIN);
   // Configue port pins
   // Configuration                       Direction    Input            Pullup         Drive Level      Sense Level
   // ================================    ==========   ==============   ============   ==============   =============
   NRF_P0-&amp;gt;PIN_CNF[LED_RED_PIN]        = (PIN_OUTPUT | PIN_DISCONNECT | PIN_PULLUP   | PIN_DRIVE_H0D1 | PIN_SENSE_OFF);
   NRF_P0-&amp;gt;PIN_CNF[LED_GREEN_PIN]      = (PIN_OUTPUT | PIN_DISCONNECT | PIN_PULLUP   | PIN_DRIVE_H0D1 | PIN_SENSE_OFF);
   NRF_P0-&amp;gt;PIN_CNF[LED_BLUE_PIN]       = (PIN_OUTPUT | PIN_DISCONNECT | PIN_PULLUP   | PIN_DRIVE_H0D1 | PIN_SENSE_OFF);
   NRF_P0-&amp;gt;PIN_CNF[I2C_SCL_PIN]        = (PIN_INPUT  | PIN_CONNECT    | PIN_PULLUP   | PIN_DRIVE_H0D1 | PIN_SENSE_OFF);
   NRF_P0-&amp;gt;PIN_CNF[I2C_SDA_PIN]        = (PIN_INPUT  | PIN_CONNECT    | PIN_PULLUP   | PIN_DRIVE_H0D1 | PIN_SENSE_OFF);
   NRF_P0-&amp;gt;PIN_CNF[PIN_TWIM_STRETCH]   = (PIN_OUTPUT | PIN_DISCONNECT | PIN_PULLUP   | PIN_DRIVE_H0D1 | PIN_SENSE_OFF);
   NRF_P0-&amp;gt;PIN_CNF[PIN_TWIM_SPOOF_ACK] = (PIN_OUTPUT | PIN_DISCONNECT | PIN_PULLUP   | PIN_DRIVE_H0D1 | PIN_SENSE_OFF);
#if defined(FEATURE_COUNTER_FOR_SPOOF_ACK)
   NRF_P0-&amp;gt;PIN_CNF[I2C_SCL_INPUT_PIN]  = (PIN_INPUT  | PIN_CONNECT    | PIN_PULLUP   | PIN_DRIVE_S0S1 | PIN_SENSE_OFF);
#endif
   // Set up i2c peripheral
   pTWIM-&amp;gt;PSEL.SCL=I2C_SCL_PIN;
   pTWIM-&amp;gt;PSEL.SDA=I2C_SDA_PIN;
   pTWIM-&amp;gt;FREQUENCY = TWI_FREQUENCY_FREQUENCY_K100; //100/250/400 KHz
#if defined(FEATURE_TX_ONLY_NO_RX)
   // Do just Tx then stop, don&amp;#39;t start a subsequent Rx
   pTWIM-&amp;gt;SHORTS = (TWIM_SHORTS_LASTTX_STOP_Enabled &amp;lt;&amp;lt; TWIM_SHORTS_LASTTX_STOP_Pos);
#else
   // Do Tx followed by Rx response then stop
   pTWIM-&amp;gt;SHORTS = (TWIM_SHORTS_LASTTX_STARTRX_Enabled &amp;lt;&amp;lt; TWIM_SHORTS_LASTTX_STARTRX_Pos) | (TWIM_SHORTS_LASTRX_STOP_Enabled &amp;lt;&amp;lt; TWIM_SHORTS_LASTRX_STOP_Pos);
#endif
   // Transmit data
   pTWIM-&amp;gt;TXD.MAXCNT = sizeof(TxBuffer);
   pTWIM-&amp;gt;TXD.PTR = (uint32_t)&amp;amp;TxBuffer[0];
   // 1-255 bytes receive data
   pTWIM-&amp;gt;RXD.MAXCNT = sizeof(RxBuffer);
   pTWIM-&amp;gt;RXD.PTR = (uint32_t)&amp;amp;RxBuffer[0];
   pTWIM-&amp;gt;RXD.LIST = 0;
   // Clear all events
   pTWIM-&amp;gt;EVENTS_ERROR     = 0;
   pTWIM-&amp;gt;EVENTS_SUSPENDED = 0;
   pTWIM-&amp;gt;EVENTS_TXSTARTED = 0;
   pTWIM-&amp;gt;EVENTS_RXSTARTED = 0;
   pTWIM-&amp;gt;EVENTS_STOPPED   = 0;
   pTWIM-&amp;gt;EVENTS_LASTRX    = 0;
   pTWIM-&amp;gt;EVENTS_LASTTX    = 0;
   // Disable all interrupts
   pTWIM-&amp;gt;INTENCLR = 0xFFFFFFFFUL;
   __DSB();

   // Configure GPIOTE-&amp;gt;TASKS_OUT[GPIOTE_TWIM_STRETCH] to control PIN_TWIM_STRETCH
   NRF_GPIOTE-&amp;gt;CONFIG[GPIOTE_TWIM_STRETCH] = (GPIOTE_CONFIG_MODE_Task       &amp;lt;&amp;lt; GPIOTE_CONFIG_MODE_Pos)     |
                                             (GPIOTE_CONFIG_OUTINIT_High    &amp;lt;&amp;lt; GPIOTE_CONFIG_OUTINIT_Pos)  |
                                             (GPIOTE_CONFIG_POLARITY_LoToHi &amp;lt;&amp;lt; GPIOTE_CONFIG_POLARITY_Pos) |
                                             (PIN_TWIM_STRETCH              &amp;lt;&amp;lt; GPIOTE_CONFIG_PSEL_Pos);
   // Configure GPIOTE-&amp;gt;TASKS_OUT[GPIOTE_TWIM_SPOOF_ACK] to read
   NRF_GPIOTE-&amp;gt;CONFIG[GPIOTE_TWIM_SPOOF_ACK] = (GPIOTE_CONFIG_MODE_Task       &amp;lt;&amp;lt; GPIOTE_CONFIG_MODE_Pos)     |
                                               (GPIOTE_CONFIG_OUTINIT_High    &amp;lt;&amp;lt; GPIOTE_CONFIG_OUTINIT_Pos)  |
                                               (GPIOTE_CONFIG_POLARITY_LoToHi &amp;lt;&amp;lt; GPIOTE_CONFIG_POLARITY_Pos) |
                                               (PIN_TWIM_SPOOF_ACK            &amp;lt;&amp;lt; GPIOTE_CONFIG_PSEL_Pos);
#if defined(FEATURE_COUNTER_FOR_SPOOF_ACK)
   // Configure GPIOTE-&amp;gt;TASKS_OUT[GPIOTE_TWIM_SPOOF_ACK] to control PPI_SCL_INPUT falling edge
   // Data is transferred Most Significant Bit (MSB) first. Any number of data bytes can be transferred from the
   // master to slave between the START and STOP conditions. Data on the SDA line must remain stable
   // during the high phase of the clock period, as changes in the data line when the SCL is high are
   // interpreted as control commands (START or STOP).
   NRF_GPIOTE-&amp;gt;CONFIG[GPIOTE_TWIM_SPOOF_SCL] = (GPIOTE_CONFIG_MODE_Event      &amp;lt;&amp;lt; GPIOTE_CONFIG_MODE_Pos)     |
                                               (GPIOTE_CONFIG_OUTINIT_High    &amp;lt;&amp;lt; GPIOTE_CONFIG_OUTINIT_Pos)  |
                                               (GPIOTE_CONFIG_POLARITY_HiToLo &amp;lt;&amp;lt; GPIOTE_CONFIG_POLARITY_Pos) |
                                               (I2C_SCL_INPUT_PIN             &amp;lt;&amp;lt; GPIOTE_CONFIG_PSEL_Pos);
#endif

// Stretch the 2nd byte before the associated Ack clock cycle
#define SPOOF_STRETCH_LENGTH_USECS (16*(2*10)-0)   // 16MHz   20uSecs Start synch stretch
#define SPOOF_2ND_ACK_STOP_OFFSET  (SPOOF_STRETCH_LENGTH_USECS-(16*(1*10)))
#define SPOOF_STRETCH_START_USECS  (16*(20*10+4))  // 16MHz   80uSecs Start synch stretch
#define SPOOF_STRETCH_STOP_USECS   (SPOOF_STRETCH_START_USECS+SPOOF_STRETCH_LENGTH_USECS)
#define SPOOF_1ST_ACK_START_USECS  (16*(10*10+9))  // 16MHz   80uSecs Start synch spoof 1st byte Ack
#define SPOOF_1ST_ACK_STOP_USECS   (16*(11*10+6))  // 16MHz   90uSecs End synch spoof 1st byte Ack
#define SPOOF_2ND_ACK_START_USECS  (16*(20*10+9))  // 16MHz   80uSecs Start synch spoof 2nd byte Ack
#define SPOOF_2ND_ACK_STOP_USECS   (16*(21*10+6))  // 16MHz   90uSecs End synch spoof 2nd byte Ack
// Cycle ends on 2nd Ack, make sure strecth has gone by then
STATIC_ASSERT(SPOOF_STRETCH_STOP_USECS &amp;lt; SPOOF_2ND_ACK_START_USECS+SPOOF_STRETCH_LENGTH_USECS, &amp;quot;SPOOF_STRETCH_STOP_USECS not &amp;lt; SPOOF_2ND_ACK_START_USECS&amp;quot;);

   // TWIM Stretch: Configure pTimerStretch as Timer to generate EVENTS_COMPARE[0] at 90uSecs, EVENTS_COMPARE[1] at 190uSecs
   pTimerStretch-&amp;gt;MODE = TIMER_MODE_MODE_Timer &amp;lt;&amp;lt; TIMER_MODE_MODE_Pos;
   pTimerStretch-&amp;gt;BITMODE = TIMER_BITMODE_BITMODE_16Bit &amp;lt;&amp;lt; TIMER_BITMODE_BITMODE_Pos;
   pTimerStretch-&amp;gt;SHORTS = TIMER_SHORTS_COMPARE5_CLEAR_Enabled &amp;lt;&amp;lt; TIMER_SHORTS_COMPARE5_CLEAR_Pos;
   pTimerStretch-&amp;gt;PRESCALER   =  0;  // 16MHz clock
   pTimerStretch-&amp;gt;CC[0]       = SPOOF_STRETCH_START_USECS;  // 16MHz* 90  =  90uSecs Start synch stretch
   pTimerStretch-&amp;gt;CC[1]       = SPOOF_STRETCH_STOP_USECS;   // 16MHz*190 = 190uSecs  End synch stretch, clear timer to restart
   pTimerStretch-&amp;gt;CC[2]       = SPOOF_1ST_ACK_START_USECS;  // 80uSecs Start synch spoof Ack 1st byte
   pTimerStretch-&amp;gt;CC[3]       = SPOOF_1ST_ACK_STOP_USECS;   // 90uSecs End synch spoof Ack 1st byte
   pTimerStretch-&amp;gt;CC[4]       = SPOOF_2ND_ACK_START_USECS+SPOOF_2ND_ACK_STOP_OFFSET;  // 80uSecs Start synch spoof Ack 2nd byte
   pTimerStretch-&amp;gt;CC[5]       = SPOOF_2ND_ACK_STOP_USECS+SPOOF_2ND_ACK_STOP_OFFSET;   // 90uSecs End synch spoof Ack 2nd byte
   //pTimerStretch-&amp;gt;TASKS_START =  1;
   // Configure PPI channel with connection between TIMER-&amp;gt;EVENTS_COMPARE[0] and GPIOTE-&amp;gt;TASKS_CLR[GPIOTE_TWIM_STRETCH]
   NRF_PPI-&amp;gt;CH[PPI_STRETCH_LOW].EEP  = (uint32_t)&amp;amp;pTimerStretch-&amp;gt;EVENTS_COMPARE[0];
   NRF_PPI-&amp;gt;CH[PPI_STRETCH_LOW].TEP  = (uint32_t)&amp;amp;NRF_GPIOTE-&amp;gt;TASKS_CLR[GPIOTE_TWIM_STRETCH];
   // Configure PPI channel with connection between TIMER-&amp;gt;EVENTS_COMPARE[1] and GPIOTE-&amp;gt;TASKS_SET[GPIOTE_TWIM_STRETCH]
   NRF_PPI-&amp;gt;CH[PPI_STRETCH_HIGH].EEP = (uint32_t)&amp;amp;pTimerStretch-&amp;gt;EVENTS_COMPARE[1];
   NRF_PPI-&amp;gt;CH[PPI_STRETCH_HIGH].TEP = (uint32_t)&amp;amp;NRF_GPIOTE-&amp;gt;TASKS_SET[GPIOTE_TWIM_STRETCH];
#if !defined(FEATURE_COUNTER_FOR_SPOOF_ACK)
   // Data is transferred Most Significant Bit (MSB) first. Any number of data bytes can be transferred from the
   // master to slave between the START and STOP conditions. Data on the SDA line must remain stable
   // during the high phase of the clock period, as changes in the data line when the SCL is high are
   // interpreted as control commands (START or STOP)
   // Configure PPI channel with connection between TIMER-&amp;gt;EVENTS_COMPARE[2] and GPIOTE-&amp;gt;TASKS_CLR[GPIOTE_TWIM_SPOOF_ACK]
   NRF_PPI-&amp;gt;CH[PPI_SPOOF_ACK_1_LOW].EEP  = (uint32_t)&amp;amp;pTimerStretch-&amp;gt;EVENTS_COMPARE[2];
   NRF_PPI-&amp;gt;CH[PPI_SPOOF_ACK_1_LOW].TEP  = (uint32_t)&amp;amp;NRF_GPIOTE-&amp;gt;TASKS_CLR[GPIOTE_TWIM_SPOOF_ACK];
   // Configure PPI channel with connection between TIMER-&amp;gt;EVENTS_COMPARE[3] and GPIOTE-&amp;gt;TASKS_SET[GPIOTE_TWIM_SPOOF_ACK]
   NRF_PPI-&amp;gt;CH[PPI_SPOOF_ACK_1_HIGH].EEP = (uint32_t)&amp;amp;pTimerStretch-&amp;gt;EVENTS_COMPARE[3];
   NRF_PPI-&amp;gt;CH[PPI_SPOOF_ACK_1_HIGH].TEP = (uint32_t)&amp;amp;NRF_GPIOTE-&amp;gt;TASKS_SET[GPIOTE_TWIM_SPOOF_ACK];
   // Configure PPI channel with connection between TIMER-&amp;gt;EVENTS_COMPARE[4] and GPIOTE-&amp;gt;TASKS_CLR[GPIOTE_TWIM_SPOOF_ACK]
   NRF_PPI-&amp;gt;CH[PPI_SPOOF_ACK_2_LOW].EEP  = (uint32_t)&amp;amp;pTimerStretch-&amp;gt;EVENTS_COMPARE[4];
   NRF_PPI-&amp;gt;CH[PPI_SPOOF_ACK_2_LOW].TEP  = (uint32_t)&amp;amp;NRF_GPIOTE-&amp;gt;TASKS_CLR[GPIOTE_TWIM_SPOOF_ACK];
   // Configure PPI channel with connection between TIMER-&amp;gt;EVENTS_COMPARE[5] and GPIOTE-&amp;gt;TASKS_SET[GPIOTE_TWIM_SPOOF_ACK]
   NRF_PPI-&amp;gt;CH[PPI_SPOOF_ACK_2_HIGH].EEP = (uint32_t)&amp;amp;pTimerStretch-&amp;gt;EVENTS_COMPARE[5];
   NRF_PPI-&amp;gt;CH[PPI_SPOOF_ACK_2_HIGH].TEP = (uint32_t)&amp;amp;NRF_GPIOTE-&amp;gt;TASKS_SET[GPIOTE_TWIM_SPOOF_ACK];
#endif
   // Configure PPI channel with connection between TWIM-&amp;gt;EVENTS_TXSTARTED and TIMER-&amp;gt;TASKS_START
   NRF_PPI-&amp;gt;CH[PPI_STRETCH_TXSTART].EEP  = (uint32_t)&amp;amp;pTWIM-&amp;gt;EVENTS_TXSTARTED;
   NRF_PPI-&amp;gt;CH[PPI_STRETCH_TXSTART].TEP  = (uint32_t)&amp;amp;pTimerStretch-&amp;gt;TASKS_START;
   //NRF_PPI-&amp;gt;FORK[PPI_STRETCH_TXSTART].TEP = (uint32_t)&amp;amp;NRF_GPIOTE-&amp;gt;TASKS_SET[GPIOTE_TWIM_STRETCH];
#if !defined(FEATURE_TX_ONLY_NO_RX)
   // Configure PPI channel with connection between TWIM-&amp;gt;EVENTS_RXSTARTED and TIMER-&amp;gt;TASKS_START
   NRF_PPI-&amp;gt;CH[PPI_STRETCH_RXSTART].EEP  = (uint32_t)&amp;amp;pTWIM-&amp;gt;EVENTS_RXSTARTED;
   NRF_PPI-&amp;gt;CH[PPI_STRETCH_RXSTART].TEP  = (uint32_t)&amp;amp;pTimerStretch-&amp;gt;TASKS_START;
   //NRF_PPI-&amp;gt;FORK[PPI_STRETCH_RXSTART].TEP = (uint32_t)&amp;amp;NRF_GPIOTE-&amp;gt;TASKS_SET[GPIOTE_TWIM_STRETCH];
#endif
   // Configure PPI channel with connection between TWIM-&amp;gt;EVENTS_STOPPED and TIMER-&amp;gt;TASKS_STOP
   NRF_PPI-&amp;gt;CH[PPI_STRETCH_STOP].EEP = (uint32_t)&amp;amp;pTWIM-&amp;gt;EVENTS_STOPPED;
   NRF_PPI-&amp;gt;CH[PPI_STRETCH_STOP].TEP = (uint32_t)&amp;amp;pTimerStretch-&amp;gt;TASKS_STOP;
   NRF_PPI-&amp;gt;FORK[PPI_STRETCH_STOP].TEP = (uint32_t)&amp;amp;NRF_GPIOTE-&amp;gt;TASKS_OUT[GPIOTE_TWIM_STRETCH];

#if defined(FEATURE_COUNTER_FOR_SPOOF_ACK)
   // Configure PPI channel with connection between fallling edge of SCL and pCounterAck clock
   NRF_PPI-&amp;gt;CH[PPI_SCL_INPUT].EEP = (uint32_t)&amp;amp;NRF_GPIOTE-&amp;gt;EVENTS_IN[GPIOTE_TWIM_SPOOF_SCL]; // Event
   NRF_PPI-&amp;gt;CH[PPI_SCL_INPUT].TEP = (uint32_t)&amp;amp;pCounterAck-&amp;gt;TASKS_COUNT;

   // TWIM Ack: Configure pCounterAck as Counter to generate ACK after every 8 SCL clock cycles
   pCounterAck-&amp;gt;MODE = TIMER_MODE_MODE_Counter &amp;lt;&amp;lt; TIMER_MODE_MODE_Pos;
   pCounterAck-&amp;gt;BITMODE = TIMER_BITMODE_BITMODE_16Bit &amp;lt;&amp;lt; TIMER_BITMODE_BITMODE_Pos;
   pCounterAck-&amp;gt;SHORTS = TIMER_SHORTS_COMPARE1_CLEAR_Enabled &amp;lt;&amp;lt; TIMER_SHORTS_COMPARE1_CLEAR_Pos;
   pCounterAck-&amp;gt;PRESCALER   =  0;  // 16MHz clock
   pCounterAck-&amp;gt;CC[0]       =  7;//8;  // Start synch spoof Ack
   pCounterAck-&amp;gt;CC[1]       = 10;  // End synch spoof Ack, clear counter to restart
   pCounterAck-&amp;gt;CC[2]       = 17;//8;  // Start synch spoof Ack
   pCounterAck-&amp;gt;CC[3]       = 20;  // End synch spoof Ack, clear counter to restart
   // Configure PPI channel with connection between TIMER-&amp;gt;EVENTS_COMPARE[0] and GPIOTE-&amp;gt;TASKS_CLR[GPIOTE_TWIM_SPOOF_ACK]
   NRF_PPI-&amp;gt;CH[PPI_SPOOF_ACK_1_LOW].EEP  = (uint32_t)&amp;amp;pCounterAck-&amp;gt;EVENTS_COMPARE[0];
   NRF_PPI-&amp;gt;CH[PPI_SPOOF_ACK_1_LOW].TEP  = (uint32_t)&amp;amp;NRF_GPIOTE-&amp;gt;TASKS_CLR[GPIOTE_TWIM_SPOOF_ACK];
   // Configure PPI channel with connection between TIMER-&amp;gt;EVENTS_COMPARE[1] and GPIOTE-&amp;gt;TASKS_SET[GPIOTE_TWIM_SPOOF_ACK]
   NRF_PPI-&amp;gt;CH[PPI_SPOOF_ACK_1_HIGH].EEP = (uint32_t)&amp;amp;pCounterAck-&amp;gt;EVENTS_COMPARE[1];
   NRF_PPI-&amp;gt;CH[PPI_SPOOF_ACK_1_HIGH].TEP = (uint32_t)&amp;amp;NRF_GPIOTE-&amp;gt;TASKS_SET[GPIOTE_TWIM_SPOOF_ACK];
   // Configure PPI channel with connection between TIMER-&amp;gt;EVENTS_COMPARE[2] and GPIOTE-&amp;gt;TASKS_CLR[GPIOTE_TWIM_SPOOF_ACK]
   NRF_PPI-&amp;gt;CH[PPI_SPOOF_ACK_2_LOW].EEP  = (uint32_t)&amp;amp;pCounterAck-&amp;gt;EVENTS_COMPARE[2];
   NRF_PPI-&amp;gt;CH[PPI_SPOOF_ACK_2_LOW].TEP  = (uint32_t)&amp;amp;NRF_GPIOTE-&amp;gt;TASKS_CLR[GPIOTE_TWIM_SPOOF_ACK];
   // Configure PPI channel with connection between TIMER-&amp;gt;EVENTS_COMPARE[3] and GPIOTE-&amp;gt;TASKS_SET[GPIOTE_TWIM_SPOOF_ACK]
   NRF_PPI-&amp;gt;CH[PPI_SPOOF_ACK_2_HIGH].EEP = (uint32_t)&amp;amp;pCounterAck-&amp;gt;EVENTS_COMPARE[3];
   NRF_PPI-&amp;gt;CH[PPI_SPOOF_ACK_2_HIGH].TEP = (uint32_t)&amp;amp;NRF_GPIOTE-&amp;gt;TASKS_SET[GPIOTE_TWIM_SPOOF_ACK];
   pCounterAck-&amp;gt;TASKS_START =  1;
#endif

   // Enable PPI channels
   NRF_PPI-&amp;gt;CHENSET = (1UL &amp;lt;&amp;lt; PPI_STRETCH_TXSTART  ) |
                      (1UL &amp;lt;&amp;lt; PPI_STRETCH_RXSTART  ) |
                      (1UL &amp;lt;&amp;lt; PPI_STRETCH_STOP     ) |
                      (1UL &amp;lt;&amp;lt; PPI_STRETCH_LOW      ) |
                      (1UL &amp;lt;&amp;lt; PPI_STRETCH_HIGH     ) |
                      (1UL &amp;lt;&amp;lt; PPI_SPOOF_ACK_1_LOW  ) |
                      (1UL &amp;lt;&amp;lt; PPI_SPOOF_ACK_1_HIGH ) |
                      (1UL &amp;lt;&amp;lt; PPI_SPOOF_ACK_2_LOW  ) |
                      (1UL &amp;lt;&amp;lt; PPI_SPOOF_ACK_2_HIGH ) |
#if defined(FEATURE_COUNTER_FOR_SPOOF_ACK)
                      (1UL &amp;lt;&amp;lt; PPI_SCL_INPUT        ) |
#endif
                      (0);

   // Enable TWIM - note 6 not 5 for TWIM
   pTWIM-&amp;gt;ENABLE = 6;
}

void I2C_startTX(uint8_t addr)
{
   char InfoPacket[120] = &amp;quot;&amp;quot;;
   uint32_t Timeout = 200; // Some big number of uSecs
   pTWIM-&amp;gt;ADDRESS=addr;
   //snprintf(InfoPacket, sizeof(InfoPacket)-1, &amp;quot;I2C_startTX addr: 0x%x 0x%x\n&amp;quot;, &amp;amp;(pTWIM-&amp;gt;ADDRESS), addr);
   //uartSend(InfoPacket, strlen(InfoPacket));
   pTWIM-&amp;gt;TASKS_STARTTX = 1;
   __DSB();
   // Wait until the transfer start event is indicated
   while (pTWIM-&amp;gt;EVENTS_TXSTARTED == 0) ;
   // Wait until the transfer end event or error is indicated
#if defined(FEATURE_TX_ONLY_NO_RX)
   // Just wait for Tx, not starting a subsequent Rx
   while ((pTWIM-&amp;gt;EVENTS_LASTTX == 0) &amp;amp;&amp;amp; (pTWIM-&amp;gt;EVENTS_STOPPED == 0) &amp;amp;&amp;amp; (pTWIM-&amp;gt;EVENTS_ERROR == 0) &amp;amp;&amp;amp; (--Timeout &amp;gt; 0))
#else
   // Wait until the transfer receive start event is indicated
   while (pTWIM-&amp;gt;EVENTS_RXSTARTED == 0) ;
   // Wait for Rx response
   while ((pTWIM-&amp;gt;EVENTS_LASTRX == 0) &amp;amp;&amp;amp; (pTWIM-&amp;gt;EVENTS_STOPPED == 0) &amp;amp;&amp;amp; (pTWIM-&amp;gt;EVENTS_ERROR == 0) &amp;amp;&amp;amp; (--Timeout &amp;gt; 0))
#endif
   {
      __DSB();
      nrf_delay_us(50);
   }
   if ((pTWIM-&amp;gt;ERRORSRC) || (pTWIM-&amp;gt;EVENTS_ERROR == 1))
   {
      // Write &amp;#39;1&amp;#39; to clear Anak and Dnak
      pTWIM-&amp;gt;ERRORSRC = pTWIM-&amp;gt;ERRORSRC;
      pTWIM-&amp;gt;EVENTS_ERROR = 0;
      pTWIM-&amp;gt;TASKS_STOP   = 1;
      __DSB();
      while(pTWIM-&amp;gt;EVENTS_STOPPED == 0) ;
   }
   // Disable TWIM
   pTWIM-&amp;gt;ENABLE = 0;
}

void Test_I2C(void)
{
   I2C_init();
   I2C_startTX(LSM6DS3TR_ADDRESS | 0x01);
   while(1) ;
}
&lt;/pre&gt;&lt;/p&gt;&lt;div style="clear:both;"&gt;&lt;/div&gt;</description></item><item><title>RE: Errata 219 is incomplete or misleading. Unexpected behaviour at 100kHz. TWIM clock too short after clock stretch.</title><link>https://devzone.nordicsemi.com/thread/521442?ContentTypeID=1</link><pubDate>Wed, 05 Feb 2025 04:39:15 GMT</pubDate><guid isPermaLink="false">137ad170-7792-4731-bb38-c0d22fbe4515:a14ebab2-dccc-4b3b-bda1-ff389c0f2845</guid><dc:creator>hmolesworth</dc:creator><description>&lt;p&gt;Looks like the steps I outlined work by using a single timer and an output pin which provides a synchronous stretch. Not sure about the CC numbers, would have to test with the application.&lt;/p&gt;
&lt;p&gt;&lt;pre class="ui-code" data-mode="c_cpp"&gt;// Errata 219 - Proof of concept work-around
//
// Set a spare port pin to be H0D1 output
// Start a Timer at the same time as the TWIM events STARTED using PPI
// Set the Timer CC[0] register to trigger the spare port pin Low via PPI after a time equal to 9 x SCL
//  clock cycles, say 90 uSecs, after the 9th clock and coincident with a slave clock stretch if one is
//  in progress but before the next byte clock if no slave stretch is in progress
// Set the Timer CC[1] to clear the timer and set the port pin High (actually float as open-drain) via
//  PPI to end the synchronous stretch say after another 100uSec, ie at 190uSec
// Stop and Clear the Timer and set the port pin High (actually float as open-drain) on the TWIM events
//  STOPPED using PPI
// Tune this synchronous stretch to be longer than the longest slave stretch but without generating
//  the race-hazard pulse
#define WHO_AM_I          0x0F
#define I_AM_LSM6DS       0x6A
#define LSM6DS3TR_ADDRESS 0xD4

static uint8_t TxBuffer[] = {WHO_AM_I};
static volatile uint8_t RxBuffer[3];
static NRF_TWIM_Type * const pTWIM = NRF_TWIM1;
// nRF52833 DK
#define I2C_SCL_PIN      13
#define I2C_SDA_PIN      14
#define LED_RED_PIN      15
#define LED_GREEN_PIN    16
#define LED_BLUE_PIN     17
#define PIN_TWIM_STRETCH  3 // Connect externally to I2C_SCL_PIN

#define PPI_STRETCH_TXSTART  0 // Start pTimerStretch with TWIM Tx
#define PPI_STRETCH_RXSTART  1 // Start pTimerStretch with TWIM Rx
#define PPI_STRETCH_STOP     2 // Stop pTimerStretch after TWIM tx
#define PPI_STRETCH_LOW      3 // Drive synchronoud stretch output pin low
#define PPI_STRETCH_HIGH     4 // Float synchronoud stretch output pin

#define GPIOTE_TWIM_STRETCH  0 // GPIOTE to control synchronoud stretch output pin

// TIMER0 is used by the RADIO, so don&amp;#39;t use for now
static NRF_TIMER_Type * const pTimerStretch = NRF_TIMER1; // 4 x CC

void I2C_init(void)
{
   pTWIM-&amp;gt;ENABLE = 0;
   // Testing i2c twi twim, i2c pins both released
   NRF_P0-&amp;gt;OUT = (1 &amp;lt;&amp;lt; I2C_SCL_PIN) | (1 &amp;lt;&amp;lt; I2C_SDA_PIN);
   // Start with green led on
   NRF_P0-&amp;gt;OUT |= (1 &amp;lt;&amp;lt; LED_RED_PIN) | (0 &amp;lt;&amp;lt; LED_GREEN_PIN) | (1 &amp;lt;&amp;lt; LED_BLUE_PIN);
   // Configue port pins
   // Configuration                       Direction    Input            Pullup         Drive Level      Sense Level
   // ================================    ==========   ==============   ============   ==============   =============
   NRF_P0-&amp;gt;PIN_CNF[LED_RED_PIN]        = (PIN_OUTPUT | PIN_DISCONNECT | PIN_PULLUP   | PIN_DRIVE_H0D1 | PIN_SENSE_OFF);
   NRF_P0-&amp;gt;PIN_CNF[LED_GREEN_PIN]      = (PIN_OUTPUT | PIN_DISCONNECT | PIN_PULLUP   | PIN_DRIVE_H0D1 | PIN_SENSE_OFF);
   NRF_P0-&amp;gt;PIN_CNF[LED_BLUE_PIN]       = (PIN_OUTPUT | PIN_DISCONNECT | PIN_PULLUP   | PIN_DRIVE_H0D1 | PIN_SENSE_OFF);
   NRF_P0-&amp;gt;PIN_CNF[I2C_SCL_PIN]        = (PIN_INPUT  | PIN_CONNECT    | PIN_PULLUP   | PIN_DRIVE_H0D1 | PIN_SENSE_OFF);
   NRF_P0-&amp;gt;PIN_CNF[I2C_SDA_PIN]        = (PIN_INPUT  | PIN_CONNECT    | PIN_PULLUP   | PIN_DRIVE_H0D1 | PIN_SENSE_OFF);
   NRF_P0-&amp;gt;PIN_CNF[PIN_TWIM_STRETCH]   = (PIN_OUTPUT | PIN_DISCONNECT | PIN_PULLUP   | PIN_DRIVE_H0D1 | PIN_SENSE_OFF);
   // Set up i2c peripheral
   pTWIM-&amp;gt;PSEL.SCL=I2C_SCL_PIN;
   pTWIM-&amp;gt;PSEL.SDA=I2C_SDA_PIN;
   pTWIM-&amp;gt;FREQUENCY = TWI_FREQUENCY_FREQUENCY_K100; //100 KHz
   pTWIM-&amp;gt;SHORTS = TWIM_SHORTS_LASTTX_STOP_Msk;
   // Transmit data
   pTWIM-&amp;gt;TXD.MAXCNT = sizeof(TxBuffer);
   pTWIM-&amp;gt;TXD.PTR = (uint32_t)&amp;amp;TxBuffer[0];
   // 1-255 bytes receive data
   pTWIM-&amp;gt;RXD.MAXCNT = 0;
   pTWIM-&amp;gt;RXD.PTR = (uint32_t)&amp;amp;RxBuffer[0];
   // Clear all events
   pTWIM-&amp;gt;EVENTS_ERROR     = 0;
   pTWIM-&amp;gt;EVENTS_SUSPENDED = 0;
   pTWIM-&amp;gt;EVENTS_TXSTARTED = 0;
   pTWIM-&amp;gt;EVENTS_RXSTARTED = 0;
   pTWIM-&amp;gt;EVENTS_STOPPED   = 0;
   pTWIM-&amp;gt;EVENTS_LASTRX    = 0;
   pTWIM-&amp;gt;EVENTS_LASTTX    = 0;
   // Disable all interrupts
   pTWIM-&amp;gt;INTENCLR = 0xFFFFFFFFUL;
   __DSB();

   // Configure GPIOTE-&amp;gt;TASKS_OUT[GPIOTE_TWIM_STRETCH] to control PIN_TWIM_STRETCH
   NRF_GPIOTE-&amp;gt;CONFIG[GPIOTE_TWIM_STRETCH] = (GPIOTE_CONFIG_MODE_Task       &amp;lt;&amp;lt; GPIOTE_CONFIG_MODE_Pos)     |
                                             (GPIOTE_CONFIG_OUTINIT_High    &amp;lt;&amp;lt; GPIOTE_CONFIG_OUTINIT_Pos)  |
                                             (GPIOTE_CONFIG_POLARITY_LoToHi &amp;lt;&amp;lt; GPIOTE_CONFIG_POLARITY_Pos) |
                                             (PIN_TWIM_STRETCH              &amp;lt;&amp;lt; GPIOTE_CONFIG_PSEL_Pos);
   // TWIM Stretch: Configure pTimerStretch as Timer to generate EVENTS_COMPARE[0] at 90uSecs, EVENTS_COMPARE[1] at 190uSecs
   pTimerStretch-&amp;gt;MODE = TIMER_MODE_MODE_Timer &amp;lt;&amp;lt; TIMER_MODE_MODE_Pos;
   pTimerStretch-&amp;gt;BITMODE = TIMER_BITMODE_BITMODE_16Bit &amp;lt;&amp;lt; TIMER_BITMODE_BITMODE_Pos;
   pTimerStretch-&amp;gt;SHORTS = TIMER_SHORTS_COMPARE1_CLEAR_Enabled &amp;lt;&amp;lt; TIMER_SHORTS_COMPARE1_CLEAR_Pos;
   pTimerStretch-&amp;gt;PRESCALER   =  0;  // 16MHz clock
   pTimerStretch-&amp;gt;CC[0]       = 16*(12*10);  // 16MHz* 90  =  90uSecs Start synch stretch
   pTimerStretch-&amp;gt;CC[1]       = 16*(22*10);  // 16MHz*190 = 190uSecs  End synch stretch, clear timer to restart
   //pTimerStretch-&amp;gt;TASKS_START =  1;
   // Configure PPI channel with connection between TIMER-&amp;gt;EVENTS_COMPARE[0] and GPIOTE-&amp;gt;TASKS_CLR[GPIOTE_TWIM_STRETCH]
   NRF_PPI-&amp;gt;CH[PPI_STRETCH_LOW].EEP  = (uint32_t)&amp;amp;pTimerStretch-&amp;gt;EVENTS_COMPARE[0];
   NRF_PPI-&amp;gt;CH[PPI_STRETCH_LOW].TEP  = (uint32_t)&amp;amp;NRF_GPIOTE-&amp;gt;TASKS_CLR[GPIOTE_TWIM_STRETCH];
   // Configure PPI channel with connection between TIMER-&amp;gt;EVENTS_COMPARE[1] and GPIOTE-&amp;gt;TASKS_SET[GPIOTE_TWIM_STRETCH]
   NRF_PPI-&amp;gt;CH[PPI_STRETCH_HIGH].EEP = (uint32_t)&amp;amp;pTimerStretch-&amp;gt;EVENTS_COMPARE[1];
   NRF_PPI-&amp;gt;CH[PPI_STRETCH_HIGH].TEP = (uint32_t)&amp;amp;NRF_GPIOTE-&amp;gt;TASKS_SET[GPIOTE_TWIM_STRETCH];
   // Configure PPI channel with connection between TWIM-&amp;gt;EVENTS_TXSTARTED and TIMER-&amp;gt;TASKS_START
   NRF_PPI-&amp;gt;CH[PPI_STRETCH_TXSTART].EEP  = (uint32_t)&amp;amp;pTWIM-&amp;gt;EVENTS_TXSTARTED;
   NRF_PPI-&amp;gt;CH[PPI_STRETCH_TXSTART].TEP  = (uint32_t)&amp;amp;pTimerStretch-&amp;gt;TASKS_START;
   // Configure PPI channel with connection between TWIM-&amp;gt;EVENTS_RXSTARTED and TIMER-&amp;gt;TASKS_START
   NRF_PPI-&amp;gt;CH[PPI_STRETCH_RXSTART].EEP  = (uint32_t)&amp;amp;pTWIM-&amp;gt;EVENTS_RXSTARTED;
   NRF_PPI-&amp;gt;CH[PPI_STRETCH_RXSTART].TEP  = (uint32_t)&amp;amp;pTimerStretch-&amp;gt;TASKS_START;
   // Configure PPI channel with connection between TWIM-&amp;gt;EVENTS_STOPPED and TIMER-&amp;gt;TASKS_STOP
   NRF_PPI-&amp;gt;CH[PPI_STRETCH_STOP].EEP = (uint32_t)&amp;amp;pTWIM-&amp;gt;EVENTS_STOPPED;
   NRF_PPI-&amp;gt;CH[PPI_STRETCH_STOP].TEP = (uint32_t)&amp;amp;pTimerStretch-&amp;gt;TASKS_STOP;
   NRF_PPI-&amp;gt;FORK[PPI_STRETCH_STOP].TEP = (uint32_t)&amp;amp;NRF_GPIOTE-&amp;gt;TASKS_OUT[GPIOTE_TWIM_STRETCH];

   // Enable PPI channels
   NRF_PPI-&amp;gt;CHENSET = (1UL &amp;lt;&amp;lt; PPI_STRETCH_TXSTART) |
                      (1UL &amp;lt;&amp;lt; PPI_STRETCH_RXSTART) |
                      (1UL &amp;lt;&amp;lt; PPI_STRETCH_STOP   ) |
                      (1UL &amp;lt;&amp;lt; PPI_STRETCH_LOW    ) |
                      (1UL &amp;lt;&amp;lt; PPI_STRETCH_HIGH   );

   // Enable TWIM - note 6 not 5 for TWIM
   pTWIM-&amp;gt;ENABLE = 6;
}
void I2C_startTX(uint8_t addr)
{
   char InfoPacket[120] = &amp;quot;&amp;quot;;
   uint32_t Timeout = 2000; // Some big number of uSecs
   pTWIM-&amp;gt;ADDRESS=addr;
   snprintf(InfoPacket, sizeof(InfoPacket)-1, &amp;quot;I2C_startTX addr: 0x%x 0x%x\n&amp;quot;, &amp;amp;(pTWIM-&amp;gt;ADDRESS), addr);
   uartSend(InfoPacket, strlen(InfoPacket));
   pTWIM-&amp;gt;TASKS_STARTTX = 1;
   __DSB();
   // Wait until the transfer start event is indicated
   while (pTWIM-&amp;gt;EVENTS_TXSTARTED == 0) ;
   // Wait until the transfer end event or error is indicated
   while ((pTWIM-&amp;gt;EVENTS_LASTTX == 0) &amp;amp;&amp;amp; (pTWIM-&amp;gt;EVENTS_STOPPED == 0) &amp;amp;&amp;amp; (pTWIM-&amp;gt;EVENTS_ERROR == 0) &amp;amp;&amp;amp; (--Timeout &amp;gt; 0))
   {
      __DSB();
      nrf_delay_us(500);
   }
   if ((pTWIM-&amp;gt;ERRORSRC) || (pTWIM-&amp;gt;EVENTS_ERROR == 1))
   {
      pTWIM-&amp;gt;TASKS_STOP = 1;
      pTWIM-&amp;gt;ERRORSRC = pTWIM-&amp;gt;ERRORSRC;
      while(pTWIM-&amp;gt;EVENTS_STOPPED == 0) ;
   }
   // Disable TWIM
   pTWIM-&amp;gt;ENABLE = 0;
}

void Test_I2C(void)
{
   I2C_init();
   I2C_startTX(LSM6DS3TR_ADDRESS | 0x01);
   while(1) ;
}&lt;/pre&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Set a spare port pin to be H0D1 output&lt;/li&gt;
&lt;li&gt;Start a Timer at the same time as the TWIM events STARTED using PPI&lt;/li&gt;
&lt;li&gt;Set the Timer CC[0] register to trigger the spare port pin Low via PPI after a time&amp;nbsp;equal to 9 x SCL clock cycles, say 90 uSecs, after the 9th clock and coincident with a slave clock stretch if one is in progress&amp;nbsp;but before the next byte clock if no slave stretch is in progress&lt;/li&gt;
&lt;li&gt;Set the Timer CC[1] to clear the timer and set the port pin High (actually float as open-drain) via PPI to end the synchronous stretch say after another 100uSec, ie at 190uSec&lt;/li&gt;
&lt;li&gt;Stop and Clear the Timer and&amp;nbsp;&lt;span&gt;set the port pin High (actually float as open-drain)&amp;nbsp;&lt;/span&gt;on the TWIM events STOPPED using PPI&lt;/li&gt;
&lt;li&gt;Tune this&amp;nbsp;&lt;span&gt;synchronous stretch to be longer than the longest slave stretch but without generating the race-hazard pulse&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;span&gt;Seems to work; the&amp;nbsp;new output pin should be connected to the SCL to extend the stretch initiated by the TI slave by a (safe) synchronous amount, or if no added stretch from the slave it will add a spurious (but safe) stretch. The clocks for both TWIM and Timer are synchronous from 16MHz regardless of whether using the crystal. The synchronous stretch is added for every&amp;nbsp;byte regardless of whether requested by the slave.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;Edit: Added missing FORK, fixed a typo, playing with timing&lt;/span&gt;&lt;/p&gt;&lt;div style="clear:both;"&gt;&lt;/div&gt;</description></item><item><title>RE: Errata 219 is incomplete or misleading. Unexpected behaviour at 100kHz. TWIM clock too short after clock stretch.</title><link>https://devzone.nordicsemi.com/thread/521430?ContentTypeID=1</link><pubDate>Wed, 05 Feb 2025 01:51:32 GMT</pubDate><guid isPermaLink="false">137ad170-7792-4731-bb38-c0d22fbe4515:99049769-57b0-412e-8778-df8c39ae370f</guid><dc:creator>hmolesworth</dc:creator><description>&lt;p&gt;Uh-oh, that last trace shows more clearly that the TI slave is clock-stretching before the ACK/NAK clock pulse, presumably so that it can decide whether to Ack or Nak the data byte. SUSPEND activates after the ACK/NAK clock cycle, so that solution won&amp;#39;t work - pity.&lt;/p&gt;
&lt;p&gt;Proposal:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Set a spare port pin to be H0D1 output&lt;/li&gt;
&lt;li&gt;Start a Timer at the same time as the TWIM events STARTED using PPI&lt;/li&gt;
&lt;li&gt;Set the Timer CC[0] register to trigger the spare port pin Low via PPI after a time&amp;nbsp;equal to 9 x SCL clock cycles, say 90 uSecs, after the 9th clock and coincident with a slave clock stretch if one is in progress&amp;nbsp;but before the next byte clock if no slave stretch is in progress&lt;/li&gt;
&lt;li&gt;Set the Timer CC[1] to clear the timer and set the port pin High (actually float as open-drain) via PPI to end the synchronous stretch say after another 100uSec, ie at 190uSec&lt;/li&gt;
&lt;li&gt;Stop and Clear the Timer and&amp;nbsp;&lt;span&gt;set the port pin High (actually float as open-drain)&amp;nbsp;&lt;/span&gt;on the TWIM events STOPPED using PPI&lt;/li&gt;
&lt;li&gt;Tune this&amp;nbsp;&lt;span&gt;synchronous stretch to be longer than the longest slave stretch but without generating the race-hazard pulse&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;span&gt;I think this should work; the spare output pin should be connected to the SCL to extend the stretch initiated by the TI slave by a (safe) synchronous amount, or if no added stretch from the slave it will add a spurious (but safe) stretch. The clocks for both TWIM and Timer are synchronous from 16MHz regardless of whether using the crystal. The synchronous stretch is added for every&amp;nbsp;byte regardless of whether requested by the slave, but who cares.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;Edits in progress; I may even test this ..&lt;/span&gt;&lt;/p&gt;&lt;div style="clear:both;"&gt;&lt;/div&gt;</description></item><item><title>RE: Errata 219 is incomplete or misleading. Unexpected behaviour at 100kHz. TWIM clock too short after clock stretch.</title><link>https://devzone.nordicsemi.com/thread/521413?ContentTypeID=1</link><pubDate>Tue, 04 Feb 2025 20:09:44 GMT</pubDate><guid isPermaLink="false">137ad170-7792-4731-bb38-c0d22fbe4515:9ced371b-0488-49c7-83ea-68e7b20a5469</guid><dc:creator>VictorOh</dc:creator><description>&lt;p&gt;So I took some more measurements and found that there extra pulse is present:&lt;/p&gt;
&lt;p&gt;&lt;img style="max-height:240px;max-width:320px;" src="https://devzone.nordicsemi.com/resized-image/__size/640x480/__key/communityserver-discussions-components-files/4/UBLOX_5F00_NACK_5F00_BUSY.png" alt=" " /&gt;&lt;/p&gt;
&lt;p&gt;Will try out the workarounds and see if it improves the reliability of the communication.&lt;/p&gt;&lt;div style="clear:both;"&gt;&lt;/div&gt;</description></item><item><title>RE: Errata 219 is incomplete or misleading. Unexpected behaviour at 100kHz. TWIM clock too short after clock stretch.</title><link>https://devzone.nordicsemi.com/thread/521386?ContentTypeID=1</link><pubDate>Tue, 04 Feb 2025 16:55:19 GMT</pubDate><guid isPermaLink="false">137ad170-7792-4731-bb38-c0d22fbe4515:aebb5678-619a-41dc-a96f-dc2e815b50ba</guid><dc:creator>hmolesworth</dc:creator><description>&lt;p&gt;Looks to me like there are 3 clock stretches here, 1st 90uSec ok, 2nd 60uSec ok, 3rd 80uSec has short clock pulse. Using just the timer to hold off transmission for 100uSec would mean the short clock pulse would not occur, it would stay high for the duration of SUSPEND, so SUSPEND on every clock edge (some of these SUSPENDs are of course redundant but I think benign) and also clear and start the Timer on each clock pulse, then the timer issues the RESUME and timer stop after 100uSec (or maybe 200 uSec).&lt;/p&gt;
&lt;p&gt;What can go wrong? Well the assumption is that SUSPEND really does suspend so the SCL doesn&amp;#39;t get pushed low after that short rising edge, and that&amp;#39;s a question for Nordic hardware guys.&lt;/p&gt;
&lt;p&gt;16MHz clock with SCL at 100kHz is 160 16M clocks per clock; I suspect there is a race hazard internally that shows up if the external asynchronous event - the end of the hold-SCL-low clock stretch - is not an integer multiple (or close to) of 160 16M clocks. Funnily enough the one that fails is the one that is an integer multiple.&lt;/p&gt;
&lt;p&gt;Will this work? Best I can think of without hardware design changes ..&lt;/p&gt;
&lt;p&gt;Edit: Maybe you could look at the numbers more accurately than I can for the discussion on the race hazard, no help for us but would allow Nordic to fix the issue in the next die revision :-)&lt;/p&gt;
&lt;p&gt;Edit2: Worst case if this doesn&amp;#39;t work try having a spare open-drain output pin H0D1 to be driven low every 8 clock edges to force a clean known-width (using 100uSec timer) stretch after every byte.&lt;/p&gt;&lt;div style="clear:both;"&gt;&lt;/div&gt;</description></item><item><title>RE: Errata 219 is incomplete or misleading. Unexpected behaviour at 100kHz. TWIM clock too short after clock stretch.</title><link>https://devzone.nordicsemi.com/thread/521378?ContentTypeID=1</link><pubDate>Tue, 04 Feb 2025 16:23:50 GMT</pubDate><guid isPermaLink="false">137ad170-7792-4731-bb38-c0d22fbe4515:5f288617-334a-4d47-99d0-00573dcd229b</guid><dc:creator>VictorOh</dc:creator><description>&lt;p&gt;&lt;img class="align-left" style="float:left;max-height:240px;max-width:320px;" alt=" " src="https://devzone.nordicsemi.com/resized-image/__size/640x480/__key/communityserver-discussions-components-files/4/UBLOX_5F00_UNSEAL_5F00_0_5F00_BAD_5F00_0.png" /&gt;&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p&gt;So I am having this issue where after a NACK (its clock pulse seems awfully short) and the SDA line does not lift. I want to make sure this is the same issue before proceeding to test these workarounds.&lt;/p&gt;&lt;div style="clear:both;"&gt;&lt;/div&gt;</description></item><item><title>RE: Errata 219 is incomplete or misleading. Unexpected behaviour at 100kHz. TWIM clock too short after clock stretch.</title><link>https://devzone.nordicsemi.com/thread/521280?ContentTypeID=1</link><pubDate>Tue, 04 Feb 2025 11:21:05 GMT</pubDate><guid isPermaLink="false">137ad170-7792-4731-bb38-c0d22fbe4515:6053bb56-9847-4635-bd48-0f77a1b5525c</guid><dc:creator>hmolesworth</dc:creator><description>&lt;p&gt;I did something similar here using an external pin input for SPIM XON-XOFF, but in this case the TWIM RESUME would be triggered by the timer not a clock edge. The counter would not be required if just accept multiple TWIM TASKS_SUSPEND which should be ok, then just the timer would be needed for the hold-off time until the slave stops holding SCL low.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://devzone.nordicsemi.com/f/nordic-q-a/117144/how-to-do-spim-with-handshakes/517554"&gt;how-to-do-spim-with-handshakes&lt;/a&gt;&lt;/p&gt;&lt;div style="clear:both;"&gt;&lt;/div&gt;</description></item><item><title>RE: Errata 219 is incomplete or misleading. Unexpected behaviour at 100kHz. TWIM clock too short after clock stretch.</title><link>https://devzone.nordicsemi.com/thread/521197?ContentTypeID=1</link><pubDate>Mon, 03 Feb 2025 23:34:59 GMT</pubDate><guid isPermaLink="false">137ad170-7792-4731-bb38-c0d22fbe4515:388040d6-a0e0-45a4-bd65-967d5b80b818</guid><dc:creator>hmolesworth</dc:creator><description>&lt;p&gt;Another option is to simply avoid the stretch by predicting in advance which commands involve the slave issuing a clock stretch and for those commands issue a TASKS_SUSPEND followed by a TASKS_RESUME; the SUSPEND triggered by a counter counting SCK cycles and the RESUME by a timer for a fixed period after the SUSPEND. That way the TWIM peripheral would be unaware of the clock stretch.&lt;/p&gt;
&lt;p&gt;Edit: Simpler still just SUSPEND/RESUME for every byte, slows down transfer but safe fix. Trigger a counter on SCL edges with (say) TWIM TASKS_SUSPEND at counter CC=3 and a timer with TASKS_START at say counter CC=4 (or 5,6,7,8 ..) On required timer time (maybe 2 bytes equivalent at 100kHz) trigger counter TASKS_CLEAR and TWIM TASKS_RESUME and timer TASKS_STOP. May be able to use the SCL pin as event as I think only GPIOTE tasks steal the pin for exclusive use, but if not simply use a spare input pin connected externally to SCL; it has to be GPIOTE feeding the PPI events as there is no SPIM event generated on every byte of a multi-byte transfer..&lt;/p&gt;&lt;div style="clear:both;"&gt;&lt;/div&gt;</description></item><item><title>RE: Errata 219 is incomplete or misleading. Unexpected behaviour at 100kHz. TWIM clock too short after clock stretch.</title><link>https://devzone.nordicsemi.com/thread/521196?ContentTypeID=1</link><pubDate>Mon, 03 Feb 2025 22:31:12 GMT</pubDate><guid isPermaLink="false">137ad170-7792-4731-bb38-c0d22fbe4515:9b1b830c-26dc-417b-b0b9-dadeb1a15ae6</guid><dc:creator>hmolesworth</dc:creator><description>&lt;p&gt;Yes but such a capacitor would hammer every clock pulse .. not pretty but workable; cleaner with a schmidt-input buffer to the external slave.&lt;/p&gt;
&lt;p&gt;Edit: Assuming the clock stretch only occurs after the 9th clock (ack/nak) the capacitor could be connected from the SCK pin to another spare output pin. That latter pin could be set as H0D1 and driven low (connecting the cpacitor to GND) by a counter with PPI/GPIOTE every 9 SCK clock pulses and driven open-drain (floating) on the next clock pulse. That way only the first clock pulse in each 9-cycle burst would be capacitively loaded.&lt;/p&gt;&lt;div style="clear:both;"&gt;&lt;/div&gt;</description></item><item><title>RE: Errata 219 is incomplete or misleading. Unexpected behaviour at 100kHz. TWIM clock too short after clock stretch.</title><link>https://devzone.nordicsemi.com/thread/521195?ContentTypeID=1</link><pubDate>Mon, 03 Feb 2025 22:25:50 GMT</pubDate><guid isPermaLink="false">137ad170-7792-4731-bb38-c0d22fbe4515:f1e4a65b-e4d9-485b-a134-46cbc9d32851</guid><dc:creator>VictorOh</dc:creator><description>&lt;p&gt;So, say, could a well selected capacitor might filter out that short pulse and not affect the regular pulses?&lt;/p&gt;&lt;div style="clear:both;"&gt;&lt;/div&gt;</description></item><item><title>RE: Errata 219 is incomplete or misleading. Unexpected behaviour at 100kHz. TWIM clock too short after clock stretch.</title><link>https://devzone.nordicsemi.com/thread/521193?ContentTypeID=1</link><pubDate>Mon, 03 Feb 2025 22:21:53 GMT</pubDate><guid isPermaLink="false">137ad170-7792-4731-bb38-c0d22fbe4515:0d62c6b5-08e8-4a5f-82d5-a1f64dd12fbe</guid><dc:creator>hmolesworth</dc:creator><description>&lt;p&gt;Looking at the trace supplied above, it&amp;nbsp;appears that rather than a shortened clock pulse following the stretch what is happening is that a phantom (variable-width short) clock pulse is appearing since counting this shortened pulse there are 10 clock pulses not the expected 9 clock pulses.&lt;/p&gt;
&lt;p&gt;Assuming for the moment that this is indeed happening, an auxiliary clock could be spoofed by filtering out any pulse less than (say) 1.5uSecs providing a clean clock to the external device. At 100kHz or less it may be possible to set the filter such that the now-delayed clock to the slave does not push out the SDA return beyond the sample point. Using external hardware gating the unwanted short pulse can be removed with little delay on the spoofed clock so fewer worries about sampling point timing.&lt;/p&gt;
&lt;p&gt;Spoofing such a clock may be possible without external gates using just the internal PPI/GPIOTE with a timer for the output spoofed clock which resets the count on a short clock pulse and therefore does not trigger that short clock pulse bur&amp;nbsp;triggers just the normal-width clock pulses. The width of these pulses is controlled by a 2nd timer, no software or interrupts required.&lt;/p&gt;&lt;div style="clear:both;"&gt;&lt;/div&gt;</description></item><item><title>RE: Errata 219 is incomplete or misleading. Unexpected behaviour at 100kHz. TWIM clock too short after clock stretch.</title><link>https://devzone.nordicsemi.com/thread/521190?ContentTypeID=1</link><pubDate>Mon, 03 Feb 2025 20:51:01 GMT</pubDate><guid isPermaLink="false">137ad170-7792-4731-bb38-c0d22fbe4515:e01ed0e9-1b3f-4d7e-bd92-091a546e54b4</guid><dc:creator>VictorOh</dc:creator><description>&lt;p&gt;Are there any ways to make this clock stretching on the TWIM peripheral to be compliant with the I2C specification?&lt;/p&gt;&lt;div style="clear:both;"&gt;&lt;/div&gt;</description></item><item><title>RE: Errata 219 is incomplete or misleading. Unexpected behaviour at 100kHz. TWIM clock too short after clock stretch.</title><link>https://devzone.nordicsemi.com/thread/511330?ContentTypeID=1</link><pubDate>Wed, 20 Nov 2024 14:13:15 GMT</pubDate><guid isPermaLink="false">137ad170-7792-4731-bb38-c0d22fbe4515:e150da90-de8c-4c1c-9871-12e05e785330</guid><dc:creator>Edvin</dc:creator><description>&lt;p&gt;Edit: It is not present on the nRF54L series. That is a documentation bug. But it is present on the nRF52 series and nRF53 series.&lt;/p&gt;
&lt;p&gt;Best regards,&lt;/p&gt;
&lt;p&gt;Edvin&lt;/p&gt;&lt;div style="clear:both;"&gt;&lt;/div&gt;</description></item><item><title>RE: Errata 219 is incomplete or misleading. Unexpected behaviour at 100kHz. TWIM clock too short after clock stretch.</title><link>https://devzone.nordicsemi.com/thread/511320?ContentTypeID=1</link><pubDate>Wed, 20 Nov 2024 13:54:36 GMT</pubDate><guid isPermaLink="false">137ad170-7792-4731-bb38-c0d22fbe4515:86252f7b-b0e1-44d0-83dc-e8ac538f92a6</guid><dc:creator>Edvin</dc:creator><description>&lt;p&gt;Hello Tom!&lt;/p&gt;
&lt;p&gt;Sorry for the delay. I was able to nest it up by talking to someone from our HW team, who actually pointed me towards the product specification:&lt;/p&gt;
&lt;p&gt;&lt;a href="https://docs.nordicsemi.com/bundle/ps_nrf5340/page/twim.html"&gt;https://docs.nordicsemi.com/bundle/ps_nrf5340/page/twim.html&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;As it says:&lt;/p&gt;
&lt;p&gt;&amp;quot;&lt;em&gt;TWIM supports clock stretching performed by the slaves. The SCK pulse following a stretched clock cycle may be shorter than specified by the I&lt;sup&gt;2&lt;/sup&gt;C specification.&lt;/em&gt;&amp;quot;&lt;/p&gt;
&lt;p&gt;So it is a known issue, and it occurs regardless of the baud rate, unfortunately.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;FYI, it is also present on the &lt;span style="text-decoration:line-through;"&gt;&lt;a href="https://docs.nordicsemi.com/bundle/ps_nrf54L15/page/twim.html"&gt;nRF54L15&lt;/a&gt;&lt;/span&gt;, as well as the &lt;a href="https://docs.nordicsemi.com/bundle/ps_nrf52840/page/twim.html"&gt;nRF52840&lt;/a&gt;&amp;nbsp;(and the rest of the nRF52 series. It is the same SPIM peripheral).&amp;nbsp;&lt;/p&gt;
&lt;p&gt;I was not aware until now, but this means that it is not compliant for clock stretching, according to the I2C standard. I am sorry.&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p&gt;Best regards,&lt;/p&gt;
&lt;p&gt;Edvin&lt;/p&gt;&lt;div style="clear:both;"&gt;&lt;/div&gt;</description></item><item><title>RE: Errata 219 is incomplete or misleading. Unexpected behaviour at 100kHz. TWIM clock too short after clock stretch.</title><link>https://devzone.nordicsemi.com/thread/511266?ContentTypeID=1</link><pubDate>Wed, 20 Nov 2024 11:30:50 GMT</pubDate><guid isPermaLink="false">137ad170-7792-4731-bb38-c0d22fbe4515:57f69c9e-cae6-498d-b342-fcede847f7cd</guid><dc:creator>SST</dc:creator><description>&lt;p&gt;Hi Edvin, Any news?&lt;/p&gt;
&lt;p&gt;Thanks,&lt;/p&gt;
&lt;p&gt;Tom&lt;/p&gt;&lt;div style="clear:both;"&gt;&lt;/div&gt;</description></item><item><title>RE: Errata 219 is incomplete or misleading. Unexpected behaviour at 100kHz. TWIM clock too short after clock stretch.</title><link>https://devzone.nordicsemi.com/thread/510149?ContentTypeID=1</link><pubDate>Tue, 12 Nov 2024 14:56:51 GMT</pubDate><guid isPermaLink="false">137ad170-7792-4731-bb38-c0d22fbe4515:f4c60fef-4268-4cc2-a1ca-0d9afe9f6386</guid><dc:creator>Edvin</dc:creator><description>&lt;p&gt;Hello,&lt;/p&gt;
&lt;p&gt;Sorry for the late reply. I am looking around internally to see if I can find someone who can answer this in more detail than I can. I am sorry for the inconvenience. I will get back to you as soon as I know more. Hopefully tomorrow.&lt;/p&gt;
&lt;p&gt;Best regards,&lt;/p&gt;
&lt;p&gt;Edvin&lt;/p&gt;&lt;div style="clear:both;"&gt;&lt;/div&gt;</description></item></channel></rss>