680 likes | 869 Views
现代电子技术实验 —— 综合实验之单片机部分. 习友宝. 目录. 1. 本课程简介及要求. 2. 实验开发板介绍. 3. C51 程序设计简介. 4. 开发环境与实验例程. 1. 本课程简介及要求. 1 )课程性质 必修课程:纳入本科培养方案。 综合:先修课程的综合;体现在模拟与数字、硬件与软件技术等的 综合 。 实验:实践动手完成相应的 设计 。 2 )课程目的 实践能力与工程素质的培养。 3 )教学方式 学生为主,教师为辅:教师讲解后,学生 自主完成 ,教师值班指导。
E N D
目录 1. 本课程简介及要求 2. 实验开发板介绍 3. C51程序设计简介 4. 开发环境与实验例程
1. 本课程简介及要求 1)课程性质 必修课程:纳入本科培养方案。 综合:先修课程的综合;体现在模拟与数字、硬件与软件技术等的综合。 实验:实践动手完成相应的设计。 2)课程目的 实践能力与工程素质的培养。 3)教学方式 学生为主,教师为辅:教师讲解后,学生自主完成,教师值班指导。 时间:2周(第1周讲课3次,第2周五考试,其余时间开放,每天上午9:00~晚上9:00)。
4)考核方式 平时设计(含设计报告)50分;考试(现场完成,1小时)50分。 考虑完成时间因素。 5)课程内容 具有实际工程应用背景的小课题。 模拟实际电子设备或系统的设计。 目前,主要基于FPGA或单片机两种开发工具。这里主要介绍基于单片机的综合应用。 单片机选型:增强型51单片机。
5)课程内容(续) 硬件:提供统一的单片机开发实验板,但需理解其设计原理、常用芯片、接口设计等。 软件:完成基于开发板的程序设计。 编程语言:C51(面向51单片机的C语言)。
6)平时设计题目 基本要求: (1)程序运行后,在8位数码管的高2位显示自己的学号;低4位显示“秒表计时器”(定时中断实现),显示从“00.00”开始,即00秒00(1/100秒,即10ms)。要求:用定时中断实现(不用Delay()函数)。 (2)当“秒表计时器”显示到“10.00”(即10秒)时,启动TLC549 A/D转换器进行电压测量(输入电压来自电位器,调节范围0~2.49V,单位:V),并将电压值显示在8位数码管的低3位。高2位仍显示学号。 (3)根据显示的电压值来调节一个发光二极管指示灯的亮度(如LED1),即电压为零时,完全不亮,电压为最大2.49V时,全亮。 要求能够线性调节,即A/D转换器TLC549为8bit,理论上可对应256级亮度。提示:用PWM控制,PWM波频率适当即可,建议取2.56ms或25.6ms的周期,亮度等级为25级。
(4)调节电位器,若电压超过2.00V,则声光报警,即用另一个发光二极管指示灯(如LED2)闪烁(亮0.5s、灭0.5s);蜂鸣器响(用500Hz方波驱动);若输入电压低于2.00V后,则撤销声光报警。(4)调节电位器,若电压超过2.00V,则声光报警,即用另一个发光二极管指示灯(如LED2)闪烁(亮0.5s、灭0.5s);蜂鸣器响(用500Hz方波驱动);若输入电压低于2.00V后,则撤销声光报警。 (5)设置一个按键,当按下该按键时,蜂鸣器响0.1秒(按键提示音),同时启动LM75A数字温度传感器完成温度的测量,并将温度值(2位整数1位小数)显示在低3位数码管上。高2位仍显示学号。 (6)再次按下该键时,蜂鸣器响0.1秒,同时再回到电压测量状态。 扩展要求: (7)对电压测量值进行简单的数据处理,如去除尖峰干扰的平均滤波:每12个测量值数据为一组,去掉最大值和最小值后的10个测量值进行算术平均后,作为显示值。为不影响测量结果的实时性,还可进行滚动滤波。 (8)其他:LCD、扩展接口。 设计报告要求: 按科技论文规范。
7)参考资料 (1)51单片机参考书(主要了解单片机结构和内部资源) (2)C语言、C51程序设计参考书 (3)本课程的电子版资料,包括实验指导书、实验例程、主要元器件数据手册(datasheet)、软件资料等 (4)网络资源
2. 实验开发板介绍 (1)开发板上单片机的选型 P89LV51RD2:80C51 内核的ISP低功耗增强型单片机 含64kB flash和1024字节的数据RAM;另8KB 引导 flash(用来支持 ISP 和 IAP 功能) 可选择X2方式:每个机器周期6个时钟 3V工作电压,操作频率为0~33MHz,兼容TTL和CMOS逻辑电平 SPI(串行外围接口)和增强型UART PCA(可编程计数器阵列),具有PWM和捕获/比较 4个8位I/O口,3个高电流P1口(16mA) 3个16位定时器/计数器 可编程看门狗定时器(WDT) 8个中断源,4个中断优先级 2个DPTR寄存器 低功耗模式、掉电模式,外部中断唤醒、空闲模式 详见其datasheet。
P89LV51RD2 TQFP44封装 引脚图
外接+5V供电,板上主电源为+3.3V。 开发板在PC机 Windows 环境软件开发工具 Keil uVision的支持下,不需要安装特别的驱动程序,通过RS232串口就能够方便地进行硬件在线仿真、调试,支持单步、全速、断点、跳出、变量观察、外围设备访问等仿真功能。 设置跳线选择,所有4个端口P1、P3、P0、P2可作为普通I/O口;或P3口第二功能、 P0、P2口作为数据/地址。 晶振频率:为常用的11.0592MHz。 复位方式:RC上电复位、按键复位 外部32KB静态RAM(U9,IS62LV256),地址为0x0000~0x7FFF,其中0x0000~0x03ff(1kB)可设置为片内或片外。 8个LED灯、8位拨动开关、8个按键、8个共阳极数码管、蜂鸣器
扩展并行输出接口(74HC573)、输入(74HC245) 实时时钟(PCF8563) 64kb(8kB)EEPROM存储器(AT24LC64 ) 1-Wire总线的数字温度计(DS18B20 ) I2C接口的数字温度计(LM75A ) 8位串行接口的逐次比较式A/D转换器(TLC549 ) 提供128x64 LCD模块接口,可并行连接或串行连接。 扩展功能连接(8位数据线、高8位地址线、3.3V电源线)。 可完成的主要实验内容有: 基本:LED灯、拨动开关、键盘、蜂鸣器、LED动态扫描显示、液晶屏、I2C总线、EEPROM数据存储、数字温度计、电压测量、频率测量、电子钟,等等。 扩展:LCD显示;其他测量与控制,如直流电机、步进电机应用
(4)开发板原理图介绍 (4)开发板原理图-1
地址分配 外部扩展RAM: CS_=A15:0x0000~0x7fff I/O地址:0x8000~0xbfff, 0xc000~0xffff(可用于扩展) 地址译码电路:
P1口线 的分配
3. C51程序设计简介 1)C51与标准C语言的差异 兼容标准C:语法、结构、程序设计方法等相同 具有特殊性:面向51单片机,与单片机的硬件资源相关 库函数:特定的库函数 数据类型:增加了面向单片机的特殊数据类型,如位操作的位类型、寄存器类型等 变量存储模式:与51单片机的存储器密切相关 函数:增加了用于处理单片机中断的中断函数 专用编译器:如常用的Keil,代码紧凑、使用方便。 集成开发环境:编辑、编译、调试。如常用的Keil uVision。
2)C51的数据类型 与标准C一样,有基本数据类型、组合数据类型;但char型与short型相同,float型与double型相同。 C51的特定数据类型(如特殊功能寄存器型、位类型) (1)字符型(“字节型”):char signed char(默认):带符号字节数据,补码表示的数值范围:(1000 0000) -128~+127 (0111 1111)。 unsigned char:无符号字节数据或字符(ASCII码),可表示0~255。 因此,我们可称其为“字节型”。
(2)整形:int singed int(默认):存放一个双字节数据, 补码表示的数值范围为:-32768~+32767; unsigned int:存放一个双字节无符号数,数值范围为0~65535。 (3) 长整型:long singed long(默认):存放一个4字节数据; unsigned long:存放一个4字节无符号数据。 注:由于所定义的不同类型的变量,需要占用单片机内存,所以尽量选用满足变量范围的类型。
(4) 浮点型:float float:4个字节表示,格式符合IEEE-754标准的单精度浮点型数据; C51中,double双精度浮点型和long double与float相同; 包含指数和尾数两部分,最高位为符号位,“1”表示负数,“0”表示正数,其次的8位为阶码,最后的23位为尾数的有效数位,由于尾数的整数部分隐含为“1”,所以尾数的精度为24位。 浮点数均为带符号数。
注:单片机的数据处理尽量采用整数,而不用小数(浮点型);对小数的处理,将其小数点移位变成整数即可。注:单片机的数据处理尽量采用整数,而不用小数(浮点型);对小数的处理,将其小数点移位变成整数即可。 如:8位A/D转换器测电压: unsigned int Vx; unsigned char V100,V10,V1; NADC = ReadADC(); Vx=[(NADC)/256]*2.50V,其中Vref=2.50V Vx=(NADC*250)/256; V100 = Vx/100; //百位数 V10 = (Vx-100*V100)/10; //十位数 V1 = (Vx-100*V100)%10; //个位数 小数点隐含在百位数。
(5)指针型:* *:指针型变量中存放的是指向另一个数据的地址。 如:int a, b,*p; //指针变量的声明,p是指向整形变量的指针变量 p = &a; //指针变量赋初值 b = *p; //相当于 b = a; 实际应用中,常将单片机外部存储器或I/O端口的访问用指针操作。
(6)特殊功能寄存器型:sfr或sfr16 C51扩展的数据类型。 sfr:字节型特殊功能寄存器类型,可访问51单片机内部所有特殊功能寄存器; sfr16:双字节型特殊功能寄存器类型,可访问51单片机内部所有双字节的特殊功能寄存器。 在reg51.h(或reg52.h)头文件中,已有标准51单片机寄存器的声明,因此,C51程序的开头都是:#include <reg51.h>,这样,在程序中就可直接使用单片机的特殊功能寄存器了。 对于特定型号单片机的特殊功能寄存器,就需要用sfr或sfr16重新定义。
(7)位类型:bit型 C51扩展的数据类型。可用于访问51单片机中的可寻址的位单元,占一个二进制位,其值为’0’或’1’。 bit型:bit定义的位变量,其位地址是可以变化的; sbit型:sbit定义的位变量与51单片机的一个可寻址位单元或可位寻址的字节单元(如可位寻址的特殊功能寄存器)中的某一位相联系,其对应的位地址是不可变化的。 常用于设定程序中的一些“标志位”。如: bdata unsigned char flags; //定义可位寻址的字节 sbit int10ms_flag = flags^0; //定义标志位 或者:bitbdata int10ms_flag; 又如:sbit LED = P1^0; //定义LED由P1.0控制
(8)变量类型的转换 数据运算中出现数据类型不一致时,就需要转换; 隐式转换,按如下优先级: bitcharintlongfloat signedunsigned 强制类型转换:“()” 如:unsigned char a; unsigned int b=256; a = (unsigned char)b; //此时,a=0
(9)组合数据类型 包括数组类型、结构类型、联合类型、枚举类型等。 数组: 一维数据,如: unsigned char disp_buf[8]; 字符数组,如:unsigned char string[10]; 数组常与指针一起用; 二维数组。
联合: 也称“共同体”,将不同数据类型组织成一个整体——联合变量;这些不同类型变量存储在单片机同一存储区。典型应用如: unsigned char ch_word_h,ch_word_l; union //union后的联合体名可省略 { unsigned char byte[2]; unsigned int word; }ch_word; //定义联合变量ch_word,以存储一个汉字 ch_word.word = 0x8090; //一个汉字编码(双字节,高位’1’) ch_word_h = ch_word.byte[0]; //取编码的高字节,应为0x80 ch_word_l = ch_word.byte[1]; //取编码的低字节,应为0x90 可以看到,51单片机是以低字节在前、高字节在后存储的
结构体: 将一类相关变量“归类”在一起。如: struct date { unsigned int year; unsigned char month , day; }d1; //定义结构体变量d1 注意与联合体的区别:结构体内变量分别独立存储(独立存在);而联合体是共用同一存储区,对其中一个变量赋值,另外的变量也就有了相应的值。
枚举: 对于具有固定取值范围的变量,将其值一一列出,并用相应符号表示,这些符号具有从0开始的序列号值(整数,依次加1)。如: enum weekday //定义枚举类型,枚举名为weekday { Sun, //枚举元素,其后用’,’ Mon, Tue, Wed, Thu, Fri, Sat //最后一个元素不用’,’ } d1; //定义枚举变量d1 d1 = Sun; //d1的值实际为0
(10)变量引用 当一个项目(project,即一个完整程序)中有多个源程序文件时,如果在一个文件中定义的变量需要在另一个文件中使用(“引用”),需在该变量定义前加“extern”。 如:在file1.c中,定义: unsigned char key_value; 若在file2.c中需要使用key_value,则: extern unsigned char key_value;
(11)数据类型的别名 用typedef为已有数据类型取别名,可增加程序的可读性,且简洁明了。如首先定义: typedef unsigned int uchar; typedef unsigned char uint; 其后就可用uint和uchar 定义变量了,如: uchar a1=0x12; uint a2=0x1234;
(12)变量名的命名 规则:由字母、数字和下划线三种字符组成,且第一个字母必须为字母或下划线。 为提高程序的可读性,一般由多个英文单词(或缩写)连接而成。 注意规范: 多个英文单词的首字母大写,如:KeyValue 多个英文单词之间用下划线连接,如:key_value
3)C51的常数 整形常数:如100,-1;100L则为长整形——用4字节存储; 十六进制表示:如0x64; 浮点数常数(实型常数):十进制表示,如1.23;指数表示,如1.234e2。 字符常数:对于可显示字符,加单引号表示,如’a’, ‘1’;对于不可显示字符,用转义字符(反斜杠“\”)表示,如’\n’表示换行,也可用其ASCII码值表示:’\x0a’。 字符串常数:加双引号,如“abc123”,注:字符串后有一个结束符’\x00’。
定义符号常数:const 如,在程序开头,定义: const float PI = 3.14159; 则程序中可用PI代替该常数。
4)变量的存储 程序中的变量,其在程序运行过程中,存在时间、作用范围以及对于单片机实际物理存储区域有所不同。 全局变量与局部变量: 全局变量:在源程序开头定义的变量,其作用范围为本文件中,其他文件需要引用的话,需加extern。 局部变量:函数中定义的变量,其作用范围为本函数中;函数执行时才分配(占用)内存空间,结束时释放。 尽量少定义全局变量:占内存;可靠性 有些固定的数据表(不需要改变其值),可加code,存储于程序存储器。如LED七段码表: code unsigned char disp_seg[] = {0x3F,0x06,0x5B,0x4F,0x66,0x6D,0x7D,0x07,0x7F,0x6F,0x77,0x7c,0x39,0x5f,0x79,0x71};
从作用范围来说,有自动(auto)、外部(extern)、静态(static)和寄存器(register)等4种。从作用范围来说,有自动(auto)、外部(extern)、静态(static)和寄存器(register)等4种。 自动变量,auto: 函数中的变量,可省略auto关键字,缺省为auto。 外部变量,extern: 文件外;函数外;占用固定内存空间。 静态变量,static: 函数体内定义:内部静态变量。典型应用,如在定时中断服务函数中,用一个静态变量来计定时中断的次数: static unsigned char int10ms_num=0; 函数外部定义:外部静态变量,本文件中有效。 寄存器变量,register: 对应单片机寄存器,速度快。如,可用于for(;;)循环变量; 可不专门声明,编译器可自动识别程序中频繁使用的变量。
变量的存储器类型 定义变量在单片机中的物理存储区域,缺省时编译器按具体编译模式默认指定存储区。如下表:
3种编译模式默认的存储器类型 small模式(小模式):编译时函数参数和变量被默认在片内RAM中,存储器类型为data。 compact模式(紧凑模式)。编译时函数参数和变量被默认在片外RAM的低256字节空间,存储器类型为pdata。 large模式(大模式)。编译时函数参数和变量被默认在片外RAM的64K字节空间,存储器类型为xdata。 可在程序开头用预处理指令说明编译模式: #pragma small //编译模式:small 或:开发环境中设置。 一般均采用small模式。
5)绝对地址访问 对code、data、pdata和xdata空间进行绝对寻址(规定只能以无符号数方式访问)。 在absacc.h (absolute address access)头文件中,有如下宏定义: #define CBYTE ((unsigned char volatile code *) 0) #define DBYTE ((unsigned char volatile data *) 0) #define PBYTE ((unsigned char volatile pdata *) 0) #define XBYTE ((unsigned char volatile xdata *) 0) #define CWORD ((unsigned int volatile code *) 0) #define DWORD ((unsigned int volatile data *) 0) #define PWORD ((unsigned int volatile pdata *) 0) #define XWORD ((unsigned int volatile xdata *) 0)
以XBYTE为例,说明如下: #define XBYTE ((unsigned char volatile xdata *) 0) 0为常数,(unsigned char xdata *)为强制类型转换,将0转换为指向外部存储器的字节型指针; 关键字volatile修饰的变量,可防止编译器优化; 因此,XBYTE应该是一个指针,指向64kB外部存储器(或I/O)的0x0000地址; 对0x1234的访问(读/写、输入/输出)可用如下形式: unsigned char a=10,b; XBYTE[0x1234] = a; //读0x1234存储单元;或输入 b = XBYTE[0x1234] ; //写0x1234存储单元;或输出 用XWORD每次可访问一个字(相邻地址的2个字节)。
用指针实现绝对地址访问 #define pXBYTE (unsigned char xdata *) unsigned char xdata *pbyte;//定义指向xdata的指针 pbyte = pXBYTE(0x1234); //指针赋值为访问地址; //pbyte=0x1234也可,但加上强制类型转换更规范 *pbyte = 10; //写操作 用_at_访问 unsigned char xdata temp _at_ 0x1000; //在xdata //的0x1000单元定义字节变量temp(全局变量) temp = 10; //写操作
6)C51的运算符与表达式 (1)赋值运算符:= (2)算术运算符:+、-、*、/、% (3)关系运算符:>、<、>=、<=、==、!= (4)逻辑运算符:||、&&、! (5)位运算符:&、|、~、^、<<、>> (6)复合赋值运算符:在赋值运算符“=”的前面加上其它运算符: +=、-=、*=、/=、%=, 如a+=2; 相当于a=a+2; &=、|=、^=、~=, >>=、<<=, 如a>>=2; 相当于a=a>>2;
(7)自加:++,自减:-- 如:a++; //相当于a=a+1; (8)逗号运算符:, 将多个(两个以上)表达式连接起来; 按从左至右的顺序依次计算出各表达式的值,而整个逗号表达式的值是最右边的表达式的值。 如: x=(a=1,b=2); //x的值为2 (9)条件运算符:?: 如:c=(a>b)?a:b; //当满足条件a>b时,c=a;否则c=b