上海电力学院
16位单片机大作业
实验报告
实验名称: 洗衣机控制器
专 业: 通信工程 姓 名: 班 级: 学 号:
一、设计目的
运用C语言,深入了解MC9S12XS128单片机的知识,根据题目的要求进行软硬件的设计和调试,从而加深对于本课程知识点的理解,掌握了如何使用CodeWarrior来整合各种驱动模块,再通过算法实现硬件上的运行。
二、功能描述
最终运行程序实现的的功能为:
通过对于CodeWarrior 软件的操作,编写代码,最终实现freescale MC9S12XS128单片机简单控制洗衣机洗衣功能。
按自己的要求将实验箱连线,通电后,LCD显示屏上面上排显示可以设定的时间,下排显示倒计时的时间,通过键盘输入设定的时间,开始计时,LED显示灯(绿灯亮起)。设置好固定的洗衣时间后,开始执行“洗衣”,倒计时完成后,即“洗衣”完成,在LCD显示屏上面显示“洗衣”的时间到,即时间到00:00,LED所有灯亮。
三.实验设备
1.PC机
一台
2.S12嵌入式开发系统 3.连接导线 4.小键盘
一台
若干
一个
四.模块
(一)定时模块
Vcc
定时器实现溢出中断接线图
PTA口(KEY1-8)
(二)LED指示灯模块
0x55:绿灯亮 0xFF:全部灯亮 (三)LCD显示模块
表7-1 HD44780引脚信号
图7-1 MCU与LCD的连接
(四)键盘输入模块 键盘接线原理图:
实验箱提供一个16键键盘,用于键盘中断信号的输入。键盘插孔如下图所示。
本实验中:
(1) 键盘采用手动接线,将键盘接入双排插孔下一排插孔。 (2) PTP0~3分别接键盘接线处的1~4; (3) PTA0~3分别接5-8; (4) PTB口接8个小灯。
2
行线m1
m2 m3 m4
PTB0 PTB1 PTB2 PTB3 PTB4 PTB5 PTB6 PTB7
PTB(LED1-LD0-7)
五.设计方案
实验中需要添加模块都是平时上课的时候做过的实验模块,所以要实现预
期的功能,只需要在以前的程序的基础上稍加修改,把各个模块在主程序中联系起来,加以调用,就可以实现所要求的功能。
主要驱动模块有:
计数定时模块:Timer.c LCD显示模块:LCD.c LED指示模块:Light.c 键盘输入模块:KBI_I.c
六.程序设计代码
[main.c]
//包含头文件
#include "Includes.h" //包含总头文件 #include
//在此添加全局变量定义 uint8 receive_data[32];
uint8 valve,mark,i, period,duty;
extern uint8 time[3],T[6]; //主函数 void main()
{
//0.1 主程序使用的变量定
uint32 mRuncount=0; //运行计数器 uint8 c;
//0.2 关总中断 DisableInterrupt();
//0.3 芯片初始化 MCUInit(FBUS_32M);
//0.4 模块初始化
Light_Init(Light_Run_PORT,Light_Run,Light_OFF); //RUN指示灯初始化为暗 //PWMInit(0); //初始化PWM通道1
TimerInit(); //(1)定时器1初始化 LCDInit(); //液晶显示初始化 SCIInit(0,FBUS_32M,9600); //串口0初始化 KBInit(); //键盘初始化
//0.5 开放中断
EnableSCIReInt0; //开放SCI0接收中断
EnableKBint(); //开放键盘中断 EnableInterrupt(); //开放总中断 DDRB=0xff; PORTB=0x55;
memcpy(receive_data,"Start:00:00******Stop: : *****",32); LCDShow((uint8 *)receive_data); // 主循环
for(;;) {
if((receive_data[22]=='0')&&(receive_data[23]=='0')&&(receive_data[25]=='0')&&(receive_data[26]=='0'))
{
DisableTimer; PORTB=0xFF; } } }
[isr.c]
//头文件包含,及宏定义区
//头文件包含
#include "Includes.h" //包含总头文件 uint8 j=0,TimInterCount=0; uint8 T[6]=0; uint8 time[3]=0;
extern uint8 valve;
extern uint8 receive_data[32];
#pragma CODE_SEG __NEAR_SEG NON_BANKED
//中断服务程序区
//未定义的中断处理函数,本函数不能删除,默认 __interrupt void isr_default(void) {
DisableInterrupt(); EnableInterrupt(); }
//定时器溢出中断处理程序
__interrupt void isrTimOver(void)
{
DisableInterrupt(); //关总中断
TimInterCount++; //中断次数加一,中断4次为一秒 if (TimInterCount >= 4) {
receive_data[22]=time[1]/10+'0'; receive_data[23]=time[1]%10+'0'; receive_data[25]=time[2]/10+'0'; receive_data[26]=time[2]%10+'0'; SecAdd1(time); //定时器更新 LCDShow((uint8 *)receive_data); TimInterCount=0;
}
TFLG2 = 0x80; //清除定时器溢出标志位
EnableInterrupt(); //开总中断 }
//键盘中断
__interrupt void isrKeyBoard(void) {
uint16 i;
DisableInterrupt(); DisableKBint();
for (i=0 ; i
valve = KBScanN(10);//扫描键值,存于value中 if(j
if(j==0)
{
receive_data[6]= KBDef(valve);
T[0]= KBDef(valve)-'0'; }
if(j==1) {
receive_data[7]= KBDef(valve);
T[1]= KBDef(valve)-'0'; }
if(j==2) {
receive_data[9]= KBDef(valve);
T[2]= KBDef(valve)-'0'; }
if(j==3) {
receive_data[10]= KBDef(valve); T[3]= KBDef(valve)-'0'; } j++; }
time[1]=T[0]*10+T[1]; time[2]=T[2]*10+T[3]; if(valve==0xD7) {
EnableTimer; PORTA=0xAA; }
LCDShow((uint8 *)receive_data);
KBInit(); //键盘初始化 EnableKBint(); //开放键盘中断 EnableInterrupt(); //开放总中断 }
[Timer.c]
//头文件包含,及宏定义区
//头文件包含
#include "Timer.h" //定时器构件头文件
void TimerInit(void) {
//允许主定时器开始计数
TSCR1 = 0x80;
//禁止定时器溢出中断,中断一次时间计算:t=n/(fbus/p)≈1/4秒, //其中n=65535,fbus=32MHZ TSCR2 = 0b00000111; // |||_
// ||__\分频因子为2^7=128 // |___/ }
void SecAdd1(uint8 *p)
{
*(p+2) -= 1; //秒减1 if (*(p+2)
*(p+2) = 60; //清秒 *(p+1) -= 1; //分减1 if (*(p+1)
*(p+1) = 60; //清时 *p -= 1; //时减1 if (*p
}
[Light.c]
#include "Light.h" //指示灯驱动程序头文件
void Light_Init(uint16 port,uint8 name,uint8 state) {
GPIO_Init(port,name,1,state); //初始化指示灯 }
void Light_Control(uint16 port,uint8 name,uint8 state) {
GPIO_Set(port,PRT,name,state); //控制引脚状态 }
void Light_Change(uint16 port,uint8 name) {
if(GPIO_Get(port,PRT,name)==Light_ON) //若原来为"亮",则变"暗" GPIO_Set(port,PRT,name,Light_OFF);
else //若原来为"暗",则变"亮" GPIO_Set(port,PRT,name,Light_ON); }
[LCD.c]
#include "LCD.h" //LCD构件头文件
//构件函数实现
void LCDInit(void)
{
uint16 i;
// 定义数据口为输出
GPIO_Set(LCD_P0,DDR,LCD_B0,1); GPIO_Set(LCD_P1,DDR,LCD_B1,1); GPIO_Set(LCD_P2,DDR,LCD_B2,1); GPIO_Set(LCD_P3,DDR,LCD_B3,1); GPIO_Set(LCD_P4,DDR,LCD_B4,1); GPIO_Set(LCD_P5,DDR,LCD_B5,1); GPIO_Set(LCD_P6,DDR,LCD_B6,1); GPIO_Set(LCD_P7,DDR,LCD_B7,1);
// 定义控制口为输出
GPIO_Set(LCDRS_P,DDR,LCDRS_B,1); GPIO_Set(LCDRW_P,DDR,LCDRW_B,1); GPIO_Set(LCDE_P,DDR,LCDE_B,1);
// 设置指令,RS,R/W = 00, 写指令代码 GPIO_Set(LCDRS_P,PRT,LCDRS_B,0); GPIO_Set(LCDRW_P,PRT,LCDRW_B,0);
// 功能设置
LCD_Command(0b00111000);
//||||||||____ 可设任意值(0/1) //||||||______ F = 0,5*7点阵模式 //|||||_______ N = 1,2行显示 //||||________ DL = 1,8位数据总线 //|||_________ 固定为001
// 显示开关控制
LCD_Command(0b00001000);
//||||||||____ B = 0,不闪烁
//|||||||_____ C = 0,关光标显示 //||||||______ D = 0,关显示 //|||||_______ 固定为00001 // 清屏
// 清DD RAM内容,光标回原位,清AC LCD_Command(0b00000001);
// 等待清屏完毕,时间 > 1.6ms
for (i=0; i
// 输入方式设置
LCD_Command(0b00000110);
//||||||||____ 显示不移动,光标左移(A = 1)
//|||||||_____ 数据读写操作后,AC自动增1
//||||||______ 固定为000001
// 光标或画面移位设置
LCD_Command(0b00010100);
//||||||||____ 可设任意值(0/1)
//||||||______ 光标右移一个字符位,AC自动加1
//||||________ 固定为0001
// 显示开关控制
LCD_Command(0b00001100);
//||||||||____ B = 0,不闪烁
//|||||||_____ C = 0,关光标显示
//||||||______ D = 1,开显示
//|||||_______ 固定为00001
}
void Delay1(uint16 k)
{
uint16 i,j;
for (i=0; i
for (j=0; j
}
//LCDShow:在HD44780显示屏上显示数据
void LCDShow(uint8 str[])
{
uint8 i;
// LCD初始化
LCDInit();
// 显示第1行16个字符
GPIO_Set(LCDRS_P,PRT,LCDRS_B,0); //RS,R/W = 00(写的是指令) GPIO_Set(LCDRW_P,PRT,LCDRW_B,0);
LCD_Command(0x80); //后7位为DD RAM地址(0x00)
//2.2写16个数据到DD RAM
GPIO_Set(LCDRS_P,PRT,LCDRS_B,1); //RS,R/W = 10(写的是数据) //设置显示首地址
GPIO_Set(LCDRW_P,PRT,LCDRW_B,0);
// 将要显示在第1行上的16个数据逐个写入DD RAM中
for (i = 0;i
{
LCD_Command(str[i]);
}
// 显示第2行16个字符
//设置显示首地址
GPIO_Set(LCDRS_P,PRT,LCDRS_B,0); //RS,R/W = 00(写的是指令) GPIO_Set(LCDRW_P,PRT,LCDRW_B,0);
LCD_Command(0xC0); //后7位为DD RAM地址(0x40)
// 再写16个数据到DD RAM
GPIO_Set(LCDRS_P,PRT,LCDRS_B,1); //RS,R/W = 10(写的是数据) GPIO_Set(LCDRW_P,PRT,LCDRW_B,0);
// 将要显示在第2行上的16个数据逐个写入DD RAM中
for (i = 16;i
{
LCD_Command(str[i]);
}
}
//LCD_Command:执行给定的cmd命令
void LCD_Command(uint8 cmd)
{
uint8 i;
// 等待 > 40us
Delay1(5);
// 数据送到LCD的数据线上
GPIO_Set(LCD_P0,PRT,LCD_B0,((cmd >> 0) & 0x01));
GPIO_Set(LCD_P1,PRT,LCD_B1,((cmd >> 1) & 0x01));
GPIO_Set(LCD_P2,PRT,LCD_B2,((cmd >> 2) & 0x01));
GPIO_Set(LCD_P3,PRT,LCD_B3,((cmd >> 3) & 0x01));
GPIO_Set(LCD_P4,PRT,LCD_B4,((cmd >> 4) & 0x01));
GPIO_Set(LCD_P5,PRT,LCD_B5,((cmd >> 5) & 0x01));
GPIO_Set(LCD_P6,PRT,LCD_B6,((cmd >> 6) & 0x01));
GPIO_Set(LCD_P7,PRT,LCD_B7,((cmd >> 7) & 0x01));
// 给出E信号的下降沿,使数据写入LCD
GPIO_Set(LCDE_P,PRT,LCDE_B,1);
for(i=0;i
asm("NOP");
// Lcd结束接收数据
GPIO_Set(LCDE_P,PRT,LCDE_B,0);
// 等待 > 40us
Delay1(5);
}
[KBI_I.c]
//头文件包含,及宏定义区
//头文件包含
#include "KBI_I.h" //键盘构件头文件
//构件函数实现
void KBInit(void)
{
GPPort_Set(KB_P,PTI,1,0x0F); //复位相应寄存器
GPPort_Set(KB_P,DDR,0,0xF0); //定义列线(PTP0~3)为输入
GPPort_Set(KB_A,PRT,0,0xF0); //复位相应寄存器
GPPort_Set(KB_A,DDR,1,0x0F); //行线(PTA0~3)为输出
GPPort_Set(KB_P,PER,1,0x0F); //输入引脚(列线)有内部上拉电阻
GPPort_Set(KB_P,PPS,0,0xF0); //下降沿产生中断
DisableKBint(); //禁止键盘中断
GPPort_Set(KB_P,PIF,1,0x0F); //清除键盘中断请求
}
uint8 KBScan1(void)
{
uint8 i,tmp;
for (i = 0; i
{
GPPort_Set(KB_A,PRT,1,0x0F); //当前扫描的一行,输出低电平
GPPort_Set(KB_A,PRT,0,~(1
asm("NOP");
asm("NOP");
//读取键盘口数据寄存器
tmp=GPPort_Get(KB_P,PTI)&0x0F;
//通过观察4根列线中是否出现低电平来判断当前行有无按键
if (tmp != 0x0F) //当前行有键按下
{
tmp= (tmp
break; //退出循环不再扫描
}
}
if (i == 4) //无按键,以后将返回0xff tmp = 0xFF;
return (tmp);
}
//键盘定义表
const uint8 KBTable[] =
{
0xEE,'7',0xDE,'8',0xBE,'9',0x7E,'C',
0xED,'4',0xDD,'5',0xBD,'6',0x7D,'D',
0xEB,'1',0xDB,'2',0xBB,'3',0x7B,'E',
0xE7,'0',0xD7,'A',0xB7,'B',0x77,'F',
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;
}
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_value_now = KBScan1();
if (KB_value_now == KB_value_last)
return KB_value_now; //返回扫描的键值
else
KB_value_last = KB_value_now;
}
//返回出错标志
return 0xFF;
}
七、设计心得
在本次的实验中,要求我们将平时所学的各个模块添加在一起,从而实现洗衣机控制器的各个功能。
本设计着重在于算法的功能实现,硬件连接较之简单。由于程序是在做过的实验中改动的,在一开始程序的修改、编写上遇到了不少的问题,有的算法需要自己计算写入,有的值需要设定,在输入代码的过程中,出现了许多的错误,比如说少写分号,漏写大括号,不需要用到的函数的调用没有注释掉等,这些错误使程序调试的时候有不少错误,其间也询问过老师很多次,老师都一一给我解决了。在后来连接到实验箱下载设定参数看现象的时候也出现了许多的问题,比如键盘控制不了输入,通过老师以及同学的帮助,对应着程序作了修改最终让键盘输入功能得以实现。通过了本次实验使我对程序设计有了新的认识与体会,做实验不单单是在软件上运行出结果就表明对了,当其与硬件连接后能否对应现象也是非常重要的,我们不仅要学好软件,也要将硬件部分做好,多学习,多思考,在今后的运用中也将会更加的得心应手。