飞思卡尔单片机应用实验指导书
0赞实验一 飞思卡尔嵌入式实验系统入门—GPIO控制实验
一、实验目的:
熟悉CodeWarrior集成开发环境及飞思卡尔嵌入式实验开发系统
掌握AW60的GPIO结构及控制方法
掌握CodeWarrior工程结构及创建汇编工程
熟悉汇编指令及应用汇编语言编程方法
二、实验内容
1.运用CodeWarrior新建工程,进行工程程序编辑、编译、下载、调试
2.利用飞思卡尔嵌入式实验开发系统根据实验需要进行硬件连接
3.根据连接在PTD口上的两个拨动开关的状态,控制接中PTB口上的八个LED处在以下四种不同状态:八个LED亮—灭循环;四个灯交替亮—灭;一个LED左移流水灯;两个LED右移流水灯。
实验接线原理图
![]() |
|||
![]() |
图1-1 I/O口实验接线图
三、实验步骤
1.将飞思卡尔嵌入式实验开发系统实验箱接上电源,写入器BDM接头插接入核心卡BDM座,USB头接入PC机USB口。
2.PC机上启动CodeWarrior,新建工程LED.mcp(注意设置工程保存路径)
3.观察工程文件结构,查看相应文件。
4.在main.asm中编辑工程主文件
5.编辑相关子程序
6.编译,如果有错误修改,直至编译通过
7.链接、下载,调试观察LED灯现象
四、思考题
1.CodeWarrior建立工程有什么工程框架文件,这些文件的作用?
2.嵌入式开发系统有哪些主要元器件?
3.如果采用模块化设计方法,将系统初始化、LED灯控制、延时程序等各自形成文件,工程应用这些文件时应注意什么?
4.上拉电阻和下拉电阻分内置的和外接的,内置的电阻一般只有几K或十几K,外接电阻可以是几M、十几M甚至更大。当要求功耗较低时,应该怎样选择内置的还是外接的电阻?
5.该实验的开关通过I/O口控制小灯的程序,分别叙述断点调试和单步调试的步骤,其中包含:利用汇编语言时,注意观察寄存器值的变化并记录寄存器中每次的值
实验二 串口通信(SCI)实验
一、实验目的:
1.进一步熟悉嵌入式开发系统环境、汇编、C语言、调试方式。
2.加强串口通信(SCI)基本原理及编程原理的理解。
3.理解C语言工程结构,掌握运用08C建立工程的方法。
4.分别运用查询方式、中断方式实现通信。
二、实验要求:
1.仔细阅读本实验指导书。
2.复习有关的串行通信(SCI)的章节
3.熟悉AW60串行通信(SCI)的工作方法及编程。
4.根据实验内容要求编写好程序,为实验做充分地准备。
![]() |
实验原理图如下:
三.实验设备及其连接
1.PC机 一台
2.飞思卡尔嵌入式实验开发系统 一台
3.串行通信线 一根
4.万用表 一只
四.实验内容
1.理解串行通信(SCI)原理。
2.运行与理解各子程序。
3.主程序运行课本的样例程序。
4.编制一个查询方式通信程序。
将字符通过SCI一个一个地发送,发送完一个字符后,控制接在PTB0~PTB7的8个LED灯显示被发送字符的ASCII码。
5.编制一个中断方式通信程序。
编程要求同上一条。
五.编程提示
1.按照结构要求写好编程代码和注释。
2.计算波特率,SCInInit(SCI初始化子程序)设置SCI比特率寄存器(SCInBDH,SCInBDL),设置允许SCI、正常码输出、8位数据、无校验,即设置SCI控制寄存器1(SCIxC1)相应位,设置是否允许发送与接收、是中断接收还是查询接收,即设置SCI控制寄存器2(SCIxC2)相应位。
3.查询方式通信程序的主程序主体是一个死循环,循环体中是不断检测SCI是否发送完毕,即检测SCI状态寄存器1(SCIxS1)第七位是否为1,为1则将字符的ASCII码输出到接在PTB口的LED上,并延时一会。
4.中断方式通信程序的主程序也主体是一个死循环,但该循环体是一个空循环体,所有接收和发送数据程序代码放在中断程序中,这里没有检测代码,数据开始发送和接收的条件就是中断的条件。
六.实验报告要求
1.小结AW60串行通信(SCI)的原理及编程,并画出其流程图和程序代码与硬件接线图。
2.小结中断方式和查询方式的编程方法,并画出其流程图和程序代码与硬件接线图。
3.回答下列问题
(1)串行SCI通信有哪些中断?各在什么情况下发生,作用是什么?
(2)如何知道串行口TX发送了信号?
提示:方法一,利用万用表在TX端发送0或者255持续0.5秒钟时的电压值。
方法二,在TX端发送0或255持续0.5秒钟的波形时的小灯变化。
七.参考例程:
void main(void) {
unsigned char SerialBuff[]="Hello! World!"; //初始化存放接收数据的数组
//1 关总中断
DisableInterrupts; //禁止总中断
//2 芯片初始化
MCUInit();
//3 模块初始化
SCIInit(); //串行口初始化
//4 开放中断
EnableInterrupts; //开放总中断
SCISendN(13,SerialBuff); //串口发送“Hello World!”
//5 主循环
while (1)
{
if
((SCI1S1&SCI1S1_RDRF_MASK)!=0) {
PTBD=SCI1D ;
if((SCI1S1&SCI1S1_TDRE_MASK)!=0)
SCI1D=PTBD;
}
else
PTBD=0x00;
}
}
void MCUInit(void)
{
SOPT = 0b01100000; //$70 System Options Register(write once)
ICGC2 = 0b00110000; //$30 internal clock generation 2
ICGC1 = 0b01111000; //$78 internal clock generation 1
//等待FLL稳定
while(!ICGS1_LOCK);
PTBDD=0xff ;
PTBD=0xff ;
}
void SCIInit()
{
unsigned int ubgs,baud=9600;
unsigned char sysclk=20;
//1.计算波特率并设置:ubgs = fsys/(波特率*16)(其中fsys=sysclk*1000000)
ubgs = sysclk*(10000/(baud/100))/16; //理解参考上一行,此处便于CPU运算
SCI1BDH= (unsigned char)((ubgs & 0xFF00) >> 8);
SCI1BDL= (unsigned char)(ubgs & 0x00FF);
//无校验,正常模式(开始信号+ 8位数据(先发最低位) + 停止信号)
SCI1C1= 0;
//允许发送,允许接收,查询方式收发
SCI1C2= (0
| SCI1C2_TE_MASK
| SCI1C2_RE_MASK );
}
void SCISend1(unsigned char ch)
{
while(!(SCI1S1 & SCI1S1_TDRE_MASK));//判断发送缓冲区是否为空
SCI1D = ch;
}
void SCISendN(unsigned char n, unsigned char ch[])
{
unsigned i;
for (i = 0; i < n; i++)
SCISend1(ch[i]);
}
unsigned char SCIRe1(unsigned char *p)
{
unsigned int k;
unsigned char i;
for (k = 0; k < 0x0b; k++)//有时间限制
if((SCI1S1 & SCI1S1_RDRF_MASK) != 0)//判断接收缓冲区是否满
{
i = SCI1D;
*p = 0x00;
break;
}
if (k >= 0x0b) //接收失败
{
i = 0xff;
*p = 0x01;
}
return i;
}
unsigned char SCIReN(unsigned n,unsigned char ch[])
{
unsigned char m;
unsigned char fp; //接收标志
m = 0;
while (m < n)
{
ch[m] = SCIRe1(&fp);
if (fp == 1)
{
return 1; //接收失败
}
m++;
}
return 0; //接收成功
}
实验三 键盘中断及LED数码块实验
一、实验目的:
1.熟练运用嵌入式开发系统环境、C语言及调试方式。
2.复习串行通信接口(SCI)的内容。
3.加强键盘中断基本原理及编程原理的理解。
4.理解“行扫描”法的原理并能进行键值识别和键值编码。
5.理解键盘接线原理图(如图3-1)。
6.理解LED数码块的显示原理,初步掌握LED数码块显示编程方法。
实验箱提供一个16键键盘,用于键盘中断信号的输入。系统提供两种接线方式:
①当将键盘接入上一排插孔时为固定接线,键盘接线原理图如图3-1所示。
②当将键盘接入下一排插孔时为手动接线,连线的位置在键盘的左边。
实验箱提供四个LED数码块,PB0~PB7为段码接口,PTD0、PTD1、PTD4、PTD5为位码控制接口。
![]() ![]() ![]() |
二、实验要求:
1.仔细阅读本实验指导书。
2.复习有关的键盘中断和串行通信接口(SCI)的章节。
3.熟悉AW60键盘模块的工作方法及编程。
4.根据实验内容要求编写好程序,为实验做充分地准备。
三.实验设备及其连接
1.PC机 一台
2.飞思卡尔嵌入式实验开发系统 一台
3.串行通信线 一根
四.实验内容
1.理解并运行按键扫描及键值键码发送样例程序(将按键的键值及键码从串行口发送到PC机端的串口工具软件);
2.编制一个中断方式的16键键盘程序,使用“行扫描”法识别按键;
采用键盘中断方式。PTD7,3,2及PTG4接键盘4根列线,PTG3-0接键盘4根行线。要求按下的一个键的键值和键面定义值(键的ASCII码值)通过串口在PC方软件界面显示,同时用小灯显示按键的键面定义值(键的ASCII码值),PTB7-PTB0口与小灯相连;
3.理解并运行LED数码块样例程序(在LED数码块上显示“2011”);
4.参考按键及LED数码块样例程序,设计一个按键显示程序。将按键的键码在LED数码块上显示。
7段LED显示字型码 |
||||||||||
显示字符 |
h |
g |
f |
e |
d |
c |
b |
a |
字型码 |
|
共阴极 |
共阳极 |
|||||||||
0 |
|
0 |
1 |
1 |
1 |
1 |
1 |
1 |
3F |
C0 |
1 |
|
0 |
0 |
0 |
0 |
1 |
1 |
0 |
06 |
F9 |
2 |
|
1 |
0 |
1 |
1 |
0 |
1 |
1 |
5B |
A4 |
3 |
|
1 |
0 |
0 |
1 |
1 |
1 |
1 |
4F |
B0 |
4 |
|
1 |
1 |
0 |
0 |
1 |
1 |
0 |
66 |
99 |
5 |
|
1 |
1 |
0 |
1 |
1 |
0 |
1 |
6D |
92 |
6 |
|
1 |
1 |
1 |
1 |
1 |
0 |
1 |
7D |
82 |
7 |
|
0 |
0 |
0 |
0 |
1 |
1 |
1 |
07 |
F8 |
8 |
|
1 |
1 |
1 |
1 |
1 |
1 |
1 |
7F |
80 |
9 |
|
1 |
1 |
0 |
1 |
1 |
1 |
1 |
6F |
90 |
A |
|
1 |
1 |
1 |
0 |
1 |
1 |
1 |
77 |
88 |
B |
|
1 |
1 |
1 |
1 |
1 |
0 |
0 |
7C |
83 |
C |
|
0 |
1 |
1 |
1 |
0 |
0 |
1 |
39 |
C6 |
D |
|
1 |
0 |
1 |
1 |
1 |
1 |
0 |
5E |
A1 |
E |
|
1 |
1 |
1 |
1 |
0 |
0 |
1 |
79 |
86 |
F |
|
1 |
1 |
1 |
0 |
0 |
0 |
1 |
71 |
8E |
五.编程提示
1.利用构件式方法编程,可以使程序结构清晰,可移植性好;
2.矩阵式键盘采用扫描法来确定哪一个键被按下,键盘的接口硬件确定后,每个键的键值就确定了,但每个键的键码可以根据需要定义;
3.PTD7,3,2及PTG4-PTG0与键盘中断输入引脚复用,设置键盘中断允许寄存器,当键盘有键被按下时,立即产生中断,中断程序处理按键事件,比如确定哪个键被按下,然后转换为该键的定义值。
4.键盘的键面标示码(键码)键盘扫描的键值对应关系通过列表对应起来,即键盘定义表对应表示。当通过“行扫描”法获得某个键的键值时,通过查表法就可以得到它的定义值。
六.实验报告要求
1.按实验报告格式认真完成实验报告,要求画出工程的总体流程图;
2.本实验中用的是键盘中断编程方式,也可以使用查询编程方式,请尽量少修改代码改用查询编程方式重新编写相应的子程序和主程序。提示:注意重键问题。
2.识别是否有键按下以及哪个键被按下有哪些方法?
3.有哪些方法可以用来消除键盘抖动?
4.AW60的键盘中断模块用行扫描方式最多能够实现几列的键盘?
七.参考例程:
void main(void)
{
//1 关总中断
DisableInterrupts;
//2 芯片初始化
MCUInit();
//3 模块初始化
//3.1 SCI初始化
SCI1Init(SYSTEM_CLOCK,9600); //用SCI1,系统时钟为时钟源,波特率为9600
//3.2 键盘初始化
KBInit();
//4 开中断
//4.1 开键盘中断
KBI1SC |=(1<<1);
//4.2 开总中断
EnableInterrupts;
//5 主循环
while (1)
{
}
}
键盘模块初始化
void KBInit(void)
{
PTDD &= 0b01110011; //键盘口复位
PTGD &= 0b11100000;
PTDDD &= 0b01110011; //定义列线(7-4位)为输入
PTGDD &= 0b11101111;
PTDPE |= 0b10001100; //输入引脚(列线)有内部上拉电阻
PTGPE |= 0b00010000;
PTGDD |= 0b00001111; //行线(3-0位)为输出
KBI1SC &=~(1<<1); //屏蔽键盘中断(KBIE = 0)
KBI1PE = (0
|KBI1PE_KBIPE7_MASK
|KBI1PE_KBIPE6_MASK
|KBI1PE_KBIPE5_MASK
|KBI1PE_KBIPE4_MASK); //允许输入引脚(列线)的中断可进入
KBI1SC = (0
|KBI1SC_KBACK_MASK); //清除键盘中断请求(KBACK = 1)
}
键盘一次扫描
uint8 KBScan1(void)
{
uint8 line,i,tmp,tmp1,tmp2;
line=0b11111110; //使第一根行线为0(低电平)
for (i = 1; i <= 4; i++) //最多将扫描4根行线
{
//当前扫描的一行,输出低电平
PTGD = line; //输出开始扫描
asm("NOP");
asm("NOP");
//读取键盘口数据寄存器
tmp1 = PTDD; //输入扫描结果
tmp2 = PTGD;
//整合扫描结果,即键盘输入引脚的4位
tmp = (tmp1 & 0x80);
tmp1 &= 0x0C;
tmp1 = (tmp1<<3);
tmp |= tmp1;
tmp |= (tmp2 & 0x1f);
//通过观察4根列线中是否出现低电平来判断当前行有无按键
if ((tmp & 0xF0) != 0xF0) //当前行有键按下
{
break; //退出循环不再扫描
}
else //当前行无按键,准备扫描下一行
line = (line << 1) | 0x01;
}
if (i == 5) //无按键,以后将返回0xFF
tmp = 0xFF;
return (tmp);
}
键盘进行N次扫描(去抖)
uint8 KBScanN(uint8 KB_count)
{
uint8 i, KB_value_last, KB_value_now;
//先扫描一次得到的键值,便于下面比较
if (0 == KB_count || 1 == KB_count)
return KBScan1();
KB_value_now = KB_value_last = KBScan1();
//以下多次扫描消除误差
for (i=0; i<KB_count-1; i++)
{
KB_value_now = KBScan1();
if (KB_value_now == KB_value_last)
return KB_value_now; //返回扫描的键值
else
KB_value_last = KB_value_now;
}
//返回出错标志
return 0xFF;
}
键码表:
const uint8 KBtable[] =
{
//自已添入键值键码表
0x00 //键码表结束标志
};
键码转换
uint8 KBDef(uint8 valve)
{
uint8 KeyPress; //键定义值
uint8 i;
i = 0;
KeyPress = 0xff;
while (KBtable[i] != 0x00) //在键盘定义表中搜索欲转换的键值,直至表尾
{
if(KBtable[i] == valve) //在表中找到相应的键值
{
KeyPress = KBtable[i+1]; //取出对应的键定义值
break;
}
i += 2; //指向下一个键值,继续判断
}
return KeyPress;
}
键盘中断程序(自行设计)
interrupt 22 void isrKeyBoard(void)
{
uint8 value;
uint16 i;
for(i=0; i<1000; i++);
DisableInterrupts; //关总中断
KBI1SC &=~(1<<1); //屏蔽键盘中断
value = KBScanN(10); //扫描键值,存于value中
SCI1Send1(value); //发送键值
SCI1Send1(KBDef(value)); //键值转化为定义值并发送
KBInit(); //键盘初始化键盘中断
KBI1SC |=(1<<1); //开放键盘中断
EnableInterrupts ; //开总中断
}
LED显示:
void main(void) {
LEDBUF[0]='2';
LEDBUF[1]='0';
LEDBUF[2]='1';
LEDBUF[3]='1';
DisableInterrupts;
MCUInit();
LEDinit();
EnableInterrupts;
while(1){
LEDshow(LEDBUF);
}
}
LED.C中包含三个函数及段码表和位码表:
void LEDinit(void)
{
PTBDD = 0xFF; //数据口为输出
PTDDD |= 0x33; //位选口为输出
}
void LEDshow(uint8 *Buf)
{
uint8 i,c;
//uint16 j;
for (i = 0;i <= 3;i++)
{
c = Buf[i]-'0';
LEDshow1(3-i,c);
//延时
Delay(10);
}
}
void LEDshow1(uint8 i, uint8 c)
{
PTDD = CStable[i];
PTBD = Dtable[c];
}
//显示码表(共阴极)
const uint8 Dtable[10] =
// 0 1 2 3 4 5 6 7 8 9
{0x3F,0x06,0x5B,0x4F,0x66, 0x6D,0x7D,0x07,0x7F,0x6F};
//片选表 (电平为低片选)
const uint8 CStable[4] =
// 0 1 2 3
{0xDF,0xEF,0xFD,0xFE};
公共函数中包含一个延时程序:
void Delay(uint16 count)
{ uint8 i ;
uint16 j;
for(j=0; j<count; j++)
for(i=0; i<200; i++)
;
}
实验五 定时器输入捕捉与输出比较功能实验
一、实验目的:
1.熟练运用嵌入式开发系统环境、C语言及调试方式;
2.理解定时器工作原理;
3.初步掌握定时器输入捕捉与输出比较功能及程序设计方法;
4.进一步熟悉LED数码块显示编程原理及方法。
二、实验要求:
1.仔细阅读本实验指导书。
2.复习有关的定时器章节。
3.熟悉定时器模块的原理及定时器模块输入捕捉及输出比较功能及及编程方法。
4.根据实验内容要求编写好程序,为实验做充分地准备。
三.实验设备及其连接
1.PC机 一台
2.飞思卡尔嵌入式实验开发系统 一台
3.PTD0插孔接一个LED灯,PTE2(定时器1通道0)接一个拨动开关。
4.PTE3(定时器1通道1)接口连接到一个LED灯。
四.实验内容
1.理解并运行输入捕捉样例程序(经PTD0上插孔连接一个LED灯,当接在定时器1通道0上的开关上下拨动时,捕捉通道上的上升沿与下降沿,捕捉到时让LED灯亮一会然后熄灭);
2.理解并运行输出捕捉样例程序(根据接在PTD7、PTD6上的两个拨动开关的状态,在定时器1通道1上分别在比较成功输出拉高、比较成功输出拉低、比较成功时输出翻转三种不同情况下,观察接在通道1PTE3的LED灯的不同状态。注意拨动开关推到上面为1,推到下面为0);
3.在理解上面两个样例程序的基础上,自行设计一个运用定时器1通道1的输出比较功能输出一个频率固定方波,由定时器1的通道0对其进行输入捕捉,并将捕捉到的脉冲数在LED数码块上显示。
五.编程提示
根据功能要求,将定时器模块的相关寄存器列出并确定设定值,因是要对输入的脉冲计数,所以应该设置通道只捕捉上升沿或下降沿,当捕捉到时可用中断方式对脉冲计数加1(也可用查询方式)。当计数达到9999时计数值应复位到0。
LED数码块显示部分可参考前面实验程序,即将脉冲计数值转换成BCD码,送入显示缓冲区,然后调用显示子程序即可。
六.实验报告要求
1.按实验报告格式认真完成实验报告,要求画出工程的总体流程图;
回答下列问题:
2.回答下列问题
(1)通道的输出比较输出的三种形式有什么差别,如果希望用输出比较功能形成一个脉冲输出源,应该选择什么方式,输出的脉冲有什么特点?
(2)本实验是利用输入捕捉功能对通道上输入的脉冲计数,如果要计算输入脉冲的频率要如何进行?如果输入脉冲的周期较长,要注意什么问题?如何解决?
(3)本实验对脉冲计数可用查询方式和中断方式,比较两种方式的不同,你更愿意用什么方式,为什么?
七.参考例程:
输入捕捉参考例程:
Main.C
//总头文件
#include "Includes.h"
//在此添加全局变量定义
//主函数
void main(void)
{
//1 关总中断
DisableInterrupts; //禁止总中断
//2 芯片初始化
MCUInit();
//3 模块初始化
BLinit(); //(1) 小灯控制引脚(I/O)初始化
TPM1Ch0Init(); //(2) 定时器1通道0初始化
//4 开放中断
TPM1C0SC|=TPM1C0SC_CH0IE_MASK; //(1) 允许定时器1通道0的输入捕捉中断
EnableInterrupts; //(2) 开放总中断
//5 主循环
while (1)
{
}
}
Blinit.c
#include "MC9S08AW60.h"
void BLinit(void){
PTDD=0xff;
PTDDD=0b00000001;
}
TPM.c
#include "TPM.h"
//-------------------------------------------------------------------------*
//函数名: TPM1Ch0Init *
//功 能: 初始化定时器通道 *
//参 数: *
//返 回: 无 *
//说 明: 无 *
//-------------------------------------------------------------------------*
void TPM1Ch0Init()
{
//设置定时器状态和控制寄存器
//分频因子64,设置为系统时钟,禁止溢出中断,清溢出标志
TPM1SC=0b00001110;
//设置定时器通道状态和控制寄存器
//选择输入捕捉方式,捕捉上升沿,允许通道中断
TPM1C0SC=0b01001000;
}
Isc.c
#include "Includes.h"
interrupt 5 void T1Ch0In(void)
{
uint8 temp ;
DisableInterrupts; //禁止总中断
PTDD&=~PTDD_PTDD0_MASK ; //接在PTD0上的LED亮
delay(100); //延时
PTDD|=PTDD_PTDD0_MASK ; //接在PTD0上的LED暗
//清除输入捕捉标志位
temp = TPM1C0SC; //(1)读该寄存器
TPM1C0SC&= ~TPM1C0SC_CH0F_MASK; //(2)向输入捕捉标志位写0
EnableInterrupts; //开放总中断
}
General.c
#include "General.h" //包含通用函数头文件
//-------------------------------------------------------------------------*
// 函数名: Delay() *
// 功 能: 延时 *
// 参 数: count(小于65535) *
// 返 回: 无 *
//-------------------------------------------------------------------------*
void delay(uint16 count)
{
uint16 i,j;
for(j=0; j<count; j++)
for(i=0; i<200; i++)
;
}
输出比较参考例程:
Main.c
//总头文件
#include "Includes.h"
//在此添加全局变量定义
//主函数
void main(void)
{
//1 关总中断
DisableInterrupts; //禁止总中断
//2 芯片初始化
MCUInit();
//3 模块初始化
TPM1Init(); // 定时器1中模块初始化
PD_Init();
//4 开放中断
EnableInterrupts; //(2) 开放总中断
//5 主循环
while (1)
{
uint8 i;
i=PTDD&0b11000000;
i>>=6;
TPM1outCInit(1,i);
delay(200);
}
}
TPM.c
#include "TPM.h"
//-------------------------------------------------------------------------*
//函数名: TPM1Ch0Init *
//功 能: 初始化定时器通道 *
//参 数: *
//返 回: 无 *
//说 明: 无 *
//-------------------------------------------------------------------------*
void TPM1Ch0Init()
{
//设置定时器状态和控制寄存器
//分频因子64,设置为系统时钟,禁止溢出中断,清溢出标志
TPM1SC=0b00001110;
//设置定时器通道状态和控制寄存器
//选择输入捕捉方式,捕捉上升沿,允许通道中断
TPM1C0SC=0b01001000;
}
void TPM1Init(){
TPM1SC=0b00001111;
TPM1MODH=0x7f;
TPM1MODL=0xff;
}
void TPM1outCInit(uint8 CHo,uint8 Edge)
{
//设置定时器状态和控制寄存器
//分频因子64,设置为系统时钟,禁止溢出中断,清溢出标志
uint8 TPM1Cn=0b00010000;
if (Edge>3) Edge=3;
Edge<<=2;
switch(CHo){
case 0: TPM1C0VH=0x3f;TPM1C0VL=0xff;TPM1C0SC=TPM1Cn|Edge;break;
case 1: TPM1C1VH=0x3f;TPM1C1VL=0xff;TPM1C1SC=TPM1Cn|Edge;break;
case 2: TPM1C2VH=0x3f;TPM1C2VL=0xff;TPM1C2SC=TPM1Cn|Edge;break;
case 3: TPM1C3VH=0x3f;TPM1C3VL=0xff;TPM1C3SC=TPM1Cn|Edge;break;
case 4: TPM1C4VH=0x3f;TPM1C4VL=0xff;TPM1C4SC=TPM1Cn|Edge;break;
case 5: TPM1C5VH=0x3f;TPM1C5VL=0xff;TPM1C5SC=TPM1Cn|Edge;break;
}
}
PD_Init.c
//PTD7,PTD6设置为输入,有上拉电阻
#include "MC9S08AW60.h"
void PD_Init(){
PTDD=0b11111111;
PTDPE=0b11000000;
PTDDD=0b00111111;
}
General.C
#include "General.h" //包含通用函数头文件
//-------------------------------------------------------------------------*
// 函数名: Delay()
// 功 能: 延时
// 参 数: count(小于65535)
// 返 回: 无
//-------------------------------------------------------------------------*
void delay(uint16 count)
{
uint16 i,j;
for(j=0; j<count; j++)
for(i=0; i<200; i++)
;
}
实验六 AD转换与PWM综合实验
一、实验目的:
1.熟练运用嵌入式开发系统环境、C语言及调试方式;
2.理解定时器模块及AD模块工作原理;
3.初步掌握定时器PWM输出功能及程序设计方法;
4.初步掌握AD模块进行单通道AD转换功能及程序设计方法;
5.进一步熟悉LED数码块显示编程原理及方法。
二、实验要求:
1.仔细阅读本实验指导书。
2.复习有关的定时器及AD转换章节。
3.熟悉定时器模块的原理及PWM功能及编程方法。
4.熟悉AD模块原理、结构、AD转换功能和编程方法。
4.根据实验内容要求编写好程序,为实验做充分地准备。
三.实验设备及其连接
1.PC机 一台
2.飞思卡尔嵌入式实验开发系统 一台
3.PE2(定时器1通道)接口连接到一个LED灯,观察PWM输出占空比变化的PWM时,LED灯的变化。
4.通过实验箱上电位器输出插孔将电位器电压输入到AD转换模块的15号通道PTD7,对电位器输出电压进行AD转换,并将转换后得到的数值再还原为电压值在LED数码块上显示。
四.实验内容
1.理解并运行PWM例程序(经PTD0上插孔连接一个LED灯,当接在定时器1通道0上的开关上下拨动时,捕捉通道上的上升沿与下降沿,捕捉到时让LED灯亮一会然后熄灭);
2.理解并运行AD转换样例程序(将用测试线连接电位器输出插也及PTD7,旋转电位器调节其输出电压,在LED数码块上显示对应的电压值。);
3.在理解上面两个样例程序的基础上,自行设计用电位器控制PWM占空比,PWM输出接在一个LED灯上,并在LED数据块电位器输出电压值,观察LED灯的亮度与电压值的关系。
五.编程提示
1.在改变PWM的周期和占空比之前要禁止使用TPM(TPMxSC的CLKSB、CLKSA为00),然后设置占空比、周期等,再重新写TPMxSC,否则改变不起作用。
2.实验箱上的LED数码块的接口连接是固定的,要注意AD转换通道与LED数码块接口不要冲突。
3.当GPIO作为LED输出段码驱动接口时,共阴级数码块可考虑在输出接口上加上位电阻增强驱动能力。
六.实验报告要求
1.按实验报告格式认真完成实验报告,要求画出工程的总体流程图;
回答下列问题:
2.回答下列问题
(1)定时器通道的PWM输出与输出比较有什么共同及不同处?如果用于小直流电机的速度控制,应该用什么方式?
(2)在本实验中,如果AD转换中需要有数值比较功能,当通道送入电压超过4.5V时需要报警,即让一个LED灯闪烁,应该如何设计?
七.参考例程:
PWM输出程序:
Main.c
//总头文件
#include "includes.h"
void main(void) {
DisableInterrupts;
MCUInit(); //MCU初始化
TPM1PWMInit(0); //定时器1初始化
EnableInterrupts;
while(1){
uint8 i;
for(i=0;i<=200;i++){
if (i<=100) TPM1PWMRun(0,i,0x0998);
else TPM1PWMRun(0,200-i,0x0998);
delay(100);
}
}
}
TMP.c
void TPM1PWMInit(uint8 CHn)
{
//分频因子64,设置为系统时钟,禁止溢出中断,清溢出标志
TPM1SC=0b00000110;
TPM1CNTH=0x00;
TPM1CNTL=0x00;
TPM1MODH=0x10; //预置寄存器设定值= 0x1012
TPM1MODL=0x12;
if (CHn>5) CHn=5;
switch(CHn){
case 0:TPM1C0SC=0b00110100;break;
case 1:TPM1C1SC=0b00110100;break;
case 2:TPM1C2SC=0b00110100;break;
case 3:TPM1C3SC=0b00110100;break;
case 4:TPM1C4SC=0b00110100;break;
case 5:TPM1C5SC=0b00110100;break;
}
}
void TPM1PWMRun(uint8 CHn,uint8 percent,uint16 ModP){
uint16 duty,MP;
uint8 dutyH,dutyL;
MP=ModP ;
TPM1SC=0x00;
TPM1CNTH=0x00;
TPM1CNTL=0x00;
TPM1MODL=(uint8)(ModP);
TPM1MODH=(uint8)(ModP>>8);
duty=(uint16)((MP/100)*percent);
dutyL=(uint8)(duty);
dutyH=(uint8)(duty>>8);
if (CHn>5) CHn=5;
switch(CHn){
case 0:
TPM1C0VH=dutyH;TPM1C0VL=dutyL;TPM1C0SC=0b00111100;break;
case 1:
TPM1C1VH=dutyH;TPM1C1VL=dutyL;TPM1C1SC=0b00111100;break;
case 2:
TPM1C2VH=dutyH;TPM1C2VL=dutyL;TPM1C2SC=0b00111100;break;
case 3:
TPM1C3VH=dutyH;TPM1C3VL=dutyL;TPM1C3SC=0b00111100;break;
case 4:
TPM1C4VH=dutyH;TPM1C4VL=dutyL;TPM1C4SC=0b00111100;break;
case 5:
TPM1C5VH=dutyH;TPM1C5VL=dutyL;TPM1C5SC=0b00111100;break;
}
TPM1SC=0b00001110;
}
Generalfun.c
void delay(uint16 count)
{
uint8 i;
uint16 j;
for(j=0; j<count; j++)
for(i=0; i<200; i++)
;
}
AD转换例程:
Main.c
//总头文件
#include "includes.h"
void main(void) {
uint8 V1Value;
DisableInterrupts;
LEDBUF[0]=0;
LEDBUF[1]=0;
LEDBUF[2]=0;
LEDBUF[3]=0;
MCUInit(); //MCU初始化
ADCInit(); //AD模块初始化
LEDinit(); //LED数码块接口初始化
EnableInterrupts;
while(1){
V1Value=ADCAve(15,50); //取得中值滤波法得到的AD转换数值
ADCV_Vol(V1Value,LEDBUF); //将AD转换数值转化为对应的电压值的
//BCD码存入LED显示缓冲区LEDBUF
LEDshow(LEDBUF) ; //LED数码块显示
}
}
ADC.c
//头文件
#include "ADC.h"
void ADCInit(void)
{
ADC1CFG = 0b11010001;//低功耗,4分频,长采样,8位模式,总线时钟2分频
ADC1SC1=0b00000000;
ADC1SC2=0b00000000;//软件触发,禁止比较功能
}
uint8 ADCValue(uint8 channel)
{
uint8 SC1_3;
uint8 resultH;
uint8 result;
//1 选取通道号AD0-AD27 = 00000-11011
channel &= 0b00011111; //取通道号变量的低五位(实际通道号)
SC1_3 =ADC1SC1
&(ADC1SC1_COCO_MASK
|ADC1SC1_AIEN_MASK
|ADC1SC1_AIEN_MASK);
// 取ADC1SC1的高三位(取上电复位默认值000)
// 单次转换, 禁止中断, 转换未完成
ADC1SC1 = SC1_3 | channel; //合并上述8位
//2 取A/D转换结果
while ((ADC1SC1 & ADC1SC1_COCO_MASK) == 0); //未完成则执行空操作
resultH = ADC1RH;
result =ADC1RL;
return result;
}
uint8 ADCMid(uint8 channel) //中值滤波
{
uint8 i,j,k,tmp;
//1 取三次A/D转换结果
i = ADCValue(channel);
j = ADCValue(channel);
k = ADCValue(channel);
//2 从三次A/D转换结果中取中值
tmp = (i > j) ? j : i;
tmp = (tmp > k) ? tmp : k;
return tmp;
}
uint8 ADCAve(uint8 channel, uint8 n) //均值滤波
{
uint16 i;
uint32 j;
j = 0;
for (i = 0; i < n; i++)
j += ADCMid(channel);
j = j/n;
return (uint8)j;
}
//将AD转换得到的数值量还原为实际的电压值,将电压值的BCD码存储显示缓冲区
void ADCV_Vol(uint8 ADC_V,uint8 *p){
uint8 V,V1;
V=(uint8)(ADC_V/51);
V1=ADC_V%51;
*p=V;
V=(uint8)(V1*10)/51;
V1=(V1*10)%51;
*(p+1)=V;
V=(uint8)(V1*10)/51;
V1=(V1*10)%51;
*(p+2)=V;
V=(uint8)(V1*10)/51;
*(p+3)=V;
}
LED.c
#include "led.h"
#include "Generalfun.h"
void LEDinit(void)
{
PTBDD = 0xFF; //数据口为输出
PTBPE=0xff; //为B口输出配置上拉电阻
PTBDS=0xff;
PTDDD |= 0x33; //位选口为输出
}
void LEDshow(uint8 *Buf)
{
uint8 i,c;
//uint16 j;
for (i = 0;i <= 3;i++)
{ c = Buf[i];
LEDshow1(3-i,c);
//延时
delay(10);
}
}
const uint8 Dtable[10] =
// 0 1 2 3 4 5 6 7 8 9
{0x3F,0x06,0x5B,0x4F,0x66, 0x6D,0x7D,0x07,0x7F,0x6F};
//片选表 (电平为低片选)
const uint8 CStable[4] =
// 0 1 2 3
{0xDF,0xEF,0xFD,0xFE};
void LEDshow1(uint8 i, uint8 c)
{
PTDD = CStable[i];
PTBD = Dtable[c];
}