飞思卡尔单片机电子钟课程设计之欧阳与创编
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
目录
第一章系统概要2
1.1 系统背景2
1.2 系统功能3
第二章系统硬件设计3
2.1 系统原理图3
2.2 单片机(MCU)模块4
2.2.1 MC9S08AW60单片机性能概述4
2.2.2 内部结构简图5
2.3 串行通信模块5
2.3.1 MAX232引脚图5
2.3.2 串行通信的电路原理7
2.4 液晶显示模块8
第三章系统软件设计9
3.1 MCU方(C)程序9
3.1.1串行通信子程序16
3.1.2 LCD子程序19
第四章系统测试22
第五章总结展望22
5.1 总结22
5.2 展望22
参考文献22
第一章系统概要
1.1 系统背景
数字时钟,当我们听到这几个字时,第一反应就是我们所说的数字,不错数字钟就是以数字显示取代模拟表盘的钟表,在显示上它用数字反应出此时的时间,相比模拟钟能给人一种一目了然的感觉,不仅如此它还能同时显示时、分、秒。
而且能对时、分、秒准确校时,这是普通钟所不及的。
由于单片机集成度高、功能强、可靠性高、体积小、功耗地、使用方便、价格低廉等一系列优点,目前已经渗入到人们工作和生活的方方面面,几乎“无处不在,无所不为”。
单片机的应用领域已从面向工业控制、通讯、交通、智能仪表等迅速发展到家用消费产品、办公自动化、汽车电子、PC机外围以及网络通讯等广大领域。
1.2 系统功能
在实验箱上有一个启动键,当按下启动键给以一个低电平,电子时钟从当前设定值开始走时。
按秒刷新,要求在LCD屏上显示。
若按启动键给以高电平,则时间暂停,再按,时间继续按秒刷新。
第二章系统硬件设计
2.1 系统原理图
该系统由AW60最小系统电路为主要结构,利用串口进行数据的控制与采集。
首先将开关接在AW60上的PORT_D口上,用于控制数字时钟系统的开关。
然后将LCD的数据线7-14引脚(D0-D7)分别与MCU的PTA0-PTA7连接,LCD的控制线RS、R/W、E(4、5、6引脚)分别于MCU的PTC4、PTC6、PTF6连接,用于输出时间。
数字时钟必须要有晶振电路,所以将该晶振电路与AW60的PTG5和PTG6相连,用于时间的自加。
由于在运行系统时,以防电流不稳定,所以在PTB0端设置一个下拉电阻,稳定电流。
2.2 单片机(MCU)模块
2.2.1MC9S08AW60单片机性能概述
(1)最高达40MHz的CPU工作频率和20Hz的内部总线工作频率表;时钟源选项包括晶振、谐振器、外部时钟或内部产生的时钟。
(2)相比HC08 CPU指令集,S08 CPU增加了BGND指令。
(3)单线后台调试模式接口;增强的断点能力,允许单一的断点设置在线调试(在片内调试的模块增加了多于两个的断点)。
(4)内含32个中断/复位源;内含2KB的片内RAM;内含60KB的片内在线可编程Flash存储器,带有块保护和安全选项。
(5)可选的计算机正常操作(COP)复位;低电压检测和复位或中断;非法操作码检测与复位;非法地址检测与复位。
(6)ADC:多达16个通道,10位A/D转换器与自动比较功能;两个串行通信接口SCI模块与可选的13位中断;一个串行外设接口SPI模块;集成电路互连总线I2C模块运作高达100kbps的最高总线负载;8引
脚键盘中断KBI模块。
(7)Timers:1个2通道和1个6通道16位定时器/脉冲宽度调制器模板。
具有输入、捕捉、输出比较、脉宽调制功能。
2.2.2 内部结构简图
1. 内部结构简图
如图所示,给出了AW60的内部结构图,它对于我们理解和应用AW60 MCU有重要作用,在学习了基本有法后,应在反过来熟悉这个内部结构图,以便更好地理解AW60 MCU的基本原理。
从内部结构图可以看出,AW60主要有以下几个部分:S08 CPU、存储器、定时器接口模块、定时器模块、看门狗模块、通用IO模块、串口通信模块(SCI)、串行外设接口(SPI)模块、I2C(IIC)模块、A/D转换模块、键盘中断模块、时钟发生模块、复位与中断模块等。
2.3 串行通信模块
2.3.1 MAX232引脚图
在MCU中,若用RS-232总线进行串行通信,则需外接电路实现电平转换。
在发送端,需要用驱动电
路将TTL 电平转换成RS-232电平;在接受端,需要用接收电路将RS-232电平。
转化为TTL电平。
电平转换器不仅可以由晶振管分立元件构成,也可以直接使用集成电路。
目前使用MAX232芯片较多,该芯片使用单一+5V电源供电实现电平转换。
如图所示,给出了MAX232的引脚说明。
各引脚含义简要说明如下:
Vcc(16脚):正电源端,一般接+5V。
GND(15脚):地。
V S+(2脚):V S+=2V CC-1.5V=8.5V。
VS-(6脚):V S-=-2VCC-1.5V=-11.5V。
C2+、C2-(4、5脚):一般接1μF的电解电容。
C1+、C1-(1、3脚):一般接1μF的电解电容。
表 MAX232芯片输入输出引脚分类与基本接法
在正常情况下,(1)T1IN=5V,则T1OUT=-9V;
T1IN=0V,则T1OUT=9V。
(2)将R1IN与T1OUT 相连,令T1IN=5V,则R1OUT=5V;令T1IN=0V,则R1OUT=0V。
MAX232芯片进行电平转换的基本原理:(1)发送过程:MCU的TxD(TTL电平)经过MAX232的11脚(T1IN)送到MAX232内部,在内部TTL电平被“提升”为232电平,通过14脚(T1OUT)发送出去。
接受过程:外部232电平经过MAX232的13脚(R1IN)进入到MAX232的内部,在内部232电平被“降低”为TTL电平,经过12脚(R1OUT送到MCU的RxD,进入MCU内部。
2.3.2 串行通信的电路原理
从基本原理的角度看,串行通信接口SCI的主要功能是:接收时,把外部的单线输入的数据变成一个
字节的并行数据送入MCU内部;发送时,把需要发送的一个字节的并行数据转换为单线输入。
为了设置波特率,SCI应具有波特率寄存器。
为了能够设置通信格式、是否校验、是否允许中断等,SCI应具有控制寄存器。
而要知道串口是否有数据可收、数据是否发送出去等,需要有SCI状态寄存器。
当然,若一个寄存器不够用,控制与状态寄存器可能有多个。
而SCI数据寄存器存放要发送的数据,也存放接受的数据,这并不冲突,因为发送与接收的实际工作是通过“发送移位寄存器”和“接收以为寄存器”完成的。
编程时,程序员并不直接与“发送移位寄存器”和“接收移位寄存器”打交道,只与数据寄存器打交道,所以MCU中并没有设置“发送移位寄存器和“接收移位寄存器”的映像地址。
发送时,程序员通过判定状态寄存器的相应位,了解是否可以发送一个新的数据。
若可以发送,则将待发送的数据放入“SCI数据寄存器”中就可以了,剩下的工作由MCU 自动完成:将数据从“SCI数据寄存器”送到“发送移位寄存器”,硬件驱动将“发送移位寄存器”的数据一位一位地按照规定的波特率移到发送引脚TxD,供对方接收。
接收时,数据一位一位地从接收引脚
RxD进入“接收移位寄存器”,当收到一个完成字节时,MCU会自动将数据送入“SCI数据寄存器”,并将状态寄存器的相应位改变,供程序员判定并取出数据。
2.4 液晶显示模块
器把
字符型液晶显示模块目前在国际上已经规范化,其电特性及接口特性是统一的,因此,只要设计出一种型号的接口电路,在指令上稍加修改即可使用各种规格的字符型液晶显示模块。
点阵字符型液晶显示模块的控制器大多数为日立公司生产的HD44780及其兼容的控制电路,如SED1278(SEIKO EPSON)、KS0066(SAMSUNG)、NJU6408(NER JAPANRADIO)等。
字符型液晶显示模块的主要特点如下:
1.液晶显示屏是以若干5*8或5*11点阵块组成的显示字符群。
每个点阵块为一个字符位,字符间距和行距都为一个点的宽度。
2.主控制电路为HD44780(HITACHI)及其他公司的兼容电路。
从程序员的角度来说,LCD的显示接口与编程是面向HD44780的,只要了解HD44780的编程结构即可进行LCD的显示编程。
3.内部具有字符发生器ROM,可显示192种字符(160个5*7点阵字符和32个5*10点阵字符)。
4.具有64字节的字符发生器RAM,可以定义8个5*8点阵字符或4个5*11点阵字符。
5.具有64字节的数据显示RAM,供显示编程时使用
6.标准接口特性,与MC9S08系列MCU容易接口。
7.模块结构紧凑、轻巧、装配容易。
8.单+5V电源供电(宽温型需要加-7V驱动电源)。
9.低功耗、高可靠性。
第三章系统软件设计3.1 MCU方(C)程序
main.c
#include "Includes.h"
#include "LCD.h"
#include "SCI.h"
#include "timer.h"
#include "GPIO.h"
//在此添加全局变量定义
uint8 g_time[8];
void main(void){
uint8 g_DispalyInit[]="00:00:00";
uint8 remember;
uint32 mRuncount=0;
uint8 i;
uint8 m;
int n=1;
//1 关总中断
DisableInterrupt(); //禁止总中断
//2 芯片初始化
MCUInit();
//3 模块初始化
Light_Init(Light_Run_PORT,Light_Run,Light_OFF);
LCDinit();
TPMinit(TPM_NUM_1);
SCIInit(SCI_NUM_1,SYSTEM_CLOCK,9600);
//定时器
//内存初始化
g_time[0]=0;
g_time[1]=0;
g_time[2]=':';
g_time[3]=0;
g_time[4]=0;
g_time[5]=':';
g_time[6]=0;
g_time[7]=0;
remember=g_time[7];
//开放中断
//LCD
LCDshow(g_DispalyInit);
while(n){
if(GPIO_Get(LCD_Run_PORT,0)==LCD_Run){ remember = g_time[7];
n = 0;
EnableSCIReInt();
EnableInterrupt();
EnabletimerInt(TPM_NUM_1);
}
//4 主循环
while (!n){
if(g_time[7]!=remember){
for(i=0;i<8;i++) {
if(i==2 || i ==5){
g_DispalyInit[i] =g_time[i];
}
else{
m=g_time[i];
g_DispalyInit[i]=m+'0';
}
}
LCDshow(g_DispalyInit);
SCISendN(SCI_NUM_1,3,g_time);
remember=g_time[7];
}
if(GPIO_Get(LCD_Run_PORT,0)!=LCD_Run){ LCDshow(g_DispalyInit);
n = 1;
DisableInterrupt();
DisableSCIReInt();
DisabletimerInt(TPM_NUM_1);
}
}
}
}
isr.c
#include "Includes.h"
//此处为用户新定义中断处理函数的存放处 #include "timer.h"
//此处为用户新定义中断处理函数的存放处interrupt void isrT1Out(void){
DisableInterrupt();
SecAdd1(g_time);
TPM_CSTR(1) &=~(TPM1SC_TOF_MASK);
EnableInterrupt();
}
//未定义的中断处理函数,本函数不能删除
interrupt void isrDummy(void){
}
//中断处理子程序类型定义
typedef void( *ISR_func_t)(void);
//中断矢量表,如果需要定义其它中断函数,请修改下表中的相应项目
const ISR_func_t ISR_vectors[] @0xFFCC = {
isrDummy, // 0xFFCC //时基中断
isrDummy, // 0xFFCE //IIC中断
isrDummy, // 0xFFD0 //ADC转换中断
isrDummy, // 0xFFD2 //键盘中断
isrDummy, // 0xFFD4 //SCI2发送中断
isrDummy, // 0xFFD6 //SCI2接收中断
isrDummy, // 0xFFD8 //SCI2错误中断
isrDummy, // 0xFFDA //SCI1发送中断
isrDummy, // 0xFFDC //SCI1接收中断
isrDummy, // 0xFFDE //SCI1错误中断
isrDummy, // 0xFFE0 //SPI中断
isrDummy, // 0xFFE2 //TPM2溢出中断
isrDummy, // 0xFFE4 //TPM2通道1输入捕捉/输出比较中断
isrDummy, // 0xFFE6 //TPM2通道0输入捕捉/输出比较中断
isrT1Out, // 0xFFE8 //TPM1溢出中断
isrDummy, // 0xFFEA //TPM1通道5输入捕捉/输出比较中断
isrDummy, // 0xFFEC //TPM1通道4输入捕捉/输出比较中断
isrDummy, // 0xFFEE //TPM1通道3输入捕捉/输出比较中断
isrDummy, // 0xFFF0 //TPM1通道2输入捕捉/输出比较中断
isrDummy, // 0xFFF2 //TPM1通道1输入捕捉/输出比较中断
isrDummy, // 0xFFF4 //TPM1通道0输入捕捉/输出比较中断
isrDummy, // 0xFFF6 //ICG的PLL锁相状
态变化中断
isrDummy, // 0xFFF8 //低电压检测中断
isrDummy, // 0xFFFA //IRQ引脚中断
isrDummy // 0xFFFC //SWI指令中断
//RESET是特殊中断,其向量由开发环境直接设置(在本软件系统的Start08.o文件中)
};
timer.c
#include "timer.h"
void TPMinit(uint8 TPMNo){
if(TPMNo > 2)
TPMNo = 2;
else if(TPMNo < 1)
TPMNo=1;
TPM_CSTR(TPMNo)=0b00010110;
TPM_CNTH(TPMNo) = 0x00;
TPM_CNTL(TPMNo) = 0x00;
TPM_MODH(TPMNo) = 0x7A;
TPM_MODL(TPMNo) = 0x12;
}
void SecAdd1(uint8 *p){
*(p+7)+=1;
if(*(p+7)>=10){
*(p+7) = 0;
*(p+6)+=1;
if(*(p+6)>=6){
*(p+6) = 0;
*(p+4)+=1;
if(*(p+4)>=10){
*(p+4) = 0;
*(p+3)+=1;
if(*(p+3)>=6){
*(p+3) = 0;
*(p+1)+=1;
if(*(p+1)>=9){
*(p+1) = 0;
*p+=1;
}
if((*p*10+*(p+1))>=24) *p = 0;
*(p+1) = 0;
}
}
}
}
}
timer.h
#ifndef timeR_H
#define timeR_H
#include "MC9S08AW60.h"
#include "Type.h"
#define TPM_CSTR(x)(*(vuint8 *)(0x00000020+(x-1)*64))
#define TPM_CNTH(x)(*(vuint8 *)(0x00000021+(x-1)*64))
#define TPM_CNTL(x)(*(vuint8 *)(0x00000022+(x-1)*64))
#define TPM_MODH(x)(*(vuint8 *)(0x00000023+(x-1)*64))
#define TPM_MODL(x)(*(vuint8 *)(0x00000024+(x-1)*64))
#define EnabletimerInt(x) TPM_CSTR(x) |= TPM1SC_TOIE_MASK
#define DisabletimerInt(x) TPM_CSTR(x) &=~TPM1SC_TOIE_MASK
#define TPM_NUM_1 1
#define TPM_NUM_2 2
#define TPM1_CH_0 0
#define TPM1_CH_1 1
#define TPM1_CH_2 2
#define TPM1_CH_3 3
#define TPM1_CH_4 4
#define TPM1_CH_5 5
#define TPM2_CH_0 0
#define TPM2_CH_1 1
void TPMinit(uint8 TPMNo);
void SecAdd1(uint8 *p);
#endif
3.1.1串行通信子程序
SCI.c
#include "SCI.h"
void SCIInit(uint8 SCINo, uint8 sysclk, uint16 baud){
uint16 ubgs;
ubgs=0;
if(SCINo>2){
SCINo=2;
}
ubgs=sysclk*(10000/(baud/100))/16;
SCI_BDH(SCINo)=(uint8)((ubgs&0xFF00)>>8); SCI_BDL(SCINo)=(uint8)(ubgs&0x00FF);
SCI_C1(SCINo)=0b00000000;
SCI_C2(SCINo)=0b00001100;
}
void SCISend1(uint8 SCINo, uint8 ch) {
if(SCINo>2){
SCINo=2;
}
while(!(SCI_S1(SCINo)&0b1000000));
SCI_D(SCINo)=ch;
}
uint8 SCIRe1(uint8 SCINo, uint8 *p) {
uint16 k;
uint8 i;
if(SCINo>2){
SCINo=2;
}
for(k=0;k<0xfbbb;k++)
if((SCI_S1(SCINo)&0b00100000)!=0){
i=SCI_D(SCINo);
*p=0x00;
break;
}
if(k>=0xfbbb){
i=0xff;
*p=0x01;
}
return i;
}
void SCISendN(uint8 SCINo, uint16 n, uint8 ch[]) { uint16 i;
if(SCINo>2) {
SCINo=2;
}
for(i=0;i<n;i++)
SCISend1(SCINo,ch[i]);
}
uint8 SCIReN(uint8 SCINo, uint16 n, uint8 ch[]) { uint16 m;
uint8 fp;
m=0;
if(SCINo>2) {
SCINo=2;
}
while(m<n) {
ch[m]=SCIRe1(SCINo,&fp);
if(fp==1) {
return 1;
}
m++;
}
return 0;
}
void SCISendString(uint8 SCINo, char *p){
uint32 k;
if(SCINo>2) {
SCINo=2;
}
if(p==0) return;
for(k=0;p[k]!='\0';++k) {
SCISend1(SCINo,p[k]);
}
}
SCI.h
#ifndef SCI_H
#define SCI_H
#include "MC9S08AW60.h"
#include "Type.h"
#define SCI_BDH(x) (*(vuint8 *)(0x00000038+(x-1)*8))
#define SCI_BDL(x) (*(vuint8 *)(0x00000039+(x-1)*8))
#define SCI_C1(x) (*(vuint8 *)(0x0000003A+(x-1)*8))
#define SCI_C2(x) (*(vuint8 *)(0x0000003B+(x-1)*8))
#define SCI_S1(x) (*(vuint8 *)(0x0000003C+(x-
1)*8))
#define SCI_S2(x) (*(vuint8 *)(0x0000003D+(x-1)*8))
#define SCI_C3(x) (*(vuint8 *)(0x0000003E+(x-1)*8))
#define SCI_D(x) (*(vuint8 *)(0x0000003F+(x-1)*8))
#define EnableSCIReInt() SCI1C2 |=(SCI1C2_RIE_MASK)
#define DisableSCIReInt() SCI1C2 &=~(SCI1C2_RIE_MASK)
#define SCI_NUM_1 1
#define SCI_NUM_2 2
void SCIInit(uint8 SCINo,uint8 sysclk,uint16 baud);
void SCISend1(uint8 SCINo,uint8 ch);
void SCISendN(uint8 SCINo,uint16 n,uint8 ch[]);
uint8 SCIRe1(uint8 SCINo,uint8 *p);
uint8 SCIReN(uint8 SCINo,uint16 n,uint8 ch[]);
void SCISendString(uint8 SCINo,char *p);
#endif
3.1.2 LCD子程序
LCD.c
#include "LCD.h"
#include "GPIO.h"
void LCDinit(void) {
uint16 i;
LCDdataD = 0b11111111;
LCDctrlD1 |= (1 << LcdRS);
LCDctrlD1 |= (1 << LcdRW);
LCDctrl1 &=~(1 << LcdRS);
LCDctrl1 &=~(1 << LcdRW);
LCDctrlD2 |= (1 << LcdE);
LCDctrl2 |= (1 << LcdE);
LCDcommand (0b00111000);
LCDcommand (0b00001000);
LCDcommand (0b00000001);
for(i=0;i<4000;i++)
asm("NOP");
LCDcommand (0b00000110);
LCDcommand (0b00010100);
LCDcommand (0b00001100);
GPIO_Init(LCD_Run_PORT,0,0,0); }
void LCDcommand(uint8 cmd){
uint16 i;
for(i=0;i<1000;i++)
asm("NOP");
LCDdata=cmd;
LCDctrl2 |= (1<<LcdE);
asm("NOP");
asm("NOP");
asm("NOP");
LCDctrl2 &=~(1<<LcdE);
for(i=0;i<1000;i++)
asm("NOP");
}
void LCDshow(uint8 str[]) {
uint8 i;
LCDinit();
LCDctrl1 &=~(1<<LcdRS);
LCDctrl1 &=~(1<<LcdRW);
LCDcommand (0b10000000);
LCDctrl1 |=1<<LcdRS;
LCDctrl1 |=~(1<<LcdRW);
for(i=0;i<8;i++) {
LCDcommand(str[i]);
}
}
LCD.h
#ifndef LCD_H
#define LCD_H
#include "MC9S08AW60.h"
#include "Type.h"
#include "GeneralFun.h"
#define LCDdata PTAD
#define LCDdataD PTADD
#define LCDctrl1 PTCD
#define LCDctrlD1 PTCDD
#define LCDctrl2 PTFD
#define LCDctrlD2 PTFDD
#define LcdRS 4
#define LcdRW 6
#define LcdE 6
#define LCD_Run_PORT PORT_E
#define LCD_Run 1
void LCDinit(void);
void LCDcommand(uint8 cmd);
void LCDshow(uint8 str[]);
void LCDshoww(uint8 str[]);
#endif
第四章系统测试
调试界面截图:
运行界面截图:
第五章总结展望
5.1 总结
经过了为期一周半的单片机课程设计,首先是对与飞思卡尔的单片机系统有了一定的了解。
由于之前就做过几次的实验,而且以前也上过C语言的课程。
这次的课程设计,思路很清晰。
课程是做一个基于LCD 显示的计数器。
在原有LCD液晶程序和计数器程序
修改的基础上,经过几次修改和整理,在结束之前还是完成了此次的设计。
LCD上能够显示计数。
这次的课程设计不一样。
由于是在原有程序的基础上修改整合,需要对原有的程序进行一个整体的了解和深入。
这对与实际的开发很有帮助。
能够深入了解飞思卡尔的设计思路。
拓展我们的思路。
课程设计的内容虽然没有什么太大的实际意义。
但是,我们能够了解到实际开发的一些步骤和思路。
对于以后的工作也很有帮助的吧。
每一次的课程设计,都是一次学习,都是一次进步。
5.2 展望
对于此次课程设计,我们只是做了一些简单的工作。
虽然有研究过飞思卡尔的单片机程序,但是毕竟自己的知识能力有限。
不可能在短短的一周半时间太过于深入。
这一周半的时间,了解了单片机的很多东西吧。
对自己的要求是,要多动手,自己动手写代码学习的才更快。
对于编程,看别人的百遍不及自己动手写一遍。
如果可能,希望完全自己动手设计一个计数器的程序。
欧阳与创编 2021.03.08
参考文献
【1】王宜怀、张书奎、王林等著,嵌入式技术基础与实践,清华大学出版社
【2】谭浩强著,C语言程序设计(第四版),北京:清华大学出版社
【3】顾波著,单片机技术基础及应用,中国电力出版社
【4】谢晖著,单片机原理及应用,化学工业出版社【5】张跃常、戴卫恒著,Freescale系列单片机常有模块与综合系统设计实例精讲,电子工业出版社
欧阳与创编 2021.03.08。