I2C Atmel - Page 1
Forum section: electronics / general

author: h2541
author_rank: Contributor
timestamp: July 21, 2016, 04:45:37 pm
content: Hello,did someone already using I2C on the card from atmel? If Yes, would you code examples that do not use the ASF? I find a lot of examples for UART but not for I2C.Thank you

author: ataradov
author_rank: Super Contributor
timestamp: July 21, 2016, 05:04:11 pm
content: What is "card"?What MCU you are using?

author: h2541
author_rank: Contributor
timestamp: July 21, 2016, 05:07:26 pm
content: It's SAMD 09

author: ataradov
author_rank: Super Contributor
timestamp: July 21, 2016, 05:11:33 pm
content: Here is bare metal example for D21, it should work on D09 without modifications:Code:/*- Includes ----------------------------------------------------------------*/#include <stdlib.h>#include <stdint.h>#include <stdbool.h>#include "hal.h"#include "halGpio.h"#include "i2c.h"/*- Definitions -------------------------------------------------------------*/HAL_GPIO_PIN(SDA, A, 8);HAL_GPIO_PIN(SCL, A, 9);#define I2C_SERCOM SERCOM0#define I2C_SERCOM_PMUX PORT_PMUX_PMUXE_C_Val#define I2C_SERCOM_GCLK_ID SERCOM0_GCLK_ID_CORE#define I2C_SERCOM_CLK_GEN 0#define I2C_SERCOM_APBCMASK PM_APBCMASK_SERCOM0#define I2C_ADDRESS 0xa0enum{I2C_TRANSFER_WRITE = 0,I2C_TRANSFER_READ = 1,};/*- Implementations ---------------------------------------------------------*///-----------------------------------------------------------------------------void i2c_init(void){HAL_GPIO_SDA_out();//HAL_GPIO_SDA_pullup();HAL_GPIO_SDA_pmuxen(I2C_SERCOM_PMUX);HAL_GPIO_SCL_out();//HAL_GPIO_SDA_pullup();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);//dbg_log("I2C: RXNACK during write (address)\r\n");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);//dbg_log("I2C: RXNACK during write (data)\r\n");return false;}}I2C_SERCOM->I2CM.CTRLB.reg |= SERCOM_I2CM_CTRLB_CMD(3);return true;}//-----------------------------------------------------------------------------bool i2c_write_start(void){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;}return true;}//-----------------------------------------------------------------------------bool i2c_write_byte(uint8_t byte){I2C_SERCOM->I2CM.DATA.reg = byte;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;}return true;}//-----------------------------------------------------------------------------void i2c_write_stop(void){I2C_SERCOM->I2CM.CTRLB.reg |= SERCOM_I2CM_CTRLB_CMD(3);}//-----------------------------------------------------------------------------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;}//-----------------------------------------------------------------------------bool i2c_busy(void){bool busy;I2C_SERCOM->I2CM.ADDR.reg = I2C_ADDRESS | I2C_TRANSFER_WRITE;while (0 == (I2C_SERCOM->I2CM.INTFLAG.reg & SERCOM_I2CM_INTFLAG_MB));busy = (0 != (I2C_SERCOM->I2CM.STATUS.reg & SERCOM_I2CM_STATUS_RXNACK));I2C_SERCOM->I2CM.CTRLB.reg |= SERCOM_I2CM_CTRLB_CMD(3);return busy;}hal_gpio.h file that this code uses can be found herehttps://github.com/ataradov/mcu-starter-projects/tree/master/samd21. Again, D09 and D21 are similar in this respect.

author: h2541
author_rank: Contributor
timestamp: July 21, 2016, 05:56:28 pm
content: Thank you, I adapted the code of the I2C particularly for configuration pin.In the main, I have to do:Code:i2c_init();i2c_write_start();i2c_read(@; 0x8);i2c_write_stop();My card(master) needs to come retrieve data from an external component(slave). If I understood the functioning, my card is the master and it sends a START bit with the function i2c_write_start().Then the master (SAM) to send the external component address with the function i2c_read(addres,size). But the data is broken down into two parts, the 15..8 bit then 7..0.Then the master send the STOP bit with the function i2c_write_stop();

author: ataradov
author_rank: Super Contributor
timestamp: July 21, 2016, 06:03:48 pm
content: Quote from: h2541 on July 21, 2016, 05:56:28 pmWhy do you start write and then do read?Functions i2c_write() and i2c_read() perform full write and read cycles.i2c_write_start(), i2c_write_byte() and i2c_write_stop() are designed to perform "split" I2C write, just in case you don't have the entire buffer in the memory, for example.Quote from: h2541 on July 21, 2016, 05:56:28 pmSlave address in this example is fixed to I2C_ADDRESS. You can change that if you want/need to, of course.Code:uint8_t data[] = {1, 2, 3, 4, 5, 6};i2c_init();i2c_write(data, 6);

author: h2541
author_rank: Contributor
timestamp: July 21, 2016, 06:19:30 pm
content: But if I want to read data from my slave, I have to question the slave. The master must write the START bit, then the slave address. Then I must say to my master to read data from my slave.Once the master has received the data from the slave, he should write the stop bit?

author: ataradov
author_rank: Super Contributor
timestamp: July 21, 2016, 06:33:34 pm
content: That's exactly what i2c_read() function does. If you want to do all those things manually, then look at the code of that function.

author: h2541
author_rank: Contributor
timestamp: July 21, 2016, 06:55:42 pm
content: Ok, if I want to do this:QuoteI can do after i2c_init():Code:uint16_t i2c_read(void){SERCOM0->I2CM.ADDR.reg = I2C_ADDRESS | I2C_TRANSFER_READ; //Master : adress slavewhile (0 == (SERCOM0->I2CM.INTFLAG.reg & SERCOM_I2CM_INTFLAG_SB));if (SERCOM0->I2CM.STATUS.reg & SERCOM_I2CM_STATUS_RXNACK) // SLave responded with a NACK{SERCOM0->I2CM.CTRLB.reg |= SERCOM_I2CM_CTRLB_CMD(3); // Stop conditionreturn false;}SERCOM0->I2CM.CTRLB.reg &= ~SERCOM_I2CM_CTRLB_ACKACT; //Send NACKfor (int i = 0; i < size-1; i++){data = SERCOM0->I2CM.DATA.reg;while (0 == (SERCOM0->I2CM.INTFLAG.reg & SERCOM_I2CM_INTFLAG_SB));}if (size){SERCOM0->I2CM.CTRLB.reg |= SERCOM_I2CM_CTRLB_ACKACT;SERCOM0->I2CM.CTRLB.reg |= SERCOM_I2CM_CTRLB_CMD(3);data[size-1] = SERCOM0->I2CM.DATA.reg;}return true;}

author: ataradov
author_rank: Super Contributor
timestamp: July 21, 2016, 08:38:33 pm
content: To implement this all you need to do is:1. Set I2C_ADDRESS to 0b1001 (A2) (A1) (A0) - substitute your (A2) (A1) (A0)2.Code:uint8_t buf;i2c_init();i2c_read(buf, 3);After that buf will contain upper byte, buf will contain lower byte and buf will contain configuration register.

author: h2541
author_rank: Contributor
timestamp: July 21, 2016, 08:44:13 pm
content: It is not necessary to describe the SDA and SCL signals?

author: ataradov
author_rank: Super Contributor
timestamp: July 21, 2016, 08:47:35 pm
content: You assign them to pins in i2c_init(), but you don't have to manually wiggle them. That's the point of having I2C peripheral.

author: h2541
author_rank: Contributor
timestamp: July 21, 2016, 08:50:34 pm
content: Ok, but the ACk of the master and slave will do as the chronogram

author: ataradov
author_rank: Super Contributor
timestamp: July 21, 2016, 08:52:18 pm
content: Yes, they will. I2C is a standard and I2C peripheral implements that standard.

author: h2541
author_rank: Contributor
timestamp: July 21, 2016, 08:55:39 pm
content: OK fine. Anyway thank you for your help.

author: h2541
author_rank: Contributor
timestamp: July 22, 2016, 09:25:03 am
content: I modified the function i2c_read that it returns me the result of the buffer:Code:uint16_t i2c_read(uint8_t *data, int size){SERCOM0->I2CM.ADDR.reg = I2C_ADDRESS | I2C_TRANSFER_READ;//while (0 == (SERCOM0->I2CM.INTFLAG.reg & SERCOM_I2CM_INTFLAG_SB));if (SERCOM0->I2CM.STATUS.reg & SERCOM_I2CM_STATUS_RXNACK){SERCOM0->I2CM.CTRLB.reg |= SERCOM_I2CM_CTRLB_CMD(3);//return false;}SERCOM0->I2CM.CTRLB.reg &= ~SERCOM_I2CM_CTRLB_ACKACT;for (int i = 0; i < size-1; i++){data = SERCOM0->I2CM.DATA.reg;//while (0 ==(SERCOM0->I2CM.INTFLAG.reg & SERCOM_I2CM_INTFLAG_SB));}if (size){SERCOM0->I2CM.CTRLB.reg |= SERCOM_I2CM_CTRLB_ACKACT;SERCOM0->I2CM.CTRLB.reg |= SERCOM_I2CM_CTRLB_CMD(3);data[size-1] = SERCOM0->I2CM.DATA.reg;}return data;}in the mainCode:uint8_t buf3;uint16_t voltage=i2c_read(&buf3, 3);But the problem is that when I do vary my voltage value that turns my slaveBut all this I need to use the function i2c_read? In this function, I have to place the lines containing the comments while in otherwise the program crashes .

author: ataradov
author_rank: Super Contributor
timestamp: July 22, 2016, 04:17:51 pm
content: Ok, you clearly don't know C, so why start from braking a working code? Why not see that is works as is?To get the value from my working code do this:Code:uint8_t buf;i2c_init();i2c_read(buf, 3);uint16_t value = ((uint16_t)buf << 8) | buf;// here value has the result you need.Your code will always return the same value, because you are returning two lower bytes of the address of the supplied buffer. There are other mistakes as well.

author: h2541
author_rank: Contributor
timestamp: July 22, 2016, 04:59:28 pm
content: I rework the code base and it still gives me the same value except this time it's 3164 (C5C) in decimal.But when I vary the voltage it returns me constantly that value C5CWith buf= 92; buf  = 92 and buf  = 92and value = 23644 (5C5C).But what remains amazing is that the DATA register does not change and stay at 0

author: ataradov
author_rank: Super Contributor
timestamp: July 22, 2016, 05:26:07 pm
content: Can you show your code as it stands right now. And also what type of sensor you are using and how it is connected to the MCU?

author: h2541
author_rank: Contributor
timestamp: July 22, 2016, 05:35:47 pm
content: Code:/*- Definitions -------------------------------------------------------------*/HAL_GPIO_PIN(SDA, A, 8);HAL_GPIO_PIN(SCL, A, 9);#define I2C_SERCOM SERCOM0#define I2C_SERCOM_PMUX PORT_PMUX_PMUXE_C_Val#define I2C_SERCOM_GCLK_ID SERCOM0_GCLK_ID_CORE#define I2C_SERCOM_CLK_GEN 0#define I2C_SERCOM_APBCMASK PM_APBCMASK_SERCOM0#define I2C_ADDRESS 0xa0enum{I2C_TRANSFER_WRITE = 0,I2C_TRANSFER_READ = 1,};/*- Implementations ---------------------------------------------------------*///-----------------------------------------------------------------------------void i2c_init(void){HAL_GPIO_SDA_out();//HAL_GPIO_SDA_pullup();HAL_GPIO_SDA_pmuxen(I2C_SERCOM_PMUX);HAL_GPIO_SCL_out();//HAL_GPIO_SDA_pullup();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_read(uint8_t *data, int size){SERCOM0->I2CM.ADDR.reg = I2C_ADDRESS | I2C_TRANSFER_READ;//while (0 == (SERCOM0->I2CM.INTFLAG.reg & SERCOM_I2CM_INTFLAG_SB));if (SERCOM0->I2CM.STATUS.reg & SERCOM_I2CM_STATUS_RXNACK){SERCOM0->I2CM.CTRLB.reg |= SERCOM_I2CM_CTRLB_CMD(3);return false;}SERCOM0->I2CM.CTRLB.reg &= ~SERCOM_I2CM_CTRLB_ACKACT;for (int i = 0; i < size-1; i++){data = SERCOM0->I2CM.DATA.reg;//while (0 == (SERCOM0->I2CM.INTFLAG.reg & SERCOM_I2CM_INTFLAG_SB));}if (size){SERCOM0->I2CM.CTRLB.reg |= SERCOM_I2CM_CTRLB_ACKACT;SERCOM0->I2CM.CTRLB.reg |= SERCOM_I2CM_CTRLB_CMD(3);data[size-1] = SERCOM0->I2CM.DATA.reg;}return true;}MainCode:sys_init();i2c_init();uint8_t buf;i2c_read (&buf,3);But why when I chang the voltage it's the same value

author: ataradov
author_rank: Super Contributor
timestamp: July 22, 2016, 05:41:37 pm
content: Well, why did you comment out waiting for salve to be on the bus? You can't just comment out thing from the code and expect it to work. If those parts were not needed, they would not be there.Also, this code expects shifted address, so it must be 0b10010010, assuming that (A2) (A1) (A0) are 001 in your schematic.Also, you did not answer the question about the type of sensor and how it is connected to the MCU.

author: h2541
author_rank: Contributor
timestamp: July 22, 2016, 05:50:48 pm
content: I comment out waiting for salve to be on the bus because it remains blocked in the two "while" I commented and it never comes out.SDA(4)==> PA14SCL(3)==> PA15

author: ataradov
author_rank: Super Contributor
timestamp: July 22, 2016, 05:52:14 pm
content: Quote from: h2541 on July 22, 2016, 05:50:48 pmAnd instead of commenting this code out you should really think why there is no slave. In this case you have the wrong address.

author: ataradov
author_rank: Super Contributor
timestamp: July 22, 2016, 06:01:42 pm
content: And as I said, this code needs shifted address, so it must read 0b10010010.

author: h2541
author_rank: Contributor
timestamp: July 22, 2016, 06:06:46 pm
content: Yes , it was the wrong address sorry. now I can take my changes thank you

author: OliverK
author_rank: Contributor
timestamp: June 17, 2017, 11:14:23 am
content: Could u send me your complete code, im working on a simillair project with I2C and a samd10 witch is almst the same

author: ataradov
author_rank: Super Contributor
timestamp: June 17, 2017, 05:34:01 pm
content: Here is a complete I2C masterhttps://github.com/ataradov/dgw/blob/master/embedded/i2c_master.c

author: OliverK
author_rank: Contributor
timestamp: July 11, 2017, 02:17:36 pm
content: Hi i have one question about your codeCode:void i2c_pins(int mask, int value){if (mask & I2C_PINS_SDA){HAL_GPIO_SDA_out();HAL_GPIO_SDA_write(value & I2C_PINS_SDA);}else{HAL_GPIO_SDA_in();HAL_GPIO_SDA_clr();}if (mask & I2C_PINS_SCL){HAL_GPIO_SCL_out();HAL_GPIO_SCL_write(value & I2C_PINS_SCL);}else{HAL_GPIO_SCL_in();HAL_GPIO_SCL_clr();}HAL_GPIO_SDA_pmuxdis();HAL_GPIO_SCL_pmuxdis();}Could you explain what int mask and int value are for?

author: ataradov
author_rank: Super Contributor
timestamp: July 11, 2017, 05:17:45 pm
content: This is just a feature that lets you set any arbitrary values on the SCL and SDA pins, going around normal I2C peripheral.I needed this, because I used SCL and/or SCL low on reset as an entry condition for the bootloader.

author: d-jokic
author_rank: Contributor
timestamp: October 02, 2017, 11:38:05 pm
content: Hi,Regarding your project, where can I find the following files: stdlib.h, stdint.h, and stdbool.h? ThanksQuote from: ataradov on June 17, 2017, 05:34:01 pm

author: ataradov
author_rank: Super Contributor
timestamp: October 02, 2017, 11:38:58 pm
content: Quote from: d-jokic on October 02, 2017, 11:38:05 pmThey are a part of the standard C library, they come with a compiler.

author: d-jokic
author_rank: Contributor
timestamp: October 02, 2017, 11:54:00 pm
content: That's what I've realized, but for some reason it was throwing an error. It's better now; no idea why that happened. My second issue is the hal.h file. I believe that there is an entire hal file that my project should have. I'm not familiar with what hal is, and was wondering if this is correct.Thanks for your help!

author: ataradov
author_rank: Super Contributor
timestamp: October 02, 2017, 11:55:23 pm
content: Quote from: d-jokic on October 02, 2017, 11:54:00 pmWhat file are you talking about? There is a complete project that is self sufficient.

author: d-jokic
author_rank: Contributor
timestamp: October 02, 2017, 11:58:39 pm
content: In the code you posted on the forum, there is an include hal.h. This file isn't in the github .

author: ataradov
author_rank: Super Contributor
timestamp: October 03, 2017, 12:01:43 am
content: Quote from: d-jokic on October 02, 2017, 11:58:39 pmThat file was taken from a different project. Just use the whole project from GitHub.You can't just copy-paste things and expect them to work. The code posted here is just to illustrate the idea, you will have to adopt it to your needs anyway.

author: d-jokic
author_rank: Contributor
timestamp: October 03, 2017, 12:30:12 am
content: Ok, makes sense. I misunderstood and thought they were one and the same. In your i2c_master.c file, I can't find anywhere where you declare the slave address.Also what is "T_RISE" and how is it used in i2c_init? One other thing is that F_CPU is not declared anywhere, however it's used in the initialization functions. I'm not sure how that's supposed to work.Sorry for all the questions, I'm very new to embedded (and Atmel) and usually rely heavily on comments to figure out what code does. I'm assuming the name is a give away however, I'm not familiar with it.Thank you!

author: ataradov
author_rank: Super Contributor
timestamp: October 03, 2017, 01:02:04 am
content: Quote from: d-jokic on October 03, 2017, 12:30:12 amHave you tried to read the code? It is right there "bool i2c_start(int addr)".Quote from: d-jokic on October 03, 2017, 12:30:12 amRead the datasheet.Quote from: d-jokic on October 03, 2017, 12:30:12 amIt is declared in the project settings or in the Makefile.

author: d-jokic
author_rank: Contributor
timestamp: October 03, 2017, 06:12:42 pm
content: Hi Alex,Thank you for your help thus far. However, I'm unable to acquire data from my sensor. The code does compile. I am using a max30105 particle sensor connected as follows: SDA --> PA22 and SCL --> PA23. I am uploading the code via the Arduino Zero (and therefore EDBG debugger), I don't believe this is causing the problem. The sensor is supposed to have a light turn on when it is on, this is currently not happening./ I have also checked that the sensor is functional. I have included my code below. One issue I was having was that the slave address entered in i2c_init is an int; does this mean that the integer representation of the address needs to be entered rather than the hexadecimal? I have tried hexadecimal, binary and integer and none have worked. The address is 0x57, or 87 in decimal. I just wanted to make sure I was implementing your code correctly:i2c_master.cCode:/*- Includes ----------------------------------------------------------------*/#include <stdlib.h>#include <stdint.h>#include <stdbool.h>#include "samd21.h"#include "hal_gpio.h"#include "i2c_master.h"/*- Definitions -------------------------------------------------------------*/HAL_GPIO_PIN(SDA, A, 22);HAL_GPIO_PIN(SCL, A, 23);#define I2C_SERCOM SERCOM2#define I2C_SERCOM_PMUX PORT_PMUX_PMUXE_D_Val#define I2C_SERCOM_GCLK_ID SERCOM2_GCLK_ID_CORE#define I2C_SERCOM_CLK_GEN 0#define I2C_SERCOM_APBCMASK PM_APBCMASK_SERCOM2#define T_RISE 215e-9 // Depends on the board, actuallyenum{I2C_TRANSFER_WRITE = 0,I2C_TRANSFER_READ = 1,};enum{I2C_PINS_SDA = (1 << 0),I2C_PINS_SCL = (1 << 1),};/*- Implementations ---------------------------------------------------------*///-----------------------------------------------------------------------------int i2c_init(int freq){int baud = ((float)F_CPU / freq - (float)F_CPU * T_RISE - 10.0) / 2.0;if (baud < 0)baud = 0;else if (baud > 255)baud = 255;freq = (float)F_CPU / (2.0 * (5.0 + baud) + (float)F_CPU * T_RISE);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.CTRLA.reg = SERCOM_I2CM_CTRLA_SWRST;while (I2C_SERCOM->I2CM.CTRLA.reg & SERCOM_I2CM_CTRLA_SWRST);I2C_SERCOM->I2CM.CTRLB.reg = SERCOM_I2CM_CTRLB_SMEN;while (I2C_SERCOM->I2CM.SYNCBUSY.reg);I2C_SERCOM->I2CM.BAUD.reg = SERCOM_I2CM_BAUD_BAUD(baud);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);HAL_GPIO_SDA_in();HAL_GPIO_SDA_clr();HAL_GPIO_SDA_pmuxen(I2C_SERCOM_PMUX);HAL_GPIO_SCL_in();HAL_GPIO_SCL_clr();HAL_GPIO_SCL_pmuxen(I2C_SERCOM_PMUX);return freq;}//-----------------------------------------------------------------------------bool i2c_start(int addr){I2C_SERCOM->I2CM.INTFLAG.reg = SERCOM_I2CM_INTFLAG_ERROR;I2C_SERCOM->I2CM.ADDR.reg = addr;while (0 == (I2C_SERCOM->I2CM.INTFLAG.reg & SERCOM_I2CM_INTFLAG_MB) &&0 == (I2C_SERCOM->I2CM.INTFLAG.reg & SERCOM_I2CM_INTFLAG_SB));if (I2C_SERCOM->I2CM.STATUS.reg & SERCOM_I2CM_STATUS_RXNACK ||I2C_SERCOM->I2CM.INTFLAG.reg & SERCOM_I2CM_INTFLAG_ERROR){I2C_SERCOM->I2CM.CTRLB.reg |= SERCOM_I2CM_CTRLB_CMD(3);return false;}return true;}//-----------------------------------------------------------------------------bool i2c_stop(void){if ((I2C_SERCOM->I2CM.INTFLAG.reg & SERCOM_I2CM_INTFLAG_MB) ||(I2C_SERCOM->I2CM.INTFLAG.reg & SERCOM_I2CM_INTFLAG_SB)){I2C_SERCOM->I2CM.CTRLB.reg |= SERCOM_I2CM_CTRLB_CMD(3);}return true;}//-----------------------------------------------------------------------------bool i2c_read_byte(uint8_t *byte, bool last){while (1){int flags = I2C_SERCOM->I2CM.INTFLAG.reg;if (flags & SERCOM_I2CM_INTFLAG_SB)break;if (flags & (SERCOM_I2CM_INTFLAG_MB | SERCOM_I2CM_INTFLAG_ERROR))return false;}if (last)I2C_SERCOM->I2CM.CTRLB.reg |= SERCOM_I2CM_CTRLB_ACKACT | SERCOM_I2CM_CTRLB_CMD(3);elseI2C_SERCOM->I2CM.CTRLB.reg &= ~SERCOM_I2CM_CTRLB_ACKACT;*byte = I2C_SERCOM->I2CM.DATA.reg;return true;}//-----------------------------------------------------------------------------bool i2c_write_byte(uint8_t byte){I2C_SERCOM->I2CM.DATA.reg = byte;while (1){int flags = I2C_SERCOM->I2CM.INTFLAG.reg;if (flags & SERCOM_I2CM_INTFLAG_MB)break;if (flags & (SERCOM_I2CM_INTFLAG_SB | SERCOM_I2CM_INTFLAG_ERROR))return false;}if (I2C_SERCOM->I2CM.STATUS.reg & SERCOM_I2CM_STATUS_RXNACK){I2C_SERCOM->I2CM.CTRLB.reg |= SERCOM_I2CM_CTRLB_CMD(3);return false;}return true;}//-----------------------------------------------------------------------------bool i2c_busy(int addr){bool busy;I2C_SERCOM->I2CM.ADDR.reg = addr | I2C_TRANSFER_WRITE;while (0 == (I2C_SERCOM->I2CM.INTFLAG.reg & SERCOM_I2CM_INTFLAG_MB));busy = (0 != (I2C_SERCOM->I2CM.STATUS.reg & SERCOM_I2CM_STATUS_RXNACK));I2C_SERCOM->I2CM.CTRLB.reg |= SERCOM_I2CM_CTRLB_CMD(3);return busy;}//-----------------------------------------------------------------------------void i2c_pins(int mask, int value){if (mask & I2C_PINS_SDA){HAL_GPIO_SDA_out();HAL_GPIO_SDA_write(value & I2C_PINS_SDA);}else{HAL_GPIO_SDA_in();HAL_GPIO_SDA_clr();}if (mask & I2C_PINS_SCL){HAL_GPIO_SCL_out();HAL_GPIO_SCL_write(value & I2C_PINS_SCL);}else{HAL_GPIO_SCL_in();HAL_GPIO_SCL_clr();}HAL_GPIO_SDA_pmuxdis();HAL_GPIO_SCL_pmuxdis();}main.cCode://-----------------------------------------------------------------------------#include <stdlib.h>#include <stdint.h>#include <stdbool.h>#include <string.h>#include "samd21.h"#include "hal_gpio.h"#include "i2c_master.h"//-----------------------------------------------------------------------------#define PERIOD_FAST 100#define PERIOD_SLOW 500uint8_t data;//-----------------------------------------------------------------------------static void timer_init(void){PM->APBCMASK.reg |= PM_APBCMASK_TC3;GCLK->CLKCTRL.reg = GCLK_CLKCTRL_ID(TC3_GCLK_ID) |GCLK_CLKCTRL_CLKEN | GCLK_CLKCTRL_GEN(0);TC3->COUNT16.CTRLA.reg = TC_CTRLA_MODE_COUNT16 | TC_CTRLA_WAVEGEN_MFRQ |TC_CTRLA_PRESCALER_DIV256 | TC_CTRLA_PRESCSYNC_RESYNC;TC3->COUNT16.COUNT.reg = 0;timer_set_period(PERIOD_SLOW);TC3->COUNT16.CTRLA.reg |= TC_CTRLA_ENABLE;TC3->COUNT16.INTENSET.reg = TC_INTENSET_MC(1);NVIC_EnableIRQ(TC3_IRQn);}static void sys_init(void){// Switch to 8MHz clock (disable prescaler)SYSCTRL->OSC8M.bit.PRESC = 0;// Enable interruptsasm volatile ("cpsie i");}//-----------------------------------------------------------------------------int main(void){sys_init();i2c_init(100);i2c_start(0x57);while (1){i2c_read_byte(&data, true);}return 0;}Thanks again for your help. I'm hoping I can figure this out soon.

author: ataradov
author_rank: Super Contributor
timestamp: October 03, 2017, 06:32:48 pm
content: You need to specify full 8-bit address, so it would be 0xAE.And I'm not sure what you expect from a simple read. This sensors requires configuration. It has a register map, so you need to write a register index you want to read before you can read the value. There are pseudo-code examples in the datasheet. Active != transaction on the bus.And you are specifying the last byte flag, but still continue to read in a loop. That will create some unknown condition on the bus.And there can be a lot of other problems. You need a scope/logic analyzer to see what's going on on the lines.

author: d-jokic
author_rank: Contributor
timestamp: October 03, 2017, 07:30:17 pm
content: Thanks for the info. This is more complicated than I anticipated, due to the complexity of the sensor.This sensor has an Arduino library in which they assign names to all the registers; e.g:Code:static const uint8_t MAX30105_FIFOWRITEPTR =0x04;static const uint8_t MAX30105_FIFOOVERFLOW =0x05;static const uint8_t MAX30105_FIFOREADPTR =0x06;static const uint8_t MAX30105_FIFODATA =0x07;I know you can't just copy paste arduino library code and expect it to work on a mcu (since most libraries make use of other arduino libraries, aka they're written half in C and half in Arduino) but for something like this, I can copy this into my code right? Rather than combing through the datasheet looking for register addresses

author: ataradov
author_rank: Super Contributor
timestamp: October 03, 2017, 07:38:41 pm
content: You can obviously copy all definitions like this. You can't copy code, but you can look at how that code is structured.No matter what, any I2C stuff will be built from basic blocks: START, READ/WRITE, STOP. There is not a whole lot you can expect from I2C. Just see how they do it.This is not really complex, it is pretty typical for I2C devices have a number of registers. The demo application by the same link reads and writes temperature from an I2C device, and it does setup some configuration registers. Yours will be similar.

author: d-jokic
author_rank: Contributor
timestamp: October 04, 2017, 01:46:47 am
content: So to clarify, the sensor requires configuration: from the data sheet I've gotten the following pseudo code. I'm having trouble understanding what needs to be done and am not sure if my interpretations are correct. Your input would really be appreciated!The FIFO Write Pointer points to the location where the MAX30105 writes the next sample & advances for each sample pushed on to the FIFO; FIFO_WR_PTR is the bits within this register than can be accessedThe FIFO Read Pointer points to the location from where the processor gets the next sample from the FIFO through the I2C interface; advances each time a sample is popped from the FIFO. FIFO_RD_PTR is the bits within this register than can be accessedThis is the first part of the pseudo code:Code:(1) START;(2) Send device address + write mode*slave acknowledges*(3) Send address of FIFO_WR_PTR;*slave acknowledges*(4) REPEATED_START;(5) Send device address + read mode(6) Read FIFO_WR_PTR;(7) STOP;My interpretations:Preliminary step: recommended to first clear the FIFO_WR_PTR, OVF_COUNTER, and FIFO_RD_PTR registers to all zeroes (0x00) to ensure FIFO is empty before every new sensor reading(1)--> send a start condition(2)--> send the slave address and set master to write mode*slave acknowledges*(3)--> send the address 0x04, which is the address of the FIFO write pointer*slave acknowledges*(4)--> send another start condition(5)--> send the slave address and set the master to read mode(6)--> master reads data stored; what data would be here though?(7)--> send stop commandThen is goes on to say:Code:The central processor evaluates the number of samples to be read from the FIFO:NUM_AVAILABLE_SAMPLES = FIFO_WR_PTR – FIFO_RD_PTRNUM_SAMPLES_TO_READ = < less than or equal to NUM_AVAILABLE_SAMPLES >My interpretation:FIFO_WR_PTR points to where the next byte of data can be stored; and FIFO_RD_PTR points to the next byte of data to be read. This gives us how many samples can be read, but I'm not sure how.Code:Second transaction: Read NUM_SAMPLES_TO_READ samples from the FIFO:(1)START;(2)Send device address + write mode(3)Send address of FIFO_DATA;(4)REPEATED_START;(5)Send device address + read mode(6)for (i = 0; i < NUM_SAMPLES_TO_READ; i++) {Read FIFO_DATA;}My interpretation(1) start command(2) send the slave address and set master to write mode(3) send address of FIFO DATA (where we want to store data)(4) start command again(5) send slave address and set master to read(6) read NUM_SAMPLES_TO_READ worth of dataMy question is a dumb one, but how to I actually go about writing to these registers? I'm also not sure to which register to actually write the commands. The data sheet says " in practice only the FIFO_RD_PTR register should be written to in operation". But that doesn't make sense to me.Datasheet I've been referencing:https://datasheets.maximintegrated.com/en/ds/MAX30105.pdf

author: ataradov
author_rank: Super Contributor
timestamp: October 04, 2017, 02:58:54 am
content: You will have too figure out how to use that device yourself, or ask on Maxim forum.As far as I2C goes, your understanding is correct. The data that is read will be the value of the register indexed by the address specified in the write command. This is pretty typical for I2C devices that need to report more than one value, or need configuration.Basic approach when designing things like this is to first of all read a register with a known value, like ID, configuration, whatever. This will basically ensure that you can talk to the device and it understands you. Then start actually writing hard code, and you will probably understand how to do that based on your experience from the first basic step.

author: funkathustra
author_rank: Regular Contributor
timestamp: October 04, 2017, 05:19:15 pm
content: Agree with @ataradovFew things to add: basically every i2C device these days uses SMBus-style registers to interact with the device. When I first started, it was confusing. Official Linux documentation actually helped me understand what needed to happen:https://www.kernel.org/doc/Documentation/i2c/smbus-protocolThere's absolutely no reason you can't just manipulate the Arduino library for it, too. I had a look at it, and it's very typical I2C stuff.Notice how everything calls into writeRegister8() and readRegister8()? Those functions could easily be implemented from the code @ataradov was nice enough to provide.This is good stuff to learn, as 95% of sensors these days use I2C, and use it in a register-based way. Once you port a few libraries over, you'll get comfortable with this stuff enough that you'll be able to find random chips on DigiKey that hobbyist libraries don't exist for, and integrate them into your design easily.Very powerful stuff! Keep going!

author: d-jokic
author_rank: Contributor
timestamp: October 04, 2017, 10:08:14 pm
content: Thanks to both of you. I took your advice and ported over readregister8 and writeregister8. However, it's still not functional; using a multimeter I found that both SDA and SCL are high, meaning that communication doesn't start. One thing I wasn't sure about in Alex's code was what to set int freq to in i2c_init(). Google says that the most widely used clock rate is 100kHz, so I put that.This is the Arduino code; it's part of a class (MAX30105) and _i2cPort is an i2c instance through the wire library. I don't have an equivalent for _i2cPort because Alex's code is set up to run on certain pins (PA8 and PA9) ans I'm only using 1 i2c right now.Code:uint8_t MAX30105::readRegister8(uint8_t address, uint8_t reg) {_i2cPort->beginTransmission(address);_i2cPort->write(reg);_i2cPort->endTransmission(false);_i2cPort->requestFrom(address, 1); // Request 1 bytereturn (_i2cPort->read());}void MAX30105::writeRegister8(uint8_t address, uint8_t reg, uint8_t value) {_i2cPort->beginTransmission(address);_i2cPort->write(reg);_i2cPort->write(value);_i2cPort->endTransmission();}This is my port over using Alex's code:Code:uint8_t readRegister8(uint8_t reg) {i2c_init(100000);//initialize i2ci2c_start(SLAVE_ADDRESS_WRITE);//start isc; send slave address and set to writei2c_write_byte(reg);//write register of interest to slavei2c_start(SLAVE_ADDRESS_READ);//restart command; resend slave address + read modei2c_read_byte(&read_register_value,true);//read 1 bit from the slavei2c_stop();return (read_register_value);}void writeRegister8(uint8_t reg, uint8_t value) {i2c_init(100000);//initialize i2ci2c_start(SLAVE_ADDRESS_WRITE);//start isc; send slave address and set to writei2c_write_byte(reg);//write register of interest to slavei2c_write_byte(value);//read 1 bit from the slavei2c_stop();}The readregister8 and writeregister8 functions are also used for a function called bitMask, which is used for configuring the sensor (setting up some registers regarding adc range, sampling speed, etc)Arduino code:Code:void MAX30105::bitMask(uint8_t reg, uint8_t mask, uint8_t thing){// Grab current register contextuint8_t originalContents = readRegister8(_i2caddr, reg);// Zero-out the portions of the register we're interested inoriginalContents = originalContents & mask;// Change contentswriteRegister8(_i2caddr, reg, originalContents | thing);}Ported:Code:void bitMask(uint8_t reg, uint8_t mask, uint8_t thing){// Grab current register contextuint8_t originalContents = readRegister8(reg);// Zero-out the portions of the register we're interested inoriginalContents = originalContents & mask;// Change contentswriteRegister8(reg, originalContents | thing);}I probably made a mistake somewhere along the way (probably over simplified or forgot a step). I'll keep combing over it and I'll see if I can find it. If you spy anything that looks odd, please let me know!-----------------Overall code----------------------------------------MAIN.CCode:int i = 0;uint8_t array;uint8_t activeLEDs;uint8_t read_register_value;uint8_t MAX30105_FIFOWRITEPTR =0x04;uint8_t MAX30105_FIFOOVERFLOW =0x05;uint8_t MAX30105_FIFOREADPTR =0x06;uint8_t MAX30105_FIFODATA =0x07;uint8_t MAX30105_FIFOCONFIG =0x08;uint8_t MAX30105_RESET_MASK =0xBF;uint8_t MAX30105_RESET =0x40;uint8_t MAX30105_MODECONFIG =0x09;uint8_t MAX30105_PARTICLECONFIG =0x0A; // Note, sometimes listed as "SPO2" config in datasheet (pg. 11)uint8_t MAX30105_LED1_PULSEAMP =0x0C;uint8_t MAX30105_MULTILEDCONFIG1 = 0x11;uint8_t MAX30105_SAMPLEAVG_MASK =0xE0;uint8_t MAX30105_SAMPLEAVG_1 =0x00;uint8_t MAX30105_MODE_MASK =0xF8;uint8_t MAX30105_MODE_REDONLY =0x02;uint8_t MAX30105_ADCRANGE_MASK =0x9F;uint8_t MAX30105_ADCRANGE_4096 =0x20;uint8_t MAX30105_SAMPLERATE_MASK = 0xE3;uint8_t MAX30105_SAMPLERATE_1000 = 0x14;uint8_t MAX30105_PULSEWIDTH_MASK = 0xFC;uint8_t MAX30105_PULSEWIDTH_411 =0x03;uint8_t MAX30105_SLOT1_MASK =0xF8;uint8_t SLOT_RED_LED =0x01;uint8_t SLAVE_ADDRESS_WRITE = 0xAE; //10101110uint8_t SLAVE_ADDRESS_READ = 0xAF; //10101111static void sys_init(void){// Switch to 8MHz clock (disable prescaler)SYSCTRL->OSC8M.bit.PRESC = 0;// Enable interruptsasm volatile ("cpsie i");}uint8_t readRegister8(uint8_t reg) {i2c_init(100000);//initialize i2ci2c_start(SLAVE_ADDRESS_WRITE);//start isc; send slave address and set to writei2c_write_byte(reg);//write register of interest to slavei2c_start(SLAVE_ADDRESS_READ);//restart command; resend slave address + read modei2c_read_byte(&read_register_value,true);//read 1 bit from the slavei2c_stop();return (read_register_value);}void writeRegister8(uint8_t reg, uint8_t value) {i2c_init(100000);//initialize i2ci2c_start(SLAVE_ADDRESS_WRITE);//start isc; send slave address and set to writei2c_write_byte(reg);//write register of interest to slavei2c_write_byte(value);//read 1 bit from the slavei2c_stop();}//Given a register, read it, mask it, and then set the thingvoid bitMask(uint8_t reg, uint8_t mask, uint8_t thing){// Grab current register contextuint8_t originalContents = readRegister8(reg);// Zero-out the portions of the register we're interested inoriginalContents = originalContents & mask;// Change contentswriteRegister8(reg, originalContents | thing);}//-------------------------------------------------------------------------------------------------------------------------------------------------int main(void){sys_init();//Initializing particle sensor-----------------------------------------------------------------------------------------------------------------bitMask(MAX30105_MODECONFIG, MAX30105_RESET_MASK, MAX30105_RESET);//soft resetbitMask(MAX30105_FIFOCONFIG, MAX30105_SAMPLEAVG_MASK, MAX30105_SAMPLEAVG_1);//setFIFOAverage - 1bitMask(MAX30105_MODECONFIG, MAX30105_MODE_MASK, MAX30105_MODE_REDONLY);//setLEDMode - red onlyactiveLEDs = 1;bitMask(MAX30105_PARTICLECONFIG, MAX30105_ADCRANGE_MASK, MAX30105_ADCRANGE_4096);//setADCRange - 4096bitMask(MAX30105_PARTICLECONFIG, MAX30105_SAMPLERATE_MASK, MAX30105_SAMPLERATE_1000);//setSampleRate - 1000bitMask(MAX30105_PARTICLECONFIG, MAX30105_PULSEWIDTH_MASK, MAX30105_PULSEWIDTH_411);//setPulseWidth - 411writeRegister8(MAX30105_LED1_PULSEAMP, 0x1F);//setPulseAmplitudeRed(0x1F);bitMask(MAX30105_MULTILEDCONFIG1, MAX30105_SLOT1_MASK, SLOT_RED_LED);//enableSlot(1, SLOT_RED_LED);//clearFIFOwriteRegister8(MAX30105_FIFOWRITEPTR, 0);writeRegister8(MAX30105_FIFOOVERFLOW, 0);writeRegister8(MAX30105_FIFOREADPTR, 0);//TRANSACTION 1 -------------------------------------------------------------------------------------------------------------//Send start command to salve address 0x57 and set to write mode (master writes to slave)i2c_init(100000);i2c_start(SLAVE_ADDRESS_WRITE);//Send FIFO address to slave; tells slave we want to read from this address. Saves data to FIFOi2c_write_byte(MAX30105_FIFOWRITEPTR);//repeated start; send start command + slave address + read mode. Now master reads data from slavei2c_start(SLAVE_ADDRESS_READ);//read byte from FIFO write pointeri2c_read_byte(&MAX30105_FIFOWRITEPTR, true);i2c_stop();//TRANSACTION 2 -------------------------------------------------------------------------------------------------------------//number of samples is difference between MAX30105_FIFOWRITEPTR (stopped at last sample loaded into FIFO in transaction1int num_available_samples = MAX30105_FIFOWRITEPTR - MAX30105_FIFOREADPTR;int num_to_read = num_available_samples;//Send start command to salve address 0x57 and set to write mode (master writes to slave)i2c_start(SLAVE_ADDRESS_WRITE);//Send FIFO address to slave; tells slave we want to read from this address. Extract data from FIFO to masteri2c_write_byte(MAX30105_FIFODATA);i2c_start(SLAVE_ADDRESS_READ);for (i = 0; i < num_to_read; i++) {array = i2c_read_byte(&MAX30105_FIFODATA, false);}i2c_stop();while (1){}return 0;}-------------------------------------------------------------------I2C_MASTER.CCode:#include <stdlib.h>#include <stdint.h>#include <stdbool.h>#include "samd21.h"#include "hal_gpio.h"#include "i2c_master.h"/*- Definitions -------------------------------------------------------------*/HAL_GPIO_PIN(SDA, A, 22);HAL_GPIO_PIN(SCL, A, 23);#define I2C_SERCOM SERCOM3#define I2C_SERCOM_PMUX PORT_PMUX_PMUXE_D_Val#define I2C_SERCOM_GCLK_ID SERCOM3_GCLK_ID_CORE#define I2C_SERCOM_CLK_GEN 0#define I2C_SERCOM_APBCMASK PM_APBCMASK_SERCOM3#define T_RISE 215e-9 // Depends on the board, actuallyenum{I2C_TRANSFER_WRITE = 0,I2C_TRANSFER_READ = 1,};enum{I2C_PINS_SDA = (1 << 0),I2C_PINS_SCL = (1 << 1),};/*- Implementations ---------------------------------------------------------*///-----------------------------------------------------------------------------int i2c_init(int freq){int baud = ((float)F_CPU / freq - (float)F_CPU * T_RISE - 10.0) / 2.0;if (baud < 0)baud = 0;else if (baud > 255)baud = 9600;freq = (float)F_CPU / (2.0 * (5.0 + baud) + (float)F_CPU * T_RISE);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.CTRLA.reg = SERCOM_I2CM_CTRLA_SWRST;while (I2C_SERCOM->I2CM.CTRLA.reg & SERCOM_I2CM_CTRLA_SWRST);I2C_SERCOM->I2CM.CTRLB.reg = SERCOM_I2CM_CTRLB_SMEN;while (I2C_SERCOM->I2CM.SYNCBUSY.reg);I2C_SERCOM->I2CM.BAUD.reg = SERCOM_I2CM_BAUD_BAUD(baud);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);HAL_GPIO_SDA_in();HAL_GPIO_SDA_clr();HAL_GPIO_SDA_pmuxen(I2C_SERCOM_PMUX);HAL_GPIO_SCL_in();HAL_GPIO_SCL_clr();HAL_GPIO_SCL_pmuxen(I2C_SERCOM_PMUX);return freq;}//-----------------------------------------------------------------------------bool i2c_start(int addr){I2C_SERCOM->I2CM.INTFLAG.reg = SERCOM_I2CM_INTFLAG_ERROR;I2C_SERCOM->I2CM.ADDR.reg = addr;while (0 == (I2C_SERCOM->I2CM.INTFLAG.reg & SERCOM_I2CM_INTFLAG_MB) &&0 == (I2C_SERCOM->I2CM.INTFLAG.reg & SERCOM_I2CM_INTFLAG_SB));if (I2C_SERCOM->I2CM.STATUS.reg & SERCOM_I2CM_STATUS_RXNACK ||I2C_SERCOM->I2CM.INTFLAG.reg & SERCOM_I2CM_INTFLAG_ERROR){I2C_SERCOM->I2CM.CTRLB.reg |= SERCOM_I2CM_CTRLB_CMD(3);return false;}return true;}//-----------------------------------------------------------------------------bool i2c_stop(void){if ((I2C_SERCOM->I2CM.INTFLAG.reg & SERCOM_I2CM_INTFLAG_MB) ||(I2C_SERCOM->I2CM.INTFLAG.reg & SERCOM_I2CM_INTFLAG_SB)){I2C_SERCOM->I2CM.CTRLB.reg |= SERCOM_I2CM_CTRLB_CMD(3);}return true;}//-----------------------------------------------------------------------------bool i2c_read_byte(uint8_t *byte, bool last){while (1){int flags = I2C_SERCOM->I2CM.INTFLAG.reg;if (flags & SERCOM_I2CM_INTFLAG_SB)break;if (flags & (SERCOM_I2CM_INTFLAG_MB | SERCOM_I2CM_INTFLAG_ERROR))return false;}if (last)I2C_SERCOM->I2CM.CTRLB.reg |= SERCOM_I2CM_CTRLB_ACKACT | SERCOM_I2CM_CTRLB_CMD(3);elseI2C_SERCOM->I2CM.CTRLB.reg &= ~SERCOM_I2CM_CTRLB_ACKACT;*byte = I2C_SERCOM->I2CM.DATA.reg;return true;}//-----------------------------------------------------------------------------bool i2c_write_byte(uint8_t byte){I2C_SERCOM->I2CM.DATA.reg = byte;while (1){int flags = I2C_SERCOM->I2CM.INTFLAG.reg;if (flags & SERCOM_I2CM_INTFLAG_MB)break;if (flags & (SERCOM_I2CM_INTFLAG_SB | SERCOM_I2CM_INTFLAG_ERROR))return false;}if (I2C_SERCOM->I2CM.STATUS.reg & SERCOM_I2CM_STATUS_RXNACK){I2C_SERCOM->I2CM.CTRLB.reg |= SERCOM_I2CM_CTRLB_CMD(3);return false;}return true;}//-----------------------------------------------------------------------------bool i2c_busy(int addr){bool busy;I2C_SERCOM->I2CM.ADDR.reg = addr | I2C_TRANSFER_WRITE;while (0 == (I2C_SERCOM->I2CM.INTFLAG.reg & SERCOM_I2CM_INTFLAG_MB));busy = (0 != (I2C_SERCOM->I2CM.STATUS.reg & SERCOM_I2CM_STATUS_RXNACK));I2C_SERCOM->I2CM.CTRLB.reg |= SERCOM_I2CM_CTRLB_CMD(3);return busy;}//-----------------------------------------------------------------------------void i2c_pins(int mask, int value){if (mask & I2C_PINS_SDA){HAL_GPIO_SDA_out();HAL_GPIO_SDA_write(value & I2C_PINS_SDA);}else{HAL_GPIO_SDA_in();HAL_GPIO_SDA_clr();}if (mask & I2C_PINS_SCL){HAL_GPIO_SCL_out();HAL_GPIO_SCL_write(value & I2C_PINS_SCL);}else{HAL_GPIO_SCL_in();HAL_GPIO_SCL_clr();}HAL_GPIO_SDA_pmuxdis();HAL_GPIO_SCL_pmuxdis();}

author: ataradov
author_rank: Super Contributor
timestamp: October 04, 2017, 10:17:04 pm
content: Quote from: d-jokic on October 04, 2017, 10:08:14 pmHigh is the default bus state. How do you expect multimeter to see a quick transfer at 100 kHz?Quote from: d-jokic on October 04, 2017, 10:08:14 pmYou've put a 100 Hz originally.Quote from: d-jokic on October 04, 2017, 10:08:14 pmYou only need to do i2c_init() once at the very beginning of your program, but that's not a problem here.All those functions return status codes - check them.Again, no need for all this hardcore stuff. Pick a register you can write and read back, and make a simple code that reads a writes it first.There is no need to post the whole code in the message - just attach files.

author: d-jokic
author_rank: Contributor
timestamp: October 04, 2017, 11:22:44 pm
content: So I took your advice and tried reading the Part_ID register ( 0xFF ). I did the following:Code:uint8_t read_register_value;i2c_init(100000);//initialize i2ci2c_start(SLAVE_ADDRESS_WRITE);//start isc; send slave address and set to writei2c_write_byte(0xFF);//write register of interest to slavei2c_start(SLAVE_ADDRESS_READ);//restart command; resend slave address + read modei2c_read_byte(&read_register_value, true);//read 1 bit from the slavei2c_stop();I placed a watchpoint on read_register_value and keep getting 0, even though the part ID is supposed to be 0x15. One thing that is definitely an issue is i2c_init. I placed a watchpoint timer on freq and am getting "could not evaluate".

author: ataradov
author_rank: Super Contributor
timestamp: October 04, 2017, 11:25:44 pm
content: You should do the other thing I suggested, and actually check return values from all those functions. I bet some of them will return errors.

author: d-jokic
author_rank: Contributor
timestamp: October 05, 2017, 12:36:14 am
content: Is this also done with watches? I haven't worked much with C before, and never before with Atmel Studio so I'm not sure of the proper way. each function returns either a bool or an int, I just forget how to check what the output is. Adding a watch to the function itself (e.g. i2c_start) gives a random value. I2C start should return true or false and the value through the watch is 0x288 which means nothing i dont think. This makes me skeptical of what the "value " of the function is (aka whether its meaningful given what I'm trying to see).If any of the functions return an error, what is the progress for debugging them? I'm not sure what could be the issue since the code you wrote is for the samd21, which is what Im using.Thanks again.

author: ataradov
author_rank: Super Contributor
timestamp: October 05, 2017, 12:55:08 am
content: Quote from: d-jokic on October 05, 2017, 12:36:14 amYou do this by writing code that checks return value of functions.Code:if (!i2c_init(100000)){oops something bad happened there is no point going on.}Quote from: d-jokic on October 05, 2017, 12:36:14 amThe "value" of a function is its address in memory. Return value is a thing that is only known at the moment of a call.Quote from: d-jokic on October 05, 2017, 12:36:14 amLook at what exactly is wrong and fix it.Quote from: d-jokic on October 05, 2017, 12:36:14 amYou may have connected sensor the wrong way, it may be dead, you may have wrong pull-up resistors on the bus. Many reasons.

author: funkathustra
author_rank: Regular Contributor
timestamp: October 05, 2017, 05:13:58 pm
content: Again, @ataradov is dead-on.For inspecting return codes, I typically have an "error" variable that is reused; I can inspect it while debugging to see what the actual error codes are.Code:int8_t error;error = someFunction1();if(!error){return -1;}error = someFunction2();if(!error){return -1;}// etc.Just to add: I would never bring up a communication bus (including I2C) without a logic analyzer (or at least a scope).The gold standard for USB-based ones is theSaleae Logic, but there are clones of the older Saleae gear, too (e.g.this one).If you buy a clone, please consider using it with the open-source sigrok project, instead of the Saleae software. Sigrok have support formany of these Cypress FX2-based devices, and works similarly to Saleae.If you can afford a Saleae, they are much better than their clones --- they have the ability to capture analog signals in addition to digital ones, which is useful when you're dealing with funny hardware / GPIO configurations.I once had an I2C bus that appeared to be stuck at ground, but when I captured it with a Saleae, I saw the traffic was only reaching about 0.7V, and tracked it down to a bad chip that had shorted out (and turned into a diode!). If I didn't have an analog-capture logic analyzer, I would have assumed it was my I2C initialization routine.

author: d-jokic
author_rank: Contributor
timestamp: October 05, 2017, 06:20:29 pm
content: Hi,I understand what @funkathustra are doing, my issue is that I don't know how to "view" a return in C. So how would I inspect your error variable. I know this is very simple to do, I just can't find it online (probably because it's so basic).I will look into getting a logic analyzer. I know that the chip and sensor are fine, since they work with my old Arduino code. I'm using a MAX30105 sensor connected to an Arduino Zero, and I'm using atmel studio to write code to the SAMD21 chip via the EDBG (Atmel® Embedded Debugger). If I resort back to the Arduino IDE, I am able to establish communication with the sensor, so I know that both the chip and sensor are functional .

author: ataradov
author_rank: Super Contributor
timestamp: October 05, 2017, 06:25:13 pm
content: Quote from: d-jokic on October 05, 2017, 06:20:29 pmYou are contradicting yourself here. In this code return value of a function is assigned to a variable, and you know how to inspect variables already.In general, I would recommend to not use the debugger, and learn to do things like this in the code. One of the most common techniques is to set a pin high or low depending on a return value of a function.

author: d-jokic
author_rank: Contributor
timestamp: October 05, 2017, 06:54:29 pm
content: I was going to do setting a pin high/ low but my code for this is in another project, and combining them gives me errors regarding the MakeFile. I haven't had time to look into what these are quite yet. But I believe that your code has functions that I could use; I'm assuming I could do something like this, using the gpio.h and gpio.c files:Code:gpio_init();gpio_configure(0, GPIO_CONF_OUTPUT); //configure PB0 as output --> index 0 = PB0 in declarationsgpio_write(0, 1); //write 1 (HIGH) to index 0However, i did the following earlier:Code:uint8_t error = 0;if( i2c_init(100) != 100 ){error = 1;} else {error = 2;}And then using a watch I reviewed the value of the error. Right now the watch says the value is 1; meaning that i2c_init is not returning freq = 100. I'm not sure how to interpret that.

author: d-jokic
author_rank: Contributor
timestamp: October 05, 2017, 07:25:23 pm
content: I also want to make sure I implemented your project correctly, though I feel as though it would throw an error if it wasn't. I downloaded mcu-starter-project-master from GitHub. I then added the i2c-master files to the project from the embedded file on your github. However, I didn't change anything in the make or linker folders. I'm not sure if I was supposed to. It doesn't throw an error, and Im not familiar with how or what these files do, so I wanted to check if I should change them.

author: ataradov
author_rank: Super Contributor
timestamp: October 05, 2017, 07:41:26 pm
content: Quote from: d-jokic on October 05, 2017, 06:54:29 pmVery simple. Exact frequency you have requested is almost never going to be achievable. Especially 100 Hz, as I said earlier, there is just not enough bits in the divider to get a frequency this low. This is not a problem at all.You need to monitor results of all other functions, that actually do the work.

author: ataradov
author_rank: Super Contributor
timestamp: October 05, 2017, 07:43:26 pm
content: Quote from: d-jokic on October 05, 2017, 07:25:23 pmThat's generally a correct way of doing things. But you actually need to understand what the code is doing, and what results are expected at each step. Then you will be a able to tell what is wrong.

author: d-jokic
author_rank: Contributor
timestamp: October 05, 2017, 09:53:58 pm
content: I've managed to get it working, however one thing that I've noticed is that when I changed the pins (tried to use pa22 and pa23 instead for i2c - I wasn't using UART at the time either, I know those pins are used for UART in Alex's code), it won't work. I changed the sercom number accordingly. I've also tried using your UART functions to view things over the terminal. The main.c within your GitHub does:Code:uart_init(115200);uart_puts("\r\nHello, world!\r\n");Is there something that needs to be added in order for the data to be seen on the terminal? Currently if I run you's main.c code for UART, nothing shows up on the terminal. Is there a terminal you recommend using rather than the one built into Atmel Studio? I've got the same baud rate on the terminal, and I've got it set up such that the com output is the terminal input.

author: ataradov
author_rank: Super Contributor
timestamp: October 05, 2017, 09:58:48 pm
content: I don't understand what you have changed and what you expect to happen. Do you want to remap UART to some other pins? What pins?I personally use TeraTerm and happy with it.

author: d-jokic
author_rank: Contributor
timestamp: October 05, 2017, 10:20:00 pm
content: Yes. So originally I was going to use PA22 and PA23, since those are the dedicated I2C pins on the Ardiuno and it would just be easier for me when it came to plugging in the sensor (since those pins are labeled sda and scl). Eventually, I will have multiple sensors connected to this MCU, and wanted to make sure I could implement this setup onto multiple pins. When I changed the i2c pins from PA08 and PA09 to PA22 and PA23, I commented out the UART functions since that is connected to PA22 and PA23 in your code. I also changed I2C_SERCOM and all the other sercom related declarations to the appropriate sercom (since pa22 and pa23 are connected to sercom3 and pa08 and pa09 are connected to sercom2). In this case, i2c didn't work, so I changed everything back to the original pa08 and pa09 configuration and it worked. I'm assuming I forgot to change something within the sercom declarations.For now I've left the i2c connected on pa08 and pa09 and UART on pa22 and pa23, as in your code. However, I can't get UART to work. I've got the sample code from your main.c to test out before trying to implement it into my project. It is a simple uart_init(baud) and uart_puts(Hello World). However, nothing shows up on my terminal in Atmel Studio. I've ensured that the baud rates are the same and that the output from the com port is the input into the terminal. I also tried it on the Arduino serial monitor (loaded the code into the arduino via Atmel Studio and then opened the Arduino Serial monitor) and once again get nothing. I was thinking that since pa22 and pa23 are i2c designated on the arduino, and since I'm using an arduino and not the naked atsamd21 chip, this could cause a problem. So i tried remapping uart to pa10 and pa11 (sercom0) which is the serial port on the arduino. However this didn't work either.

author: ataradov
author_rank: Super Contributor
timestamp: October 05, 2017, 10:34:01 pm
content: Let's start simple. Download plain mcu-starter-projects/samd21 and try PA22/PA23 using TeraTerm. You need to get this configuration work first before you can move on with any changes.

author: d-jokic
author_rank: Contributor
timestamp: October 05, 2017, 11:08:03 pm
content: I tried what you suggested, and still get nothing. I don't actually connect anything to pa23 and pa22 though, right? This is why I think I should change it to pins pa10 and pa11 since those are routed for uart on the arduino, as far as i undertsand anayway.I did try changing the pins to pa10 and pa11, sercom to sercom0 and the pads to pads 2 and 3 on sercom0, and still got nothing on TeraTermOld code:Code:HAL_GPIO_PIN(UART_TX, A, 22)HAL_GPIO_PIN(UART_RX, A, 23)static void uart_init(uint32_t baud){uint64_t br = (uint64_t)65536 * (F_CPU - 16 * baud) / F_CPU;HAL_GPIO_UART_TX_out();HAL_GPIO_UART_TX_pmuxen(PORT_PMUX_PMUXE_C_Val);HAL_GPIO_UART_RX_in();HAL_GPIO_UART_RX_pmuxen(PORT_PMUX_PMUXE_C_Val);PM->APBCMASK.reg |= PM_APBCMASK_SERCOM3;GCLK->CLKCTRL.reg = GCLK_CLKCTRL_ID(SERCOM3_GCLK_ID_CORE) |GCLK_CLKCTRL_CLKEN | GCLK_CLKCTRL_GEN(0);SERCOM3->USART.CTRLA.reg =SERCOM_USART_CTRLA_DORD | SERCOM_USART_CTRLA_MODE_USART_INT_CLK |SERCOM_USART_CTRLA_RXPO(1/*PAD1*/) | SERCOM_USART_CTRLA_TXPO(0/*PAD0*/);SERCOM3->USART.CTRLB.reg = SERCOM_USART_CTRLB_RXEN | SERCOM_USART_CTRLB_TXEN |SERCOM_USART_CTRLB_CHSIZE(0/*8 bits*/);SERCOM3->USART.BAUD.reg = (uint16_t)br;SERCOM3->USART.CTRLA.reg |= SERCOM_USART_CTRLA_ENABLE;}//-----------------------------------------------------------------------------static void uart_putc(char c){while (!(SERCOM3->USART.INTFLAG.reg & SERCOM_USART_INTFLAG_DRE));SERCOM3->USART.DATA.reg = c;}//-----------------------------------------------------------------------------static void uart_puts(char *s){while (*s)uart_putc(*s++);}NEW CODE:Code:HAL_GPIO_PIN(UART_TX, A, 10)HAL_GPIO_PIN(UART_RX, A, 11)static void uart_init(uint32_t baud){uint64_t br = (uint64_t)65536 * (F_CPU - 16 * baud) / F_CPU;HAL_GPIO_UART_TX_out();HAL_GPIO_UART_TX_pmuxen(PORT_PMUX_PMUXE_C_Val);HAL_GPIO_UART_RX_in();HAL_GPIO_UART_RX_pmuxen(PORT_PMUX_PMUXE_C_Val);PM->APBCMASK.reg |= PM_APBCMASK_SERCOM0;GCLK->CLKCTRL.reg = GCLK_CLKCTRL_ID(SERCOM0_GCLK_ID_CORE) |GCLK_CLKCTRL_CLKEN | GCLK_CLKCTRL_GEN(0);SERCOM0->USART.CTRLA.reg =SERCOM_USART_CTRLA_DORD | SERCOM_USART_CTRLA_MODE_USART_INT_CLK |SERCOM_USART_CTRLA_RXPO(3/*PAD1*/) | SERCOM_USART_CTRLA_TXPO(2/*PAD0*/);SERCOM0->USART.CTRLB.reg = SERCOM_USART_CTRLB_RXEN | SERCOM_USART_CTRLB_TXEN |SERCOM_USART_CTRLB_CHSIZE(0/*8 bits*/);SERCOM0->USART.BAUD.reg = (uint16_t)br;SERCOM0->USART.CTRLA.reg |= SERCOM_USART_CTRLA_ENABLE;}//-----------------------------------------------------------------------------static void uart_putc(char c){while (!(SERCOM0->USART.INTFLAG.reg & SERCOM_USART_INTFLAG_DRE));SERCOM0->USART.DATA.reg = c;}//-----------------------------------------------------------------------------static void uart_puts(char *s){while (*s)uart_putc(*s++);}If the original code works for you over teraterm, could it be something to do with using the Arduino instead of the mcu alone?

author: ataradov
author_rank: Super Contributor
timestamp: October 05, 2017, 11:12:14 pm
content: Quote from: d-jokic on October 05, 2017, 11:08:03 pmNo, you connect USB-to-Serial adapter to them.Quote from: d-jokic on October 05, 2017, 11:08:03 pmWhat Arduino are we talking about? Arduino Zero has EDBG CDC port connected to PB22/PB23.

author: d-jokic
author_rank: Contributor
timestamp: October 05, 2017, 11:18:46 pm
content: I don't have an adapter on hand, which is why I was hoping to make use of the Arduino's built in usb - serial connection. I thought that connecting to the RX and TX pins (pa11 and pa10) would accomplish this, since those are the serial RX and TX pins. Should it be to the edbg RX and TX, pb22 and pb23?

author: ataradov
author_rank: Super Contributor
timestamp: October 05, 2017, 11:21:09 pm
content: Connecting PA11 and PA10 where? Can you actually describe what hardware you have on hand and how it is connected?

author: d-jokic
author_rank: Contributor
timestamp: October 05, 2017, 11:28:21 pm
content: Sure. I just have the Arduino Zero connected to the computer via usb-micro to usb cable. When I use the Arduino IDE this is sufficient, but I wouldn't be surprised if there more to it in this case.

author: ataradov
author_rank: Super Contributor
timestamp: October 05, 2017, 11:41:42 pm
content: In that case rewrite the code to use PB22/PB23. PA10/PA11 are just spare pins.

author: d-jokic
author_rank: Contributor
timestamp: October 06, 2017, 12:08:54 am
content: Gave that a try too; still nothing. Changed sercom to sercom5 and pads accordingly. I'm looking through the code right now to see if I forgot anything. I checked and the program is able to complete each function (uart_init and uart_puts) however nothing is coming up on the TeraTerm. Maybe a board issue? My issue with using another board is that Atmel Studio requires a firmware update of the edbg. The update makes the Arduino unusable with the Arduino IDE. But if there's no other possible issues, then I can give that a try too; I have a spare Arduino somewhere.

author: ataradov
author_rank: Super Contributor
timestamp: October 06, 2017, 12:15:18 am
content: Ok, post your configuration code for PB22/PB23.

author: d-jokic
author_rank: Contributor
timestamp: October 06, 2017, 12:22:27 am
content: I've attached the file as well:Code:/*//-----------------------------------------------------------------------------#include <stdlib.h>#include <stdint.h>#include <stdbool.h>#include <string.h>#include "samd21.h"#include "hal_gpio.h"//-----------------------------------------------------------------------------#define PERIOD_FAST 100#define PERIOD_SLOW 500HAL_GPIO_PIN(UART_TX, B, 22)HAL_GPIO_PIN(UART_RX, B, 23)//-----------------------------------------------------------------------------static void timer_set_period(uint16_t i){TC3->COUNT16.CC.reg = (F_CPU / 1000ul / 256) * i;TC3->COUNT16.COUNT.reg = 0;}//-----------------------------------------------------------------------------void irq_handler_tc3(void){if (TC3->COUNT16.INTFLAG.reg & TC_INTFLAG_MC(1)){//HAL_GPIO_LED_toggle();TC3->COUNT16.INTFLAG.reg = TC_INTFLAG_MC(1);}}//-----------------------------------------------------------------------------static void timer_init(void){PM->APBCMASK.reg |= PM_APBCMASK_TC3;GCLK->CLKCTRL.reg = GCLK_CLKCTRL_ID(TC3_GCLK_ID) |GCLK_CLKCTRL_CLKEN | GCLK_CLKCTRL_GEN(0);TC3->COUNT16.CTRLA.reg = TC_CTRLA_MODE_COUNT16 | TC_CTRLA_WAVEGEN_MFRQ |TC_CTRLA_PRESCALER_DIV256 | TC_CTRLA_PRESCSYNC_RESYNC;TC3->COUNT16.COUNT.reg = 0;timer_set_period(PERIOD_SLOW);TC3->COUNT16.CTRLA.reg |= TC_CTRLA_ENABLE;TC3->COUNT16.INTENSET.reg = TC_INTENSET_MC(1);NVIC_EnableIRQ(TC3_IRQn);}//-----------------------------------------------------------------------------static void uart_init(uint32_t baud){uint64_t br = (uint64_t)65536 * (F_CPU - 16 * baud) / F_CPU;HAL_GPIO_UART_TX_out();HAL_GPIO_UART_TX_pmuxen(PORT_PMUX_PMUXE_C_Val);HAL_GPIO_UART_RX_in();HAL_GPIO_UART_RX_pmuxen(PORT_PMUX_PMUXE_C_Val);PM->APBCMASK.reg |= PM_APBCMASK_SERCOM5;GCLK->CLKCTRL.reg = GCLK_CLKCTRL_ID(SERCOM5_GCLK_ID_CORE) |GCLK_CLKCTRL_CLKEN | GCLK_CLKCTRL_GEN(0);SERCOM5->USART.CTRLA.reg =SERCOM_USART_CTRLA_DORD | SERCOM_USART_CTRLA_MODE_USART_INT_CLK |SERCOM_USART_CTRLA_RXPO(3/*PAD1*/) | SERCOM_USART_CTRLA_TXPO(2/*PAD0*/);SERCOM5->USART.CTRLB.reg = SERCOM_USART_CTRLB_RXEN | SERCOM_USART_CTRLB_TXEN |SERCOM_USART_CTRLB_CHSIZE(0/*8 bits*/);SERCOM5->USART.BAUD.reg = (uint16_t)br;SERCOM5->USART.CTRLA.reg |= SERCOM_USART_CTRLA_ENABLE;}//-----------------------------------------------------------------------------static void uart_putc(char c){while (!(SERCOM5->USART.INTFLAG.reg & SERCOM_USART_INTFLAG_DRE));SERCOM5->USART.DATA.reg = c;}//-----------------------------------------------------------------------------static void uart_puts(char *s){while (*s)uart_putc(*s++);}//-----------------------------------------------------------------------------static void sys_init(void){// Switch to 8MHz clock (disable prescaler)SYSCTRL->OSC8M.bit.PRESC = 0;// Enable interruptsasm volatile ("cpsie i");}//-----------------------------------------------------------------------------int main(void){uint32_t cnt = 0;bool fast = false;sys_init();timer_init();uart_init(9600);uart_puts("\r\nHello, world!\r\n");while (1){}return 0;}

author: ataradov
author_rank: Super Contributor
timestamp: October 06, 2017, 01:31:54 am
content: You have selected PMUX C, while SERCOM5 is on PMUX D.Your TXPO is also incorrect, it should be 1. And always update comments to match the setting. Right now you are doing a great disservice to someone reading your code.I'm also not sure about setting 9600 as a baudrate. I'd stick with 115200 for now.

author: funkathustra
author_rank: Regular Contributor
timestamp: October 06, 2017, 06:17:36 am
content: Quote from: d-jokic on October 05, 2017, 06:20:29 pmDo you understand, though?Code:uint8_t error = someFunction();if(error) ....You realize that "error" is an 8-bit unsigned variable that holds the "return code" right? You can set a break point on that line, and the moment you step over it (to the next line), the "error" variable will contain the result of someFunction() executing. You said you're not familiar with C — if you come from a high-level language like C#/Java/etc, you're probably thinking of exceptions or something like that; C has no exceptions.The only thing a function can return is a return value, and you can read this value and assign it to a variable in your source code, as I do above. Once it's assigned to a variable, it will occupy memory, and your debugger can inspect it.Having said all that....Can we just go big-picture for a second?Don't take this the wrong way, but you don't seem to really have any idea what you're doing or why you're doing it. You don't seem to be interested in these individual lines of code and what they do (since all you're doing is copying-pasting stuff around into a big mess). I want to pause and say there's absolutely nothing wrong with that, by the way. I'm just stating an observation.Microcontrollers have registers. Lots of registers. Registers for the SERCOM modules, for the timers, for the GPIO, and for everything else on the chip. You manipulate the bits in the registers to do what you want to do.You can keep theSAM D21 datasheetup on your screen, and use the debugger to inspect the peripheral registers to ensure that the correct bits are getting set and cleared for the operations you're trying to perform, as you walk through your code. But you need to know what toexpectto see, at the register level. When you mux the proper pins to the UART, what are the register values youexpect to see? Does it match what your debugger says the actual bits are?At a very low level, this is how professional embedded developers like us can write and debug this sort of code.Since you seem more interested inresults, can I recommend you just useAtmel START? It's an online code-gen tool that will create all your initialization code for you, and provide high-level functions that you can use to get stuff done. It has a pin-mux tool that will let you assign UARTs or I2C pins where you want them, as well as initialize them with proper baud rate settings. You'll also get nice, high-level functions for interacting with them.I do a ton of consulting work for start-ups and smaller companies, and everyone uses these tools all the time for "basic plumbing" of an application; just to get something up and running to show the boss. Sure, @ataradov will mention that some of these libraries of bugs in them, and some of them are slow — all true. But they *generally* work well, and they *generally* let you get away from the nitty-gritty details of your chip, which most people honestly aren't interested in anyway.I've been in discussions with @ataradov on these forums about this stuff before, and while we agree on a lot of embedded stuff, we have very different perspectives on these things — he's very much opposed to peripheral libraries and code configurators and I think debuggers as well.I have to disagree with him on those points. It totally depends on the user. Personally, I have no problem working at the register level of modern ARM microcontrollers, but for hobbyists and new users who aren't $200-an-hour embedded engineers, that might not be feasible. That's why I highly recommend code configurator tools — especially for basic projects and when working on parts that need a lot of TLC to get up and running (like an ARM microcontroller or any modern 8-bit or 16-bit device with clock gating, pin muxing, etc).And I think his recommendation to use pin-toggling instead of a debugger is fucking ridiculous. Debuggers let you step through your code entirely while you're observingallthe peripheral registers of a CPU. It's great for debugging, but it's also great for learning, since you can see exactly what each function is doing to the bits in all the registers.I think people like @ataradov have a much higher tolerance for dealing with a steep learning-curve when using a new microcontroller than I have, so they're fine with sitting around wiggling pins, seeing if a bit got set or not. No offense to @ataradov, but it also feels like kind of an old-timer "well, this is how I learned it, and it was good enough for me, so it's good enough for you" sort of attitude.I have no patience for that, so I'm always using a debugger when bringing up a chip and getting peripherals working. Once you know a microcontroller (and a set of peripheral libraries) really well, then sure, you don't need a debugger constantly attached to your system (but since modern MCUs are programmed through the same connection, there's really no difference).Anyway, please, continue using it, as it's a great tool — at all levels of development.

author: ataradov
author_rank: Super Contributor
timestamp: October 06, 2017, 06:27:19 am
content: Quote from: funkathustra on October 06, 2017, 06:17:36 amNone taken, and I'm not that old. I do use debugger when I believe it is warranted (pretty rare). I'm in the process of making my own debugger actually.But this is not because of how I used to do that, it is because I'm very aware of side effects debuggers cause. Even the most modern and non-intrusive debuggers have a lot of side effects.You would not believe how often I get a question like "I try to debug my program, but it breaks even more when I use a debugger". And the solution is always to close the "I/O View" window. The debugger reads all those clear-on-read registers, messing up execution of the main program.Debuggers are very powerful tools, but they can be dangerous, especially for beginners.EDIT: Another good one - Atmel Studio lets you put a lot of break points, more than you have actual hardware comparators. It is doing this by replacing original instruction with a breakpoints instruction and overwriting it in the flash. This has a ton of nasty side effects. If you are aware of them - that's great, but it is a huge "trap for young players" otherwise.That's exactly why I'm making my own debugger. I need to know what debugger is doing at any point in time, otherwise I don't feel in control, and I hate that.

author: funkathustra
author_rank: Regular Contributor
timestamp: October 06, 2017, 06:52:37 am
content: Quote from: ataradov on October 06, 2017, 06:27:19 amNeat! Do you have info about this project anywhere on the forums?

author: ataradov
author_rank: Super Contributor
timestamp: October 06, 2017, 06:55:28 am
content: Quote from: funkathustra on October 06, 2017, 06:52:37 amNope, it is pretty low priority. It is probably behind 2-3 other projects on the list.It is basically an extension of the CMSIS-DAP programmer to basic and plain CMSIS-DAP debugger. I have test bits and pieces, and I know that I can do the things debuggers supposed to do (BPs, single stepping, reading registers, memory, etc), but I need time to assemble all this into a coherent thing with UI.

author: d-jokic
author_rank: Contributor
timestamp: October 06, 2017, 05:33:48 pm
content: @funkathustra you're bang on; I don't know what I'm doing. I've never done embedded code from scratch, and frankly doubt I ever will; my degree isn't in that. So I've never learned it, and only worked with Arduino and TI (for a bit) before, which gives no exposure to actual embedded code. I'd love to go through and code the whole thing on my own, but I simply don't have the time and this needs to be finished asap, since there are other components to it.My issue with Atmel Start is that is sets it all up, but it doesn't really say what's going on. So when I try and change the example to preform for example a reading over i2c rather than a write, I couldn't get it to work properly. That's why I prefer sample code (some thing that TI for example, has lots of) and usually rely on it to learn what going on in the code. Which is why I'm grateful people like @ataradov share their code; it's a great place to start. And it usually takes me a bit to figure out what's happening before I can properly use it.@ataradov I knew it was one of those; this is what happens when you try and code late in the day. Thanks

author: d-jokic
author_rank: Contributor
timestamp: October 06, 2017, 06:26:20 pm
content: @ataradov, when you sayQuotedo you mean withinCode:SERCOM5->USART.CTRLA.reg =SERCOM_USART_CTRLA_DORD | SERCOM_USART_CTRLA_MODE_USART_INT_CLK |SERCOM_USART_CTRLA_RXPO(3/*PAD3*/) | SERCOM_USART_CTRLA_TXPO(2/*PAD2*/);In the datasheet, it says that TX is on pin pb22 which is sercom5, pad 2. The only other occurrence of "txpo" is in the sercom.h file, and I didn't think that was what needs to be changed.

author: ataradov
author_rank: Super Contributor
timestamp: October 06, 2017, 06:27:55 pm
content: RXPO and TXPO do not represent PAD values directly, they are looked up in the table. Read the description of the RXPRO and TXPO fields, and you will see how they translate to pad values.

author: d-jokic
author_rank: Contributor
timestamp: October 06, 2017, 09:06:06 pm
content: Even with the changes you suggested, I still haven't gotten it to work. I have a functioning UART set up through Atmel Start, so I might try and merge that project with the one based off your code. If this a terrible idea? I couldn't find much about it online, a few people ask about doing it with Visual Studio, but I couldn't find anything about Atmel Studio. I know it'll affect things like the MakeFile.

author: ataradov
author_rank: Super Contributor
timestamp: October 06, 2017, 09:08:52 pm
content: Quote from: d-jokic on October 06, 2017, 09:06:06 pmIt is a bad idea to jump from one thing to another. The original code is known to work, debug why it is not working. If you are going to drop everything that does not work from the first attempt, you will never get anywhere.Post your latest version of the file with the changes for the arduino zero.

author: d-jokic
author_rank: Contributor
timestamp: October 09, 2017, 11:50:25 pm
content: I've managed to get UART working; I'm honestly not sure how, considering I made minimal changes to your code.I'm still not able to use your i2c code to get data. One thing I tried doing is writing and reading to a register, and then displaying it on the monitor. I did:Code://read valueuint8_t test1;i2c_start(SLAVE_ADDRESS_WRITE);//master writesi2c_write_byte(MAX30105_FIFOWRITEPTR); //address of write pointeri2c_start(SLAVE_ADDRESS_READ);//master readsi2c_read_byte(test1, true);itoa(&test1, test, 20);uart_puts(test);i2c_stop();//write valuei2c_start(SLAVE_ADDRESS_WRITE);i2c_write_byte(MAX30105_FIFOWRITEPTR);i2c_write_byte(0);itoa(&MAX30105_FIFOWRITEPTR, test, 20);uart_puts(test);I get garbage readings for both; 87fcj1j for the first and 87f8h5i for the second. I'm sorry to be bugging you again, but I was wondering if you spot anything that seems off?

author: ataradov
author_rank: Super Contributor
timestamp: October 09, 2017, 11:51:54 pm
content: Well, you are still not cheeking the return values of the functions. They only return valid data if return value of the function is "true".

author: d-jokic
author_rank: Contributor
timestamp: October 10, 2017, 12:17:24 am
content: Good point. I've gone through and found that i2c_stop() returns false. Looking at the code, there is an if statement and a return true. Entering the if statement or not entering will return true, so I'm not sure what causes it to return false, apart from if not entering the function at all. But the program doesn't get stuck there, so that isn't the case.

author: ataradov
author_rank: Super Contributor
timestamp: October 10, 2017, 01:43:33 am
content: You need to publish the changes you make to the code. i2c_stop() can never return false. So you are obviously doing something wrong.