旧乡故客
发表于: 2018-7-29 15:58:00 | 显示全部楼层

本系列文章主要介绍如何使用EFM8控制器制作一款智能环境光监测仪,共包含5个部分:

●    Part 1:在LCD上显示测量值

●    Part 2:理解和实现ADC

●    Part 3:测量和解析环境光照度

●    Part 4:过零检测

●    Part 5:使用双向可控硅调节灯亮度


所需的硬件/软件

●    SLSTK2000A EFM8评估板

●    Simplicity Studio集成开发环境

●    SCILAB


简介

该系列文章的总体目标是设计一款智能环境光监测仪,用于分析室内光照强度,并据此执行对应的响应动作,如控制灯调光器。在开发该项目的过程中,我们需要一种简便的方法来报告表示电流和电压幅度的模数转换值。因此,我们将先编写固件,该固件可以将存储在变量中的普通数值转换为一系列单独的数字,然后这些数字将决定我们传输哪个像素数据数组到LCD模块中。


该固件设计为显示三位数的测量值,单位为毫伏、伏、微安或毫安。如果显示的单位为伏或毫安,则自动启用第一个数字后面的小数点。这意味着显示接口可以处理0μA至9.99 mA的电流幅度和0 mV至9.99 V的电压幅度。但是,量程范围内的大部分值永远不会被使用 - 光传感器的最大输出电流为5 mA,并且ADC无法测量高于其参考电压的电压,在此设计中为2.4 V.


端口I / O

hw_cnfg5.jpg

如上图所示,SPI信号被使能并映射到连接到相应LCD信号的引脚。 SPI芯片选择信号由固件和P0.1输出引脚控制,因为内置的SPI芯片选择信号与LCD接口不兼容。


外设和中断

在本篇文章中,我们只需要使用两个外设:SPI和Timer4。 SPI配置为3线主机模式,时钟分频器设置为产生875 kHz的SPI时钟频率。

spiconfig.jpg


SPI中断使能,因为SPI传输由SPI中断服务程序中的状态机控制。发送每个字节后,将触发中断。 Timer4用于短延时,例如LCD模块数据表中指定的建立和保持延迟时间。一个Timer4计数约为490 ns,因此如果我们需要6μs的延迟,我们将Timer4寄存器设置为零并等待计数达到12。

timer4config.jpg


固件

该项目的固件可分为三个主要部分:LCD通信接口、将存储在变量中的数字转换为一系列单个数字的函数,以及用于更新LCD像素数据阵列的程序。


LCD接口

我们使用多行更新模式与LCD通信。当微控制器启动时,它会将LCD的所有像素点清除为白色。随后通过将128位像素数据写入一个或多个行地址来更新LCD。所有LCD更新都由“LCDControl.c”源文件中的UpdateLCD()函数启动,数据传输过程在SPI中断服务程序中继续。该项目中的LCD通信接口包括对我们在前几篇文章中使用的改进:每次调用UpdateLCD()都可以通过将适当的第一行和最后一行地址放入LCDLineStart和LCDLineStop变量来指定要更新的显示部分。

SPILCD_flowchart4.jpg

将数值转换为单个数字

重要的是,要理解存储在变量中的数值与我们看到的数值的一系列数字基本上不同。变量只是一个1和0的序列;此序列可以以多种方式解释 - 例如,无符号整数、有符号整数或浮点值。然后需要进一步转换以视觉形式表达该解释值。将变量值转换为一系列数字或字符的标准C语言方式是printf()函数,它包含在库中。但是,尽可能避免使用库例程是明智的,主要是因为设计自己的代码更有趣、更有价值、更有启发性。但是,也有一些实际的好处,因为您定制设计的代码可以提供所有所需的功能,同时还可以提高执行速度或降低内存需求。


数值转换过程的关键是取余运算符,由“%”符号表示:

  1. /*the modulus operator is used to obtain the first digit, which
  2. corresponds to the remainder that would result from dividing
  3. by 10; we then twice divide the measured value by 10 and repeat
  4. the modulus operation to obtain the remainders corresponding
  5. to the next two digits*/
  6. remainder = MeasuredValue % 10;
  7. SetLCDDigit(DIGIT_POS_3, MatchDigittoArray(remainder));

  8. MeasuredValue = MeasuredValue/10;
  9. remainder = MeasuredValue % 10;
  10. SetLCDDigit(DIGIT_POS_2, MatchDigittoArray(remainder));

  11. MeasuredValue = MeasuredValue/10;
  12. remainder = MeasuredValue % 10;
  13. SetLCDDigit(DIGIT_POS_1, MatchDigittoArray(remainder));
复制代码

如果将变量的整数值除以“%”符号右侧的数字,取余运算符返回产生的余数。如代码摘录所示,我们使用“MeasuredValue%10”来提取对应于最右边数字的数字。您可以将其视为将所有数字向右移动一位,然后在越过小数点时将最右边的数字切掉。

modulus.jpg


但请注意,模数运算符实际上并未改变原始值。因此,在提取第一个数字后,我们将原始值除以10并重复模数运算以提取下一个数字。 MatchDigittoArray()函数包含一个简单的switch语句,用于确定哪个LCD像素数据数组与其余变量中包含的数字相对应。


从数字到像素数据

二维数组用于保持LCD像素数据。在该项目中,像素数据阵列具有30行,因为数字显示在LCD屏幕的中间30行中。以下功能将数字的像素值复制到LCD像素数据阵列中;传递给该函数的参数是数字位置(第一、第二或第三)和指向数字像素数据数组的指针。类似的代码用于显示适当的单位缩写(μA,mA,mV或V)。

  1. void SetLCDDigit(unsigned char DigitPosition, unsigned char *LCD_Digit)
  2. {
  3.         unsigned char row;
  4.         unsigned char column_byte;
  5.         unsigned char column_byte_begin, column_byte_end;

  6.         /*this switch statement determines which column bytes to
  7.          modify based on the chosen digit position (first, second, or third)*/
  8.         switch(DigitPosition)
  9.         {
  10.                 case DIGIT_POS_1: column_byte_begin = 0;
  11.                 break;

  12.                 case DIGIT_POS_2: column_byte_begin = DIGIT_WIDTH_BYTE;
  13.                 break;

  14.                 case DIGIT_POS_3: column_byte_begin = DIGIT_WIDTH_BYTE*2;
  15.                 break;
  16.         }

  17.         column_byte_end = column_byte_begin + DIGIT_WIDTH_BYTE;

  18.         /*here the LCD display data array is loaded with the bytes
  19.          from the appropriate pixel data array generated by Scilab*/
  20.         for(row = 0; row < DIGIT_HEIGHT_PIX; row++)
  21.         {
  22.                 for(column_byte = column_byte_begin; column_byte < column_byte_end; column_byte++)
  23.                 {
  24.                         LCDDisplayData[row][column_byte] = *LCD_Digit;
  25.                         LCD_Digit++;
  26.                 }
  27.         }

  28.         //wait until the SPI state variable indicates that the bus is available for a new transfer
  29.         while(LCDTxState != IDLE);

  30.         //the SPI state machine needs to know the first and last lines to be updated
  31.         LCDLineStart = DIGIT_BEGIN_LINE;
  32.         LCDLineStop = DIGIT_BEGIN_LINE + DIGIT_HEIGHT_PIX;
  33.         UpdateLCD();
  34. }
复制代码

如果单位为毫安或伏特,则小数点自动显示,如果单位为微安或毫伏,则自动关闭。 像素数据阵列被更新以显示或不显示小数点,如下所示:

  1. void SetDecimalPoint(unsigned char DecimalPointStatus)
  2. {
  3.         unsigned char row;
  4.         unsigned char column_byte;

  5.         //the decimal point can only be located after the first digit
  6.         column_byte = DIGIT_WIDTH_BYTE - 1;

  7.         /*the decimal point requires an area of 4 pixels by 4 pixels,
  8.          but the displayed shape is rounded because the top and bottom
  9.          lines have 2 horizontal black pixels and the 2 middle lines have
  10.          4 horizontal black pixels*/

  11.         /*note the use of bitwise AND and OR operations here: bitwise
  12.          operations are needed because the decimal point does not cover
  13.          8 horizontal pixels (i.e., one horizontal byte), and AND and OR are used
  14.          to ensure that other pixels in the byte are not altered*/

  15.         if(DecimalPointStatus == DEC_POINT_OFF)
  16.         {
  17.                 row = (DIGIT_HEIGHT_PIX - DEC_POINT_HEIGHT);
  18.                 LCDDisplayData[row][column_byte] |= BIT0;
  19.                 LCDDisplayData[row][column_byte + 1]  |= BIT7;
  20.                 row++;
  21.                 LCDDisplayData[row][column_byte] |= (BIT1|BIT0);
  22.                 LCDDisplayData[row][column_byte + 1] |= (BIT7|BIT6);
  23.                 row++;
  24.                 LCDDisplayData[row][column_byte] |= (BIT1|BIT0);
  25.                 LCDDisplayData[row][column_byte + 1]  |= (BIT7|BIT6);
  26.                 row++;
  27.                 LCDDisplayData[row][column_byte] |= BIT0;
  28.                 LCDDisplayData[row][column_byte + 1]  |= BIT7;
  29.         }

  30.         if(DecimalPointStatus == DEC_POINT_ON)
  31.         {
  32.                 row = (DIGIT_HEIGHT_PIX - DEC_POINT_HEIGHT);
  33.                 LCDDisplayData[row][column_byte] &= ~BIT0;
  34.                 LCDDisplayData[row][column_byte + 1]  &= ~BIT7;
  35.                 row++;
  36.                 LCDDisplayData[row][column_byte] &= ~(BIT1|BIT0);
  37.                 LCDDisplayData[row][column_byte + 1] &= ~(BIT7|BIT6);
  38.                 row++;
  39.                 LCDDisplayData[row][column_byte] &= ~(BIT1|BIT0);
  40.                 LCDDisplayData[row][column_byte + 1] &= ~(BIT7|BIT6);
  41.                 row++;
  42.                 LCDDisplayData[row][column_byte] &= ~BIT0;
  43.                 LCDDisplayData[row][column_byte + 1]  &= ~BIT7;
  44.         }

  45.         //wait until the SPI state variable indicates that the bus is available for a new transfer
  46.         while(LCDTxState != IDLE);

  47.         //the SPI state machine needs to know the first and last lines to be updated
  48.         LCDLineStart = DEC_PNT_LINE_BEGIN;
  49.         LCDLineStop = DEC_PNT_LINE_END;
  50.         UpdateLCD();
  51. }
复制代码

“AmbientLightMonitor_main.c”中的while循环如下所示:

  1. number = 800;

  2. while (1)
  3. {
  4.         ConvertMeasurementandDisplay(CURRENT, number);

  5.         //these instructions provide a delay, so that the displayed number increments more slowly
  6.         while(LCDTxState != IDLE);
  7.         for(n = 0; n < 0xFFFF; n++)
  8.                 SFRPAGE = TIMER4_PAGE; TMR4 = 0; while(TMR4 < 0xFF00);

  9.         number++;
  10.         if(number == 10000)        //ConvertMeasurementandDisplay() only accepts numbers up to 9999
  11.                 number = 0;
  12.    }  
复制代码

SCILAB

数字和单位缩写的像素数据数组是使用Scilab脚本生成的。在本篇文章中,数字(以及使用此脚本处理的digit.bmp图像)的尺寸是30个垂直像素乘24个水平像素。 它们比之前使用的10×8像素字符更大、更具视觉吸引力。 请注意,水平像素尺寸再次选为8的倍数,以确保在更新LCD像素数据数组时不需要使用笨拙的按位操作。

跳转到指定楼层
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

主题 29 | 回复: 32



手机版|

GMT+8, 2024-12-21 21:24 , Processed in 0.052299 second(s), 9 queries , Gzip On, MemCache On. Powered by Discuz! X3.5

YiBoard一板网 © 2015-2022 地址:河北省石家庄市长安区高营大街 ( 冀ICP备18020117号 )

快速回复 返回顶部 返回列表