Plotting graphs

    This site uses cookies. By continuing to browse this site, you are agreeing to our Cookie Policy.

    • Plotting graphs

      Hello,

      I would need to use and integrate STemWin graphics functions in my project, but I would like to learn how to do this.
      The STemWin chapter is a new subject for me, so any information / tutorials / recommandations are greatly appreciated.

      Here is my information :
      1. Tools : CubeMX + Atollic TrueStudio + STemWin
      2. Board : STM32F429I Disc1

      Purpose of project :
      3. Measuring a value with ADC, then plotting it as a graph using STemWin.

      Please feel free to comment if you need further information.

      I thank you in advance for your reply and with kind regards,

      Sue
    • Hi,

      Take a look to the example below. It is very basic. I create a window and display a graph. How you feed the buffer for the graph is up to you.

      C Source Code

      1. #include "DIALOG.h"
      2. #include <stdlib.h>
      3. #include <string.h>
      4. /*********************************************************************
      5. *
      6. * Typedef
      7. *
      8. **********************************************************************
      9. */
      10. typedef struct {
      11. U16 * pData;
      12. int NumElements;
      13. } GRAPH_INFO;
      14. /*********************************************************************
      15. *
      16. * Static data
      17. *
      18. **********************************************************************
      19. */
      20. static GRAPH_INFO _GraphInfo;
      21. /*********************************************************************
      22. *
      23. * Static code
      24. *
      25. **********************************************************************
      26. */
      27. /*********************************************************************
      28. *
      29. * _GetADValue
      30. */
      31. static int _GetADValue(void) {
      32. return (rand() % 80 + 1) - 40;
      33. }
      34. /*********************************************************************
      35. *
      36. * _UpdateData
      37. */
      38. static void _UpdateData(void) {
      39. int i;
      40. //
      41. // Shift elemts in buffer
      42. //
      43. for (i = 0; i < _GraphInfo.NumElements - 1; i++) {
      44. *(_GraphInfo.pData + i) = *(_GraphInfo.pData + i + 1);
      45. }
      46. //
      47. // Add new value
      48. //
      49. *(_GraphInfo.pData + _GraphInfo.NumElements - 1) = _GetADValue();
      50. }
      51. /*********************************************************************
      52. *
      53. * _cbWinADV
      54. */
      55. static void _cbWinADV(WM_MESSAGE * pMsg) {
      56. int yPos;
      57. switch (pMsg->MsgId) {
      58. case WM_PAINT:
      59. //
      60. // Clear window with white
      61. //
      62. GUI_SetBkColor(GUI_WHITE);
      63. GUI_Clear();
      64. GUI_SetColor(GUI_RED);
      65. //
      66. // Draw graph
      67. //
      68. if (_GraphInfo.pData) {
      69. yPos = WM_GetWindowSizeY(pMsg->hWin) / 2;
      70. GUI_DrawGraph(_GraphInfo.pData, _GraphInfo.NumElements, 0, yPos);
      71. }
      72. break;
      73. default:
      74. WM_DefaultProc(pMsg);
      75. break;
      76. }
      77. }
      78. /*********************************************************************
      79. *
      80. * _cbBk
      81. */
      82. static void _cbBk(WM_MESSAGE * pMsg) {
      83. switch (pMsg->MsgId) {
      84. case WM_PAINT:
      85. GUI_SetBkColor(GUI_BLACK);
      86. GUI_Clear();
      87. break;
      88. default:
      89. WM_DefaultProc(pMsg);
      90. break;
      91. }
      92. }
      93. /*********************************************************************
      94. *
      95. * Public code
      96. *
      97. **********************************************************************
      98. */
      99. /*********************************************************************
      100. *
      101. * MainTask
      102. */
      103. void MainTask(void) {
      104. int xSize;
      105. int ySize;
      106. WM_HWIN hWin;
      107. memset((void *)&_GraphInfo, 0, sizeof(GRAPH_INFO));
      108. GUI_Init();
      109. WM_MULTIBUF_Enable(1);
      110. WM_SetCallback(WM_HBKWIN, _cbBk); // A callback which clears the background for regions without a window
      111. //
      112. // Get the size of the screen
      113. //
      114. xSize = LCD_GetXSize();
      115. ySize = LCD_GetYSize();
      116. //
      117. // Create a window which should show the AD value, the value gets drawn in the callback function _cbWinADV()
      118. //
      119. hWin = WM_CreateWindowAsChild(10, 10, xSize - 20, ySize - 10, WM_HBKWIN, WM_CF_SHOW, _cbWinADV, 0);
      120. //
      121. // Init a buffer which can hold enough points for the graph
      122. //
      123. xSize = WM_GetWindowSizeX(hWin);
      124. _GraphInfo.pData = malloc(xSize * sizeof(U16)); // Init points with 0
      125. memset((void *)_GraphInfo.pData, 0, xSize * sizeof(U16));
      126. _GraphInfo.NumElements = xSize;
      127. //
      128. // Super loop, GUI_Delay() keeps the application alive, returns after 100ms
      129. //
      130. while (1) {
      131. GUI_Delay(100);
      132. _UpdateData();
      133. WM_InvalidateWindow(hWin);
      134. }
      135. }
      136. /*************************** End of file ****************************/
      Display All
      Regards,

      Sven
    • Hi Sven,

      I thank you very much for your quick reply and your helpful help.

      So, I have created the project using CubeMX 5.0.1 + Atollic TrueStudio + STemWin. In the Graphics / STemWin section, I click the execute button to open GUI Builder and create Framewin c file, then generate the code.
      Using Atollic, I have FramewinDLG and GUI_App files. And the code creates the skeleton of graphics on the screen. So far, it is okay.

      I have then worked and tried to integrate the code you had given me, so that I can create the graph. But, I have experienced some troubles to know how to integrate the code in my project. And the questions I have now are the following :
      1. How should I use the code in my project ?
      2. Which part of the code should be placed in the FramewinDLG ? And which code for GUI_App ?
      3. What is about the function CreateFramewin ?

      Please feel free to comment if you need further or detailed information about it.

      I thank you in advance for your technical help and with kind regards,

      Sue
      Files
      • FramewinDLG.txt

        (3.64 kB, downloaded 86 times, last: )
      • GUI_App.txt

        (3.32 kB, downloaded 221 times, last: )
    • Hi,

      try the code below.

      Simply call CreateFramewin() from GRAPHICS_MainTask(). Every 100ms a timer expires and calles the callback function _cbDialog(). Here I react on WM_TIMER and get a new AD value. This value gets set as new point for the data object attached to the graph.

      C Source Code

      1. /*********************************************************************
      2. * *
      3. * SEGGER Microcontroller GmbH & Co. KG *
      4. * Solutions for real time microcontroller applications *
      5. * *
      6. **********************************************************************
      7. * *
      8. * C-file generated by: *
      9. * *
      10. * GUI_Builder for emWin version 5.44 *
      11. * Compiled Nov 10 2017, 08:53:57 *
      12. * (c) 2017 Segger Microcontroller GmbH & Co. KG *
      13. * *
      14. **********************************************************************
      15. * *
      16. * Internet: www.segger.com Support: support@segger.com *
      17. * *
      18. **********************************************************************
      19. */
      20. // USER START (Optionally insert additional includes)
      21. #include <stdlib.h>
      22. // USER END
      23. #include "DIALOG.h"
      24. /*********************************************************************
      25. *
      26. * Defines
      27. *
      28. **********************************************************************
      29. */
      30. #define ID_FRAMEWIN_0 (GUI_ID_USER + 0x00)
      31. #define ID_GRAPH_0 (GUI_ID_USER + 0x01)
      32. // USER START (Optionally insert additional defines)
      33. // USER END
      34. /*********************************************************************
      35. *
      36. * Static data
      37. *
      38. **********************************************************************
      39. */
      40. // USER START (Optionally insert additional static data)
      41. // USER END
      42. /*********************************************************************
      43. *
      44. * _aDialogCreate
      45. */
      46. static const GUI_WIDGET_CREATE_INFO _aDialogCreate[] = {
      47. { FRAMEWIN_CreateIndirect, "Framewin", ID_FRAMEWIN_0, 0, 0, 240, 320, 0, 0x0, 0 },
      48. { GRAPH_CreateIndirect, "Graph", ID_GRAPH_0, 15, 23, 200, 63, 0, 0x0, 0 },
      49. // USER START (Optionally insert additional widgets)
      50. // USER END
      51. };
      52. /*********************************************************************
      53. *
      54. * Static code
      55. *
      56. **********************************************************************
      57. */
      58. // USER START (Optionally insert additional static code)
      59. /*********************************************************************
      60. *
      61. * _GetADValue
      62. */
      63. static I16 _GetADValue(void) {
      64. return (I16)(rand() % 50 + 1);
      65. }
      66. /*********************************************************************
      67. *
      68. * _cbBk
      69. */
      70. static void _cbBk(WM_MESSAGE * pMsg) {
      71. switch (pMsg->MsgId) {
      72. case WM_PAINT:
      73. GUI_SetBkColor(GUI_BLACK);
      74. GUI_Clear();
      75. break;
      76. default:
      77. WM_DefaultProc(pMsg);
      78. break;
      79. }
      80. }
      81. // USER END
      82. /*********************************************************************
      83. *
      84. * _cbDialog
      85. */
      86. static void _cbDialog(WM_MESSAGE * pMsg) {
      87. // USER START (Optionally insert additional variables)
      88. int xSize;
      89. WM_HWIN hItem;
      90. static GRAPH_DATA_Handle hData;
      91. // USER END
      92. switch (pMsg->MsgId) {
      93. // USER START (Optionally insert additional message handling)
      94. case WM_INIT_DIALOG:
      95. //
      96. // Init a buffer which can hold enough points for the graph
      97. //
      98. xSize = WM_GetWindowSizeX(pMsg->hWin);
      99. hData = GRAPH_DATA_YT_Create(GUI_RED, xSize, NULL, xSize);
      100. hItem = WM_GetDialogItem(pMsg->hWin, ID_GRAPH_0);
      101. GRAPH_AttachData(hItem, hData);
      102. WM_CreateTimer(WM_GetClientWindow(pMsg->hWin), 0, 100, 0);
      103. break;
      104. case WM_TIMER:
      105. GRAPH_DATA_YT_AddValue(hData, _GetADValue());
      106. WM_RestartTimer((WM_HTIMER)pMsg->Data.v, 0);
      107. break;
      108. // USER END
      109. default:
      110. WM_DefaultProc(pMsg);
      111. break;
      112. }
      113. }
      114. /*********************************************************************
      115. *
      116. * Public code
      117. *
      118. **********************************************************************
      119. */
      120. /*********************************************************************
      121. *
      122. * CreateFramewin
      123. */
      124. WM_HWIN CreateFramewin(void);
      125. WM_HWIN CreateFramewin(void) {
      126. WM_HWIN hWin;
      127. hWin = GUI_CreateDialogBox(_aDialogCreate, GUI_COUNTOF(_aDialogCreate), _cbDialog, WM_HBKWIN, 0, 0);
      128. return hWin;
      129. }
      Display All

      Regards,
      Sven
    • Hi Sven,

      I thank you very much for your reply and support with my issues. This is nice of you.

      So, I started the project with CubeMX + GUI Builder + Atollic, then generated the code. I then copied the code you have given me in the FramewinDLG.c of the project.
      The code compiled successfully without error. However, the display screen is the same between before and after the implementation of the last code. Even the background screen did not change its colour to BLUE, as the code written after WM_PAINT in the _cbBK callback routine. I can not attach the picture of the display screen as its size is too big.

      So, my questions are the following :
      #1. Did I misunderstand something ? Or, did I miss to code something ?
      #2. How can I learn this part of the emWIN efficiently, so that I will have a good understanding of the subject ?

      Any suggestions or tips are greatly appreciated, as I have got stuck technically with this matter.

      I thank you in advance and with kind regards,

      Sue
      Files
      • FramewinDLG.txt

        (4.56 kB, downloaded 81 times, last: )
      • GUI_App.txt

        (3.3 kB, downloaded 86 times, last: )
    • Hi,

      The callback function _cbBk() is not used, therefore it won't do anything. This was a mistake of mine because _cbBk() was a leftover of the previous example.

      But the dialog with the GRAPH widget should work. Try to set a breakpoint into WM_TIMER. If you don't get there you should check the timing functions GUI_X_GetTime() and GUI_X_Delay(). Those functions are most likely located in a file named GUI_X<*>.c and required for any timing related functionalities of emWin.

      The code in WM_TIMER takes care about updating the GRAPH widget. If you never end up there the GRAPH widget doesn't change.

      One very important point with emWin is Window Manager especially the callback mechanism (more in chapter 18 of the emWin user manual). Every widget (and the dialogs created with the GUIBuilder) is based on the Window Manager. Also, sometimes it can be handy to create a WM-window (WM_CreateWindowAsChild()) instead of a predefined widget.

      In my first example I have set up a single WM-window and used it to draw a graph.

      So I recommend to take a look into the chapter about the Window Manager and how it works.

      Regards,
      Sven
    • Good morning Sven,

      I thank you a lot for your reply, and also for your recommendation. Indeed, I learn and get to know more about emWin and Window Manager on your detailed manual. It helps to better understanding about the subject.

      So, here are the steps that I have been taking :
      #1. Using GUI Builder to make simple and progressive samples, with the help of the manual.
      #2. Taking back the samples you have given me and implement them in my project.

      It is a bit difficult for me to say when I would get back to give you the feedback about the samples. I have been working to make all these points to function correctly.

      I thank you for your support and see you soon.

      With kind regards,

      Sue
    • Hi Sven,

      I hope that you enjoyed your weekend.

      So, IT SIMPLY WORKS! And I am a happy guy. :)

      Firstly, I followed your suggestion about the timer and checked those 2 files. Activating the Systick handler had resolved this issue.
      Secondly, I made a step-by-step approach to work on the display. And yes, I am now able to display an image and also a window for the graph.
      Third step, I implemented your 2nd code in the graph window and can see its graph.
      So, that is a good step forward for me and I THANK YOU very much for your helpful support and assistance. That is really great! :thumbsup:

      These days I have been learning to implement the reading input via ADC, and then display the values on the graph.
      And I have this question which is not obvious for me :
      #1. Where should I put my codes in the files generated by CubeMX + GUI Builder + STemWin ? The dedicated space for "user code" seems to be very limited.
      #2. Where is the super loop for "the user code" ?

      Again, I thank you in advance for your help and reply, and have a nice day.

      With kind regards,

      Sue
      Files
      • GUI_App.txt

        (3.59 kB, downloaded 91 times, last: )
      • main.txt

        (18.21 kB, downloaded 107 times, last: )
      • WindowDLG.txt

        (4.09 kB, downloaded 88 times, last: )
    • Hi,

      The super loop is the while loop in GRAPHICS_MainTask() you will never return from it and it calls periodically GUI_Delay(). I would replace the HAL_Delays by GUI_Delays. The two HAL Delay() will cause a pause of 2 seconds. In this pause emWin has no chance to update the screen.

      Not sure about the CubeMX procedure to generate code. This is an ST tool and I'm not really familiar with it. Regarding the GUIBuilder. You should place the code between the 'user code' sections.

      To be honest, I use the GUIBuilder only to create a starting point for dialogs. The main part is done manually. For a beginner this might be a bit harder, but it gives you way more flexibility.

      In most cases I create a dialog with the GUIBuilder and copy paste the required parts into my real application.

      Regards,
      Sven
    • Good morning Sven,

      I thank you for your helpful reply.

      So, your explication confirms my understanding about the super loop and also the several C files generated by CubeMX and GUI Builder.
      Using CubeMX 5.0.1 and GUI Builder to start a project, there are main, GUI_App, WindowDLG / FramewinDLG and also other files generated. The super loop in function GRAPHICS_MainTask() is not clearly dedicated for 'user code'. And consequently, when the same project is regenerated with CubeMX, all the codes that are implemented in the super loop are deleted.

      I have got one question regarding this :
      #1. Is there a method to implement the codes within the super loop that will NOT be deleted ?
      My understanding is that adding the 'user section' in this super loop would be necessary and important.

      I thank you in advance for your answer and have a nice day.

      With kind regards,

      Sue
    • Hi,

      CubeMX is not a SEGGER tool. I can't say if there is a place where the user can place code in the super loop. I suggest to contact ST.
      But I agree with you that this should be possible.

      The GUIBuilder is a SEGGER tool and it shouldn't touch the user code (between the marks) in c-files generated by this tool.

      You could get around this by setting up a timer in the dialog. Each time the timer expires you can poll the AD values (not that elegant but should work).

      In WM_INIT_DIALOG call this:

      WM_CreateTimer(pMsg->hWin, YOUR_TIMER_ID, Period, 0);

      Add another case, WM_TIMER, to react on the timer message. In this case do something like this:

      Id = WM_GetTimerId((WM_HTIMER)pMsg->Data.v);
      if (Id == YOUR_TIMER_ID) {
      ADValue = GetADValue();
      WM_RestartTimer((WM_HTIMER)pMsg->Data.v, Period);
      }

      This way you can use the AD value directly within the dialog.

      Regards,
      Sven
    • Hey Sven,

      I thank you very much for your reply and also for your recommendation tip about the super loop issues. I am going to work on using the timer to update the values.

      The super loop without dedicated 'user section' is situated in GUI_App.c file; and I going to post this in ST forum to resolve the issues.
      And I will get back here to let you know the results.

      My project has been progressing fine, and I am so happy about it.
      So, BIG thank you for supporting my technical issues with STemWin. Your helps are really helpful and precise.

      I will certainly come back here in near future as my project goes.....

      Have a nice day and with kind regards,

      Sue
    • Hi Sven,

      I hope that you have been doing okay....

      So, I have been experiencing some technical difficulties with the graphical display of my project.
      And here are the points :
      #1. I can manage to measure and to display 2 graphs on the same window, however I am not able to make them suitable to my project.
      #2. The graphs of my project should correspond to the attached file below : photovoltaics I-U measurement.
      #3. And here are the detailed steps :
      #3.1. There are 2 measurements : the current I (via ADC) and the voltage U (via DAC). There is a potentiostat module for this. The power graph is the result of voltage x current.
      #3.2. The measurement starts by activating a push button once. It is a single sequence measurement.
      #3.3. The next push button will refresh the values / the displays and start another measurement.

      My questions are the following :
      #4. How should I proceed to make a single sequence measurement ?
      #5. What are the differences between GRAPH_DATA_YT (used in my codes) and GRAPH_DATA_XY ?
      #6. Do you think that I should use GRAPH_DATA_XY functions for my project ?

      I thank you in advance for your useful help and with my kind regards,

      Sue
      Images
      • 171_Mpp.jpg

        22.1 kB, 470×320, viewed 75 times
    • Hi,

      Attached is an example on how something like on the image can be realized. It is not complete (no scale, most likely to small, etc.), but should give you an idea on how to start.

      I just use 3 points for my data, you should use a point for every pixel in x-direction. After getting the data from ADC the data should be reprocessed to fit to the graph.

      Unfortunately, the GRAPH is not able to plot the data with anti aliased lines. In this case you should use our spline API (available since version 5.44).

      Regards,
      Sven
      Files
      • GraphSample.zip

        (20.34 kB, downloaded 56 times, last: )
    • Hey Sven,

      I thank you very much for your reply to my question. That is really great.

      I will require some times to take your example and make it work into my project, as I have been experiencing some technical issues with Atollic IDE, these last days.....
      But, as soon as I can make some progress on it, I will get back to you.

      Thank you again and have a nice day. :)

      With kind regards,

      Sue
    • Hello Sven,

      I am going to install and use Embedded Studio IDE, and understanding how it works.....

      Regarding your reply, my understanding is the following then :
      #1. The measurements start once an interrupt input (button) is activated. It is a single sequence measurement.
      #2. Read the ADC current and DAC voltage inputs.
      #3. Save the values in arrays : current, voltage and power points.
      #4. Reprocess these values and plot / display them on the screen window.
      #5. End of sequence.
      Please feel free to comment as your advice will be greatly appreciated. :thumbsup: Thank you.

      So far, I still can not make "the touch screen" function working on my STM32F429 board. Hence, there is still no chance to load your last code.

      Have a nice afternoon and with my kind regards,

      Sue
    • Dear Sven,

      Great and thank you for your feedback : it assures me that my method is correct.

      As for the Touch Screen configuration with STemWin, I have been collecting the questions+answers about this subject from Segger forum. And even though there are still some unclear points for me, I have gotten better understandings about the subject.

      I am afraid that the Touch Configuration file has not been attached. So, may I ask you to send me this file ? :)

      I thank you a lot for your help and wish you a nice weekend.

      With kind regards,

      Sue