Electronics

What is the value of portTICK_PERIOD_MS and configTICK_RATE_HZ on the ESP32 using PlatformIO?

When you use PlatformIO with the Arduino framework in its default configuration on the ESP32, configTICK_RATE_HZ is set to 1000. In other words, FreeRTOS has a default tick frequency of 1kHz. This is defined in sdkconfig.h:

#define CONFIG_FREERTOS_HZ 1000

Hence portTICK_PERIOD_MS is 1. In my opinion, a tick rate of 1kHz is a sane configuration for most usecases.

I found the value by using the following code on an ESP32:

Serial.println("Timing: ");
Serial.println(portTICK_PERIOD_MS);
Serial.println(configTICK_RATE_HZ);

 

Posted by Uli Köhler in Arduino, Electronics, Embedded, ESP8266/ESP32, FreeRTOS, PlatformIO

How to read 8-bit I2C register using Arduino Wire library: A minimal example

The following code demonstrates how to read a register that is 1 byte (8 bits) long over I2C. It will work with almost all I2C devices like EEPROMs, ADCs and others, provided you have the correct. Note that some devices like the LAN9303 have a slightly different addressing scheme or other peculiarities. In my opinion, it’s most efficient to just try out the standard way of reading a register and start from there.

Note that this code does not implement error handling for the sake of simplicity. Additionally, we wait for data using delay() instead of Wire.available(). This is a minimal example so it creates minimal confusion for the reader. We will provide a full example with error handling in a followup post.

const uint8_t SLAVE_I2C_ADDRESS = 0b1010;
const uint16_t SLAVE_I2C_REGISTER_ADDRESS = 0x50;

Wire.beginTransmission(SLAVE_I2C_ADDRESS);
Wire.write(SLAVE_I2C_REGISTER_ADDRESS);
Wire.endTransmission();
Wire.requestFrom(SLAVE_I2C_ADDRESS, 1); // This register is 8 bits = 1 byte long
delay(2); // Wait for data to be available
// Read directly into an uint8_t
uint8_t buf = (uint8_t)Wire.read();
// Print register value
Serial.printf("Register value: %02x\r\n", buf);

Also see:

Posted by Uli Köhler in Arduino, C/C++, Electronics, Embedded, PlatformIO

How to read 16-bit I2C register using Arduino Wire library: A minimal example

The following code demonstrates how to read a register that is 2 bytes (16 bits) long over I2C. It will work with almost all I2C devices like EEPROMs, ADCs and others, provided you have the correct. Note that some devices like the LAN9303 have a slightly different addressing scheme or other peculiarities. In my opinion, it’s most efficient to just try out the standard way of reading a register and start from there.

Note that this code does not implement error handling for the sake of simplicity. Additionally, we wait for data using delay() instead of Wire.available(). This is a minimal example so it creates minimal confusion for the reader. We will provide a full example with error handling in a followup post.

Option 1: Reading the register into an uint16_t (recommended)

const uint8_t SLAVE_I2C_ADDRESS = 0b1010;
const uint16_t SLAVE_I2C_REGISTER_ADDRESS = 0x50;

Wire.beginTransmission(SLAVE_I2C_ADDRESS);
Wire.write(SLAVE_I2C_REGISTER_ADDRESS);
Wire.endTransmission();
Wire.requestFrom(SLAVE_I2C_ADDRESS, 2); // This register is 16 bits = 2 bytes long
delay(5); // Wait for data to be available
// Read directly into an uint32_t
uint16_t buf;
Wire.readBytes((uint8_t*)&buf, 2);
// Print register value
Serial.printf("Register value: %04x\r\n", __builtin_bswap16(buf));

For an explanation on why we need __builtin_bswap16(), see How to print 16-bit uint16_t as four hex digits in Arduino

Option 2: Reading the register into an uint8_t array

const uint8_t SLAVE_I2C_ADDRESS = 0b1010;
const uint16_t SLAVE_I2C_REGISTER_ADDRESS = 0x50;

Wire.beginTransmission(SLAVE_I2C_ADDRESS);
Wire.write(SLAVE_I2C_REGISTER_ADDRESS);
Wire.endTransmission();
Wire.requestFrom(SLAVE_I2C_ADDRESS, 2); // This register is 16 bits = 2 bytes long
delay(5); // Wait for data to be available
// Read into a 2-byte buffer
uint8_t buf[2];
Wire.readBytes(buf, 2);
// Print register value
Serial.printf("Register value: %02x%02x\r\n", buf[0], buf[1]);

Also see:

Posted by Uli Köhler in Arduino, C/C++, Electronics, Embedded, PlatformIO

How to read 32-bit I2C register using Arduino Wire library: A minimal example

The following code demonstrates how to read a register that is 4 bytes (32 bits) long over I2C. It will work with almost all I2C devices like EEPROMs, ADCs and others, provided you have the correct. Note that some devices like the LAN9303 have a slightly different addressing scheme or other peculiarities. In my opinion, it’s most efficient to just try out the standard way of reading a register and start from there.

Note that this code does not implement error handling for the sake of simplicity. Additionally, we wait for data using delay() instead of Wire.available(). This is a minimal example so it creates minimal confusion for the reader. We will provide a full example with error handling in a followup post.

Option 1: Reading the register into an uint32_t (recommended)

const uint8_t SLAVE_I2C_ADDRESS = 0b1010;
const uint16_t SLAVE_I2C_REGISTER_ADDRESS = 0x50;

Wire.beginTransmission(SLAVE_I2C_ADDRESS);
Wire.write(SLAVE_I2C_REGISTER_ADDRESS);
Wire.endTransmission();
Wire.requestFrom(SLAVE_I2C_ADDRESS, 4); // This register is 32 bits = 4 bytes long
delay(5); // Wait for data to be available
// Read directly into an uint32_t
uint32_t buf;
size_t actually_read = Wire.readBytes((uint8_t*)&buf, 4);
// Print register value
Serial.printf("Register value: %08lx\r\n", __builtin_bswap32(buf));

For an explanation on why we need __builtin_bswap32(), see How to print 32-bit uint32_t as eight hex digits in Arduino

Option 2: Reading the register into an uint8_t array

const uint8_t SLAVE_I2C_ADDRESS = 0b1010;
const uint16_t SLAVE_I2C_REGISTER_ADDRESS = 0x50;

Wire.beginTransmission(SLAVE_I2C_ADDRESS);
Wire.write(SLAVE_I2C_REGISTER_ADDRESS);
Wire.endTransmission();
Wire.requestFrom(SLAVE_I2C_ADDRESS, 4); // This register is 32 bits = 4 bytes long
delay(5); // Wait for data to be available
// Read into a 4-byte buffer
uint8_t buf[4];
size_t actually_read = Wire.readBytes(buf, 4);
// Print register value
Serial.printf("Register value: %02x%02x%02x%02x\r\n", buf[0], buf[1], buf[2], buf[3]);

Also see:

Posted by Uli Köhler in Arduino, C/C++, Electronics, Embedded, PlatformIO

Which strap resistor value to use for LAN9303?

The LAN9303 datasheet currently does explictly specify a strapping resistor value. However, the evalboard schematic effectively uses a 10kΩ resistor when strapping to GND or a 20kΩ resistor when strapping to VDD. I have experimentally verified that 10kOhm strapping resistors work when strapping to GND. On my boards, I do not explicitly strap to VDD because most strappable pins have internal pull-ups and I don’t need to use the LEDs.

Hence, my recommendation is to strap the LAN9303 using 10kΩ resistors.

Posted by Uli Köhler in Electronics, Embedded, Networking

How to get the LAN9303 into I2C managed mode

In order to set the LAN9303 into I2C managed mode where you can configure the chip using I2C,  strap MNGT1 to 1 and strap MNGT0 to 0. Since the MNGT[1:0] strap pins have an internal pull-up, you only need to strap MNGT0 (pin 26 on the QFN package) to GND using a 10kΩ resistor from this pin to GND.

Posted by Uli Köhler in Electronics, Embedded, Networking

How to access LAN9303 registers over I2C using Arduino / PlatformIO

The LAN9303 has some peculiarities when accessing its registers. This post will not cover indirect register access but only access to the registers which are directly accessible over I2C. Note that the prerequisite for this is to configure the LAN9303 into a mode where management over the I2C slave interface is enabled. See How to get the LAN9303 into I2C managed mode for more info on how to do that.

The main point to take away is that you do not write the register offset from the datasheet (such as 0x50 for the Chip ID and revision register) in the I2C address byte but the address divided by 4 (0x50 >> 2 == 0x14). This is evidenced by figure 8-8 from the datasheet, copyright Microchip, listing the address byte as A[9:2] as opposed to the standard A[7:0]:

 

Example on how to access the register at offset 0x50 (Chip ID and revision register) in Arduino using the Wire library:

const uint8_t LAN9303_I2C_ADDRESS = 0b1010;
const uint16_t LAN9303_CHIPID_REV_Register = 0x50;

Wire.beginTransmission(LAN9303_I2C_ADDRESS);
Wire.write(LAN9303_CHIPID_REV_Register >> 2);
Wire.endTransmission();
Wire.requestFrom(LAN9303_I2C_ADDRESS, 4); // This register is 32 bits = 4 bytes long
delay(5); // Wait for data to be available

// Read directly into an uint8_t
uint32_t buf;
size_t actually_read = Wire.readBytes((uint8_t*)&buf, 4);
// Check if we have received all 4 bytes
if(actually_read != 4) {
    Serial.println("Did not read enough bytes");
}

// Print register value
Serial.printf("LAN9303 Chip ID and revision: %08lx\r\n", __builtin_bswap32(buf));

This will print

LAN9303 Chip ID and revision: 93030001

in other words: Chip ID = 0x9303, revision = 0x0001

 

Posted by Uli Köhler in Arduino, C/C++, Electronics, Embedded, Networking, PlatformIO

How to print 64-bit uint64_t as sixteen hex digits in Arduino

When using Arduino, you often want to print the hex value of a 64-bit value such as a uint64_t, consisting of sixteen hex digits. For example, if you have uint64_t val = 169557370125;, you intend to print 000000277a68250d.

In Arduino you can do that using Serial.printf() with %08lx08lx as format specifier, splitting the uint64_t in two uint32_t instances and printing those one after another:

Serial.printf("%08lx%08lx\r\n",
    ((uint32_t)((val >> 32) & 0xFFFFFFFF)),
    ((uint32_t)(val & 0xFFFFFFFF)));

 

See How to print 32-bit uint32_t as eight hex digits in Arduino for more information on what %x means and why we need to use the 08 in %08x as a printf format specifier.

Posted by Uli Köhler in Arduino, Electronics, Embedded, PlatformIO

How to print 32-bit uint32_t as eight hex digits in Arduino

When using Arduino, you often want to print the hex value of a 32-bit value such as a uint32_t, consisting of eight hex digits. For example, if you have uint32_t val = 9177025;, you intend to print 008C07C1.

In Arduino you can do that using Serial.printf() with %08lx as format specifier. Furthermore, you typically want to invert the byte order of the uint32_t using __builtin_bswap32()since it’s more inuitive to write the hex value MSB-first (big-endian) while most hardware platforms represent the uint32_t as LSB-first (little-endian):

Serial.printf("val = %08lx\r\n", __builtin_bswap32(val));

When using printf, %x means to format the value as hex whereas l tells printf() that the argument is a long (32 bit) as opposed to an int (16 bit). 08 means to pad the value with 0s up to a length of 8 digits. If you would format 9177025 using just %x, it would print 008C07C1  instead of 008C07C1. This is why you need to use %08lx instead.

Posted by Uli Köhler in Arduino, Electronics, Embedded, PlatformIO

How to print 16-bit uint16_t as four hex digits in Arduino

When using Arduino, you often want to print the hex value of a 16-bit value such as a uint16_t, consisting of four hex digits. For example, if you have uint16_t val = 2022;, you intend to print 07E6.

In Arduino you can do that using Serial.printf() with %04x as format specifier.

Furthermore, you typically want to invert the byte order of the uint16_t using __builtin_bswap16()since it’s more inuitive to write the hex value MSB-first (big-endian) while most hardware platforms represent the uint16_t as LSB-first (little-endian):

Serial.printf("val = %04x\r\n", __builtin_bswap16(val));

When using printf, %x means to format the value as hex. 04 means to pad the value with 0s up to a length of 4 digits. If you would format 2022 using just %x, it would print 7E6  instead of 07E5. This is why you need to use %04x instead.

Posted by Uli Köhler in Arduino, Electronics, Embedded, PlatformIO

How to print byte as two hex digits in Arduino

In embedded programming, you often want to print the hex value of a byte, consisting of two hex digits. For example, if you have uint8_t val = 14;, you intend to print 0x0E.

In Arduino you can do that using Serial.printf() with %02x as format specifier:

Serial.printf("val = %02x\r\n", val);

When using printf, %x means to format the value as hex. 02 means to pad the value with 0s up to a length of 2 digits. If you would format 14 using just %x, it would print E  instead of 0E. This is why you need to use %02x.

Posted by Uli Köhler in Arduino, Electronics, Embedded, PlatformIO

Arduino I2C: Wire.endTransmission() before or after Wire.requestFrom() ?

The correct order to call Wire commands in Arduino is:

Wire.beginTransmission(MY_I2C_ADDR);
Wire.write(addr);
Wire.endTransmission();
Wire.requestFrom(MY_I2C_ADDR, 1); // Request one byte
delay(5); // Wait for data to be available
uint8_t value = Wire.read();

So you call Wire.endTransmission() after Wire.write() and call Wire.requestFrom() directly after Wire.endTransmission()

Posted by Uli Köhler in Arduino, Electronics, Embedded, PlatformIO

What is the LAN9303 I2C slave address?

I experimentally determined that the LAN9303 uses the I2C slave address 0b0001010. This is listed in the datasheet in section 8.5.1.

Posted by Uli Köhler in Electronics, Embedded, Networking

Teensy 4.x Quadrature Encoder minimal example in PlatformIO/Arduino

This example uses PlatformIO and the Teensy-4.x-Quad-Encoder-Library to implement a hardware quadrature encoder on pins 0 and 1. Remember that the teensy will be destroyed if you use 5V encoder signals. You can only use 3.3V signals!

#include <Arduino.h>
#include <QuadEncoder.h>

QuadEncoder encoder(1, 0, 1, 0);

void setup() {
    Serial.begin(115200);
    encoder.setInitConfig();
    encoder.init();
}

void loop() {
    int32_t position = encoder.read();
    // Compute position in mm and print it
    constexpr float mm_per_count = 0.0012;
    float mm = position * mm_per_count;
    Serial.printf("Position: %+3.4f (count %ld)\r\n", mm, position);
    // Print every 50ms
    delay(50);
}
[env:teensy41]
platform = teensy
board = teensy41
framework = arduino
monitor_speed = 115200
lib_deps =
    git+https://github.com/mjs513/Teensy-4.x-Quad-Encoder-Library

 

Posted by Uli Köhler in C/C++, Electronics, Embedded, PlatformIO, Teensy

How to add GitHub repository to PlatformIO lib_deps

Use git+https like this:

lib_deps =
    git+https://github.com/mjs513/Teensy-4.x-Quad-Encoder-Library

 

Posted by Uli Köhler in Electronics, Embedded, PlatformIO

Which pins on the Teensy 4.1 are 5V-tolerant?

None of the Teensy 4.1 IO pins are 5V tolerant. You can not connect any 5V signals to any of those pins, doing so will severely damage the hardware. You always need to use some kind of level shifter when connecting 5V signals to the Teensy.

Posted by Uli Köhler in Electronics, Teensy

FR4 PCB thermal expansion online calculator

Calculate the thermal expansion in millimeters (mm) of a FR4 PCB in X/Y direction. A typical temperature difference default is 65°C, the difference between 20°C room temperature and the max temperature rating of 85°C parts. The default 13ppm/°C thermal expansion coefficient is from this website.

TechOverflow calculators:
You can enter values with SI suffixes like 12.2m (equivalent to 0.012) or 14k (14000) or 32u (0.000032).
The results are calculated while you type and shown directly below the calculator, so there is no need to press return or click on a Calculate button. Just make sure that all inputs are green by entering valid values.

mm

°C

ppm/°C

Posted by Uli Köhler in Calculators, Electronics, Physics

How to set pin speed/alternate function in STM32 Arduino (PlatformIO)

You can use the STM32 HAL (STM32CubeMX) even when using Arduino as a framework for STM32 boards in PlatformIO:

GPIO_InitTypeDef pinInit = {
  .Pin = GPIO_PIN_8,
  .Mode = GPIO_MODE_AF_PP,
  .Pull = GPIO_NOPULL,
  .Speed = GPIO_SPEED_FREQ_VERY_HIGH,
  .Alternate = GPIO_AF1_TIM1
};
HAL_GPIO_Init(GPIOA, &pinInit);

Use

#include <stm32f4xx_hal_gpio.h>

in order to include the HAL functions.

Posted by Uli Köhler in Arduino, Electronics, PlatformIO, STM32

How to use HAL_GPIO_Init() in modern C++ (STM32)

In modern C++, you can directly initialize structs like a GPIO_InitTypeDef, making the code much prettier and less prone to errors. The following example configures PA8 of a STM32 in alternate function 1 mode (TIM1 output).

GPIO_InitTypeDef pinInit = {
  .Pin = GPIO_PIN_8,
  .Mode = GPIO_MODE_AF_PP,
  .Pull = GPIO_NOPULL,
  .Speed = GPIO_SPEED_FREQ_VERY_HIGH,
  .Alternate = GPIO_AF1_TIM1
};
HAL_GPIO_Init(GPIOA, &pinInit);

As opposed to the old, much more verbose way of doing that:

GPIO_InitTypeDef pinInit;
pinInit.Pin = GPIO_PIN_8;
pinInit.Mode = GPIO_MODE_AF_PP;
pinInit.Pull = GPIO_NOPULL;
pinInit.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
pinInit.Alternate = GPIO_AF1_TIM1;
HAL_GPIO_Init(GPIOA, &pinInit);

 

 

Posted by Uli Köhler in C/C++, Electronics, STM32

What are SO1 & SO2 on the DRV8601?

SO1 and SO2 are the outputs of the two current shunt amplifiers on the DRV8601. These are analog outputs and are typically connected to an ADC.

 

Posted by Uli Köhler in Electronics