雷竞技注册
项目

如何使用EFM8微控制器在LCD上显示图像

2015年7月24日经过罗伯特·凯

使用EFM8的USB功能将图像从PC传输到LCD。

了解如何使用EFM8的USB功能将图像从PC传输到LCD。

推荐水平

中间的

本系列中的前一篇文章

所需的硬件/软件

项目概述

在以前的项目中,我们探讨了如何实现EFM8的SPI功能、与LCD模块通信、格式化和打印10×8像素字符,以及使用VCPXpress库在EFM8和SciLab之间建立USB连接。目前的项目将这些功能结合在一起,以便在LCD上高效方便地显示128×128像素的图像。目标是从任何标准灰度.bmp图像文件开始,使用Scilab对其进行处理,然后通过USB将其传输到EFM8微控制器,以便我们可以在128×128像素的LCD上显示它。本项目仅处理单个图像,但此处介绍的技术可以很容易地适用于显示由一系列类似图像组成的简单动画。

该过程开始使用创建图像Paint.NET或者其他一些图像编辑应用程序。该图像被加载到Scilab中,处理成与LCD兼容的格式,转换成像素数据矩阵,并通过64字节USB数据包传输到EFM8。然后使用更新的SPI状态机将该像素数据(一次四行)传输到LCD模块。

端口I/O

端口I/O配置与我们在上一篇文章中使用的相同。

SPI信号映射到适当的端口引脚,除了芯片选择信号,我们通过P0.1手动驱动。我们不需要直接配置USB数据线的端口引脚;所有USB外设的初始化都是通过VCPXpress库完成的。

外围设备和中断

外设和中断设置与我们在上一个文章中使用的相同:SPI配置用于与LCD模块通信,并且Timer4用于短延迟。我们在这个项目中不使用Timer2,因为我们不需要帧速率。相反,LCD被顺序更新,因为从PC接收到像素数据分组。

固件

此项目的VCP配置与我们在上一个项目中使用的配置相同。不过,还有一些额外的USB功能:以前EFM8只从Scilab接收数据,而现在EFM8也传输数据。

void USBTxByte(unsigned char BytetoSend){Block_Write(&BytetoSend,1,&USBBytesTransmitted);}

如上所述的Block_Write()函数的名称所暗示,VCPxPress库能够通过一个函数调用传输字节数组。但是,在该项目中,来自EFM8的USB传输仅用于流量控制:EFM8发送一个字节以告知Scilab,即时间发送更多数据。因此,USBTXByte()函数只是一种使用Block_Write()来发送单个字节的方便方式。

接收到的USB数据包使用以下代码进行处理:

if(api_interruptcode&rx_complete)// usb读取完成{if(usbbytesreceived == 1 && usbrxpacket [0] == new_image_flag){clear_lcd = true;NextLinetowrite = 0;//将新图像标志字节返回给PC进行流量控制USBTXByte(new_image_flag);//继续下一个USB读取过程block_read(usbrxpacket,usb_packet_size,&usbbytesreceived);}否则如果(usbbytesreceive == usb_packet_size){/ *这个标志告诉while循环在imageTolcd_main.c中处理接收的USB像素数据包* / usb_packet_received = true;//继续下一个USB读取过程block_read(usbrxpacket,usb_packet_size,&usbbytesreceived);}}

当Scilab脚本完成将图像文件转换为LCD像素数据后,它将发送一个单字节数据包,该数据包的值在ImagetoLCD_Defs.h中定义为NEW_image_标志。因此,如果接收的分组长度为1,并且单个接收字节具有NEW_IMAGE_FLAG的值,则微控制器知道新图像正在传送中。它清除LCD,将新的_IMAGE_标志传输到PC,并将零加载到NextLinetoWrite,NextLinetoWrite是一个变量,它保存微控制器接收下一个像素数据包时要更新的第一行地址。如果接收到的数据包长度为64字节而不是1字节,则该数据包将带来实际的像素数据。在这种情况下,我们只需将USB_PACKET_RECEIVED标志设置为true;LCD更新完成后,将传输流量控制字节。

当ImagetoLCD_main.c中的无限循环意识到所接收的USB_数据包_已设置为true时,它调用ProcessUSBRxPacket():

void processusbrxpacket(){unsigned char n = 0,行,列;//将接收的像素数据复制到LCD显示数据阵列(Row = 0;行

在这里,我们将像素数据转换到适当的二维数组中。在这个项目中,LCDDisplayData[][]是4行16列:我们仍然需要16列的128位字节持有水平数据,但是我们只需要4行因为像素数据从64年的个人电脑传输字节数据包,和64字节/每行16字节= 4行。在更新数组之后,程序等待直到LCD通信接口空闲,然后调用UpdateLCDLines()。

该项目需要对控制到LCD的SPI传输的状态机进行一些更改。以前我们有updateAllCdlines()函数,它(正如您可能从名称猜到的)启动一个进程,在一次SPI传输中更新所有LCD行。但是现在,我们在一次SPI传输期间只更新了四行,并且在过程结束时执行了两个额外的任务:

ImagetoLCD.zip

Scilab

Scilab脚本以图像处理部分开始:

输入图像必须是灰度,128×128像素.bmp文件。SegmentByThreshold()函数将图像从灰度转换为黑白,因为我们的LCD可以打开或允许的灰色。一系列Bitset()操作将此图像数据转换为可以发送到EFM8并直接传输到LCD的像素数据。请注意,对于我们在此处使用的那种尴尬的按位操作,不完全优化Scilab等复杂的计算应用程序。换句话说,上面代码中的双用于循环块需要很长时间才能执行(例如,使用运行Windows 8.1的2.5 GHz处理器约23秒)。因此,如果要调整此代码以显示动画序列,则需要在开始向EFM8发送数据之前将所有图像转换为LCD像素格式。

Scilab脚本中的另一个主要部分是For循环,通过VCP连接将像素数据发送到EFM8:

使用slSendArray()函数将四行像素数据转换为一维数组,并将其作为单个64字节数据包传输。然后,脚本在发送接下来的四行像素数据之前,从EFM8中读取单个确认字节。重要提示:对此脚本中的SLREADYTE()的调用具有“1”,用于第二个参数,即响应= SLRREADYTE(EFM8PORT,1)。此“1”表示该功能将,也就是说Scilab在至少一个字节到达之前什么都不会做。这样做的好处是脚本可以尽可能快地运行,因为只要EFM8发送确认字节,就会继续执行。但问题是,如果出现问题,字节永远不会出现,Scilab将处于昏迷状态,直到您关闭程序并重新打开它。因此,在调试阶段,更好的方法是使用sleep()函数给EFM8响应时间,然后不阻塞地读取字节,即slReadByte(EFM8Port,0.).

Scilab脚本还调用TIC()和TOC()来测量并显示传输和显示一个图像所需的时间长度。使用上述同一个2.5 GHz Windows机器,该过程仅需50毫秒,这意味着该系统应该能够舒适地保持每秒10张图像的动画帧速率。

usb_image_to_efm8.zip.

你自己试试这个项目吧!得到bom。