STM32 I2C Lecture 53- Exercise : Testing I2C interrupt APIs part 1

  • Post author:
  • Post category:Blog

 

Exercise : Testing I2C interrupt APIs part 1

 

 


Now let’s test the interrupt related code and APIs using an application.

Consider the same exercise i2c_master_rx_testing (Figure 1) that we coded while testing our MasterSendData and MasterReceiveData APIs.

Testing I2C interrupt APIs part 1
Figure 1. Exercise i2c_master_rx_testing.

 

Steps for testing I2C interrupt APIs:

1. Create a source file: Copy the i2c_master_rx_testing file shown in Figure 1 and paste it under the src. 

2. Change the name to 012i2c_master_rx_testingIT.c, as shown in Figure 2, and save it by pressing ok.

Testing I2C interrupt APIs part 1
Figure 2. Naming the exercise.

 

3.Exclude the 011i2c_master_rx_testing.c file from the build: Right-click on the file, go to properties (Figure 3), tick the checkbox named exclude resource from the build, and click on apply (Figure 4).

 Testing I2C interrupt APIs part 1
Figure 3. Excluding the old source file from the build (Step 1).

 

Testing I2C interrupt APIs part 1
Figure 4. Excluding the old source file from the build (step 2).

 

4.Change all the APIs to IT mode APIs.

5.No need to change GPIO_ButtonInit(), I2C1_GPIOInits(), I2C1_Inits(), peripheral control and I2C_ManageAcking() APIs (Figure 5). You must keep the while statement used to wait till the button is pressed, the delay function, and the statement to send the command code 0x51 (Figure 6).

Testing I2C interrupt APIs part 1
Figure 5. Code that does not need modification.

 

Testing I2C interrupt APIs part 1
Figure 6. Code that does not need modification.

 

6. Replace I2C_MasterSendData() API with I2C_MasterSendDataIT(), as shown in Figure 7. Remember that you have to check the return value of MasterSendDataIT() since it returns a busy state variable (Figure 8). If the busy state is BUSY_IN_TX or BUSY_IN_RX, then the application has to wait until the state becomes I2C ready.

Testing I2C interrupt APIs part 1
Figure 7. Replacing I2C_MasterSendData() API with I2C_MasterSendDataIT().

 

Testing I2C interrupt APIs part 1
Figure 8. Definition of I2C_MasterSendDataIT().

 

7. Make the I2C_MasterSendDataIT() wait until the application’s state becomes I2C_READY using a while loop, as shown in Figure 9.

Testing I2C interrupt APIs part 1
Figure 9. Code to make the I2C_MasterSendDataIT() wait.

If the MasterSendDataIT() function returns a value not equal to I2C_READY, then the application hangs in the while loop. Otherwise, the application proceeds to the next statement.

 

8. Replace I2C_MasterReceiveData() API with I2C_MasterReceiveDataIT(), as shown in Figure 10.

Testing I2C interrupt APIs part 1
Figure 10. Replacing I2C_MasterReceiveData() API with I2C_MasterReceiveDataIT().

 

9. Using a while loop, wait till the application’s state becomes I2C_READY (Figure 11).

Testing I2C interrupt APIs part 1
Figure 11. Code to make the I2C_MasterReceiveDataIT() wait.

 

10.  Command code 0x52 is sent, as shown in Figure 12. Again, replace remaining I2C_MasterSendData() and I2C_MasterReceiveData() with I2C_MasterSendDataIT() and I2C_MasterReceiveDataIT(), respectively (Figure 12).

Testing I2C interrupt APIs part 1
Figure 12. Replacing the APIs.

 

In Figure 12, the rcv_buf is a global buffer.

11. If you want to use semi hosting, then you can enable the printf statement in Figure 13.

Testing I2C interrupt APIs part 1
Figure 13. Code for semi hosting.

 

12.You have to do the IRQ config of the I2C peripheral since you are communicating using interrupts. Now let’s do I2C IRQ configuration using I2C_IRQInterruptConfig API after peripheral configuration, as shown in Figure 14. 

You have to mention the IRQ number of the interrupt as a parameter of the I2C_IRQInterruptConfig API. The I2C event interrupt and error interrupts are enabled by using IRQ numbers IRQ_NO_I2C1_EV and IRQ_NO_I2C1_ER, respectively (Figure 14).

Testing I2C interrupt APIs part 1
Figure 14. IRQ config of the I2C peripheral.

You may also configure the priority, but that is not necessary. The priority doesn’t matter here since you have only I2C interrupts.

13. Implement the interrupt service routines for I2C1 event interrupt and I2C1 error interrupt:

  • Go to the startup code(startup_stm32.s) to get the ISR name.
  • Search for I2C1, as shown in Figure 15.
Testing I2C interrupt APIs part 1
Figure 15. Search for ISR name.

 

  • Copy the ISR names, shown in Figure 16.
Testing I2C interrupt APIs part 1
Figure 16. Copy the ISR names.

 

  • Go to the application and create the IRQ handlers at the end, as shown in Figure 17.
Testing I2C interrupt APIs part 1
Figure 17. Creation of IRQ handlers.

 

  • Inside IRQ handlers of Figure 17, you have to call the IRQ handling APIs, which are already developed. Now call I2C_EV_IRQHandling, as shown in Figure 18, and pass the address of the handler (I2C1Handle) as a parameter.
Testing I2C interrupt APIs part 1
Figure 18. Calling the IRQ handling APIs inside the IRQ handlers.

 

Whenever an event happens during the I2C communication, the I2C1 event handler will be called, where the event IRQ handling APIs are called in order to decode that event and take the appropriate action. Whereas in the case of error, IRQ handling APIs are called.

  • Once the control enters into the IRQHandling API, the IRQHandling API notifies the application about the different events through the callbacks. Therefore now you have to implement I2C_ApplicationEventCallback() in your application.

a. Create the function I2C_ApplicationEventCallback() (Figure 19).

Testing I2C interrupt APIs part 1
Figure 19. Creation of I2C_ApplicationEventCallback().

 

b. Figure 20 shows the various events generated by the driver and are defined in the driver header file. You can check for all these events.

Testing I2C interrupt APIs part 1
Figure 20. Various events generated by the driver.

 

c. For example, in our application, let’s check for TX complete, RX complete, and errors in the semi hosting, as shown in Figure 21.

Testing I2C interrupt APIs part 1
Figure 21. Checking the generation of various events.

Here I2C_ERROR_AF means ACK failure. Now the question is, why the ACK failure happens? For example, in master, the ACK failure happens when a slave fails to send ACK for the byte sent from the master. That means the master has sent 1 byte but didn’t receive the ACK from the slave, resulting in ACK failure.

 

d. When ACK failure occurs in the case of a master, then you can conclude something like slave device might have been removed from the bus or slave device might have gone bad or it may not want more data. Therefore, the master should close the communication by calling I2C_CloseSendData() API, as shown in Figure 22, and should not send more data to the slave.

Testing I2C interrupt APIs part 1
Figure 22. Call for I2C_CloseSendData() API.

 

e. In the definition of I2C_CloseSendData() API (Figure 23), we turn off a couple of interrupts, and we make TxRx state as I2C_READY, TxBuffer=NULL, and TxLen=0.

Testing I2C interrupt APIs part 1
Figure 23. Definition of I2C_CloseSendData() API.

 

f. The stop condition is didn’t generated in the CloseSendData() API. Therefore, you have to generate the stop condition in the callback function to release the bus. There is an API to generate the stop condition, which is a private API. But if the application wants to release the bus, then the application should call generate stop condition API. Therefore, now you have to make this API as public instead of private. For that, first cut the API shown in Figure 24, then go to the driver file and paste the API under the peripheral control APIs section and remove the static keyword (Figure 25).

Also, remove the static keyword from the definition of GenerateStopCondition() API.

Testing I2C interrupt APIs part 1
Figure 24. Prototype of private generate stop condition function.

 

Testing I2C interrupt APIs part 1
Figure 25. Prototype of generate stop condition function.

 

Since the GenerateStopCondition() API is made public, now you can call it from the application callback function, as shown in Figure 26.

Testing I2C interrupt APIs part 1
Figure 26. Call for GenerateStopCondition() API from the application callback function.

 

g. Look at Figure 27. If ACK failure occurs in the very first line, then the application should not go further. Because the ACK failure happened in the first line itself and for our application, executing the second line without executing the first line doesn’t make any sense. Therefore, the program control must hang in the application callback function (Figure 27) to pause code execution.

Testing I2C interrupt APIs part 1
Figure 27. Code to hang in the application callback function.

 

14.  Since you have used a printf statement, it is necessary to enable the semi hosting (Figure 28 and 29).

Testing I2C interrupt APIs part 1
Figure 28. Enabling semi hosting.

 

Testing I2C interrupt APIs part 1
Figure 29. Enabling semi hosting.

 

15.  On the Arduino side, download the sketch 002 I2CSlaveTxString (Figure 30).

Testing I2C interrupt APIs part 1
Figure 30. Arduino sketch 002 I2CSlaveTxString.

 

16. Compile the code (Figure 31).

Testing I2C interrupt APIs part 1
Figure 31. Code compilation.

 

17.  There seems to be one warning, as shown in Figure 32.

Testing I2C interrupt APIs part 1
Figure 32. Results of compilation.

 

18. To solve this problem, you have to extern the initialise_monitor_handles() shown in Figure 33 by enabling the statement in Figure 34.

Testing I2C interrupt APIs part 1
Figure 33. Call for initialise_monitor_handles().

 

Testing I2C interrupt APIs part 1
Figure 34. Making function initialise_monitor_handles() as extern.

 

19.  Again, build the project. Now the project builds successfully without any errors (Figure 35).

Testing I2C interrupt APIs part 1
Figure 35. Results of compilation.

 

20.  Download the code into the chip.

Testing I2C interrupt APIs part 1
Figure 36. Downloading the code into the chip.

 

21. Go into the debug mode (Figure 37). You can see debug mode in Figure 38, and it is saying that semi hosting is enabled.

Testing I2C interrupt APIs part 1
Figure 37. Enter into the debug mode.

 

Testing I2C interrupt APIs part 1
Figure 38. Debug mode.

 

22.  Go to the breakpoints and remove all (Figure 39).

Testing I2C interrupt APIs part 1
Figure 39. Removing the breakpoints.

 

23.  Hit the resume button (Figure 40). Now the code is running, and you have to press the user button.

Testing I2C interrupt APIs part 1
Figure 40. Run the code.

 

24.  Start tracing by pressing the user button (Figure 41).

Testing I2C interrupt APIs part 1
Figure 41. Capturing I2C trace.

 

You can see the I2C trace of your application in Figure 42.

Testing I2C interrupt APIs part 1
Figure 42. I2C trace of the application.

 

25. Observe the console in Figure 43. Application is running, Tx is completed, Rx is completed, Tx is completed, but the data is not received.

Testing I2C interrupt APIs part 1
Figure 43. Output on the console.

 

Look at the trace in Figure 44, the data is received, and the last 4 characters of data are EBA and \n. But the data is not printed on the console. The problem is the program has to wait until the receive completes. Without that, you cannot proceed to the next line, or you cannot execute the printf statement in Figure 45.

Testing I2C interrupt APIs part 1
Figure 44. I2C trace of the application.

 

Testing I2C interrupt APIs part 1
Figure 45. Debug mode.

 

26. To solve this problem, set the rxComplt flag after Rx completed, as shown in Figure 46.

Testing I2C interrupt APIs part 1
Figure 46. Code to set the rxComplt variable.

 

27. Declare the flag variable (Figure 47).

Testing I2C interrupt APIs part 1
Figure 47. Flag variable declaration.

 

28. In the application after the while statement, wait until the rxComplt variable is set, as shown in Figure 48. After printing the data, you can make rxComplt = RESET again.

Testing I2C interrupt APIs part 1
Figure 48. Code to wait until rxComplt is set.

 

29.Remember that you have one more MasterReceiveData() API. After the completion of receive data here also the ApplicationEventCallback() will be executed for the event I2C Rx complete, and the flag will be set. Therefore, before checking whether rxComplt is set or reset, make sure that you clear the rxComplt flag, which was set for the previous MasterReceiveData event. That means RESET the rxComplt variable, as shown in Figure 49.

Testing I2C interrupt APIs part 1
Figure 49. Code to reset the rxComplt variable.

 

30.  Build the project. There is one warning saying while clause does not guard. Basically, this warning is harmless; you can keep it as it is. But if you want to solve this warning, remove the semicolon of while and give the guards, as shown in Figure 50.

Testing I2C interrupt APIs part 1
Figure 50. Resolving the warning.

 

31. Build the project. Now the project is error-free.

32.Program the chip (Figure 51).

Testing I2C interrupt APIs part 1
Figure 51. Programming the chip.

 

33. Go to the debugging mode (Figure 52).

Testing I2C interrupt APIs part 1
Figure 52. Entering into the debug mode.

 

34.  Now you are in the debugging mode (Figure 53). Reset the Arduino board and remove all the breakpoints.

Testing I2C interrupt APIs part 1
Figure 53. Debug mode.

 

35. Hit the run button (Figure 54).

Testing I2C interrupt APIs part 1
Figure 54. Run the application.

 

36. Hit the user button. Now you can observe that the data is received from the slave (Figure 55).

Testing I2C interrupt APIs part 1
Figure 55. Data received from the slave.

 

FastBitLab

The FastBit Embedded Brain Academy uses the power of internet to bring the online courses related to the field of embedded system programming, Real time operating system, Embedded Linux systems, etc at your finger tip with very low cost. Backed with strong experience of industry, we have produced lots of courses with the customer enrolment over 3000+ across 100+ countries.