In this blog, we will see how we can interface an OLED display with a PSoC 6 device and add it to our application to make a simple standalone system. We developed a ModusToolbox application to interface a DHT-11 sensor with a PSoC 6 device and print the values on a serial terminal application. Printing on a serial terminal implies that the device needs to be always connected to a PC which can be an inconvenience.
If you are new to the blog series, then you can refer to the previous blog here.
A quick description on the OLED display before we get started. There are many variations of OLED displays to choose from. I am using an OLED display module containing an SSD1306 controller that is manufactured by Solomon Systech Limited and a 128x64 pixel dot-matrix OLED. This is a monochrome OLED display and can be interfaced using the I2C interface. Additional information about this display can be found here.
At this point, you might be expecting me to talk about the driver files needed by the OLED display and how to write low-level I2C interface APIs to interact with the driver files. Now, what if I told you that we have a ready-made board utility library that you can use right off the bat? That’s right! We have one! To add to it, we can now use the emWin library with OLED displays. Isn't that cool!
We will start with the application developed in the previous blog. If you do not have the application, you can download it from here. Build and program the application once to confirm that you have the application set up and are ready to begin. To use the OLED display we need to import the libraries and we are going to do just that. Open Library Manager and enable abstraction-rtos (under Abstraction Layers), display-oled-ssd1306 (under Board Utils) and emwin (under PSoC 6 Middleware). After the libraries are imported, open the application makefile, and add EMWIN_OSNTS and FREERTOS to the COMPONENTS variable as shown:
Just a few more steps, we are almost there. Copy FreeRTOSConfig.h file from <makefile_directory>/libs/freertos/Source/portable directory to <makefile_directory>/source directory. The <makefile_directory> is the directory in which the application makefile exists. The makefile will now consider the copied FreeRTOSConfig.h (in the source directory) and ignore the .h file in the libs directory. Now we need to make the following edits in the FreeRTOSConfig.h file that we copied to the source directory:
- Comment “#warning This is a template” line
- Change the values of configUSE_MUTEXES and configUSE_RECURSIVE_MUTEXES macros to 1 like this –
#define configUSE_MUTEXES 1
#define configUSE_RECURSIVE_MUTEXES 1
All done? Good! We now have the libraries needed for our application set up. Go ahead and build the project to ensure the libraries are set up correctly.
Next, create oled_task.h/.c files which will have the OLED Task for our application. The first step is to initialize the display and the libraries. To do that:
- Include mtb_ssd1306.h and GUI.h
- Initialize I2C peripheral using the I2C HAL Driver
- Initialize the OLED display using mtb_ssd1306_init_i2c() API
- Finally, initialize the emWin library using GUI_Init() API
If you are stuck or face any issues with the above steps you can refer to the Display OLED SSD1306 Guide. After you are done with these steps, your OLED task should be something similar to this:
You can now use the emWin library APIs in your task to interact with the OLED display. emWin Library has a vast set of features and I will not be covering all the features in this blog post. Refer to the emWin Graphics Library Guide for more information on all the features available. Let me explain what I have done so that you can try and replicate the same.
After initialization the OLED task performs the following functions:
- Displays the Infineon + Cypress logo
- Displays the tagline
- In the loop, it continuously updates the humidity and temperature readings
Digging into each of these functions in a little more detail:
Displaying the logo:
Just showing the humidity and temperature will achieve our end goal but to make things a bit more interesting I decided to display the Infineon + Cypress logo and the tagline. This way I get to learn how to display images using emWin! To display an image you will have to first convert the image into a C structure. How do you do that? I will get to this question at the end of this blog. If you know how to do that, great! If not, for now, let's assume we have the structure ready. Once you have the C structure, you first need to set the colour of the image and the background colour and then display the image using the emWin API - GUI_DrawBitmap(). The image needs to stay for a while for us to perceive the image, so a delay is added and then the display is cleared each time using GUI_Clear().
Displaying the tagline:
This functionality is more straightforward as it is just displaying the text. A few APIs that prove to be useful:
- GUI_SetTextMode() – You can use this API to reverse the foreground and background colour, invert the colours, etc.
- GUI_SetFont() – Sets the font to be used for text output.
- GUI_SetTextAlign() – Sets the alignment for the text in both the X and Y-axis.
Finally, I have used the GUI_DispStringAt() API to display the text at the specified X and Y position (in pixels) respectively on the OLED display.
Displaying the sensor readings:
Before we get into displaying the sensor readings we will have to consider one more key aspect – how to receive the sensor readings from the DHT task. Previously we had used xQueueReceive() API in the Print task to receive data. We cannot do the same here because the API removes the data from the queue when reading from the queue. So, only one task will receive the data. We can use the xQueuePeek() API which just reads the data from the queue without removing the data. Sounds good? Not really. We now have another problem in our hands. The queue is never cleared so how do we inform the tasks that the elements in the queue are updated? I could think of 2 approaches to tackle this issue:
- All tasks except the lowest priority task (Print task in this case) peeks and the lowest priority task clears the queue using the receive API. In this case, we will have to ensure that the Print task is always executed last and it is executed every time the queue is updated without fail. This might cause synchronization issues especially when there are a lot of tasks in the application.
- Use FreeRTOS notifications to notify each task that the queue is updated. This works. But we will have to create a new task notification for each new task added to the application which can become a bit messy. On the same lines, we could use an event group to notify each task when the queue is updated and I decided to go with this approach. If you have a better approach to tackle this issue please leave a comment. I would love to know about it and learn.
Now that we can get the data from the DHT task we will have to display this data. I have used the same APIs again to display the strings ‘Humidity:’ and ‘Temperature:’. To display the float temperature and humidity values I have used the GUI_DispFloat(). This API also takes in the number of characters to display (including the decimal point) and I have specified 5 characters in this application.
That’s pretty much it for the OLED task. Few modifications to the other tasks to incorporate the new developments in the other tasks like the event group etc and you should be good to go! Like before, the entire application is shared on our Community Code Examples Git Repo and you can check it out using this link. Hope you have fun trying it out! To make things a little more interesting, you can try using EInk or TFT displays and experiment with emWin a bit more to customize the application. You can use our code examples as a reference. Created something cool? Feel free to share it with the community
Now let me get back to the question as promised,
How do I convert an image to a C structure?
For this, I used a tool – Segger Bitmap Converter. Download and install the tool. After the tool is set up, click on File > Open and select the image that you want to convert to a C structure. For example, I will select an image of PSoC 6 and the image should open in the tool as shown:
The first thing to do is to reduce the image size to fit the display that you choose. For the OLED display that I am using, I need to reduce it to something less than 128x64. To do this, click on Image > Scale and you can reduce the size in the window that appears:
Next, we will have to make the image monochrome. Click on Image > Conver to > BW
It's ready to be converted to a C structure! Click on File > Save As and give a name to the C file. Next, select 1 bit per pixel in the Format specifications window and click Ok. A .c file will be generated and you will find a similar structure in the file:
You can extern this structure and use it in your application.
There is an entire section dedicated to 2D Graphics in the emWin User Guide and Reference Manual. A lot of fascinating features, do check it out!