[STM32G4]

STM32G474RE移植TouchGFX

[复制链接]
9636|2
手机看帖
扫描二维码
随时随地手机跟帖
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屏。
主要的代码如下:
写一个字节:
/****************************************************************************
* 名        称:uint8_t OLED_Write_Byte(uint8_t data)
* 功        能:OLED写一个字节
* 入口参数:无
* 出口参数:接收的数据
* 返回值  :0失败,1成功
* 说        明:无
****************************************************************************/
uint8_t OLED_Write_Byte(uint8_t data)
{
        if(!HAL_SPI_Transmit(&hspi2, &data, 1, 10))
                return 0;
        else
                return 1;
}
写数据与指令:
/****************************************************************************
* 名        称:void OLED_Write_Operate(uint8_t mode,uint8_t data)
* 功        能:OLED写操作
* 入口参数:模式,数据
* 出口参数:无
* 返回值  :无
* 说        明:无
****************************************************************************/
void OLED_Write_Operate(uint8_t mode,uint8_t data)
{
        OLED_CS_Low;
        if(mode)
        {
                OLED_Write_Data;
        }
        else
        {
                OLED_Write_Cmd;
        }       
        OLED_Write_Byte(data);       
        OLED_CS_High;
}
画点:
/*
* Draw one pixel in the screenbuffer
* X => X Coordinate
* Y => Y Coordinate
* color => Pixel color
*/
void ssd1306_DrawPixel(uint8_t x, uint8_t y, SSD1306_COLOR color) {
    if(x >= MAX_COLUMN || y >= MAX_ROW) {
        // Don't write outside the buffer
        return;
    }
   
    // Draw in the right color
    if(color == White) {
        SSD1306_Buffer[x + (y / 8) * MAX_COLUMN] |= 1 << (y % 8);
    } else {
        SSD1306_Buffer[x + (y / 8) * MAX_COLUMN] &= ~(1 << (y % 8));
    }
}
画图片:
/* Draw a bitmap */
void ssd1306_DrawBitmap(uint8_t x, uint8_t y, const unsigned char* bitmap, uint8_t w, uint8_t h, SSD1306_COLOR color) {
    int16_t byteWidth = (w + 7) / 8; // Bitmap scanline pad = whole byte
    uint8_t byte = 0;

    if (x >= SSD1306_WIDTH || y >= SSD1306_HEIGHT) {
        return;
    }

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

            if (byte & 0x80) {
                ssd1306_DrawPixel(x + i, y, color);
            }
        }
    }
    return;
}
把整个图形缓冲区更新到OLED显存:
/* Write the screenbuffer with changed to the screen */
void ssd1306_UpdateScreen(void) {
    // Write data to each page of RAM. Number of pages
    // depends on the screen height:
    //
    //  * 32px   ==  4 pages
    //  * 64px   ==  8 pages
    //  * 128px  ==  16 pages
    for(uint8_t i = 0; i < SSD1306_HEIGHT/8; i++) {
                OLED_Write_Operate(OLED_Mode_Cmd,0xB0 + i);// Set the current RAM page address.
        OLED_Write_Operate(OLED_Mode_Cmd,(0x00 + SSD1306_X_OFFSET_LOWER));
        OLED_Write_Operate(OLED_Mode_Cmd,(0x10 + SSD1306_X_OFFSET_UPPER));
                OLED_CS_Low;
                OLED_Write_Data;
                HAL_SPI_Transmit(&hspi2, &SSD1306_Buffer[SSD1306_WIDTH*i], SSD1306_WIDTH, 1000);
                OLED_CS_High;
    }
}
使用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的头文件的引用:
#include "oled.h"
#include <touchgfx/hal/OSWrappers.hpp>
2)修改flushFrameBuffer函数,添加获取图片、更新到OLED的功能,代码如下:
void TouchGFXHAL::flushFrameBuffer(const touchgfx::Rect& rect)
{
    // Calling parent implementation of flushFrameBuffer(const touchgfx::Rect& rect).
    //
    // To overwrite the generated implementation, omit call to parent function
    // and implemented needed functionality here.
    // Please note, HAL::flushFrameBuffer(const touchgfx::Rect& rect) must
    // be called to notify the touchgfx framework that flush has been performed.
    // To calculate he start adress of rect,
    // use advanceFrameBufferToRect(uint8_t* fbPtr, const touchgfx::Rect& rect)
    // defined in TouchGFXGeneratedHAL.cpp

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

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

}

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


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读出来的时间。

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

135

主题

665

帖子

6

粉丝