Atmel SAMD10 i2c master issues - Page 1
Forum section: electronics / general

author: muxx
author_rank: Contributor
timestamp: October 27, 2016, 03:39:15 pm
content: guys,I have been implementing various bits to use SAMD10 without ASF. thanks to westfw for the reference, that helped me with UART and clocks. I also implemented (rather crudely ATM) external interrupts and a GPIO library.now I am struggling with the I2C master. I am using standard speed and "smart mode", polling for interrupts. acknowledging address and transmitting data seems to work, but reading fails in a very bizarre way: as soon as I set up the read transaction and the address gets acknowledged, the host continuously generates clocks, and the slave obliges.setting SERCOM_I2CM_CTRLB_ACKACT to high or CTRLB.bit.CMD to STOP doesn't seem to do anything, at least timely. when I look at the signals the host generates a few dosens ACKs and then NAKs, but no STOP in sight.the code is like this:/* we got the SERCOM_I2CM_INTFLAG_SB already, SERCOM_I2CM_CTRLB_ACKACT is cleared and the first byte of received data is already in DATA */while (rxc--){if (rxc == 0){c->CTRLB.reg |= SERCOM_I2CM_CTRLB_ACKACT | SERCOM_I2CM_CTRLB_CMD(3);SYNCBUSY(c);}*rxbuf++ = c->DATA.reg;SYNCBUSY(c);if (!waitint(SERCOM_I2CM_INTFLAG_SB)) { /* error */ };}now, the loop terminates, and the buffer contains at least one byte of correct data, but the master clock generation continues despite me issuing the STOP command.I checked and rechecked the datasheet and found nothing. any ideas? haven't found any chip erratas either.thanks in advance.

author: muxx
author_rank: Contributor
timestamp: October 27, 2016, 04:52:40 pm
content: found the answer by not-so-educated guessing. don't enable SERCOM_I2CM_CTRLA_SCLSM!how that would continue generating clocks without any DATA register reading is beyond me.

author: ataradov
author_rank: Super Contributor
timestamp: October 27, 2016, 10:43:06 pm
content: Just for reference, here is my typical starter code for I2C master.Code:void i2c_init(void){HAL_GPIO_SDA_out();HAL_GPIO_SDA_pmuxen(I2C_SERCOM_PMUX);HAL_GPIO_SCL_out();HAL_GPIO_SCL_pmuxen(I2C_SERCOM_PMUX);PM->APBCMASK.reg |= I2C_SERCOM_APBCMASK;GCLK->CLKCTRL.reg = GCLK_CLKCTRL_ID(I2C_SERCOM_GCLK_ID) |GCLK_CLKCTRL_CLKEN | GCLK_CLKCTRL_GEN(I2C_SERCOM_CLK_GEN);I2C_SERCOM->I2CM.CTRLB.reg = SERCOM_I2CM_CTRLB_SMEN;while (I2C_SERCOM->I2CM.SYNCBUSY.reg);I2C_SERCOM->I2CM.BAUD.reg = SERCOM_I2CM_BAUD_BAUD(48);while (I2C_SERCOM->I2CM.SYNCBUSY.reg);I2C_SERCOM->I2CM.CTRLA.reg = SERCOM_I2CM_CTRLA_ENABLE |SERCOM_I2CM_CTRLA_MODE_I2C_MASTER |SERCOM_I2CM_CTRLA_SDAHOLD(3);while (I2C_SERCOM->I2CM.SYNCBUSY.reg);I2C_SERCOM->I2CM.STATUS.reg |= SERCOM_I2CM_STATUS_BUSSTATE(1);while (I2C_SERCOM->I2CM.SYNCBUSY.reg);}bool i2c_write(uint8_t *data, int size){I2C_SERCOM->I2CM.ADDR.reg = I2C_ADDRESS | I2C_TRANSFER_WRITE;while (0 == (I2C_SERCOM->I2CM.INTFLAG.reg & SERCOM_I2CM_INTFLAG_MB));if (I2C_SERCOM->I2CM.STATUS.reg & SERCOM_I2CM_STATUS_RXNACK){I2C_SERCOM->I2CM.CTRLB.reg |= SERCOM_I2CM_CTRLB_CMD(3);return false;}for (int i = 0; i < size; i++){I2C_SERCOM->I2CM.DATA.reg = data;while (0 == (I2C_SERCOM->I2CM.INTFLAG.reg & SERCOM_I2CM_INTFLAG_MB));if (I2C_SERCOM->I2CM.STATUS.reg & SERCOM_I2CM_STATUS_RXNACK){I2C_SERCOM->I2CM.CTRLB.reg |= SERCOM_I2CM_CTRLB_CMD(3);return false;}}I2C_SERCOM->I2CM.CTRLB.reg |= SERCOM_I2CM_CTRLB_CMD(3);return true;}bool i2c_read(uint8_t *data, int size){I2C_SERCOM->I2CM.ADDR.reg = I2C_ADDRESS | I2C_TRANSFER_READ;while (0 == (I2C_SERCOM->I2CM.INTFLAG.reg & SERCOM_I2CM_INTFLAG_SB));if (I2C_SERCOM->I2CM.STATUS.reg & SERCOM_I2CM_STATUS_RXNACK){I2C_SERCOM->I2CM.CTRLB.reg |= SERCOM_I2CM_CTRLB_CMD(3);return false;}I2C_SERCOM->I2CM.CTRLB.reg &= ~SERCOM_I2CM_CTRLB_ACKACT;for (int i = 0; i < size-1; i++){data = I2C_SERCOM->I2CM.DATA.reg;while (0 == (I2C_SERCOM->I2CM.INTFLAG.reg & SERCOM_I2CM_INTFLAG_SB));}if (size){I2C_SERCOM->I2CM.CTRLB.reg |= SERCOM_I2CM_CTRLB_ACKACT;I2C_SERCOM->I2CM.CTRLB.reg |= SERCOM_I2CM_CTRLB_CMD(3);data[size-1] = I2C_SERCOM->I2CM.DATA.reg;}return true;}

author: muxx
author_rank: Contributor
timestamp: October 28, 2016, 10:14:12 am
content: thanks Alex. I also wrote code that does TX, RX, TX+RX, seems to work reliably now.I think in your code you need to wait for SYNCBUSY after issuing the STOP command, essentially doing it synchronously. in other cases, like writing ADDR also requires synchronisation, but I assume polling for the interrupt achieves the same effect.I can see that you are not using the SERCOM2_GCLK_ID_SLOW either.so far I found three problems in the I2C reference manual:. one is that it refers to STATUS.SYNCBUSY, which doesn't exist (SYNCBUSY.SYSOP does). two, SERCOM_I2CM_CTRLA_SCLSM and "smart mode" are poorly documented. neither explains the behaviour I was seeing (master clock generated without read DATA interaction). three, which accesses need synchronisation is inconsistent throughout the document. the reference code from Atmel (ATSAMD21J18) seems to synchronise at every step (even on reads!), which doesn't seem right.

author: ataradov
author_rank: Super Contributor
timestamp: October 28, 2016, 03:14:30 pm
content: Quote from: muxx on October 28, 2016, 10:14:12 amSynchronization is never required. The worst that can happen, your code will be stuck for a few cycles if you access two synchronized registers in a row.Quote from: muxx on October 28, 2016, 10:14:12 amIt is only required for SMBus timeouts. I don't use them, so no slow clock.Quote from: muxx on October 28, 2016, 10:14:12 amThere are picturesQuote from: muxx on October 28, 2016, 10:14:12 amI may be able to help understand what is going on if you provide logic analyzer of scope traces and corresponding code.Quote from: muxx on October 28, 2016, 10:14:12 amSome reads may need synchronization in some cases. In general, I personally never synchronize anything unless absolutely necessary.

author: muxx
author_rank: Contributor
timestamp: October 28, 2016, 03:28:16 pm
content: thanks Alex.WRT synchronisation, I am only going by the stuff that's in the datasheet, don't shoot the messenger.the code was very similar to yours, only enabling the SERCOM_I2CM_CTRLA_SCLSM bit.as soon as I set up a read transaction by programming the READ address, the clock just keeps on going regardless of whether I read DATA or not. that's the gist of it.say I was reading four bytes, but the clock would run forever (not even 256 packets or something). moreover, transmitting STOP after those 4 bytes also did not work (it would not appear on the bus).currently I have my code fixed. I'll try to recreate the issue and show you the logic analyzer dump.BTW, thanks for EDBG. I only found it through your homepage and it's a good replacement for OpenOCD that I was using for programming.cheers.

author: ataradov
author_rank: Super Contributor
timestamp: October 28, 2016, 03:57:49 pm
content: Quote from: muxx on October 28, 2016, 03:28:16 pmGist is not going to suffice here. I worked with this I2C for quite some time and with different configurations and never seen anything like that.Did you have some IDE open by any chance and had I/O View open on the I2C peripheral? Reads by the debugger are still reads.

author: muxx
author_rank: Contributor
timestamp: October 28, 2016, 05:00:36 pm
content: no, none of that crap. as I said, toggled that one bit and it's fixed.I won't be able to revert my code until next week. if you are curious, try enabling that one bit and leave the rest of your code unchanged.

author: ataradov
author_rank: Super Contributor
timestamp: October 28, 2016, 08:00:58 pm
content: Quote from: muxx on October 28, 2016, 05:00:36 pmOk, I actually misread your original post, I thought you are talking about SERCOM_I2CM_CTRLB_SMEN, not SERCOM_I2CM_CTRLA_SCLSM.I never used SCLSM. I don't have time to try at the moment, but it seems like it is possible to live without this feature. I've managed so far

author: muxx
author_rank: Contributor
timestamp: October 31, 2016, 09:37:40 am
content: oh, no. it's not the "smart mode" enable. smart mode is quite clear actually and it works as expected.I enabled SERCOM_I2CM_CTRLA_SCLSM as I was reading the TRM and writing the code. I read the description, looked at the diagram and thought "why not?". I just don't see how the description matches what I was seeing.

author: muxx
author_rank: Contributor
timestamp: October 31, 2016, 12:25:57 pm
content: ok, recreated the problem for everyone's amusement.the setup part:c->CTRLA.reg = SERCOM_I2CM_CTRLA_LOWTOUTEN | SERCOM_I2CM_CTRLA_INACTOUT(3) | SERCOM_I2CM_CTRLA_SCLSM |SERCOM_I2CM_CTRLA_SPEED(0) | SERCOM_I2CM_CTRLA_MEXTTOEN | SERCOM_I2CM_CTRLA_MODE_I2C_MASTER |SERCOM_I2CM_CTRLA_SDAHOLD(2);the read loop:if (rxc > 0){/* start and repeated start are no different *//* same address code as above */c->CTRLB.reg &= ~SERCOM_I2CM_CTRLB_ACKACT;c->ADDR.reg = SERCOM_I2CM_ADDR_ADDR(a | 0x01);gpio_set(9, 1);SYNCBUSY(c);if (!i2cm_waitint(c, SERCOM_I2CM_INTFLAG_SB)) { err = I2CME_A_NOSB; goto failnobus; };while (rxc--){if (rxc == 0){c->CTRLB.reg |= SERCOM_I2CM_CTRLB_ACKACT | SERCOM_I2CM_CTRLB_CMD(3);SYNCBUSY(c);}*rxbuf++ = c->DATA.reg;gpio_toggle(9);if (rxc)if (!i2cm_waitint(c, SERCOM_I2CM_INTFLAG_SB)) { err = I2CME_A_NOSB; goto txstop; };}return I2CME_OK; /* STOP already transmitted */}now you can see that I set GPIO9 to 1 when I just wrote the address and then toggle it every time I read from the DATA register. in this example I read 4 bytes, so in all you see 5 transitions on GPIO9 as expected.however, even though the read loop had already terminated and the NAK after the fourth byte had propagated OK, the STOP condition is nowhere to be seen and the clock keeps on running. there's probably valid use for the SERCOM_I2CM_CTRLA_SCLSM feature (DMA?), but I don't think it's properly documented in the TRM. the diagrams are also not helpful, because in both of them my understanding is that the read is still synchronised by reading the DATA register, and this is NOT what I am seeing through experiment.see the waveforms attached.