2.1 系统应用程序组成
本设计采用C语言编程,编译环境为keil UV3。
keil c51 是美国Keil Software 公司出品的51 系列兼容单片机C 语言软件开发系统,和汇编相比,C 在功能上、结构性、可读性、可维护性上有明显的优势,因而易学易用。
Keil c51 软件提供丰富的库函数和功能强大的集成开发调试工具,全Windows 界面。另外重要的一点,只要看一下编译后生成的汇编代码,就能体会到keil c51 生成的目标代码效率非常之高,多数语句生成的汇编代码很紧凑,容易理解。在开发大型软件时更能体现高级语言的优势。
Keil C51 可以完成编辑、编译、连接、调试、仿真等整个开发流程。开发人员可用IDE 本身或其它编辑器编辑C 或汇编源文件,然后分别有C51 及A51 编辑器编译连接生成单片机可执行的二进制文件(.HEX),然后通过单片机的烧写软件将HEX 文件烧入单片机内。
软件主要三个方面:一是初始化系统;二是按键检测;三是数据采集、数据处理并进行显示。这三个方面的操作分别在主程序中来进行。程序采用模块化的结构,这样程序结构清楚,易编程和易读性好,也便于调试和修改。程序结构如图10所示。
图10 程序结构
2.2 主程序流程图
系统程序固化在STC12C5A60S2内部的flash存储器中,分为主程序和若干子程序。主程序的功能是系统初始化,管理和调用各个子程序。本设计的程序流程图如图11所示。
图11 程序流程图
2.3 AD采样程序块
本文设计的STC12C5A60S2片内AD程序如下:
#include "config.h"
//---------------------------------------------------------------------
// SPEED1 SPEED0 A/D转换所需时间
#define AD_SPEED 0x60 // 0110,0000 1 1 90 个时钟周期转换一次,
// CPU工作频率21MHz时 A/D转换速度约 300KHz
//#define AD_SPEED 0x40 //0100,0000 1 0 140 个时钟周期转换一次
//#define AD_SPEED 0x20 //0010,0000 0 1 280 个时钟周期转换一次
//#define AD_SPEED 0x00 //0000,0000 0 0 420 个时钟周期转换一次
//---------------------------------------------------------------------
unsigned int get_AD_result(unsigned char channel)
{
unsigned char AD_finished=0; //存储 A/D 转换标志
ADC_RES = 0; //A/D 转换结果高8位
ADC_RESL = 0; //A/D 转换结果低2位
channel &= 0x07; //0000,0111 清0高5位
ADC_CONTR = AD_SPEED;
_nop_();
ADC_CONTR|=0xE0; //1110,0000清ADC_FLAG,ADC_START位和低三位
ADC_CONTR |= channel; //选择 A/D 当前通道
_nop_();
ADC_CONTR |= 0x80; //启动 A/D 电源
delay(1); //使输入电压达到稳定
ADC_CONTR |= 0x08; //0000,1000 令 ADCS = 1, 启动A/D转换
AD_finished = 0;
while (AD_finished == 0 ) //等待A/D转换结束
{
AD_finished = (ADC_CONTR & 0x10); //0001,0000 测试A/D转换结束否
}
ADC_CONTR &= 0xE7; //1111,0111 清 ADC_FLAG 位, 关闭A/D转换
return (ADC_RES<<2|ADC_RESL);//返回转换后的结果
}
2.4 液晶显示程序块
本文设计的1602液晶操作程序如下:
/*****************************************************
函 数 名:WriteDataLCD()
功 能:向1602写数据
说 明:
入口参数:WDLCD
返 回 值:无
*****************************************************/
void WriteDataLCD(unsigned char WDLCD)
{
ReadStatusLCD(); //检测忙
LCD_Data = WDLCD;
LCD_RS = 1;
LCD_RW = 0;
LCD_E = 0; //若晶振速度太高可以在这后加小的延时
LCD_E = 0;
LCD_E = 0; //延时
LCD_E = 1;
}
/*****************************************************
函 数 名:WriteCommandLCD()
功 能:向1602写指令
说 明:
入口参数:WDLCD,BuysC
返 回 值:无
*****************************************************/
void WriteCommandLCD(unsigned char WCLCD,BuysC) //BuysC为0时忽略忙检测
{
if (BuysC) ReadStatusLCD(); //根据需要检测忙
LCD_Data = WCLCD;
LCD_RS = 0;
LCD_RW = 0;
LCD_E = 0;
LCD_E = 0;
LCD_E = 0;
LCD_E = 1;
}
/*****************************************************
函 数 名:ReadDataLCD()
功 能:从1602读数据
说 明:
入口参数:无
返 回 值:LCD_Data
*****************************************************/
unsigned char ReadDataLCD(void)
{
LCD_RS = 1;
LCD_RW = 1;
LCD_E = 0;
LCD_E = 0;
LCD_E = 0;
LCD_E = 1;
return(LCD_Data);
}
/*****************************************************
函 数 名:ReadStatusLCD()
功 能:读取1602状态
说 明:如果为忙,则一直等到非忙为止
入口参数:无
返 回 值:LCD_Data
*****************************************************/
unsigned char ReadStatusLCD(void)
{
LCD_Data = 0xFF;
LCD_RS = 0;
LCD_RW = 1;
LCD_E = 0;
LCD_E = 0;
LCD_E = 0;
LCD_E = 1;
delay_18B20(200);
// Delay5Ms(); //检测忙信号 proteus仿真时,延迟5MS,关闭while循环
while (LCD_Data&Busy) //硬件使用时,不延迟,打开while循环
return(LCD_Data);
}
/*****************************************************
函 数 名:LCDInit()
功 能:1602初始化
说 明:
入口参数:无
返 回 值:无
*****************************************************/
void LCDInit(void)
{
LCD_Data = 0;
WriteCommandLCD(0x38,0); //三次显示模式设置,不检测忙信号
Delay5Ms();
WriteCommandLCD(0x38,0);
Delay5Ms();
WriteCommandLCD(0x38,0);
Delay5Ms();
WriteCommandLCD(0x38,1); //显示模式设置,开始要求每次检测忙信号
WriteCommandLCD(0x08,1); //关闭显示
WriteCommandLCD(0x01,1); //显示清屏
WriteCommandLCD(0x06,1); // 显示光标移动设置
WriteCommandLCD(0x0C,1); // 显示开及光标设置
}
/*****************************************************
函 数 名:DisplayOneChar()
功 能:按指定位置在1602显示一个字符
说 明:X为列,Y为行,DData为字符
入口参数:X,Y,DData
返 回 值:无
*****************************************************/
//按指定位置显示一个字符
void DisplayOneChar(unsigned char X, unsigned char Y, unsigned char DData)
{
Y &= 0x1;
X &= 0xF; //X不能大于15,Y不能大于1
if (Y) X |= 0x40; //当要显示第二行时地址码+0x40;
X |= 0x80; //算出指令码
WriteCommandLCD(X, 0); //这里不检测忙信号,发送地址码
WriteDataLCD(DData);
}
/*****************************************************
函 数 名:DisplayListChar()
功 能:按指定位置在1602显示一串字符
说 明:X为列,Y为行,*DData为字符串
入口参数:X,Y,DData
返 回 值:无
*****************************************************/
void DisplayListChar(unsigned char X, unsigned char Y, unsigned char code *DData)
{
unsigned char ListLength;
ListLength = 0;
Y &= 0x1;
X &= 0xF; //X不能大于15,Y不能大于1
while (DData[ListLength]!='\\0') //若到达字串尾则退出
{
if (X <= 0xF) //X坐标应小于0xF
{
DisplayOneChar(X, Y, DData[ListLength]); //显示单个字符
ListLength++;
X++;
}
}
}
/*****************************************************
函 数 名:Delay5Ms()
功 能:5ms延时
说 明:
入口参数:无
返 回 值:无
*****************************************************/
void Delay5Ms(void)
{
unsigned int TempCyc = 5552;
while(TempCyc--);
}
/*****************************************************
函 数 名:Delay400Ms()
功 能:400ms延时
说 明:
入口参数:无
返 回 值:无
*****************************************************/
void Delay400Ms(void)
{
unsigned char TempCycA = 5;
unsigned int TempCycB;
while(TempCycA--)
{
TempCycB=7269;
while(TempCycB--);
}
}下载本文