[STM32G4] STM32G474RE移植TouchGFX

[复制链接]
 楼主| lulugl 发表于 2024-2-16 13:38 | 显示全部楼层 |阅读模式
本帖最后由 lulugl 于 2024-2-16 13:38 编辑

#有奖活动# #申请原创# @21小跑堂

【移植器材】
1、NUCLEO-G474RE开发板
2、SH1106显示屏(SPI接口)
【开发环境】
1、win11
2、STM32CubeMAX6.10.0
3、MDK5.38
4、TouchGFX4.23.0
5、Vscode
【开发板简介】
板子搭载的是M4内核,速度快,高达170M主频,和数**算加速器CORDIC大大提高运算能力, 输入电压范围1.71~3.6V,512Flash,128K的SRAM,1个用户LED,一个用户按键,32.768khz的外部低速晶振,外接24M的高速晶振,Micro-AB连接器,Arduino™ Uno V3连接器可扩与Arduino™ Uno V3连接,板载STLINK_V3仿真调试器,调试器的主控是STM32F723,下载速度得到的很大的提高。
实物图:
1857565ced9bb3ecb1.png
硬件框图:
7899365ceda91a8532.png
开发板硬件布局:
7301965cedad026d87.png
【sh1106OLED屏介绍】
支持最大 132 X 64 点矩阵面板
嵌入式 132 X 64 位 SRAM
工作电压:
逻辑电源:VDD1 = 1.65V-3.5V
DC-DC电源:VDD2 = 3.0V-4.2V
OLED工作电源:
外部VPP电源 = 6.4V-14.0V
内部VPP发生器 = 6.4V-9.0V
最大段输出电流:200mA
最大公共灌电流:27mA
8 位 6800 系列并行接口,8 位 8080 系列并行接口,3 线和 4 线串行外设接口,400KHz 快速 I2C 总线接口
可编程帧频和复用率
行重映射和列重映射(ADC)
垂直滚动
片内振荡器
可编程内部电荷泵电路输出
单色无源OLED面板上的256步对比度控制
低功耗
睡眠模式:< 5μA
VDD1 = 0V,VDD2 = 3.0V – 4.2V:< 5μA
VDD1,2 = 0V,VPP = 6.4V –14.0V:< 5μA
广泛的工作温度范围:-40至+ 85°C
可提供COG形式,厚度:300mm
        SH1106 是具有用于有机 / 聚合物发光二极管点矩阵图形显示系统的控制器的单芯片CMOS OLED / PLED 驱动器。 SH1106 由 132 个段,64 个公共端组成,可支持 132 X 64 的最大显示分辨率。它是为公共阴极型 OLED 面板设计的。
        SH1106 嵌入了对比度控制,显示 RAM 振荡器和高效的 DC-DC 转换器,从而减少了外部组件的数量和功耗。 SH1106 适用于各种紧凑型便携式应用,例如手机的子显示屏,计算器和 MP3 播放器等。
【TouchGFX介绍】
ouchGFX是一款针对STM32微控制器进行了优化的免费高级图形软件框架。借助STM32图形功能和架构,TouchGFX可通过创建类似于智能手机的图形用户界面,来加快HMI-of-things技术革新。
TouchGFX框架包含易于使用的拖放式图形构建PC工具TouchGFX Designer (TouchGFXDesigner)以及强大的优化图形处理内核TouchGFX引擎。结合WYSIWYG仿真器和自动代码生成功能,TouchGFX大大简化了GUI开发。通过对完成的原型进行快速迭代,它涵盖了从早期设计草图到生成独家最终产品的所有步骤。
TouchGFX Designer可作为独立的软件工具提供,便于快速轻松地进行图形评估和概念验证。TouchGFX框架(包括TouchGFX Designer)包含在STM32Cube MCU软件包中。它完全兼容STM32CubeMX初始化和代码生成工具,便于在统一项目环境中无缝地联合开发图形和主应用程序。
所有功能
结构:轻松创建多屏幕内容和相关转换
小部件:广泛的可定制小部件,如滑动容器和周期进度,便于轻松创建GUI
皮肤:

  • 一组即用型图形化皮肤,可实现一致的原型设计,而无需图形化设计工具
  • 不限制使用自定义图形
  • 交互:动态交互,便于创建用户友好型应用
  • 自定义容器:
  • 创建用户可重用的应用控件
  • 具有统一观感的轻松平台开发

文本处理:

  • 在单一位置指定和管理的字体和排版
  • 完整的翻译服务

完全支持多种字母和脚本,如拉丁语、西里尔语、阿拉伯语、汉语和日语
代码生成:

  • TouchGFXDesigner可生成和维护高性能C++代码
  • 工具生成的代码与用户代码完全分离
  • 各种代码扩展可实现独特的动画与系统互联
  • 支持多种集成式开发环境,如IAR Embedded Workbench、Arm Keil和基于GCC的IDE


【实现步骤】
1、使用STM32CubeMAX生成基于NUCLEO-G474RE开发板的工程。
8130565cedd5f3fe19.png
2、生成工程后,配置SPI2为OLED屏的驱动。spi的配置如下:
3515365cedde496447.png
这里特别需要提醒一下,sh1106的SPI时序需要配置CPOL为HIGH,CPHA为2 Edge。原因可以查阅sh1106的时序图。
3、配置SH1106的复位(RST),数据/命令(DC),片选(CS)引脚。具体如下:
8264165cedeeaf1149.png
配置这些引脚主要是兼顾开发板上的接线,使用开发板上的CN10的如下针脚:
4256965cedf3d31f88.png
4、配置好后生成MDK工程,添加OLED的驱动。使得可以点亮OLED屏。
主要的代码如下:
写一个字节:
  1. /****************************************************************************
  2. * 名        称:uint8_t OLED_Write_Byte(uint8_t data)
  3. * 功        能:OLED写一个字节
  4. * 入口参数:无
  5. * 出口参数:接收的数据
  6. * 返回值  :0失败,1成功
  7. * 说        明:无
  8. ****************************************************************************/
  9. uint8_t OLED_Write_Byte(uint8_t data)
  10. {
  11.         if(!HAL_SPI_Transmit(&hspi2, &data, 1, 10))
  12.                 return 0;
  13.         else
  14.                 return 1;
  15. }
写数据与指令:
  1. /****************************************************************************
  2. * 名        称:void OLED_Write_Operate(uint8_t mode,uint8_t data)
  3. * 功        能:OLED写操作
  4. * 入口参数:模式,数据
  5. * 出口参数:无
  6. * 返回值  :无
  7. * 说        明:无
  8. ****************************************************************************/
  9. void OLED_Write_Operate(uint8_t mode,uint8_t data)
  10. {
  11.         OLED_CS_Low;
  12.         if(mode)
  13.         {
  14.                 OLED_Write_Data;
  15.         }
  16.         else
  17.         {
  18.                 OLED_Write_Cmd;
  19.         }       
  20.         OLED_Write_Byte(data);       
  21.         OLED_CS_High;
  22. }
画点:
  1. /*
  2. * Draw one pixel in the screenbuffer
  3. * X => X Coordinate
  4. * Y => Y Coordinate
  5. * color => Pixel color
  6. */
  7. void ssd1306_DrawPixel(uint8_t x, uint8_t y, SSD1306_COLOR color) {
  8.     if(x >= MAX_COLUMN || y >= MAX_ROW) {
  9.         // Don't write outside the buffer
  10.         return;
  11.     }
  12.    
  13.     // Draw in the right color
  14.     if(color == White) {
  15.         SSD1306_Buffer[x + (y / 8) * MAX_COLUMN] |= 1 << (y % 8);
  16.     } else {
  17.         SSD1306_Buffer[x + (y / 8) * MAX_COLUMN] &= ~(1 << (y % 8));
  18.     }
  19. }
画图片:
  1. /* Draw a bitmap */
  2. void ssd1306_DrawBitmap(uint8_t x, uint8_t y, const unsigned char* bitmap, uint8_t w, uint8_t h, SSD1306_COLOR color) {
  3.     int16_t byteWidth = (w + 7) / 8; // Bitmap scanline pad = whole byte
  4.     uint8_t byte = 0;

  5.     if (x >= SSD1306_WIDTH || y >= SSD1306_HEIGHT) {
  6.         return;
  7.     }

  8.     for (uint8_t j = 0; j < h; j++, y++) {
  9.         for (uint8_t i = 0; i < w; i++) {
  10.             if (i & 7) {
  11.                 byte <<= 1;
  12.             } else {
  13.                 byte = (*(const unsigned char *)(&bitmap[j * byteWidth + i / 8]));
  14.             }

  15.             if (byte & 0x80) {
  16.                 ssd1306_DrawPixel(x + i, y, color);
  17.             }
  18.         }
  19.     }
  20.     return;
  21. }
把整个图形缓冲区更新到OLED显存:
  1. /* Write the screenbuffer with changed to the screen */
  2. void ssd1306_UpdateScreen(void) {
  3.     // Write data to each page of RAM. Number of pages
  4.     // depends on the screen height:
  5.     //
  6.     //  * 32px   ==  4 pages
  7.     //  * 64px   ==  8 pages
  8.     //  * 128px  ==  16 pages
  9.     for(uint8_t i = 0; i < SSD1306_HEIGHT/8; i++) {
  10.                 OLED_Write_Operate(OLED_Mode_Cmd,0xB0 + i);// Set the current RAM page address.
  11.         OLED_Write_Operate(OLED_Mode_Cmd,(0x00 + SSD1306_X_OFFSET_LOWER));
  12.         OLED_Write_Operate(OLED_Mode_Cmd,(0x10 + SSD1306_X_OFFSET_UPPER));
  13.                 OLED_CS_Low;
  14.                 OLED_Write_Data;
  15.                 HAL_SPI_Transmit(&hspi2, &SSD1306_Buffer[SSD1306_WIDTH*i], SSD1306_WIDTH, 1000);
  16.                 OLED_CS_High;
  17.     }
  18. }
使用TouchGFX有这几个函数就可以了,因为touchfx是把一整个显示以图片的方式提交给画图与更新函数,这个以后细说。
程序到此,我们把程序下载到开发板如果能正常的填满整个OLED屏就成功了。接下面开始配置touchgfx。
6、下载touchgfx软件包:
在官方教程:安装 | TouchGFX Documentation里有详细的下载安装toucgfx软件包。大家可以去这里查看,本章节不予介绍。
7、下载好后,我们开始配置软件包,首先需要打开CRC功能,这是STM32把touchgfx设为免费专用的工具。
3973665cee215523ad.png
我们只需要打开CRC即可,参数不需要特殊配置。
8、开始TIM7,由于我这次移植没有使用操作系统,所以需要用定时器来定时调用页面刷新。
4753265cee286c6055.png
同时需要开始中断使能。
7、配置touchGFX
9673965cee484555e2.png
按如下配置好为黑白模式、指定128*64
9245065cee53d3b7b0.png
修改栈空间为0xA000
9190065cee696452e9.png
接着生成代码,并打开在工程的\TouchGFX下面打开TouchGFX Desgner
2153865cee6c406910.png
2、打开设计器后向界面添加一个数据时钟控件:
6956265cee9bad0fc1.png
3、更新一下代码,然后用vscode 打开工程文件夹,添加touchGFX自定义代码:
1)添加OLED的头文件的引用:
  1. #include "oled.h"
  2. #include <touchgfx/hal/OSWrappers.hpp>
2)修改flushFrameBuffer函数,添加获取图片、更新到OLED的功能,代码如下:
  1. void TouchGFXHAL::flushFrameBuffer(const touchgfx::Rect& rect)
  2. {
  3.     // Calling parent implementation of flushFrameBuffer(const touchgfx::Rect& rect).
  4.     //
  5.     // To overwrite the generated implementation, omit call to parent function
  6.     // and implemented needed functionality here.
  7.     // Please note, HAL::flushFrameBuffer(const touchgfx::Rect& rect) must
  8.     // be called to notify the touchgfx framework that flush has been performed.
  9.     // To calculate he start adress of rect,
  10.     // use advanceFrameBufferToRect(uint8_t* fbPtr, const touchgfx::Rect& rect)
  11.     // defined in TouchGFXGeneratedHAL.cpp

  12.     TouchGFXGeneratedHAL::flushFrameBuffer(rect);
  13.        
  14.           const unsigned char* bitmap = (const unsigned char*) getClientFrameBuffer(); //获取图片
  15.     ssd1306_Fill(Black);//清屏
  16.     ssd1306_DrawBitmap(0, 0, bitmap, 128, 64, White); //写入图形
  17.     ssd1306_UpdateScreen(); //更新屏幕
  18. }
3)添加给C调用的函数如下,获取vSync信号
  1. extern "C"
  2. void touchgfxSignalVSync(void)
  3. {
  4.         /* VSync has occurred, increment TouchGFX engine vsync counter */
  5.         touchgfx::HAL::getInstance()->vSync();
  6.         /* VSync has occurred, signal TouchGFX engine */
  7.         touchgfx::OSWrappers::signalVSync();
  8. }
4)在main.c中添回TIM7的回调函数,实现周期调用刷新
  1. /* USER CODE BEGIN 4 */
  2. extern void touchgfxSignalVSync(void);
  3. void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
  4. {
  5.         if (htim->Instance == TIM7)
  6.         {
  7.                 touchgfxSignalVSync();
  8.         }
  9. }
  10. /* USER CODE END 4 */
5)在主程序中启用TIM7的启动
  1.   /* USER CODE BEGIN 2 */
  2.         HAL_TIM_Base_Start_IT(&htim7);
  3.         OLED_Init();

  4.   /* USER CODE END 2 */
到此,用MDK编译好后就可以实现touchGFX来实现点亮屏了。
为了实现动态的显示时间,我们配置RTC如下:
1676665ceecf3c43a0.png
6)修改ModelListener.hpp
添加Types.h
增加虚函数UpdateTime()
9879765ceee75a0993.png
7、修改model.cpp
• 通过HAL接口获取RTC时间
  1. #include <gui/model/Model.hpp>
  2. #include <gui/model/ModelListener.hpp>
  3. #include "stm32g4xx_hal.h"
  4. extern RTC_HandleTypeDef hrtc;
  5. RTC_TimeTypeDef Time = {0};
  6. RTC_DateTypeDef Date = {0};
  7. Model::Model() : modelListener(0)
  8. {

  9. }

  10. void Model::tick()
  11. {
  12.     HAL_RTC_GetTime(&hrtc, &Time, RTC_FORMAT_BIN);
  13.     HAL_RTC_GetDate(&hrtc, &Date, RTC_FORMAT_BIN);
  14.     modelListener->UpdateTime(Time.Hours, Time.Minutes, Time.Seconds);
  15. }


8、修改Screen1Presenter.hpp
• 添加UpdateTime接口
1682865cef03d702eb.png
9、Screen1Presenter.cpp增加UpdateTime()
• 通知view更新显示
404365cef0f0d4329.png
10、修改Screen1View.hpp
• 增加UpdateTime()
4889165cef1358d59b.png
11、修改screen1View.cpp增加UpdateTime()
• 设置digitalClock控件时间
9884965cef1c8ef270.png
到此工程就全部完成了。
【总结】
在STM32上使用TouchGFX的图形化设计,简单易用,可支持的字库多,同时移植到其他的芯片也方便快捷。
编译下载到开发板后效果如下:
1211265cef3421b2fc.jpg

梅花香自123 发表于 2024-2-22 18:40 | 显示全部楼层
楼主,您提到了通过TouchGFX实现了动态的显示时间,这一功能是否达到了预期效果?
 楼主| lulugl 发表于 2024-2-22 21:54 | 显示全部楼层
梅花香自123 发表于 2024-2-22 18:40
楼主,您提到了通过TouchGFX实现了动态的显示时间,这一功能是否达到了预期效果? ...

可以的呀,实时更新的。从RTC读出来的时间。
您需要登录后才可以回帖 登录 | 注册

本版积分规则

180

主题

830

帖子

12

粉丝
快速回复 在线客服 返回列表 返回顶部