一、系统概述
本文使用MAX78000 FTHR开发板,在开发板上外接了一块TFT显示屏和OLED显示屏,对显示功能进行了测试。此外,对板载指示灯的显示控制,也进行了测试。最终,显示屏及指示灯可以在红、绿、蓝三种状态进行切换显示。
针对TFT显示屏,测试了屏幕的SPI接口,实现了字符显示和图形显示。
针对OLED显示屏,测试了屏幕的I2C接口,移植了U8g2显示驱动库,实现了中英文及特殊图形显示。
针对LED指示灯,测试了红绿蓝不同颜色的切换显示。
总之,通过此次显示系统的开发,掌握了MAX78000的SPI、I2C等接口的使用方法,熟悉了显示屏的驱动功能,为后续项目开发打下了基础。
二、硬件设计
下图是本系统的硬件接线图。主要分为TFT屏的连接和OLED屏的连接。
图1 系统硬件接线图
TFT屏的接线主要分为三部分,电源线、通讯线和其它线。
下图是官方推荐屏幕的连接效果。
因官方推荐的Adafruit屏幕排母和MAX78000FTHR的排针是兼容的,直接对插即可。而我们采用的屏幕与它硬件不完全一致,需要对照Adafruit屏幕用到的引脚自己引线。
下图是Adafruit屏幕的引脚图,只要在图中找出电源(Vcc、GND),SPI接口(SCLK、MOSI) ,控制接口(CS 、DC、 Reset)等7个引脚的对应开发板上接口的位置即可。
电源线为VCC和GND分别连接开发板的3V3和GND即可,此外将屏幕的LED背光脚也接到3V3。
通讯线为SPI通讯,这次只用到了SCK和MOSI两根线。因为主要是屏幕接收数据,所有MISO线可以不接。
其它线包括RESET、CS、DC等几根线。其中RESET线连接开发板的硬件RESET脚即可,这样比较方便,无需再对GPIO进行指定。
OLED屏的接线是两根电源线,两根通讯线。电源也是3.3V,通讯线连接到图1开发板右下角的I2C接口。
三、软件设计
首先要熟悉MAX78000的开发环境,
Eclipse开发环境使用
双击图标,运行Eclipse
等待检查升级,检查完成后即进入IDE编程环境。
点击File---New菜单,如果菜单下能看到Maxim图标则点击该图标,如果看不到,则点击File---New---Project。选择Maxim Microcontrollers,点击Next。
输入工程名称,如abc,点击Next
按照下图,选择芯片型号、开发板型号、示例工程、下载器类型。
点击完成即可。
开发环境准备好后,即可测试几个示例程序。
软件功能的开发可分为三部分:LED指示灯控制、TFT显示功能开发、OLED显示功能开发。
LED指示灯控制,这一部分最简单,参考官方的“Hello World”例程即可。
/*LED灯控制函数*/
void ctrlLED()
{
if (page_id==0)
{
LED_On(LED1);
LED_Off(LED2);
LED_Off(LED3);
}
else if (page_id==1)
{
LED_Off(LED1);
LED_On(LED2);
LED_Off(LED3);
}
else if (page_id==2)
{
LED_Off(LED1);
LED_Off(LED2);
LED_On(LED3);}
}
TFT显示功能开发,这一部分可参考“TFT_Demo”例程,在此基础上理解TFT屏绘制矩形图案、绘制线条等函数的使用。
/*TFT屏打印字符串函数*/
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);
}
/*TFT屏显示函数*/
void drawTFT(void)
{
area_t _area;
area_t *area;
char buff[TFT_BUFF_SIZE];
MXC_TFT_SetBackGroundColor(LIGTH_GREY);//设置屏幕背景色
area = &_area;
area->x = 100;
area->y = 50;
area->w = 60;
area->h = 30;
if (page_id==0)
{RectColor=RED;
sprintf(buff, "RED ");
TFT_Print(buff, 100, 10, (int)&Arial28x28[0]);//屏幕显示字符串
}
else if (page_id==1)
{RectColor=GREEN;
sprintf(buff, "GREEN");
TFT_Print(buff, 90, 10, (int)&Arial28x28[0]);
}
else if (page_id==2)
{ RectColor=BLUE;
sprintf(buff, "BLUE ");
TFT_Print(buff, 100, 10, (int)&Arial28x28[0]);
}
MXC_TFT_FillRect(area, RectColor);//绘制填充矩形
MXC_TFT_Line(100, 100,160,100,RectColor);//绘制矩形框
MXC_TFT_Line(160, 100,160,200,RectColor);
MXC_TFT_Line(160, 200,100,200,RectColor);
MXC_TFT_Line(100, 200,100,100,RectColor);
}
OLED显示功能开发,这是此项目中最复杂的部分。首先,参考I2C相关的例程,理解MAX78000的I2C函数的使用方法。然后,借助以前项目经验,自己写驱动程序,测试基础的OLED显示功能。最后,参考STM32对U8G2显示库的移植过程,完成MAX78000的U8g2显示。
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两个函数。
下面是回调函数主要代码:
#include <string.h>
#include "stdint.h"
#include "driver_oled_ssd1306.h"
#include "u8x8.h"
#include <stdio.h>
#include "i2c.h"
#include "mxc_delay.h"
#include "tmr.h"
static mxc_i2c_req_t reqMaster;
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.
}
下面是U8g2的应用程序,实现3个画面显示不同内容。
/*u8g2显示函数*/
void drawOLED(u8g2_t *u8g2)
{
if (page_id==0)
{
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==1)
{
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==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);}
}
程序都完成后,将三部分程序都完成后,将几个程序进行整合与测试。
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);//TFT屏初始化
int error;
error = MXC_I2C_Init(OLED_I2C, 1, 0);//I2C初始化
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(3));
/* 延时 3s */
u8g2_FirstPage(&u8g2);
ctrlPageID();//控制界面自动切换
ctrlLED(); //控制指示灯显示
drawTFT();//控制TFT屏显示
do
{
drawOLED(&u8g2);//u8g2控制OLED显示
} while( u8g2_NextPage(&u8g2) );
}
}
三、效果演示
系统上电后,TFT屏、OLED屏、指示灯等都会在三个状态间进行自动切换。下面是演示效果。
四、心得体会
因种种原因,没有进行AI相关项目。
此显示系统仅完成初步功能,后续计划和摄像头结合,完成颜色识别追踪等功能。