基于MAX78000FTHR开发板的颜色识别与显示系统(中期报告)
本项目计划使用MAX78000实现板载摄像头的颜色识别功能,并将识别结果通过TFT屏、OLED屏、LED等进行显示。项目中期主要实现了显示部分的功能,图像采集与处理部分计划后续完成。
标签
SPI
OLED
U8G2
MAX78000
TFT
I2C
chinaking
更新2022-12-01
上海大学
730

基于MAX78000FTHR开发板的颜色识别与显示系统
                     项目中期报告

                                                                       老胡

一、开发板介绍

本次活动使用的开发板的是美信公司的MAX78000FTHR,官网链接

https://www.maximintegrated.com/cn/products/microcontrollers/MAX78000FTHR.html

实物图片如下

FmD3arQOJ-L9r6jwtGN9h8c6I7xV

评估板包括各种外设,例如CMOS VGA图像传感器、数字麦克风、低功耗立体声音频CODEC、1MB QSPI SRAM、micro SD存储卡连接器、RGB指示LED和按键。

下面是开发板的硬件接口信息:

Fo0nzjYSMobTvbl2L4SWpLgL3eQf

详细信息可查阅官网上的介绍及开发板使用文档MAX78000FTHR.pdf和原理图文档max78000-fthr-schematic.pdf。

二、项目介绍及设计思路

2.1 项目模块探索

2.1.1 TFT屏幕

 收到开发板时,首先吸引到我的就是这款摄像头,因为此前参加Funpack活动中的板子基本不带视频功能,要显示视频图像就离不开屏幕。

本人先通过官方文档进行学习。入门开发时,主要参考github上的文档“Getting Started with the MAX78000FTHR” ,即MAX78000FTHR入门指南。

https://github.com/MaximIntegratedAI/MaximAI_Documentation/blob/master/MAX78000_Feather/README.md

在阅读文档时发现,开发板可以选配一款屏幕,官方推荐的是Adafruit公司的2.4寸TFT屏。当时就赶紧登录Adafruit官网,下载屏幕的手册及图纸。因过于匆忙,只关注到屏幕芯片是ILI9341,就在淘宝上买了一款。买了之后才发现,虽然驱动芯片都是ILI9341,但这个屏幕分并口驱动型和串口驱动型。本人采购的是并口屏,这款并口屏和ArduinoUNO是兼容的。而官方使用的是SPI串口屏。因此又重新购买了一块SPI串口屏。

FgUW7-MfwrQtLpFLMKSjq2rXoESG

FnSq_849DhhCWpV4i-fMKPdxJ6-s

2.1.2 OLED显示屏

     在此前参加的Funpack活动中,多次用到了OLED显示屏。

在Funpack第1季第6期《玩转MAX32660-EVSYS开发板及ADXL345传感器》就使用了这款屏幕。初步了解这款屏幕的简单驱动方法。

https://www.eetree.cn/project/detail/262

FhI1RuK46RiMNn5D2Y9NmfGu75N5

在Funpack第1季第11期《LPC55S69-EVK读取SD卡BMP图片并显示到OLED》,又使用U8g2的驱动库,实现了BMP图片文件的读取与显示,领略到U8g2的强大功能。

https://www.eetree.cn/project/detail/611

FmUeYmtVHBEDLzNkYxX1exu2VUD3

本次在TFT彩屏之外,再增加一块OLED屏幕,主要是考虑项目中有可能遇到中文显示,单色屏加u8g2驱动的方法比较方便,类似部分手机,可以作为副屏辅助显示。另外,借此机会,深入学习下U8g2驱动的具体移植过程。

2.2项目方案探索

    项目开始阶段,最初准备做一个人脸识别或物体识别的项目。

此前本人参加过一次硬禾学堂关于摄像头的活动,完成了《基于UnitV2 AI Camera的流水线异常品识别系统》。https://www.eetree.cn/project/detail/436

项目中使用AI摄像头,实现了不同颜色色块的识别。了解了数据集收集、标定、训练等操作流程。

Fr8SNi6gNwQYbbCSF5FbLtLvd8cO

因MAX78000本身支持CNN卷积神经网络运算,开始打算学习这一块内容,可以和此前项目进行对比。但实际操作中,发现二者区别较大。其中最大的区别是MAX78000需要在显卡性能较高的服务器上进行训练,而且要自己在linux下搭建训练环境,而前者只需要将图片在官方服务器训练,而且具有较易操作的Windows界面。因受到网络限制及硬件限制等因素,本人最终放弃了CNN这个方向。

    后来一边选题一边在参加Funpack第二季第3期的活动,完成了《基于TCS3200和ESP32-E的颜色识别显示与报警系统》,除了颜色传感器,也使用上了TFT和OLED屏幕。

https://www.bilibili.com/video/BV1td4y1C7ui/?spm_id_from=333.999.0.0&vd_source=72392648ddf7ddba5ee4fd1acff3c430

FsVSysq0EmvhXB_WVNdo_4MI_7JV

后来受到启发,并参考了网上的文章得到确认,如果只是用摄像头识别简单颜色,可以不用AI功能,只将摄像头作为一个传感器来使用。 至此,确定了项目的基本方案。还是用摄像头做颜色识别,和此前两个项目的功能相似,但具体实现方案、使用的核心处理器都有所不同。

Fnvi-Yz6K2tj5lDKkQqdBdnW2Op8

主要计划使用摄像头和屏幕,完成颜色识别与显示功能;如果有时间精力,再在此基础上,追加SD卡读写等功能。

2.3搜集素材

项目开发离不开参考素材,主要来源是以下几个方面:

  1. MAX官网

https://www.maximintegrated.com/cn/products/microcontrollers/MAX78000FTHR.htm

  1. MAX gitlab

https://github.com/MaximIntegratedAI

  1. SDK的demo

SDK提供了丰富的demo,可以融会贯通用于自身项目中。

  1. 历史项目

许多开发板的功能是相通的,可以从历史项目中找到可以借鉴的经验。

  1. 电子森林

下面链接有活动方汇集的主要资料 https://www.eetree.cn/project/detail/1152

  1. CSDN、bilibili、国外网站等

三、项目开发流程

3.1 安装MaximSDK开发环境

本次选用官方的Eclipse MaximSDK软件做为开发环境。同样在官网MAX78000FTHR主页下,在“设计与质量”菜单下,可以找到安装包。

在Windows下使用MaximMicrosSDK.exe进行安装。

若是Linux系统,也可用MaximMicrosSDK_linux.run文件进行安装。

本人使用的是Windows系统,双击exe文件安装即可。

Fj677So-TlYMlPCiXS1OSz8sUjiO

3.2 开发入门

开始开发时,主要参考github上的文档“Getting Started with the MAX78000FTHR” ,即MAX78000FTHR入门指南。这一章节内容主要翻译自该文档。

https://github.com/MaximIntegratedAI/MaximAI_Documentation/blob/master/MAX78000_Feather/README.md

3.2.1  固件升级

MAX78000FTHR 开发板集成了MAX32625PICO调试器,开发前需要将调试器固件升级到最新版。这些更新包含修复的一些Bug及改进,以确保能更好的使用调试器。

升级MAX32625PICO固件流程。

  1. 下载0.2.bin 文件。
  2. 将自带的micro-USB线插到开发板上,数据线另一端先不要连接电脑。
  3. 按住开发板上的SW5按键不要松开。
  4. 继续按住SW5按键同时将数据线另一端插到电脑上,直到开发板上的红色指示灯D3由闪烁变成常亮。
  5. 此时,电脑的文件系统里会出现一个名称为MAINTENANCE的盘符。
  6. 将0.2.bin文件拖动到MAINTENANCE的盘里。这一步将升级PICO固件。
  7. 升级完成后,PICO会重启,盘符会变成"DAPLINK“。
  8. 双击打开DAPLINK 盘。
  9. 检查TXT文件
  10. 现在,MAX78000FTHR 开发板的PICO调试器就可以使用了。

3.2.2  Eclipse开发环境使用

双击图标,运行Eclipse

Fu6rkE2M9gqOiGkliFrif5jgPwCW

等待检查升级,检查完成后即进入IDE编程环境。

点击File---New菜单,如果菜单下能看到Maxim图标则点击该图标,如果看不到,则点击File---New---Project。选择Maxim Microcontrollers,点击Next。

Fv-J9eCalQyoy-4uPLr-P-P7NNQU

输入工程名称,如abc,点击Next

FjHAPfzszf8zkIrN7TVVqsxeKzpt

按照下图,选择芯片型号、开发板型号、示例工程、下载器类型。

FkekW9h2OAQapYvpyr_jYN96uJP3

点击完成即可。

3.3 测试官方例程

开发环境准备好后,即可测试几个示例程序。注意,最好将eclipse及SDK更新到最新版本。打开Eclipse MaximSDK时,会自动提示更新系统,请及时更新。在11月下旬更新过一次SDK,所以TFT相关的部分程序有所变化。

如果开发板无法下载时,可以关闭eclipse或开发板断电重启,如果还无法解决,则需要对开发板修复和解锁。详见入门手册的

“How to Unlock a MAX78000 That Can No Longer Be Programmed”这一章节。

因为我们前面已经准备好屏幕,即可很直观的运行TFT_Demo、CameraIF、cats-dogs-demo

等三个工程,因为这三个示例工程都和TFT屏适配。

例程1  TFT_Demo

本例程主要测试TFT显示功能,如显示图片、绘制图形、打印字符等。

首先,需要进行硬件连接,因官方推荐的Adafruit屏幕排母和MAX78000FTHR的排针是兼容的,直接对插即可。

Fnjp8uPh4VdRFOTXt4xNuQ4_XE_U

而我们采用的屏幕与它硬件不完全一致,需要对照Adafruit屏幕用到的引脚自己引线。

下图是Adafruit屏幕的引脚图,只要在图中找出电源(Vcc、GND),SPI接口(SCLK、MOSI) ,控制接口(CS 、DC、 Reset)等7个引脚的对应开发板上接口的位置即可。

Fip3YG5tqE-OSzDg_9O3W-iLMveI

当然,CS 、DC、 Reset等引脚也可以自己选择不同GPIO来配置,但这种直接替代的方法比较节省时间。

    接线图如下图所示:

FrfD9HJ9uwKV4SE1p-HvicksmX58

接线完成后,按照3.2.2章节的步骤,直接建立基于TFT_Demo的示例工程即可演示。

例程2  CameraIF

本例程主要测试摄像头基本功能。接线与例程1相同。

需要使能下面一行代码,否则编译报错。

#define STREAM_ENABLE

运行后是录像机模式,会自动刷新视频。

如果也使能BUTTON的话,

#define BUTTON

则切换到照相机模式。

照相机模式时,按下开发板上SW1按钮会拍照。

例程3  cats-dogs-demo

本例程主要测试CNN功能,可以进行猫狗识别。接线与例程1相同。

Main文件中增加一行,使能TFT显示功能。

#define TFT_ENABLE

然后,编译下载,按下开发板上SW1按钮会进行猫狗识别。

四、开发自己的项目

4.1 主要功能

本项目主要是计划实现板载摄像头的颜色识别功能,并将检测结果通过TFT屏、OLED屏、LED等进行显示。

4.2 硬件连接

FjUqI6Q-9Ft5FHkqmDECGN5IznSt

4.3软件设计

本系统主要分为三部分,

第一:获取摄像头采集的图像信息,这一部分可以参考CameraIF示例来完成。

第二:图像处理与分析。根据图像信息,进行颜色判断。

第三:效果演示。将判断结果发送到屏幕等外设进行演示。

目前项目中期,仅完成了第一和第三部分。第一部分是示例工程,此次不再详细介绍。

第三部分的难点,是u8g2的移植,移植了U8g2,OLED屏就可以灵活的显示中文和字符。其它模块的显示功能都可以借鉴demo。

U8g2的移植过程

移植过程主要借鉴了下面一篇博文

https://blog.csdn.net/qq_43862401/article/details/121809470

该文章是针对STM32移植u8g2,但过程是相似的。

  1. 下载U8g2源文件

https://github.com/olikraus/u8g2

  1. 精简文件。

新建一个u8g2的文件夹,将U8g2源文件的csrc夹复制到新建的文件夹下。

去掉无用的驱动文件

u8x8_d_ 开头的文件,只保留u8x8_ssd1306_128x64_noname.c

精简精简u8g2_d_setup.c

只保留 u8g2_Setup_ssd1306_i2c_128x64_noname_2相关函数,其它都删掉。

精简u8g2_d_memory.c

只保留u8g2_m_16_8_2相关内容。

  1. 创建两个回调函数。即I2C通讯部分的函数需要参考MAX78000的demo来重新创建。

即在driver_oled_ssd1306.c文件中,创建u8x8_byte_hw_i2c_max78000和u8g2_gpio_and_delay_max78000两个函数。

下面是主要代码:

uint8_t u8x8_byte_hw_i2c_max78000(u8x8_t *u8x8, uint8_t msg, uint8_t arg_int, void *arg_ptr)
{
  static uint8_t buffer[32];		/* u8g2/u8x8 will never send more than 32 bytes between START_TRANSFER and END_TRANSFER */
  static uint8_t buf_idx;
  uint8_t *data;

  switch(msg){

    case U8X8_MSG_BYTE_SEND:
      data = (uint8_t *)arg_ptr;
      while( arg_int > 0 ){
				buffer[buf_idx++] = *data;
				data++;
				arg_int--;
			}
    break;

    case U8X8_MSG_BYTE_INIT:
      /* add your custom code to init i2c subsystem */
    break;

    case U8X8_MSG_BYTE_START_TRANSFER:
      buf_idx = 0;
    break;

    case U8X8_MSG_BYTE_END_TRANSFER:
    	 reqMaster.i2c     = OLED_I2C;
		 reqMaster.addr    = OLED_I2C_ADDR;
		 reqMaster.tx_buf  = buffer;
		 reqMaster.tx_len  = buf_idx;
		 reqMaster.rx_buf  = NULL;
		 reqMaster.rx_len  = 0;
		 reqMaster.restart = 0;
    	 MXC_I2C_MasterTransaction(&reqMaster);
    break;

    default:
      return 0;
  }
  return 1;
}

uint8_t u8g2_gpio_and_delay_max78000(U8X8_UNUSED u8x8_t *u8x8, U8X8_UNUSED uint8_t msg, U8X8_UNUSED uint8_t arg_int, U8X8_UNUSED void *arg_ptr)
{
	switch(msg){

	case U8X8_MSG_GPIO_AND_DELAY_INIT:
	    break;

	case U8X8_MSG_DELAY_MILLI:
		MXC_Delay(MXC_DELAY_MSEC(arg_int));
	    break;

	case U8X8_MSG_GPIO_I2C_CLOCK:
        break;

    case U8X8_MSG_GPIO_I2C_DATA:
        break;

	default:
		return 0;
	}
	return 1; // command processed successfully.
}

接下来是main.c的主要代码

/***** Includes *****/
#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include "board.h"
#include "mxc_device.h"
#include "mxc_delay.h"
#include "icc.h"
#include "dma.h"

#include "nvic_table.h"
#include "i2c_regs.h"
#include "i2c.h"
#include "driver_oled_ssd1306.h"
#include "u8g2.h"

#include "tft_ili9341.h"
#include "led.h"

#define ENABLE_TFT
#define TFT_BUFF_SIZE 32 // TFT buffer size

u8g2_t u8g2;
int page_id;
int RectColor;

void draw(u8g2_t *u8g2)
{
	if (page_id==0)
	{
	u8g2_SetFontMode(u8g2, 1);
	u8g2_SetFont(u8g2, u8g2_font_wqy16_t_gb2312);

	u8g2_DrawGlyph(u8g2,30,20,9733);
	u8g2_DrawGlyph(u8g2,50,20,9734);
	u8g2_DrawGlyph(u8g2,70,20,9733);
	u8g2_DrawGlyph(u8g2,90,20,9734);

	u8g2_DrawUTF8(u8g2,25,40,"自 强 不 息");

	u8g2_DrawGlyph(u8g2,30,60,9733);
	u8g2_DrawGlyph(u8g2,50,60,9734);
	u8g2_DrawGlyph(u8g2,70,60,9733);
	u8g2_DrawGlyph(u8g2,90,60,9734); }
	else if (page_id==1)
	{
	u8g2_SetFontMode(u8g2, 1);

	u8g2_SetFont(u8g2, u8g2_font_inr16_mr);
	u8g2_DrawStr(u8g2,5,40,"I");;

    u8g2_SetFont(u8g2, u8g2_font_open_iconic_human_4x_t);
	u8g2_DrawGlyph(u8g2,20,50,66);

	u8g2_SetFont(u8g2, u8g2_font_inr16_mr);
	u8g2_DrawStr(u8g2,55,40,"CHINA");;
	}
	else if (page_id==2)
	{
	u8g2_SetFontMode(u8g2, 1);

	u8g2_SetFont(u8g2, u8g2_font_open_iconic_weather_4x_t);
	u8g2_DrawGlyph(u8g2,10,50,66);
	u8g2_DrawGlyph(u8g2,50,50,68);
	u8g2_DrawGlyph(u8g2,90,50,69);
	}
}


void TFT_Print(char *str, int x, int y, int font)
{
    // fonts id
    text_t text;
    text.data = str;
    text.len = 20;
    MXC_TFT_PrintFont(x, y, font, &text, NULL);
}

void TFT_Feather_test(void)
{
    area_t _area;
    area_t *area;

    MXC_TFT_SetBackGroundColor(BLACK);

    area = &_area;
    area->x = 50;
    area->y = 150;
    area->w = 128;
    area->h = 64;

    MXC_TFT_FillRect(area, RectColor);

}


int main()
{
    MXC_Delay(MXC_DELAY_MSEC(500)); //Wait for PMIC to power-up

    /* Enable cache */
      MXC_ICC_Enable(MXC_ICC0);

      /* Set system clock to 100 MHz */
      MXC_SYS_Clock_Select(MXC_SYS_CLOCK_IPO);
      SystemCoreClockUpdate();
      MXC_TFT_Init(MXC_SPI0, 1, NULL, NULL);


    int error;
    //Setup the I2C
    error = MXC_I2C_Init(OLED_I2C, 1, 0);

    if (error != E_NO_ERROR) {
        printf("-->Failed master\n");
        while (1)
            ;
    } else {
        printf("\n-->I2C Initialization Complete");
    }
    MXC_I2C_SetFrequency(OLED_I2C, I2C_FREQ);


    #ifdef SSD1306_USE_I2C_HW
    u8g2_Setup_ssd1306_i2c_128x64_noname_2(&u8g2, U8G2_R0, u8x8_byte_hw_i2c_max78000, u8g2_gpio_and_delay_max78000);
    #endif


   u8g2_InitDisplay(&u8g2);
   u8g2_SetPowerSave(&u8g2, 0);

    while (1)
   	    {
    	    MXC_Delay(MXC_DELAY_SEC(1));
   	    	  /* Delay 1s */
   	        u8g2_FirstPage(&u8g2);

   	        if (page_id==0)
   	        {
   	          page_id=1;
   	          RectColor=RED;
   	          LED_Off(LED2);
   	          LED_Off(LED3);
   	          LED_On(LED1);
   	        }
   	        else if (page_id==1)
   	        {
			  page_id=2;
			  RectColor=GREEN;
			  LED_Off(LED1);
			  LED_Off(LED3);
		      LED_On(LED2);
   	        }
   	        else if (page_id==2)
   	        {
   	           page_id=0;
   	           RectColor=BLUE;
   	           LED_Off(LED1);
   	           LED_Off(LED2);
   	           LED_On(LED3);
   	        }
   	       TFT_Feather_test();

   	        do
   	        {
   	          draw(&u8g2);
   	        } while( u8g2_NextPage(&u8g2) );

   	     MXC_Delay(MXC_DELAY_SEC(1));
   	    }
}

4.4 演示效果

目前项目中期已完成部分功能,下面是演示效果,三种状态下不同显示内容可以自动切换。为下一步开发打下了基础。

Fs5brlpf732gDEeZKzn_jupvh3Cl

FoZOCKZNIs2IIrs1HJ_K_gmnFRE-

4.5遇到的主要问题

问题1:Eclipse工程中,使用u8g2文件夹时,总是找不到头文件。

FuClfeDi8DtaX578SNXdLERaMiam

开始是在工程属性下进行配置,仍然报错,无奈只能将文件夹去掉。

最后在Bulin Liu的指点下,修改了makefile,解决了这个问题。

Fic5pRmqmdEA82Nz-XtcM8uqS-89

问题2:U8g2显示汉字的函数选择问题。

本人最初使用U8g2显示汉字时,都是使用的u8g2_DrawGlyph()函数,这个函数需要在参数里指定汉字的所属编码,才能正确显示。而查找编码非常麻烦,因为U8g2官网只提供了相应汉字表的图片格式,难以搜查,只能人工查找。

最后在HonestQiao|乔楚 大佬的指点下,改用u8g2_DrawUTF8()函数,可以直接输入中文,非常方便。需要注意工程设置里文件的编码格式改为UTF-8,否则汉字会显示空白。

FrI_pbpYckSz4x70PSBRjd0DLkbI

最后,本人的体会是虽然项目实现的功能比较简单,但收获很大。

遇到问题一定要思路清晰,深入理解,囫囵吞枣的话,代码很难蒙混过关。

还有就是不懂就要问,别人的经验可以帮我们少走弯路,节省时间。

真心感谢大家无私的指导与交流!

 

团队介绍
老胡,自动化工程师,电子爱好者。
评论
0 / 100
查看更多
目录
硬禾服务号
关注最新动态
0512-67862536
info@eetree.cn
江苏省苏州市苏州工业园区新平街388号腾飞创新园A2幢815室
苏州硬禾信息科技有限公司
Copyright © 2023 苏州硬禾信息科技有限公司 All Rights Reserved 苏ICP备19040198号