 So let's have a look on the I3C interface. So we will have a quick overview what the I3C is, what are some new features, and how it's compatible with the I2C. Then, yeah, we will have a look what are the, let's say, advantages and disadvantages, and then we will have a short quick demo with the LSM6DSO sensor, which is a gyroscope accelerometer sensor from SD that supports the I3C. So quick introduction to the I3C. It's a MIPI standardized protocol, and it's designed to improve or overcome some of the I2C limitations, such as limited speed or need for external signals for interrupts. And the name I3C stands for the improved I2C. As on I2C, we have a two-wire interface, so we have two signals, SDA and clocks. With the I3C, we have now a new naming. Instead of master state, we have a controller and a target. So on the bus, there is always some I3C controller, or named primary controller, but in more advanced applications, there can be more devices that can take this role. And it's compatible with the I2C with some limitations. We will talk about later, and it's defined by the MIPI specification, and the basic specification with the basic features is available for free to everybody who registers on their website. So it might be more open than the I2C, and more people might adopt it. Now, what are the new features? One of them is push-pull driving. So the I3C allows to switch to the push-pull drive. So in the old I2C, the speed was limited by the open drain bus. If we wanted to go to the higher frequency, we needed to reduce the pull-up size on the data lines. That meant we need to increase the drive capability of the devices, and we can then face some speed limitations. So I3C allows to switch to push-pull drive when the arbitration is done. And in the push-pull, we can achieve up to 12.5 MHz. So that's basically the same speed as a full-speed USB. It still uses the open drain, and this is during the arbitration. So when it starts communicating, it's running in the open drain. We'll later also see why there is this arbitration. Okay, so this also requires that the I3C controller needs to control this pull-up. So in STN32-H5, when you set up the I3C peripheral as controller, there is embedded pull-up resistor that is switched on and off during the communication. And the clock signal is also driven in the push-pull mode. So that means we cannot use the clock stretching. So for example, if we would want to use some S2C Slave in the legacy mode that uses the clock stretching, we cannot use it. Another new feature is dynamic address assignment. So you can assign different 7-bit addresses to the targets, which can work as a priority on the bus. And also there is this process of discovering the devices on the bus and assigning these addresses. So we read some sort of unique ID. It's 64-bit. And based on this, we assign the 7-bit address. And we can also assign these addresses based on static or I2C legacy addresses. So for example, we could do this with the sensor we will use in the hands-on, because of course, at least in the current time, all the sensors that support I3C will probably support I2C as an option. The I3C also introduces command codes. So these are some standardized commands. You can send on the bus that has described the format. So it allows to do some operation in a standard way and you don't need to study the sensor datasheet to know which data to send to do some common tasks. That's also good. In the, you have the In-Bend Interups. So this allows any I3C target to initialize communication on the bus and send some notification to the I3C controller. So that is also one of the reasons we have the arbitration header. So some other devices can start communication at the same time. And we have the dynamic address assignment to select if we have multiple devices that allow this In-Bend Interups to configure the priority between these devices. And we can also send some payload with the interrupt, which for example, can be the timing control, which is another feature that describes how the timing should work if we get some notification from the sensor. But there could be some communication ongoing. So there is some latency between the event happening and on the controller side receiving the interrupt. So there is some described mechanism how to calculate this latency. There are some more advanced features. I don't think those are that interesting. So you can assign group addresses. You can have sort of multicast. So then you can send the command codes to group of devices. It might be useful with many devices on the same bus. Then there is a hot join mechanism. So that is when, for example, you are in some low power mode and then you power up and power up some sensors or additional I3C targets. They start this hot join request and then the I3C controller assigns them address and basically restart the enumeration process. And secondary controller is that you can switch the controller role to some different device. So that was from the theory. So the I3C is compatible with the I2C, but there are some limitations. So you might need to be careful when using this. One of the points is that not all I2C slides will follow the I2C specification. Also the I3C in the high speed relies on 15 nanoseconds spike filter to be implemented in the I2C slide. So it will send shorter clock pulses than 15 nanoseconds. And since there is the filter, the I2C slide shouldn't see it. As I mentioned before, the clock stretching is not allowed and certain addresses are reserved for I3C. And also the I3C targets might have still the I2C mode which might require some configuration during the startup. Also the I3C basic specification has some nice table that compares a different feature with compatibility. We can have a look there what some of them might limit some functions, some of them might break the compatibility completely. So why to use the I3C? Where we can have a higher data rate. We can reduce the pin count using the in-band interrupt. We can achieve lower power consumption because we switch to the push-pull mode and we don't need to fight with the external pull-up on the bus. And we have some standardized commands for basic operations. And also the I3C allows to add additional data lines. So we don't have this feature in the H5, but this concept allows in the future to extend the bandwidth on the bus. And also it allows double data rates which are also not supported in the H5. But it's another possibility to increase the bandwidth. So there is some comparison of the speed from the specification. So here we have the SDR which we use on the H5. So here it should correspond roughly to the 12.5 MHz. Then when you switch to the double data rate you can get up to twice or even more speed. What might be challenging at the moment? So compatibility with the I2C might depend on the devices you want to use on the bus. The higher frequencies might lead to some issues with signal integrity. If you are running, for example, 400 kHz I2C bus you don't need to care that much about the trace lengths and parasitic capacitances. It also requires some initialization at the beginning and might be a bit more complex to I2C. Sorry, there is a mistake. And yeah, it might not be that available at the moment, but that might change in the future. There are different kind of transfer that can happen on the bus. We will not have a detailed look, but just to summarize them. So we have private read write commands. So this is like, for example, accessing the sensor registers. You read write directly to the device. Then we have some specific command code commands. So it's assigning the dynamic address or resetting targets. So these have some special timing constraints. Then we have the I3C regular command code. So we can do a broadcast to all devices. Of course, this can work only for write or we can issue this command to a specific device in a read or write mode. We have the in-band interrupts. Then we can use the legacy I2C transfers. So this one will be done only in the open drain mode. And hold join request and controller row request. So just to have a quick look on one of the transfers. So for the private write. So for example, we are writing some data to the sensor. We can usually we start from here. So here is the arbitration header. We send some reserved byte in the open brain mode. Then when we receive acknowledge we issue the repeated start and we are already switched to the push-pull mode. Then it's quite similar to the I2C communication just in the push-pull mode. Also the acknowledge is a bit different. So for example in the write mode it will send a parity bit. We can also skip the arbitration header but that for example requires that no other device will issue interrupt. Or we can start from previous transaction. So we had some previous communication. We don't need to go to the open drain mode again. We just start from the repeated start and write the data. So I think that is all from the theory. Now we will go to the practical part. So what we will use is we will use this extension shield XNUCLO IKS01A3. So it's an extension board that has many different sensors on it. And one of them is LSM6DSO. So that's the sensor that supports the I3C. Unfortunately this board is mainly targeting I2C. So there are a few things we need to do. First we need to remove these jumpers to disconnect most of the sensors from the bus. And the other thing is there are level shifters because most of the sensors operate at 1.8 volts while the board will run on 3.3 volts. These level shifters will also limit the maximum frequency we can use on the I3C. But we will be able to demonstrate some of the features such as the in-band interrupt. So we will create a simple demo project. We will use the in-band interrupt feature to get some notification from the sensor. And to configure the sensor to work properly we will use the XCube MEMS extension package. So I will now switch to the cube IDE and we will start with creating a new project. This might take some time. So for this project we will select the board. So it's nukleo h563 and this will also do some configuration for us. I will click on next. I will type h3c-demo and I will click finish. And here I will select yes to initialize Periferos in the default mode. It will reduce the number of steps we need to do for the configuration. So we have this running. So we will start with the I3C configuration. So in the connectivity you should see here the I3C. So on this bigger h5 we have one I3C instance. On the small one the h5 zero we have two instances. And here I will select the controller mode. And I will configure some parameters. So here I will switch to the mixed bus. So although we will use only I3C I will use this to help me with the timing because we have the level shifter inside and we want to avoid small pulses. So I will also set the duty cycle to 50% and we will in the code copy this timing for the I2C to the I3C. So unfortunately at this moment the Qubemix will force us to use basically a good timing for the I3C but one that assumes there is the 15 nanosecond field in the legacy I2C devices and having really short pulses on the clock line. So we need to avoid it due to the level shifters. That's all in this part. I will switch to the interrupts and enable both interrupts because we will use them in the project. And now I need to also configure the GPIO pins because I want to use the PB8 and PB9. So here I can either use the feature here that I will hold CTRL and click on the pin to move it there or I can click here and select the function. Now I have both pins here and those are on the nuclear board on the I2C on the Arduino header. And I think that should be all for the I3C. Now at the bottom part I will configure PA14 as a XTI input. So I will configure some peripherals that the XQubemix package will use but we will not use it really in the application but it's required to configure the package to this one. And also I need to go to the GPIO and enable the corresponding interrupt in the NVIC. So it's the XT line 14. And now I will configure the I2C too. So again this is just for the package to be included without issue. I can use the default pinout and configuration. Then I will use USART3 which is already configured for printing some data. So I can leave it there. The same is basically for the iCache since we started on the board with peripherals in default mode we have it configured. And now I will configure the XQubemix extension. So I will go to the software packs and select components. And here you can see different kind of extension packages. And here I have the mems package. So you can see I have it already downloaded. And from here I can select some demo application. So I will resize this to see it. So I can select some demo application for this IKS board. So this is the one we have the LSM6DSO and wake up. So we will use this demo. Now it complains so we can have a look here. I think there should pop up some message that basically tell us to enable the board part. So it should be this one board extension. And now there is the green. So it's OK. You can click OK. And now here in the middleware tab I will see the XQubemix package. I enable the both parts that I selected previously and now I need to configure some peripherals to the package. So here I will configure the interrupt from the center which is PA14. Then I will select the button. So I have it already configured from the board. Similar for the USART or we use it in the UART mode but the peripheral is USART so we need to select USART here. Here we select the I2C but we will override it in the project with the I3C and we select the green LED. And one last thing before we generate the code is that in the project manager in the advanced settings or the I3C in it we will change this checkboxes. So we will not generate the function call because we will call it from the MEMS package and we will uncheck this visibility. It will be visible outside the main C file and we can generate the code. So here you have the steps in more detail but I think I can skip to the part where we copied the code. So here we will create a new source file with this code. So I go back here in the core source. I will create a new source file. I think what was the name? Yeah, I3C, EREC, IO. I will copy the code here. I need to do the same thing for the header file. So this is basically like a BSP for the I3C. That overbrides the I2C and we will go through it at the end of the hands-on what is done there but at the moment we will just copy the code here. And one last modification should be in the MEMS target. There is the configuration file and we will use some a bit trick to redirect it to this I3C EREC IO files we created to use those functions. So I click here. I think I need to place in the user code section. That should be all. So I assume in the future we will have a new XCube MEMS package that will support this out of the box. So you could use the I3C directly but now we are using this dirty trick. So I have the board here. You can see that I don't have the extension board connected. So I can now try to download the code and there should be some error messages from the code because we will not get to the enumeration state. So I assume I see the enumeration fail. So I will now quickly disconnect the board so I can plug the extension shield. So even if you don't have the extension shield you should see some messages there. And now if I connect and do a reset. Okay, I forgot one more thing. So I need to do one more modification here in the main file because I need to change the timing for the I3C as I mentioned before. So we want to use the same timing as for the I2C. So I just overwrite these two lines. So I cannot set it now in the Cube MEMS. This is due to the level shifter. If there was no level shifter it would work properly. So now if I download it, I assume it here. Okay, now I see I done the enumeration successfully. I can put back the board. And the demo here is that it detects vibration and turns the LED on. So it's like, for example, with mobile phone, if you pick it up it will light up and wake up. So if I tap on the table, the LED should be blinking. So this is basically the demo from the XCube package. So maybe we can go just quickly through the code that is here. So at the beginning we have the BSP I3C in it. So here we initialize the I3C. Then we start the dynamic address assignment. Here we also reset the dynamic address. Because the sensor can have some address assigned from previous running of the example. So we reset that and we wait for the enumeration. If it's done successfully, we configure the device for the inventing trap. So maybe this is also I can show you that really the interrupt is coming from the I3C peripheral. If I put a breakpoint here and I run it. So so far I don't have the interrupt, but if I tap on the table you can see in the debug it's coming from the I3C1 event interrupt handler. Then if I remove this, you'll get the blank on the LED. And so then we have some callbacks for the dynamic address assignment. So when we detect some new device or we don't detect any new devices, so we finish the process or we don't receive any acknowledge, then we can at least print the enumeration failed. And then we have the read and write registers. So if I start with the read register, we use low layer here because it's simpler and actually the peripheral is quite easy to use. So you basically set up what kind of transfer you want to do. So in this case we do private write and then we transmit the data. And here it's one data for the register address and then I switch to the read direction. So it's like a new transfer and I read from the device. Here I check some flags if there is some error. In this part I know there is a FIFO inside so I don't need to wait for some flag before I write the data. The similar is for the write register. There is just one small hack that the X-Cube MEMS will disable the I3C feature in the sensor because it expects it to use I2C to avoid some errors. So we know that if we write to this specific register we will modify the data. So just to summarize some takeaways from this hands-on. So the I3C is faster. You can basically compare it to the full speed USB at the maximum frequency. It reduces the pin count because we can use the in-band interrupt. It's a more open standard because the basic specification is available to everyone. So we might see more easier adaptation in the market. It's compatible with the I2C with some limitations and it can be extended in the future. So in applications where you would need more bandwidth in the future you can put more data lines or switch to the double data rate.