1、概述

首先介绍下什么是HaaS:2020年9月17日,在云栖大会上阿里云IoT团队正式发布了HaaS(Hardware as a Servie)。HaaS是一种物联网设备云端一体开发框架,它的目的是通过数量收敛的硬件积木(比如主控板:HaaS100,HaaS eduk1, wifi+BT模组;比如各种认证的传感器)和丰富、标准的软件积木(包括各种组件、云端服务以及钉钉公版小程序)持续降低物联网开发门槛,让用户(包括c/c++,JS,Python用户)可以快速用我们提供的软硬件积木搭建应用,并且不用关心任何硬件调试,只需要关注“云端钉”的业务逻辑代码即可。

本用例会分两篇文章介绍如何实现一个云端一体化可升级的电子相册(这个电子相册不但能播放图片,还要能通过云端随时更新图片),此篇文章主要介绍如何基于HaaS eduk1把图片显示出来;

硬件:HaaS eduk1开发板,ili9341 lcd屏;
软件:AliOS Things 3.3 版本;

2、步骤

2.1、硬件连接

如下图为HaaS eduk1 的扩展口:

如下图为ili9341的扩展脚:


选择eduk1 的spi0 驱动ili9341,对应连线关系为:

eduk1

ili9341

GPIO_P07

SDI(MOSI)

GPIO_P06

CS

GPIO_P05

SCK

GPIO_P04

SDO(MISO)

GPIO_P22

LED

GPIO_P03

DC

GPIO_P02

RESET

GND

GND

3V3

VCC

按照上表接完线如下图:

2.2、初始化ili9341

步骤2.1完成了硬件的连接,接下来对ili9341进行软件初始化;首先需要选择一个solution,这里选择ota_demo,以此用例进行开发,把ili9341的显示能力集成到这个用例里,选择此用例的原因是后面需要借助OTA的能力对相册的图片进行升级;软件的用例选择和环境搭建请参考《HaaS EDU k1_day1_硬件介绍及开发环境搭建》 ;AliOS Things 3.3已经支持了对ili9341的驱动,具体细节可参考 components/drivers/external_device/ili9341/README.md,调用相关函数可实现画点画线等;根据步骤1的接线图,重新初始化ili9341,代码如下:

void user_ili9341_hw_init()
{user_ili9341.spi_port = 0;  //选择spi0user_ili9341.spi_freq = 26000000;user_ili9341.gpio_bgl_id = HAL_GPIO_PIN_P2_2; //GPIO_22 控制背光灯user_ili9341.gpio_dc_id = HAL_GPIO_PIN_P0_3;user_ili9341.gpio_reset_id = HAL_GPIO_PIN_P0_2;ili9341_hw_init(&user_ili9341);return;
}

2.3、解析bmp文件

AliOS Things 3.3 已经集成了ili9341驱动,但还不能直接显示一幅图片,本文介绍如何显示bmp格式的图片,ili9341 spi lcd支持的是16位色深的图片,所以我们需要将24位色深的bmp格式的图片转成16位的然后再送显;简单介绍下bmp格式的图片:BMP(全称Bitmap)是Windows操作系统中的标准图像文件格式,使用非常广。它采用位映射存储格式,除了图像深度可选以外,不采用其他任何压缩,因此,BMP文件所占用的空间很大。BMP文件的图像深度可选lbit、4bit、8bit及24bit。BMP文件存储数据时,图像的扫描方式是按从左到右、从下到上的顺序。其数据存储方式:
1.在windows中,颜色顺序为:B G R;
2.BMP的存储行顺序和图像显示顺序是上下颠倒的,即BMP存储的最后一行是真实图片显示的第一行;
另外BMP的组成为:位图信息+实际的图像数据;如下数据结构为bmp的图片信息头:

typedef struct {             // BMP文件头结构char magic[2];           // 位图文件的类型(BM)。unsigned int size;       // 位图文件的大小,以字节为单位short reserved1;         // 位图文件保留字(0)short reserved2;         // 位图文件保留字(0)unsigned int offset;     // 位图数据的起始位置,以相对于位图
} bmp_file_head_t;typedef struct {               //图像信息区unsigned int info_size;    //本结构体所占用字节数(40bytes)int width;                 // 位图的宽度,以像素为单位,像素数量是4字节对齐的int height;                // 位图的高度,以像素为单位unsigned short planes;     // 目标设备的级别,必须为1unsigned short count;      // 每个像素所需的位数,必须是1(双色)// 4(16色),8(256色)或24(真彩色)之一unsigned int compression;  // 位图压缩类型,必须是 0(不压缩),// 1(BI_RLE8压缩类型)或2(BI_RLE4压缩类型)之一unsigned int image_size;   // 位图的大小,以字节为单位unsigned int xmeter;       // 位图水平分辨率,每米像素数unsigned int ymeter;       // 位图垂直分辨率,每米像素数unsigned int cused;         // 位图实际使用的颜色表中的颜色数unsigned int cimportant;    // 位图显示过程中重要的颜色数
} bmp_attr_info_t;

2.4、将24位RGB值转成16位RGB值

通过步骤2.3了解到BMP图片的构成,可根据上面的数据结构获取图片的色深,长和宽等信息,本文介绍的方案只支持320 * 240的图片,如下代码:

void pic_display(char *pic_path)
{int fd = 0;int i = 0;int ret = 0;memset(pic_frame, 0x00, sizeof(pic_frame));memset(tmp_buf, 0x00, sizeof(tmp_buf));if(pic_path == NULL) {printf("pic path is null!\r\n");return;}fd = aos_open(pic_path, O_RDONLY);if (fd < 0) {printf("open bmp %s failed\r\n", pic_path);}else {bmp_file_head_t bmp_head;bmp_attr_info_t bmp_attr;uint8_t tmp_color_r = 0;uint8_t tmp_color_g = 0;uint8_t tmp_color_b = 0;uint16_t color = 0;uint16_t tmp = 0;int line = 0;int column = 0;printf("open bmp success!\r\n");ret = aos_read(fd, tmp_buf, 54);if (ret < 0) {printf("read file failed\r\n");}bmp_head.magic[0] = tmp_buf[0];bmp_head.magic[1] = tmp_buf[1];bmp_head.size = EXTRACT_U32(&tmp_buf[2]);bmp_head.reserved1 = EXTRACT_U16(&tmp_buf[6]);bmp_head.reserved2 = EXTRACT_U16(&tmp_buf[8]);bmp_head.offset = EXTRACT_U32(&tmp_buf[10]);printf("bmp magic = %c%c\r\n", bmp_head.magic[0], bmp_head.magic[1]);printf("bmp size = %d\r\n", bmp_head.size);printf("bmp offset = %d\r\n", bmp_head.offset);memcpy(&bmp_attr, &tmp_buf[14], sizeof(bmp_attr));printf("bmp w = %d, h = %d\r\n", bmp_attr.width, bmp_attr.height);printf("bmp count = %d\r\n", bmp_attr.count);printf("bmp body size = %d\r\n", bmp_attr.image_size);if ((bmp_head.magic[0] != 'B') || (bmp_head.magic[1] != 'M')) {printf("pic isn't bmp!\r\n");} else {if((bmp_attr.count == 24) && (bmp_attr.width <= 320) && (bmp_attr.height <=240)) {memset(src_pic_frame, 0x00, sizeof(src_pic_frame));ret = aos_read(fd, src_pic_frame, bmp_attr.image_size);if (ret < 0) {printf("read bmp body failed\r\n");} else {i = 0;for (line = 239; line > 0; line--) {for (column = line * 960; column < (line * 960 + 959);) {tmp_color_b = src_pic_frame[column]; tmp_color_g = src_pic_frame[column + 1];tmp_color_r = src_pic_frame[column + 2];column = column + 3;tmp = (tmp_color_r >> 3) & 0xff;tmp = tmp << 11;color = tmp;tmp = (tmp_color_g >> 2) & 0xff;tmp = tmp << 5;color |= tmp;tmp = (tmp_color_b >> 3) & 0xff;color |= tmp;if (i < 320 * 240) {pic_frame[i++] = color;}else {printf("color numb err, column = %d\r\n", column);}}}}}}aos_close(fd);}ili9341_draw_frame(user_ili9341, pic_frame);printf("nano entry here!\r\n");
}

先打开bmp图片获取图片的信息,判断图片是否为BMP图片以及图片的规格,默认只支持24位的图片;函数解析图片为24位bmp图片后,会按照从低向上的顺序读取图片数据,并按照B R G的顺序将颜色数据转换成16位色深的数据保存在frame 缓存中,然后送显。

2.5、编译运行

2.5.1、将bmp图片制作和导入文件系统

从网上下载任意图片,然后用window自带的画图工具将图片打开,选择重新调整大小 ---> 调整图片为320 * 240 ----> 图片另存为bmp 24位图片;这样就完成了bmp图片的制作,然后将制作好的图片copy到AliOS Things代码的  hardware/chip/haas1000/prebuild/data中,这样当HaaS eduk1 运行时可以通过“/data/”路径下获取对应的图片;

2.5.2、代码添加

了解以上的步骤后,将本文的附件中的代码copy到一个单独的C文件中,比如bmp_parse.c,然后在 solutions/ota_demo/package.yaml 中增加 - ili9341: dev_aos 组件和 - "bmp_parse.c" 源文件;
接下来将bmp_parse.c中的函数 user_ili9341_hw_init() 和 pic_display(char pic_path) 在otaappdemo.c里的 application_start调用即可,其中pic_display(char pic_path) 的参数pic_path为bmp图片的存储路径即1中/data/xxx.bmp;如下图:

2.5.3、编译

完成以上所有步骤后,既可以编译和下载固件,具体参考《HaaS EDU k1_day1_硬件介绍及开发环境搭建》 ;

3、效果

运行起来的实际效果如下图:

4、附件

代码如下

#include <fcntl.h>
#include "ili9341.h"
#include "hal_iomux_haas1000.h"#define COLOR_RED         (0xF800)
#define COLOR_BREEN       (0x07E0)
#define COLOR_BLUE        (0x001F)typedef struct {             // BMP文件头结构char magic[2];           // 位图文件的类型(BM)。unsigned int size;       // 位图文件的大小,以字节为单位short reserved1;         // 位图文件保留字(0)short reserved2;         // 位图文件保留字(0)unsigned int offset;     // 位图数据的起始位置,以相对于位图
} bmp_file_head_t;typedef struct {               //图像信息区unsigned int info_size;    //本结构体所占用字节数(40bytes)int width;                 // 位图的宽度,以像素为单位,像素数量是4字节对齐的int height;                // 位图的高度,以像素为单位unsigned short planes;     // 目标设备的级别,必须为1unsigned short count;      // 每个像素所需的位数,必须是1(双色)// 4(16色),8(256色)或24(真彩色)之一unsigned int compression;  // 位图压缩类型,必须是 0(不压缩),// 1(BI_RLE8压缩类型)或2(BI_RLE4压缩类型)之一unsigned int image_size;   // 位图的大小,以字节为单位unsigned int xmeter;       // 位图水平分辨率,每米像素数unsigned int ymeter;       // 位图垂直分辨率,每米像素数unsigned int cused;         // 位图实际使用的颜色表中的颜色数unsigned int cimportant;    // 位图显示过程中重要的颜色数
} bmp_attr_info_t;typedef struct {bmp_file_head_t bmp_header;bmp_attr_info_t bmp_info;
} bmp_file_info_t;#define EXTRACT_U16(d) (*((unsigned char *)(d)) | (*((unsigned char *)(d) + 1) << 8))
#define EXTRACT_U32(d)                                              \(*((unsigned char *)(d)) | (*((unsigned char *)(d) + 1) << 8) | \(*((unsigned char *)(d) + 2) << 16) | (*((unsigned char *)(d) + 3) << 24))uint16_t pic_frame[320 * 240];
uint8_t src_pic_frame[320 * 240 * 3];
uint8_t tmp_buf[100];
ili9341_dev_t user_ili9341 = {0};void user_ili9341_hw_init()
{user_ili9341.spi_port = 0;user_ili9341.spi_freq = 26000000;user_ili9341.gpio_bgl_id = HAL_GPIO_PIN_P2_2;user_ili9341.gpio_dc_id = HAL_GPIO_PIN_P0_3;user_ili9341.gpio_reset_id = HAL_GPIO_PIN_P0_2;ili9341_hw_init(&user_ili9341);return;
}void pic_display(char *pic_path)
{int fd = 0;int i = 0;int ret = 0;memset(pic_frame, 0x00, sizeof(pic_frame));memset(tmp_buf, 0x00, sizeof(tmp_buf));if(pic_path == NULL) {printf("pic path is null!\r\n");return;}fd = aos_open(pic_path, O_RDONLY);if (fd < 0) {printf("open bmp %s failed\r\n", pic_path);}else {bmp_file_head_t bmp_head;bmp_attr_info_t bmp_attr;uint8_t tmp_color_r = 0;uint8_t tmp_color_g = 0;uint8_t tmp_color_b = 0;uint16_t color = 0;uint16_t tmp = 0;int line = 0;int column = 0;printf("open bmp success!\r\n");ret = aos_read(fd, tmp_buf, 54);if (ret < 0) {printf("read file failed\r\n");}bmp_head.magic[0] = tmp_buf[0];bmp_head.magic[1] = tmp_buf[1];bmp_head.size = EXTRACT_U32(&tmp_buf[2]);bmp_head.reserved1 = EXTRACT_U16(&tmp_buf[6]);bmp_head.reserved2 = EXTRACT_U16(&tmp_buf[8]);bmp_head.offset = EXTRACT_U32(&tmp_buf[10]);printf("bmp magic = %c%c\r\n", bmp_head.magic[0], bmp_head.magic[1]);printf("bmp size = %d\r\n", bmp_head.size);printf("bmp offset = %d\r\n", bmp_head.offset);memcpy(&bmp_attr, &tmp_buf[14], sizeof(bmp_attr));printf("bmp w = %d, h = %d\r\n", bmp_attr.width, bmp_attr.height);printf("bmp count = %d\r\n", bmp_attr.count);printf("bmp body size = %d\r\n", bmp_attr.image_size);if ((bmp_head.magic[0] != 'B') || (bmp_head.magic[1] != 'M')) {printf("pic isn't bmp!\r\n");} else {if((bmp_attr.count == 24) && (bmp_attr.width <= 320) && (bmp_attr.height <=240)) {memset(src_pic_frame, 0x00, sizeof(src_pic_frame));ret = aos_read(fd, src_pic_frame, bmp_attr.image_size);if (ret < 0) {printf("read bmp body failed\r\n");} else {i = 0;for (line = 239; line > 0; line--) {for (column = line * 960; column < (line * 960 + 959);) {tmp_color_b = src_pic_frame[column]; tmp_color_g = src_pic_frame[column + 1];tmp_color_r = src_pic_frame[column + 2];column = column + 3;tmp = (tmp_color_r >> 3) & 0xff;tmp = tmp << 11;color = tmp;tmp = (tmp_color_g >> 2) & 0xff;tmp = tmp << 5;color |= tmp;tmp = (tmp_color_b >> 3) & 0xff;color |= tmp;if (i < 320 * 240) {pic_frame[i++] = color;}else {printf("color numb err, column = %d\r\n", column);}}}}}}aos_close(fd);}ili9341_draw_frame(user_ili9341, pic_frame);printf("nano entry here!\r\n");
}

开发者支持

如需更多技术支持,可加入钉钉开发者群,或者关注微信公众号。

更多技术与解决方案介绍,请访问HaaS官方网站https://haas.iot.aliyun.com。

手把手教你做一个电子相册相关推荐

  1. python手机版做小游戏代码大全-Python大牛手把手教你做一个小游戏,萌新福利!...

    原标题:Python大牛手把手教你做一个小游戏,萌新福利! 引言 最近python语言大火,除了在科学计算领域python有用武之地之外,在游戏.后台等方面,python也大放异彩,本篇博文将按照正规 ...

  2. 手把手教你做一个自己的chrome扩展程序

    手把手教你做一个自己的chrome扩展程序 [目录] first.效果 1.收藏夹修改 (1).鼠标移动到收藏夹上的动作效果 (2).收藏夹框 (3)百度搜索框功能 2.右上文字修改 3.背景图片修改 ...

  3. 手把手教你做一个Java贪吃蛇小游戏

    大家好,我是孙不坚1208,这篇博客给大家分享一下:如何做一个贪吃蛇小游戏(Java版)的exe应用程序,希望能给需要帮助的朋友带来方便. 手把手教你做一个Java贪吃蛇小游戏的exe应用程序 一.J ...

  4. Blender图解教程:手把手教你做一个马里奥金币 之 图片转法线贴图法(附模型下载)

    <Blender图解教程:手把手教你做一个马里奥金币 之 比较传统的方法>介绍了一种用Blender制作法线贴图的流程,本文介绍一种更加省事的方法. 步骤 效果图 概要 步骤 1. 建模 ...

  5. 手把手教你做一个物联网视频监控项目(三)流媒体方案实现

    往期文章 手把手教你做一个物联网视频监控项目(一) 介绍 手把手教你做一个物联网视频监控项目(二)MJPG-streamer方案实现 文章目录 前言 一.软硬件准备 二.流媒体方案的实现之FFmpeg ...

  6. 手把手教你做一个jsp servlet mysql实现的学生签到考勤请假管理系统附带视频开发教程和完整源码

    今天给大家演示的是一款由jsp+servlet+my色口数据库实现的学生请假签到考勤管理系统,采用了MVC的设计模式,结构层次非常清晰,此外系统还有完整的开发教程. 下面我们先来看看文档结构: 下面来 ...

  7. R数据分析:跟随top期刊手把手教你做一个临床预测模型

    临床预测模型也是大家比较感兴趣的,今天就带着大家看一篇临床预测模型的文章,并且用一个例子给大家过一遍做法. 这篇文章来自护理领域顶级期刊的文章,文章名在下面 Ballesta-Castillejos ...

  8. 手把手教你做一个非常酷的PoV显示器(附源码)

    关注+星标公众号,不错过精彩内容 来源 | DF创客社区 作者 | Amal Shajan 今天为大家分享一个DIY产品,如下: 前两天天我在浏览购物网站的时候,被一个购物清单吸引住了, 5个ATti ...

  9. 手把手教你做一个自定义表格标签

    如果你用公司的平台进行开发的话,许多时候向按钮,输入框,树,菜单等都是直接用一个标签设置几个属性就可以了.全局上样式是统一的,而且容易维护. 之前我已经发使用自定义标签来做数据字典的示例,也就是说自定 ...

最新文章

  1. csu1377Putter HOJ12816
  2. JBPM学习(六):详解流程图
  3. QT的QOpenGLFunctions类的使用
  4. flash动画设计期末作业_「2019年下学期」第二十五二十六节:期末作品三-吉祥物设计...
  5. python定期自动运行_干货分享 | 适合 Python 入门的 8 款强大工具,不会就你还不知道吧!...
  6. 管理数据通用权限系统快速开发框架设计
  7. 雪,是死掉的雨,是雨的精魂
  8. app 之间发送文件 ios
  9. 【Web前端】hexo博客管理
  10. 文档02_JavaScript
  11. 智能驾驶的深度神经网络模型嵌入式部署的线路思考
  12. 上传pdf图片 文件
  13. 如何从XP安装光盘中提取taskmgr.exe
  14. android mac地址不可用,Android手机里的mac地址显示不可用是为什么。我的手机是海信E920....
  15. 微信小程序全栈开发实践 第三章 微信小程序开发常用的API介绍及使用 -- 3.9 网络接口简介(九)扩展wxp模块的request3方法,实现用户登录的自动融合
  16. 微信小程序输入框字数限制以及计算
  17. 行云创新:车云一体化平台,实现软件定义汽车
  18. 3w最简单led灯电路图_为何感应LED车库灯更适用于地下车库?
  19. 03【设计模式的七大原则】
  20. 大一html5期末大作业 :基于html实现红色主题设计题材(梵高 5页)

热门文章

  1. 初相识 | 全方位认识 sys 系统库
  2. 计算机毕业设计django基于python网上拍卖系统
  3. 图片文字提取,清华图片文字提取工具,wrod怎么提取图片文字,怎么把图片里面的文字提取?
  4. 使用HBase Coprocessor
  5. 目标检测 YOLOv5 - YOLOv5:v6版本多机多卡训练出现的错误及解决方案
  6. 海康机器人工业相机常用参数功能设置与获取(持续更新全-C语言)
  7. 2D人体姿态估计综述
  8. python 与汇编jmp的联动
  9. 钉钉小程序 实现Tab选项卡
  10. 组态王通过网口接入EMCP云平台