当前的项目将这些功能融合在一起,以有效,方便地在LCD上显示128 x-128像素图像。目的是从任何标准的灰度.bmp映像文件开始,然后使用SCILAB对其进行处理,然后通过USB将其传输到EFM8微控制器,以便我们可以在128 x-128 x-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,因为我们不需要帧速率。相反,当从PC接收像素数据包时,LCD会顺序更新。
固件
该项目的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 read complete{if(USBBytesReceived == 1 && USBRxPacket[0] == NEW_IMAGE_FLAG){CLEAR_LCD = TRUE;
NextLinetoWrite = 0;
//return the new image flag byte to the PC for flow controlUSBTxByte(NEW_IMAGE_FLAG);//continue with the next USB read procedureBlock_Read(USBRxPacket, USB_PACKET_SIZE, &USBBytesReceived);}
else if(USBBytesReceived == USB_PACKET_SIZE){/*this flag tells the while loop in ImagetoLCD_main.cto process a received USB pixel data packet*/USB_PACKET_RECEIVED = TRUE;//continue with the next USB read procedureBlock_Read(USBRxPacket, USB_PACKET_SIZE, &USBBytesReceived);}
}
当Scilab脚本完成将图像文件转换为LCD像素数据时,它将发送一个单字节数据包,其中具有Imagetolcd_defs.h中定义为new_image_flag中的值。因此,如果接收到的数据包长度是一个,并且单个字节具有new_image_flag的值,则微控制器知道新图像正在途中。它清除了LCD,将new_image_flag传输到PC,并将零加载到NextLineToWrite中,该变量是一个变量,当MicroController接收下一个像素数据包时,它保留了要更新的行地址。如果接收到的数据包长度为64个字节而不是一个字节,则数据包将带来实际的像素数据。在这种情况下,我们只是将USB_Packet_received标志设置为true; LCD更新完成后,流量控制字节将发送。
当Imagetolcd_main.c中的无限循环意识到USB_Packet_received已将其设置为true时,它调用ProcessususBrxPacket():
void ProcessUSBRxPacket()
{
unsigned char n = 0, row, column;
//copy the received pixel data to the LCD display data arrayfor(row = 0; row < LINES_PER_PACKET; row++){for(column = 0; column < NUM_LINE_DATA_BYTES; column++){LCDDisplayData[row][column] = USBRxPacket[n];n++;}
}
//wait until the SPI state variable indicates that the bus is available for a new transferwhile(LCDTxState != IDLE);UpdateLCDLines();
}
在这里,我们将像素数据传输到适当的二维阵列中。在此项目中,lcddisplaydata [] []是4行乘16列:我们仍然需要16列字节来容纳128位水平数据,但是我们只需要4行,因为像素数据是从64个字节数据包中的PC传输的,和64个字节除以16个字节等于4行。更新数组后,程序等待直到LCD通信接口为空闲,然后调用UpdatelCdlines()。
该项目需要对管理SPI转移到LCD的状态机器进行一些更改。以前,我们具有UpdateAlllCdlines()函数,该函数(您可能会从名称中猜到)启动一个过程,该过程更新一个SPI传输中的所有LCD线路。但是现在,我们在一个SPI传输期间仅更新四行,并且在该过程结束时执行了另外两个任务:

SCILAB脚本从图像处理部分开始:

输入映像必须是灰度,128 x-128像素.bmp文件。段bythreshold()函数将图像从灰度转换为黑色和白色,因为使用我们的LCD,像素是打开或关闭的 - 允许使用灰色。一系列的BITSET()操作将此图像数据转换为可以发送到EFM8并直接传输到LCD的像素数据。请注意,像SCILAB这样的复杂计算应用程序并未针对我们在这里使用的那种尴尬的位置操作进行优化。换句话说,上述代码中的双循环块需要很长时间才能执行(例如,运行Windows 8.1的2.5 GHz处理器约23秒)。因此,如果要调整此代码以显示动画序列,则需要在开始将数据发送到EFM8之前将所有图像转换为LCD像素格式。
SCILAB脚本中的另一个主要部分是通过VCP连接将像素数据发送到EFM8的FO循环:

使用SLSENDARRAY()函数将四行像素数据转换为一维数组,并以单个64字节数据包的形式传输。然后,该脚本在发送接下来的四行像素数据之前,先读取EFM8的单个确认字节。重要说明:此脚本中对SlreadByte()的调用为第二个参数,即响应= SlreadByte(efm8port,1)。此“ 1”表示该函数将阻止,也就是说,直到至少一个字节到达之前,Scilab都不会做任何事情。这里的优势在于,脚本会尽快运行,因为EFM8发送确认字节后,执行将继续。但是,问题是,如果出现问题,字节永远不会出现,那么Scilab将陷入昏迷状态,直到您关闭程序并重新打开程序为止。因此,在调试阶段进行此操作的更好方法是使用Sleep()函数给EFM8响应时间,然后读取字节而不阻止,即SlreadByte(efm8port,0)。
SCILAB脚本还调用TIC()和TOC()来测量和显示传输和显示一个图像所需的时间。在上面提到的相同的2.5 GHz Windows机器的情况下,该过程仅需约50毫秒,这意味着该系统应该能够舒适地维持每秒10张图像的动画帧速率。