本帖最后由 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,下载速度得到的很大的提高。
实物图:
硬件框图:
开发板硬件布局:
【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开发板的工程。
2、生成工程后,配置SPI2为OLED屏的驱动。spi的配置如下:
这里特别需要提醒一下,sh1106的SPI时序需要配置CPOL为HIGH,CPHA为2 Edge。原因可以查阅sh1106的时序图。
3、配置SH1106的复位(RST),数据/命令(DC),片选(CS)引脚。具体如下:
配置这些引脚主要是兼顾开发板上的接线,使用开发板上的CN10的如下针脚:
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设为免费专用的工具。
我们只需要打开CRC即可,参数不需要特殊配置。
8、开始TIM7,由于我这次移植没有使用操作系统,所以需要用定时器来定时调用页面刷新。
同时需要开始中断使能。
7、配置touchGFX
按如下配置好为黑白模式、指定128*64
修改栈空间为0xA000
接着生成代码,并打开在工程的\TouchGFX下面打开TouchGFX Desgner
2、打开设计器后向界面添加一个数据时钟控件:
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如下:
6)修改ModelListener.hpp
• 添加Types.h
• 增加虚函数UpdateTime()
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接口
9、Screen1Presenter.cpp增加UpdateTime()
• 通知view更新显示
10、修改Screen1View.hpp
• 增加UpdateTime()
11、修改screen1View.cpp增加UpdateTime()
• 设置digitalClock控件时间
到此工程就全部完成了。
【总结】
在STM32上使用TouchGFX的图形化设计,简单易用,可支持的字库多,同时移植到其他的芯片也方便快捷。
编译下载到开发板后效果如下:
|