目录
1、定时器/计数器0/1介绍
1.1 定时器介绍
1.2 单片机定时/计数器原理
2、定时器/计数器0和1的相关寄存器
2.1 定时器/计数器控制寄存器TCON
2.2 定时器/计数器工作模式寄存器TMOD
2.3 定时器/计数器工作模式
2.3.1 模式0(13位定时器/计数器)
2.3.2 模式1(16位定时器/计数器)
2.3.3 模式2(8位自动重装模式)
2.3.4 模式3(两个8位计数器)
2.4 时间常数的存储——特殊功能寄存器TL0、TH0、TL1、TH1
3、定时/计数器实战篇
在介绍定时器之前我们先科普下几个知识:
(1)CPU 时序的有关知识
①振荡周期:为单片机提供定时信号的振荡源的周期(晶振周期或外加振荡周期)。
②状态周期:2个振荡周期为1个状态周期,用S表示。振荡周期又称S周期或时钟周期。
③机器周期:1个机器周期含6个状态周期,12个振荡周期。
④指令周期:完成 1条指令所占用的全部时间,它以机器周期为单位。例如:外接晶振为 12MHz 时,51 单片机相关周期的具体值为:振荡周期=1/12us;状态周期=1/6us;机器周期=1us;指令周期=1~4us;(2)学习定时器前需要明白的几点
①51单片机有两组定时器/计数器,因为既可以定时,又可以计数,故称之为定时器/计数器。
②定时器/计数器和单片机的CPU是相互独立的。定时器/计数器工作的过程是自动完成的,不需要 CPU 的参与。
③51单片机中的定时器/计数器是根据机器内部的时钟或者是外部的脉冲信号对寄存器中的数据加 1。
有了定时器/计数器之后,可以增加单片机的效率,一些简单的重复加1的工作可以交给定时器/计数器处理。CPU转而处理一些复杂的事情。同时可以实现精确定时作用。
STC89C52系列单片机的定时器0和定时器1,与传统8051的定时器完全兼容,当在定时器1做波特率发生器时,定时器0可以当两个8位定时器用。
STC89C52系列单片机内部设置的两个16位定时器/计数器T0和T1都具有计数方式和定时方式两种工作方式。对每个定时器/计数器(T0和T1),在特殊功能寄存器TMOD中都有一控制位C/T来选择T0或T1为定时器还是计数器。定时器/计数器的核心部件是一个加法(也有减法)的计数器,其本质是对脉冲进行计数。只是计数脉冲来源不同:如果计数脉冲来自系统时钟,则为定时方式,此时定时器/计数器每12个时钟或者每6个时钟得到一个计数脉冲,计数值加1;如果计数脉冲来自单片机外部引脚(T0为P3.4,T1为P3.5),则为计数方式,每来一个脉冲加1。
当定时器/计数器工作在定时模式时,可在烧录用户程序时在STC-ISP编程器中设置使能6T(双倍速)模式(系统频率是否加倍选择:采用6T (双倍速) 模式;不选:不加倍,采用12T (单倍速) 模式)是系统时钟➗12还是系统时钟➗6后让T0和T1进行计数。当定时器/计数器工作在计数模式时,对外部脉冲计数不分频。
定时器/计数器0有4种工作模式:模式0(13位定时器/计数器),模式1(16位定时器/计数器模式),模式2(8位自动重装模式),模式3(两个8位定时器/计数器)。定时器/计数器1除模式3外,其他工作模式与定时器/计数器0相同,T1在模式3时无效,停止计数。学习了相关寄存器之后,对于定时器计数器的掌握就得心应手了。
定时器/计数器关键的寄存器如下表格:
接下来,我们就对定时器/计数器0和1的相关寄存器进行学习,而后进行实操。
定时器/计数器的控制寄存器TCON代表的是(Timer Control),即定时器/计数器控制寄存器。TCON为定时器/计数器T0、T1的控制寄存器,同时也锁存T0、T1溢出中断源和外部请求中断源等,TCON格式如下:
TCON:定时器/计数器中断控制寄存器(可位寻址)
该寄存器与定时/计数器有关的位就只是高四位,低四位是与外部中断有关的。也就是TCON 的低 4 位用于控制外部中断。TCON 的高 4 位用于控制定 时/计数器的启动和中断申请。在外部中断的那篇博客也讲过相关含义的,既然这里学到了该寄存,再讲一次也无妨。 为了更加方便理解,上图:
以下位是与定时/计数器有关,TCON定时/计数器控制寄存器高四位含义:
TF1(Timer1 Overflow Flag):定时器/计数器T1溢出标志。
T1被允许计数以后,从初值开始加1计数。当最高位产生溢出时由硬件置“1”TF1,向CPU请求中断,一直保持到CPU响应中断时,才由硬件清“0”TF1 (TF1也可由程序查询清“0”)。
TR1(Timer1 Run Control):定时器T1的运行控制位。
该位由软件置位和清零。当GATE (TMOD.7) =0,TR1=1时就允许T1开始计数,TR1=0时禁止T1计数。当GATE(TMOD.7) =1,TR1=1且INT1输入高电平时,才允许T1计数。
TF0(Timer0 Overflow Flag):定时器/计数器T0溢出中断标志。
T0被允许计数以后,从初值开始加1计数,当最高位产生溢出时,由硬件置“1”TF0,向CPU请求中断,一直保持CPU响应该中断时,才由硬件清“0”TF0( TF0也可由程序查询清“0”)。
TR0(Timer0 Run Control):定时器T0的运行控制位。
该位由软件置位和清零。当GATE (TMOD.3) =0,TR0=1时就允许T0开始计数,TR0=0时禁止T0计数。当GATE(TMOD.3) =1,TR0=0且INT0输入高电平时,才允许T0计数。
以下四位是与外部中断有关,TCON定时/计数器控制寄存器低四位含义:
IE1(External Interrupt1 Enable):外部中断1请求源(INT1/P3.3) 标志。
IE1=1,外部中断向CPU请求中断,当CPU响应该中断时由硬件清“0”IE1。
IT1(Interrupt1 Type Control):外部中断1触发方式控制位。
IT1=0时,外部中断1为低电平触发方式,当INT1 (P3.3)输入低电平时,置位IE1 。采用低电平触发方式时,外部中断源(输入到INT1) 必须保持低电平有效,直到该中断被CPI 响应,同时在该中断服务程序执行完之前,外部中断源必须被清除(P3.3要变高),否则将产生另一次中断。当IT1=1时,则外部中断1(INT1)端口由“1”一>“0”下降沿跳变,激活中断请求标志位IE1 ,向主机请求中断处理。
IE0(External Interrupt0 Enable):外部中断0请求源 (INTO/P3.2) 标志。
IE0=1外部中断0向CPU请求中断,当CPU响应外部中断时,由硬件清“0”IE0(边沿触发方式)。
IT0(Interrupt0 Type Control):外部中断0触发方式控制位。
IT0=0时,外部中断0为低电平触发方式,当INT0 (P3.2)输入低电平时,置位IE0。采用低电平触发方式时,外部中断源(输入到INTO) 必须保持低电平有效,直到该中断被CPU响应,同时在该中断服冬程序执行完之前,外部中断源必须被清除(P3.2要变高) ,否则将产生另一次中断。当IT0=1时,则外部中断0(INT0)端口由“1”一>“0”下降沿跳变,激活中断请求标志位IE0,向主机请求中断处理。
在51单片机中,TMOD是定时器/计数器工作模式寄存器(Timer/Counter Mode Register)。它是一个8位的寄存器,用于设置定时器0(T0)和定时器1(T1)的工作模式。定时和计数功能由特殊功能寄存器TMOD的控制位C/T进行选择,TMOD寄存器的各位信息如下表所列。可以看出,2个定时/计数器有4种操作模式,通过TMOD的M1和M0选择。2个定时/计数器的模式0、1和2都相同,模式3不同,各模式下的功能如下所述。
TMOD:定时器/计数器工作模式寄存器(不可位寻址)
8位分为两组,高4位为定时/计数器1的方式控制字段,低4位为定时/计数器0的方式控制字段。
GATE:门控位
(1)GATE=0时,定时/计数器只由软件控制位TRx(x为0或者1)来控制启动/停止。TRx位为1时,定时/计数器启动工作;为0时,定时/计数器停止工作。
(2)GATE=1,定时/计数器的启动要受到外部中断引脚(INT0/INT1脚)和TRx共同控制。只有当外部中断引脚INT0/INT1为高电平时,同时TR0/TR1置1时,才能启动定时/计数器0/1;
C/T:定时/计数器工作模式选择位
(1)C/T=0时,定时/计数器为定时器方式,定时/计数器对晶振脉冲的分频信号(机器周期)进行计数,从定时/计数器的计数值便可求得计数时间,因此称为定时器方式。
(2)C/T=1时,定时/计数器为计数器方式,定时/计数器对外部引脚 T0(P3.4)或T1(P3.5)上输入的脉冲进行计数。CPU 在每个机器周期的 S5P2期间,对 T0或T1引脚进行采样,如在前一个机器周期采得的值为 1,后一个机器周期采得的值为 0,则计数器加1。由于确认一次负跳变需要两个机器周期,因此最高计数频率为晶振频率的 1/24(12T 模式)或1/12(6T模式)。
M1、M0:定时/计数器工作方式选择位
这里我主要讲的定时/计数器工作方式0、1、2,至于方式3,因为基本用不到,所以就不过多的解释,感兴趣的可以自己去了解。关于定时/计数器工作方式0、1、2原理的解读以定时/计数器0为例子进行讲解,至于定时/计数器1的工作方式0、1、2的原理和定时/计数器0是一样的。为了方便理解定时/计数器各个模式内部工作原理图的与门、或门、非门。上图:
当CT=0时,多路开关连接到系统时钟的分频输出,T0对时钟周期计数,T0工作在定时方式。当C/T=1时,多路开关连接到外部脉冲输入P3.4/T0,即T0工作在计数方式。
STC89C52系列单片机的定时器有两种计数速率:一种是12T模式,每12个时钟加1,与传统8051单片机相同:另外一种是6T模式,每6个时钟加1,速度是传统8051单片机的2倍。T0的速率在烧录用户程序时在STC-ISP编程器中设置。
该模式下的13位寄存器包含TH0全部8个位及TL0的低5位。TL0的高3位不定,可将其忽略。
上图是属于:模式0:M0=0;M1=0;定时器模式:C/T=0;门口位:GATE=0;单纯由定时/计数器运行控制位控制:TR0=1;
方式0为13位计数,由TL0的低5位(高3位未用)和TH0的8位组成。机器周期每来一个脉冲,特殊功能寄存器(Timer Low0)TL0计数值加1,当TL0计数值加到TL0的最大值时,再加1时,TL0溢出,TL0归0;TL0的低5位溢出时向TH0进位,TH0计数值加1,当TH0计数值加到TH0的最大值时,TH0溢出时,置位TCON中的TF0标志,向CPU发出中断请求。
(2)当 GATE=1时,由外中断引脚信号控制或门的输出,此时控制与门的开启由外中断引脚信号和 TR0 共同控制定时/计数器的开启。当 TR0=1 时,外中断引脚信号引脚的高电平启动计数,外中断引脚信号引脚的低电平停止计数。这种方式常用来测量外中断引脚上正脉冲的宽度。如下图:
定时/计数器工作模式选择位C/T=1时,定时/计数器为计数器方式,具体的计数方式参考定时器/计数器工作模式寄存器TMOD介绍中C/T的讲解,这里就不过多的陈述了;计数模式时,计数脉冲是 T0 引脚上的外部脉冲。计数初值与计数个数的关系为:X=2(13)-N。其中2(13)表示 2 的 13 次方。(X表示计数初值,N表示计数个数)。
注意:该方式不能像方式2一样,硬件自动重装TL0和TH0初值,是需要我们自己软件重装TH0和TH0初值。也就是说使用该方法时,不仅需要在定时/计数器初始化时,给定时/计数器特殊功能寄存器TL0和TH0赋初始值,而且还需要在TH0溢出后进入定时/计数器中断函数后再赋初TL0和TH0始值一次。
模式1除了使用了TH0及TL0全部16位外,由 TL0 作为低 8 位,TH0 作为高 8 位,组成了16 位加 1 计数器。其他与模式0完全相同。即此模式下定时器/计数器0作为16位定时器/计数器,如下图所示。
需要注意的一点是:当C/T=1;处于计数模式时,计数脉冲是 T0 引脚上的外部脉冲。计数初值与计数个数的关系为:X=2(16)- N。其中2(16)表示 2 的 16 次方。(X表示计数初值,N表示计数个数)。
注意:该方式也不能像方式2一样,硬件自动重装TL0和TH0初值,是需要我们自己软件重装TH0和TH0初值。也就是说使用该方法时,不仅需要在定时/计数器初始化时,给定时/计数器特殊功能寄存器TL0和TH0赋初始值,而且还需要在TH0溢出后进入定时/计数器中断函数后再赋TL0和TH0初始值一次。
该模式的原理和前面两种模式相同,唯一不同点,可以通过观察下图:
该方式只需 TL0溢出时,就可以置位TCON中的TF0标志,向CPU发出中断请求。该方式硬件自动重装TL0的初值,就不需要TL0溢出后进入定时/计数器中断函数后再赋TL0初始值一次。至于自动重装是该这么理解呢?
TL0的溢出不仅置位TF0,而且将TH0内容重新装入TL0,TH0内容由软件预置,重装时TH0内容不变。什么意思呢?意思是:当TL0溢出时,TL0归0,不仅置位TF0,向CPU发出中断请求,而且还将TH0的值赋值给TL0(TL0=TH0),因为我们一般初始化定时/计数器的时候都会给寄存器TL0和TH0赋初值的。这一操作下来,那我们就不需要在进入中断函数的时候再给寄存器TL0赋初始值了。
还需要注意的一点是:当C/T=1;处于计数模式时,计数脉冲是 T0 引脚上的外部脉冲。计数初值与计数个数的关系为:X=2(8)- N。其中2(8)表示 2 的 8 次方。(X表示计数初值,N表示计数个数)。
对定时器1,在模式3时,定时器1停止计数,效果与将TR1设置为0相同。
对定时器0,此模式下定时器0的TL0及TH0作为2个独立的8位计数器。下图为模式3时的定时器0逻辑图。TL0占用定时器0的控制位:CT、GATE、TR0、INT0及TF0。TH0限定为定时器功能(计数器周期),占用定时器1的TR1及TF1。此时,TH0控制定时器1中断。
模式3是为了增加一个附加的8位定时器/计数器而提供的,使单片机具有三个定时器/计数器。模式3只适用于定时器/计数器0,定时器T1处于模式3时相当于TR1=0,停止计数,而T0可作为两个定时器用。
该模式其实也就是特殊功能寄存器TH0控制定时/计数器1中断(区别:一般定时/计数器1中断的控制是TH1和TL1的),TL0控制定时/计数器0中断。没什么难的,但是,一般我们用不到。
在51单片机上的特殊功能寄存器(Special Function Register,SFR)区,有两组寄存器是专门用来存放计数器的时间常数的。它们就是TL0、TH0和TL1、TH1,各自都是8位的。其中TL0和TH0分管时间常数的低8位和高8位,控制计数器0;TL1和TH1同理,控制计数器1。
为什么要把低8位和高8位分开存储呢?因为这样可以为计数器提供更多的工作方式。这4个寄存器既用来存放时间常数,同时也可看作4个定时计数器。51单片机的计数器有4种工作方式:方式0,方式1,方式2,方式3。(工作方式的设置是通过设定TMOD寄存器的M1和M0位来控制的)。
这里我们也是以定时/计数器0为例子,配置定时初始化程序。说明波特率的计算,也就是TH0和TL0的初始值如何给,我们这里直接使用STC-ISP软件生成的。
第一步:设置定时器/计数器控制寄存器TCON:低四位与外部中断有关,与定时/计数器无关,不需要管。①只需要打开定时/计数器0就可以——>TCON.4/TR0=1;②清除TF0标志——>TCON.5/TF0=0;综合的:TR0=1;TF0=0;(等同于TCON=0x10;)如下图:
第二步:设置定时器/计数器工作模式寄存器TMOD:我们这里使用的是定时/计数器0、定时模式、单纯TR0控制定时器开启、工作模式1(16位定时/计数器模式);
定时器0,那么工作模式寄存器TMOD高四位都为零;定时模式、单纯TR0控制定时器开启——>C/T=0;GATE=0;工作模式1(16位定时/计数器模式)——>M1=0;M0=1;综合得:TMOD=0x01;如下图:
第三步:设置特殊功能寄存器TL0、TH0初始值:晶振为:12.000MHz;设置为1毫秒溢出一(1毫秒执行一次中断)次——>TH0=0xFC;TL0=0x18;综合得:TH0=0xFC;TL0=0x18;无图:
第四步:配置中断允许寄存器IE:①开启CPU的总中断允许控制位——>EA/IE.7=1;②开启定时/计数器0中断允许位——>ET0/IE.1=1;综合得:EA=1;ET0=1;(等同于IE=0x82);如下图:
综合上述所有步骤,就可以完成定时/计数器初始化,我们将它封装成一个函数,如下:
/** * @brief 定时器0初始化,1毫秒@12.000MHz * @param 无 * @retval 无 */ void Timer0Init(void) { TMOD &= 0xF0; //设置定时器模式 TMOD |= 0x01; //设置定时器模式 //TMOD=0x01; //和上面的两步是一样的 TL0 = 0x18; //设置定时初值 TH0 = 0xFC; //设置定时初值 TF0 = 0; //清除TF0标志 TR0 = 1; //定时器0开始计时 ET0=1; EA=1; }
定时/计数器1也是同理的。
下面我们开始用我们写好的定时/计数器口初始化驱动程序进行实操了,实操内容为:电子时钟;
现象图如下:
程序如下:
main.c
#include
#include "Delay.h" #include "LCD1602.h" #include "Timer0.h" unsigned char Sec=55,Min=59,Hour=23; void main() { LCD_Init(); Timer0Init(); LCD_ShowString(1,1,"Clock:"); //上电显示静态字符串 LCD_ShowString(2,1," : :"); while(1) { LCD_ShowNum(2,1,Hour,2); //显示时分秒 LCD_ShowNum(2,4,Min,2); LCD_ShowNum(2,7,Sec,2); } } void Timer0_Routine() interrupt 1 { static unsigned int T0Count; TL0 = 0x18; //设置定时初值 TH0 = 0xFC; //设置定时初值 T0Count++; if(T0Count>=1000) //定时器分频,1s { T0Count=0; Sec++; //1秒到,Sec自增 if(Sec>=60) { Sec=0; //60秒到,Sec清0,Min自增 Min++; if(Min>=60) { Min=0; //60分钟到,Min清0,Hour自增 Hour++; if(Hour>=24) { Hour=0; //24小时到,Hour清0 } } } } } Timer0.c
#include
/** * @brief 定时器0初始化,1毫秒@12.000MHz * @param 无 * @retval 无 */ void Timer0Init(void) { TMOD &= 0xF0; //设置定时器模式 TMOD |= 0x01; //设置定时器模式 //TMOD=0x01; //和上面的两步是一样的 TL0 = 0x18; //设置定时初值 TH0 = 0xFC; //设置定时初值 TF0 = 0; //清除TF0标志 TR0 = 1; //定时器0开始计时 ET0=1; EA=1; } /*定时器中断函数模板 void Timer0_Routine() interrupt 1 { static unsigned int T0Count; TL0 = 0x18; //设置定时初值 TH0 = 0xFC; //设置定时初值 T0Count++; if(T0Count>=1000) { T0Count=0; } } */ Timer0.h
#ifndef __TIMER0_H__ #define __TIMER0_H__ void Timer0Init(void); #endif
Delay.c
void Delay(unsigned int xms) { unsigned char i, j; while(xms--) { i = 2; j = 239; do { while (--j); } while (--i); } }
Delay.h
#ifndef __DELAY_H__ #define __DELAY_H__ void Delay(unsigned int xms); #endif
LCD1602.c
#include
//引脚配置: sbit LCD_RS=P2^6; sbit LCD_RW=P2^5; sbit LCD_EN=P2^7; #define LCD_DataPort P0 //函数定义: /** * @brief LCD1602延时函数,12MHz调用可延时1ms * @param 无 * @retval 无 */ void LCD_Delay() { unsigned char i, j; i = 2; j = 239; do { while (--j); } while (--i); } /** * @brief LCD1602写命令 * @param Command 要写入的命令 * @retval 无 */ void LCD_WriteCommand(unsigned char Command) { LCD_RS=0; LCD_RW=0; LCD_DataPort=Command; LCD_EN=1; LCD_Delay(); LCD_EN=0; LCD_Delay(); } /** * @brief LCD1602写数据 * @param Data 要写入的数据 * @retval 无 */ void LCD_WriteData(unsigned char Data) { LCD_RS=1; LCD_RW=0; LCD_DataPort=Data; LCD_EN=1; LCD_Delay(); LCD_EN=0; LCD_Delay(); } /** * @brief LCD1602设置光标位置 * @param Line 行位置,范围:1~2 * @param Column 列位置,范围:1~16 * @retval 无 */ void LCD_SetCursor(unsigned char Line,unsigned char Column) { if(Line==1) { LCD_WriteCommand(0x80|(Column-1)); } else if(Line==2) { LCD_WriteCommand(0x80|(Column-1+0x40)); } } /** * @brief LCD1602初始化函数 * @param 无 * @retval 无 */ void LCD_Init() { LCD_WriteCommand(0x38);//八位数据接口,两行显示,5*7点阵 LCD_WriteCommand(0x0c);//显示开,光标关,闪烁关 LCD_WriteCommand(0x06);//数据读写操作后,光标自动加一,画面不动 LCD_WriteCommand(0x01);//光标复位,清屏 } /** * @brief 在LCD1602指定位置上显示一个字符 * @param Line 行位置,范围:1~2 * @param Column 列位置,范围:1~16 * @param Char 要显示的字符 * @retval 无 */ void LCD_ShowChar(unsigned char Line,unsigned char Column,char Char) { LCD_SetCursor(Line,Column); LCD_WriteData(Char); } /** * @brief 在LCD1602指定位置开始显示所给字符串 * @param Line 起始行位置,范围:1~2 * @param Column 起始列位置,范围:1~16 * @param String 要显示的字符串 * @retval 无 */ void LCD_ShowString(unsigned char Line,unsigned char Column,char *String) { unsigned char i; LCD_SetCursor(Line,Column); for(i=0;String[i]!='\0';i++) { LCD_WriteData(String[i]); } } /** * @brief 返回值=X的Y次方 */ int LCD_Pow(int X,int Y) { unsigned char i; int Result=1; for(i=0;i 0;i--) { LCD_WriteData(Number/LCD_Pow(10,i-1)%10+'0'); } } /** * @brief 在LCD1602指定位置开始以有符号十进制显示所给数字 * @param Line 起始行位置,范围:1~2 * @param Column 起始列位置,范围:1~16 * @param Number 要显示的数字,范围:-32768~32767 * @param Length 要显示数字的长度,范围:1~5 * @retval 无 */ void LCD_ShowSignedNum(unsigned char Line,unsigned char Column,int Number,unsigned char Length) { unsigned char i; unsigned int Number1; LCD_SetCursor(Line,Column); if(Number>=0) { LCD_WriteData('+'); Number1=Number; } else { LCD_WriteData('-'); Number1=-Number; } for(i=Length;i>0;i--) { LCD_WriteData(Number1/LCD_Pow(10,i-1)%10+'0'); } } /** * @brief 在LCD1602指定位置开始以十六进制显示所给数字 * @param Line 起始行位置,范围:1~2 * @param Column 起始列位置,范围:1~16 * @param Number 要显示的数字,范围:0~0xFFFF * @param Length 要显示数字的长度,范围:1~4 * @retval 无 */ void LCD_ShowHexNum(unsigned char Line,unsigned char Column,unsigned int Number,unsigned char Length) { unsigned char i,SingleNumber; LCD_SetCursor(Line,Column); for(i=Length;i>0;i--) { SingleNumber=Number/LCD_Pow(16,i-1)%16; if(SingleNumber<10) { LCD_WriteData(SingleNumber+'0'); } else { LCD_WriteData(SingleNumber-10+'A'); } } } /** * @brief 在LCD1602指定位置开始以二进制显示所给数字 * @param Line 起始行位置,范围:1~2 * @param Column 起始列位置,范围:1~16 * @param Number 要显示的数字,范围:0~1111 1111 1111 1111 * @param Length 要显示数字的长度,范围:1~16 * @retval 无 */ void LCD_ShowBinNum(unsigned char Line,unsigned char Column,unsigned int Number,unsigned char Length) { unsigned char i; LCD_SetCursor(Line,Column); for(i=Length;i>0;i--) { LCD_WriteData(Number/LCD_Pow(2,i-1)%2+'0'); } } LCD1602.h
#ifndef __LCD1602_H__ #define __LCD1602_H__ //用户调用函数: void LCD_Init(); void LCD_ShowChar(unsigned char Line,unsigned char Column,char Char); void LCD_ShowString(unsigned char Line,unsigned char Column,char *String); void LCD_ShowNum(unsigned char Line,unsigned char Column,unsigned int Number,unsigned char Length); void LCD_ShowSignedNum(unsigned char Line,unsigned char Column,int Number,unsigned char Length); void LCD_ShowHexNum(unsigned char Line,unsigned char Column,unsigned int Number,unsigned char Length); void LCD_ShowBinNum(unsigned char Line,unsigned char Column,unsigned int Number,unsigned char Length); #endif