基于MAX78000FTHR开发板的颜色识别与显示系统
项目中期报告
老胡
一、开发板介绍
本次活动使用的开发板的是美信公司的MAX78000FTHR,官网链接
https://www.maximintegrated.com/cn/products/microcontrollers/MAX78000FTHR.html
实物图片如下
评估板包括各种外设,例如CMOS VGA图像传感器、数字麦克风、低功耗立体声音频CODEC、1MB QSPI SRAM、micro SD存储卡连接器、RGB指示LED和按键。
下面是开发板的硬件接口信息:
详细信息可查阅官网上的介绍及开发板使用文档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串口屏。
2.1.2 OLED显示屏
在此前参加的Funpack活动中,多次用到了OLED显示屏。
在Funpack第1季第6期《玩转MAX32660-EVSYS开发板及ADXL345传感器》就使用了这款屏幕。初步了解这款屏幕的简单驱动方法。
https://www.eetree.cn/project/detail/262
在Funpack第1季第11期《LPC55S69-EVK读取SD卡BMP图片并显示到OLED》,又使用U8g2的驱动库,实现了BMP图片文件的读取与显示,领略到U8g2的强大功能。
https://www.eetree.cn/project/detail/611
本次在TFT彩屏之外,再增加一块OLED屏幕,主要是考虑项目中有可能遇到中文显示,单色屏加u8g2驱动的方法比较方便,类似部分手机,可以作为副屏辅助显示。另外,借此机会,深入学习下U8g2驱动的具体移植过程。
2.2项目方案探索
项目开始阶段,最初准备做一个人脸识别或物体识别的项目。
此前本人参加过一次硬禾学堂关于摄像头的活动,完成了《基于UnitV2 AI Camera的流水线异常品识别系统》。https://www.eetree.cn/project/detail/436
项目中使用AI摄像头,实现了不同颜色色块的识别。了解了数据集收集、标定、训练等操作流程。
因MAX78000本身支持CNN卷积神经网络运算,开始打算学习这一块内容,可以和此前项目进行对比。但实际操作中,发现二者区别较大。其中最大的区别是MAX78000需要在显卡性能较高的服务器上进行训练,而且要自己在linux下搭建训练环境,而前者只需要将图片在官方服务器训练,而且具有较易操作的Windows界面。因受到网络限制及硬件限制等因素,本人最终放弃了CNN这个方向。
后来一边选题一边在参加Funpack第二季第3期的活动,完成了《基于TCS3200和ESP32-E的颜色识别显示与报警系统》,除了颜色传感器,也使用上了TFT和OLED屏幕。
后来受到启发,并参考了网上的文章得到确认,如果只是用摄像头识别简单颜色,可以不用AI功能,只将摄像头作为一个传感器来使用。 至此,确定了项目的基本方案。还是用摄像头做颜色识别,和此前两个项目的功能相似,但具体实现方案、使用的核心处理器都有所不同。
主要计划使用摄像头和屏幕,完成颜色识别与显示功能;如果有时间精力,再在此基础上,追加SD卡读写等功能。
2.3搜集素材
项目开发离不开参考素材,主要来源是以下几个方面:
- MAX官网
https://www.maximintegrated.com/cn/products/microcontrollers/MAX78000FTHR.htm
- MAX gitlab
https://github.com/MaximIntegratedAI
- SDK的demo
SDK提供了丰富的demo,可以融会贯通用于自身项目中。
- 历史项目
许多开发板的功能是相通的,可以从历史项目中找到可以借鉴的经验。
- 电子森林
下面链接有活动方汇集的主要资料 https://www.eetree.cn/project/detail/1152
- CSDN、bilibili、国外网站等
三、项目开发流程
3.1 安装MaximSDK开发环境
本次选用官方的Eclipse MaximSDK软件做为开发环境。同样在官网MAX78000FTHR主页下,在“设计与质量”菜单下,可以找到安装包。
在Windows下使用MaximMicrosSDK.exe进行安装。
若是Linux系统,也可用MaximMicrosSDK_linux.run文件进行安装。
本人使用的是Windows系统,双击exe文件安装即可。
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固件流程。
- 下载0.2.bin 文件。
- 将自带的micro-USB线插到开发板上,数据线另一端先不要连接电脑。
- 按住开发板上的SW5按键不要松开。
- 继续按住SW5按键同时将数据线另一端插到电脑上,直到开发板上的红色指示灯D3由闪烁变成常亮。
- 此时,电脑的文件系统里会出现一个名称为MAINTENANCE的盘符。
- 将0.2.bin文件拖动到MAINTENANCE的盘里。这一步将升级PICO固件。
- 升级完成后,PICO会重启,盘符会变成"DAPLINK“。
- 双击打开DAPLINK 盘。
- 检查TXT文件
- 现在,MAX78000FTHR 开发板的PICO调试器就可以使用了。
3.2.2 Eclipse开发环境使用
双击图标,运行Eclipse
等待检查升级,检查完成后即进入IDE编程环境。
点击File---New菜单,如果菜单下能看到Maxim图标则点击该图标,如果看不到,则点击File---New---Project。选择Maxim Microcontrollers,点击Next。
输入工程名称,如abc,点击Next
按照下图,选择芯片型号、开发板型号、示例工程、下载器类型。
点击完成即可。
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的排针是兼容的,直接对插即可。
而我们采用的屏幕与它硬件不完全一致,需要对照Adafruit屏幕用到的引脚自己引线。
下图是Adafruit屏幕的引脚图,只要在图中找出电源(Vcc、GND),SPI接口(SCLK、MOSI) ,控制接口(CS 、DC、 Reset)等7个引脚的对应开发板上接口的位置即可。
当然,CS 、DC、 Reset等引脚也可以自己选择不同GPIO来配置,但这种直接替代的方法比较节省时间。
接线图如下图所示:
接线完成后,按照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 硬件连接
4.3软件设计
本系统主要分为三部分,
第一:获取摄像头采集的图像信息,这一部分可以参考CameraIF示例来完成。
第二:图像处理与分析。根据图像信息,进行颜色判断。
第三:效果演示。将判断结果发送到屏幕等外设进行演示。
目前项目中期,仅完成了第一和第三部分。第一部分是示例工程,此次不再详细介绍。
第三部分的难点,是u8g2的移植,移植了U8g2,OLED屏就可以灵活的显示中文和字符。其它模块的显示功能都可以借鉴demo。
U8g2的移植过程
移植过程主要借鉴了下面一篇博文
https://blog.csdn.net/qq_43862401/article/details/121809470
该文章是针对STM32移植u8g2,但过程是相似的。
- 下载U8g2源文件
https://github.com/olikraus/u8g2
- 精简文件。
新建一个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相关内容。
- 创建两个回调函数。即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 演示效果
目前项目中期已完成部分功能,下面是演示效果,三种状态下不同显示内容可以自动切换。为下一步开发打下了基础。
4.5遇到的主要问题
问题1:Eclipse工程中,使用u8g2文件夹时,总是找不到头文件。
开始是在工程属性下进行配置,仍然报错,无奈只能将文件夹去掉。
最后在Bulin Liu的指点下,修改了makefile,解决了这个问题。
问题2:U8g2显示汉字的函数选择问题。
本人最初使用U8g2显示汉字时,都是使用的u8g2_DrawGlyph()函数,这个函数需要在参数里指定汉字的所属编码,才能正确显示。而查找编码非常麻烦,因为U8g2官网只提供了相应汉字表的图片格式,难以搜查,只能人工查找。
最后在HonestQiao|乔楚 大佬的指点下,改用u8g2_DrawUTF8()函数,可以直接输入中文,非常方便。需要注意工程设置里文件的编码格式改为UTF-8,否则汉字会显示空白。
最后,本人的体会是虽然项目实现的功能比较简单,但收获很大。
遇到问题一定要思路清晰,深入理解,囫囵吞枣的话,代码很难蒙混过关。
还有就是不懂就要问,别人的经验可以帮我们少走弯路,节省时间。
真心感谢大家无私的指导与交流!