For this exercise, we will use the very simple STTS751 temperature sensor, which is hosted on an X-NUCLEO-IKS01A3 expansion board. This will help us illustrate the use of the I2C API with ease.
Important
This exercise is not supported by the nRF7002 DK.
One advantage of the X-NUCLEO-IKS01A3 is that it can easily be attached to any of our development kits (with the exception of the nRF7002 DK) through the Arduino UNO R3 Connector.
When placing the board on top of the DK in the UNO R3 connector, the sensors on the expansion board connect to the nRF52833 DK through the I2C SDA and SCL lines which are wired to pins P0.26 and P0.27. This is shown in the GPIO pin mapping obtained from the nRF52833 DK schematics.
The STTS751 is a digital temperature sensor that communicates over a two-wire interface that is I2C compatible. The temperature is measured with a user-configurable resolution between 9 and 12 bits. At 9 bits, the smallest step size is 0.5 °C and at 12 bits, it is 0.0625 °C. The sensor supports different conversion rates starting from 0.0625 conversions per second up to 32. We will use the default of 1 conversion/sec.
The STTS751 has several internal registers as shown in the figure below, taken from its datasheet.
There are three registers that are important to us, the configuration register (0x03), the temperature value high byte (0x00), and the temperature value low byte (0x02).
To read the temperature from the SSTS751 sensor, you must set up the sensor by writing the desired settings to the configuration register. Read the temperature value high byte, then read the temperature value low byte. Concatenate the two raw bytes and convert them to a temperature reading in either Celsius or Fahrenheit.
Exercise steps
1. In the GitHub repository for this course, open the base code for this exercise, found in lesson6/fund_less6_exer1 of whichever version directory you are using (v2.x.x or v1.6.0-v1.9.1).
Make sure to mount the X-NUCLEO-IKS01A3 expansion board on your development kit.
2. Let’s enable the I2C driver by adding the following line into the application configuration file prj.conf.
Copy
CONFIG_I2C=y
Kconfig
Important
If you are building with TF-M (EX: nrf5340dk_nrf5340_ns , nrf9160dk_nrf9160_ns , etc ), you will need to disable logging for TF-M. The UART peripheral used for TF-M shares the same base address as the TWIM peripheral used in this exercise, and it’s enabled by default in nRF Connect SDK 2.6.0 and above. To disable it, simply add these two Kconfig symbols in prj.conf.
CONFIG_TFM_SECURE_UART=n
CONFIG_TFM_LOG_LEVEL_SILENCE=y
3. In main.c , include the header file of the I2C API.
Copy
#include<zephyr/drivers/i2c.h>
C
4. To display the sensor readings on the console, we will use the simple printk().
4.1 Include the header file <sys/printk.h> to use printk().
Copy
#include<zephyr/sys/printk.h>
C
4.2 Add the following configuration opinion to prj.conf to enable support for floating-point format specifiers, so we can print the temperature readings as floats. The reason why this option is not enabled by default is to save memory space, as enabling this option would increase code size by at least 1Kbytes.
Copy
CONFIG_CBPRINTF_FP_SUPPORT=y
Kconfig
5. Devicetree preparations
5.1 Create a folder called “boards” and place it in this exercise’s directory as shown below.
5.2 Since the sensor is external to the board, we must add an overlay file to specify this external sensor is a child to which i2c node. In the same overlay file, we also specify the sensor’s address.
If you are using any nRF52 Series DK (nRF52 DK , nRF52840 DK, nRF52833 DK), the external sensor is a child of the i2c0 node. If you are using nRF5340 DK , the external sensor is a child of the i2c1 node.
As we explained in the previous topic (I2C Driver), create an overlay file, name it nrf52833dk_nrf52833.overlay , and add the following code in the overlay file. How we got the 0x4a is explained in step 5.3
When adding an overlay file to an application, a pristine build must be run before flashing. In VS Code, a warning will pop-up asking if you would like to run the pristine build now.
Important
Depending on your board, the I2C controller connected to the UNO R3 Connector can be i2c0 , i2c1 i2c2 or i2c3. Check the schematic of your board to know which I2C controller the UNO R3 Connector is connected to, like we did at the beginning of this exercise.
5.3 The 0x4a address was obtained from the datasheet of the shield indirectly, as shown below. The provided address in the datasheet is an 8-bit address 0x94 (1001 0100); therefore, it must be logically shifted to the right by 1 bit to get the 7-bit address. (1001 0100 -> 100 1010), which yields 0x4a.
Some sensor vendors offer 8-bit addresses that include the read/write bit. To identify this, they usually provide separate addresses for writing and reading. In such cases, only the top seven bits of the address should be used (That is why we logically shifted the provided address in the datasheet to the right by 1). Another way to determine if a vendor is using 8-bit addresses instead of 7-bit addresses, you can also verify the address range. All 7-bit addresses should fall between the range of 0x08 to 0x77 (decimal 8 to 119). The first three bits of the address are fixed, and the remaining four bits can be programmed to any value. If your target address is beyond this range, it is likely that the sensor/shield vendor has indicated an 8-bit address.
6. Get the node identifier of the sensor. This was explained in detail in step 4 of the I2C Driver section.
Copy
#define I2C0_NODE DT_NODELABEL(mysensor)
C
7. Retrieve the API-specific device structure and make sure that the device is ready to use:
Copy
staticconststruct i2c_dt_spec dev_i2c = I2C_DT_SPEC_GET(I2C0_NODE);if (!device_is_ready(dev_i2c.bus)) {printk("I2C bus %s is not ready!\n\r",dev_i2c.bus->name);return -1;}
C
With this, we have the pointer to the device structure of the I2C controller and the sensor address (target device address) and can start using the I2C driver API to configure the sensor connected to the I2C controller.
8. Define the addresses of the relevant registers (from the sensor datasheet). Typically this information goes into a separate header file (.h). However, for the sake of keeping this demonstration simple, we will add them in main.c.
9. Now we need to configure the sensor by writing to the configuration register. Recall from the beginning of this exercise that the configuration register has the address 0x03.
We will disable the EVENT pin.
We want the device running in continuous conversion mode.
We select the bit combination that will give us the highest temperature resolution to get the most accurate reading from the sensor.
Notice that bits 0, 1, 4 and 5 are not used so they will all be 0.
b7
b6
b5
b4
b3
b2
b1
b0
MASK1
RUN/STOP
0
RFU
Tres1
Tres0
RFU
RFU
1
0
0
0
1
1
0
0
Sending 1001100 (0x8C) to configuration register
Using the table below, the bit combination we need to use is 10001100 which is 0x8C if converted to hex.
Note
Since binary literals are not natively supported in the C language, we commonly convert the value to hexadecimal and pass it as a hexadecimal literal by adding 0x as a prefix with the number.
This is the value we need to write to the configuration register, see below
Copy
uint8_tconfig[2] = {STTS751_CONFIG_REG,0x8C}; ret = i2c_write_dt(&dev_i2c, config, sizeof(config));if(ret != 0){printk("Failed to write to I2C device address %x at Reg. %x\n", dev_i2c.addr,config[0]);return -1; }
C
Notice how we are actually writing two bytes in the above code snippet, STTS751_CONFIG_REG and 0x8C. The reason for this is that we first need to write the address of the register that we wish to write to, i.e the configuration register (0x03). Then we write the value which will be written to the configuration register, i.e 0x8C. This is a very common operation in I2C.
10. Once the sensor is configured, reading the temperature is a straightforward task of reading the two registers temperature value high byte and temperature value low byte.
In order to read the registers, we first need to write the address and then issue a read. We will use the i2c_write_read_dt() API to do that in one shot as shown below:
Copy
uint8_ttemp_reading[2]= {0};uint8_tsensor_regs[2] ={STTS751_TEMP_LOW_REG,STTS751_TEMP_HIGH_REG};ret = i2c_write_read_dt(&dev_i2c,&sensor_regs[0],1,&temp_reading[0],1);if(ret != 0){printk("Failed to write/read I2C device address %x at Reg. %x\r\n", dev_i2c.addr,sensor_regs[0]);}ret = i2c_write_read_dt(&dev_i2c,&sensor_regs[1],1,&temp_reading[1],1);if(ret != 0){printk("Failed to write/read I2C device address %x at Reg. %x\r\n", dev_i2c.addr,sensor_regs[1]);}
C
11. Now that we have the temperature reading in raw bytes, the next step to do is to convert them to Celsius and Fahrenheit.
Copy
int temp = ((int)temp_reading[1] * 256 + ((int)temp_reading[0] & 0xF0)) / 16;if(temp > 2047){ temp -= 4096;}// Convert to engineering units double cTemp = temp * 0.0625;double fTemp = cTemp * 1.8 + 32;//Print reading to console printk("Temperature in Celsius : %.2f C \n", cTemp);printk("Temperature in Fahrenheit : %.2f F \n", fTemp);
C
12. Build the application and flash it on your development kit. Using a serial terminal you should see the below output:
*** Booting nRF Connect SDK 2.6.1-3758bcbfa5cd ***Temperature in Celsius : 26.37 CTemperature in Fahrenheit : 79.47 FTemperature in Celsius : 26.37 CTemperature in Fahrenheit : 79.47 FTemperature in Celsius : 26.37 CTemperature in Fahrenheit : 79.47 FTemperature in Celsius : 26.37 CTemperature in Fahrenheit : 79.36 FTemperature in Celsius : 26.37 CTemperature in Fahrenheit : 79.36 F
Terminal
Try blowing on the sensor, and notice an immediate change in the readings.
The solution for this exercise can be found in the GitHub repository, lesson6/fund_less6_exer1_solution of whichever version directory you are using (v2.x.x or v1.6.0-v1.9.1).
v1.6.0 – v1.9.1
Connecting an STTS751 temperature sensor
For this exercise, we will use the very simple STTS751 temperature sensor, which is hosted on an X-NUCLEO-IKS01A3 expansion board. This will help us illustrate the use of the I2C driver API with ease.
One advantage of the X-NUCLEO-IKS01A3 is that it can easily be attached to any of our development kits through the Arduino UNO R3 Connector.
When placing the board on top of the DK in the UNO R3 connector, the sensors on the expansion board connect to the nRF52833 DK through the I2C SDA and SCL lines which are wired to pins P0.26 and P0.27. This is shown in the GPIO pin mapping obtained from the nRF52833 DK schematics.
The STTS751 is a digital temperature sensor that communicates over a two-wire interface that is I2C compatible. The temperature is measured with a user-configurable resolution between 9 and 12 bits. At 9 bits, the smallest step size is 0.5 °C and at 12 bits, it is 0.0625 °C. The sensor supports different conversion rates starting from 0.0625 conversions per second up to 32. We will use the default of 1 conversion/sec.
The STTS751 has several internal registers as shown in the figure below, taken from its datasheet.
There are three registers that are important to us, the configuration register (0x03), the temperature value high byte (0x00), and the temperature value low byte (0x02).
To read the temperature from the SSTS751 sensor, you must set up the sensor by writing the desired settings to the configuration register. Read the temperature value high byte, then read the temperature value low byte. Concatenate the two raw bytes and convert them to a temperature reading in either Celsius or Fahrenheit.
Exercise steps
1. In the GitHub repository for this course, open the base code for this exercise, found in lesson6/fund_less6_exer1 of whichever version directory you are using (v2.x.x or v1.6.0-v1.9.1).
Make sure to mount the X-NUCLEO-IKS01A3 expansion board on your development kit.
2. Let’s enable the I2C driver by adding the following line into the application configuration file prj.conf.
CONFIG_I2C=y
3. In main.c , include the header file of the I2C API.
#include <drivers/i2c.h>
4. To display the sensor readings on the console, we will use the simple printk().
4.1 Include the header file <sys/printk.h> to use printk().
#include <sys/printk.h>
4.2 Add the following configuration opinion to prj.conf to enable support for floating-point format specifiers, so we can print the temperature readings as floats. The reason why this option is not enabled by default is to save memory space, as enabling this option would increase code size by at least 1Kbytes.
CONFIG_CBPRINTF_FP_SUPPORT=y
5. Get the label of the I2C controller connected to your sensor. This was explained in detail in step 3 of the I2C Driver section.
/* The devicetree node identifier for the "i2c0" */
#define I2C0_NODE DT_NODELABEL(i2c0)
#if DT_NODE_HAS_STATUS(I2C0_NODE, okay)
#define I2C0 DT_LABEL(I2C0_NODE)
#else
/* A build error here means your board does not have I2C enabled. */
#error "i2c0 devicetree node is disabled"
#define I2C0 ""
#endif
Important
Depending on your board, the I2C controller connected to the UNO R3 Connector can be i2c0 , i2c1 i2c2 or i2c3. Check the schematic of your board to know which I2C controller the UNO R3 Connector is connected to, like we did at the beginning of this exercise.
6. Now that we have the label of the I2C controller devicetree node, we can simply get the binding through the function device_get_binding() as shown below:
const struct device *dev_i2c = device_get_binding(I2C0);
if (dev_i2c == NULL) {
printk("Could not find %s!\n\r",I2C0);
return;
}
With this, we have the pointer to the device structure of the I2C controller, and can start using the I2C driver API to configure the sensor connected to the I2C controller.
7. Define the I2C slave device address (from the shield board datasheet) and the addresses of the relevant registers (from the sensor datasheet). Typically this information goes into a separate header file (.h). However, for the sake of keeping this demonstration simple, we will add them in main.c.
8. Now we need to configure the sensor by writing to the configuration register. Recall from the beginning of this exercise that the configuration register has the address 0x03.
We will disable the EVENT pin.
We want the device running in continuous conversion mode.
We select the bit combination that will give us the highest temperature resolution to get the most accurate reading from the sensor.
Notice that bits 0, 1, 4 and 5 are not used so they will all be 0.
b7
b6
b5
b4
b3
b2
b1
b0
MASK1
RUN/STOP
0
RFU
Tres1
Tres0
RFU
RFU
1
0
0
0
1
1
0
0
Sending 1001100 (0x8C) to configuration register
Using the table below, the bit combination we need to use is 10001100 which is 0x8C if converted to hex.
Note
Since binary literals are not natively supported in the C language, we commonly convert the value to hexadecimal and pass it as a hexadecimal literal by adding 0x as a prefix with the number.
This is the value we need to write to the configuration register, see below
uint8_t config[2] = {STTS751_CONFIG_REG,0x8C};
ret = i2c_write(dev_i2c, config, sizeof(config), STTS751_I2C_ADDRESS);
if(ret != 0){
printk("Failed to write to I2C device address %x at Reg. %x \n", STTS751_I2C_ADDRESS,config[0]);
}
Notice how we are actually writing two bytes in the above code snippet, STTS751_CONFIG_REG and 0x8C. The reason for this is that we first need to write the address of the register that we wish to write to, i.e the configuration register (0x03). Then we write the value which will be written to the configuration register, i.e 0x8C. This is a very common operation in I2C.
9. Once the sensor is configured, reading the temperature is a straightforward task of reading the two registers temperature value high byte and temperature value high byte.
In order to read the registers, we first need to write the address and then issue a read. We will use the i2c_write_read() API to do that in one shot as shown below:
uint8_t temp_reading[2]= {0};
uint8_t sensor_regs[2] ={STTS751_TEMP_LOW_REG,STTS751_TEMP_HIGH_REG};
ret = i2c_write_read(dev_i2c,STTS751_I2C_ADDRESS,&sensor_regs[0],1,&temp_reading[0],1);
if(ret != 0){
printk("Failed to write/read I2C device address %x at Reg. %x \n", STTS751_I2C_ADDRESS,sensor_regs[0]);
}
ret = i2c_write_read(dev_i2c,STTS751_I2C_ADDRESS,&sensor_regs[1],1,&temp_reading[1],1);
if(ret != 0){
printk("Failed to write/read I2C device address %x at Reg. %x \n", STTS751_I2C_ADDRESS,sensor_regs[1]);
}
10. Now that we have the temperature reading in raw bytes, the next step to do is to convert them to Celsius and Fahrenheit.
int temp = ((int)temp_reading[1] * 256 + ((int)temp_reading[0] & 0xF0)) / 16;
if(temp > 2047)
{
temp -= 4096;
}
// Convert to engineering units
double cTemp = temp * 0.0625;
double fTemp = cTemp * 1.8 + 32;
//Print reading to console
printk("Temperature in Celsius : %.2f C \n", cTemp);
printk("Temperature in Fahrenheit : %.2f F \n", fTemp);
11. Build the application and flash it on your development kit. Using a serial terminal you should see the below output:
*** Booting Zephyr OS build v2.7.0-ncs1 ***
Temperature in Celsius : 26.37 C
Temperature in Fahrenheit : 79.47 F
Temperature in Celsius : 26.37 C
Temperature in Fahrenheit : 79.47 F
Temperature in Celsius : 26.37 C
Temperature in Fahrenheit : 79.47 F
Temperature in Celsius : 26.37 C
Temperature in Fahrenheit : 79.36 F
Temperature in Celsius : 26.37 C
Temperature in Fahrenheit : 79.36 F
Try blowing on the sensor, and notice an immediate change in the readings.
The solution for this exercise can be found in the GitHub repository, lesson6/fund_less6_exer1_solution of whichever version directory you are using (v2.x.x or v1.6.0-v1.9.1).
Nordic Developer Academy Privacy Policy
1. Introduction
In this Privacy Policy you will find information on Nordic Semiconductor ASA (“Nordic Semiconductor”) processes your personal data when you use the Nordic Developer Academy.
References to “we” and “us” in this document refers to Nordic Semiconductor.
2. Our processing of personal data when you use the Nordic Developer Academy
2.1 Nordic Developer Academy
Nordic Semiconductor processes personal data in order to provide you with the features and functionality of the Nordic Developer Academy. Creating a user account is optional, but required if you want to track you progress and view your completed courses and obtained certificates. If you choose to create a user account, we will process the following categories of personal data:
Email
Name
Password (encrypted)
Course progression (e.g. which course you have completely or partly completed)
Certificate information, which consists of name of completed course and the validity of the certificate
Course results
During your use of the Nordic Developer Academy, you may also be asked if you want to provide feedback. If you choose to respond to any such surveys, we will also process the personal data in your responses in that survey.
The legal basis for this processing is GDPR article 6 (1) b. The processing is necessary for Nordic Semiconductor to provide the Nordic Developer Academy under the Terms of Service.
2.2 Analytics
If you consent to analytics, Nordic Semiconductor will use Google Analytics to obtain statistics about how the Nordic Developer Academy is used. This includes collecting information on for example what pages are viewed, the duration of the visit, the way in which the pages are maneuvered, what links are clicked, technical information about your equipment. The information is used to learn how Nordic Developer Academy is used and how the user experience can be further developed.
2.2 Newsletter
You can consent to receive newsletters from Nordic from within the Nordic Developer Academy. How your personal data is processed when you sign up for our newsletters is described in the Nordic Semiconductor Privacy Policy.
3. Retention period
We will store your personal data for as long you use the Nordic Developer Academy. If our systems register that you have not used your account for 36 months, your account will be deleted.
4. Additional information
Additional information on how we process personal data can be found in the Nordic Semiconductor Privacy Policy and Cookie Policy.
Nordic Developer Academy Terms of Service
1. Introduction
These terms and conditions (“Terms of Use”) apply to the use of the Nordic Developer Academy, provided by Nordic Semiconductor ASA, org. nr. 966 011 726, a public limited liability company registered in Norway (“Nordic Semiconductor”).
Nordic Developer Academy allows the user to take technical courses related to Nordic Semiconductor products, software and services, and obtain a certificate certifying completion of these courses. By completing the registration process for the Nordic Developer Academy, you are agreeing to be bound by these Terms of Use.
These Terms of Use are applicable as long as you have a user account giving you access to Nordic Developer Academy.
2. Access to and use of Nordic Developer Academy
Upon acceptance of these Terms of Use you are granted a non-exclusive right of access to, and use of Nordic Developer Academy, as it is provided to you at any time. Nordic Semiconductor provides Nordic Developer Academy to you free of charge, subject to the provisions of these Terms of Use and the Nordic Developer Academy Privacy Policy.
To access select features of Nordic Developer Academy, you need to create a user account. You are solely responsible for the security associated with your user account, including always keeping your login details safe.
You will able to receive an electronic certificate from Nordic Developer Academy upon completion of courses. By issuing you such a certificate, Nordic Semiconductor certifies that you have completed the applicable course, but does not provide any further warrants or endorsements for any particular skills or professional qualifications.
Nordic Semiconductor will continuously develop Nordic Developer Academy with new features and functionality, but reserves the right to remove or alter any existing functions without notice.
3. Acceptable use
You undertake that you will use Nordic Developer Academy in accordance with applicable law and regulations, and in accordance with these Terms of Use. You must not modify, adapt, or hack Nordic Developer Academy or modify another website so as to falsely imply that it is associated with Nordic Developer Academy, Nordic Semiconductor, or any other Nordic Semiconductor product, software or service.
You agree not to reproduce, duplicate, copy, sell, resell or in any other way exploit any portion of Nordic Developer Academy, use of Nordic Developer Academy, or access to Nordic Developer Academy without the express written permission by Nordic Semiconductor. You must not upload, post, host, or transmit unsolicited email, SMS, or \”spam\” messages.
You are responsible for ensuring that the information you post and the content you share does not;
contain false, misleading or otherwise erroneous information
infringe someone else’s copyrights or other intellectual property rights
contain sensitive personal data or
contain information that might be received as offensive or insulting.
Such information may be removed without prior notice.
Nordic Semiconductor reserves the right to at any time determine whether a use of Nordic Developer Academy is in violation of its requirements for acceptable use.
Violation of the at any time applicable requirements for acceptable use may result in termination of your account. We will take reasonable steps to notify you and state the reason for termination in such cases.
4. Routines for planned maintenance
Certain types of maintenance may imply a stop or reduction in availability of Nordic Developer Academy. Nordic Semiconductor does not warrant any level of service availability but will provide its best effort to limit the impact of any planned maintenance on the availability of Nordic Developer Academy.
5. Intellectual property rights
Nordic Semiconductor retains all rights to all elements of Nordic Developer Academy. This includes, but is not limited to, the concept, design, trademarks, know-how, trade secrets, copyrights and all other intellectual property rights.
Nordic Semiconductor receives all rights to all content uploaded or created in Nordic Developer Academy. You do not receive any license or usage rights to Nordic Developer Academy beyond what is explicitly stated in this Agreement.
6. Liability and damages
Nothing within these Terms of Use is intended to limit your statutory data privacy rights as a data subject, as described in the Nordic Developer Academy Privacy Policy. You acknowledge that errors might occur from time to time and waive any right to claim for compensation as a result of errors in Nordic Developer Academy. When an error occurs, you shall notify Nordic Semiconductor of the error and provide a description of the error situation.
You agree to indemnify Nordic Semiconductor for any loss, including indirect loss, arising out of or in connection with your use of Nordic Developer Academy or violations of these Terms of Use. Nordic Semiconductor shall not be held liable for, and does not warrant that (i) Nordic Developer Academy will meet your specific requirements, (ii) Nordic Developer Academy will be uninterrupted, timely, secure, or error-free, (iii) the results that may be obtained from the use of Nordic Developer Academy will be accurate or reliable, (iv) the quality of any products, services, information, or other material purchased or obtained by you through Nordic Developer Academy will meet your expectations, or that (v) any errors in Nordic Developer Academy will be corrected.
You accept that this is a service provided to you without any payment and hence you accept that Nordic Semiconductor will not be held responsible, or liable, for any breaches of these Terms of Use or any loss connected to your use of Nordic Developer Academy. Unless otherwise follows from mandatory law, Nordic Semiconductor will not accept any such responsibility or liability.
7. Change of terms
Nordic Semiconductor may update and change the Terms of Use from time to time. Nordic Semiconductor will seek to notify you about significant changes before such changes come into force and give you a possibility to evaluate the effects of proposed changes. Continued use of Nordic Developer Academy after any such changes shall constitute your acceptance of such changes. You can review the current version of the Terms of Use at any time at https://academy.nordicsemi.com/terms-of-service/
8. Transfer of rights
Nordic Semiconductor is entitled to transfer its rights and obligation pursuant to these Terms of Use to a third party as part of a merger or acquisition process, or as a result of other organizational changes.
9. Third Party Services
To the extent Nordic Developer Academy facilitates access to services provided by a third party, you agree to comply with the terms governing such third party services. Nordic Semiconductor shall not be held liable for any errors, omissions, inaccuracies, etc. related to such third party services.
10. Dispute resolution
The Terms of Use and any other legally binding agreement between yourself and Nordic Semiconductor shall be subject to Norwegian law and Norwegian courts’ exclusive jurisdiction.