所有关于电路
项目

使用EFM8微控制器在LCD上显示字符

2015年7月13日通过罗伯特Keim

了解如何使用图像编辑器设计字符,并在由EFM8微控制器控制的LCD上显示它们。

了解如何使用图像编辑器设计字符,并在由EFM8微控制器控制的LCD上显示它们。

介绍

上一篇文章探讨了如何使用EFM8微控制beplay体育下载不了器的SPI功能在128 × 128像素的LCD上显示滚动的水平线。在本文中,我们将使用相同的低级接口来更新所有大写字母的LCD显示;更好的是,您将学习有效设计和显示任何字符或符号的技术,可以用10个垂直像素和8个水平像素充分表示。

本系列前面的文章

必需的硬件/软件

项目概况

目标是在128×128像素LCD上设计和显示所有大写字母。将文本打印到典型字处理器样式中的LCD屏幕:光标跟踪将显示下一个字符的位置,并且通过调用名为InstentCharacter()的函数来简单地“键入”新字符。

字体是一种定制设计的固定宽度(和固定高度)字符集,该组合为下一节中描述的技术。固定字符大小由8个水平像素为10个垂直像素(尽管该字符实际限制为9像素,因为包括空白行和空白列以在相邻字符之间提供分离)。出于几种不同的原因选择了这种尺寸。LCD为128像素宽,因此8个水平像素可确保偶数数量的字符跨屏幕。我们使用10个垂直像素而不是8,因为得到的更薄的字母有点更令人愉悦。这些维度也制作足够大的字母,以便易易读但足够小,以便液晶显示器一次可以显示有意义的字符数。然而,也许最重要的原因是以下内容:用于将像素数据从字符数组复制到LCD数据缓冲区的代码具有8像素宽度的更简单,因为表示像素状态的位与字节边界对齐。这意味着我们可以使用清洁字节操作正确更新LCD数据,而不是尴尬(且较低的有效)位操作,其中必须将来自一个字节的位复制到一个字节的一部分和另一个字节的一部分。

设计人物

将视觉符号转换为LCD兼容的数据是一个严重的非直接进程:谁可以通过将一系列十六进制值分配给字节数组来绘制字母G?因此,我们需要一个方便的方法来直观地设计一个字符,将此视觉信息转化为可以合并到我们的微控制器代码中并传输到LCD模块的数据字节。该过程从图像编辑软件开始;我们在这里使用paint.net.,一个功能强大的自由映像和照片编辑应用程序。

获取空白画布,并将其大小调整为10像素高8像素宽,并一直放大。使用铅笔工具填充单个像素,直到字母看起来对,然后将图像另存为灰度.bmp文件。

现在是scilab的时候了,你可以下载这里。Scilab是类似于MATLAB的免费数字计算软件。与Matlab一样,Scilab不仅仅是嘎吱作响的数字。在该项目中,我们将使用它来分析字符图像并将其转换为C代码,以便我们可以将其复制和粘贴到我们的EFM8源文件中。除了Scilab本身之外,您还需要安装图像处理设计工具箱;这可以通过Scilab的原子模块管理器轻松完成:

使用Scilab的最基本方式是在控制台窗口中键入单个命令。但是,当您使用Scinotes文本编辑器开始组合和组织这些命令时,允许您将SciLAB指令合并到执行的相干源代码文件中,如以高级编程语言编写的应用程序的相干源代码文件将Scilab指令。以下是用于此项目的Scinotes脚本(LCD_Image_Handler.Sce)的一些代码:

代码

imagefilename = UigetFile([“*。BMP”]);if(length(imagefilename)== 0)中断结束currentimage = ReadImage(ImageFilename);Currentimage = SegmentByThreshold(Currentimage,100);Currentimage = Flipdim(Currentimage,2);Pixeldata = UINT8(零(10,1));对于row = 1:10 for column = 1:8 if(currentimage(行,列)==%t)pixeldata(行)= bitset(pixeldata(行),列,1);else pixeldata(行)= bitset(pixeldata(行),列,0);结束结束

Display_Characters_.zip

lcd_image_handler.sce执行如下:首先,它打开文件对话框;选择要处理的.bmp文件。

图像被转换为黑白(如果任何像素值不完全是0或255),10字节数组中的每个位都根据对应像素是黑还是白来设置或清除。10字节数组表示一个字符的大小(即10个垂直像素乘以8个水平像素):一个字节提供8个水平像素值,因此10字节数组包含10行,每一行有8个水平像素。最后,使用printf()例程将数组名(取自文件名)和像素数据输出到Scilab控制台。这个过程一直重复,直到你点击文件对话框中的“取消”。

可以将这些变量定义直接复制并粘贴到EFM8项目中的源文件中。该脚本还生成用于在项目的头文件中声明这些变量的代码,因此最终输出如下所示:

如您所见,此过程是高效且完全灵活的。使用此Scinotes脚本,您可以为您可以绘制10行逐列图像的任何符号快速创建LCD兼容的像素数据。

端口I / O.

端口I/O配置与我们在以前的SPI项目

SPI信号映射到适当的端口引脚,除了我们通过P0.1手动驱动的芯片选择信号。

外围设备和中断

外围设备和中断设置类似于我们前面使用的:SPI配置用于与LCD模块通信,Timer2中断控制帧速率,Timer4用于短延迟。唯一的区别是Timer2被配置为5 Hz的中断频率,而不是60 Hz。在这个项目中,每当Timer2溢出时,一个新的字母就会被打印到LCD屏幕上,并且较慢的更新频率使得在字符被覆盖之前更容易观察字符。

固件

在上一个项目中,我们使用LCD的单行更新模式:驱动芯片选择到逻辑高电平,EFM8发送模式选择字节,然后发送线路地址,然后是128位像素数据。传输以两个虚拟字节完成,芯片选择返回逻辑低电平。此模式对于毫不匹扰的应用程序很好,但新固件是不同的。整个LCD的像素值存储在单个二维阵列中,并且每当Timer2中断告诉固件更新显示时,所有这些数据位都被传送到LCD。换句话说,我们正在向LCD发送更多的数据。(注意:在该特定实现中,微控制器仅控制前60个LCD线,而不是所有128,因为应用程序只有1024个字节的片上RAM;整个LCD足够大的阵列需要2048个字节。通过选择具有4352字节的RAM的代码兼容的EFM8设备,在自定义设计中容易地纠正此限制。)

使用所有这些额外的数据到LCD,使用“多线更新模式;”是有意义的。顾名思义,此模式允许我们在一个SPI传输期间更新多行的像素值。这意味着我们在SPI中断程序中需要一个新的状态机:

固件的另一个重要的附加功能是将scilab生成的阵列中的像素数据插入到二维阵列中,该二维阵列保存了整个LCD的像素数据:

代码

该函数的输入是一个指向像素数据数组的指针;无符号字符row_pixel;无符号字符column_byte;// row_pixel = LCDCursor[ROW];column_byte = LCDCursor[COL]; column_byte = LCDCursor[COL];/*从像素数据数组的每个字节复制到LCDDisplayData,从当前光标的位置开始n < CHAR_HEIGHT;n++) {LCDDisplayData[row_pixel][column_byte] = *LCD_Character;LCD_Character + +;//指向数组row_pixel的下一个字节; //the next byte corresponds to pixel data for the next line } LCDCursor[COL]++; //move the cursor one character width to the right /*if the cursor has reached the end of the line, return the cursor to the far left and move it down by one character height*/ if(LCDCursor[COL] == NUM_LINE_DATA_BYTES) { LCDCursor[COL] = 0; LCDCursor[ROW] = LCDCursor[ROW] + CHAR_HEIGHT; if(LCDCursor[ROW] == NUM_LINES) //if the cursor has reached the end of the display area, LCDCursor[ROW] = 0; //return the cursor to the top line } while(UPDATE_LCD == FALSE); //wait here until Timer2 initiates an LCD update UPDATE_LCD = FALSE; UpdateAllLCDLines(); }

imagehandler_and_lcdtext.zip.

如果您检查了上面的代码并阅读了注释,您应该能够很好地了解它是如何工作的。注意,这个例程既更新LCD像素数据数组,又管理光标位置。

该项目的整体功能是重复打印所有大写字母,从A到Z再次从A到Z.基本程序流程如下:

  • 微控制器清除液晶屏。
  • Timer2已启用。
  • 该程序进入无限循环并使用insertCharacter()函数打印a,然后使用b,然后是c,等到z,然后再次回到z。
  • insert特征()函数(参见上面的代码摘录)更新LCD像素数据阵列,管理光标,等待Timer2设置LCD更新的标志,然后通过updateAlealllcdlines()函数启动更新。
  • 只有当SPI状态变量表示该SPI接口空闲时,Timer2 ISR才会设置更新标志位。这确保了InsertCharacter()函数在前一个传输完成之前不会尝试发起新的SPI传输。

视频

下一篇文章串联:通过USB与EFM8微控制器汇流

为自己提供这个项目!BOM。