当前位置:首页 > > 嵌入式云IOT技术圈
[导读]上篇分享了事件驱动型的菜单框架,也实现了一个基本的小项目。但是怎么看怎么都觉得界面不爽,单纯显示文字的方式实在是太单调了,如果想要显示颜色丰富的图片,MCU资源受限又很难直接在程序中直接定义图片大数组。于是,这次选择在SD卡中放图片,通过Fatfs去读取SD卡中的图片来进行显示……

上次我们分享了事件驱动型的菜单框架,也实现了一个基本的小项目,如下:

基于事件型表驱动法菜单框架之小熊派简易气体探测器实战项目开发(上)

但是怎么看怎么都觉得界面不爽,单纯显示文字的方式实在是太单调了,如果想要显示颜色丰富的图片,MCU资源受限又很难直接在程序中直接定义图片大数组。

于是,我选择在SD卡中放图片,通过Fatfs去读取SD卡中的图片来进行显示,图片都是我自己在阿里图库上找的开源素材,然后用PS自己P的:

废话不多说,来看看实际效果吧!演示视频如下:

这样看起来效果就舒服一点啦。

1、新增功能项

本节分享的内容相较于上篇文章修改/增加了如下功能:

底层配置

  • 修改LCD寄存器,提升LCD刷屏速度
  • 增加Fatfs、SD卡读写功能

应用逻辑

  • 增加模拟长按开机识别
  • 增加开机LOGO以及其它UI的显示
  • 增加菜单(阈值设置、设置、调试模式、仪器信息)

1.1、关于底层配置

1.1.1、提升屏刷新速度

由于要刷图,所以只能想办法尽量提升屏的刷新速度,于是在LCD手册里有这么一个寄存器,可以提升屏的刷新速度:

在LCD驱动初始化代码里,这个寄存器默认配置的是60Hz,也就是0x0F这个值

/* Frame Rate Control in Normal Mode */
LCD_Write_Cmd(0xC6);
// LCD_Write_Data(0x0F); //60HZ
LCD_Write_Data(0x01);  //111Hz 提升屏的刷新速度

本来设置为0x00为119Hz,但是设置完LCD就黑屏了,改为0x01就不会,目前没找到具体原因,将就着用吧。

1.1.2、增加Fatfs、SD卡读写功能


之前也分享了配置方法,详情可以看以下文章:

基于小熊派SD卡+Fatfs+移植开源iniparse解析库并使用

1.2、关于应用逻辑

1.2.1、增加模拟长按开机识别

由于开机前需要加载整幅图片,会存在刷新慢的问题,我们可以先关掉LCD背光,然后等图刷完了再开背光,这样看到就是一张完整的图,这个过程可以用长按一个按键开机的方式来代替,代码实现如下:

void PowerOn(void)
{
    static uint32_t power_press_count = 0;
    HAL_Delay(500);
    while(1)
    {
        if(HAL_GPIO_ReadPin(KEY1_GPIO_Port, KEY1_Pin) == 0)
        {
            power_press_count ++;
      
            if(power_press_count >= 100)
            {
                //开指示灯
                HAL_GPIO_WritePin(GPIOC, LED_Pin, GPIO_PIN_SET);
                while(HAL_GPIO_ReadPin(KEY1_GPIO_Port, KEY1_Pin) == 0)
                {
                    HAL_Delay(10);
                }
                break;
            }
        }
        else
        {
            power_press_count = 0;
        }

        HAL_Delay(10);
    }
}

然后在显示开机LOGO前调用即可,显示开机LOGO调用完毕后打开背光显示。

1.2.2、增加开机LOGO以及其它UI的显示

图片资源采用的是24位bmp图,这些图片都存放在SD卡根目录下的LOGO文件夹的子文件夹中:

(1)开机LOGO


(2)主页面UI

(3)检测页面动画LOGO

显示接口移植了硬石科技提供的bsp_bmp.c,他们用的是320*480的RGB屏,小熊派用的是240*240的屏,根据现有的屏我做了一些修改:

uint8_t pColorData[960];     /* 一行真彩色数据缓存 320 * 3 = 960 */
//uint8_t pColorData[720];     /* 一行真彩色数据缓存 240 * 3 = 720 */

显示bmp函数:

/**
  * 函数功能: 显示bmp图片, 24位真彩色
  * 输入参数: x:显示图片左上角x轴坐标
  *           y:显示图片左上角y轴坐标
  *           pic_name:显示图片文件名称
  * 返 回 值: 无
  * 说    明:图片宽度和高度根据图片大小而定
  */
void Lcd_show_bmp(uint16_t x, uint16_t y,char *pic_name)
{
 uint16_t i, j, k;
 int width, height, l_width;
  
 BMP_FileHeader FileHeader;
 BMP_InfoHeader InfoHeader;
  
/*-------------------------------------------------------------------------------------------------------*/
 f_res=f_open(&file,pic_name, FA_OPEN_EXISTING|FA_READ); 
 if(f_res == FR_OK)
 {
  BMP_DEBUG_PRINTF("Open file success\r\n");

  /* 读取文件头信息  两个字节*/         
  f_res=f_read(&file,&FileHeader,sizeof(BMP_FileHeader),&f_num);     
    
  /* 判断是不是bmp文件 "BM"*/
  if(FileHeader.bfType!=0x4d42)
  {
   BMP_DEBUG_PRINTF("file is not .bmp file!\r\n");
   return;
  }
  else
  {
   BMP_DEBUG_PRINTF("Ok this is .bmp file\r\n"); 
  }
  /* 读取BMP文件头信息*/      
  showBmpHeader(&FileHeader);
   
  /* 读取位图信息头信息 */
  f_res=f_read(&file,&InfoHeader,sizeof(BMP_InfoHeader),&f_num);      
  showBmpInforHeader(&InfoHeader);
 }    
 else
 {
  BMP_DEBUG_PRINTF("file open fail!\r\n");
  return;
 }  
/*-------------------------------------------------------------------------------------------------------*/
 width  = InfoHeader.biWidth;
 height = InfoHeader.biHeight; 
  
 /* 计算位图的实际宽度并确保它为32的倍数 */
 l_width = WIDTHBYTES(width* InfoHeader.biBitCount);
  
 if((l_width>960)||(InfoHeader.biBitCount!=24))
 {
  BMP_DEBUG_PRINTF("\n SORRY, PIC IS TOO BIG (X<=320 and bit!=16)\n");
  return;
  }
  f_lseek(&file,FileHeader.bfOffBits);
   
  if(InfoHeader.biBitCount == 24)
  {
    for(i=0;i    {
      /* 开一个图片大小的窗口*/
      LCD_OpenWindow(x, y+height-i-1, width, 1);
   LCD_Write_Cmd(0x2C);
      /* 读取一行bmp的数据到数组pColorData里面 */
      f_read(&file,pColorData,l_width,&f_num);      
      for(j=0;j      {        
        k = j*3;                  //一行中第K个像素的起点        
        //LCD_WRITE_DATA(RGB24TORGB16(pColorData[k+2],pColorData[k+1],pColorData[k])); //写入LCD-GRAM
        //这里调用的是写一次写两个字节的函数
        LCD_Write_2Byte(RGB24TORGB16(pColorData[k+2],pColorData[k+1],pColorData[k]));
      }
    }
  }
 f_close(&file);    
}

修改完后在如果需要图片显示则调用如下接口:

#define START_LOGO "0:/UI/start_logo/start_logo.bmp"

//最开始的时候调用挂载,只挂载一次就好了
//在串行FLASH挂载文件系统,文件系统挂载时会对串行FLASH初始化
f_res = f_mount(&fs, (TCHAR const*)SDPath, 1);
if(f_res == FR_OK)
  printf("》SD卡文件系统挂载成功\n");


//以后只需要调用显示即可
Lcd_show_bmp(0,0,START_LOGO);

注意:图片的路径是SD卡下存放的路径,一定要选对。

1.2.2、增加菜单(阈值设置、设置、调试模式、仪器信息)

在主页面长按右键进入菜单:

此时按左键可以切换选项,短按右键进入某一菜单项,长按右键退出回到主页面,这里只实现了仪器信息,其它项目还没有实现,但要添加也非常简单,期待后期分享。

菜单的实现也很简单,菜单项切换的逻辑和主页面的切换逻辑是一样的:

其余功能:后续还可以做报警记录存储、数据上传到OneNet或者华为云等平台、参数设置等等,总之这个项目可拓展性非常强,这些功能将在本项目开发的下一章节持续进行拓展并分享,欢迎及时关注我的码云仓库与微信公众号文章更新。

本节代码已同步到码云的代码仓库中:

获取方法如下:

1、新建一个文件夹

2、使用git clone远程获取小熊派所有案例代码

我还将之前做的一些项目以及练习例程在近期内全部上传完毕,与大家一起分享交流:

公众号粉丝福利时刻

这里我给大家申请到了福利,本公众号读者购买小熊派开发板可享受9折优惠,有需要购买小熊派以及腾讯物联网开发板的朋友,淘宝搜索即可,跟客服说你是公众号:嵌入式云IOT技术圈 的粉丝,立享9折优惠!

往期精彩

STM32系统bootloader应用

【C进阶】拿着"sizeof这些用法和坑"去吹牛吧!

基于事件型表驱动法菜单框架之小熊派简易气体探测器实战项目开发(上)

上海出差之行--领略外滩美景、RT-Thread总部之旅、嵌友面基、返程记录

觉得本次分享的文章对您有帮助,随手点[在看]并转发分享,也是对我的支持。

免责声明:本文内容由21ic获得授权后发布,版权归原作者所有,本平台仅提供信息存储服务。文章仅代表作者个人观点,不代表本平台立场,如有问题,请联系我们,谢谢!

本站声明: 本文章由作者或相关机构授权发布,目的在于传递更多信息,并不代表本站赞同其观点,本站亦不保证或承诺内容真实性等。需要转载请联系该专栏作者,如若文章内容侵犯您的权益,请及时联系本站删除。
换一批
延伸阅读

随着现代家庭生活方式不断升级,厨房已不再只是功能空间,而逐渐成为融合审美表达、健康管理与智能体验的重要场域。消费者在关注空间整体性的同时,也对食材储存的安全性、洁净度以及使用便利性提出了更高要求。基于对中国家庭真实使用场...

关键字: 冰箱 嵌入式

在嵌入式系统、工业物联网等各类电子设备中,UART与网口是两种应用广泛的通信接口,前者作为经典的串行通信接口,承担着简单设备互联、调试日志传输等基础任务,后者则专注于高速、远距离的数据交互,是设备接入网络、实现大数据量传...

关键字: 嵌入式 通信接口 网口通讯

在软件开发领域,设计模式被誉为“解决特定问题的最佳实践”,但在嵌入式开发中,它却常常处于“边缘地带”。许多嵌入式工程师职业生涯中可能从未刻意使用过设计模式,甚至认为这些“软件工程理论”与单片机、传感器、实时系统等硬件紧密...

关键字: 嵌入式 设计模式

美国旧金山和中国苏州2026年2月8日 /美通社/ -- 信达生物制药集团(香港联交所股票代码:01801),一家致力于研发、生产和销售肿瘤、自身免疫、代谢、眼科等重大疾病领域创新药物的生物制药公司,宣布与礼来制药达成战...

关键字: COM 代码 创始人 控制

香港2026年2月4日 /美通社/ -- 华钦科技集团(纳斯达克代码:CLPS,以下简称"华钦科技"或"集团")今日宣布其董事会已通过一项集团股份回购计划的决议。该决议声明,当集团股价低于每股2美元时,集团可在公开市场上...

关键字: PS BSP 代码 纳斯达克

在居住结构持续演进与消费需求不断升级的背景下,中国家庭厨房正经历从“功能集合”向“系统空间”的深层转变。厨房不再只是烹饪的场所,而逐渐成为融合效率、健康、美学与家庭互动的重要生活空间。基于对这一趋势的长期洞察,西门子家电...

关键字: 嵌入式 蒸玲珑

在物联网设备、工业控制系统和智能家居等嵌入式场景中,轻量级WEB服务器扮演着核心角色。它们不仅需要满足资源受限环境下的性能需求,还需兼顾安全性、可扩展性和开发效率。本文从资源占用、功能特性、适用场景三个维度,对比分析六大...

关键字: 嵌入式 WEB服务器

在嵌入式软件开发工具领域,一场悄然的变革正在发生。随着全球软件行业向订阅制转型,嵌入式软件开发工具的授权模式也迎来了重要调整。市场上的嵌入式软件开发工具基本可以分为三类:商用开发工具,开源开发工具和厂商私有开发工具,其中...

关键字: 嵌入式 MCU RISC-V

在资源受限的嵌入式系统中,传统调试工具(如JTAG)往往成本高昂且占用引脚资源。本文介绍一种基于串口的低成本调试方案,通过自定义协议实现内存数据的实时监控,硬件成本可降低80%以上,特别适用于8/16位MCU开发场景。

关键字: 嵌入式 串口 内存数据

随着国家家电以旧换新补贴政策的持续推进,绿色节能、品质升级正成为越来越多家庭的新年焕新关键词。面对消费者在居住空间、生活效率与健康体验上的多元需求,西门子家电围绕新春焕新节点,正式开启“开门红”焕新季,通过国家补贴与企业...

关键字: 嵌入式 咖啡机 嵌饮机
关闭