LPC55S69-EVK读取SD卡BMP图片并显示到OLED(Funpack第11期)
本次设计使用LPC55S69-EVK开发板,实现了读取SD卡上BMP格式的图片,并显示到OLED单色屏。开发板依次读取3张图片,即NXP、DigiKey、硬禾学堂等3个logo,进行循环显示。
标签
LPC55S69
OLED
sd卡
funpack第11期
U8G2
BMP解码
chinaking
更新2021-10-29
972

一、概述

本次设计使用LPC55S69-EVK开发板,实现了读取SD卡上BMP格式的图片,并显示到OLED单色屏。开发板依次读取3张图片,即NXP、DigiKey、硬禾学堂等3个logo,进行循环显示。

系统硬件使用了硬禾学堂赠送的mikroBUS扩展模块,模块自带1块0.96寸OLED显示屏。显示屏通过I2C接口与开发板通讯。

程序中主要是对BMP图像进行解码,调用u8g2库完成显示功能。

Fg6VU3iNZKDVAUyl44HpbTJyDlLm

二、具体设计

2.1硬件准备

LPC55S69-EVK开发板    1块   NXP公司

MikroBUS模块(带OLED)  1块   硬禾学堂

SD卡(2G容量)            1张  

Micro USB下载线        1根

LPC55S69-EVK开发板是NXP公司推出的,LPC55S6x MCU家族是全球首款基于通用Cortex-M33的微控制器,控制器为双核结构,运行频率高达100MHz。

开发板具有USB、SD卡插槽、音频输入输出、I2C、SPI等接口,并支持mikroBUS接口扩展。

MikroBUS模块(带OLED)是硬禾学堂在Funpack第7期赠送的,此次主要用到模块上的OLED显示屏,显示屏大小为0.96寸,分辨率128*64,色彩为单色。

由于MikroBUS是标准接口,只需将模块插到开发板即可使用。

下载时,使用开发板的USB端口下载,端口号为P6。

2.2软件开发

2.2.1 软件工具

MCUXpresso IDE v11.4.1_6260:NXP官方IDE。

SDK_2_10_1_LPCXpresso55S69

U8g2库:用于驱动OLED显示屏。

PCtoLCD2002:用于取字模,该软件主要用于早期开发时,测试u8g2显示字模数组用。

WinHex:用于打开BMP文件,查看二进制数据。

FlashMagic:开发板异常时,刷Hex文件用。

2.2.2 实现过程

本次任务完成的思路和过程

本人先测试u8g2显示部分,使用OLED屏幕显示字符或汉字,确保屏幕驱动及硬件正常。

然后,使用了PCtoLCD2002.exe生成了一个字模数组,测试u8g2的 BMP函数功能正常。

最后,开发完成操作SD卡的BMP文件并显示的功能。

下面重点介绍操作BMP文件并显示的程序的具体操作步骤。

FmGfXVHmR3OHRVp5r5DyzePLoVr8

其中,最关键的步骤是BMP解码及U8g2显示。

为操作简便,此次操作的图片都提前进行了处理。因采用的OLED分辨率只有128*64,所以将3张图片大小都编辑成128*64,类型都是单色BMP格式。

为正确解码,需要将图像、字模数组和BMP文件内容三者进行对比,找出规律。因程序可以识别字模数组并显示,只需将BMP文件解码成字模数组相同的格式即可。

以NXP图片为例

FmvL7nF2kom-egiWKHKam7oUfrlL

FjieGm9ONN7zBHCh4uF6Lw5EkBBx

FuVQN9-0DEMNslCSMQj6QIlV4Hao

字模数组每行16个byte,共64行,上图主要显示中间的非零行。

FqNnzn8A928c3MrlvOgl1P54v2vZ

                                        BMP文件内容(部分)

根据BMP文件格式信息可知,BMP文件前面62个byte都是文件头、信息头、调色板等信息,主要包含了图像类型、大小、高度、宽度、偏移量等信息,见参考文献3。只有第63个byte开始才是图像内容。上图中,第一个红框表示图片内容的起始行,第二个红框标出了第一个非空行。图像内容也有64行,上图主要截取了前半部分。

将字模数组和BMP文件比较后可知:

相同点:对于单色BMP位图,二者都是由16*64的Byte矩阵表示像素颜色。1个byte表示8个像素,因此,恰好每行128个像素,共64行。

不同点:

a)总体来看,行号不一样。

字模数组,与图像对应关系是从第一行开始,从左到右,从上到下。

BMP文件,与图像对应关系是从最后一行开始,从左到右,从下到上。

所以,对应行号时,BMP文件的第1行,对应的是数组的最后1行;数组的第1行,对应的是BMP文件的最后1行。

b)具体到每个byte来看,最高位的位置不一样。

字模数组,8个像素1组,靠左的是低位。

BMP文件,8个像素1组,靠左的是高位。

比较BMP文件非空第1行和字模数组非空最后1行,二者对应关系如下。

FsIuXMkcNNE7qt3zwgsboTFG2jvs

字模数组里面,0x03对应BMP文件里是0xC0,即二进制0000 0011和1100 0000。

因此,需要将读到BMP文件的内容进行转换和对应,转换成和字模数组相同的格式,才能用u8g2正确显示。

  编程开始前,先将SD卡插到读卡器,格式化,创建一个MEDIA文件夹,并放置3张预先处理过的BMP文件,图像大小128*64,类型都是单色BMP格式。

Fq7u6RGzR4Y1Cyi-7Ue_Tc7jBn0i

2.2.3主要代码

bmp_decode.c 文件主要代码

unsigned char byte_change(unsigned char data)
{

 data = ((data & 0xAA) >> 1) | ((data & 0x55) << 1);
 data = ((data & 0xCC) >> 2) | ((data & 0x33) << 2);
 data = (data >> 4) | (data << 4);

 return data;
}

void BMPShow(char *filename,uint16_t x,uint16_t y)
{  
    FIL  file;   
	unsigned char buff[64];     
    unsigned int i,j;
    unsigned int m,n;
	unsigned int px,py;
	unsigned int  iWidth,iHeigth,iBitCount;//iWidth,iHeigth,iBitCount;  //图像的宽度, 高度 ,每个像素所需色彩位数
	unsigned int a,b;

    unsigned long filelen,iOffbits,colorlist;//filelen文件大小  iOffbits偏移量
	unsigned long color;
	unsigned int  rb;
	unsigned int  rb1;
	unsigned char *work,*c_ptr,*pan_ptr;


	filelen=0;
	iOffbits=0;
	
	if (f_open(&file,(char*)filename, FA_READ|FA_OPEN_ALWAYS) != FR_OK )  //打开图片
	{
	    PRINTF("BMPFile %s cannot open!\n",filename);
	}
	else
	{
		f_read(&file, buff, 54, &rb);//读取文件头信息 54Byte
		if(buff[0]==0x42 && buff[1]==0x4D)        //文件格式标记为BM
		{
			py=0;
			px=0;
			iWidth  = buff[18]+(buff[19]<<8);  //X
			iHeigth = buff[22]+(buff[23]<<8);  //Y
			iBitCount = buff[28];
			if(iWidth%2 != 0)
			{
				iWidth+=1;
			}
			PRINTF("BMP File Width=%u,Height=%u.\n",iWidth,iHeigth);//图片宽度 高度
			switch(iBitCount)
            {
            case 1:     //双色  黑白图片
	PRINTF("2 color file \n");
	filelen = buff[2]+(buff[3]<<8)+(buff[4]<<16)+(buff[5]<<24);  //读取文件长度

                iOffbits=buff[10];  //初始时,偏移量=62
                PRINTF("BMP Offbits =%u \n",iOffbits);
				colorlist = iWidth;
				if(iWidth%32 != 0)
				{
					iWidth=(((iWidth>>5)+1)<<5);
				}
				work = (unsigned char*)malloc(480>>3);
                while(py<iHeigth)
                { 

					if(work == NULL)
					{					
						while(px<iWidth)
						{
				f_lseek(&file,iOffbits);
				f_read(&file, buff, 1, &rb);//buff[0]=fgetc(file);
				iOffbits +=1;
				for(i=0;i<8;i++)
				{
					if(buff[0]&0x80)
					{			 
                                   //color=GLCD_COLOR_WHITE;
								} 
								else
								{
								 
                                    //color=GLCD_COLOR_BLACK;
								}
					buff[0]=(buff[0]<<1);

				//lcd_point(px+i+x,iHeigth-py+y-1,color);
							}
							px+=8;
						}

					}else
					{
					f_lseek(&file,iOffbits);//找到偏移的位置
					f_read(&file, work, 16, &rb);//读取偏移量指定的一行

                  // bmp2里面一共有16*64,共计1024个数据,行头元素分别是0 16 32 48.。。。。1008
					for(m=0;m<16;m++)
					{
					bmp2[1008+62-iOffbits+m]=byte_change(work[m]);
                  //将此行读到的每个数据倒向后,赋值给bmp2
					}
					iOffbits += 16;//偏移量每次增加16
				//PRINTF("BMP Offbits =%u \n",iOffbits);
					}
					px=0;
					py++;
                  if (iOffbits>=1070)
                   {
					u8g2_FirstPage(&u8g2);
					  do
					{
					u8g2_SetFontMode(&u8g2, 1);
					u8g2_SetFont(&u8g2, u8g2_font_ncenB14_tr);
					u8g2_DrawXBMP(&u8g2,0,0, bmp_x, bmp_y, bmp2);
					} while( u8g2_NextPage(&u8g2) );
                  }
					if(iOffbits > 1070)            //读完整个文件
					{
						break;
					}
                }
				if(work != NULL)	free(work);
				break;	       
				default :
				PRINTF("- not 32/24/16 bmp color file \n");
			}	

		}
	    f_close(&file); 
        PRINTF("BMP File OK.\n");
    }
}

sdcard_fatfs.c 主要代码

int main(void)
{
    const TCHAR driverNumberBuffer[3U] = {SDDISK + '0', ':', '/'};

    /* set BOD VBAT level to 1.65V */
    POWER_SetBodVbatLevel(kPOWER_BodVbatLevel1650mv, kPOWER_BodHystLevel50mv, false);
    CLOCK_EnableClock(kCLOCK_InputMux);
    /* attach 12 MHz clock to FLEXCOMM0 (debug console) */
    CLOCK_AttachClk(BOARD_DEBUG_UART_CLK_ATTACH);

    BOARD_InitPins();
    BOARD_BootClockPLL150M();
    BOARD_InitDebugConsole();

    PRINTF("FATFS example to demonstrate how to use FATFS with SD card.\r\n");
    PRINTF("Please insert a card into board.\r\n");

#ifdef SSD1306_USE_I2C_HW
    u8g2_Setup_ssd1306_i2c_128x64_noname_2(&u8g2, U8G2_R0, u8x8_byte_hw_i2c_lpc55, u8x8_gpio_and_delay_lpc55);
#endif
    u8g2_InitDisplay(&u8g2);
    u8g2_SetPowerSave(&u8g2, 0);

    if (sdcardWaitCardInsert() != kStatus_Success)
    {
        return -1;
    }

    if (f_mount(&g_fileSystem, driverNumberBuffer, 0U))
    {
        PRINTF("Mount volume failed.\r\n");
        return -1;
    }

    scan_files(path);
    while(1)
    {
		if(iPageID==0)
		{ BMPShow("2:/MEDIA/nxp.bmp",0,0);
		  iPageID=1;
		  SDK_DelayAtLeastUs(3000000, SDK_DEVICE_MAXIMUM_CPU_CLOCK_FREQUENCY);
		}
		else  if(iPageID==1)
		{  BMPShow("2:/MEDIA/digikey.bmp",0,0);
		   iPageID=2;
		   SDK_DelayAtLeastUs(3000000, SDK_DEVICE_MAXIMUM_CPU_CLOCK_FREQUENCY);
		 }
		else  if(iPageID==2)
		{  BMPShow("2:/MEDIA/yhxt.bmp",0,0);
		   iPageID=0;
		   SDK_DelayAtLeastUs(3000000, SDK_DEVICE_MAXIMUM_CPU_CLOCK_FREQUENCY);
		 }
    }


}

三、演示效果

开发板上电,依次循环显示三个LOGO.

FpUecjzJygcuhkV4rl0jLFAMhbU2

四、心得体会

4.1 如何配置IDE

需要注意设置头文件和源文件路径。

4.2 如何刷hex文件

进入ISP模式,直接刷flash。

4.3 如何配置Keil

待完成

 

 

参考文献

[1]如何利用u8g2在LPC55的IOTKIT上驱动oled并跑两个小游戏

[2]LPC55S69+tf卡+lcd实现lcd显示jpg图片

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